Fossil

Check-in [85a0ada6]
Login

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

Overview
Comment:Merge in latest from trunk
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | nick.lloyd-git-interop
Files: files | file ages | folders
SHA1:85a0ada6919b78f9a6ca42a58d75371457f8efb4
User & Date: nick.lloyd 2016-09-21 16:06:43
Context
2016-09-21
19:05
Fix a problem that can occasionally occur with repeated syncs to/from a git repository, where a fossil-generated mark clashes with a mark previously generated by git, causing the sync to fail. check-in: 69668f8c user: nick.lloyd tags: nick.lloyd-git-interop
16:06
Merge in latest from trunk check-in: 85a0ada6 user: nick.lloyd tags: nick.lloyd-git-interop
14:13
More typo fixes in whyusefossil.wiki. check-in: f2ce2e41 user: drh tags: trunk
2016-05-16
17:58
Remove C99-isms that MSVC has trouble with. check-in: addfe489 user: drh tags: nick.lloyd-git-interop
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to .fossil-settings/ignore-glob.

1
2
3
4
5

6
compat/openssl*
compat/tcl*
fossil
fossil.exe
win/fossil.exe

*sqlite3-see.*





>

1
2
3
4
5
6
7
compat/openssl*
compat/tcl*
fossil
fossil.exe
win/fossil.exe
*shell-see.*
*sqlite3-see.*

Changes to VERSION.

1
1.35
|
1
1.36

Changes to ajax/js/fossil-ajaj.js.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
..
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
...
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260

    License: Public Domain
*/

/**
    Constructor for a new Fossil AJAJ client. ajajOpt may be an optional
    object suitable for passing to the WhAjaj.Connector() constructor.
    
    On returning, this.ajaj is-a WhAjaj.Connector instance which can 
    be used to send requests to the back-end (though the convenience 
    functions of this class are the preferred way to do it). Clients 
    are encouraged to use FossilAjaj.sendCommand() (and friends) instead 
    of the underlying WhAjaj.Connector API, since this class' API 
    contains Fossil-specific request-calling handling (e.g. of authentication
    info) whereas WhAjaj is more generic.
*/
function FossilAjaj(ajajOpt)
{
    this.ajaj = new WhAjaj.Connector(ajajOpt);
    return this;
................................................................................
};

/**
    Sends a command to the fossil back-end. Command should be the
    path part of the URL, e.g. /json/stat, payload is a request-specific
    value type (may often be null/undefined). ajajOpt is an optional object
    holding WhAjaj.sendRequest()-compatible options.
    
    This function constructs a Fossil/JSON request envelope based
    on the given arguments and adds this.auth.authToken and a requestId
    to it.
*/
FossilAjaj.prototype.sendCommand = function(command, payload, ajajOpt) {
    var req;
    ajajOpt = ajajOpt || {};
................................................................................
    if(command) ajajOpt.url = this.ajaj.derivedOption('url',ajajOpt) + command;
    this.ajaj.sendRequest(req,ajajOpt);
};

/**
    Sends a login request to the back-end.

    ajajOpt is an optional configuration object suitable for passing 
    to sendCommand().

    After the response returns, this.auth will be
    set to the response payload.
    
    If name === 'anonymous' (the default if none is passed in) then this
    function ignores the pw argument and must make two requests - the first
    one gets the captcha code and the second one submits it.
    ajajOpt.onResponse() (if set) is only called for the actual login
    response (the 2nd one), as opposed to being called for both requests.
    However, this.ajaj.callbacks.onResponse() _is_ called for both (because
    it happens at a lower level).
    
    If this object has an onLogin() function it is called (with
    no arguments) before the onResponse() handler of the login is called
    (that is the 2nd request for anonymous logins) and any exceptions
    it throws are ignored.

*/
FossilAjaj.prototype.login = function(name,pw,ajajOpt) {
................................................................................
    }
    else doLogin();
};

/**
    Logs out of fossil, invaliding this login token.

    ajajOpt is an optional configuration object suitable for passing 
    to sendCommand().
    
    If this object has an onLogout() function it is called (with
    no arguments) before the onResponse() handler is called.
    IFF the response succeeds then this.auth is unset.
*/
FossilAjaj.prototype.logout = function(ajajOpt) {
    var self = this;
    ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
................................................................................
    };
    this.sendCommand('/json/logout', undefined, ajajOpt );
};

/**
    Sends a HAI request to the server. /json/HAI is an alias /json/version.

    ajajOpt is an optional configuration object suitable for passing 
    to sendCommand().
*/
FossilAjaj.prototype.HAI = function(ajajOpt) {
    this.sendCommand('/json/HAI', undefined, ajajOpt);
};


................................................................................
    mode, feeds it the request envelope, and returns the response envelope
    via the same mechanisms defined for the HTTP-based implementations.

    The interface is otherwise compatible with the "normal"
    FossilAjaj.sendCommand() front-end (it is, however, fossil-specific, and
    not back-end agnostic like the WhAjaj.sendImpl() interface intends).

    
*/
FossilAjaj.rhinoLocalBinarySendImpl = function(request,args){
    var self = this;
    request = request || {};
    if(!args.fossilBinary){
        throw new Error("fossilBinary is not set on AJAX options!");
    }
    var url = args.url.split('?')[0].split(/\/+/);
    if(url.length>1){
        // 3x shift(): protocol, host, 'json' part of path
        request.command = (url.shift(),url.shift(),url.shift(), url.join('/'));
    }
    delete args.url;
    //print("rhinoLocalBinarySendImpl SENDING: "+WhAjaj.stringify(request));    
    var json;
    try{
        var pargs = [args.fossilBinary, 'json', '--json-input', '-'];
        var p = java.lang.Runtime.getRuntime().exec(pargs);
        var outs = p.getOutputStream();
        var osr = new java.io.OutputStreamWriter(outs);
        var osb = new java.io.BufferedWriter(osr);
        
        json = JSON.stringify(request);
        osb.write(json,0, json.length);
        osb.close();
        var ins = p.getInputStream();
        var isr = new java.io.InputStreamReader(ins);
        var br = new java.io.BufferedReader(isr);
        var line;







|
|
|
|
|
|







 







|







 







|




|







|







 







|

|







 







|







 







|













|







|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
..
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
...
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260

    License: Public Domain
*/

/**
    Constructor for a new Fossil AJAJ client. ajajOpt may be an optional
    object suitable for passing to the WhAjaj.Connector() constructor.

    On returning, this.ajaj is-a WhAjaj.Connector instance which can
    be used to send requests to the back-end (though the convenience
    functions of this class are the preferred way to do it). Clients
    are encouraged to use FossilAjaj.sendCommand() (and friends) instead
    of the underlying WhAjaj.Connector API, since this class' API
    contains Fossil-specific request-calling handling (e.g. of authentication
    info) whereas WhAjaj is more generic.
*/
function FossilAjaj(ajajOpt)
{
    this.ajaj = new WhAjaj.Connector(ajajOpt);
    return this;
................................................................................
};

/**
    Sends a command to the fossil back-end. Command should be the
    path part of the URL, e.g. /json/stat, payload is a request-specific
    value type (may often be null/undefined). ajajOpt is an optional object
    holding WhAjaj.sendRequest()-compatible options.

    This function constructs a Fossil/JSON request envelope based
    on the given arguments and adds this.auth.authToken and a requestId
    to it.
*/
FossilAjaj.prototype.sendCommand = function(command, payload, ajajOpt) {
    var req;
    ajajOpt = ajajOpt || {};
................................................................................
    if(command) ajajOpt.url = this.ajaj.derivedOption('url',ajajOpt) + command;
    this.ajaj.sendRequest(req,ajajOpt);
};

/**
    Sends a login request to the back-end.

    ajajOpt is an optional configuration object suitable for passing
    to sendCommand().

    After the response returns, this.auth will be
    set to the response payload.

    If name === 'anonymous' (the default if none is passed in) then this
    function ignores the pw argument and must make two requests - the first
    one gets the captcha code and the second one submits it.
    ajajOpt.onResponse() (if set) is only called for the actual login
    response (the 2nd one), as opposed to being called for both requests.
    However, this.ajaj.callbacks.onResponse() _is_ called for both (because
    it happens at a lower level).

    If this object has an onLogin() function it is called (with
    no arguments) before the onResponse() handler of the login is called
    (that is the 2nd request for anonymous logins) and any exceptions
    it throws are ignored.

*/
FossilAjaj.prototype.login = function(name,pw,ajajOpt) {
................................................................................
    }
    else doLogin();
};

/**
    Logs out of fossil, invaliding this login token.

    ajajOpt is an optional configuration object suitable for passing
    to sendCommand().

    If this object has an onLogout() function it is called (with
    no arguments) before the onResponse() handler is called.
    IFF the response succeeds then this.auth is unset.
*/
FossilAjaj.prototype.logout = function(ajajOpt) {
    var self = this;
    ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
................................................................................
    };
    this.sendCommand('/json/logout', undefined, ajajOpt );
};

/**
    Sends a HAI request to the server. /json/HAI is an alias /json/version.

    ajajOpt is an optional configuration object suitable for passing
    to sendCommand().
*/
FossilAjaj.prototype.HAI = function(ajajOpt) {
    this.sendCommand('/json/HAI', undefined, ajajOpt);
};


................................................................................
    mode, feeds it the request envelope, and returns the response envelope
    via the same mechanisms defined for the HTTP-based implementations.

    The interface is otherwise compatible with the "normal"
    FossilAjaj.sendCommand() front-end (it is, however, fossil-specific, and
    not back-end agnostic like the WhAjaj.sendImpl() interface intends).


*/
FossilAjaj.rhinoLocalBinarySendImpl = function(request,args){
    var self = this;
    request = request || {};
    if(!args.fossilBinary){
        throw new Error("fossilBinary is not set on AJAX options!");
    }
    var url = args.url.split('?')[0].split(/\/+/);
    if(url.length>1){
        // 3x shift(): protocol, host, 'json' part of path
        request.command = (url.shift(),url.shift(),url.shift(), url.join('/'));
    }
    delete args.url;
    //print("rhinoLocalBinarySendImpl SENDING: "+WhAjaj.stringify(request));
    var json;
    try{
        var pargs = [args.fossilBinary, 'json', '--json-input', '-'];
        var p = java.lang.Runtime.getRuntime().exec(pargs);
        var outs = p.getOutputStream();
        var osr = new java.io.OutputStreamWriter(outs);
        var osb = new java.io.BufferedWriter(osr);

        json = JSON.stringify(request);
        osb.write(json,0, json.length);
        osb.close();
        var ins = p.getInputStream();
        var isr = new java.io.InputStreamReader(ins);
        var br = new java.io.BufferedReader(isr);
        var line;

Changes to ajax/js/whajaj.js.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
..
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
..
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
...
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
...
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
...
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
...
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
...
562
563
564
565
566
567
568
569
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
...
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
...
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
...
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
...
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
....
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
....
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
....
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194

    All functionality is part of a class named WhAjaj, and that class
    acts as namespace for this framework.

    Author: Stephan Beal (http://wanderinghorse.net/home/stephan/)

    License: Public Domain
    
    This framework is directly derived from code originally found in 
    http://code.google.com/p/jsonmessage, and later in 
    http://whiki.wanderinghorse.net, where it contained quite a bit 
    of application-specific logic. It was eventually (the 3rd time i 
    needed it) split off into its own library to simplify inclusion 
    into my many mini-projects.
*/


/**
    The WhAjaj function is primarily a namespace, and not intended 
    to called or instantiated via the 'new' operator.
*/
function WhAjaj()
{
}

/** Returns a millisecond Unix Epoch timestamp. */
................................................................................
WhAjaj.msTimestamp = function()
{
    return (new Date()).getTime();
};

/** Returns a Unix Epoch timestamp (in seconds) in integer format.

    Reminder to self: (1.1 %1.2) evaluates to a floating-point value 
    in JS, and thus this implementation is less than optimal.
*/
WhAjaj.unixTimestamp = function()
{
    var ts = (new Date()).getTime();
    return parseInt( ""+((ts / 1000) % ts) );
};
................................................................................
    )
    ;
};

/**
    Parses window.location.search-style string into an object
    containing key/value pairs of URL arguments (already urldecoded).
    
    If the str argument is not passed (arguments.length==0) then 
    window.location.search.substring(1) is used by default. If 
    neither str is passed in nor window exists then false is returned.

    On success it returns an Object containing the key/value pairs
    parsed from the string. Keys which have no value are treated
    has having the boolean true value.
    
    FIXME: for keys in the form "name[]", build an array of results,
    like PHP does.
 
*/
WhAjaj.processUrlArgs = function(str) {
    if( 0 === arguments.length ) {
        if( ('undefined' === typeof window) ||
            !window.location ||
            !window.location.search )  return false;
        else str = (''+window.location.search).substring(1);
................................................................................
};

/**
    A simple wrapper around JSON.stringify(), using my own personal
    preferred values for the 2nd and 3rd parameters. To globally
    set its indentation level, assign WhAjaj.stringify.indent to
    an integer value (0 for no intendation).
    
    This function is intended only for human-readable output, not
    generic over-the-wire JSON output (where JSON.stringify(val) will
    produce smaller results).
*/
WhAjaj.stringify = function(val) {
    if( ! arguments.callee.indent ) arguments.callee.indent = 4;
    return JSON.stringify(val,0,arguments.callee.indent);
};

/**
    Each instance of this class holds state information for making 
    AJAJ requests to a back-end system. While clients may use one 
    "requester" object per connection attempt, for connections to the
    same back-end, using an instance configured for that back-end 
    can simplify usage. This class is designed so that the actual 
    connection-related details (i.e. _how_ it connects to the 
    back-end) may be re-implemented to use a client's preferred 
    connection mechanism (e.g. jQuery).
    
    The optional opt paramater may be an object with any (or all) of 
    the properties documented for WhAjaj.Connector.options.ajax. 
    Properties set here (or later via modification of the "options" 
    property of this object) will be used in calls to 
    WhAjaj.Connector.sendRequest(), and these override (normally) any
    options set in WhAjaj.Connector.options.ajax. Note that 
    WhAjaj.Connector.sendRequest() _also_ takes an options object, 
    and ones passed there will override, for purposes of that one 
    request, any options passed in here or defined in 
    WhAjaj.Connector.options.ajax. See WhAjaj.Connector.options.ajax
    and WhAjaj.Connector.prototype.sendRequest() for more details
    about the precedence of options.
    
    Sample usage:
    
    @code
    // Set up common connection-level options:
    var cgi = new WhAjaj.Connector({
        url: '/cgi-bin/my.cgi',
        timeout:10000,
        onResponse(resp,req) { alert(JSON.stringify(resp,0.4)); },
        onError(req,opt) {
................................................................................
        onResponse(resp,req){ alert(WhAjaj.stringify(resp)); }
    });
    @endcode

    For common request types, clients can add functions to this
    object which act as wrappers for backend-specific functionality. As
    a simple example:
    
    @code
    cgi.login = function(name,pw,ajajOpt) {
        this.sendRequest(
            {command:"json/login",
              name:name,
              password:pw
            }, ajajOpt );
    };
    @endcode
    
    TODOs:
    
    - Caching of page-load requests, with a configurable lifetime.
    
    - Use-cases like the above login() function are a tiny bit
    problematic to implement when each request has a different URL
    path (i know this from the whiki and fossil implementations).
    This is partly a side-effect of design descisions made back in
    the very first days of this code's life. i need to go through
    and see where i can bend those conventions a bit (where it won't
    break my other apps unduly).
................................................................................
WhAjaj.Connector.options = {
    /**
        A (meaningless) prefix to apply to WhAjaj.Connector-generated
        request IDs.
    */
    requestIdPrefix:'WhAjaj.Connector-',
    /**
        Default options for WhAjaj.Connector.sendRequest() connection 
        parameters. This object holds only connection-related 
        options and callbacks (all optional), and not options 
        related to the required JSON structure of any given request. 
        i.e. the page name used in a get-page request are not set 
        here but are specified as part of the request object.

        These connection options are a "normalized form" of options 
        often found in various AJAX libraries like jQuery, 
        Prototype, dojo, etc. This approach allows us to swap out 
        the real connection-related parts by writing a simple proxy 
        which transforms our "normalized" form to the 
        backend-specific form. For examples, see the various 
        implementations stored in WhAjaj.Connector.sendImpls.

        The following callback options are, in practice, almost 
        always set globally to some app-wide defaults:

        - onError() to report errors using a common mechanism.
        - beforeSend() to start a visual activity notification
        - afterSend() to disable the visual activity notification

        However, be aware that if any given WhAjaj.Connector instance is 
        given its own before/afterSend callback then those will 
        override these. Mixing shared/global and per-instance
        callbacks can potentially lead to confusing results if, e.g.,
        the beforeSend() and afterSend() functions have side-effects
        but are not used with their proper before/after partner.
        
        TODO: rename this to 'ajaj' (the name is historical). The 
        problem with renaming it is is that the word 'ajax' is 
        pretty prevelant in the source tree, so i can't globally 
        swap it out.
    */
    ajax: {
        /**
            URL of the back-end server/CGI.
        */
        url: '/some/path',

        /**
            Connection method. Some connection-related functions might
            override any client-defined setting.
            
            Must be one of 'GET' or 'POST'. For custom connection 
            implementation, it may optionally be some 
            implementation-specified value.

            Normally the API can derive this value automatically - if the
            request uses JSON data it is POSTed, else it is GETted.
        */
        method:'GET',

        /**
            A hint whether to run the operation asynchronously or 
            not. Not all concrete WhAjaj.Connector.sendImpl() 
            implementations can support this. Interestingly, at 
            least one popular AJAX toolkit does not document 
            supporting _synchronous_ AJAX operations. All common 
            browser-side implementations support async operation, but 
            non-browser implementations might not.
        */
        asynchronous:true,

        /**
            A HTTP authentication login name for the AJAX 
            connection. Not all concrete WhAjaj.Connector.sendImpl() 
            implementations can support this.
        */
        loginName:undefined,

        /**
            An HTTP authentication login password for the AJAJ 
            connection. Not all concrete WhAjaj.Connector.sendImpl() 
            implementations can support this.
        */
        loginPassword:undefined,

        /**
            A connection timeout, in milliseconds, for establishing 
            an AJAJ connection. Not all concrete 
            WhAjaj.Connector.sendImpl() implementations can support this.
        */
        timeout:10000,

        /**
            If an AJAJ request receives JSON data from the back-end,
            that data is passed as a plain Object as the response
................................................................................
            ignore it (only those which need a way to map specific
            requests to responses will need it). The 3rd parameter
            is the same as the 'this' object for the context of the
            callback, but is provided because the instance-level
            callbacks (set in (WhAjaj.Connector instance).callbacks,
            require it in some cases (because their 'this' is
            different!).
            
            Note that the response might contain error information
            which comes from the back-end. The difference between
            this error info and the info passed to the onError()
            callback is that this data indicates an
            application-level error, whereas onError() is used to
            report connection-level problems or when the backend
            produces non-JSON data (which, when not in jsonp mode,
            is unexpected and is as fatal to the request as a
            connection error).
        */
        onResponse: function(response, request, opt){},

        /**
            If an AJAX request fails to establish a connection or it 
            receives non-JSON data from the back-end, this function 
            is called (e.g. timeout error or host name not 
            resolvable). It is passed the originating request and the
            "normalized" connection parameters used for that 
            request. The connectOpt object "should" (or "might") 
            have an "errorMessage" property which describes the 
            nature of the problem.
            
            Clients will almost always want to replace the default 
            implementation with something which integrates into 
            their application.
        */
        onError: function(request, connectOpt)
        {
            alert('AJAJ request failed:\n'
                +'Connection information:\n'
                +JSON.stringify(connectOpt,0,4)
            );
        },

        /**
            Called before each connection attempt is made. Clients 
            can use this to, e.g.,  enable a visual "network activity
            notification" for the user. It is passed the original 
            request object and the normalized connection parameters 
            for the request. If this function changes opt, those 
            changes _are_ applied to the subsequent request. If this 
            function throws, neither the onError() nor afterSend() 
            callbacks are triggered and WhAjaj.Connector.sendImpl() 
            propagates the exception back to the caller.
        */
        beforeSend: function(request,opt){},

        /**
            Called after an AJAJ connection attempt completes, 
            regardless of success or failure. Passed the same 
            parameters as beforeSend() (see that function for 
            details).
            
            Here's an example of setting up a visual notification on 
            ajax operations using jQuery (but it's also easy to do 
            without jQuery as well):
            
            @code
            function startAjaxNotif(req,opt) {
                var me = arguments.callee;
                var c = ++me.ajaxCount;
                me.element.text( c + " pending AJAX operation(s)..." );
                if( 1 == c ) me.element.stop().fadeIn();
            }
            startAjaxNotif.ajaxCount = 0.
            startAjaxNotif.element = jQuery('#whikiAjaxNotification');
            
            function endAjaxNotif() {
                var c = --startAjaxNotif.ajaxCount;
                startAjaxNotif.element.text( c+" pending AJAX operation(s)..." );
                if( 0 == c ) startAjaxNotif.element.stop().fadeOut();
            }
            @endcode

            Set the beforeSend/afterSend properties to those 
            functions to enable the notifications by default.            
        */
        afterSend: function(request,opt){},

        /**
            If jsonp is a string then the WhAjaj-internal response
            handling code ASSUMES that the response contains a JSONP-style
            construct and eval()s it after afterSend() but before onResponse().
            In this case, onResponse() will get a string value for the response
            instead of a response object parsed from JSON. 
        */
        jsonp:undefined,
        /**
            Don't use yet. Planned future option.
        */
        propagateExceptions:false
    }
................................................................................
    else v = this.options[key];
    if( undefined !== v ) return v;
    else v = WhAjaj.Connector.options.ajax[key];
    return v;
};

/**
    Returns a unique string on each call containing a generic 
    reandom request identifier string. This is not used by the core 
    API but can be used by client code to generate unique IDs for 
    each request (if needed).

    The exact format is unspecified and may change in the future.

    Request IDs can be used by clients to "match up" responses to 
    specific requests if needed. In practice, however, they are 
    seldom, if ever, needed. When passing several concurrent 
    requests through the same response callback, it might be useful 
    for some clients to be able to distinguish, possibly re-routing 
    them through other handlers based on the originating request type.
    
    If this.options.requestIdPrefix or 
    WhAjaj.Connector.options.requestIdPrefix is set then that text
    is prefixed to the returned string.
*/
WhAjaj.Connector.prototype.generateRequestId = function()
{
    if( undefined === arguments.callee.sequence )
    {
................................................................................
        if( ! opt.hasOwnProperty(k) ) continue /* proactive Prototype kludge! */;
        this.options[k] = opt[k];
    }
    return this.options;
};

/**
    An internal helper object which holds several functions intended 
    to simplify the creation of concrete communication channel 
    implementations for WhAjaj.Connector.sendImpl(). These operations
    take care of some of the more error-prone parts of ensuring that
    onResponse(), onError(), etc. callbacks are called consistently 
    using the same rules.
*/
WhAjaj.Connector.sendHelper = {
    /**
        opt is assumed to be a normalized set of 
        WhAjaj.Connector.sendRequest() options. This function 
        creates a url by concatenating opt.url and some form of 
        opt.urlParam.
        
        If opt.urlParam is an object or string then it is appended 
        to the url. An object is assumed to be a one-dimensional set 
        of simple (urlencodable) key/value pairs, and not larger 
        data structures. A string value is assumed to be a 
        well-formed, urlencoded set of key/value pairs separated by 
        '&' characters.
        
        The new/normalized URL is returned (opt is not modified). If 
        opt.urlParam is not set then opt.url is returned (or an 
        empty string if opt.url is itself a false value).
        
        TODO: if opt is-a Object and any key points to an array, 
        build up a list of keys in the form "keyname[]". We could 
        arguably encode sub-objects like "keyname[subkey]=...", but 
        i don't know if that's conventions-compatible with other 
        frameworks.
    */
    normalizeURL: function(opt) {
        var u = opt.url || '';
        if( opt.urlParam ) {
            var addQ = (u.indexOf('?') >= 0) ? false : true;
            var addA = addQ ? false : ((u.indexOf('&')>=0) ? true : false);
................................................................................
                tail = opt.urlParam;
            }
            u = u + (addQ ? '?' : '') + (addA ? '&' : '') + tail;
        }
        return u;
    },
    /**
        Should be called by WhAjaj.Connector.sendImpl() 
        implementations after a response has come back. This 
        function takes care of most of ensuring that framework-level 
        conventions involving WhAjaj.Connector.options.ajax 
        properties are followed.
        
        The request argument must be the original request passed to 
        the sendImpl() function. It may legally be null for GET requests.
        
        The opt object should be the normalized AJAX options used 
        for the connection.
        
        The resp argument may be either a plain Object or a string 
        (in which case it is assumed to be JSON).

        The 'this' object for this call MUST be a WhAjaj.Connector
        instance in order for callback processing to work properly.
        
        This function takes care of the following:
        
        - Calling opt.afterSend()
        
        - If resp is a string, de-JSON-izing it to an object.
        
        - Calling opt.onResponse()
        
        - Calling opt.onError() in several common (potential) error 
        cases.

        - If resp is-a String and opt.jsonp then resp is assumed to be
        a JSONP-form construct and is eval()d BEFORE opt.onResponse()
        is called. It is arguable to eval() it first, but the logic
        integrates better with the non-jsonp handler.

        The sendImpl() should return immediately after calling this.
        
        The sendImpl() must call only one of onSendSuccess() or 
        onSendError(). It must call one of them or it must implement 
        its own response/error handling, which is not recommended 
        because getting the documented semantics of the 
        onError/onResponse/afterSend handling correct can be tedious.
    */
    onSendSuccess:function(request,resp,opt) {
        var cb = this.callbacks || {};
        if( WhAjaj.isFunction(cb.afterSend) ) {
            try {cb.afterSend( request, opt );}
            catch(e){}
................................................................................
    },
   /**
        Should be called by sendImpl() implementations after a response
        has failed to connect (e.g. could not resolve host or timeout
        reached). This function takes care of most of ensuring that
        framework-level conventions involving WhAjaj.Connector.options.ajax
        properties are followed.
        
        The request argument must be the original request passed to 
        the sendImpl() function. It may legally be null for GET 
        requests.

        The 'this' object for this call MUST be a WhAjaj.Connector
        instance in order for callback processing to work properly.
        
        The opt object should be the normalized AJAX options used 
        for the connection. By convention, the caller of this 
        function "should" set opt.errorMessage to contain a 
        human-readable description of the error.
        
        The sendImpl() should return immediately after calling this. The
        return value from this function is unspecified.
    */
    onSendError: function(request,opt) {
        var cb = this.callbacks || {};
        if( WhAjaj.isFunction(cb.afterSend) ) {
            try {cb.afterSend( request, opt );}
................................................................................
            try {opt.onError( request, opt );}
            catch(e) {/*ignore*/}
        }
    }
};

/**
    WhAjaj.Connector.sendImpls holds several concrete 
    implementations of WhAjaj.Connector.prototype.sendImpl(). To use 
    a specific implementation by default assign 
    WhAjaj.Connector.prototype.sendImpl to one of these functions.
    
    The functions defined here require that the 'this' object be-a
    WhAjaj.Connector instance.
    
    Historical notes:

    a) We once had an implementation based on Prototype, but that 
    library just pisses me off (they change base-most types' 
    prototypes, introducing side-effects in client code which 
    doesn't even use Prototype). The Prototype version at the time 
    had a serious toJSON() bug which caused empty arrays to 
    serialize as the string "[]", which broke a bunch of my code. 
    (That has been fixed in the mean time, but i don't use 
    Prototype.)
    
    b) We once had an implementation for the dojo library, 
    
    If/when the time comes to add Prototype/dojo support, we simply
    need to port:
    
    http://code.google.com/p/jsonmessage/source/browse/trunk/lib/JSONMessage/JSONMessage.inc.js
    
    (search that file for "dojo" and "Prototype") to this tree. That 
    code is this code's generic grandfather and they are still very 
    similar, so a port is trivial.    
    
*/
WhAjaj.Connector.sendImpls = {
    /**
        This is a concrete implementation of 
        WhAjaj.Connector.prototype.sendImpl() which uses the 
        environment's native XMLHttpRequest class to send whiki 
        requests and fetch the responses.

        The only argument must be a connection properties object, as 
        constructed by WhAjaj.Connector.normalizeAjaxParameters().

        If window.firebug is set then window.firebug.watchXHR() is 
        called to enable monitoring of the XMLHttpRequest object.

        This implementation honors the loginName and loginPassword 
        connection parameters.

        Returns the XMLHttpRequest object.

        This implementation requires that the 'this' object be-a 
        WhAjaj.Connector.
        
        This implementation uses setTimeout() to implement the 
        timeout support, and thus the JS engine must provide that 
        functionality.
    */
    XMLHttpRequest: function(request, args)
    {
        var json = WhAjaj.isObject(request) ? JSON.stringify(request) : request;
        var xhr = new XMLHttpRequest();
        var startTime = (new Date()).getTime();
................................................................................
        {
            args.errorMessage = e.toString();
            WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
            return undefined;
        }
    }/*XMLHttpRequest()*/,
    /**
        This is a concrete implementation of 
        WhAjaj.Connector.prototype.sendImpl() which uses the jQuery 
        AJAX API to send requests and fetch the responses.

        The first argument may be either null/false, an Object 
        containing toJSON-able data to post to the back-end, or such an
        object in JSON string form.

        The second argument must be a connection properties object, as 
        constructed by WhAjaj.Connector.normalizeAjaxParameters().

        If window.firebug is set then window.firebug.watchXHR() is 
        called to enable monitoring of the XMLHttpRequest object.

        This implementation honors the loginName and loginPassword 
        connection parameters.

        Returns the XMLHttpRequest object.

        This implementation requires that the 'this' object be-a 
        WhAjaj.Connector.
    */
    jQuery:function(request,args)
    {
        var data = request || undefined;
        var whself = this;
        if( data ) {
................................................................................
        {
            args.errorMessage = e.toString();
            WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
            return undefined;
        }
    }/*jQuery()*/,
    /**
        This is a concrete implementation of 
        WhAjaj.Connector.prototype.sendImpl() which uses the rhino 
        Java API to send requests and fetch the responses.

        Limitations vis-a-vis the interface:

        - timeouts are not supported.

        - asynchronous mode is not supported because implementing it
................................................................................
        json = json.join('');
        //print("READ IN JSON: "+json);
        WhAjaj.Connector.sendHelper.onSendSuccess.apply( self, [request, json, args] );
    }/*rhino*/
};

/**
    An internal function which takes an object containing properties 
    for a WhAjaj.Connector network request. This function creates a new 
    object containing a superset of the properties from:

    a) opt
    b) this.options
    c) WhAjaj.Connector.options.ajax

    in that order, using the first one it finds.

    All non-function properties are _deeply_ copied via JSON cloning 
    in order to prevent accidental "cross-request pollenation" (been 
    there, done that). Functions cannot be cloned and are simply 
    copied by reference.
    
    This function throws if JSON-copying one of the options fails
    (e.g. due to cyclic data structures).
    
    Reminder to self: this function does not "normalize" opt.urlParam
    by encoding it into opt.url, mainly for historical reasons, but
    also because that behaviour was specifically undesirable in this
    code's genetic father.
*/
WhAjaj.Connector.prototype.normalizeAjaxParameters = function (opt)
{
................................................................................
    cp( this.options );
    cp( WhAjaj.Connector.options.ajax );
    // no, not here: rc.url = WhAjaj.Connector.sendHelper.normalizeURL(rc);
    return rc;
};

/**
    This is the generic interface for making calls to a back-end 
    JSON-producing request handler. It is a simple wrapper around 
    WhAjaj.Connector.prototype.sendImpl(), which just normalizes the 
    connection options for sendImpl() and makes sure that 
    opt.beforeSend() is (possibly) called.
    
    The request parameter must either be false/null/empty or a 
    fully-populated JSON-able request object (which will be sent as 
    unencoded application/json text), depending on the type of 
    request being made. It is never semantically legal (in this API) 
    for request to be a string/number/true/array value. As a rule, 
    only POST requests use the request data. GET requests should 
    encode their data in opt.url or opt.urlParam (see below).
    
    opt must contain the network-related parameters for the request. 
    Paramters _not_ set in opt are pulled from this.options or 
    WhAjaj.Connector.options.ajax (in that order, using the first 
    value it finds). Thus the set of connection-level options used 
    for the request are a superset of those various sources.
    
    The "normalized" (or "superimposed") opt object's URL may be 
    modified before the request is sent, as follows:

    if opt.urlParam is a string then it is assumed to be properly 
    URL-encoded parameters and is appended to the opt.url. If it is 
    an Object then it is assumed to be a one-dimensional set of 
    key/value pairs with simple values (numbers, strings, booleans, 
    null, and NOT objects/arrays). The keys/values are URL-encoded 
    and appended to the URL.

    The beforeSend() callback (see below) can modify the options
    object before the request attempt is made.
   
    The callbacks in the normalized opt object will be triggered as
    follows (if they are set to Function values):
    
    - beforeSend(request,opt) will be called before any network 
    processing starts. If beforeSend() throws then no other 
    callbacks are triggered and this function propagates the 
    exception. This function is passed normalized connection options
    as its second parameter, and changes this function makes to that
    object _will_ be used for the pending connection attempt.
    
    - onError(request,opt) will be called if a connection to the 
    back-end cannot be established. It will be passed the original 
    request object (which might be null, depending on the request 
    type) and the normalized options object. In the error case, the 
    opt object passed to onError() "should" have a property called 
    "errorMessage" which contains a description of the problem.
    
    - onError(request,opt) will also be called if connection 
    succeeds but the response is not JSON data.
    
    - onResponse(response,request) will be called if the response 
    returns JSON data. That data might hold an error response code - 
    clients need to check for that. It is passed the response object 
    (a plain object) and the original request object.
    
    - afterSend(request,opt) will be called directly after the
    AJAX request is finished, before onError() or onResonse() are
    called. Possible TODO: we explicitly do NOT pass the response to
    this function in order to keep the line between the responsibilities
    of the various callback clear (otherwise this could be used the same
    as onResponse()). In practice it would sometimes be useful have the
    response passed to this function, mainly for logging/debugging
................................................................................
{
    if( !WhAjaj.isFunction(this.sendImpl) )
    {
        throw new Error("This object has no sendImpl() member function! I don't know how to send the request!");
    }
    var ex = false;
    var av = Array.prototype.slice.apply( arguments, [0] );
    
    /**
        FIXME: how to handle the error, vis-a-vis- the callbacks, if 
        normalizeAjaxParameters() throws? It can throw if 
        (de)JSON-izing fails.
    */
    var norm = this.normalizeAjaxParameters( WhAjaj.isObject(opt) ? opt : {} );
    norm.url = WhAjaj.Connector.sendHelper.normalizeURL(norm);
    if( ! request ) norm.method = 'GET';
    var cb = this.callbacks || {};
    if( this.callbacks && WhAjaj.isFunction(this.callbacks.beforeSend) ) {







|
|
|
|
|
|





|







 







|







 







|
|
|





|


|







 







|










|
|

|
|
|
|

|
|
|
|
|

|
|
|
|



|

|







 







|









|

|

|







 







|
|
|
|
|


|
|
|
|
|
|


|






|
|




|
|
|
|











|
|
|








|
|
|
|
|
|





|
|





|
|





|
|







 







|













|
|
|

|
|
|

|
|
|











|

|
|
|
|
|
|





|
|
|

|
|
|

|









|







|
|








|







 







|
|
|




|
|
|
|
|

|
|







 







|
|


|




|
|
|

|
|
|
|
|
|

|
|
|

|
|
|
|
|







 







|
|
|
|

|
|

|
|

|
|




|

|

|

|

|
|








|
|
|
|
|







 







|
|
|




|
|
|
|

|







 







|
|
|

|


|


|
|
|
|
|
|
|

|
|
|


|

|
|
|
|
|



|
|
|


|


|


|




|

|
|
|







 







|
|


|



|


|


|




|







 







|
|







 







|
|








|
|
|

|


|







 







|
|
|
|

|
|
|
|
|
|
|

|
|
|
|
|

|
|


|
|
|
|
|




|


|
|
|
|



|
|
|
|
|
|

|
|

|
|
|
|

|







 







|

|
|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
..
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
..
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
...
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
...
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
...
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
...
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
...
562
563
564
565
566
567
568
569
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
...
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
...
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
...
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
...
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
....
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
....
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
....
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194

    All functionality is part of a class named WhAjaj, and that class
    acts as namespace for this framework.

    Author: Stephan Beal (http://wanderinghorse.net/home/stephan/)

    License: Public Domain

    This framework is directly derived from code originally found in
    http://code.google.com/p/jsonmessage, and later in
    http://whiki.wanderinghorse.net, where it contained quite a bit
    of application-specific logic. It was eventually (the 3rd time i
    needed it) split off into its own library to simplify inclusion
    into my many mini-projects.
*/


/**
    The WhAjaj function is primarily a namespace, and not intended
    to called or instantiated via the 'new' operator.
*/
function WhAjaj()
{
}

/** Returns a millisecond Unix Epoch timestamp. */
................................................................................
WhAjaj.msTimestamp = function()
{
    return (new Date()).getTime();
};

/** Returns a Unix Epoch timestamp (in seconds) in integer format.

    Reminder to self: (1.1 %1.2) evaluates to a floating-point value
    in JS, and thus this implementation is less than optimal.
*/
WhAjaj.unixTimestamp = function()
{
    var ts = (new Date()).getTime();
    return parseInt( ""+((ts / 1000) % ts) );
};
................................................................................
    )
    ;
};

/**
    Parses window.location.search-style string into an object
    containing key/value pairs of URL arguments (already urldecoded).

    If the str argument is not passed (arguments.length==0) then
    window.location.search.substring(1) is used by default. If
    neither str is passed in nor window exists then false is returned.

    On success it returns an Object containing the key/value pairs
    parsed from the string. Keys which have no value are treated
    has having the boolean true value.

    FIXME: for keys in the form "name[]", build an array of results,
    like PHP does.

*/
WhAjaj.processUrlArgs = function(str) {
    if( 0 === arguments.length ) {
        if( ('undefined' === typeof window) ||
            !window.location ||
            !window.location.search )  return false;
        else str = (''+window.location.search).substring(1);
................................................................................
};

/**
    A simple wrapper around JSON.stringify(), using my own personal
    preferred values for the 2nd and 3rd parameters. To globally
    set its indentation level, assign WhAjaj.stringify.indent to
    an integer value (0 for no intendation).

    This function is intended only for human-readable output, not
    generic over-the-wire JSON output (where JSON.stringify(val) will
    produce smaller results).
*/
WhAjaj.stringify = function(val) {
    if( ! arguments.callee.indent ) arguments.callee.indent = 4;
    return JSON.stringify(val,0,arguments.callee.indent);
};

/**
    Each instance of this class holds state information for making
    AJAJ requests to a back-end system. While clients may use one
    "requester" object per connection attempt, for connections to the
    same back-end, using an instance configured for that back-end
    can simplify usage. This class is designed so that the actual
    connection-related details (i.e. _how_ it connects to the
    back-end) may be re-implemented to use a client's preferred
    connection mechanism (e.g. jQuery).

    The optional opt paramater may be an object with any (or all) of
    the properties documented for WhAjaj.Connector.options.ajax.
    Properties set here (or later via modification of the "options"
    property of this object) will be used in calls to
    WhAjaj.Connector.sendRequest(), and these override (normally) any
    options set in WhAjaj.Connector.options.ajax. Note that
    WhAjaj.Connector.sendRequest() _also_ takes an options object,
    and ones passed there will override, for purposes of that one
    request, any options passed in here or defined in
    WhAjaj.Connector.options.ajax. See WhAjaj.Connector.options.ajax
    and WhAjaj.Connector.prototype.sendRequest() for more details
    about the precedence of options.

    Sample usage:

    @code
    // Set up common connection-level options:
    var cgi = new WhAjaj.Connector({
        url: '/cgi-bin/my.cgi',
        timeout:10000,
        onResponse(resp,req) { alert(JSON.stringify(resp,0.4)); },
        onError(req,opt) {
................................................................................
        onResponse(resp,req){ alert(WhAjaj.stringify(resp)); }
    });
    @endcode

    For common request types, clients can add functions to this
    object which act as wrappers for backend-specific functionality. As
    a simple example:

    @code
    cgi.login = function(name,pw,ajajOpt) {
        this.sendRequest(
            {command:"json/login",
              name:name,
              password:pw
            }, ajajOpt );
    };
    @endcode

    TODOs:

    - Caching of page-load requests, with a configurable lifetime.

    - Use-cases like the above login() function are a tiny bit
    problematic to implement when each request has a different URL
    path (i know this from the whiki and fossil implementations).
    This is partly a side-effect of design descisions made back in
    the very first days of this code's life. i need to go through
    and see where i can bend those conventions a bit (where it won't
    break my other apps unduly).
................................................................................
WhAjaj.Connector.options = {
    /**
        A (meaningless) prefix to apply to WhAjaj.Connector-generated
        request IDs.
    */
    requestIdPrefix:'WhAjaj.Connector-',
    /**
        Default options for WhAjaj.Connector.sendRequest() connection
        parameters. This object holds only connection-related
        options and callbacks (all optional), and not options
        related to the required JSON structure of any given request.
        i.e. the page name used in a get-page request are not set
        here but are specified as part of the request object.

        These connection options are a "normalized form" of options
        often found in various AJAX libraries like jQuery,
        Prototype, dojo, etc. This approach allows us to swap out
        the real connection-related parts by writing a simple proxy
        which transforms our "normalized" form to the
        backend-specific form. For examples, see the various
        implementations stored in WhAjaj.Connector.sendImpls.

        The following callback options are, in practice, almost
        always set globally to some app-wide defaults:

        - onError() to report errors using a common mechanism.
        - beforeSend() to start a visual activity notification
        - afterSend() to disable the visual activity notification

        However, be aware that if any given WhAjaj.Connector instance is
        given its own before/afterSend callback then those will
        override these. Mixing shared/global and per-instance
        callbacks can potentially lead to confusing results if, e.g.,
        the beforeSend() and afterSend() functions have side-effects
        but are not used with their proper before/after partner.

        TODO: rename this to 'ajaj' (the name is historical). The
        problem with renaming it is is that the word 'ajax' is
        pretty prevelant in the source tree, so i can't globally
        swap it out.
    */
    ajax: {
        /**
            URL of the back-end server/CGI.
        */
        url: '/some/path',

        /**
            Connection method. Some connection-related functions might
            override any client-defined setting.

            Must be one of 'GET' or 'POST'. For custom connection
            implementation, it may optionally be some
            implementation-specified value.

            Normally the API can derive this value automatically - if the
            request uses JSON data it is POSTed, else it is GETted.
        */
        method:'GET',

        /**
            A hint whether to run the operation asynchronously or
            not. Not all concrete WhAjaj.Connector.sendImpl()
            implementations can support this. Interestingly, at
            least one popular AJAX toolkit does not document
            supporting _synchronous_ AJAX operations. All common
            browser-side implementations support async operation, but
            non-browser implementations might not.
        */
        asynchronous:true,

        /**
            A HTTP authentication login name for the AJAX
            connection. Not all concrete WhAjaj.Connector.sendImpl()
            implementations can support this.
        */
        loginName:undefined,

        /**
            An HTTP authentication login password for the AJAJ
            connection. Not all concrete WhAjaj.Connector.sendImpl()
            implementations can support this.
        */
        loginPassword:undefined,

        /**
            A connection timeout, in milliseconds, for establishing
            an AJAJ connection. Not all concrete
            WhAjaj.Connector.sendImpl() implementations can support this.
        */
        timeout:10000,

        /**
            If an AJAJ request receives JSON data from the back-end,
            that data is passed as a plain Object as the response
................................................................................
            ignore it (only those which need a way to map specific
            requests to responses will need it). The 3rd parameter
            is the same as the 'this' object for the context of the
            callback, but is provided because the instance-level
            callbacks (set in (WhAjaj.Connector instance).callbacks,
            require it in some cases (because their 'this' is
            different!).

            Note that the response might contain error information
            which comes from the back-end. The difference between
            this error info and the info passed to the onError()
            callback is that this data indicates an
            application-level error, whereas onError() is used to
            report connection-level problems or when the backend
            produces non-JSON data (which, when not in jsonp mode,
            is unexpected and is as fatal to the request as a
            connection error).
        */
        onResponse: function(response, request, opt){},

        /**
            If an AJAX request fails to establish a connection or it
            receives non-JSON data from the back-end, this function
            is called (e.g. timeout error or host name not
            resolvable). It is passed the originating request and the
            "normalized" connection parameters used for that
            request. The connectOpt object "should" (or "might")
            have an "errorMessage" property which describes the
            nature of the problem.

            Clients will almost always want to replace the default
            implementation with something which integrates into
            their application.
        */
        onError: function(request, connectOpt)
        {
            alert('AJAJ request failed:\n'
                +'Connection information:\n'
                +JSON.stringify(connectOpt,0,4)
            );
        },

        /**
            Called before each connection attempt is made. Clients
            can use this to, e.g.,  enable a visual "network activity
            notification" for the user. It is passed the original
            request object and the normalized connection parameters
            for the request. If this function changes opt, those
            changes _are_ applied to the subsequent request. If this
            function throws, neither the onError() nor afterSend()
            callbacks are triggered and WhAjaj.Connector.sendImpl()
            propagates the exception back to the caller.
        */
        beforeSend: function(request,opt){},

        /**
            Called after an AJAJ connection attempt completes,
            regardless of success or failure. Passed the same
            parameters as beforeSend() (see that function for
            details).

            Here's an example of setting up a visual notification on
            ajax operations using jQuery (but it's also easy to do
            without jQuery as well):

            @code
            function startAjaxNotif(req,opt) {
                var me = arguments.callee;
                var c = ++me.ajaxCount;
                me.element.text( c + " pending AJAX operation(s)..." );
                if( 1 == c ) me.element.stop().fadeIn();
            }
            startAjaxNotif.ajaxCount = 0.
            startAjaxNotif.element = jQuery('#whikiAjaxNotification');

            function endAjaxNotif() {
                var c = --startAjaxNotif.ajaxCount;
                startAjaxNotif.element.text( c+" pending AJAX operation(s)..." );
                if( 0 == c ) startAjaxNotif.element.stop().fadeOut();
            }
            @endcode

            Set the beforeSend/afterSend properties to those
            functions to enable the notifications by default.
        */
        afterSend: function(request,opt){},

        /**
            If jsonp is a string then the WhAjaj-internal response
            handling code ASSUMES that the response contains a JSONP-style
            construct and eval()s it after afterSend() but before onResponse().
            In this case, onResponse() will get a string value for the response
            instead of a response object parsed from JSON.
        */
        jsonp:undefined,
        /**
            Don't use yet. Planned future option.
        */
        propagateExceptions:false
    }
................................................................................
    else v = this.options[key];
    if( undefined !== v ) return v;
    else v = WhAjaj.Connector.options.ajax[key];
    return v;
};

/**
    Returns a unique string on each call containing a generic
    reandom request identifier string. This is not used by the core
    API but can be used by client code to generate unique IDs for
    each request (if needed).

    The exact format is unspecified and may change in the future.

    Request IDs can be used by clients to "match up" responses to
    specific requests if needed. In practice, however, they are
    seldom, if ever, needed. When passing several concurrent
    requests through the same response callback, it might be useful
    for some clients to be able to distinguish, possibly re-routing
    them through other handlers based on the originating request type.

    If this.options.requestIdPrefix or
    WhAjaj.Connector.options.requestIdPrefix is set then that text
    is prefixed to the returned string.
*/
WhAjaj.Connector.prototype.generateRequestId = function()
{
    if( undefined === arguments.callee.sequence )
    {
................................................................................
        if( ! opt.hasOwnProperty(k) ) continue /* proactive Prototype kludge! */;
        this.options[k] = opt[k];
    }
    return this.options;
};

/**
    An internal helper object which holds several functions intended
    to simplify the creation of concrete communication channel
    implementations for WhAjaj.Connector.sendImpl(). These operations
    take care of some of the more error-prone parts of ensuring that
    onResponse(), onError(), etc. callbacks are called consistently
    using the same rules.
*/
WhAjaj.Connector.sendHelper = {
    /**
        opt is assumed to be a normalized set of
        WhAjaj.Connector.sendRequest() options. This function
        creates a url by concatenating opt.url and some form of
        opt.urlParam.

        If opt.urlParam is an object or string then it is appended
        to the url. An object is assumed to be a one-dimensional set
        of simple (urlencodable) key/value pairs, and not larger
        data structures. A string value is assumed to be a
        well-formed, urlencoded set of key/value pairs separated by
        '&' characters.

        The new/normalized URL is returned (opt is not modified). If
        opt.urlParam is not set then opt.url is returned (or an
        empty string if opt.url is itself a false value).

        TODO: if opt is-a Object and any key points to an array,
        build up a list of keys in the form "keyname[]". We could
        arguably encode sub-objects like "keyname[subkey]=...", but
        i don't know if that's conventions-compatible with other
        frameworks.
    */
    normalizeURL: function(opt) {
        var u = opt.url || '';
        if( opt.urlParam ) {
            var addQ = (u.indexOf('?') >= 0) ? false : true;
            var addA = addQ ? false : ((u.indexOf('&')>=0) ? true : false);
................................................................................
                tail = opt.urlParam;
            }
            u = u + (addQ ? '?' : '') + (addA ? '&' : '') + tail;
        }
        return u;
    },
    /**
        Should be called by WhAjaj.Connector.sendImpl()
        implementations after a response has come back. This
        function takes care of most of ensuring that framework-level
        conventions involving WhAjaj.Connector.options.ajax
        properties are followed.

        The request argument must be the original request passed to
        the sendImpl() function. It may legally be null for GET requests.

        The opt object should be the normalized AJAX options used
        for the connection.

        The resp argument may be either a plain Object or a string
        (in which case it is assumed to be JSON).

        The 'this' object for this call MUST be a WhAjaj.Connector
        instance in order for callback processing to work properly.

        This function takes care of the following:

        - Calling opt.afterSend()

        - If resp is a string, de-JSON-izing it to an object.

        - Calling opt.onResponse()

        - Calling opt.onError() in several common (potential) error
        cases.

        - If resp is-a String and opt.jsonp then resp is assumed to be
        a JSONP-form construct and is eval()d BEFORE opt.onResponse()
        is called. It is arguable to eval() it first, but the logic
        integrates better with the non-jsonp handler.

        The sendImpl() should return immediately after calling this.

        The sendImpl() must call only one of onSendSuccess() or
        onSendError(). It must call one of them or it must implement
        its own response/error handling, which is not recommended
        because getting the documented semantics of the
        onError/onResponse/afterSend handling correct can be tedious.
    */
    onSendSuccess:function(request,resp,opt) {
        var cb = this.callbacks || {};
        if( WhAjaj.isFunction(cb.afterSend) ) {
            try {cb.afterSend( request, opt );}
            catch(e){}
................................................................................
    },
   /**
        Should be called by sendImpl() implementations after a response
        has failed to connect (e.g. could not resolve host or timeout
        reached). This function takes care of most of ensuring that
        framework-level conventions involving WhAjaj.Connector.options.ajax
        properties are followed.

        The request argument must be the original request passed to
        the sendImpl() function. It may legally be null for GET
        requests.

        The 'this' object for this call MUST be a WhAjaj.Connector
        instance in order for callback processing to work properly.

        The opt object should be the normalized AJAX options used
        for the connection. By convention, the caller of this
        function "should" set opt.errorMessage to contain a
        human-readable description of the error.

        The sendImpl() should return immediately after calling this. The
        return value from this function is unspecified.
    */
    onSendError: function(request,opt) {
        var cb = this.callbacks || {};
        if( WhAjaj.isFunction(cb.afterSend) ) {
            try {cb.afterSend( request, opt );}
................................................................................
            try {opt.onError( request, opt );}
            catch(e) {/*ignore*/}
        }
    }
};

/**
    WhAjaj.Connector.sendImpls holds several concrete
    implementations of WhAjaj.Connector.prototype.sendImpl(). To use
    a specific implementation by default assign
    WhAjaj.Connector.prototype.sendImpl to one of these functions.

    The functions defined here require that the 'this' object be-a
    WhAjaj.Connector instance.

    Historical notes:

    a) We once had an implementation based on Prototype, but that
    library just pisses me off (they change base-most types'
    prototypes, introducing side-effects in client code which
    doesn't even use Prototype). The Prototype version at the time
    had a serious toJSON() bug which caused empty arrays to
    serialize as the string "[]", which broke a bunch of my code.
    (That has been fixed in the mean time, but i don't use
    Prototype.)

    b) We once had an implementation for the dojo library,

    If/when the time comes to add Prototype/dojo support, we simply
    need to port:

    http://code.google.com/p/jsonmessage/source/browse/trunk/lib/JSONMessage/JSONMessage.inc.js

    (search that file for "dojo" and "Prototype") to this tree. That
    code is this code's generic grandfather and they are still very
    similar, so a port is trivial.

*/
WhAjaj.Connector.sendImpls = {
    /**
        This is a concrete implementation of
        WhAjaj.Connector.prototype.sendImpl() which uses the
        environment's native XMLHttpRequest class to send whiki
        requests and fetch the responses.

        The only argument must be a connection properties object, as
        constructed by WhAjaj.Connector.normalizeAjaxParameters().

        If window.firebug is set then window.firebug.watchXHR() is
        called to enable monitoring of the XMLHttpRequest object.

        This implementation honors the loginName and loginPassword
        connection parameters.

        Returns the XMLHttpRequest object.

        This implementation requires that the 'this' object be-a
        WhAjaj.Connector.

        This implementation uses setTimeout() to implement the
        timeout support, and thus the JS engine must provide that
        functionality.
    */
    XMLHttpRequest: function(request, args)
    {
        var json = WhAjaj.isObject(request) ? JSON.stringify(request) : request;
        var xhr = new XMLHttpRequest();
        var startTime = (new Date()).getTime();
................................................................................
        {
            args.errorMessage = e.toString();
            WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
            return undefined;
        }
    }/*XMLHttpRequest()*/,
    /**
        This is a concrete implementation of
        WhAjaj.Connector.prototype.sendImpl() which uses the jQuery
        AJAX API to send requests and fetch the responses.

        The first argument may be either null/false, an Object
        containing toJSON-able data to post to the back-end, or such an
        object in JSON string form.

        The second argument must be a connection properties object, as
        constructed by WhAjaj.Connector.normalizeAjaxParameters().

        If window.firebug is set then window.firebug.watchXHR() is
        called to enable monitoring of the XMLHttpRequest object.

        This implementation honors the loginName and loginPassword
        connection parameters.

        Returns the XMLHttpRequest object.

        This implementation requires that the 'this' object be-a
        WhAjaj.Connector.
    */
    jQuery:function(request,args)
    {
        var data = request || undefined;
        var whself = this;
        if( data ) {
................................................................................
        {
            args.errorMessage = e.toString();
            WhAjaj.Connector.sendHelper.onSendError.apply( whself, [request, args] );
            return undefined;
        }
    }/*jQuery()*/,
    /**
        This is a concrete implementation of
        WhAjaj.Connector.prototype.sendImpl() which uses the rhino
        Java API to send requests and fetch the responses.

        Limitations vis-a-vis the interface:

        - timeouts are not supported.

        - asynchronous mode is not supported because implementing it
................................................................................
        json = json.join('');
        //print("READ IN JSON: "+json);
        WhAjaj.Connector.sendHelper.onSendSuccess.apply( self, [request, json, args] );
    }/*rhino*/
};

/**
    An internal function which takes an object containing properties
    for a WhAjaj.Connector network request. This function creates a new
    object containing a superset of the properties from:

    a) opt
    b) this.options
    c) WhAjaj.Connector.options.ajax

    in that order, using the first one it finds.

    All non-function properties are _deeply_ copied via JSON cloning
    in order to prevent accidental "cross-request pollenation" (been
    there, done that). Functions cannot be cloned and are simply
    copied by reference.

    This function throws if JSON-copying one of the options fails
    (e.g. due to cyclic data structures).

    Reminder to self: this function does not "normalize" opt.urlParam
    by encoding it into opt.url, mainly for historical reasons, but
    also because that behaviour was specifically undesirable in this
    code's genetic father.
*/
WhAjaj.Connector.prototype.normalizeAjaxParameters = function (opt)
{
................................................................................
    cp( this.options );
    cp( WhAjaj.Connector.options.ajax );
    // no, not here: rc.url = WhAjaj.Connector.sendHelper.normalizeURL(rc);
    return rc;
};

/**
    This is the generic interface for making calls to a back-end
    JSON-producing request handler. It is a simple wrapper around
    WhAjaj.Connector.prototype.sendImpl(), which just normalizes the
    connection options for sendImpl() and makes sure that
    opt.beforeSend() is (possibly) called.

    The request parameter must either be false/null/empty or a
    fully-populated JSON-able request object (which will be sent as
    unencoded application/json text), depending on the type of
    request being made. It is never semantically legal (in this API)
    for request to be a string/number/true/array value. As a rule,
    only POST requests use the request data. GET requests should
    encode their data in opt.url or opt.urlParam (see below).

    opt must contain the network-related parameters for the request.
    Paramters _not_ set in opt are pulled from this.options or
    WhAjaj.Connector.options.ajax (in that order, using the first
    value it finds). Thus the set of connection-level options used
    for the request are a superset of those various sources.

    The "normalized" (or "superimposed") opt object's URL may be
    modified before the request is sent, as follows:

    if opt.urlParam is a string then it is assumed to be properly
    URL-encoded parameters and is appended to the opt.url. If it is
    an Object then it is assumed to be a one-dimensional set of
    key/value pairs with simple values (numbers, strings, booleans,
    null, and NOT objects/arrays). The keys/values are URL-encoded
    and appended to the URL.

    The beforeSend() callback (see below) can modify the options
    object before the request attempt is made.

    The callbacks in the normalized opt object will be triggered as
    follows (if they are set to Function values):

    - beforeSend(request,opt) will be called before any network
    processing starts. If beforeSend() throws then no other
    callbacks are triggered and this function propagates the
    exception. This function is passed normalized connection options
    as its second parameter, and changes this function makes to that
    object _will_ be used for the pending connection attempt.

    - onError(request,opt) will be called if a connection to the
    back-end cannot be established. It will be passed the original
    request object (which might be null, depending on the request
    type) and the normalized options object. In the error case, the
    opt object passed to onError() "should" have a property called
    "errorMessage" which contains a description of the problem.

    - onError(request,opt) will also be called if connection
    succeeds but the response is not JSON data.

    - onResponse(response,request) will be called if the response
    returns JSON data. That data might hold an error response code -
    clients need to check for that. It is passed the response object
    (a plain object) and the original request object.

    - afterSend(request,opt) will be called directly after the
    AJAX request is finished, before onError() or onResonse() are
    called. Possible TODO: we explicitly do NOT pass the response to
    this function in order to keep the line between the responsibilities
    of the various callback clear (otherwise this could be used the same
    as onResponse()). In practice it would sometimes be useful have the
    response passed to this function, mainly for logging/debugging
................................................................................
{
    if( !WhAjaj.isFunction(this.sendImpl) )
    {
        throw new Error("This object has no sendImpl() member function! I don't know how to send the request!");
    }
    var ex = false;
    var av = Array.prototype.slice.apply( arguments, [0] );

    /**
        FIXME: how to handle the error, vis-a-vis- the callbacks, if
        normalizeAjaxParameters() throws? It can throw if
        (de)JSON-izing fails.
    */
    var norm = this.normalizeAjaxParameters( WhAjaj.isObject(opt) ? opt : {} );
    norm.url = WhAjaj.Connector.sendHelper.normalizeURL(norm);
    if( ! request ) norm.method = 'GET';
    var cb = this.callbacks || {};
    if( this.callbacks && WhAjaj.isFunction(this.callbacks.beforeSend) ) {

Changes to ajax/wiki-editor.html.

275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
            onResponse:function(resp,req){
                TheApp.onResponse(resp,req);
                if(resp.resultCode) return;
                delete p.isNew;
                p.timestamp = resp.payload.timestamp;
            }
        });
        
    };

    TheApp.createNewPage = function(){
        var name = prompt("New page name?");
        if(!name) return;
        var p = {
            name:name,







|







275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
            onResponse:function(resp,req){
                TheApp.onResponse(resp,req);
                if(resp.resultCode) return;
                delete p.isNew;
                p.timestamp = resp.payload.timestamp;
            }
        });

    };

    TheApp.createNewPage = function(){
        var name = prompt("New page name?");
        if(!name) return;
        var p = {
            name:name,

Changes to auto.def.

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
...
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
    # search for the system SQLite once with -ldl, and once without. If
    # the library can only be found with $extralibs set to -ldl, then
    # the code below will append -ldl to LIBS.
    #
    foreach extralibs {{} {-ldl}} {

      # Locate the system SQLite by searching for sqlite3_open(). Then check
      # if sqlite3_strlike() can be found as well. If we can find open() but
      # not strlike(), then the system SQLite is too old to link against
      # fossil.
      #
      if {[check-function-in-lib sqlite3_open sqlite3 $extralibs]} {
        if {![check-function-in-lib sqlite3_strlike sqlite3 $extralibs]} {
          user-error "system sqlite3 too old (require >= 3.10.0)"
        }

        # Success. Update symbols and return.
        #
        define USE_SYSTEM_SQLITE 1
        define-append LIBS -lsqlite3
        define-append LIBS $extralibs
................................................................................
}

# Helper for OpenSSL checking
proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto}}} {
    msg-checking "Checking for $msg..."
    set rc 0
    if {[is_mingw]} {
        lappend libs -lgdi32 -lwsock32
    }
    if {[info exists ::zlib_lib]} {
        lappend libs $::zlib_lib
    }
    msg-quiet cc-with [list -cflags $cflags -libs $libs] {
        if {[cc-check-includes openssl/ssl.h] && \
                [cc-check-functions SSL_new]} {
................................................................................
        } else {
            define-append LIBS -lssl -lcrypto
        }
        if {[info exists ::zlib_lib]} {
            define-append LIBS $::zlib_lib
        }
        if {[is_mingw]} {
            define-append LIBS -lgdi32 -lwsock32
        }
        msg-result "HTTPS support enabled"

        # Silence OpenSSL deprecation warnings on Mac OS X 10.7.
        if {[string match *-darwin* [get-define host]]} {
            if {[cctest -cflags {-Wdeprecated-declarations}]} {
                define-append EXTRA_CFLAGS -Wdeprecated-declarations







|
|



|
|







 







|







 







|







86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
...
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
    # search for the system SQLite once with -ldl, and once without. If
    # the library can only be found with $extralibs set to -ldl, then
    # the code below will append -ldl to LIBS.
    #
    foreach extralibs {{} {-ldl}} {

      # Locate the system SQLite by searching for sqlite3_open(). Then check
      # if sqlite3_trace_v2() can be found as well. If we can find open() but
      # not trace_v2(), then the system SQLite is too old to link against
      # fossil.
      #
      if {[check-function-in-lib sqlite3_open sqlite3 $extralibs]} {
        if {![check-function-in-lib sqlite3_trace_v2 sqlite3 $extralibs]} {
          user-error "system sqlite3 too old (require >= 3.14.0)"
        }

        # Success. Update symbols and return.
        #
        define USE_SYSTEM_SQLITE 1
        define-append LIBS -lsqlite3
        define-append LIBS $extralibs
................................................................................
}

# Helper for OpenSSL checking
proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto}}} {
    msg-checking "Checking for $msg..."
    set rc 0
    if {[is_mingw]} {
        lappend libs -lgdi32 -lwsock32 -lcrypt32
    }
    if {[info exists ::zlib_lib]} {
        lappend libs $::zlib_lib
    }
    msg-quiet cc-with [list -cflags $cflags -libs $libs] {
        if {[cc-check-includes openssl/ssl.h] && \
                [cc-check-functions SSL_new]} {
................................................................................
        } else {
            define-append LIBS -lssl -lcrypto
        }
        if {[info exists ::zlib_lib]} {
            define-append LIBS $::zlib_lib
        }
        if {[is_mingw]} {
            define-append LIBS -lgdi32 -lwsock32 -lcrypt32
        }
        msg-result "HTTPS support enabled"

        # Silence OpenSSL deprecation warnings on Mac OS X 10.7.
        if {[string match *-darwin* [get-define host]]} {
            if {[cctest -cflags {-Wdeprecated-declarations}]} {
                define-append EXTRA_CFLAGS -Wdeprecated-declarations

Changes to src/add.c.

383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
...
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
...
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
**
** The temporary table "fremove" is dropped after being processed.
*/
static void process_files_to_remove(
  int dryRunFlag /* Zero to actually operate on the file-system. */
){
  Stmt remove;
  if( db_table_exists(db_name("temp"), "fremove") ){
    db_prepare(&remove, "SELECT x FROM fremove ORDER BY x;");
    while( db_step(&remove)==SQLITE_ROW ){
      const char *zOldName = db_column_text(&remove, 0);
      if( !dryRunFlag ){
        file_delete(zOldName);
      }
      fossil_print("DELETED_FILE %s\n", zOldName);
................................................................................
#else
      caseSensitive = 1;  /* Unix */
#endif
      caseSensitive = db_get_boolean("case-sensitive",caseSensitive);
    }
    if( !caseSensitive && g.localOpen ){
      db_multi_exec(
         "CREATE INDEX IF NOT EXISTS %s.vfile_nocase"
         "  ON vfile(pathname COLLATE nocase)",
         db_name("localdb")
      );
    }
  }
  return caseSensitive;
}

/*
................................................................................
**
** The temporary table "fmove" is dropped after being processed.
*/
static void process_files_to_move(
  int dryRunFlag /* Zero to actually operate on the file-system. */
){
  Stmt move;
  if( db_table_exists(db_name("temp"), "fmove") ){
    db_prepare(&move, "SELECT x, y FROM fmove ORDER BY x;");
    while( db_step(&move)==SQLITE_ROW ){
      const char *zOldName = db_column_text(&move, 0);
      const char *zNewName = db_column_text(&move, 1);
      if( !dryRunFlag ){
        int isOldDir = file_isdir(zOldName);
        if( isOldDir==1 ){







|







 







|
|
<







 







|







383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
...
548
549
550
551
552
553
554
555
556

557
558
559
560
561
562
563
...
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
**
** The temporary table "fremove" is dropped after being processed.
*/
static void process_files_to_remove(
  int dryRunFlag /* Zero to actually operate on the file-system. */
){
  Stmt remove;
  if( db_table_exists("temp", "fremove") ){
    db_prepare(&remove, "SELECT x FROM fremove ORDER BY x;");
    while( db_step(&remove)==SQLITE_ROW ){
      const char *zOldName = db_column_text(&remove, 0);
      if( !dryRunFlag ){
        file_delete(zOldName);
      }
      fossil_print("DELETED_FILE %s\n", zOldName);
................................................................................
#else
      caseSensitive = 1;  /* Unix */
#endif
      caseSensitive = db_get_boolean("case-sensitive",caseSensitive);
    }
    if( !caseSensitive && g.localOpen ){
      db_multi_exec(
         "CREATE INDEX IF NOT EXISTS localdb.vfile_nocase"
         "  ON vfile(pathname COLLATE nocase)"

      );
    }
  }
  return caseSensitive;
}

/*
................................................................................
**
** The temporary table "fmove" is dropped after being processed.
*/
static void process_files_to_move(
  int dryRunFlag /* Zero to actually operate on the file-system. */
){
  Stmt move;
  if( db_table_exists("temp", "fmove") ){
    db_prepare(&move, "SELECT x, y FROM fmove ORDER BY x;");
    while( db_step(&move)==SQLITE_ROW ){
      const char *zOldName = db_column_text(&move, 0);
      const char *zNewName = db_column_text(&move, 1);
      if( !dryRunFlag ){
        int isOldDir = file_isdir(zOldName);
        if( isOldDir==1 ){

Changes to src/allrepo.c.

49
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64
...
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
...
269
270
271
272
273
274
275

276
277
278
279
280
281
282

/*
** Build a string that contains all of the command-line options
** specified as arguments.  If the option name begins with "+" then
** it takes an argument.  Without the "+" it does not.
*/
static void collect_argument(Blob *pExtra, const char *zArg, const char *zShort){
  if( find_option(zArg, zShort, 0)!=0 ){

    blob_appendf(pExtra, " --%s", zArg);
  }
}
static void collect_argument_value(Blob *pExtra, const char *zArg){
  const char *zValue = find_option(zArg, 0, 1);
  if( zValue ){
    if( zValue[0] ){
      blob_appendf(pExtra, " --%s %s", zArg, zValue);
................................................................................
**    unset       conjunction with the "max-loadavg" setting which cannot
**                otherwise be set globally.
**
** In addition, the following maintenance operations are supported:
**
**    add         Add all the repositories named to the set of repositories
**                tracked by Fossil.  Normally Fossil is able to keep up with
**                this list by itself, but sometime it can benefit from this
**                hint if you rename repositories.
**
**    ignore      Arguments are repositories that should be ignored by
**                subsequent clean, extras, list, pull, push, rebuild, and
**                sync operations.  The -c|--ckout option causes the listed
**                local checkouts to be ignored instead.
**
................................................................................
    collect_argv(&extra, 3);
  }else if( strncmp(zCmd, "fts-config", n)==0 ){
    zCmd = "fts-config -R";
    collect_argv(&extra, 3);
  }else if( strncmp(zCmd, "sync", n)==0 ){
    zCmd = "sync -autourl -R";
    collect_argument(&extra, "verbose","v");

  }else if( strncmp(zCmd, "test-integrity", n)==0 ){
    collect_argument(&extra, "parse", 0);
    zCmd = "test-integrity";
  }else if( strncmp(zCmd, "test-orphans", n)==0 ){
    zCmd = "test-orphans -R";
  }else if( strncmp(zCmd, "test-missing", n)==0 ){
    zCmd = "test-missing -q -R";







|
>
|







 







|







 







>







49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284

/*
** Build a string that contains all of the command-line options
** specified as arguments.  If the option name begins with "+" then
** it takes an argument.  Without the "+" it does not.
*/
static void collect_argument(Blob *pExtra, const char *zArg, const char *zShort){
  const char *z = find_option(zArg, zShort, 0);
  if( z!=0 ){
    blob_appendf(pExtra, " %s", z);
  }
}
static void collect_argument_value(Blob *pExtra, const char *zArg){
  const char *zValue = find_option(zArg, 0, 1);
  if( zValue ){
    if( zValue[0] ){
      blob_appendf(pExtra, " --%s %s", zArg, zValue);
................................................................................
**    unset       conjunction with the "max-loadavg" setting which cannot
**                otherwise be set globally.
**
** In addition, the following maintenance operations are supported:
**
**    add         Add all the repositories named to the set of repositories
**                tracked by Fossil.  Normally Fossil is able to keep up with
**                this list by itself, but sometimes it can benefit from this
**                hint if you rename repositories.
**
**    ignore      Arguments are repositories that should be ignored by
**                subsequent clean, extras, list, pull, push, rebuild, and
**                sync operations.  The -c|--ckout option causes the listed
**                local checkouts to be ignored instead.
**
................................................................................
    collect_argv(&extra, 3);
  }else if( strncmp(zCmd, "fts-config", n)==0 ){
    zCmd = "fts-config -R";
    collect_argv(&extra, 3);
  }else if( strncmp(zCmd, "sync", n)==0 ){
    zCmd = "sync -autourl -R";
    collect_argument(&extra, "verbose","v");
    collect_argument(&extra, "unversioned","u");
  }else if( strncmp(zCmd, "test-integrity", n)==0 ){
    collect_argument(&extra, "parse", 0);
    zCmd = "test-integrity";
  }else if( strncmp(zCmd, "test-orphans", n)==0 ){
    zCmd = "test-orphans -R";
  }else if( strncmp(zCmd, "test-missing", n)==0 ){
    zCmd = "test-missing -q -R";

Changes to src/attach.c.

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
...
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
...
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
...
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
...
697
698
699
700
701
702
703






704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
...
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
/*
** WEBPAGE: attachlist
** List attachments.
**
**    tkt=TICKETUUID
**    page=WIKIPAGE
**
** At most one of technote=, tkt= or page= are supplied. 
** If none is given, all attachments are listed.  If one is given,
** only attachments for the designated technote, ticket or wiki page
** are shown. TECHNOTEUUID and TICKETUUID may be just a prefix of the
** relevant technical note or ticket, in which case all attachments
** of all technical notes or tickets with the prefix will be listed.
*/
void attachlist_page(void){
................................................................................
    const char *zSrc = db_column_text(&q, 1);
    const char *zTarget = db_column_text(&q, 2);
    const char *zFilename = db_column_text(&q, 3);
    const char *zComment = db_column_text(&q, 4);
    const char *zUser = db_column_text(&q, 5);
    const char *zUuid = db_column_text(&q, 6);
    int attachid = db_column_int(&q, 7);
    // type 0 is a wiki page, 1 is a ticket, 2 is a tech note
    int type = db_column_int(&q, 8);
    const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
    int i;
    char *zUrlTail;
    for(i=0; zFilename[i]; i++){
      if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
        zFilename = &zFilename[i+1];
................................................................................
      zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
    }
    @ <li><p>
    @ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a>
    if( moderation_pending(attachid) ){
      @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
    }
    @ <br><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a>
    @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br />
    if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++;
    if( zComment && zComment[0] ){
      @ %!W(zComment)<br />
    }
    if( zPage==0 && zTkt==0 && zTechNote==0 ){
      if( zSrc==0 || zSrc[0]==0 ){
................................................................................
      zTechNote = db_text(0, "SELECT substr(tagname,7) FROM tag"
                             " WHERE tagname GLOB 'event-%q*'", zTechNote);
      if( zTechNote==0) fossil_redirect_home();
    }
    zTarget = zTechNote;
    zTargetType = mprintf("Tech Note <a href=\"%R/technote/%s\">%S</a>",
                           zTechNote, zTechNote);
  
  }else{
    if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){
      login_needed(g.anon.ApndTkt && g.anon.Attach);
      return;
    }
    if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTkt) ){
      zTkt = db_text(0, "SELECT substr(tagname,5) FROM tag"
................................................................................
    if( g.perm.Write && g.perm.WrWiki ){
      style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
    }
  }
  zDate = db_text(0, "SELECT datetime(%.12f)", pAttach->rDate);

  if( P("confirm")
   && ((zTktUuid && g.perm.WrTkt) || 
       (zWikiName && g.perm.WrWiki) ||
       (zTNUuid && g.perm.Write && g.perm.WrWiki))
  ){
    int i, n, rid;
    char *zDate;
    Blob manifest;
    Blob cksum;
................................................................................
    }else{
      @ <pre>
      @ %h(z)
      @ </pre>
    }
  }else if( strncmp(zMime, "image/", 6)==0 ){
    int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc);
    @ <i>(file is %d(sz) bytes of image data)</i><br>
    @ <img src="%R/raw/%s(zSrc)?m=%s(zMime)"></img>
    style_submenu_element("Image", "Image", "%R/raw/%s?m=%s", zSrc, zMime);
  }else{
    int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc);
    @ <i>(file is %d(sz) bytes of binary data)</i>
  }
  @ </blockquote>
................................................................................
**                                     is to be made. The attachment will be
**                                     to the most recently modified tech note
**                                     with the specified timestamp.
**         -t|--technote TECHNOTE-ID   Specifies the technote to be
**                                     updated by its technote id.
**
**       One of PAGENAME, DATETIME or TECHNOTE-ID must be specified.






*/
void attachment_cmd(void){
  int n;
  db_find_and_open_repository(0, 0);
  if( g.argc<3 ){
    goto attachment_cmd_usage;
  }
  n = strlen(g.argv[2]);
  if( n==0 ){
    goto attachment_cmd_usage;
  }

  if( strncmp(g.argv[2],"add",n)==0 ){
    const char *zPageName;        /* Name of the wiki page to attach to */
    const char *zFile;            /* Name of the file to be attached */
    const char *zETime;           /* The name of the technote to attach to */
    Manifest *pWiki = 0;          /* Parsed wiki page content */
    char *zBody = 0;              /* Wiki page content */
    int rid;
    const char *zTarget;          /* Target of the attachment */
    Blob content;                 /* The content of the attachment */
................................................................................
        usage("add PAGENAME FILENAME");
      }
      zPageName = g.argv[3];
      rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
        " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
        " ORDER BY x.mtime DESC LIMIT 1",
        zPageName
      );        
      if( (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){
        zBody = pWiki->zWiki;
      }
      if( zBody==0 ){
        fossil_fatal("wiki page [%s] not found",zPageName);
      }
      zTarget = zPageName;







|







 







|







 







|







 







|







 







|







 







|







 







>
>
>
>
>
>













|







 







|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
...
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
...
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
...
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
...
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
...
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
/*
** WEBPAGE: attachlist
** List attachments.
**
**    tkt=TICKETUUID
**    page=WIKIPAGE
**
** At most one of technote=, tkt= or page= are supplied.
** If none is given, all attachments are listed.  If one is given,
** only attachments for the designated technote, ticket or wiki page
** are shown. TECHNOTEUUID and TICKETUUID may be just a prefix of the
** relevant technical note or ticket, in which case all attachments
** of all technical notes or tickets with the prefix will be listed.
*/
void attachlist_page(void){
................................................................................
    const char *zSrc = db_column_text(&q, 1);
    const char *zTarget = db_column_text(&q, 2);
    const char *zFilename = db_column_text(&q, 3);
    const char *zComment = db_column_text(&q, 4);
    const char *zUser = db_column_text(&q, 5);
    const char *zUuid = db_column_text(&q, 6);
    int attachid = db_column_int(&q, 7);
    /* type 0 is a wiki page, 1 is a ticket, 2 is a tech note */
    int type = db_column_int(&q, 8);
    const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
    int i;
    char *zUrlTail;
    for(i=0; zFilename[i]; i++){
      if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
        zFilename = &zFilename[i+1];
................................................................................
      zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
    }
    @ <li><p>
    @ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a>
    if( moderation_pending(attachid) ){
      @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
    }
    @ <br /><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a>
    @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br />
    if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++;
    if( zComment && zComment[0] ){
      @ %!W(zComment)<br />
    }
    if( zPage==0 && zTkt==0 && zTechNote==0 ){
      if( zSrc==0 || zSrc[0]==0 ){
................................................................................
      zTechNote = db_text(0, "SELECT substr(tagname,7) FROM tag"
                             " WHERE tagname GLOB 'event-%q*'", zTechNote);
      if( zTechNote==0) fossil_redirect_home();
    }
    zTarget = zTechNote;
    zTargetType = mprintf("Tech Note <a href=\"%R/technote/%s\">%S</a>",
                           zTechNote, zTechNote);

  }else{
    if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){
      login_needed(g.anon.ApndTkt && g.anon.Attach);
      return;
    }
    if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTkt) ){
      zTkt = db_text(0, "SELECT substr(tagname,5) FROM tag"
................................................................................
    if( g.perm.Write && g.perm.WrWiki ){
      style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
    }
  }
  zDate = db_text(0, "SELECT datetime(%.12f)", pAttach->rDate);

  if( P("confirm")
   && ((zTktUuid && g.perm.WrTkt) ||
       (zWikiName && g.perm.WrWiki) ||
       (zTNUuid && g.perm.Write && g.perm.WrWiki))
  ){
    int i, n, rid;
    char *zDate;
    Blob manifest;
    Blob cksum;
................................................................................
    }else{
      @ <pre>
      @ %h(z)
      @ </pre>
    }
  }else if( strncmp(zMime, "image/", 6)==0 ){
    int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc);
    @ <i>(file is %d(sz) bytes of image data)</i><br />
    @ <img src="%R/raw/%s(zSrc)?m=%s(zMime)"></img>
    style_submenu_element("Image", "Image", "%R/raw/%s?m=%s", zSrc, zMime);
  }else{
    int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc);
    @ <i>(file is %d(sz) bytes of binary data)</i>
  }
  @ </blockquote>
................................................................................
**                                     is to be made. The attachment will be
**                                     to the most recently modified tech note
**                                     with the specified timestamp.
**         -t|--technote TECHNOTE-ID   Specifies the technote to be
**                                     updated by its technote id.
**
**       One of PAGENAME, DATETIME or TECHNOTE-ID must be specified.
**
** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
** year-month-day form, it may be truncated, the "T" may be replaced by
** a space, and it may also name a timezone offset from UTC as "-HH:MM"
** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z"
** means UTC.
*/
void attachment_cmd(void){
  int n;
  db_find_and_open_repository(0, 0);
  if( g.argc<3 ){
    goto attachment_cmd_usage;
  }
  n = strlen(g.argv[2]);
  if( n==0 ){
    goto attachment_cmd_usage;
  }

  if( strncmp(g.argv[2],"add",n)==0 ){
    const char *zPageName = 0;    /* Name of the wiki page to attach to */
    const char *zFile;            /* Name of the file to be attached */
    const char *zETime;           /* The name of the technote to attach to */
    Manifest *pWiki = 0;          /* Parsed wiki page content */
    char *zBody = 0;              /* Wiki page content */
    int rid;
    const char *zTarget;          /* Target of the attachment */
    Blob content;                 /* The content of the attachment */
................................................................................
        usage("add PAGENAME FILENAME");
      }
      zPageName = g.argv[3];
      rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
        " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
        " ORDER BY x.mtime DESC LIMIT 1",
        zPageName
      );
      if( (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){
        zBody = pWiki->zWiki;
      }
      if( zBody==0 ){
        fossil_fatal("wiki page [%s] not found",zPageName);
      }
      zTarget = zPageName;

Changes to src/blob.c.

651
652
653
654
655
656
657
658
659




660
661
662
663
664
665
666
667
668
669





















670
671
672
673
674
675
676
....
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
/*
** Return true if the blob contains a valid UUID_SIZE-digit base16 identifier.
*/
int blob_is_uuid(Blob *pBlob){
  return blob_size(pBlob)==UUID_SIZE
         && validate16(blob_buffer(pBlob), UUID_SIZE);
}
int blob_is_uuid_n(Blob *pBlob, int n){
  return blob_size(pBlob)==n && validate16(blob_buffer(pBlob), n);




}

/*
** Return true if the blob contains a valid 32-bit integer.  Store
** the integer value in *pValue.
*/
int blob_is_int(Blob *pBlob, int *pValue){
  const char *z = blob_buffer(pBlob);
  int i, n, c, v;
  n = blob_size(pBlob);





















  v = 0;
  for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){
    v = v*10 + c - '0';
  }
  if( i==n ){
    *pValue = v;
    return 1;
................................................................................

/*
** COMMAND: test-uncompress
**
** Usage: %fossil test-uncompress IN OUT
**
** Read the content of file IN, uncompress that content, and write the
** result into OUT.  This command is intended for testing of the the
** blob_compress() function.
*/
void uncompress_cmd(void){
  Blob f;
  if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
  blob_read_from_file(&f, g.argv[2]);
  blob_uncompress(&f, &f);







|
|
>
>
>
>










>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
....
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
/*
** Return true if the blob contains a valid UUID_SIZE-digit base16 identifier.
*/
int blob_is_uuid(Blob *pBlob){
  return blob_size(pBlob)==UUID_SIZE
         && validate16(blob_buffer(pBlob), UUID_SIZE);
}

/*
** Return true if the blob contains a valid filename
*/
int blob_is_filename(Blob *pBlob){
  return file_is_simple_pathname(blob_str(pBlob), 1);
}

/*
** Return true if the blob contains a valid 32-bit integer.  Store
** the integer value in *pValue.
*/
int blob_is_int(Blob *pBlob, int *pValue){
  const char *z = blob_buffer(pBlob);
  int i, n, c, v;
  n = blob_size(pBlob);
  v = 0;
  for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){
    v = v*10 + c - '0';
  }
  if( i==n ){
    *pValue = v;
    return 1;
  }else{
    return 0;
  }
}

/*
** Return true if the blob contains a valid 64-bit integer.  Store
** the integer value in *pValue.
*/
int blob_is_int64(Blob *pBlob, sqlite3_int64 *pValue){
  const char *z = blob_buffer(pBlob);
  int i, n, c;
  sqlite3_int64 v;
  n = blob_size(pBlob);
  v = 0;
  for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){
    v = v*10 + c - '0';
  }
  if( i==n ){
    *pValue = v;
    return 1;
................................................................................

/*
** COMMAND: test-uncompress
**
** Usage: %fossil test-uncompress IN OUT
**
** Read the content of file IN, uncompress that content, and write the
** result into OUT.  This command is intended for testing of the
** blob_compress() function.
*/
void uncompress_cmd(void){
  Blob f;
  if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
  blob_read_from_file(&f, g.argv[2]);
  blob_uncompress(&f, &f);

Changes to src/branch.c.

174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
...
255
256
257
258
259
260
261






262
263
264
265
266
267
268
  }


  /* Commit */
  db_end_transaction(0);

  /* Do an autosync push, if requested */
  if( !isPrivate ) autosync_loop(SYNC_PUSH, db_get_int("autosync-tries", 1));
}

#if INTERFACE
/*
** Allows bits in the mBplqFlags parameter to branch_prepare_list_query().
*/
#define BRL_CLOSED_ONLY      0x001 /* Show only closed branches */
#define BRL_OPEN_ONLY        0x002 /* Show only open branches */
#define BRL_BOTH             0x003 /* Show both open and closed branches */
#define BRL_OPEN_CLOSED_MASK 0x003
#define BRL_MTIME            0x004 /* Include lastest check-in time */
#dfeine BRL_ORDERBY_MTIME    0x008 /* Sort by MTIME. (otherwise sort by name)*/

#endif /* INTERFACE */

/*
** Prepare a query that will list branches.
**
** If (which<0) then the query pulls only closed branches. If
................................................................................
**        Create a new branch BRANCH-NAME off of check-in BASIS.
**        Supported options for this subcommand include:
**        --private             branch is private (i.e., remains local)
**        --bgcolor COLOR       use COLOR instead of automatic background
**        --nosign              do not sign contents on this branch
**        --date-override DATE  DATE to use instead of 'now'
**        --user-override USER  USER to use instead of the current default






**
**    %fossil branch list ?-a|--all|-c|--closed?
**    %fossil branch ls ?-a|--all|-c|--closed?
**
**        List all branches.  Use -a or --all to list all branches and
**        -c or --closed to list all closed branches.  The default is to
**        show only open branches.







|











|







 







>
>
>
>
>
>







174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
...
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
  }


  /* Commit */
  db_end_transaction(0);

  /* Do an autosync push, if requested */
  if( !isPrivate ) autosync_loop(SYNC_PUSH, db_get_int("autosync-tries",1),0);
}

#if INTERFACE
/*
** Allows bits in the mBplqFlags parameter to branch_prepare_list_query().
*/
#define BRL_CLOSED_ONLY      0x001 /* Show only closed branches */
#define BRL_OPEN_ONLY        0x002 /* Show only open branches */
#define BRL_BOTH             0x003 /* Show both open and closed branches */
#define BRL_OPEN_CLOSED_MASK 0x003
#define BRL_MTIME            0x004 /* Include lastest check-in time */
#define BRL_ORDERBY_MTIME    0x008 /* Sort by MTIME. (otherwise sort by name)*/

#endif /* INTERFACE */

/*
** Prepare a query that will list branches.
**
** If (which<0) then the query pulls only closed branches. If
................................................................................
**        Create a new branch BRANCH-NAME off of check-in BASIS.
**        Supported options for this subcommand include:
**        --private             branch is private (i.e., remains local)
**        --bgcolor COLOR       use COLOR instead of automatic background
**        --nosign              do not sign contents on this branch
**        --date-override DATE  DATE to use instead of 'now'
**        --user-override USER  USER to use instead of the current default
**
**        DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
**        year-month-day form, it may be truncated, the "T" may be
**        replaced by a space, and it may also name a timezone offset
**        from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward).
**        Either no timezone suffix or "Z" means UTC.
**
**    %fossil branch list ?-a|--all|-c|--closed?
**    %fossil branch ls ?-a|--all|-c|--closed?
**
**        List all branches.  Use -a or --all to list all branches and
**        -c or --closed to list all closed branches.  The default is to
**        show only open branches.

Changes to src/browse.c.

1019
1020
1021
1022
1023
1024
1025

1026
1027
1028
1029
1030
1031
1032
....
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
  const char *zUuid;
  const char *zNow;            /* Time of check-in */
  int showId = PB("showid");
  Stmt q1, q2;
  double baseTime;
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }

  zName = P("name");
  if( zName==0 ) zName = "tip";
  rid = symbolic_name_to_rid(zName, "ci");
  if( rid==0 ){
    fossil_fatal("not a valid check-in: %s", zName);
  }
  zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
................................................................................
    @ <td>
    db_bind_int(&q2, ":mid", mid);
    while( db_step(&q2)==SQLITE_ROW ){
      const char *zFUuid = db_column_text(&q2,0);
      const char *zFile = db_column_text(&q2,1);
      int fid = db_column_int(&q2,2);
      if( showId ){
        @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a> (%d(fid))<br>
      }else{
        @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br>
      }
    }
    db_reset(&q2);
    @ </td>
    @ <td>
    @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a>
    if( showId ){







>







 







|

|







1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
....
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
  const char *zUuid;
  const char *zNow;            /* Time of check-in */
  int showId = PB("showid");
  Stmt q1, q2;
  double baseTime;
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  if( exclude_spiders() ) return;
  zName = P("name");
  if( zName==0 ) zName = "tip";
  rid = symbolic_name_to_rid(zName, "ci");
  if( rid==0 ){
    fossil_fatal("not a valid check-in: %s", zName);
  }
  zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
................................................................................
    @ <td>
    db_bind_int(&q2, ":mid", mid);
    while( db_step(&q2)==SQLITE_ROW ){
      const char *zFUuid = db_column_text(&q2,0);
      const char *zFile = db_column_text(&q2,1);
      int fid = db_column_int(&q2,2);
      if( showId ){
        @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a> (%d(fid))<br />
      }else{
        @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br />
      }
    }
    db_reset(&q2);
    @ </td>
    @ <td>
    @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a>
    if( showId ){

Changes to src/bundle.c.

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
...
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
...
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
...
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
/*
** SQL code used to initialize the schema of a bundle.
**
** The bblob.delta field can be an integer, a text string, or NULL.
** If an integer, then the corresponding blobid is the delta basis.
** If a text string, then that string is a SHA1 hash for the delta
** basis, which is presumably in the master repository.  If NULL, then
** data contains contain without delta compression.
*/
static const char zBundleInit[] =
@ CREATE TABLE IF NOT EXISTS "%w".bconfig(
@   bcname TEXT,
@   bcvalue ANY
@ );
@ CREATE TABLE IF NOT EXISTS "%w".bblob(
................................................................................
  db_multi_exec(
    "INSERT INTO bconfig(bcname,bcvalue)"
    " VALUES('mtime',datetime('now'));"
  );
  db_multi_exec(
    "INSERT INTO bconfig(bcname,bcvalue)"
    " SELECT name, value FROM config"
    "  WHERE name IN ('project-code');"
  );

  /* Directly copy content from the repository into the bundle as long
  ** as the repository content is a delta from some other artifact that
  ** is also in the bundle.
  */
  db_multi_exec(
................................................................................
        deltaFrom = db_int(0,
           "SELECT max(fid) FROM mlink"
           " WHERE fnid=(SELECT fnid FROM mlink WHERE fid=%d)"
           "   AND fid<%d", rid, mnToBundle);
      }
    }

    /* Try to insert the insert the artifact as a delta
    */
    if( deltaFrom ){
      Blob basis, delta;
      content_get(deltaFrom, &basis);
      blob_delta_create(&basis, &content, &delta);
      if( blob_size(&delta)>0.9*blob_size(&content) ){
        deltaFrom = 0;
................................................................................
**      any check-ins that are descendants of check-ins already in the bundle,
**      and any tags that apply to artifacts in the bundle.
**
**   fossil bundle import BUNDLE ?--publish?
**
**      Import all content from BUNDLE into the repository.  By default, the
**      imported files are private and will not sync.  Use the --publish
**      option makes the import public.
**
**   fossil bundle ls BUNDLE
**
**      List the contents of BUNDLE on standard output
**
**   fossil bundle purge BUNDLE
**







|







 







|







 







|







 







|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
...
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
...
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
...
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
/*
** SQL code used to initialize the schema of a bundle.
**
** The bblob.delta field can be an integer, a text string, or NULL.
** If an integer, then the corresponding blobid is the delta basis.
** If a text string, then that string is a SHA1 hash for the delta
** basis, which is presumably in the master repository.  If NULL, then
** data contains content without delta compression.
*/
static const char zBundleInit[] =
@ CREATE TABLE IF NOT EXISTS "%w".bconfig(
@   bcname TEXT,
@   bcvalue ANY
@ );
@ CREATE TABLE IF NOT EXISTS "%w".bblob(
................................................................................
  db_multi_exec(
    "INSERT INTO bconfig(bcname,bcvalue)"
    " VALUES('mtime',datetime('now'));"
  );
  db_multi_exec(
    "INSERT INTO bconfig(bcname,bcvalue)"
    " SELECT name, value FROM config"
    "  WHERE name IN ('project-code','parent-project-code');"
  );

  /* Directly copy content from the repository into the bundle as long
  ** as the repository content is a delta from some other artifact that
  ** is also in the bundle.
  */
  db_multi_exec(
................................................................................
        deltaFrom = db_int(0,
           "SELECT max(fid) FROM mlink"
           " WHERE fnid=(SELECT fnid FROM mlink WHERE fid=%d)"
           "   AND fid<%d", rid, mnToBundle);
      }
    }

    /* Try to insert the artifact as a delta
    */
    if( deltaFrom ){
      Blob basis, delta;
      content_get(deltaFrom, &basis);
      blob_delta_create(&basis, &content, &delta);
      if( blob_size(&delta)>0.9*blob_size(&content) ){
        deltaFrom = 0;
................................................................................
**      any check-ins that are descendants of check-ins already in the bundle,
**      and any tags that apply to artifacts in the bundle.
**
**   fossil bundle import BUNDLE ?--publish?
**
**      Import all content from BUNDLE into the repository.  By default, the
**      imported files are private and will not sync.  Use the --publish
**      option to make the import public.
**
**   fossil bundle ls BUNDLE
**
**      List the contents of BUNDLE on standard output
**
**   fossil bundle purge BUNDLE
**

Changes to src/cache.c.

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
...
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
*/
void cache_initialize(void){
  sqlite3_close(cacheOpen(1));
}

/*
** COMMAND: cache*
** 
** Usage: %fossil cache SUBCOMMAND
**
** Manage the cache used for potentially expensive web pages such as
** /zip and /tarball.   SUBCOMMAND can be:
**
**    clear        Remove all entries from the cache.
**
................................................................................
         "  FROM cache"
         " ORDER BY tm DESC"
    );
    if( pStmt ){
      @ <ol>
      while( sqlite3_step(pStmt)==SQLITE_ROW ){
        const unsigned char *zName = sqlite3_column_text(pStmt,0);
        @ <li><p>%z(href("%R/cacheget?key=%T",zName))%h(zName)</a><br>
        @ size: %s(sqlite3_column_text(pStmt,1))
        @ hit-count: %d(sqlite3_column_int(pStmt,2))
        @ last-access: %s(sqlite3_column_text(pStmt,3))</p></li>
      }
      sqlite3_finalize(pStmt);
      @ </ol>
    }







|







 







|







235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
...
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
*/
void cache_initialize(void){
  sqlite3_close(cacheOpen(1));
}

/*
** COMMAND: cache*
**
** Usage: %fossil cache SUBCOMMAND
**
** Manage the cache used for potentially expensive web pages such as
** /zip and /tarball.   SUBCOMMAND can be:
**
**    clear        Remove all entries from the cache.
**
................................................................................
         "  FROM cache"
         " ORDER BY tm DESC"
    );
    if( pStmt ){
      @ <ol>
      while( sqlite3_step(pStmt)==SQLITE_ROW ){
        const unsigned char *zName = sqlite3_column_text(pStmt,0);
        @ <li><p>%z(href("%R/cacheget?key=%T",zName))%h(zName)</a><br />
        @ size: %s(sqlite3_column_text(pStmt,1))
        @ hit-count: %d(sqlite3_column_int(pStmt,2))
        @ last-access: %s(sqlite3_column_text(pStmt,3))</p></li>
      }
      sqlite3_finalize(pStmt);
      @ </ol>
    }

Changes to src/captcha.c.

576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
...
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
/*
** Check to see if the current request is coming from an agent that might
** be a spider.  If the agent is not a spider, then return 0 without doing
** anything.  But if the user agent appears to be a spider, offer
** a captcha challenge to allow the user agent to prove that it is human
** and return non-zero.
*/
int exclude_spiders(const char *zPage){
  const char *zCookieValue;
  char *zCookieName;
  if( g.isHuman ) return 0;
#if 0
  {
    const char *zReferer = P("HTTP_REFERER");
    if( zReferer && strncmp(g.zBaseURL, zReferer, strlen(g.zBaseURL))==0 ){
................................................................................
  if( captcha_is_correct() ){
    cgi_set_cookie(zCookieName, "1", login_cookie_path(), 8*3600);
    return 0;
  }

  /* This appears to be a spider.  Offer the captcha */
  style_header("Verification");
  form_begin(0, "%s", zPage);
  cgi_query_parameters_to_hidden();
  @ <p>Please demonstrate that you are human, not a spider or robot</p>
  captcha_generate(1);
  @ </form>
  style_footer();
  return 1;
}







|







 







|







576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
...
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
/*
** Check to see if the current request is coming from an agent that might
** be a spider.  If the agent is not a spider, then return 0 without doing
** anything.  But if the user agent appears to be a spider, offer
** a captcha challenge to allow the user agent to prove that it is human
** and return non-zero.
*/
int exclude_spiders(void){
  const char *zCookieValue;
  char *zCookieName;
  if( g.isHuman ) return 0;
#if 0
  {
    const char *zReferer = P("HTTP_REFERER");
    if( zReferer && strncmp(g.zBaseURL, zReferer, strlen(g.zBaseURL))==0 ){
................................................................................
  if( captcha_is_correct() ){
    cgi_set_cookie(zCookieName, "1", login_cookie_path(), 8*3600);
    return 0;
  }

  /* This appears to be a spider.  Offer the captcha */
  style_header("Verification");
  @ <form method='POST' action='%s(g.zPath)'>
  cgi_query_parameters_to_hidden();
  @ <p>Please demonstrate that you are human, not a spider or robot</p>
  captcha_generate(1);
  @ </form>
  style_footer();
  return 1;
}

Changes to src/checkin.c.

544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
...
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646

647






648


649



650
651
652
653
654
655
656

657
658
659
660
661
662
663
664
665
...
676
677
678
679
680
681
682
683

684
685
686
687
688
689
690
....
1458
1459
1460
1461
1462
1463
1464

1465
1466
1467
1468
1469
1470
1471
....
1550
1551
1552
1553
1554
1555
1556

1557
1558
1559





1560
1561
1562
1563
1564
1565
1566
....
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
....
1671
1672
1673
1674
1675
1676
1677



1678
1679
1680
1681
1682
1683
1684
1685






1686
1687
1688
1689
1690
1691
1692
....
1695
1696
1697
1698
1699
1700
1701

1702
1703
1704
1705
1706
1707
1708
....
1742
1743
1744
1745
1746
1747
1748

1749
1750
1751
1752
1753
1754
1755
....
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821

1822
1823





1824
1825
1826
1827
1828
1829
1830
....
1833
1834
1835
1836
1837
1838
1839

1840
1841




1842


1843
1844
1845
1846
1847
1848
1849
....
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954

1955


1956
1957
1958
1959
1960

1961
1962





1963
1964
1965
1966
1967
1968
1969
....
2009
2010
2011
2012
2013
2014
2015

2016
2017
2018
2019
2020
2021
2022
2023
....
2093
2094
2095
2096
2097
2098
2099

2100
2101





2102
2103
2104
2105
2106
2107
2108
....
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
      blob_reset(&name);
    }
  }
}

/*
** COMMAND: extras
** 
** Usage: %fossil extras ?OPTIONS? ?PATH1 ...?
**
** Print a list of all files in the source tree that are not part of
** the current checkout.  See also the "clean" command. If paths are
** specified, only files in the given directories will be listed.
**
** Files and subdirectories whose names begin with "." are normally
** ignored but can be included by adding the --dotfiles option.
**
** The GLOBPATTERN is a comma-separated list of GLOB expressions for
** files that are ignored.  The GLOBPATTERN specified by the "ignore-glob"
** is used if the --ignore option is omitted.
**
** Pathnames are displayed according to the "relative-paths" setting,
** unless overridden by the --abs-paths or --rel-paths options.
**
** Options:
**    --abs-paths      Display absolute pathnames.
**    --case-sensitive <BOOL> override case-sensitive setting
................................................................................
  }
  blob_reset(&rewrittenPathname);
  db_finalize(&q);
}

/*
** COMMAND: clean
** 
** Usage: %fossil clean ?OPTIONS? ?PATH ...?
**
** Delete all "extra" files in the source tree.  "Extra" files are
** files that are not officially part of the checkout. This operation
** cannot be undone. If one or more PATH arguments appear, then only
** the files named, or files contained with directories named, will be
** removed.
**
** Prompted are issued to confirm the removal of each file, unless

** the --force flag is used or unless the file matches glob pattern






** specified by the --clean option.  No file that matches glob patterns


** specified by --ignore or --keep will ever be deleted. The default



** values for --clean, --ignore, and --keep are determined by the
** (versionable) clean-glob, ignore-glob, and keep-glob settings.
** Files and subdirectories whose names begin with "." are automatically
** ignored unless the --dotfiles option is used.
**
** The --verily option ignores the keep-glob and ignore-glob settings
** and turns on --force, --dotfiles, and --emptydirs.  Use the --verily

** option when you really want to clean up everything.  Extreme care
** should be exercised when using the --verily option.
**
** Options:
**    --allckouts      Check for empty directories within any checkouts
**                     that may be nested within the current one.  This
**                     option should be used with great care because the
**                     empty-dirs setting (and other applicable settings)
**                     belonging to the other repositories, if any, will
................................................................................
**                     explicitly exempted via the empty-dirs setting
**                     or another applicable setting or command line
**                     argument.  Matching files, if any, are removed
**                     prior to checking for any empty directories;
**                     therefore, directories that contain only files
**                     that were removed will be removed as well.
**    -f|--force       Remove files without prompting.
**    -i|--prompt      Prompt before removing each file.

**    -x|--verily      WARNING: Removes everything that is not a managed
**                     file or the repository itself.  This option
**                     implies the --force, --emptydirs, --dotfiles, and
**                     --disable-undo options.  Furthermore, it completely
**                     disregards the keep-glob and ignore-glob settings.
**                     However, it does honor the --ignore and --keep
**                     options.
................................................................................
** and the original file will have been renamed to "<filename>-original".
*/
static int commit_warning(
  Blob *p,              /* The content of the file being committed. */
  int crnlOk,           /* Non-zero if CR/NL warnings should be disabled. */
  int binOk,            /* Non-zero if binary warnings should be disabled. */
  int encodingOk,       /* Non-zero if encoding warnings should be disabled. */

  const char *zFilename /* The full name of the file being committed. */
){
  int bReverse;           /* UTF-16 byte order is reversed? */
  int fUnicode;           /* return value of could_be_utf16() */
  int fBinary;            /* does the blob content appear to be binary? */
  int lookFlags;          /* output flags from looks_like_utf8/utf16() */
  int fHasAnyCr;          /* the blob contains one or more CR chars */
................................................................................
    }
    file_relative_name(zFilename, &fname, 0);
    zMsg = mprintf(
         "%s contains %s. Use --no-warnings or the %s to"
                 " disable this warning.\n"
         "Commit anyhow (a=all/%sy/N)? ",
         blob_str(&fname), zWarning, zDisable, zConvert);

    prompt_user(zMsg, &ans);
    fossil_free(zMsg);
    cReply = blob_str(&ans)[0];





    if( cReply=='a' || cReply=='A' ){
      allOk = 1;
    }else if( *zConvert && (cReply=='c' || cReply=='C') ){
      char *zOrig = file_newname(zFilename, "original", 1);
      FILE *f;
      blob_write_to_file(p, zOrig);
      fossil_free(zOrig);
................................................................................
        fclose(f);
      }
      return 1;
    }else if( cReply!='y' && cReply!='Y' ){
      fossil_fatal("Abandoning commit due to %s in %s",
                   zWarning, blob_str(&fname));
    }
    blob_reset(&ans);
    blob_reset(&fname);
  }
  return 0;
}

/*
** qsort() comparison routine for an array of pointers to strings.
................................................................................
**    --close                    close the branch being committed
**    --delta                    use a delta manifest in the commit process
**    --integrate                close all merged-in branches
**    -m|--comment COMMENT-TEXT  use COMMENT-TEXT as commit comment
**    -M|--message-file FILE     read the commit comment from given file
**    --mimetype MIMETYPE        mimetype of check-in comment
**    -n|--dry-run               If given, display instead of run actions



**    --no-warnings              omit all warnings about file contents
**    --nosign                   do not attempt to sign this commit with gpg
**    --private                  do not sync changes and their descendants
**    --sha1sum                  verify file status using SHA1 hashing rather
**                               than relying on file mtimes
**    --tag TAG-NAME             assign given tag TAG-NAME to the check-in
**    --date-override DATE       DATE to use instead of 'now'
**    --user-override USER       USER to use instead of the current default






**
** See also: branch, changes, checkout, extras, sync
*/
void commit_cmd(void){
  int hasChanges;        /* True if unsaved changes exist */
  int vid;               /* blob-id of parent version */
  int nrid;              /* blob-id of a modified file */
................................................................................
  const char *zComment;  /* Check-in comment */
  Stmt q;                /* Various queries */
  char *zUuid;           /* UUID of the new check-in */
  int useSha1sum = 0;    /* True to verify file status using SHA1 hashing */
  int noSign = 0;        /* True to omit signing the manifest using GPG */
  int isAMerge = 0;      /* True if checking in a merge */
  int noWarningFlag = 0; /* True if skipping all warnings */

  int forceFlag = 0;     /* Undocumented: Disables all checks */
  int forceDelta = 0;    /* Force a delta-manifest */
  int forceBaseline = 0; /* Force a baseline-manifest */
  int allowConflict = 0; /* Allow unresolve merge conflicts */
  int allowEmpty = 0;    /* Allow a commit with no changes */
  int allowFork = 0;     /* Allow the commit to fork */
  int allowOlder = 0;    /* Allow a commit older than its ancestor */
................................................................................
  }
  zComment = find_option("comment","m",1);
  forceFlag = find_option("force", "f", 0)!=0;
  allowConflict = find_option("allow-conflict",0,0)!=0;
  allowEmpty = find_option("allow-empty",0,0)!=0;
  allowFork = find_option("allow-fork",0,0)!=0;
  allowOlder = find_option("allow-older",0,0)!=0;

  noWarningFlag = find_option("no-warnings", 0, 0)!=0;
  sCiInfo.zBranch = find_option("branch","b",1);
  sCiInfo.zColor = find_option("bgcolor",0,1);
  sCiInfo.zBrClr = find_option("branchcolor",0,1);
  sCiInfo.closeFlag = find_option("close",0,0)!=0;
  sCiInfo.integrateFlag = find_option("integrate",0,0)!=0;
  sCiInfo.zMimetype = find_option("mimetype",0,1);
................................................................................
    g.markPrivate = 1;
  }

  /*
  ** Autosync if autosync is enabled and this is not a private check-in.
  */
  if( !g.markPrivate ){
    if( autosync_loop(SYNC_PULL, db_get_int("autosync-tries", 1)) ){
      prompt_user("continue in spite of sync failure (y/N)? ", &ans);
      cReply = blob_str(&ans)[0];
      if( cReply!='y' && cReply!='Y' ){
        fossil_exit(1);
      }
    }
  }

  /* Require confirmation to continue with the check-in if there is
  ** clock skew
  */
  if( g.clockSkewSeen ){

    prompt_user("continue in spite of time skew (y/N)? ", &ans);
    cReply = blob_str(&ans)[0];





    if( cReply!='y' && cReply!='Y' ){
      fossil_exit(1);
    }
  }

  /* There are two ways this command may be executed. If there are
  ** no arguments following the word "commit", then all modified files
................................................................................
  **
  ** After the following function call has returned, the Global.aCommitFile[]
  ** array is allocated to contain the "id" field from the vfile table
  ** for each file to be committed. Or, if aCommitFile is NULL, all files
  ** should be committed.
  */
  if( select_commit_files() ){

    prompt_user("continue (y/N)? ", &ans);
    cReply = blob_str(&ans)[0];




    if( cReply!='y' && cReply!='Y' ) fossil_exit(1);


  }
  isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0 OR id<-2");
  if( g.aCommitFile && isAMerge ){
    fossil_fatal("cannot do a partial commit of a merge");
  }

  /* Doing "fossil mv fileA fileB; fossil add fileA; fossil commit fileA"
................................................................................
  if( zComment ){
    blob_zero(&comment);
    blob_append(&comment, zComment, -1);
  }else if( zComFile ){
    blob_zero(&comment);
    blob_read_from_file(&comment, zComFile);
    blob_to_utf8_no_bom(&comment, 1);
  }else if(dryRunFlag){
    blob_zero(&comment);
  }else{
    char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'");
    prepare_commit_comment(&comment, zInit, &sCiInfo, vid);
    if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
      prompt_user("unchanged check-in comment.  continue (y/N)? ", &ans);
      cReply = blob_str(&ans)[0];

      if( cReply!='y' && cReply!='Y' ) fossil_exit(1);


    }
    free(zInit);
  }
  if( blob_size(&comment)==0 ){
    if( !dryRunFlag ){

      prompt_user("empty check-in comment.  continue (y/N)? ", &ans);
      cReply = blob_str(&ans)[0];





      if( cReply!='y' && cReply!='Y' ){
        fossil_exit(1);
      }
    }
  }else{
    db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
    db_end_transaction(0);
................................................................................
      blob_read_link(&content, zFullname);
    }else{
      blob_read_from_file(&content, zFullname);
    }
    /* Do not emit any warnings when they are disabled. */
    if( !noWarningFlag ){
      abortCommit |= commit_warning(&content, crnlOk, binOk,

                                    encodingOk, zFullname);
    }
    if( contains_merge_marker(&content) ){
      Blob fname; /* Relative pathname of the file */

      nConflict++;
      file_relative_name(zFullname, &fname, 0);
      fossil_print("possible unresolved merge conflict in %s\n",
................................................................................
        blob_reset(&delta);
      }
    }else if( forceDelta ){
      fossil_fatal("unable to find a baseline-manifest for the delta");
    }
  }
  if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){

    prompt_user("unable to sign manifest.  continue (y/N)? ", &ans);
    cReply = blob_str(&ans)[0];





    if( cReply!='y' && cReply!='Y' ){
      fossil_exit(1);
    }
  }

  /* If the -n|--dry-run option is specified, output the manifest file
  ** and rollback the transaction.
................................................................................
  }

  /* Clear the undo/redo stack */
  undo_reset();

  /* Commit */
  db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
  db_multi_exec("PRAGMA %s.application_id=252006673;", db_name("repository"));
  db_multi_exec("PRAGMA %s.application_id=252006674;", db_name("localdb"));
  if( dryRunFlag ){
    db_end_transaction(1);
    exit(1);
  }
  db_end_transaction(0);

  if( !g.markPrivate ){
    autosync_loop(SYNC_PUSH|SYNC_PULL, db_get_int("autosync-tries", 1));
  }
  if( count_nonbranch_children(vid)>1 ){
    fossil_print("**** warning: a fork has occurred *****\n");
  }
}







|


|
|
|




|
|
|







 







|


|
|
|
|
<

|
>
|
>
>
>
>
>
>
|
>
>
|
>
>
>
|
|
<
<

|
<
>
|
|







 







|
>







 







>







 







>
|
<
|
>
>
>
>
>







 







<







 







>
>
>






|

>
>
>
>
>
>







 







>







 







>







 







|
<
<
<
|
<







>
|
|
>
>
>
>
>







 







>
|
|
>
>
>
>
|
>
>







 







|

|





>
|
>
>





>
|
|
>
>
>
>
>







 







>
|







 







>
|
|
>
>
>
>
>







 







|
|







|





544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
...
630
631
632
633
634
635
636
637
638
639
640
641
642
643

644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662


663
664

665
666
667
668
669
670
671
672
673
674
...
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
....
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
....
1561
1562
1563
1564
1565
1566
1567
1568
1569

1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
....
1599
1600
1601
1602
1603
1604
1605

1606
1607
1608
1609
1610
1611
1612
....
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
....
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
....
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
....
1828
1829
1830
1831
1832
1833
1834
1835



1836

1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
....
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
....
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
....
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
....
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
....
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
      blob_reset(&name);
    }
  }
}

/*
** COMMAND: extras
**
** Usage: %fossil extras ?OPTIONS? ?PATH1 ...?
**
** Print a list of all files in the source tree that are not part of the
** current checkout. See also the "clean" command. If paths are specified,
** only files in the given directories will be listed.
**
** Files and subdirectories whose names begin with "." are normally
** ignored but can be included by adding the --dotfiles option.
**
** Files whose names match any of the glob patterns in the "ignore-glob"
** setting are ignored. This setting can be overridden by the --ignore
** option, whose CSG argument is a comma-separated list of glob patterns.
**
** Pathnames are displayed according to the "relative-paths" setting,
** unless overridden by the --abs-paths or --rel-paths options.
**
** Options:
**    --abs-paths      Display absolute pathnames.
**    --case-sensitive <BOOL> override case-sensitive setting
................................................................................
  }
  blob_reset(&rewrittenPathname);
  db_finalize(&q);
}

/*
** COMMAND: clean
**
** Usage: %fossil clean ?OPTIONS? ?PATH ...?
**
** Delete all "extra" files in the source tree.  "Extra" files are files
** that are not officially part of the checkout.  If one or more PATH
** arguments appear, then only the files named, or files contained with
** directories named, will be removed.

**
** If the --prompt option is used, prompts are issued to confirm the
** permanent removal of each file.  Otherwise, files are backed up to the
** undo buffer prior to removal, and prompts are issued only for files
** whose removal cannot be undone due to their large size or due to
** --disable-undo being used.
**
** The --force option treats all prompts as having been answered yes,
** whereas --no-prompt treats them as having been answered no.
**
** Files matching any glob pattern specified by the --clean option are
** deleted without prompting, and the removal cannot be undone.
**
** No file that matches glob patterns specified by --ignore or --keep will
** ever be deleted.  Files and subdirectories whose names begin with "."
** are automatically ignored unless the --dotfiles option is used.
**
** The default values for --clean, --ignore, and --keep are determined by
** the (versionable) clean-glob, ignore-glob, and keep-glob settings.


**
** The --verily option ignores the keep-glob and ignore-glob settings and

** turns on --force, --emptydirs, --dotfiles, and --disable-undo.  Use the
** --verily option when you really want to clean up everything.  Extreme
** care should be exercised when using the --verily option.
**
** Options:
**    --allckouts      Check for empty directories within any checkouts
**                     that may be nested within the current one.  This
**                     option should be used with great care because the
**                     empty-dirs setting (and other applicable settings)
**                     belonging to the other repositories, if any, will
................................................................................
**                     explicitly exempted via the empty-dirs setting
**                     or another applicable setting or command line
**                     argument.  Matching files, if any, are removed
**                     prior to checking for any empty directories;
**                     therefore, directories that contain only files
**                     that were removed will be removed as well.
**    -f|--force       Remove files without prompting.
**    -i|--prompt      Prompt before removing each file.  This option
**                     implies the --disable-undo option.
**    -x|--verily      WARNING: Removes everything that is not a managed
**                     file or the repository itself.  This option
**                     implies the --force, --emptydirs, --dotfiles, and
**                     --disable-undo options.  Furthermore, it completely
**                     disregards the keep-glob and ignore-glob settings.
**                     However, it does honor the --ignore and --keep
**                     options.
................................................................................
** and the original file will have been renamed to "<filename>-original".
*/
static int commit_warning(
  Blob *p,              /* The content of the file being committed. */
  int crnlOk,           /* Non-zero if CR/NL warnings should be disabled. */
  int binOk,            /* Non-zero if binary warnings should be disabled. */
  int encodingOk,       /* Non-zero if encoding warnings should be disabled. */
  int noPrompt,         /* Non-zero to disable prompts and assume 'No'. */
  const char *zFilename /* The full name of the file being committed. */
){
  int bReverse;           /* UTF-16 byte order is reversed? */
  int fUnicode;           /* return value of could_be_utf16() */
  int fBinary;            /* does the blob content appear to be binary? */
  int lookFlags;          /* output flags from looks_like_utf8/utf16() */
  int fHasAnyCr;          /* the blob contains one or more CR chars */
................................................................................
    }
    file_relative_name(zFilename, &fname, 0);
    zMsg = mprintf(
         "%s contains %s. Use --no-warnings or the %s to"
                 " disable this warning.\n"
         "Commit anyhow (a=all/%sy/N)? ",
         blob_str(&fname), zWarning, zDisable, zConvert);
    if( !noPrompt ){
      prompt_user(zMsg, &ans);

      cReply = blob_str(&ans)[0];
      blob_reset(&ans);
    }else{
      cReply = 'N';
    }
    fossil_free(zMsg);
    if( cReply=='a' || cReply=='A' ){
      allOk = 1;
    }else if( *zConvert && (cReply=='c' || cReply=='C') ){
      char *zOrig = file_newname(zFilename, "original", 1);
      FILE *f;
      blob_write_to_file(p, zOrig);
      fossil_free(zOrig);
................................................................................
        fclose(f);
      }
      return 1;
    }else if( cReply!='y' && cReply!='Y' ){
      fossil_fatal("Abandoning commit due to %s in %s",
                   zWarning, blob_str(&fname));
    }

    blob_reset(&fname);
  }
  return 0;
}

/*
** qsort() comparison routine for an array of pointers to strings.
................................................................................
**    --close                    close the branch being committed
**    --delta                    use a delta manifest in the commit process
**    --integrate                close all merged-in branches
**    -m|--comment COMMENT-TEXT  use COMMENT-TEXT as commit comment
**    -M|--message-file FILE     read the commit comment from given file
**    --mimetype MIMETYPE        mimetype of check-in comment
**    -n|--dry-run               If given, display instead of run actions
**    --no-prompt                This option disables prompting the user for
**                               input and assumes an answer of 'No' for every
**                               question.
**    --no-warnings              omit all warnings about file contents
**    --nosign                   do not attempt to sign this commit with gpg
**    --private                  do not sync changes and their descendants
**    --sha1sum                  verify file status using SHA1 hashing rather
**                               than relying on file mtimes
**    --tag TAG-NAME             assign given tag TAG-NAME to the check-in
**    --date-override DATETIME   DATE to use instead of 'now'
**    --user-override USER       USER to use instead of the current default
**
** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
** year-month-day form, it may be truncated, the "T" may be replaced by
** a space, and it may also name a timezone offset from UTC as "-HH:MM"
** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z"
** means UTC.
**
** See also: branch, changes, checkout, extras, sync
*/
void commit_cmd(void){
  int hasChanges;        /* True if unsaved changes exist */
  int vid;               /* blob-id of parent version */
  int nrid;              /* blob-id of a modified file */
................................................................................
  const char *zComment;  /* Check-in comment */
  Stmt q;                /* Various queries */
  char *zUuid;           /* UUID of the new check-in */
  int useSha1sum = 0;    /* True to verify file status using SHA1 hashing */
  int noSign = 0;        /* True to omit signing the manifest using GPG */
  int isAMerge = 0;      /* True if checking in a merge */
  int noWarningFlag = 0; /* True if skipping all warnings */
  int noPrompt = 0;      /* True if skipping all prompts */
  int forceFlag = 0;     /* Undocumented: Disables all checks */
  int forceDelta = 0;    /* Force a delta-manifest */
  int forceBaseline = 0; /* Force a baseline-manifest */
  int allowConflict = 0; /* Allow unresolve merge conflicts */
  int allowEmpty = 0;    /* Allow a commit with no changes */
  int allowFork = 0;     /* Allow the commit to fork */
  int allowOlder = 0;    /* Allow a commit older than its ancestor */
................................................................................
  }
  zComment = find_option("comment","m",1);
  forceFlag = find_option("force", "f", 0)!=0;
  allowConflict = find_option("allow-conflict",0,0)!=0;
  allowEmpty = find_option("allow-empty",0,0)!=0;
  allowFork = find_option("allow-fork",0,0)!=0;
  allowOlder = find_option("allow-older",0,0)!=0;
  noPrompt = find_option("no-prompt", 0, 0)!=0;
  noWarningFlag = find_option("no-warnings", 0, 0)!=0;
  sCiInfo.zBranch = find_option("branch","b",1);
  sCiInfo.zColor = find_option("bgcolor",0,1);
  sCiInfo.zBrClr = find_option("branchcolor",0,1);
  sCiInfo.closeFlag = find_option("close",0,0)!=0;
  sCiInfo.integrateFlag = find_option("integrate",0,0)!=0;
  sCiInfo.zMimetype = find_option("mimetype",0,1);
................................................................................
    g.markPrivate = 1;
  }

  /*
  ** Autosync if autosync is enabled and this is not a private check-in.
  */
  if( !g.markPrivate ){
    if( autosync_loop(SYNC_PULL, db_get_int("autosync-tries", 1), 1) ){



      fossil_exit(1);

    }
  }

  /* Require confirmation to continue with the check-in if there is
  ** clock skew
  */
  if( g.clockSkewSeen ){
    if( !noPrompt ){
      prompt_user("continue in spite of time skew (y/N)? ", &ans);
      cReply = blob_str(&ans)[0];
      blob_reset(&ans);
    }else{
      fossil_print("Abandoning commit due to time skew\n");
      cReply = 'N';
    }
    if( cReply!='y' && cReply!='Y' ){
      fossil_exit(1);
    }
  }

  /* There are two ways this command may be executed. If there are
  ** no arguments following the word "commit", then all modified files
................................................................................
  **
  ** After the following function call has returned, the Global.aCommitFile[]
  ** array is allocated to contain the "id" field from the vfile table
  ** for each file to be committed. Or, if aCommitFile is NULL, all files
  ** should be committed.
  */
  if( select_commit_files() ){
    if( !noPrompt ){
      prompt_user("continue (y/N)? ", &ans);
      cReply = blob_str(&ans)[0];
      blob_reset(&ans);
    }else{
      cReply = 'N';
    }
    if( cReply!='y' && cReply!='Y' ){
      fossil_exit(1);
    }
  }
  isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0 OR id<-2");
  if( g.aCommitFile && isAMerge ){
    fossil_fatal("cannot do a partial commit of a merge");
  }

  /* Doing "fossil mv fileA fileB; fossil add fileA; fossil commit fileA"
................................................................................
  if( zComment ){
    blob_zero(&comment);
    blob_append(&comment, zComment, -1);
  }else if( zComFile ){
    blob_zero(&comment);
    blob_read_from_file(&comment, zComFile);
    blob_to_utf8_no_bom(&comment, 1);
  }else if( dryRunFlag ){
    blob_zero(&comment);
  }else if( !noPrompt ){
    char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'");
    prepare_commit_comment(&comment, zInit, &sCiInfo, vid);
    if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
      prompt_user("unchanged check-in comment.  continue (y/N)? ", &ans);
      cReply = blob_str(&ans)[0];
      blob_reset(&ans);
      if( cReply!='y' && cReply!='Y' ){
        fossil_exit(1);
      }
    }
    free(zInit);
  }
  if( blob_size(&comment)==0 ){
    if( !dryRunFlag ){
      if( !noPrompt ){
        prompt_user("empty check-in comment.  continue (y/N)? ", &ans);
        cReply = blob_str(&ans)[0];
        blob_reset(&ans);
      }else{
        fossil_print("Abandoning commit due to empty check-in comment\n");
        cReply = 'N';
      }
      if( cReply!='y' && cReply!='Y' ){
        fossil_exit(1);
      }
    }
  }else{
    db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
    db_end_transaction(0);
................................................................................
      blob_read_link(&content, zFullname);
    }else{
      blob_read_from_file(&content, zFullname);
    }
    /* Do not emit any warnings when they are disabled. */
    if( !noWarningFlag ){
      abortCommit |= commit_warning(&content, crnlOk, binOk,
                                    encodingOk, noPrompt,
                                    zFullname);
    }
    if( contains_merge_marker(&content) ){
      Blob fname; /* Relative pathname of the file */

      nConflict++;
      file_relative_name(zFullname, &fname, 0);
      fossil_print("possible unresolved merge conflict in %s\n",
................................................................................
        blob_reset(&delta);
      }
    }else if( forceDelta ){
      fossil_fatal("unable to find a baseline-manifest for the delta");
    }
  }
  if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
    if( !noPrompt ){
      prompt_user("unable to sign manifest.  continue (y/N)? ", &ans);
      cReply = blob_str(&ans)[0];
      blob_reset(&ans);
    }else{
      fossil_print("Abandoning commit due to manifest signing failure\n");
      cReply = 'N';
    }
    if( cReply!='y' && cReply!='Y' ){
      fossil_exit(1);
    }
  }

  /* If the -n|--dry-run option is specified, output the manifest file
  ** and rollback the transaction.
................................................................................
  }

  /* Clear the undo/redo stack */
  undo_reset();

  /* Commit */
  db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
  db_multi_exec("PRAGMA repository.application_id=252006673;");
  db_multi_exec("PRAGMA localdb.application_id=252006674;");
  if( dryRunFlag ){
    db_end_transaction(1);
    exit(1);
  }
  db_end_transaction(0);

  if( !g.markPrivate ){
    autosync_loop(SYNC_PUSH|SYNC_PULL, db_get_int("autosync-tries", 1), 0);
  }
  if( count_nonbranch_children(vid)>1 ){
    fossil_print("**** warning: a fork has occurred *****\n");
  }
}

Changes to src/checkout.c.

304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  verify_all_options();

  if( !forceFlag && unsaved_changes(0) ){
    fossil_fatal("there are unsaved changes in the current checkout");
  }
  if( !forceFlag
   && db_table_exists("localdb","stash")
   && db_exists("SELECT 1 FROM %s.stash", db_name("localdb"))
  ){
    fossil_fatal("closing the checkout will delete your stash");
  }
  if( db_is_writeable("repository") ){
    char *zUnset = mprintf("ckout:%q", g.zLocalRoot);
    db_unset(zUnset, 1);
    fossil_free(zUnset);
  }
  unlink_local_database(1);
  db_close(1);
  unlink_local_database(0);
}







|












304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  verify_all_options();

  if( !forceFlag && unsaved_changes(0) ){
    fossil_fatal("there are unsaved changes in the current checkout");
  }
  if( !forceFlag
   && db_table_exists("localdb","stash")
   && db_exists("SELECT 1 FROM localdb.stash")
  ){
    fossil_fatal("closing the checkout will delete your stash");
  }
  if( db_is_writeable("repository") ){
    char *zUnset = mprintf("ckout:%q", g.zLocalRoot);
    db_unset(zUnset, 1);
    fossil_free(zUnset);
  }
  unlink_local_database(1);
  db_close(1);
  unlink_local_database(0);
}

Changes to src/clone.c.

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
...
112
113
114
115
116
117
118

119
120
121
122
123
124
125
126
...
127
128
129
130
131
132
133
134

135
136
137
138
139
140
141
**
**   Filesystem:
**     [file://]path/to/repo.fossil
**
** Note 1: For ssh and filesystem, path must have an extra leading
**         '/' to use an absolute path.
**
** Note 2: Use %HH escapes for special characters in the userid and 
**         password.  For example "%40" in place of "@", "%2f" in place
**         of "/", and "%3a" in place of ":".
**
** By default, your current login name is used to create the default
** admin user. This can be overridden using the -A|--admin-user
** parameter.
**
................................................................................
** Options:
**    --admin-user|-A USERNAME   Make USERNAME the administrator
**    --once                     Don't remember the URI.
**    --private                  Also clone private branches
**    --ssl-identity FILENAME    Use the SSL identity if requested by the server
**    --ssh-command|-c SSH       Use SSH as the "ssh" command
**    --httpauth|-B USER:PASS    Add HTTP Basic Authorization to requests

**    --verbose                  Show more statistics in output
**
** See also: init
*/
void clone_cmd(void){
  char *zPassword;
  const char *zDefaultUser;   /* Optional name of the default user */
  const char *zHttpAuth;      /* HTTP Authorization user:pass information */
................................................................................
  int nErr = 0;
  int urlFlags = URL_PROMPT_PW | URL_REMEMBER;
  int syncFlags = SYNC_CLONE;

  /* Also clone private branches */
  if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE;
  if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER;
  if( find_option("verbose",0,0)!=0) syncFlags |= SYNC_VERBOSE;

  zHttpAuth = find_option("httpauth","B",1);
  zDefaultUser = find_option("admin-user","A",1);
  clone_ssh_find_options();
  url_proxy_options();

  /* We should be done with options.. */
  verify_all_options();







|







 







>
|







 







|
>







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
**
**   Filesystem:
**     [file://]path/to/repo.fossil
**
** Note 1: For ssh and filesystem, path must have an extra leading
**         '/' to use an absolute path.
**
** Note 2: Use %HH escapes for special characters in the userid and
**         password.  For example "%40" in place of "@", "%2f" in place
**         of "/", and "%3a" in place of ":".
**
** By default, your current login name is used to create the default
** admin user. This can be overridden using the -A|--admin-user
** parameter.
**
................................................................................
** Options:
**    --admin-user|-A USERNAME   Make USERNAME the administrator
**    --once                     Don't remember the URI.
**    --private                  Also clone private branches
**    --ssl-identity FILENAME    Use the SSL identity if requested by the server
**    --ssh-command|-c SSH       Use SSH as the "ssh" command
**    --httpauth|-B USER:PASS    Add HTTP Basic Authorization to requests
**    -u|--unversioned           Also sync unversioned content
**    -v|--verbose               Show more statistics in output
**
** See also: init
*/
void clone_cmd(void){
  char *zPassword;
  const char *zDefaultUser;   /* Optional name of the default user */
  const char *zHttpAuth;      /* HTTP Authorization user:pass information */
................................................................................
  int nErr = 0;
  int urlFlags = URL_PROMPT_PW | URL_REMEMBER;
  int syncFlags = SYNC_CLONE;

  /* Also clone private branches */
  if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE;
  if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER;
  if( find_option("verbose","v",0)!=0) syncFlags |= SYNC_VERBOSE;
  if( find_option("unversioned","u",0)!=0 ) syncFlags |= SYNC_UNVERSIONED;
  zHttpAuth = find_option("httpauth","B",1);
  zDefaultUser = find_option("admin-user","A",1);
  clone_ssh_find_options();
  url_proxy_options();

  /* We should be done with options.. */
  verify_all_options();

Changes to src/codecheck1.c.

247
248
249
250
251
252
253
254
255
256
257
258
259
260
261

/*
** A list of functions that return strings that are safe to insert into
** SQL using %s.
*/
static const char *azSafeFunc[] = {
  "filename_collation",
  "db_name",
  "leaf_is_closed_sql",
  "timeline_query_for_www",
  "timeline_query_for_tty",
  "blob_sql_text",
  "glob_expr",
  "fossil_all_reserved_names",
  "configure_inop_rhs",







<







247
248
249
250
251
252
253

254
255
256
257
258
259
260

/*
** A list of functions that return strings that are safe to insert into
** SQL using %s.
*/
static const char *azSafeFunc[] = {
  "filename_collation",

  "leaf_is_closed_sql",
  "timeline_query_for_www",
  "timeline_query_for_tty",
  "blob_sql_text",
  "glob_expr",
  "fossil_all_reserved_names",
  "configure_inop_rhs",

Changes to src/config.h.

97
98
99
100
101
102
103











104
105
106
107
108
109
110
...
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154


155
156
157
158
159
160
161
#    else
#      if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION)
#        define COMPILER_NAME "pellesc32-" COMPILER_VERSION
#      else
#        define COMPILER_NAME "pellesc32"
#      endif
#    endif











#  elif defined(_MSC_VER)
#    if !defined(COMPILER_VERSION)
#      define COMPILER_VERSION COMPILER_STRINGIFY(_MSC_VER)
#    endif
#    if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION)
#      define COMPILER_NAME "msc-" COMPILER_VERSION
#    else
................................................................................
#      endif
#    endif
#    if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION)
#      define COMPILER_NAME "mingw32-" COMPILER_VERSION
#    else
#      define COMPILER_NAME "mingw32"
#    endif
#  elif defined(_WIN32)
#    define COMPILER_NAME "win32"
#  elif defined(__GNUC__)
#    if !defined(COMPILER_VERSION)
#      if defined(__VERSION__)
#        define COMPILER_VERSION __VERSION__
#      endif
#    endif
#    if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION)
#      define COMPILER_NAME "gcc-" COMPILER_VERSION
#    else
#      define COMPILER_NAME "gcc"
#    endif


#  else
#    define COMPILER_NAME "unknown"
#  endif
#endif

#if !defined(_RC_COMPILE_) && !defined(SQLITE_AMALGAMATION)








>
>
>
>
>
>
>
>
>
>
>







 







<
<











>
>







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
...
146
147
148
149
150
151
152


153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#    else
#      if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION)
#        define COMPILER_NAME "pellesc32-" COMPILER_VERSION
#      else
#        define COMPILER_NAME "pellesc32"
#      endif
#    endif
#  elif defined(__clang__)
#    if !defined(COMPILER_VERSION)
#      if defined(__clang_version__)
#        define COMPILER_VERSION __clang_version__
#      endif
#    endif
#    if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION)
#      define COMPILER_NAME "clang-" COMPILER_VERSION
#    else
#      define COMPILER_NAME "clang"
#    endif
#  elif defined(_MSC_VER)
#    if !defined(COMPILER_VERSION)
#      define COMPILER_VERSION COMPILER_STRINGIFY(_MSC_VER)
#    endif
#    if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION)
#      define COMPILER_NAME "msc-" COMPILER_VERSION
#    else
................................................................................
#      endif
#    endif
#    if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION)
#      define COMPILER_NAME "mingw32-" COMPILER_VERSION
#    else
#      define COMPILER_NAME "mingw32"
#    endif


#  elif defined(__GNUC__)
#    if !defined(COMPILER_VERSION)
#      if defined(__VERSION__)
#        define COMPILER_VERSION __VERSION__
#      endif
#    endif
#    if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION)
#      define COMPILER_NAME "gcc-" COMPILER_VERSION
#    else
#      define COMPILER_NAME "gcc"
#    endif
#  elif defined(_WIN32)
#    define COMPILER_NAME "win32"
#  else
#    define COMPILER_NAME "unknown"
#  endif
#endif

#if !defined(_RC_COMPILE_) && !defined(SQLITE_AMALGAMATION)

Changes to src/configure.c.

124
125
126
127
128
129
130


131
132
133
134
135
136
137
  { "ignore-glob",            CONFIGSET_PROJ },
  { "keep-glob",              CONFIGSET_PROJ },
  { "crnl-glob",              CONFIGSET_PROJ },
  { "encoding-glob",          CONFIGSET_PROJ },
  { "empty-dirs",             CONFIGSET_PROJ },
  { "allow-symlinks",         CONFIGSET_PROJ },
  { "dotfiles",               CONFIGSET_PROJ },



#ifdef FOSSIL_ENABLE_LEGACY_MV_RM
  { "mv-rm-files",            CONFIGSET_PROJ },
#endif

  { "ticket-table",           CONFIGSET_TKT  },
  { "ticket-common",          CONFIGSET_TKT  },







>
>







124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
  { "ignore-glob",            CONFIGSET_PROJ },
  { "keep-glob",              CONFIGSET_PROJ },
  { "crnl-glob",              CONFIGSET_PROJ },
  { "encoding-glob",          CONFIGSET_PROJ },
  { "empty-dirs",             CONFIGSET_PROJ },
  { "allow-symlinks",         CONFIGSET_PROJ },
  { "dotfiles",               CONFIGSET_PROJ },
  { "parent-project-code",    CONFIGSET_PROJ },
  { "parent-project-name",    CONFIGSET_PROJ },

#ifdef FOSSIL_ENABLE_LEGACY_MV_RM
  { "mv-rm-files",            CONFIGSET_PROJ },
#endif

  { "ticket-table",           CONFIGSET_TKT  },
  { "ticket-common",          CONFIGSET_TKT  },

Changes to src/content.c.

450
451
452
453
454
455
456




















457
458
459
460
461
462
463
...
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
...
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
....
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180

/*
** Turn dephantomization processing on or off.
*/
void content_enable_dephantomize(int onoff){
  ignoreDephantomizations = !onoff;
}





















/*
** Write content into the database.  Return the record ID.  If the
** content is already in the database, just return the record ID.
**
** If srcId is specified, then pBlob is delta content from
** the srcId record.  srcId might be a phantom.
................................................................................
  }else{
    rid = 0;  /* No entry with the same UUID currently exists */
    markAsUnclustered = 1;
  }
  db_finalize(&s1);

  /* Construct a received-from ID if we do not already have one */
  if( g.rcvid==0 ){
    db_multi_exec(
       "INSERT INTO rcvfrom(uid, mtime, nonce, ipaddr)"
       "VALUES(%d, julianday('now'), %Q, %Q)",
       g.userUid, g.zNonce, g.zIpAddr
    );
    g.rcvid = db_last_insert_rowid();
  }

  if( nBlob ){
    cmpr = pBlob[0];
  }else{
    blob_compress(pBlob, &cmpr);
  }
  if( rid>0 ){
................................................................................
  if( strncmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)==0 ) return 1;
  if( z[0]<'A' || z[0]>'Z' || z[1]!=' ' || z[0]=='I' ) return 0;
  if( z[n-1]!='\n' ) return 0;
  return 1;
}

/*
** COMMAND: test-integrity ?OPTIONS?
**
** Verify that all content can be extracted from the BLOB table correctly.
** If the BLOB table is correct, then the repository can always be
** successfully reconstructed using "fossil rebuild".
**
** Options:
**
................................................................................
  blob_reset(&x);
  if( c!='y' && c!='Y' ) return;
  db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
  db_begin_transaction();
  db_prepare(&q, "SELECT rid FROM delta WHERE srcid=:rid");
  for(i=2; i<g.argc; i++){
    int rid = atoi(g.argv[i]);
    fossil_print("Erasing artifact %d (%s)\n", 
                 rid, db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid));
    db_bind_int(&q, ":rid", rid);
    while( db_step(&q)==SQLITE_ROW ){
      content_undelta(db_column_int(&q,0));
    }
    db_reset(&q);
    db_multi_exec("DELETE FROM blob WHERE rid=%d", rid);
    db_multi_exec("DELETE FROM delta WHERE rid=%d", rid);
  }
  db_finalize(&q);
  db_end_transaction(0);
}







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
<
<
<
<
<
<
<







 







|







 







|












450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
...
548
549
550
551
552
553
554
555







556
557
558
559
560
561
562
...
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
....
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193

/*
** Turn dephantomization processing on or off.
*/
void content_enable_dephantomize(int onoff){
  ignoreDephantomizations = !onoff;
}

/*
** Make sure the g.rcvid global variable has been initialized.
**
** If the g.zIpAddr variable has not been set when this routine is
** called, use zSrc as the source of content for the rcvfrom
** table entry.
*/
void content_rcvid_init(const char *zSrc){
  if( g.rcvid==0 ){
    user_select();
    if( g.zIpAddr ) zSrc = g.zIpAddr;
    db_multi_exec(
       "INSERT INTO rcvfrom(uid, mtime, nonce, ipaddr)"
       "VALUES(%d, julianday('now'), %Q, %Q)",
       g.userUid, g.zNonce, zSrc
    );
    g.rcvid = db_last_insert_rowid();
  }
}

/*
** Write content into the database.  Return the record ID.  If the
** content is already in the database, just return the record ID.
**
** If srcId is specified, then pBlob is delta content from
** the srcId record.  srcId might be a phantom.
................................................................................
  }else{
    rid = 0;  /* No entry with the same UUID currently exists */
    markAsUnclustered = 1;
  }
  db_finalize(&s1);

  /* Construct a received-from ID if we do not already have one */
  content_rcvid_init(0);








  if( nBlob ){
    cmpr = pBlob[0];
  }else{
    blob_compress(pBlob, &cmpr);
  }
  if( rid>0 ){
................................................................................
  if( strncmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)==0 ) return 1;
  if( z[0]<'A' || z[0]>'Z' || z[1]!=' ' || z[0]=='I' ) return 0;
  if( z[n-1]!='\n' ) return 0;
  return 1;
}

/*
** COMMAND: test-integrity
**
** Verify that all content can be extracted from the BLOB table correctly.
** If the BLOB table is correct, then the repository can always be
** successfully reconstructed using "fossil rebuild".
**
** Options:
**
................................................................................
  blob_reset(&x);
  if( c!='y' && c!='Y' ) return;
  db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
  db_begin_transaction();
  db_prepare(&q, "SELECT rid FROM delta WHERE srcid=:rid");
  for(i=2; i<g.argc; i++){
    int rid = atoi(g.argv[i]);
    fossil_print("Erasing artifact %d (%s)\n",
                 rid, db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid));
    db_bind_int(&q, ":rid", rid);
    while( db_step(&q)==SQLITE_ROW ){
      content_undelta(db_column_int(&q,0));
    }
    db_reset(&q);
    db_multi_exec("DELETE FROM blob WHERE rid=%d", rid);
    db_multi_exec("DELETE FROM delta WHERE rid=%d", rid);
  }
  db_finalize(&q);
  db_end_transaction(0);
}

Changes to src/cson_amalgamation.c.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
..
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
..
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
....
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
....
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
....
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
....
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
....
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
....
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
....
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
....
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
....
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
....
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
....
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
....
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
....
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
....
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
....
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
....
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
....
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
....
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
....
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
....
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
....
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
....
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
....
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
....
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
#   ifdef _MSC_VER
#	    ifdef JSON_PARSER_DLL_EXPORTS
#		    define JSON_PARSER_DLL_API __declspec(dllexport)
#	    else
#		    define JSON_PARSER_DLL_API __declspec(dllimport)
#       endif
#   else
#	    define JSON_PARSER_DLL_API 
#   endif
#else
#	define JSON_PARSER_DLL_API 
#endif

/* Determine the integer type use to parse non-floating point numbers */
#ifdef _WIN32
typedef __int64 JSON_int_t;
#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%I64d"
#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%I64d"
#elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1)
typedef long long JSON_int_t;
#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%lld"
#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%lld"
#else 
typedef long JSON_int_t;
#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%ld"
#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%ld"
#endif


#ifdef __cplusplus
extern "C" {
#endif 

typedef enum 
{
    JSON_E_NONE = 0,
    JSON_E_INVALID_CHAR,
    JSON_E_INVALID_KEYWORD,
    JSON_E_INVALID_ESCAPE_SEQUENCE,
    JSON_E_INVALID_UNICODE_SEQUENCE,
    JSON_E_INVALID_NUMBER,
................................................................................
    JSON_E_NESTING_DEPTH_REACHED,
    JSON_E_UNBALANCED_COLLECTION,
    JSON_E_EXPECTED_KEY,
    JSON_E_EXPECTED_COLON,
    JSON_E_OUT_OF_MEMORY
} JSON_error;

typedef enum 
{
    JSON_T_NONE = 0,
    JSON_T_ARRAY_BEGIN,
    JSON_T_ARRAY_END,
    JSON_T_OBJECT_BEGIN,
    JSON_T_OBJECT_END,
    JSON_T_INTEGER,
................................................................................
    JSON_T_KEY,
    JSON_T_MAX
} JSON_type;

typedef struct JSON_value_struct {
    union {
        JSON_int_t integer_value;
        
        double float_value;
        
        struct {
            const char* value;
            size_t length;
        } str;
    } vu;
} JSON_value;

typedef struct JSON_parser_struct* JSON_parser;

/*! \brief JSON parser callback 

    \param ctx The pointer passed to new_JSON_parser.
    \param type An element of JSON_type but not JSON_T_NONE.    
    \param value A representation of the parsed value. This parameter is NULL for
        JSON_T_ARRAY_BEGIN, JSON_T_ARRAY_END, JSON_T_OBJECT_BEGIN, JSON_T_OBJECT_END,
        JSON_T_NULL, JSON_T_TRUE, and JSON_T_FALSE. String values are always returned
        as zero-terminated C strings.

    \return Non-zero if parsing should continue, else zero.
*/    
typedef int (*JSON_parser_callback)(void* ctx, int type, const JSON_value* value);


/**
   A typedef for allocator functions semantically compatible with malloc().
*/
typedef void* (*JSON_malloc_t)(size_t n);
/**
   A typedef for deallocator functions semantically compatible with free().
*/
typedef void (*JSON_free_t)(void* mem);

/*! \brief The structure used to configure a JSON parser object 
*/
typedef struct {
    /** Pointer to a callback, called when the parser has something to tell
        the user. This parameter may be NULL. In this case the input is
        merely checked for validity.
    */
    JSON_parser_callback    callback;
................................................................................
    - no comments
    - Uses realloc() for memory de/allocation.

    \param config. Used to configure the parser.
*/
JSON_PARSER_DLL_API void init_JSON_config(JSON_config * config);

/*! \brief Create a JSON parser object 

    \param config. Used to configure the parser. Set to NULL to use
        the default configuration. See init_JSON_config.  Its contents are
        copied by this function, so it need not outlive the returned
        object.
    
    \return The parser object, which is owned by the caller and must eventually
    be freed by calling delete_JSON_parser().
*/
JSON_PARSER_DLL_API JSON_parser new_JSON_parser(JSON_config const* config);

/*! \brief Destroy a previously created JSON parser object. */
JSON_PARSER_DLL_API void delete_JSON_parser(JSON_parser jc);
................................................................................
    \return Non-zero, if all characters passed to this function are part of are valid JSON.
*/
JSON_PARSER_DLL_API int JSON_parser_char(JSON_parser jc, int next_char);

/*! \brief Finalize parsing.

    Call this method once after all input characters have been consumed.
    
    \return Non-zero, if all parsed characters are valid JSON, zero otherwise.
*/
JSON_PARSER_DLL_API int JSON_parser_done(JSON_parser jc);

/*! \brief Determine if a given string is valid JSON white space 

    \return Non-zero if the string is valid, zero otherwise.
*/
JSON_PARSER_DLL_API int JSON_parser_is_legal_white_space_string(const char* s);

/*! \brief Gets the last error that occurred during the use of JSON_parser.

................................................................................
    \return True (non-zero) on success, 0 on error (e.g. !jc).
*/
JSON_PARSER_DLL_API int JSON_parser_reset(JSON_parser jc);


#ifdef __cplusplus
}
#endif 
    

#endif /* JSON_PARSER_H */
/* end file parser/JSON_parser.h */
/* begin file parser/JSON_parser.c */
/*
Copyright (c) 2007-2013 Jean Gressmann (jean@0x42.de)

................................................................................
#endif

#if defined(__cplusplus)
extern "C" {
#endif


    
/**
   This type holds the "vtbl" for type-specific operations when
   working with cson_value objects.

   All cson_values of a given logical type share a pointer to a single
   library-internal instance of this class.
*/
................................................................................
   Assumes V is a (cson_value*) ans V->value is a (T*). Returns
   V->value cast to a (T*).
*/
#define CSON_CAST(T,V) ((T*)((V)->value))
/**
   Assumes V is a pointer to memory which is allocated as part of a
   cson_value instance (the bytes immediately after that part).
   Returns a pointer a a cson_value by subtracting sizeof(cson_value)
   from that address and casting it to a (cson_value*)
*/
#define CSON_VCAST(V) ((cson_value *)(((unsigned char *)(V))-sizeof(cson_value)))

/**
   CSON_INT(V) assumes that V is a (cson_value*) of type
   CSON_TYPE_INTEGER. This macro returns a (cson_int_t*) representing
................................................................................
#define CSON_DBL(V) CSON_CAST(cson_double_t,(V))
#define CSON_STR(V) CSON_CAST(cson_string,(V))
#define CSON_OBJ(V) CSON_CAST(cson_object,(V))
#define CSON_ARRAY(V) CSON_CAST(cson_array,(V))

/**
 Holds special shared "constant" (though they are non-const)
 values. 
*/
static struct CSON_EMPTY_HOLDER_
{
    char trueValue;
    cson_string stringValue;
} CSON_EMPTY_HOLDER = {
    1/*trueValue*/,
    cson_string_empty_m
};

/**
    Indexes into the CSON_SPECIAL_VALUES array.
    
    If this enum changes in any way,
    makes damned sure that CSON_SPECIAL_VALUES is updated
    to match!!!
*/
enum CSON_INTERNAL_VALUES {
    
    CSON_VAL_UNDEF = 0,
    CSON_VAL_NULL = 1,
    CSON_VAL_TRUE = 2,
    CSON_VAL_FALSE = 3,
    CSON_VAL_INT_0 = 4,
    CSON_VAL_DBL_0 = 5,
    CSON_VAL_STR_EMPTY = 6,
................................................................................
};

/**
  Some "special" shared cson_value instances.

  These values MUST be initialized in the order specified
  by the CSON_INTERNAL_VALUES enum.
   
  Note that they are not const because they are used as
  shared-allocation objects in non-const contexts. However, the
  public API provides no way to modifying them, and clients who
  modify values directly are subject to The Wrath of Undefined
  Behaviour.
*/
static cson_value CSON_SPECIAL_VALUES[] = {
................................................................................
};


/**
    Returns non-0 (true) if m is one of our special
    "built-in" values, e.g. from CSON_SPECIAL_VALUES and some
    "empty" values.
     
    If this returns true, m MUST NOT be free()d!
 */
static char cson_value_is_builtin( void const * m )
{
    if((m >= (void const *)&CSON_EMPTY_HOLDER)
        && ( m < (void const *)(&CSON_EMPTY_HOLDER+1)))
        return 1;
................................................................................
                                int (*visitor)(cson_kvp * obj, void * visitorState ),
                                void * visitorState );
static int cson_value_list_visit( cson_value_list * self,
                                  int (*visitor)(cson_value * obj, void * visitorState ),
                                  void * visitorState );
#endif
#endif
    
#if 0
#  define LIST_T cson_value_list
#  define VALUE_T cson_value *
#  define VALUE_T_IS_PTR 1
#  define LIST_T cson_kvp_list
#  define VALUE_T cson_kvp *
#  define VALUE_T_IS_PTR 1
................................................................................
cson_value * cson_value_new_object()
{
    return cson_value_object_alloc();
}

cson_object * cson_new_object()
{
    
    return cson_value_get_object( cson_value_new_object() );
}

cson_value * cson_value_new_array()
{
    return cson_value_array_alloc();
}
................................................................................
    if( ! val || !val->api ) return cson_rc.ArgError;
    else
    {
        cson_int_t i = 0;
        int rc = 0;
        switch(val->api->typeID)
        {
            case CSON_TYPE_UNDEF: 
            case CSON_TYPE_NULL:
              i = 0;
              break;
            case CSON_TYPE_BOOL: {
              char b = 0;
              cson_value_fetch_bool( val, &b );
              i = b;
................................................................................
    if( ! val || !val->api ) return cson_rc.ArgError;
    else
    {
        cson_double_t d = 0.0;
        int rc = 0;
        switch(val->api->typeID)
        {
          case CSON_TYPE_UNDEF: 
          case CSON_TYPE_NULL:
              d = 0;
              break;
          case CSON_TYPE_BOOL: {
              char b = 0;
              cson_value_fetch_bool( val, &b );
              d = b ? 1.0 : 0.0;
................................................................................
}

#if 0
/**
   Removes and returns the last value from the given array,
   shrinking its size by 1. Returns NULL if ar is NULL,
   ar->list.count is 0, or the element at that index is NULL.
   

   If removeRef is true then cson_value_free() is called to remove
   ar's reference count for the value. In that case NULL is returned,
   even if the object still has live references. If removeRef is false
   then the caller takes over ownership of that reference count point.

   If removeRef is false then the caller takes over ownership
................................................................................
    {
        cson_value * c = cson_value_new(CSON_TYPE_INTEGER,0);
#if !defined(NDEBUG) && CSON_VOID_PTR_IS_BIG
        assert( sizeof(cson_int_t) <= sizeof(void *) );
#endif
        if( c )
        {
            *CSON_INT(c) = v;
        }
        return c;
    }
}

cson_value * cson_new_double( cson_double_t v )
{
................................................................................
{
    if( 0.0 == v ) return &CSON_SPECIAL_VALUES[CSON_VAL_DBL_0];
    else
    {
        cson_value * c = cson_value_new(CSON_TYPE_DOUBLE,0);
        if( c )
        {
            *CSON_DBL(c) = v;
        }
        return c;
    }
}

cson_string * cson_new_string(char const * str, unsigned int len)
{
................................................................................
    if( obj->kvp.count )
    {
        qsort( obj->kvp.list, obj->kvp.count, sizeof(cson_kvp*),
               cson_kvp_cmp );
    }

}
#endif    

int cson_object_unset( cson_object * obj, char const * key )
{
    if( ! obj || !key || !*key ) return cson_rc.ArgError;
    else
    {
        unsigned int ndx = 0;
................................................................................

   If p->node is-a Object then value is inserted into the object
   using p->key. In any other case cson_rc.InternalError is returned.

   Returns cson_rc.AllocError if an allocation fails.

   Returns 0 on success. On error, parsing must be ceased immediately.
   
   Ownership of val is ALWAYS TRANSFERED to this function. If this
   function fails, val will be cleaned up and destroyed. (This
   simplifies error handling in the core parser.)
*/
static int cson_parser_set_key( cson_parser * p, cson_value * val )
{
    assert( p && val );
................................................................................
              break;
          }
          ++p->totalKeyCount;
          break;
      }
      case JSON_T_STRING: {
          cson_value * v = cson_value_new_string( value->vu.str.value, value->vu.str.length );
          rc = ( NULL == v ) 
            ? cson_rc.AllocError
            : cson_parser_push_value( p, v );
          break;
      }
      default:
          assert(0);
          rc = cson_rc.InternalError;
................................................................................
   Cleans up all contents of p but does not free p.

   To properly take over ownership of the parser's root node on a
   successful parse:

   - Copy p->root's pointer and set p->root to NULL.
   - Eventually free up p->root with cson_value_free().
   
   If you do not set p->root to NULL, p->root will be freed along with
   any other items inserted into it (or under it) during the parsing
   process.
*/
static int cson_parser_clean( cson_parser * p )
{
    if( ! p ) return cson_rc.ArgError;
................................................................................
    unsigned char ch[2] = {0,0};
    cson_parse_opt const opt = opt_ ? *opt_ : cson_parse_opt_empty;
    int rc = 0;
    unsigned int len = 1;
    cson_parse_info info = info_ ? *info_ : cson_parse_info_empty;
    cson_parser p = cson_parser_empty;
    if( ! tgt || ! src ) return cson_rc.ArgError;
    
    {
        JSON_config jopt = {0};
        init_JSON_config( &jopt );
        jopt.allow_comments = opt.allowComments;
        jopt.depth = opt.maxDepth;
        jopt.callback_ctx = &p;
        jopt.handle_floats_manually = 0;
................................................................................
#else
    rc = cson_value_clone(v);
#endif
#undef TRY_SHARING
    cson_value_add_reference(rc);
    return rc;
}
    
static cson_value * cson_value_clone_array( cson_value const * orig )
{
    unsigned int i = 0;
    cson_array const * asrc = cson_value_get_array( orig );
    unsigned int alen = cson_array_length_get( asrc );
    cson_value * destV = NULL;
    cson_array * destA = NULL;
................................................................................
                return NULL;
            }
            cson_value_free(cl)/*remove our artificial reference */;
        }
    }
    return destV;
}
    
static cson_value * cson_value_clone_object( cson_value const * orig )
{
    cson_object const * src = cson_value_get_object( orig );
    cson_value * destV = NULL;
    cson_object * dest = NULL;
    cson_kvp const * kvp = NULL;
    cson_object_iterator iter = cson_object_iterator_empty;
................................................................................
              v = cson_strdup( "null", 4 );
              break;
          }
          case CSON_TYPE_STRING: {
              cson_string const * jstr = cson_value_get_string(orig);
              unsigned const int slen = cson_string_length_bytes( jstr );
              assert( NULL != jstr );
              v = cson_strdup( cson_string_cstr( jstr ), slen ); 
              break;
          }
          case CSON_TYPE_INTEGER: {
              char buf[BufSize] = {0};
              if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) )
              {
                  v = cson_strdup( buf, strlen(buf) );
................................................................................
              v = cson_strdup( "null", 4 );
              break;
          }
          case CSON_TYPE_STRING: {
              cson_string const * jstr = cson_value_get_string(orig);
              unsigned const int slen = cson_string_length_bytes( jstr );
              assert( NULL != jstr );
              v = cson_strdup( cson_string_cstr( jstr ), slen ); 
              break;
          }
          case CSON_TYPE_INTEGER: {
              char buf[BufSize] = {0};
              if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) )
              {
                  v = cson_strdup( buf, strlen(buf) );
................................................................................
    char const * colName = NULL;
    int i = 0;
    int rc = 0;
    int colCount = 0;
    assert(st);
    colCount = sqlite3_column_count(st);
    if( colCount <= 0 ) return NULL;
    
    aryV = cson_value_new_array();
    if( ! aryV ) return NULL;
    ary = cson_value_get_array(aryV);
    assert(ary);
    for( i = 0; (0 ==rc) && (i < colCount); ++i )
    {
        colName = sqlite3_column_name( st, i );
................................................................................
    error:
    cson_value_free(aryV);
    aryV = NULL;
    end:
    return aryV;
}

    
/**
    Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
    parameter is non-0.
*/
static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt )
{
#define RETURN(RC) { if(rootV) cson_value_free(rootV); return RC; }
................................................................................
    {
        sqlite3_stmt * st = NULL;
        int rc = sqlite3_prepare_v2( db, sql, -1, &st, NULL );
        if( 0 != rc ) return cson_rc.IOError /* FIXME: Better error code? */;
        rc = cson_sqlite3_stmt_to_json( st, tgt, fat );
        sqlite3_finalize( st );
        return rc;
    }        
}

int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v )
{
    int rc = 0;
    char convertErr = 0;
    if(!st) return cson_rc.ArgError;







|


|











|








|

|







 







|







 







|

|









|


|






|












|







 







|





|







 







|




|







 







|
|







 







|







 







|







 







|












|





|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
..
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
..
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
....
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
....
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
....
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
....
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
....
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
....
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
....
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
....
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
....
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
....
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
....
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
....
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
....
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
....
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
....
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
....
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
....
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
....
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
....
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
....
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
....
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
....
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
....
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
....
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
#   ifdef _MSC_VER
#	    ifdef JSON_PARSER_DLL_EXPORTS
#		    define JSON_PARSER_DLL_API __declspec(dllexport)
#	    else
#		    define JSON_PARSER_DLL_API __declspec(dllimport)
#       endif
#   else
#	    define JSON_PARSER_DLL_API
#   endif
#else
#	define JSON_PARSER_DLL_API
#endif

/* Determine the integer type use to parse non-floating point numbers */
#ifdef _WIN32
typedef __int64 JSON_int_t;
#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%I64d"
#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%I64d"
#elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1)
typedef long long JSON_int_t;
#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%lld"
#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%lld"
#else
typedef long JSON_int_t;
#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%ld"
#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%ld"
#endif


#ifdef __cplusplus
extern "C" {
#endif

typedef enum
{
    JSON_E_NONE = 0,
    JSON_E_INVALID_CHAR,
    JSON_E_INVALID_KEYWORD,
    JSON_E_INVALID_ESCAPE_SEQUENCE,
    JSON_E_INVALID_UNICODE_SEQUENCE,
    JSON_E_INVALID_NUMBER,
................................................................................
    JSON_E_NESTING_DEPTH_REACHED,
    JSON_E_UNBALANCED_COLLECTION,
    JSON_E_EXPECTED_KEY,
    JSON_E_EXPECTED_COLON,
    JSON_E_OUT_OF_MEMORY
} JSON_error;

typedef enum
{
    JSON_T_NONE = 0,
    JSON_T_ARRAY_BEGIN,
    JSON_T_ARRAY_END,
    JSON_T_OBJECT_BEGIN,
    JSON_T_OBJECT_END,
    JSON_T_INTEGER,
................................................................................
    JSON_T_KEY,
    JSON_T_MAX
} JSON_type;

typedef struct JSON_value_struct {
    union {
        JSON_int_t integer_value;

        double float_value;

        struct {
            const char* value;
            size_t length;
        } str;
    } vu;
} JSON_value;

typedef struct JSON_parser_struct* JSON_parser;

/*! \brief JSON parser callback

    \param ctx The pointer passed to new_JSON_parser.
    \param type An element of JSON_type but not JSON_T_NONE.
    \param value A representation of the parsed value. This parameter is NULL for
        JSON_T_ARRAY_BEGIN, JSON_T_ARRAY_END, JSON_T_OBJECT_BEGIN, JSON_T_OBJECT_END,
        JSON_T_NULL, JSON_T_TRUE, and JSON_T_FALSE. String values are always returned
        as zero-terminated C strings.

    \return Non-zero if parsing should continue, else zero.
*/
typedef int (*JSON_parser_callback)(void* ctx, int type, const JSON_value* value);


/**
   A typedef for allocator functions semantically compatible with malloc().
*/
typedef void* (*JSON_malloc_t)(size_t n);
/**
   A typedef for deallocator functions semantically compatible with free().
*/
typedef void (*JSON_free_t)(void* mem);

/*! \brief The structure used to configure a JSON parser object
*/
typedef struct {
    /** Pointer to a callback, called when the parser has something to tell
        the user. This parameter may be NULL. In this case the input is
        merely checked for validity.
    */
    JSON_parser_callback    callback;
................................................................................
    - no comments
    - Uses realloc() for memory de/allocation.

    \param config. Used to configure the parser.
*/
JSON_PARSER_DLL_API void init_JSON_config(JSON_config * config);

/*! \brief Create a JSON parser object

    \param config. Used to configure the parser. Set to NULL to use
        the default configuration. See init_JSON_config.  Its contents are
        copied by this function, so it need not outlive the returned
        object.

    \return The parser object, which is owned by the caller and must eventually
    be freed by calling delete_JSON_parser().
*/
JSON_PARSER_DLL_API JSON_parser new_JSON_parser(JSON_config const* config);

/*! \brief Destroy a previously created JSON parser object. */
JSON_PARSER_DLL_API void delete_JSON_parser(JSON_parser jc);
................................................................................
    \return Non-zero, if all characters passed to this function are part of are valid JSON.
*/
JSON_PARSER_DLL_API int JSON_parser_char(JSON_parser jc, int next_char);

/*! \brief Finalize parsing.

    Call this method once after all input characters have been consumed.

    \return Non-zero, if all parsed characters are valid JSON, zero otherwise.
*/
JSON_PARSER_DLL_API int JSON_parser_done(JSON_parser jc);

/*! \brief Determine if a given string is valid JSON white space

    \return Non-zero if the string is valid, zero otherwise.
*/
JSON_PARSER_DLL_API int JSON_parser_is_legal_white_space_string(const char* s);

/*! \brief Gets the last error that occurred during the use of JSON_parser.

................................................................................
    \return True (non-zero) on success, 0 on error (e.g. !jc).
*/
JSON_PARSER_DLL_API int JSON_parser_reset(JSON_parser jc);


#ifdef __cplusplus
}
#endif


#endif /* JSON_PARSER_H */
/* end file parser/JSON_parser.h */
/* begin file parser/JSON_parser.c */
/*
Copyright (c) 2007-2013 Jean Gressmann (jean@0x42.de)

................................................................................
#endif

#if defined(__cplusplus)
extern "C" {
#endif



/**
   This type holds the "vtbl" for type-specific operations when
   working with cson_value objects.

   All cson_values of a given logical type share a pointer to a single
   library-internal instance of this class.
*/
................................................................................
   Assumes V is a (cson_value*) ans V->value is a (T*). Returns
   V->value cast to a (T*).
*/
#define CSON_CAST(T,V) ((T*)((V)->value))
/**
   Assumes V is a pointer to memory which is allocated as part of a
   cson_value instance (the bytes immediately after that part).
   Returns a pointer a cson_value by subtracting sizeof(cson_value)
   from that address and casting it to a (cson_value*)
*/
#define CSON_VCAST(V) ((cson_value *)(((unsigned char *)(V))-sizeof(cson_value)))

/**
   CSON_INT(V) assumes that V is a (cson_value*) of type
   CSON_TYPE_INTEGER. This macro returns a (cson_int_t*) representing
................................................................................
#define CSON_DBL(V) CSON_CAST(cson_double_t,(V))
#define CSON_STR(V) CSON_CAST(cson_string,(V))
#define CSON_OBJ(V) CSON_CAST(cson_object,(V))
#define CSON_ARRAY(V) CSON_CAST(cson_array,(V))

/**
 Holds special shared "constant" (though they are non-const)
 values.
*/
static struct CSON_EMPTY_HOLDER_
{
    char trueValue;
    cson_string stringValue;
} CSON_EMPTY_HOLDER = {
    1/*trueValue*/,
    cson_string_empty_m
};

/**
    Indexes into the CSON_SPECIAL_VALUES array.

    If this enum changes in any way,
    makes damned sure that CSON_SPECIAL_VALUES is updated
    to match!!!
*/
enum CSON_INTERNAL_VALUES {

    CSON_VAL_UNDEF = 0,
    CSON_VAL_NULL = 1,
    CSON_VAL_TRUE = 2,
    CSON_VAL_FALSE = 3,
    CSON_VAL_INT_0 = 4,
    CSON_VAL_DBL_0 = 5,
    CSON_VAL_STR_EMPTY = 6,
................................................................................
};

/**
  Some "special" shared cson_value instances.

  These values MUST be initialized in the order specified
  by the CSON_INTERNAL_VALUES enum.

  Note that they are not const because they are used as
  shared-allocation objects in non-const contexts. However, the
  public API provides no way to modifying them, and clients who
  modify values directly are subject to The Wrath of Undefined
  Behaviour.
*/
static cson_value CSON_SPECIAL_VALUES[] = {
................................................................................
};


/**
    Returns non-0 (true) if m is one of our special
    "built-in" values, e.g. from CSON_SPECIAL_VALUES and some
    "empty" values.

    If this returns true, m MUST NOT be free()d!
 */
static char cson_value_is_builtin( void const * m )
{
    if((m >= (void const *)&CSON_EMPTY_HOLDER)
        && ( m < (void const *)(&CSON_EMPTY_HOLDER+1)))
        return 1;
................................................................................
                                int (*visitor)(cson_kvp * obj, void * visitorState ),
                                void * visitorState );
static int cson_value_list_visit( cson_value_list * self,
                                  int (*visitor)(cson_value * obj, void * visitorState ),
                                  void * visitorState );
#endif
#endif

#if 0
#  define LIST_T cson_value_list
#  define VALUE_T cson_value *
#  define VALUE_T_IS_PTR 1
#  define LIST_T cson_kvp_list
#  define VALUE_T cson_kvp *
#  define VALUE_T_IS_PTR 1
................................................................................
cson_value * cson_value_new_object()
{
    return cson_value_object_alloc();
}

cson_object * cson_new_object()
{

    return cson_value_get_object( cson_value_new_object() );
}

cson_value * cson_value_new_array()
{
    return cson_value_array_alloc();
}
................................................................................
    if( ! val || !val->api ) return cson_rc.ArgError;
    else
    {
        cson_int_t i = 0;
        int rc = 0;
        switch(val->api->typeID)
        {
            case CSON_TYPE_UNDEF:
            case CSON_TYPE_NULL:
              i = 0;
              break;
            case CSON_TYPE_BOOL: {
              char b = 0;
              cson_value_fetch_bool( val, &b );
              i = b;
................................................................................
    if( ! val || !val->api ) return cson_rc.ArgError;
    else
    {
        cson_double_t d = 0.0;
        int rc = 0;
        switch(val->api->typeID)
        {
          case CSON_TYPE_UNDEF:
          case CSON_TYPE_NULL:
              d = 0;
              break;
          case CSON_TYPE_BOOL: {
              char b = 0;
              cson_value_fetch_bool( val, &b );
              d = b ? 1.0 : 0.0;
................................................................................
}

#if 0
/**
   Removes and returns the last value from the given array,
   shrinking its size by 1. Returns NULL if ar is NULL,
   ar->list.count is 0, or the element at that index is NULL.


   If removeRef is true then cson_value_free() is called to remove
   ar's reference count for the value. In that case NULL is returned,
   even if the object still has live references. If removeRef is false
   then the caller takes over ownership of that reference count point.

   If removeRef is false then the caller takes over ownership
................................................................................
    {
        cson_value * c = cson_value_new(CSON_TYPE_INTEGER,0);
#if !defined(NDEBUG) && CSON_VOID_PTR_IS_BIG
        assert( sizeof(cson_int_t) <= sizeof(void *) );
#endif
        if( c )
        {
            memcpy(CSON_INT(c), &v, sizeof(v));
        }
        return c;
    }
}

cson_value * cson_new_double( cson_double_t v )
{
................................................................................
{
    if( 0.0 == v ) return &CSON_SPECIAL_VALUES[CSON_VAL_DBL_0];
    else
    {
        cson_value * c = cson_value_new(CSON_TYPE_DOUBLE,0);
        if( c )
        {
            memcpy(CSON_DBL(c), &v, sizeof(v));
        }
        return c;
    }
}

cson_string * cson_new_string(char const * str, unsigned int len)
{
................................................................................
    if( obj->kvp.count )
    {
        qsort( obj->kvp.list, obj->kvp.count, sizeof(cson_kvp*),
               cson_kvp_cmp );
    }

}
#endif

int cson_object_unset( cson_object * obj, char const * key )
{
    if( ! obj || !key || !*key ) return cson_rc.ArgError;
    else
    {
        unsigned int ndx = 0;
................................................................................

   If p->node is-a Object then value is inserted into the object
   using p->key. In any other case cson_rc.InternalError is returned.

   Returns cson_rc.AllocError if an allocation fails.

   Returns 0 on success. On error, parsing must be ceased immediately.

   Ownership of val is ALWAYS TRANSFERED to this function. If this
   function fails, val will be cleaned up and destroyed. (This
   simplifies error handling in the core parser.)
*/
static int cson_parser_set_key( cson_parser * p, cson_value * val )
{
    assert( p && val );
................................................................................
              break;
          }
          ++p->totalKeyCount;
          break;
      }
      case JSON_T_STRING: {
          cson_value * v = cson_value_new_string( value->vu.str.value, value->vu.str.length );
          rc = ( NULL == v )
            ? cson_rc.AllocError
            : cson_parser_push_value( p, v );
          break;
      }
      default:
          assert(0);
          rc = cson_rc.InternalError;
................................................................................
   Cleans up all contents of p but does not free p.

   To properly take over ownership of the parser's root node on a
   successful parse:

   - Copy p->root's pointer and set p->root to NULL.
   - Eventually free up p->root with cson_value_free().

   If you do not set p->root to NULL, p->root will be freed along with
   any other items inserted into it (or under it) during the parsing
   process.
*/
static int cson_parser_clean( cson_parser * p )
{
    if( ! p ) return cson_rc.ArgError;
................................................................................
    unsigned char ch[2] = {0,0};
    cson_parse_opt const opt = opt_ ? *opt_ : cson_parse_opt_empty;
    int rc = 0;
    unsigned int len = 1;
    cson_parse_info info = info_ ? *info_ : cson_parse_info_empty;
    cson_parser p = cson_parser_empty;
    if( ! tgt || ! src ) return cson_rc.ArgError;

    {
        JSON_config jopt = {0};
        init_JSON_config( &jopt );
        jopt.allow_comments = opt.allowComments;
        jopt.depth = opt.maxDepth;
        jopt.callback_ctx = &p;
        jopt.handle_floats_manually = 0;
................................................................................
#else
    rc = cson_value_clone(v);
#endif
#undef TRY_SHARING
    cson_value_add_reference(rc);
    return rc;
}

static cson_value * cson_value_clone_array( cson_value const * orig )
{
    unsigned int i = 0;
    cson_array const * asrc = cson_value_get_array( orig );
    unsigned int alen = cson_array_length_get( asrc );
    cson_value * destV = NULL;
    cson_array * destA = NULL;
................................................................................
                return NULL;
            }
            cson_value_free(cl)/*remove our artificial reference */;
        }
    }
    return destV;
}

static cson_value * cson_value_clone_object( cson_value const * orig )
{
    cson_object const * src = cson_value_get_object( orig );
    cson_value * destV = NULL;
    cson_object * dest = NULL;
    cson_kvp const * kvp = NULL;
    cson_object_iterator iter = cson_object_iterator_empty;
................................................................................
              v = cson_strdup( "null", 4 );
              break;
          }
          case CSON_TYPE_STRING: {
              cson_string const * jstr = cson_value_get_string(orig);
              unsigned const int slen = cson_string_length_bytes( jstr );
              assert( NULL != jstr );
              v = cson_strdup( cson_string_cstr( jstr ), slen );
              break;
          }
          case CSON_TYPE_INTEGER: {
              char buf[BufSize] = {0};
              if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) )
              {
                  v = cson_strdup( buf, strlen(buf) );
................................................................................
              v = cson_strdup( "null", 4 );
              break;
          }
          case CSON_TYPE_STRING: {
              cson_string const * jstr = cson_value_get_string(orig);
              unsigned const int slen = cson_string_length_bytes( jstr );
              assert( NULL != jstr );
              v = cson_strdup( cson_string_cstr( jstr ), slen );
              break;
          }
          case CSON_TYPE_INTEGER: {
              char buf[BufSize] = {0};
              if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) )
              {
                  v = cson_strdup( buf, strlen(buf) );
................................................................................
    char const * colName = NULL;
    int i = 0;
    int rc = 0;
    int colCount = 0;
    assert(st);
    colCount = sqlite3_column_count(st);
    if( colCount <= 0 ) return NULL;

    aryV = cson_value_new_array();
    if( ! aryV ) return NULL;
    ary = cson_value_get_array(aryV);
    assert(ary);
    for( i = 0; (0 ==rc) && (i < colCount); ++i )
    {
        colName = sqlite3_column_name( st, i );
................................................................................
    error:
    cson_value_free(aryV);
    aryV = NULL;
    end:
    return aryV;
}


/**
    Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
    parameter is non-0.
*/
static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt )
{
#define RETURN(RC) { if(rootV) cson_value_free(rootV); return RC; }
................................................................................
    {
        sqlite3_stmt * st = NULL;
        int rc = sqlite3_prepare_v2( db, sql, -1, &st, NULL );
        if( 0 != rc ) return cson_rc.IOError /* FIXME: Better error code? */;
        rc = cson_sqlite3_stmt_to_json( st, tgt, fat );
        sqlite3_finalize( st );
        return rc;
    }
}

int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v )
{
    int rc = 0;
    char convertErr = 0;
    if(!st) return cson_rc.ArgError;

Changes to src/db.c.

932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
...
959
960
961
962
963
964
965





































966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981

982
983
984
985
986
987
988
989
990
991
992
993

994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
....
1021
1022
1023
1024
1025
1026
1027

1028
1029
1030
1031
1032
1033
1034
1035
....
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088

1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
....
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
....
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
....
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
....
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
....
1748
1749
1750
1751
1752
1753
1754






1755
1756
1757
1758
1759
1760
1761
....
1806
1807
1808
1809
1810
1811
1812
1813






1814
1815


1816
1817
1818
1819
1820
1821
1822
....
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
....
2576
2577
2578
2579
2580
2581
2582

2583
2584
2585
2586
2587
2588
2589
....
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
....
2850
2851
2852
2853
2854
2855
2856





2857
2858
2859
2860
2861
2862
2863
2864
2865
2866


2867
2868
2869
2870
2871

2872

2873
2874
2875
2876
2877
2878
2879
....
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
....
2928
2929
2930
2931
2932
2933
2934




2935

2936
2937
2938
2939
2940
2941
2942
....
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
....
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
....
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
....
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
  sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
  sqlite3_create_function(
    db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
  );
  sqlite3_create_function(
    db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0
  );
  if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0);
  db_add_aux_functions(db);
  re_add_sql_func(db);  /* The REGEXP operator */
  foci_register(db);    /* The "files_of_checkin" virtual table */
  sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
  return db;
}

................................................................................
void db_attach(const char *zDbName, const char *zLabel){
  Blob key;
  db_encryption_key(zDbName, &key);
  db_multi_exec("ATTACH DATABASE %Q AS %Q KEY %Q",
                zDbName, zLabel, blob_str(&key));
  blob_reset(&key);
}






































/*
** zDbName is the name of a database file.  If no other database
** file is open, then open this one.  If another database file is
** already open, then attach zDbName using the name zLabel.
*/
void db_open_or_attach(
  const char *zDbName,
  const char *zLabel,
  int *pWasAttached
){
  if( !g.db ){
    assert( g.zMainDbType==0 );
    g.db = db_open(zDbName);
    g.zMainDbType = zLabel;
    if( pWasAttached ) *pWasAttached = 0;

  }else{
    assert( g.zMainDbType!=0 );
    db_attach(zDbName, zLabel);
    if( pWasAttached ) *pWasAttached = 1;
  }
}

/*
** Close the user database.
*/
void db_close_config(){
  if( g.useAttach ){

    db_detach("configdb");
    g.useAttach = 0;
    g.zConfigDbName = 0;
  }else if( g.dbConfig ){
    sqlite3_wal_checkpoint(g.dbConfig, 0);
    sqlite3_close(g.dbConfig);
    g.dbConfig = 0;
    g.zConfigDbType = 0;
    g.zConfigDbName = 0;
  }else if( g.db && fossil_strcmp(g.zMainDbType, "configdb")==0 ){
    sqlite3_wal_checkpoint(g.db, 0);
    sqlite3_close(g.db);
    g.db = 0;
    g.zMainDbType = 0;
    g.zConfigDbName = 0;
  }
}

/*
** Open the user database in "~/.fossil".  Create the database anew if
** it does not already exist.
................................................................................
** connection so that we can join between the various databases.  In that
** case, invoke this routine with useAttach as 1.
*/
int db_open_config(int useAttach, int isOptional){
  char *zDbName;
  char *zHome;
  if( g.zConfigDbName ){

    if( useAttach==g.useAttach ) return 1; /* Already open. */
    db_close_config();
  }
  zHome = fossil_getenv("FOSSIL_HOME");
#if defined(_WIN32) || defined(__CYGWIN__)
  if( zHome==0 ){
    zHome = fossil_getenv("LOCALAPPDATA");
    if( zHome==0 ){
................................................................................
    db_init_database(zDbName, zConfigSchema, (char*)0);
  }
  if( file_access(zDbName, W_OK) ){
    if( isOptional ) return 0;
    fossil_fatal("configuration file %s must be writeable", zDbName);
  }
  if( useAttach ){
    db_open_or_attach(zDbName, "configdb", &g.useAttach);
    g.dbConfig = 0;
    g.zConfigDbType = 0;
  }else{
    g.useAttach = 0;
    g.dbConfig = db_open(zDbName);
    g.zConfigDbType = "configdb";

  }
  g.zConfigDbName = zDbName;
  return 1;
}

/*
** Return TRUE if zTable exists.
*/
int db_table_exists(
  const char *zDb,      /* One of: NULL, "configdb", "localdb", "repository" */
  const char *zTable    /* Name of table */
){
  return sqlite3_table_column_metadata(g.db,
              zDb ? db_name(zDb) : 0, zTable, 0,
              0, 0, 0, 0, 0)==SQLITE_OK;
}

/*
** Return TRUE if zTable exists and contains column zColumn.
** Return FALSE if zTable does not exist or if zTable exists
** but lacks zColumn.
*/
int db_table_has_column(
  const char *zDb,       /* One of: NULL, "config", "localdb", "repository" */
  const char *zTable,    /* Name of table */
  const char *zColumn    /* Name of column in table */
){
  return sqlite3_table_column_metadata(g.db,
              zDb ? db_name(zDb) : 0, zTable, zColumn,
              0, 0, 0, 0, 0)==SQLITE_OK;
}

/*
** Returns TRUE if zTable exists in the local database but lacks column
** zColumn
*/
static int db_local_table_exists_but_lacks_column(
  const char *zTable,
  const char *zColumn
){
  return db_table_exists(db_name("localdb"), zTable)
      && !db_table_has_column(db_name("localdb"), zTable, zColumn);
}

/*
** If zDbName is a valid local database file, open it and return
** true.  If it is not a valid local database file, return 0.
*/
static int isValidLocalDb(const char *zDbName){
  i64 lsize;
  char *zVFileDef;

  if( file_access(zDbName, F_OK) ) return 0;
  lsize = file_size(zDbName);
  if( lsize%1024!=0 || lsize<4096 ) return 0;
  db_open_or_attach(zDbName, "localdb", 0);
  zVFileDef = db_text(0, "SELECT sql FROM %s.sqlite_master"
                         " WHERE name=='vfile'", db_name("localdb"));
  if( zVFileDef==0 ) return 0;

  /* If the "isexe" column is missing from the vfile table, then
  ** add it now.   This code added on 2010-03-06.  After all users have
  ** upgraded, this code can be safely deleted.
  */
  if( sqlite3_strglob("* isexe *", zVFileDef)!=0 ){
................................................................................
#ifdef FOSSIL_ENABLE_JSON
      g.json.resultCode = FSL_JSON_E_DB_NOT_VALID;
#endif
      fossil_panic("not a valid repository: %s", zDbName);
    }
  }
  g.zRepositoryName = mprintf("%s", zDbName);
  db_open_or_attach(g.zRepositoryName, "repository", 0);
  g.repositoryOpen = 1;
  /* Cache "allow-symlinks" option, because we'll need it on every stat call */
  g.allowSymlinks = db_get_boolean("allow-symlinks",
                                   db_allow_symlinks_by_default());
  g.zAuxSchema = db_get("aux-schema","");

  /* Verify that the PLINK table has a new column added by the
  ** 2014-11-28 schema change.  Create it if necessary.  This code
  ** can be removed in the future, once all users have upgraded to the
  ** 2014-11-28 or later schema.
  */
  if( !db_table_has_column("repository","plink","baseid") ){
    db_multi_exec(
      "ALTER TABLE %s.plink ADD COLUMN baseid;", db_name("repository")
    );
  }

  /* Verify that the MLINK table has the newer columns added by the
  ** 2015-01-24 schema change.  Create them if necessary.  This code
  ** can be removed in the future, once all users have upgraded to the
  ** 2015-01-24 or later schema.
  */
  if( !db_table_has_column("repository","mlink","isaux") ){
    db_begin_transaction();
    db_multi_exec(
      "ALTER TABLE %s.mlink ADD COLUMN pmid INTEGER DEFAULT 0;"
      "ALTER TABLE %s.mlink ADD COLUMN isaux BOOLEAN DEFAULT 0;",
      db_name("repository"), db_name("repository")
    );
    db_end_transaction(0);
  }
}

/*
** Flags for the db_find_and_open_repository() function.
................................................................................
      fossil_fatal("use --repository or -R to specify the repository database");
    }else{
      fossil_fatal("specify the repository name as a command-line argument");
    }
  }
}

/*
** Return the name of the database "localdb", "configdb", or "repository".
*/
const char *db_name(const char *zDb){
  assert( fossil_strcmp(zDb,"localdb")==0
       || fossil_strcmp(zDb,"configdb")==0
       || fossil_strcmp(zDb,"repository")==0
       || fossil_strcmp(zDb,"temp")==0 );
  if( fossil_strcmp(zDb, g.zMainDbType)==0 ) zDb = "main";
  return zDb;
}

/*
** Return TRUE if the schema is out-of-date
*/
int db_schema_is_outofdate(void){
  return strcmp(g.zAuxSchema,AUX_SCHEMA_MIN)<0
      || strcmp(g.zAuxSchema,AUX_SCHEMA_MAX)>0;
}

/*
** Return true if the database is writeable
*/
int db_is_writeable(const char *zName){
  return g.db!=0 && !sqlite3_db_readonly(g.db, db_name(zName));
}

/*
** Verify that the repository schema is correct.  If it is not correct,
** issue a fatal error and die.
*/
void db_verify_schema(void){
................................................................................
  if( file_access(zRepo, F_OK) ){
    fossil_fatal("no such file: %s", zRepo);
  }
  if( db_open_local(zRepo)==0 ){
    fossil_fatal("not in a local checkout");
    return;
  }
  db_open_or_attach(zRepo, "test_repo", 0);
  db_lset("repository", blob_str(&repo));
  db_record_repository_filename(blob_str(&repo));
  db_close(1);
}


/*
................................................................................
  while( db.pAllStmt ){
    db_finalize(db.pAllStmt);
  }
  db_end_transaction(1);
  pStmt = 0;
  db_close_config();

  /* If the localdb (the check-out database) is open and if it has
  ** a lot of unused free space, then VACUUM it as we shut down.
  */
  if( g.localOpen && strcmp(db_name("localdb"),"main")==0 ){
    int nFree = db_int(0, "PRAGMA main.freelist_count");
    int nTotal = db_int(0, "PRAGMA main.page_count");
    if( nFree>nTotal/4 ){
      db_multi_exec("VACUUM;");
    }
  }

  if( g.db ){
    int rc;
    sqlite3_wal_checkpoint(g.db, 0);
    rc = sqlite3_close(g.db);
    if( rc==SQLITE_BUSY && reportErrors ){
      while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){
        fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt));
      }
    }
    g.db = 0;
    g.zMainDbType = 0;
  }
  g.repositoryOpen = 0;
  g.localOpen = 0;
  assert( g.dbConfig==0 );
  assert( g.useAttach==0 );
  assert( g.zConfigDbName==0 );
  assert( g.zConfigDbType==0 );
}


/*
** Create a new empty repository database with the given name.
**
** Only the schema is initialized.  The required VAR tables entries
................................................................................
** default users "anonymous", "nobody", "reader", "developer", and their
** associated permissions will be copied.
**
** Options:
**    --template      FILE      copy settings from repository file
**    --admin-user|-A USERNAME  select given USERNAME as admin user
**    --date-override DATETIME  use DATETIME as time of the initial check-in






**
** See also: clone
*/
void create_repository_cmd(void){
  char *zPassword;
  const char *zTemplate;      /* Repository from which to copy settings */
  const char *zDate;          /* Date of the initial check-in */
................................................................................
  if( g.fSqlPrint ){
    for(i=0; i<argc; i++){
      char c = i==argc-1 ? '\n' : ' ';
      fossil_print("%s%c", sqlite3_value_text(argv[i]), c);
    }
  }
}
LOCAL void db_sql_trace(void *notUsed, const char *zSql){






  int n = strlen(zSql);
  fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";");


}

/*
** Implement the user() SQL function.  user() takes no arguments and
** returns the user ID of the current user.
*/
LOCAL void db_sql_user(
................................................................................
  ** same constraint also holds true when restoring the previously swapped
  ** database connection; otherwise, it means that no swap was performed
  ** because the main database connection was already pointing to the config
  ** database.
  */
  if( g.dbConfig ){
    sqlite3 *dbTemp = g.db;
    const char *zTempDbType = g.zMainDbType;
    g.db = g.dbConfig;
    g.zMainDbType = g.zConfigDbType;
    g.dbConfig = dbTemp;
    g.zConfigDbType = zTempDbType;
  }
}

/*
** Try to read a versioned setting string from .fossil-settings/<name>.
**
** Return the text of the string if it is found.  Return NULL if not
................................................................................
  { "th1-docs",         0,              0, 0, 0, "off"                 },
#endif
#ifdef FOSSIL_ENABLE_TH1_HOOKS
  { "th1-hooks",        0,              0, 0, 0, "off"                 },
#endif
  { "th1-setup",        0,             40, 1, 1, ""                    },
  { "th1-uri-regexp",   0,             40, 1, 0, ""                    },

  { "web-browser",      0,             32, 0, 0, ""                    },
  { 0,0,0,0,0,0 }
};

/*
** Look up a control setting by its name.  Return a pointer to the Setting
** object, or NULL if there is no such setting.
................................................................................
**    case-sensitive   If TRUE, the files whose names differ only in case
**                     are considered distinct.  If FALSE files whose names
**                     differ only in case are the same file.  Defaults to
**                     TRUE for unix and FALSE for Cygwin, Mac and Windows.
**
**    clean-glob       The VALUE is a comma or newline-separated list of GLOB
**     (versionable)   patterns specifying files that the "clean" command will
**                     delete without prompting even when the -force flag has
**                     not been used.  Example:  *.a *.lib *.o
**
**    clearsign        When enabled, fossil will attempt to sign all commits
**                     with gpg.  When disabled (the default), commits will
**                     be unsigned.  Default: off
**
**    crnl-glob        A comma or newline-separated list of GLOB patterns for
**     (versionable)   text files in which it is ok to have CR, CR+NL or mixed
................................................................................
**    th1-setup        This is the setup script to be evaluated after creating
**     (versionable)   and initializing the TH1 interpreter.  By default, this
**                     is empty and no extra setup is performed.
**
**    th1-uri-regexp   Specify which URI's are allowed in HTTP requests from
**     (versionable)   TH1 scripts.  If empty, no HTTP requests are allowed
**                     whatsoever.  The default is an empty string.





**
**    web-browser      A shell command used to launch your preferred
**                     web browser when given a URL as an argument.
**                     Defaults to "start" on windows, "open" on Mac,
**                     and "firefox" on Unix.
**
** Options:
**   --global   set or unset the given property globally instead of
**              setting or unsetting it for the open repository only.
**


** See also: configuration
*/
void setting_cmd(void){
  int i;
  int globalFlag = find_option("global","g",0)!=0;

  int unsetFlag = g.argv[1][0]=='u';

  db_open_config(1, 0);
  if( !globalFlag ){
    db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0);
  }
  if( !g.repositoryOpen ){
    globalFlag = 1;
  }
................................................................................
  if( g.argc==2 ){
    for(i=0; aSetting[i].name; i++){
      print_setting(&aSetting[i]);
    }
  }else if( g.argc==3 || g.argc==4 ){
    const char *zName = g.argv[2];
    int n = (int)strlen(zName);
    const Setting *pSetting = db_find_setting(zName, 1);
    if( pSetting==0 ){
      fossil_fatal("no such setting: %s", zName);
    }
    if( globalFlag && fossil_strcmp(pSetting->name, "manifest")==0 ){
      fossil_fatal("cannot set 'manifest' globally");
    }
    if( unsetFlag || g.argc==4 ){
................................................................................
      }else{
        db_set(pSetting->name, g.argv[3], globalFlag);
      }
      if( isManifest && g.localOpen ){
        manifest_to_disk(db_lget_int("checkout", 0));
      }
    }else{




      while( pSetting->name && fossil_strncmp(pSetting->name,zName,n)==0 ){

        print_setting(pSetting);
        pSetting++;
      }
    }
  }else{
    usage("?PROPERTY? ?VALUE? ?-global?");
  }
................................................................................
  }
  rSpan /= 356.24;         /* Convert units to years */
  return sqlite3_mprintf("%.1f years", rSpan);
}

/*
** COMMAND: test-timespan
** 
** Usage: %fossil test-timespan TIMESTAMP
**
** Print the approximate span of time from now to TIMESTAMP.
*/
void test_timespan_cmd(void){
  double rDiff;
  if( g.argc!=3 ) usage("TIMESTAMP");
................................................................................
  fossil_print("Time differences: %s\n", db_timespan_name(rDiff));
  sqlite3_close(g.db);
  g.db = 0;
}

/*
** COMMAND: test-without-rowid
** 
** Usage: %fossil test-without-rowid FILENAME...
**
** Change the Fossil repository FILENAME to make use of the WITHOUT ROWID
** optimization.  FILENAME can also be the ~/.fossil file or a local
** .fslckout or _FOSSIL_ file.
**
** The purpose of this command is for testing the WITHOUT ROWID capabilities
................................................................................
*/
void test_without_rowid(void){
  int i, j;
  Stmt q;
  Blob allSql;
  int dryRun = find_option("dry-run", "n", 0)!=0;
  for(i=2; i<g.argc; i++){
    db_open_or_attach(g.argv[i], "main", 0);
    blob_init(&allSql, "BEGIN;\n", -1);
    db_prepare(&q,
      "SELECT name, sql FROM main.sqlite_master "
      " WHERE type='table' AND sql NOT LIKE '%%WITHOUT ROWID%%'"
      "   AND name IN ('global_config','shun','concealed','config',"
                    "  'plink','tagxref','backlink','vcache');"
    );
................................................................................
** Make sure the adminlog table exists.  Create it if it does not
*/
void create_admin_log_table(void){
  static int once = 0;
  if( once ) return;
  once = 1;
  db_multi_exec(
    "CREATE TABLE IF NOT EXISTS \"%w\".admin_log(\n"
    " id INTEGER PRIMARY KEY,\n"
    " time INTEGER, -- Seconds since 1970\n"
    " page TEXT,    -- path of page\n"
    " who TEXT,     -- User who made the change\n "
    " what TEXT     -- What changed\n"
    ")", db_name("repository")
  );
}

/*
** Write a message into the admin_event table, if admin logging is
** enabled via the admin-log configuration option.
*/







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






|
<
<
<
<

<

<
<
>

<

<




|


|
>

<





<

|



<







 







>
|







 







|

<

<

<
>












|
<
|












|
<
|










|
|













|
|
|







 







|













|











|
|
<







 







<
<
<
<
<
<
<
<
<
<
<
<












|







 







|







 







|
|

|
|
|

|













<




<

<







 







>
>
>
>
>
>







 







|
>
>
>
>
>
>
|

>
>







 







<

<

<







 







>







 







|
|







 







>
>
>
>
>










>
>





>

>







 







|







 







>
>
>
>
|
>







 







|







 







|







 







|







 







|



|

|







932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
...
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009




1010

1011


1012
1013

1014

1015
1016
1017
1018
1019
1020
1021
1022
1023
1024

1025
1026
1027
1028
1029

1030
1031
1032
1033
1034

1035
1036
1037
1038
1039
1040
1041
....
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
....
1103
1104
1105
1106
1107
1108
1109
1110
1111

1112

1113

1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127

1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141

1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
....
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346

1347
1348
1349
1350
1351
1352
1353
....
1395
1396
1397
1398
1399
1400
1401












1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
....
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
....
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539

1540
1541
1542
1543

1544

1545
1546
1547
1548
1549
1550
1551
....
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
....
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
....
2014
2015
2016
2017
2018
2019
2020

2021

2022

2023
2024
2025
2026
2027
2028
2029
....
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
....
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
....
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
....
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
....
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
....
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
....
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
....
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
....
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
  sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
  sqlite3_create_function(
    db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
  );
  sqlite3_create_function(
    db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0
  );
  if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_STMT, db_sql_trace, 0);
  db_add_aux_functions(db);
  re_add_sql_func(db);  /* The REGEXP operator */
  foci_register(db);    /* The "files_of_checkin" virtual table */
  sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
  return db;
}

................................................................................
void db_attach(const char *zDbName, const char *zLabel){
  Blob key;
  db_encryption_key(zDbName, &key);
  db_multi_exec("ATTACH DATABASE %Q AS %Q KEY %Q",
                zDbName, zLabel, blob_str(&key));
  blob_reset(&key);
}

/*
** Change the schema name of the "main" database to zLabel.
** zLabel must be a static string that is unchanged for the life of
** the database connection.
**
** After calling this routine, db_database_slot(zLabel) should
** return 0.
*/
void db_set_main_schemaname(sqlite3 *db, const char *zLabel){
  if( sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, zLabel) ){
    fossil_fatal("Fossil requires a version of SQLite that supports the "
                 "SQLITE_DBCONFIG_MAINDBNAME interface.");
  }
}

/*
** Return the slot number for database zLabel.  The first database
** opened is slot 0.  The "temp" database is slot 1.  Attached databases
** are slots 2 and higher.
**
** Return -1 if zLabel does not match any open database.
*/
int db_database_slot(const char *zLabel){
  int iSlot = -1;
  Stmt q;
  if( g.db==0 ) return iSlot;
  db_prepare(&q, "PRAGMA database_list");
  while( db_step(&q)==SQLITE_ROW ){
    if( fossil_strcmp(db_column_text(&q,1),zLabel)==0 ){
      iSlot = db_column_int(&q, 0);
      break;
    }
  }
  db_finalize(&q);
  return iSlot;
}

/*
** zDbName is the name of a database file.  If no other database
** file is open, then open this one.  If another database file is
** already open, then attach zDbName using the name zLabel.
*/
void db_open_or_attach(const char *zDbName, const char *zLabel){




  if( !g.db ){

    g.db = db_open(zDbName);


    db_set_main_schemaname(g.db, zLabel);
  }else{

    db_attach(zDbName, zLabel);

  }
}

/*
** Close the per-user database file in ~/.fossil
*/
void db_close_config(){
  int iSlot = db_database_slot("configdb");
  if( iSlot>0 ){
    db_detach("configdb");

    g.zConfigDbName = 0;
  }else if( g.dbConfig ){
    sqlite3_wal_checkpoint(g.dbConfig, 0);
    sqlite3_close(g.dbConfig);
    g.dbConfig = 0;

    g.zConfigDbName = 0;
  }else if( g.db && 0==iSlot ){
    sqlite3_wal_checkpoint(g.db, 0);
    sqlite3_close(g.db);
    g.db = 0;

    g.zConfigDbName = 0;
  }
}

/*
** Open the user database in "~/.fossil".  Create the database anew if
** it does not already exist.
................................................................................
** connection so that we can join between the various databases.  In that
** case, invoke this routine with useAttach as 1.
*/
int db_open_config(int useAttach, int isOptional){
  char *zDbName;
  char *zHome;
  if( g.zConfigDbName ){
    int alreadyAttached = db_database_slot("configdb")>0;
    if( useAttach==alreadyAttached ) return 1; /* Already open. */
    db_close_config();
  }
  zHome = fossil_getenv("FOSSIL_HOME");
#if defined(_WIN32) || defined(__CYGWIN__)
  if( zHome==0 ){
    zHome = fossil_getenv("LOCALAPPDATA");
    if( zHome==0 ){
................................................................................
    db_init_database(zDbName, zConfigSchema, (char*)0);
  }
  if( file_access(zDbName, W_OK) ){
    if( isOptional ) return 0;
    fossil_fatal("configuration file %s must be writeable", zDbName);
  }
  if( useAttach ){
    db_open_or_attach(zDbName, "configdb");
    g.dbConfig = 0;

  }else{

    g.dbConfig = db_open(zDbName);

    db_set_main_schemaname(g.dbConfig, "configdb");
  }
  g.zConfigDbName = zDbName;
  return 1;
}

/*
** Return TRUE if zTable exists.
*/
int db_table_exists(
  const char *zDb,      /* One of: NULL, "configdb", "localdb", "repository" */
  const char *zTable    /* Name of table */
){
  return sqlite3_table_column_metadata(g.db, zDb, zTable, 0,

                                       0, 0, 0, 0, 0)==SQLITE_OK;
}

/*
** Return TRUE if zTable exists and contains column zColumn.
** Return FALSE if zTable does not exist or if zTable exists
** but lacks zColumn.
*/
int db_table_has_column(
  const char *zDb,       /* One of: NULL, "config", "localdb", "repository" */
  const char *zTable,    /* Name of table */
  const char *zColumn    /* Name of column in table */
){
  return sqlite3_table_column_metadata(g.db, zDb, zTable, zColumn,

                                       0, 0, 0, 0, 0)==SQLITE_OK;
}

/*
** Returns TRUE if zTable exists in the local database but lacks column
** zColumn
*/
static int db_local_table_exists_but_lacks_column(
  const char *zTable,
  const char *zColumn
){
  return db_table_exists("localdb", zTable)
      && !db_table_has_column("localdb", zTable, zColumn);
}

/*
** If zDbName is a valid local database file, open it and return
** true.  If it is not a valid local database file, return 0.
*/
static int isValidLocalDb(const char *zDbName){
  i64 lsize;
  char *zVFileDef;

  if( file_access(zDbName, F_OK) ) return 0;
  lsize = file_size(zDbName);
  if( lsize%1024!=0 || lsize<4096 ) return 0;
  db_open_or_attach(zDbName, "localdb");
  zVFileDef = db_text(0, "SELECT sql FROM localdb.sqlite_master"
                         " WHERE name=='vfile'");
  if( zVFileDef==0 ) return 0;

  /* If the "isexe" column is missing from the vfile table, then
  ** add it now.   This code added on 2010-03-06.  After all users have
  ** upgraded, this code can be safely deleted.
  */
  if( sqlite3_strglob("* isexe *", zVFileDef)!=0 ){
................................................................................
#ifdef FOSSIL_ENABLE_JSON
      g.json.resultCode = FSL_JSON_E_DB_NOT_VALID;
#endif
      fossil_panic("not a valid repository: %s", zDbName);
    }
  }
  g.zRepositoryName = mprintf("%s", zDbName);
  db_open_or_attach(g.zRepositoryName, "repository");
  g.repositoryOpen = 1;
  /* Cache "allow-symlinks" option, because we'll need it on every stat call */
  g.allowSymlinks = db_get_boolean("allow-symlinks",
                                   db_allow_symlinks_by_default());
  g.zAuxSchema = db_get("aux-schema","");

  /* Verify that the PLINK table has a new column added by the
  ** 2014-11-28 schema change.  Create it if necessary.  This code
  ** can be removed in the future, once all users have upgraded to the
  ** 2014-11-28 or later schema.
  */
  if( !db_table_has_column("repository","plink","baseid") ){
    db_multi_exec(
      "ALTER TABLE repository.plink ADD COLUMN baseid;"
    );
  }

  /* Verify that the MLINK table has the newer columns added by the
  ** 2015-01-24 schema change.  Create them if necessary.  This code
  ** can be removed in the future, once all users have upgraded to the
  ** 2015-01-24 or later schema.
  */
  if( !db_table_has_column("repository","mlink","isaux") ){
    db_begin_transaction();
    db_multi_exec(
      "ALTER TABLE repository.mlink ADD COLUMN pmid INTEGER DEFAULT 0;"
      "ALTER TABLE repository.mlink ADD COLUMN isaux BOOLEAN DEFAULT 0;"

    );
    db_end_transaction(0);
  }
}

/*
** Flags for the db_find_and_open_repository() function.
................................................................................
      fossil_fatal("use --repository or -R to specify the repository database");
    }else{
      fossil_fatal("specify the repository name as a command-line argument");
    }
  }
}













/*
** Return TRUE if the schema is out-of-date
*/
int db_schema_is_outofdate(void){
  return strcmp(g.zAuxSchema,AUX_SCHEMA_MIN)<0
      || strcmp(g.zAuxSchema,AUX_SCHEMA_MAX)>0;
}

/*
** Return true if the database is writeable
*/
int db_is_writeable(const char *zName){
  return g.db!=0 && !sqlite3_db_readonly(g.db, zName);
}

/*
** Verify that the repository schema is correct.  If it is not correct,
** issue a fatal error and die.
*/
void db_verify_schema(void){
................................................................................
  if( file_access(zRepo, F_OK) ){
    fossil_fatal("no such file: %s", zRepo);
  }
  if( db_open_local(zRepo)==0 ){
    fossil_fatal("not in a local checkout");
    return;
  }
  db_open_or_attach(zRepo, "test_repo");
  db_lset("repository", blob_str(&repo));
  db_record_repository_filename(blob_str(&repo));
  db_close(1);
}


/*
................................................................................
  while( db.pAllStmt ){
    db_finalize(db.pAllStmt);
  }
  db_end_transaction(1);
  pStmt = 0;
  db_close_config();

  /* If the localdb has a lot of unused free space,
  ** then VACUUM it as we shut down.
  */
  if( db_database_slot("localdb")>=0 ){
    int nFree = db_int(0, "PRAGMA localdb.freelist_count");
    int nTotal = db_int(0, "PRAGMA localdb.page_count");
    if( nFree>nTotal/4 ){
      db_multi_exec("VACUUM localdb;");
    }
  }

  if( g.db ){
    int rc;
    sqlite3_wal_checkpoint(g.db, 0);
    rc = sqlite3_close(g.db);
    if( rc==SQLITE_BUSY && reportErrors ){
      while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){
        fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt));
      }
    }
    g.db = 0;

  }
  g.repositoryOpen = 0;
  g.localOpen = 0;
  assert( g.dbConfig==0 );

  assert( g.zConfigDbName==0 );

}


/*
** Create a new empty repository database with the given name.
**
** Only the schema is initialized.  The required VAR tables entries
................................................................................
** default users "anonymous", "nobody", "reader", "developer", and their
** associated permissions will be copied.
**
** Options:
**    --template      FILE      copy settings from repository file
**    --admin-user|-A USERNAME  select given USERNAME as admin user
**    --date-override DATETIME  use DATETIME as time of the initial check-in
**
** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
** year-month-day form, it may be truncated, the "T" may be replaced by
** a space, and it may also name a timezone offset from UTC as "-HH:MM"
** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z"
** means UTC.
**
** See also: clone
*/
void create_repository_cmd(void){
  char *zPassword;
  const char *zTemplate;      /* Repository from which to copy settings */
  const char *zDate;          /* Date of the initial check-in */
................................................................................
  if( g.fSqlPrint ){
    for(i=0; i<argc; i++){
      char c = i==argc-1 ? '\n' : ' ';
      fossil_print("%s%c", sqlite3_value_text(argv[i]), c);
    }
  }
}
LOCAL int db_sql_trace(unsigned m, void *notUsed, void *pP, void *pX){
  sqlite3_stmt *pStmt = (sqlite3_stmt*)pP;
  char *zSql;
  int n;
  const char *zArg = (const char*)pX;
  if( zArg[0]=='-' ) return 0;
  zSql = sqlite3_expanded_sql(pStmt);
  n = (int)strlen(zSql);
  fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";");
  sqlite3_free(zSql);
  return 0;
}

/*
** Implement the user() SQL function.  user() takes no arguments and
** returns the user ID of the current user.
*/
LOCAL void db_sql_user(
................................................................................
  ** same constraint also holds true when restoring the previously swapped
  ** database connection; otherwise, it means that no swap was performed
  ** because the main database connection was already pointing to the config
  ** database.
  */
  if( g.dbConfig ){
    sqlite3 *dbTemp = g.db;

    g.db = g.dbConfig;

    g.dbConfig = dbTemp;

  }
}

/*
** Try to read a versioned setting string from .fossil-settings/<name>.
**
** Return the text of the string if it is found.  Return NULL if not
................................................................................
  { "th1-docs",         0,              0, 0, 0, "off"                 },
#endif
#ifdef FOSSIL_ENABLE_TH1_HOOKS
  { "th1-hooks",        0,              0, 0, 0, "off"                 },
#endif
  { "th1-setup",        0,             40, 1, 1, ""                    },
  { "th1-uri-regexp",   0,             40, 1, 0, ""                    },
  { "uv-sync",          0,              0, 0, 0, "off"                 },
  { "web-browser",      0,             32, 0, 0, ""                    },
  { 0,0,0,0,0,0 }
};

/*
** Look up a control setting by its name.  Return a pointer to the Setting
** object, or NULL if there is no such setting.
................................................................................
**    case-sensitive   If TRUE, the files whose names differ only in case
**                     are considered distinct.  If FALSE files whose names
**                     differ only in case are the same file.  Defaults to
**                     TRUE for unix and FALSE for Cygwin, Mac and Windows.
**
**    clean-glob       The VALUE is a comma or newline-separated list of GLOB
**     (versionable)   patterns specifying files that the "clean" command will
**                     delete without prompting or allowing undo.
**                     Example: *.a,*.lib,*.o
**
**    clearsign        When enabled, fossil will attempt to sign all commits
**                     with gpg.  When disabled (the default), commits will
**                     be unsigned.  Default: off
**
**    crnl-glob        A comma or newline-separated list of GLOB patterns for
**     (versionable)   text files in which it is ok to have CR, CR+NL or mixed
................................................................................
**    th1-setup        This is the setup script to be evaluated after creating
**     (versionable)   and initializing the TH1 interpreter.  By default, this
**                     is empty and no extra setup is performed.
**
**    th1-uri-regexp   Specify which URI's are allowed in HTTP requests from
**     (versionable)   TH1 scripts.  If empty, no HTTP requests are allowed
**                     whatsoever.  The default is an empty string.
**
**    uv-sync          If true, automatically send unversioned files as part
**                     of a "fossil clone" or "fossil sync" command.  The
**                     default is false, in which case the -u option is
**                     needed to clone or sync unversioned files.
**
**    web-browser      A shell command used to launch your preferred
**                     web browser when given a URL as an argument.
**                     Defaults to "start" on windows, "open" on Mac,
**                     and "firefox" on Unix.
**
** Options:
**   --global   set or unset the given property globally instead of
**              setting or unsetting it for the open repository only.
**
**   --exact    only consider exact name matches.
**
** See also: configuration
*/
void setting_cmd(void){
  int i;
  int globalFlag = find_option("global","g",0)!=0;
  int exactFlag = find_option("exact",0,0)!=0;
  int unsetFlag = g.argv[1][0]=='u';
  verify_all_options();
  db_open_config(1, 0);
  if( !globalFlag ){
    db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0);
  }
  if( !g.repositoryOpen ){
    globalFlag = 1;
  }
................................................................................
  if( g.argc==2 ){
    for(i=0; aSetting[i].name; i++){
      print_setting(&aSetting[i]);
    }
  }else if( g.argc==3 || g.argc==4 ){
    const char *zName = g.argv[2];
    int n = (int)strlen(zName);
    const Setting *pSetting = db_find_setting(zName, !exactFlag);
    if( pSetting==0 ){
      fossil_fatal("no such setting: %s", zName);
    }
    if( globalFlag && fossil_strcmp(pSetting->name, "manifest")==0 ){
      fossil_fatal("cannot set 'manifest' globally");
    }
    if( unsetFlag || g.argc==4 ){
................................................................................
      }else{
        db_set(pSetting->name, g.argv[3], globalFlag);
      }
      if( isManifest && g.localOpen ){
        manifest_to_disk(db_lget_int("checkout", 0));
      }
    }else{
      while( pSetting->name ){
        if( exactFlag ){
          if( fossil_strcmp(pSetting->name,zName)!=0 ) break;
        }else{
          if( fossil_strncmp(pSetting->name,zName,n)!=0 ) break;
        }
        print_setting(pSetting);
        pSetting++;
      }
    }
  }else{
    usage("?PROPERTY? ?VALUE? ?-global?");
  }
................................................................................
  }
  rSpan /= 356.24;         /* Convert units to years */
  return sqlite3_mprintf("%.1f years", rSpan);
}

/*
** COMMAND: test-timespan
**
** Usage: %fossil test-timespan TIMESTAMP
**
** Print the approximate span of time from now to TIMESTAMP.
*/
void test_timespan_cmd(void){
  double rDiff;
  if( g.argc!=3 ) usage("TIMESTAMP");
................................................................................
  fossil_print("Time differences: %s\n", db_timespan_name(rDiff));
  sqlite3_close(g.db);
  g.db = 0;
}

/*
** COMMAND: test-without-rowid
**
** Usage: %fossil test-without-rowid FILENAME...
**
** Change the Fossil repository FILENAME to make use of the WITHOUT ROWID
** optimization.  FILENAME can also be the ~/.fossil file or a local
** .fslckout or _FOSSIL_ file.
**
** The purpose of this command is for testing the WITHOUT ROWID capabilities
................................................................................
*/
void test_without_rowid(void){
  int i, j;
  Stmt q;
  Blob allSql;
  int dryRun = find_option("dry-run", "n", 0)!=0;
  for(i=2; i<g.argc; i++){
    db_open_or_attach(g.argv[i], "main");
    blob_init(&allSql, "BEGIN;\n", -1);
    db_prepare(&q,
      "SELECT name, sql FROM main.sqlite_master "
      " WHERE type='table' AND sql NOT LIKE '%%WITHOUT ROWID%%'"
      "   AND name IN ('global_config','shun','concealed','config',"
                    "  'plink','tagxref','backlink','vcache');"
    );
................................................................................
** Make sure the adminlog table exists.  Create it if it does not
*/
void create_admin_log_table(void){
  static int once = 0;
  if( once ) return;
  once = 1;
  db_multi_exec(
    "CREATE TABLE IF NOT EXISTS repository.admin_log(\n"
    " id INTEGER PRIMARY KEY,\n"
    " time INTEGER, -- Seconds since 1970\n"
    " page TEXT,    -- path of page\n"
    " who TEXT,     -- User who made the change\n"
    " what TEXT     -- What changed\n"
    ")"
  );
}

/*
** Write a message into the admin_event table, if admin logging is
** enabled via the admin-log configuration option.
*/

Changes to src/delta.c.

243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
      z += 4;
    }
#elif defined(_MSC_VER) && _MSC_VER>=1300
    while( z<zEnd ){
      sum += _byteswap_ulong(*(unsigned*)z);
      z += 4;
    }
#else    
    unsigned sum0 = 0;
    unsigned sum1 = 0;
    unsigned sum2 = 0;
    while(N >= 16){
      sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]);
      sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]);
      sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]);







|







243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
      z += 4;
    }
#elif defined(_MSC_VER) && _MSC_VER>=1300
    while( z<zEnd ){
      sum += _byteswap_ulong(*(unsigned*)z);
      z += 4;
    }
#else
    unsigned sum0 = 0;
    unsigned sum1 = 0;
    unsigned sum2 = 0;
    while(N >= 16){
      sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]);
      sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]);
      sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]);

Changes to src/descendants.c.

194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
                                            " generation INTEGER PRIMARY KEY);"
    "DELETE FROM ancestor;"
    "WITH RECURSIVE g(x,i) AS ("
    "  VALUES(%d,1)"
    "  UNION ALL"
    "  SELECT plink.pid, g.i+1 FROM plink, g"
    "   WHERE plink.cid=g.x AND plink.isprim)"
    "INSERT INTO ancestor(rid,generation) SELECT x,i FROM g;", 
    rid
  );
}

/*
** Compute the "mtime" of the file given whose blob.rid is "fid" that
** is part of check-in "vid".  The mtime will be the mtime on vid or







|







194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
                                            " generation INTEGER PRIMARY KEY);"
    "DELETE FROM ancestor;"
    "WITH RECURSIVE g(x,i) AS ("
    "  VALUES(%d,1)"
    "  UNION ALL"
    "  SELECT plink.pid, g.i+1 FROM plink, g"
    "   WHERE plink.cid=g.x AND plink.isprim)"
    "INSERT INTO ancestor(rid,generation) SELECT x,i FROM g;",
    rid
  );
}

/*
** Compute the "mtime" of the file given whose blob.rid is "fid" that
** is part of check-in "vid".  The mtime will be the mtime on vid or

Changes to src/diff.c.

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
...
129
130
131
132
133
134
135
136





137
138
139
140
141
142
143
....
2088
2089
2090
2091
2092
2093
2094
2095





2096
2097
2098
2099
2100
2101
2102
....
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
....
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
....
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
....
2441
2442
2443
2444
2445
2446
2447
2448

2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
  int *aEdit;        /* Array of copy/delete/insert triples */
  int nEdit;         /* Number of integers (3x num of triples) in aEdit[] */
  int nEditAlloc;    /* Space allocated for aEdit[] */
  DLine *aFrom;      /* File on left side of the diff */
  int nFrom;         /* Number of lines in aFrom[] */
  DLine *aTo;        /* File on right side of the diff */
  int nTo;           /* Number of lines in aTo[] */
  int (*same_fn)(const DLine *, const DLine *); /* Function to be used for comparing */
};

/*
** Return an array of DLine objects containing a pointer to the
** start of each line and a hash of that line.  The lower
** bits of the hash store the length of each line.
**
................................................................................
**
** Return 0 if the file is binary or contains a line that is
** too long.
**
** Profiling show that in most cases this routine consumes the bulk of
** the CPU time on a diff.
*/
static DLine *break_into_lines(const char *z, int n, int *pnLine, u64 diffFlags){





  int nLine, i, j, k, s, x;
  unsigned int h, h2;
  DLine *a;

  /* Count the number of lines.  Allocate space to hold
  ** the returned array.
  */
................................................................................
}

/*
** The input pParent is the next most recent ancestor of the file
** being annotated.  Do another step of the annotation.  Return true
** if additional annotation is required.
*/
static int annotation_step(Annotator *p, Blob *pParent, int iVers, u64 diffFlags){





  int i, j;
  int lnTo;

  /* Prepare the parent file to be diffed */
  p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
                                &p->c.nFrom, diffFlags);
  if( p->c.aFrom==0 ){
................................................................................

  /* Return no errors */
  return 0;
}


/* Annotation flags (any DIFF flag can be used as Annotation flag as well) */
#define ANN_FILE_VERS   (((u64)0x20)<<32) /* Show file vers rather than commit vers */
#define ANN_FILE_ANCEST (((u64)0x40)<<32) /* Prefer check-ins in the ANCESTOR table */

/*
** Compute a complete annotation on a file.  The file is identified
** by its filename number (filename.fnid) and check-in (mlink.mid).
*/
static void annotate_file(
  Annotator *p,        /* The annotator */
................................................................................
  unsigned clr1, clr2, clr;
  int bBlame = g.zPath[0]!='a';/* True for BLAME output.  False for ANNOTATE. */

  /* Gather query parameters */
  showLog = atoi(PD("log","1"));
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  if( exclude_spiders("annotate") ) return;
  load_control();
  mid = name_to_typed_rid(PD("checkin","0"),"ci");
  zFilename = P("filename");
  fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
  if( mid==0 || fnid==0 ){ fossil_redirect_home(); }
  iLimit = atoi(PD("limit","20"));
  if( P("filevers") ) annFlags |= ANN_FILE_VERS;
................................................................................
                         p->zFUuid,p[-1].zFUuid);
           @ %z(zLink)[diff-to-previous]</a>
        }
      }
#endif
    }
    @ </ol>
    @ <hr>
  }
  if( !ann.bLimit ){
    @ <h2>Origin for each line in
    @ %z(href("%R/finfo?name=%h&ci=%!S", zFilename, zCI))%h(zFilename)</a>
    @ from check-in %z(href("%R/info/%!S",zCI))%S(zCI)</a>:</h2>
    iLimit = ann.nVers+10;
  }else{
................................................................................
**
** Output the text of a file with markings to show when each line of
** the file was last modified.  The "annotate" command shows line numbers
** and omits the username.  The "blame" and "praise" commands show the user
** who made each check-in and omits the line number.
**
** Options:
**   --filevers                 Show file version numbers rather than check-in versions

**   -l|--log                   List all versions analyzed
**   -n|--limit N               Only look backwards in time by N versions
**   -w|--ignore-all-space      Ignore white space when comparing lines
**   -Z|--ignore-trailing-space Ignore whitespace at line end
**
** See also: info, finfo, timeline
*/
void annotate_cmd(void){
  int fnid;         /* Filename ID */
  int fid;          /* File instance ID */
  int mid;          /* Manifest where file was checked in */







|







 







|
>
>
>
>
>







 







|
>
>
>
>
>







 







|
|







 







|







 







|







 







|
>
|
|
|
|







111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
....
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
....
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
....
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
....
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
....
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
  int *aEdit;        /* Array of copy/delete/insert triples */
  int nEdit;         /* Number of integers (3x num of triples) in aEdit[] */
  int nEditAlloc;    /* Space allocated for aEdit[] */
  DLine *aFrom;      /* File on left side of the diff */
  int nFrom;         /* Number of lines in aFrom[] */
  DLine *aTo;        /* File on right side of the diff */
  int nTo;           /* Number of lines in aTo[] */
  int (*same_fn)(const DLine*,const DLine*); /* comparison function */
};

/*
** Return an array of DLine objects containing a pointer to the
** start of each line and a hash of that line.  The lower
** bits of the hash store the length of each line.
**
................................................................................
**
** Return 0 if the file is binary or contains a line that is
** too long.
**
** Profiling show that in most cases this routine consumes the bulk of
** the CPU time on a diff.
*/
static DLine *break_into_lines(
  const char *z,
  int n,
  int *pnLine,
  u64 diffFlags
){
  int nLine, i, j, k, s, x;
  unsigned int h, h2;
  DLine *a;

  /* Count the number of lines.  Allocate space to hold
  ** the returned array.
  */
................................................................................
}

/*
** The input pParent is the next most recent ancestor of the file
** being annotated.  Do another step of the annotation.  Return true
** if additional annotation is required.
*/
static int annotation_step(
  Annotator *p,
  Blob *pParent,
  int iVers,
  u64 diffFlags
){
  int i, j;
  int lnTo;

  /* Prepare the parent file to be diffed */
  p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
                                &p->c.nFrom, diffFlags);
  if( p->c.aFrom==0 ){
................................................................................

  /* Return no errors */
  return 0;
}


/* Annotation flags (any DIFF flag can be used as Annotation flag as well) */
#define ANN_FILE_VERS   (((u64)0x20)<<32) /* File vers not commit vers */
#define ANN_FILE_ANCEST (((u64)0x40)<<32) /* Prefer checkins in the ANCESTOR */

/*
** Compute a complete annotation on a file.  The file is identified
** by its filename number (filename.fnid) and check-in (mlink.mid).
*/
static void annotate_file(
  Annotator *p,        /* The annotator */
................................................................................
  unsigned clr1, clr2, clr;
  int bBlame = g.zPath[0]!='a';/* True for BLAME output.  False for ANNOTATE. */

  /* Gather query parameters */
  showLog = atoi(PD("log","1"));
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  if( exclude_spiders() ) return;
  load_control();
  mid = name_to_typed_rid(PD("checkin","0"),"ci");
  zFilename = P("filename");
  fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
  if( mid==0 || fnid==0 ){ fossil_redirect_home(); }
  iLimit = atoi(PD("limit","20"));
  if( P("filevers") ) annFlags |= ANN_FILE_VERS;
................................................................................
                         p->zFUuid,p[-1].zFUuid);
           @ %z(zLink)[diff-to-previous]</a>
        }
      }
#endif
    }
    @ </ol>
    @ <hr />
  }
  if( !ann.bLimit ){
    @ <h2>Origin for each line in
    @ %z(href("%R/finfo?name=%h&ci=%!S", zFilename, zCI))%h(zFilename)</a>
    @ from check-in %z(href("%R/info/%!S",zCI))%S(zCI)</a>:</h2>
    iLimit = ann.nVers+10;
  }else{
................................................................................
**
** Output the text of a file with markings to show when each line of
** the file was last modified.  The "annotate" command shows line numbers
** and omits the username.  The "blame" and "praise" commands show the user
** who made each check-in and omits the line number.
**
** Options:
**   --filevers                  Show file version numbers rather than
**                               check-in versions
**   -l|--log                    List all versions analyzed
**   -n|--limit N                Only look backwards in time by N versions
**   -w|--ignore-all-space       Ignore white space when comparing lines
**   -Z|--ignore-trailing-space  Ignore whitespace at line end
**
** See also: info, finfo, timeline
*/
void annotate_cmd(void){
  int fnid;         /* Filename ID */
  int fid;          /* File instance ID */
  int mid;          /* Manifest where file was checked in */

Changes to src/diff.tcl.

225
226
227
228
229
230
231





232



233
234
235


236
237
238
239
240
241
242
243
244
...
257
258
259
260
261
262
263




264
265
266
267
268
269
270
...
356
357
358
359
360
361
362

363
364
365
366
367
368
369

370
371
372
373
374
375
376
377


































































378
379
380

381
382
383
384
385
386
387
388
389
390
  }
  enableSync y
}

wm withdraw .
wm title . $CFG(TITLE)
wm iconname . $CFG(TITLE)





bind . <q> exit



bind . <Destroy> {after 0 exit}
bind . <Tab> {cycleDiffs; break}
bind . <<PrevWindow>> {cycleDiffs 1; break}


bind . <Return> {
  event generate .bb.files <1>
  event generate .bb.files <ButtonRelease-1>
  break
}
foreach {key axis args} {
  Up    y {scroll -5 units}
  k     y {scroll -5 units}
  Down  y {scroll 5 units}
................................................................................
} {
  bind . <$key> "scroll-$axis $args; break"
  bind . <Shift-$key> continue
}

frame .bb
::ttk::menubutton .bb.files -text "Files"




toplevel .wfiles
wm withdraw .wfiles
update idletasks
wm transient .wfiles .
wm overrideredirect .wfiles 1
listbox .wfiles.lb -width 0 -height $CFG(LB_HEIGHT) -activestyle none \
  -yscroll {.wfiles.sb set}
................................................................................
  if {$x(-column)==1} {
    grid config .lnB -column 0
    grid config .txtB -column 1
    .txtB tag config add -background $CFG(RM_BG)
    grid config .lnA -column 3
    grid config .txtA -column 4
    .txtA tag config rm -background $CFG(ADD_BG)

  } else {
    grid config .lnA -column 0
    grid config .txtA -column 1
    .txtA tag config rm -background $CFG(RM_BG)
    grid config .lnB -column 3
    grid config .txtB -column 4
    .txtB tag config add -background $CFG(ADD_BG)

  }
  .mkr config -state normal
  set clt [.mkr search -all < 1.0 end]
  set cgt [.mkr search -all > 1.0 end]
  foreach c $clt {.mkr replace $c "$c +1 chars" >}
  foreach c $cgt {.mkr replace $c "$c +1 chars" <}
  .mkr config -state disabled
}


































































::ttk::button .bb.quit -text {Quit} -command exit
::ttk::button .bb.invert -text {Invert} -command invertDiff
::ttk::button .bb.save -text {Save As...} -command saveDiff

pack .bb.quit .bb.invert -side left
if {$fossilcmd!=""} {pack .bb.save -side left}
pack .bb.files -side left
grid rowconfigure . 1 -weight 1
grid columnconfigure . 1 -weight 1
grid columnconfigure . 4 -weight 1
grid .bb -row 0 -columnspan 6
eval grid [cols] -row 1 -sticky nsew
grid .sby -row 1 -column 5 -sticky ns
grid .sbxA -row 2 -columnspan 2 -sticky ew







>
>
>
>
>
|
>
>
>



>
>

|







 







>
>
>
>







 







>







>








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



>


|







225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
...
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
...
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
  }
  enableSync y
}

wm withdraw .
wm title . $CFG(TITLE)
wm iconname . $CFG(TITLE)
# Keystroke bindings for on the top-level window for navigation and
# control also fire when those same keystrokes are pressed in the
# Search entry box.  Disable them, to prevent the diff screen from
# disappearing abruptly and unexpectedly when searching for "q".
#
# bind . <q> exit
# bind . <p> {catch searchPrev; break}
# bind . <n> {catch searchNext; break}
# bind . <Escape><Escape> exit
bind . <Destroy> {after 0 exit}
bind . <Tab> {cycleDiffs; break}
bind . <<PrevWindow>> {cycleDiffs 1; break}
bind . <Control-f> {searchOnOff; break}
bind . <Control-g> {catch searchNext; break}
bind . <Return> {
  event generate bb.files <1>
  event generate .bb.files <ButtonRelease-1>
  break
}
foreach {key axis args} {
  Up    y {scroll -5 units}
  k     y {scroll -5 units}
  Down  y {scroll 5 units}
................................................................................
} {
  bind . <$key> "scroll-$axis $args; break"
  bind . <Shift-$key> continue
}

frame .bb
::ttk::menubutton .bb.files -text "Files"
if {[tk windowingsystem] eq "win32"} {
  ::ttk::style theme use winnative
  .bb.files configure -padding {20 1 10 2}
}
toplevel .wfiles
wm withdraw .wfiles
update idletasks
wm transient .wfiles .
wm overrideredirect .wfiles 1
listbox .wfiles.lb -width 0 -height $CFG(LB_HEIGHT) -activestyle none \
  -yscroll {.wfiles.sb set}
................................................................................
  if {$x(-column)==1} {
    grid config .lnB -column 0
    grid config .txtB -column 1
    .txtB tag config add -background $CFG(RM_BG)
    grid config .lnA -column 3
    grid config .txtA -column 4
    .txtA tag config rm -background $CFG(ADD_BG)
    .bb.invert config -text Uninvert
  } else {
    grid config .lnA -column 0
    grid config .txtA -column 1
    .txtA tag config rm -background $CFG(RM_BG)
    grid config .lnB -column 3
    grid config .txtB -column 4
    .txtB tag config add -background $CFG(ADD_BG)
    .bb.invert config -text Invert
  }
  .mkr config -state normal
  set clt [.mkr search -all < 1.0 end]
  set cgt [.mkr search -all > 1.0 end]
  foreach c $clt {.mkr replace $c "$c +1 chars" >}
  foreach c $cgt {.mkr replace $c "$c +1 chars" <}
  .mkr config -state disabled
}
proc searchOnOff {} {
  if {[info exists ::search]} {
    unset ::search
    .txtA tag remove search 1.0 end
    .txtB tag remove search 1.0 end
    pack forget .bb.sframe
  } else {
    set ::search .txtA
    if {![winfo exists .bb.sframe]} {
      frame .bb.sframe
      ::ttk::entry .bb.sframe.e -width 10
      pack .bb.sframe.e -side left -fill y -expand 1
      bind .bb.sframe.e <Return> {searchNext; break}
      ::ttk::button .bb.sframe.nx -text \u2193 -width 1 -command searchNext
      ::ttk::button .bb.sframe.pv -text \u2191 -width 1 -command searchPrev
      tk_optionMenu .bb.sframe.typ ::search_type \
           Exact {No Case} {RegExp} {Whole Word}
      .bb.sframe.typ config -width 10
      set ::search_type Exact
      pack .bb.sframe.nx .bb.sframe.pv .bb.sframe.typ -side left
    }
    pack .bb.sframe -side left
    after idle {focus .bb.sframe.e}
  }
}
proc searchNext {} {searchStep -forwards +1 1.0 end}
proc searchPrev {} {searchStep -backwards -1 end 1.0}
proc searchStep {direction incr start stop} {
  set pattern [.bb.sframe.e get]
  if {$pattern==""} return
  set count 0
  set w $::search
  if {"$w"==".txtA"} {set other .txtB} {set other .txtA}
  if {[lsearch [$w mark names] search]<0} {
    $w mark set search $start
  }
  switch $::search_type {
    Exact        {set st -exact}
    {No Case}    {set st -nocase}
    {RegExp}     {set st -regexp}
    {Whole Word} {set st -regexp; set pattern \\y$pattern\\y}
  }
  set idx [$w search -count count $direction $st -- \
              $pattern "search $incr chars" $stop]
  if {"$idx"==""} {
    set idx [$other search -count count $direction $st -- $pattern $start $stop]
    if {"$idx"!=""} {
      set this $w
      set w $other
      set other $this
    } else {
      set idx [$w search -count count $direction $st -- $pattern $start $stop]
    }
  }
  $w tag remove search 1.0 end
  $w mark unset search
  $other tag remove search 1.0 end
  $other mark unset search
  if {"$idx"!=""} {
    $w mark set search $idx
    $w yview -pickplace $idx
    $w tag add search search "$idx +$count chars"
    $w tag config search -background {#fcc000}
  }
  set ::search $w
}
::ttk::button .bb.quit -text {Quit} -command exit
::ttk::button .bb.invert -text {Invert} -command invertDiff
::ttk::button .bb.save -text {Save As...} -command saveDiff
::ttk::button .bb.search -text {Search} -command searchOnOff
pack .bb.quit .bb.invert -side left
if {$fossilcmd!=""} {pack .bb.save -side left}
pack .bb.files .bb.search -side left
grid rowconfigure . 1 -weight 1
grid columnconfigure . 1 -weight 1
grid columnconfigure . 4 -weight 1
grid .bb -row 0 -columnspan 6
eval grid [cols] -row 1 -sticky nsew
grid .sby -row 1 -column 5 -sticky ns
grid .sbxA -row 2 -columnspan 2 -sticky ew

Added src/dispatch.c.





































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
/*
** Copyright (c) 2016 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to map command names (ex: "help", "commit",
** "diff") or webpage names (ex: "/timeline", "/search") into the functions
** that implement those commands and web pages and their associated help
** text.
*/
#include "config.h"
#include <assert.h>
#include "dispatch.h"


#if INTERFACE
/*
** An instance of this object defines everything we need to know about an
** individual command or webpage.
*/
struct CmdOrPage {
  const char *zName;       /* Name.  Webpages start with "/". Commands do not */
  void (*xFunc)(void);     /* Function that implements the command or webpage */
  const char *zHelp;       /* Raw help text */
  unsigned eCmdFlags;      /* Flags */
};

/***************************************************************************
** These macros must match similar macros in mkindex.c
** Allowed values for CmdOrPage.eCmdFlags.
*/
#define CMDFLAG_1ST_TIER  0x0001      /* Most important commands */
#define CMDFLAG_2ND_TIER  0x0002      /* Obscure and seldom used commands */
#define CMDFLAG_TEST      0x0004      /* Commands for testing only */
#define CMDFLAG_WEBPAGE   0x0008      /* Web pages */
#define CMDFLAG_COMMAND   0x0010      /* A command */
/**************************************************************************/

/* Values for the 2nd parameter to dispatch_name_search() */
#define CMDFLAG_ANY       0x0018      /* Match anything */
#define CMDFLAG_PREFIX    0x0020      /* Prefix match is ok */

#endif /* INTERFACE */

/*
** The page_index.h file contains the definition for aCommand[] - an array
** of CmdOrPage objects that defines all available commands and webpages
** known to Fossil.
**
** The entries in aCommand[] are in sorted order by name.  Since webpage names
** always begin with "/", all webpage names occur first.  The page_index.h file
** also sets the FOSSIL_FIRST_CMD macro to be the *approximate* index 
** in aCommand[] of the first command entry.  FOSSIL_FIRST_CMD might be
** slightly too low, and so the range FOSSIL_FIRST_CMD...MX_COMMAND might
** contain a few webpage entries at the beginning.
**
** The page_index.h file is generated by the mkindex program which scans all
** source code files looking for header comments on the functions that
** implement command and webpages.
*/
#include "page_index.h"
#define MX_COMMAND (sizeof(aCommand)/sizeof(aCommand[0]))

/*
** Given a command or webpage name in zName, find the corresponding CmdOrPage
** object and return a pointer to that object in *ppCmd.
**
** The eType field is CMDFLAG_COMMAND to lookup commands or CMDFLAG_WEBPAGE
** to look up webpages or CMDFLAG_ANY to look for either.  If the CMDFLAG_PREFIX
** flag is set, then a prefix match is allowed.
**
** Return values:
**    0:     Success.  *ppCmd is set to the appropriate CmdOrPage
**    1:     Not found.
**    2:     Ambiguous.  Two or more entries match.
*/
int dispatch_name_search(
  const char *zName,           /* Look for this name */
  unsigned eType,              /* CMDFLAGS_* bits */
  const CmdOrPage **ppCmd      /* Write the matching CmdOrPage object here */
){
  int upr, lwr, mid;
  int nName = strlen(zName);
  lwr = 0;
  upr = MX_COMMAND - 1;
  while( lwr<=upr ){
    int c;
    mid = (upr+lwr)/2;
    c = strcmp(zName, aCommand[mid].zName);
    if( c==0 ){
      *ppCmd = &aCommand[mid];
      return 0;  /* An exact match */
    }else if( c<0 ){
      upr = mid - 1;
    }else{
      lwr = mid + 1;
    }
  }
  if( (eType & CMDFLAG_PREFIX)!=0
   && lwr<MX_COMMAND
   && strncmp(zName, aCommand[lwr].zName, nName)==0
  ){
    if( lwr<MX_COMMAND-1 && strncmp(zName, aCommand[lwr+1].zName, nName)==0 ){
      return 2;  /* Ambiguous prefix */
    }else{
      *ppCmd = &aCommand[lwr];
      return 0;  /* Prefix match */
    }
  }
  return 1;  /* Not found */
}

/*
** Fill Blob with a space-separated list of all command names that
** match the prefix zPrefix.
*/
void dispatch_matching_names(const char *zPrefix, Blob *pList){
  int i;
  int nPrefix = (int)strlen(zPrefix);
  for(i=FOSSIL_FIRST_CMD; i<MX_COMMAND; i++){
    if( strncmp(zPrefix, aCommand[i].zName, nPrefix)==0 ){
      blob_appendf(pList, " %s", aCommand[i].zName);
    }
  }
}


/*
** COMMAND: test-all-help
**
** Usage: %fossil test-all-help ?OPTIONS?
**
** Show help text for commands and pages.  Useful for proof-reading.
** Defaults to just the CLI commands.  Specify --www to see only the
** web pages, or --everything to see both commands and pages.
**
** Options:
**    -e|--everything   Show all commands and pages.
**    -t|--test         Include test- commands
**    -w|--www          Show WWW pages.
*/
void test_all_help_cmd(void){
  int i;
  int mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER;

  if( find_option("www","w",0) ){
    mask = CMDFLAG_WEBPAGE;
  }
  if( find_option("everything","e",0) ){
    mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE;
  }
  if( find_option("test","t",0) ){
    mask |= CMDFLAG_TEST;
  }
  fossil_print("Help text for:\n");
  if( mask & CMDFLAG_1ST_TIER ) fossil_print(" * Commands\n");
  if( mask & CMDFLAG_2ND_TIER ) fossil_print(" * Auxiliary commands\n");
  if( mask & CMDFLAG_TEST )     fossil_print(" * Test commands\n");
  if( mask & CMDFLAG_WEBPAGE )  fossil_print(" * Web pages\n");
  fossil_print("---\n");
  for(i=0; i<MX_COMMAND; i++){
    if( (aCommand[i].eCmdFlags & mask)==0 ) continue;
    fossil_print("# %s\n", aCommand[i].zName);
    fossil_print("%s\n\n", aCommand[i].zHelp);
  }
  fossil_print("---\n");
  version_cmd();
}

/*
** WEBPAGE: help
** URL: /help?name=CMD
**
** Show the built-in help text for CMD.  CMD can be a command-line interface
** command or a page name from the web interface.
*/
void help_page(void){
  const char *zCmd = P("cmd");

  if( zCmd==0 ) zCmd = P("name");
  style_header("Command-line Help");
  if( zCmd ){
    int rc;
    char *z, *s, *d;
    const CmdOrPage *pCmd = 0;

    style_submenu_element("Command-List", "Command-List", "%s/help", g.zTop);
    if( *zCmd=='/' ){
      /* Some of the webpages require query parameters in order to work.
      ** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */
      @ <h1>The "%s(zCmd)" page:</h1>
    }else{
      @ <h1>The "%s(zCmd)" command:</h1>
    }
    rc = dispatch_name_search(zCmd, CMDFLAG_ANY, &pCmd);
    if( rc==1 ){
      @ unknown command: %s(zCmd)
    }else if( rc==2 ){
      @ ambiguous command prefix: %s(zCmd)
    }else{
      z = (char*)pCmd->zHelp;
      if( z[0]==0 ){
        @ no help available for the %s(pCmd->zName) command
      }else{
        z=s=d=mprintf("%s",z);
        while( *s ){
          if( *s=='%' && strncmp(s, "%fossil", 7)==0 ){
            s++;
          }else{
            *d++ = *s++;
          }
        }
        *d = 0;
        @ <blockquote><pre>
        @ %h(z)
        @ </pre></blockquote>
        fossil_free(z);
      }
    }
  }else{
    int i, j, n;

    @ <h1>Available commands:</h1>
    @ <table border="0"><tr>
    for(i=j=0; i<MX_COMMAND; i++){
      const char *z = aCommand[i].zName;
      if( '/'==*z || strncmp(z,"test",4)==0 ) continue;
      j++;
    }
    n = (j+6)/7;
    for(i=j=0; i<MX_COMMAND; i++){
      const char *z = aCommand[i].zName;
      if( '/'==*z || strncmp(z,"test",4)==0 ) continue;
      if( j==0 ){
        @ <td valign="top"><ul>
      }
      @ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li>
      j++;
      if( j>=n ){
        @ </ul></td>
        j = 0;
      }
    }
    if( j>0 ){
      @ </ul></td>
    }
    @ </tr></table>

    @ <h1>Available web UI pages:</h1>
    @ <table border="0"><tr>
    for(i=j=0; i<MX_COMMAND; i++){
      const char *z = aCommand[i].zName;
      if( '/'!=*z ) continue;
      j++;
    }
    n = (j+4)/5;
    for(i=j=0; i<MX_COMMAND; i++){
      const char *z = aCommand[i].zName;
      if( '/'!=*z ) continue;
      if( j==0 ){
        @ <td valign="top"><ul>
      }
      if( aCommand[i].zHelp[0] ){
        @ <li><a href="%R/help?cmd=%s(z)">%s(z+1)</a></li>
      }else{
        @ <li>%s(z+1)</li>
      }
      j++;
      if( j>=n ){
        @ </ul></td>
        j = 0;
      }
    }
    if( j>0 ){
      @ </ul></td>
    }
    @ </tr></table>

    @ <h1>Unsupported commands:</h1>
    @ <table border="0"><tr>
    for(i=j=0; i<MX_COMMAND; i++){
      const char *z = aCommand[i].zName;
      if( strncmp(z,"test",4)!=0 ) continue;
      j++;
    }
    n = (j+3)/4;
    for(i=j=0; i<MX_COMMAND; i++){
      const char *z = aCommand[i].zName;
      if( strncmp(z,"test",4)!=0 ) continue;
      if( j==0 ){
        @ <td valign="top"><ul>
      }
      if( aCommand[i].zHelp[0] ){
        @ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li>
      }else{
        @ <li>%s(z)</li>
      }
      j++;
      if( j>=n ){
        @ </ul></td>
        j = 0;
      }
    }
    if( j>0 ){
      @ </ul></td>
    }
    @ </tr></table>

  }
  style_footer();
}

/*
** WEBPAGE: test-all-help
**
** Show all help text on a single page.  Useful for proof-reading.
*/
void test_all_help_page(void){
  int i;
  style_header("Testpage: All Help Text");
  for(i=0; i<MX_COMMAND; i++){
    if( memcmp(aCommand[i].zName, "test", 4)==0 ) continue;
    @ <h2>%s(aCommand[i].zName):</h2>
    @ <blockquote><pre>
    @ %h(aCommand[i].zHelp)
    @ </pre></blockquote>
  }
  style_footer();
}

static void multi_column_list(const char **azWord, int nWord){
  int i, j, len;
  int mxLen = 0;
  int nCol;
  int nRow;
  for(i=0; i<nWord; i++){
    len = strlen(azWord[i]);
    if( len>mxLen ) mxLen = len;
  }
  nCol = 80/(mxLen+2);
  if( nCol==0 ) nCol = 1;
  nRow = (nWord + nCol - 1)/nCol;
  for(i=0; i<nRow; i++){
    const char *zSpacer = "";
    for(j=i; j<nWord; j+=nRow){
      fossil_print("%s%-*s", zSpacer, mxLen, azWord[j]);
      zSpacer = "  ";
    }
    fossil_print("\n");
  }
}

/*
** COMMAND: test-list-webpage
**
** List all web pages.
*/
void cmd_test_webpage_list(void){
  int i, nCmd;
  const char *aCmd[MX_COMMAND];
  for(i=nCmd=0; i<MX_COMMAND; i++){
    if(0x08 & aCommand[i].eCmdFlags){
      aCmd[nCmd++] = aCommand[i].zName;
    }
  }
  assert(nCmd && "page list is empty?");
  multi_column_list(aCmd, nCmd);
}


/*
** List of commands starting with zPrefix, or all commands if zPrefix is NULL.
*/
static void command_list(const char *zPrefix, int cmdMask){
  int i, nCmd;
  int nPrefix = zPrefix ? strlen(zPrefix) : 0;
  const char *aCmd[MX_COMMAND];
  for(i=nCmd=0; i<MX_COMMAND; i++){
    const char *z = aCommand[i].zName;
    if( (aCommand[i].eCmdFlags & cmdMask)==0 ) continue;
    if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue;
    aCmd[nCmd++] = aCommand[i].zName;
  }
  multi_column_list(aCmd, nCmd);
}

/*
** COMMAND: help
**
** Usage: %fossil help COMMAND
**    or: %fossil COMMAND --help
**
** Display information on how to use COMMAND.  To display a list of
** available commands use one of:
**
**    %fossil help              Show common commands
**    %fossil help -a|--all     Show both common and auxiliary commands
**    %fossil help -t|--test    Show test commands only
**    %fossil help -x|--aux     Show auxiliary commands only
**    %fossil help -w|--www     Show list of WWW pages
*/
void help_cmd(void){
  int rc;
  int isPage = 0;
  const char *z;
  const char *zCmdOrPage;
  const char *zCmdOrPagePlural;
  const CmdOrPage *pCmd = 0;
  if( g.argc<3 ){
    z = g.argv[0];
    fossil_print(
      "Usage: %s help COMMAND\n"
      "Common COMMANDs:  (use \"%s help -a|--all\" for a complete list)\n",
      z, z);
    command_list(0, CMDFLAG_1ST_TIER);
    version_cmd();
    return;
  }
  if( find_option("all","a",0) ){
    command_list(0, CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER);
    return;
  }
  else if( find_option("www","w",0) ){
    command_list(0, CMDFLAG_WEBPAGE);
    return;
  }
  else if( find_option("aux","x",0) ){
    command_list(0, CMDFLAG_2ND_TIER);
    return;
  }
  else if( find_option("test","t",0) ){
    command_list(0, CMDFLAG_TEST);
    return;
  }
  isPage = ('/' == *g.argv[2]) ? 1 : 0;
  if(isPage){
    zCmdOrPage = "page";
    zCmdOrPagePlural = "pages";
  }else{
    zCmdOrPage = "command";
    zCmdOrPagePlural = "commands";
  }
  rc = dispatch_name_search(g.argv[2], CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd);
  if( rc==1 ){
    fossil_print("unknown %s: %s\nAvailable %s:\n",
                 zCmdOrPage, g.argv[2], zCmdOrPagePlural);
    command_list(0, isPage ? CMDFLAG_WEBPAGE : (0xff & ~CMDFLAG_WEBPAGE));
    fossil_exit(1);
  }else if( rc==2 ){
    fossil_print("ambiguous %s prefix: %s\nMatching %s:\n",
                 zCmdOrPage, g.argv[2], zCmdOrPagePlural);
    command_list(g.argv[2], 0xff);
    fossil_exit(1);
  }
  z = pCmd->zHelp;
  if( z==0 ){
    fossil_fatal("no help available for the %s %s",
                 pCmd->zName, zCmdOrPage);
  }
  while( *z ){
    if( *z=='%' && strncmp(z, "%fossil", 7)==0 ){
      fossil_print("%s", g.argv[0]);
      z += 7;
    }else{
      putchar(*z);
      z++;
    }
  }
  putchar('\n');
}

Changes to src/doc.c.

82
83
84
85
86
87
88


89
90
91
92
93
94
95
...
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
...
518
519
520
521
522
523
524

525
526
527
528
529
530
531
532
533
...
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
619
620
621





622
623
624
625
626
627
628
629
...
641
642
643
644
645
646
647

648
649
650
651

652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688


689

690

691

692
693
694
695
696
697
698
699
700
701
702
703




704
705
706
707
708
709
710
  { "asf",        3, "video/x-ms-asf"                    },
  { "asx",        3, "video/x-ms-asx"                    },
  { "au",         2, "audio/ulaw"                        },
  { "avi",        3, "video/x-msvideo"                   },
  { "bat",        3, "application/x-msdos-program"       },
  { "bcpio",      5, "application/x-bcpio"               },
  { "bin",        3, "application/octet-stream"          },


  { "c",          1, "text/plain"                        },
  { "cc",         2, "text/plain"                        },
  { "ccad",       4, "application/clariscad"             },
  { "cdf",        3, "application/x-netcdf"              },
  { "class",      5, "application/octet-stream"          },
  { "cod",        3, "application/vnd.rim.cod"           },
  { "com",        3, "application/x-msdos-program"       },
................................................................................
** and any case for href.
*/
static void convert_href_and_output(Blob *pIn){
  int i, base;
  int n = blob_size(pIn);
  char *z = blob_buffer(pIn);
  for(base=0, i=7; i<n; i++){
    if( z[i]=='$' 
     && strncmp(&z[i],"$ROOT/", 6)==0
     && (z[i-1]=='\'' || z[i-1]=='"')
     && i-base>=9
     && (fossil_strnicmp(&z[i-7]," href=", 6)==0 ||
           fossil_strnicmp(&z[i-9]," action=", 8)==0)
    ){
      blob_append(cgi_output_blob(), &z[base], i-base);
................................................................................
      base = i+5;
    }
  }
  blob_append(cgi_output_blob(), &z[base], i-base);
}

/*

** WEBPAGE: doc
** URL: /doc?name=CHECKIN/FILE
** URL: /doc/CHECKIN/FILE
**
** CHECKIN can be either tag or SHA1 hash or timestamp identifying a
** particular check, or the name of a branch (meaning the most recent
** check-in on that branch) or one of various magic words:
**
**     "tip"      means the most recent check-in
................................................................................
  char *zCheckin = "tip";           /* The check-in holding the document */
  int vid = 0;                      /* Artifact of check-in */
  int rid = 0;                      /* Artifact of file */
  int i;                            /* Loop counter */
  Blob filebody;                    /* Content of the documentation file */
  Blob title;                       /* Document title */
  int nMiss = (-1);                 /* Failed attempts to find the document */


  static const char *const azSuffix[] = {
     "index.html", "index.wiki", "index.md"
#ifdef FOSSIL_ENABLE_TH1_DOCS
      , "index.th1"
#endif
  };

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  blob_init(&title, 0, 0);

  db_begin_transaction();
  while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){
    zName = P("name");




    if( zName==0 || zName[0]==0 ) zName = "tip/index.wiki";
    for(i=0; zName[i] && zName[i]!='/'; i++){}
    zCheckin = mprintf("%.*s", i, zName);
    if( fossil_strcmp(zCheckin,"ckout")==0 && g.localOpen==0 ){
      zCheckin = "tip";

    }
    if( nMiss==ArraySize(azSuffix) ){
      zName = "404.md";
    }else if( zName[i]==0 ){
      assert( nMiss>=0 && nMiss<ArraySize(azSuffix) );
      zName = azSuffix[nMiss];
    }else{
      zName += i;
    }
    while( zName[0]=='/' ){ zName++; }



    g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName);

    if( nMiss==0 ) zOrigName = zName;
    if( !file_is_simple_pathname(zName, 1) ){
      if( sqlite3_strglob("*/", zName)==0 ){
        assert( nMiss>=0 && nMiss<ArraySize(azSuffix) );
        zName = mprintf("%s%s", zName, azSuffix[nMiss]);
        if( !file_is_simple_pathname(zName, 1) ){
          goto doc_not_found;
        }
      }else{
        goto doc_not_found;
      }
    }





    if( fossil_strcmp(zCheckin,"ckout")==0 ){
      /* Read from the local checkout */
      char *zFullpath;
      db_must_be_within_tree();
      zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
      if( file_isfile(zFullpath)
       && blob_read_from_file(&filebody, zFullpath)>0 ){
        rid = 1;  /* Fake RID just to get the loop to end */
................................................................................
  ** file to the user
  */
  zMime = nMiss==0 ? P("mimetype") : 0;
  if( zMime==0 ){
    zMime = mimetype_from_name(zName);
  }
  Th_Store("doc_name", zName);

  Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
                                     "  FROM blob WHERE rid=%d", vid));
  Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
                                  " WHERE objid=%d AND type='ci'", vid));

  if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
    Blob tail;
    style_adunit_config(ADUNIT_RIGHT_OK);
    if( wiki_find_title(&filebody, &title, &tail) ){
      style_header("%s", blob_str(&title));
      wiki_convert(&tail, 0, WIKI_BUTTONS);
    }else{
      style_header("Documentation");
      wiki_convert(&filebody, 0, WIKI_BUTTONS);
    }
    style_footer();
  }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
    Blob tail = BLOB_INITIALIZER;
    markdown_to_html(&filebody, &title, &tail);
    if( blob_size(&title)>0 ){
      style_header("%s", blob_str(&title));
    }else{
      style_header("%s", nMiss>=ArraySize(azSuffix)?
                        "Not Found" : "Documentation");
    }
    convert_href_and_output(&tail);
    style_footer();
  }else if( fossil_strcmp(zMime, "text/plain")==0 ){
    style_header("Documentation");
    @ <blockquote><pre>
    @ %h(blob_str(&filebody))
    @ </pre></blockquote>
    style_footer();
  }else if( fossil_strcmp(zMime, "text/html")==0
            && doc_is_embedded_html(&filebody, &title) ){
    if( blob_size(&title)==0 ) blob_append(&title,zName,-1);
    style_header("%s", blob_str(&title));
    convert_href_and_output(&filebody);
    style_footer();
#ifdef FOSSIL_ENABLE_TH1_DOCS
  }else if( Th_AreDocsEnabled() &&
            fossil_strcmp(zMime, "application/x-th1")==0 ){


    style_header("%h", zName);

    Th_Render(blob_str(&filebody));

    style_footer();

#endif
  }else{
    cgi_set_content_type(zMime);
    cgi_set_content(&filebody);
  }
  if( nMiss>=ArraySize(azSuffix) ) cgi_set_status(404, "Not Found");
  db_end_transaction(0);
  return;

  /* Jump here when unable to locate the document */
doc_not_found:
  db_end_transaction(0);




  cgi_set_status(404, "Not Found");
  style_header("Not Found");
  @ <p>Document %h(zOrigName) not found
  if( fossil_strcmp(zCheckin,"ckout")!=0 ){
    @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a>
  }
  style_footer();







>
>







 







|







 







>

|







 







>
>










>



>
>
>
>
|
|
|
|
|
>






|



>
>
>
|
>












>
>
>
>
>
|







 







>
|
|
|
|
>







|










|




|













>
>
|
>

>
|
>












>
>
>
>







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
...
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
...
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
...
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
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
...
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
  { "asf",        3, "video/x-ms-asf"                    },
  { "asx",        3, "video/x-ms-asx"                    },
  { "au",         2, "audio/ulaw"                        },
  { "avi",        3, "video/x-msvideo"                   },
  { "bat",        3, "application/x-msdos-program"       },
  { "bcpio",      5, "application/x-bcpio"               },
  { "bin",        3, "application/octet-stream"          },
  { "bz2",        3, "application/x-bzip2"               },
  { "bzip",       4, "application/x-bzip"                },
  { "c",          1, "text/plain"                        },
  { "cc",         2, "text/plain"                        },
  { "ccad",       4, "application/clariscad"             },
  { "cdf",        3, "application/x-netcdf"              },
  { "class",      5, "application/octet-stream"          },
  { "cod",        3, "application/vnd.rim.cod"           },
  { "com",        3, "application/x-msdos-program"       },
................................................................................
** and any case for href.
*/
static void convert_href_and_output(Blob *pIn){
  int i, base;
  int n = blob_size(pIn);
  char *z = blob_buffer(pIn);
  for(base=0, i=7; i<n; i++){
    if( z[i]=='$'
     && strncmp(&z[i],"$ROOT/", 6)==0
     && (z[i-1]=='\'' || z[i-1]=='"')
     && i-base>=9
     && (fossil_strnicmp(&z[i-7]," href=", 6)==0 ||
           fossil_strnicmp(&z[i-9]," action=", 8)==0)
    ){
      blob_append(cgi_output_blob(), &z[base], i-base);
................................................................................
      base = i+5;
    }
  }
  blob_append(cgi_output_blob(), &z[base], i-base);
}

/*
** WEBPAGE: uv
** WEBPAGE: doc
** URL: /uv/FILE
** URL: /doc/CHECKIN/FILE
**
** CHECKIN can be either tag or SHA1 hash or timestamp identifying a
** particular check, or the name of a branch (meaning the most recent
** check-in on that branch) or one of various magic words:
**
**     "tip"      means the most recent check-in
................................................................................
  char *zCheckin = "tip";           /* The check-in holding the document */
  int vid = 0;                      /* Artifact of check-in */
  int rid = 0;                      /* Artifact of file */
  int i;                            /* Loop counter */
  Blob filebody;                    /* Content of the documentation file */
  Blob title;                       /* Document title */
  int nMiss = (-1);                 /* Failed attempts to find the document */
  int isUV = g.zPath[0]=='u';       /* True for /uv.  False for /doc */
  const char *zDfltTitle;
  static const char *const azSuffix[] = {
     "index.html", "index.wiki", "index.md"
#ifdef FOSSIL_ENABLE_TH1_DOCS
      , "index.th1"
#endif
  };

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  blob_init(&title, 0, 0);
  zDfltTitle = isUV ? "" : "Documentation";
  db_begin_transaction();
  while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){
    zName = P("name");
    if( isUV ){
      if( zName==0 ) zName = "index.wiki";
      i = 0;
    }else{
      if( zName==0 || zName[0]==0 ) zName = "tip/index.wiki";
      for(i=0; zName[i] && zName[i]!='/'; i++){}
      zCheckin = mprintf("%.*s", i, zName);
      if( fossil_strcmp(zCheckin,"ckout")==0 && g.localOpen==0 ){
        zCheckin = "tip";
      }
    }
    if( nMiss==ArraySize(azSuffix) ){
      zName = "404.md";
    }else if( zName[i]==0 ){
      assert( nMiss>=0 && nMiss<ArraySize(azSuffix) );
      zName = azSuffix[nMiss];
    }else if( !isUV ){
      zName += i;
    }
    while( zName[0]=='/' ){ zName++; }
    if( isUV ){
      g.zPath = mprintf("%s/%s", g.zPath, zName);
    }else{
      g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName);
    }
    if( nMiss==0 ) zOrigName = zName;
    if( !file_is_simple_pathname(zName, 1) ){
      if( sqlite3_strglob("*/", zName)==0 ){
        assert( nMiss>=0 && nMiss<ArraySize(azSuffix) );
        zName = mprintf("%s%s", zName, azSuffix[nMiss]);
        if( !file_is_simple_pathname(zName, 1) ){
          goto doc_not_found;
        }
      }else{
        goto doc_not_found;
      }
    }
    if( isUV ){
      if( unversioned_content(zName, &filebody)==0 ){
        rid = 1;
        zDfltTitle = zName;
      }
    }else if( fossil_strcmp(zCheckin,"ckout")==0 ){
      /* Read from the local checkout */
      char *zFullpath;
      db_must_be_within_tree();
      zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
      if( file_isfile(zFullpath)
       && blob_read_from_file(&filebody, zFullpath)>0 ){
        rid = 1;  /* Fake RID just to get the loop to end */
................................................................................
  ** file to the user
  */
  zMime = nMiss==0 ? P("mimetype") : 0;
  if( zMime==0 ){
    zMime = mimetype_from_name(zName);
  }
  Th_Store("doc_name", zName);
  if( vid ){
    Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
                                       "  FROM blob WHERE rid=%d", vid));
    Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
                                    " WHERE objid=%d AND type='ci'", vid));
  }
  if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
    Blob tail;
    style_adunit_config(ADUNIT_RIGHT_OK);
    if( wiki_find_title(&filebody, &title, &tail) ){
      style_header("%s", blob_str(&title));
      wiki_convert(&tail, 0, WIKI_BUTTONS);
    }else{
      style_header("%s", zDfltTitle);
      wiki_convert(&filebody, 0, WIKI_BUTTONS);
    }
    style_footer();
  }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
    Blob tail = BLOB_INITIALIZER;
    markdown_to_html(&filebody, &title, &tail);
    if( blob_size(&title)>0 ){
      style_header("%s", blob_str(&title));
    }else{
      style_header("%s", nMiss>=ArraySize(azSuffix)?
                        "Not Found" : zDfltTitle);
    }
    convert_href_and_output(&tail);
    style_footer();
  }else if( fossil_strcmp(zMime, "text/plain")==0 ){
    style_header("%s", zDfltTitle);
    @ <blockquote><pre>
    @ %h(blob_str(&filebody))
    @ </pre></blockquote>
    style_footer();
  }else if( fossil_strcmp(zMime, "text/html")==0
            && doc_is_embedded_html(&filebody, &title) ){
    if( blob_size(&title)==0 ) blob_append(&title,zName,-1);
    style_header("%s", blob_str(&title));
    convert_href_and_output(&filebody);
    style_footer();
#ifdef FOSSIL_ENABLE_TH1_DOCS
  }else if( Th_AreDocsEnabled() &&
            fossil_strcmp(zMime, "application/x-th1")==0 ){
    int raw = P("raw")!=0;
    if( !raw ){
      style_header("%h", zName);
    }
    Th_Render(blob_str(&filebody));
    if( !raw ){
      style_footer();
    }
#endif
  }else{
    cgi_set_content_type(zMime);
    cgi_set_content(&filebody);
  }
  if( nMiss>=ArraySize(azSuffix) ) cgi_set_status(404, "Not Found");
  db_end_transaction(0);
  return;

  /* Jump here when unable to locate the document */
doc_not_found:
  db_end_transaction(0);
  if( isUV && P("name")==0 ){
    uvstat_page();
    return;
  }
  cgi_set_status(404, "Not Found");
  style_header("Not Found");
  @ <p>Document %h(zOrigName) not found
  if( fossil_strcmp(zCheckin,"ckout")!=0 ){
    @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a>
  }
  style_footer();

Changes to src/encode.c.

365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
...
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
  }
  z64[n] = 0;
  return z64;
}

/*
** COMMAND: test-encode64
** 
** Usage: %fossil test-encode64 STRING
*/
void test_encode64_cmd(void){
  char *z;
  int i;
  for(i=2; i<g.argc; i++){
    z = encode64(g.argv[i], -1);
................................................................................
  zData[j] = 0;
  *pnByte = j;
  return zData;
}

/*
** COMMAND: test-decode64
** 
** Usage: %fossil test-decode64 STRING
*/
void test_decode64_cmd(void){
  char *z;
  int i, n;
  for(i=2; i<g.argc; i++){
    z = decode64(g.argv[i], &n);







|







 







|







365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
...
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
  }
  z64[n] = 0;
  return z64;
}

/*
** COMMAND: test-encode64
**
** Usage: %fossil test-encode64 STRING
*/
void test_encode64_cmd(void){
  char *z;
  int i;
  for(i=2; i<g.argc; i++){
    z = encode64(g.argv[i], -1);
................................................................................
  zData[j] = 0;
  *pnByte = j;
  return zData;
}

/*
** COMMAND: test-decode64
**
** Usage: %fossil test-decode64 STRING
*/
void test_decode64_cmd(void){
  char *z;
  int i, n;
  for(i=2; i<g.argc; i++){
    z = decode64(g.argv[i], &n);

Changes to src/event.c.

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
...
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
...
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
**
**  name=ID           Identify the technical note to display. ID must be
**                    complete.
**  aid=ARTIFACTID    Which specific version of the tech-note.  Optional.
**  v=BOOLEAN         Show details if TRUE.  Default is FALSE.  Optional.
**
** Display an existing tech-note identified by its ID, optionally at a
** specific version, and optionally with additional details. 
*/
void event_page(void){
  int rid = 0;             /* rid of the event artifact */
  char *zUuid;             /* UUID corresponding to rid */
  const char *zId;         /* Event identifier */
  const char *zVerbose;    /* Value of verbose option */
  char *zETime;            /* Time of the tech-note */
................................................................................
    tail = fullbody;
  }
  style_header("%s", blob_str(&title));
  if( g.perm.WrWiki && g.perm.Write && nextRid==0 ){
    style_submenu_element("Edit", 0, "%R/technoteedit?name=%!S", zId);
    if( g.perm.Attach ){
      style_submenu_element("Attach", "Add an attachment",
           "%R/attachadd?technote=%!S&from=%R/technote/%!S", 
           zId, zId);
    }
  }
  zETime = db_text(0, "SELECT datetime(%.17g)", pTNote->rEventDate);
  style_submenu_element("Context", 0, "%R/timeline?c=%.20s", zId);
  if( g.perm.Hyperlink ){
    if( verboseFlag ){
................................................................................
  attachment_list(zFullId, "<hr /><h2>Attachments:</h2><ul>");
  style_footer();
  manifest_destroy(pTNote);
}

/*
** Add or update a new tech note to the repository.  rid is id of
** the prior version of this technote, if any. 
**
** returns 1 if the tech note was added or updated, 0 if the
** update failed making an invalid artifact
*/
int event_commit_common(
  int rid,                 /* id of the prior version of the technote */
  const char *zId,         /* hash label for the technote */
................................................................................
  while( n>0 && fossil_isspace(zComment[n-1]) ){ n--; }
  if( n>0 ){
    blob_appendf(&event, "C %#F\n", n, zComment);
  }
  zDate = date_in_standard_format("now");
  blob_appendf(&event, "D %s\n", zDate);
  free(zDate);
  
  zETime[10] = 'T';
  blob_appendf(&event, "E %s %s\n", zETime, zId);
  zETime[10] = ' ';
  if( rid ){
    char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
    blob_appendf(&event, "P %s\n", zUuid);
    free(zUuid);







|







 







|







 







|







 







|







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
...
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
...
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
**
**  name=ID           Identify the technical note to display. ID must be
**                    complete.
**  aid=ARTIFACTID    Which specific version of the tech-note.  Optional.
**  v=BOOLEAN         Show details if TRUE.  Default is FALSE.  Optional.
**
** Display an existing tech-note identified by its ID, optionally at a
** specific version, and optionally with additional details.
*/
void event_page(void){
  int rid = 0;             /* rid of the event artifact */
  char *zUuid;             /* UUID corresponding to rid */
  const char *zId;         /* Event identifier */
  const char *zVerbose;    /* Value of verbose option */
  char *zETime;            /* Time of the tech-note */
................................................................................
    tail = fullbody;
  }
  style_header("%s", blob_str(&title));
  if( g.perm.WrWiki && g.perm.Write && nextRid==0 ){
    style_submenu_element("Edit", 0, "%R/technoteedit?name=%!S", zId);
    if( g.perm.Attach ){
      style_submenu_element("Attach", "Add an attachment",
           "%R/attachadd?technote=%!S&from=%R/technote/%!S",
           zId, zId);
    }
  }
  zETime = db_text(0, "SELECT datetime(%.17g)", pTNote->rEventDate);
  style_submenu_element("Context", 0, "%R/timeline?c=%.20s", zId);
  if( g.perm.Hyperlink ){
    if( verboseFlag ){
................................................................................
  attachment_list(zFullId, "<hr /><h2>Attachments:</h2><ul>");
  style_footer();
  manifest_destroy(pTNote);
}

/*
** Add or update a new tech note to the repository.  rid is id of
** the prior version of this technote, if any.
**
** returns 1 if the tech note was added or updated, 0 if the
** update failed making an invalid artifact
*/
int event_commit_common(
  int rid,                 /* id of the prior version of the technote */
  const char *zId,         /* hash label for the technote */
................................................................................
  while( n>0 && fossil_isspace(zComment[n-1]) ){ n--; }
  if( n>0 ){
    blob_appendf(&event, "C %#F\n", n, zComment);
  }
  zDate = date_in_standard_format("now");
  blob_appendf(&event, "D %s\n", zDate);
  free(zDate);

  zETime[10] = 'T';
  blob_appendf(&event, "E %s %s\n", zETime, zId);
  zETime[10] = ' ';
  if( rid ){
    char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
    blob_appendf(&event, "P %s\n", zUuid);
    free(zUuid);

Changes to src/export.c.

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
...
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
...
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
...
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
  char sid[13];
  char *zUuid = rid_to_uuid(rid);
  if(!zUuid){
    fossil_trace("Undefined rid=%d\n", rid);
    return -1;
  }
  mark->rid = rid;
  sprintf(sid, ":%d", COMMITMARK(rid));
  mark->name = fossil_strdup(sid);
  strcpy(mark->uuid, zUuid);
  free(zUuid);
  insert_commit_xref(mark->rid, mark->name, mark->uuid);
  return 0;
}

/*
** mark_name_from_rid()
................................................................................
**   This function returns -1 in the case that the line is blank, malformed, or
**   the rid/uuid named in 'line' does not match what is in the repository
**   database.  Otherwise, 0 is returned.
**   mark->name is dynamically allocated, and owned by the caller.
*/
int parse_mark(char *line, struct mark_t *mark){
  char *cur_tok;
  char type;
  cur_tok = strtok(line, " \t");
  if(!cur_tok||strlen(cur_tok)<2){
    return -1;
  }
  mark->rid = atoi(&cur_tok[1]);
  if(cur_tok[0]!='c'){
    /* This is probably a blob mark */
................................................................................

  cur_tok = strtok(NULL, "\n");
  if(!cur_tok||strlen(cur_tok)!=40){
    free(mark->name);
    fossil_trace("Invalid SHA-1 in marks file: %s\n", cur_tok);
    return -1;
  }else{
    strcpy(mark->uuid, cur_tok);
  }

  /* make sure that rid corresponds to UUID */
  if(fast_uuid_to_rid(mark->uuid)!=mark->rid){
    free(mark->name);
    fossil_trace("Non-existent SHA-1 in marks file: %s\n", mark->uuid);
    return -1;
................................................................................
**   character rid.
**   The function returns -1 if any of the lines in file 'f' are malformed,
**   or the rid/uuid information doesn't match what is in the repository
**   database.  Otherwise, 0 is returned.
*/
int import_marks(FILE* f, Bag *blobs, Bag *vers){
  char line[101];
  size_t len;
  while(fgets(line, sizeof(line), f)){
    struct mark_t mark;
    if(strlen(line)==100&&line[99]!='\n'){
      /* line too long */
      return -1;
    }
    if( parse_mark(line, &mark)<0 ){
................................................................................
  if( g.argc!=2 && g.argc!=3 ){ usage("--git ?REPOSITORY?"); }

  db_multi_exec("CREATE TEMPORARY TABLE oldblob(rid INTEGER PRIMARY KEY)");
  db_multi_exec("CREATE TEMPORARY TABLE oldcommit(rid INTEGER PRIMARY KEY)");
  db_multi_exec("CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT, tuuid TEXT)");
  if( markfile_in!=0 ){
    Stmt qb,qc;
    char line[100];
    FILE *f;
    int rid;

    f = fossil_fopen(markfile_in, "r");
    if( f==0 ){
      fossil_fatal("cannot open %s for reading", markfile_in);
    }







|

|







 







<







 







|







 







<







 







<







142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
...
183
184
185
186
187
188
189

190
191
192
193
194
195
196
...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
...
240
241
242
243
244
245
246

247
248
249
250
251
252
253
...
353
354
355
356
357
358
359

360
361
362
363
364
365
366
  char sid[13];
  char *zUuid = rid_to_uuid(rid);
  if(!zUuid){
    fossil_trace("Undefined rid=%d\n", rid);
    return -1;
  }
  mark->rid = rid;
  sqlite3_snprintf(sizeof(sid), sid, ":%d", COMMITMARK(rid));
  mark->name = fossil_strdup(sid);
  sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", zUuid);
  free(zUuid);
  insert_commit_xref(mark->rid, mark->name, mark->uuid);
  return 0;
}

/*
** mark_name_from_rid()
................................................................................
**   This function returns -1 in the case that the line is blank, malformed, or
**   the rid/uuid named in 'line' does not match what is in the repository
**   database.  Otherwise, 0 is returned.
**   mark->name is dynamically allocated, and owned by the caller.
*/
int parse_mark(char *line, struct mark_t *mark){
  char *cur_tok;

  cur_tok = strtok(line, " \t");
  if(!cur_tok||strlen(cur_tok)<2){
    return -1;
  }
  mark->rid = atoi(&cur_tok[1]);
  if(cur_tok[0]!='c'){
    /* This is probably a blob mark */
................................................................................

  cur_tok = strtok(NULL, "\n");
  if(!cur_tok||strlen(cur_tok)!=40){
    free(mark->name);
    fossil_trace("Invalid SHA-1 in marks file: %s\n", cur_tok);
    return -1;
  }else{
    sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", cur_tok);
  }

  /* make sure that rid corresponds to UUID */
  if(fast_uuid_to_rid(mark->uuid)!=mark->rid){
    free(mark->name);
    fossil_trace("Non-existent SHA-1 in marks file: %s\n", mark->uuid);
    return -1;
................................................................................
**   character rid.
**   The function returns -1 if any of the lines in file 'f' are malformed,
**   or the rid/uuid information doesn't match what is in the repository
**   database.  Otherwise, 0 is returned.
*/
int import_marks(FILE* f, Bag *blobs, Bag *vers){
  char line[101];

  while(fgets(line, sizeof(line), f)){
    struct mark_t mark;
    if(strlen(line)==100&&line[99]!='\n'){
      /* line too long */
      return -1;
    }
    if( parse_mark(line, &mark)<0 ){
................................................................................
  if( g.argc!=2 && g.argc!=3 ){ usage("--git ?REPOSITORY?"); }

  db_multi_exec("CREATE TEMPORARY TABLE oldblob(rid INTEGER PRIMARY KEY)");
  db_multi_exec("CREATE TEMPORARY TABLE oldcommit(rid INTEGER PRIMARY KEY)");
  db_multi_exec("CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT, tuuid TEXT)");
  if( markfile_in!=0 ){
    Stmt qb,qc;

    FILE *f;
    int rid;

    f = fossil_fopen(markfile_in, "r");
    if( f==0 ){
      fossil_fatal("cannot open %s for reading", markfile_in);
    }

Changes to src/file.c.

519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
...
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
void test_set_mtime(void){
  const char *zFile;
  char *zDate;
  i64 iMTime;
  if( g.argc!=4 ){
    usage("FILENAME DATE/TIME");
  }
  db_open_or_attach(":memory:", "mem", 0);
  iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
  zFile = g.argv[2];
  file_set_mtime(zFile, iMTime);
  iMTime = file_wd_mtime(zFile);
  zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
  fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
}
................................................................................
#endif
  blob_resize(pOut, file_simplify_name(blob_buffer(pOut),
                                       blob_size(pOut), slash));
}

/*
** COMMAND: test-canonical-name
** 
** Usage: %fossil test-canonical-name FILENAME...
**
** Test the operation of the canonical name generator.
** Also test Fossil's ability to measure attributes of a file.
*/
void cmd_test_canonical_name(void){
  int i;







|







 







|







519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
...
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
void test_set_mtime(void){
  const char *zFile;
  char *zDate;
  i64 iMTime;
  if( g.argc!=4 ){
    usage("FILENAME DATE/TIME");
  }
  db_open_or_attach(":memory:", "mem");
  iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
  zFile = g.argv[2];
  file_set_mtime(zFile, iMTime);
  iMTime = file_wd_mtime(zFile);
  zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
  fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
}
................................................................................
#endif
  blob_resize(pOut, file_simplify_name(blob_buffer(pOut),
                                       blob_size(pOut), slash));
}

/*
** COMMAND: test-canonical-name
**
** Usage: %fossil test-canonical-name FILENAME...
**
** Test the operation of the canonical name generator.
** Also test Fossil's ability to measure attributes of a file.
*/
void cmd_test_canonical_name(void){
  int i;

Changes to src/finfo.c.

280
281
282
283
284
285
286
287
288
289
290
291
292
293





294
295
296
297
298
299
300
...
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
...
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
...
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
...
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
** WEBPAGE: finfo
** URL: /finfo?name=FILENAME
**
** Show the change history for a single file.
**
** Additional query parameters:
**
**    a=DATE     Only show changes after DATE
**    b=DATE     Only show changes before DATE
**    n=NUM      Show the first NUM changes only
**    brbg       Background color by branch name
**    ubg        Background color by user name
**    ci=UUID    Ancestors of a particular check-in
**    showid     Show RID values for debugging





*/
void finfo_page(void){
  Stmt q;
  const char *zFilename;
  char zPrevDate[20];
  const char *zA;
  const char *zB;
................................................................................
    blob_appendf(&title,"<a href='%R/finfo?name=%T'>%h</a>",
                 zFilename, zFilename);
    if( fShowId ) blob_appendf(&title, " (%d)", fnid);
    blob_appendf(&title, " from check-in %z%S</a>", zLink, zUuid);
    if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
    fossil_free(zUuid);
  }else{
    blob_appendf(&title, "History of files named ");
    hyperlinked_path(zFilename, &title, 0, "tree", "");
    if( fShowId ) blob_appendf(&title, " (%d)", fnid);
  }
  @ <h2>%b(&title)</h2>
  blob_reset(&title);
  pGraph = graph_init();
  @ <table id="timelineTable" class="timelineTable">
................................................................................
      if( fpid>0 ){
        @ %z(href("%R/fdiff?sbs=1&v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
      }
    }
    if( fDebug & FINFO_DEBUG_MLINK ){
      int ii;
      char *zAncLink;
      @ <br>fid=%d(frid) pid=%d(fpid) mid=%d(fmid)
      if( nParent>0 ){
        @ parents=%d(aParent[0])
        for(ii=1; ii<nParent; ii++){
          @ %d(aParent[ii])
        }
      }
      zAncLink = href("%R/finfo?name=%T&ci=%!S&debug=1",zFilename,zCkin);
................................................................................
** a particular check-in.  This screen is intended for use by developers
** in debugging Fossil.
*/
void mlink_page(void){
  const char *zFName = P("name");
  const char *zCI = P("ci");
  Stmt q;
  
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(g.anon.Admin); return; }
  style_header("MLINK Table");
  if( zFName==0 && zCI==0 ){
    @ <span class='generalError'>
    @ Requires either a name= or ci= query parameter
    @ </span>
................................................................................
       /* 6 */ "  mperm,"
       /* 7 */ "  isaux"
       "  FROM mlink WHERE mid=%d ORDER BY 1",
       mid
    );
    @ <h1>MLINK table for check-in %h(zCI)</h1>
    render_checkin_context(mid, 1);
    @ <hr>
    @ <div class='brlist'>
    @ <table id='mlinktable'>
    @ <thead><tr>
    @ <th>File</th>
    @ <th>From</th>
    @ <th>Merge?</th>
    @ <th>New</th>







|
|





>
>
>
>
>







 







|







 







|







 







|







 







|







280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
...
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
...
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
...
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
...
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
** WEBPAGE: finfo
** URL: /finfo?name=FILENAME
**
** Show the change history for a single file.
**
** Additional query parameters:
**
**    a=DATETIME Only show changes after DATETIME
**    b=DATETIME Only show changes before DATETIME
**    n=NUM      Show the first NUM changes only
**    brbg       Background color by branch name
**    ubg        Background color by user name
**    ci=UUID    Ancestors of a particular check-in
**    showid     Show RID values for debugging
**
** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
** year-month-day form, it may be truncated, and it may also name a
** timezone offset from UTC as "-HH:MM" (westward) or "+HH:MM"
** (eastward). Either no timezone suffix or "Z" means UTC.
*/
void finfo_page(void){
  Stmt q;
  const char *zFilename;
  char zPrevDate[20];
  const char *zA;
  const char *zB;
................................................................................
    blob_appendf(&title,"<a href='%R/finfo?name=%T'>%h</a>",
                 zFilename, zFilename);
    if( fShowId ) blob_appendf(&title, " (%d)", fnid);
    blob_appendf(&title, " from check-in %z%S</a>", zLink, zUuid);
    if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
    fossil_free(zUuid);
  }else{
    blob_appendf(&title, "History of ");
    hyperlinked_path(zFilename, &title, 0, "tree", "");
    if( fShowId ) blob_appendf(&title, " (%d)", fnid);
  }
  @ <h2>%b(&title)</h2>
  blob_reset(&title);
  pGraph = graph_init();
  @ <table id="timelineTable" class="timelineTable">
................................................................................
      if( fpid>0 ){
        @ %z(href("%R/fdiff?sbs=1&v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
      }
    }
    if( fDebug & FINFO_DEBUG_MLINK ){
      int ii;
      char *zAncLink;
      @ <br />fid=%d(frid) pid=%d(fpid) mid=%d(fmid)
      if( nParent>0 ){
        @ parents=%d(aParent[0])
        for(ii=1; ii<nParent; ii++){
          @ %d(aParent[ii])
        }
      }
      zAncLink = href("%R/finfo?name=%T&ci=%!S&debug=1",zFilename,zCkin);
................................................................................
** a particular check-in.  This screen is intended for use by developers
** in debugging Fossil.
*/
void mlink_page(void){
  const char *zFName = P("name");
  const char *zCI = P("ci");
  Stmt q;

  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(g.anon.Admin); return; }
  style_header("MLINK Table");
  if( zFName==0 && zCI==0 ){
    @ <span class='generalError'>
    @ Requires either a name= or ci= query parameter
    @ </span>
................................................................................
       /* 6 */ "  mperm,"
       /* 7 */ "  isaux"
       "  FROM mlink WHERE mid=%d ORDER BY 1",
       mid
    );
    @ <h1>MLINK table for check-in %h(zCI)</h1>
    render_checkin_context(mid, 1);
    @ <hr />
    @ <div class='brlist'>
    @ <table id='mlinktable'>
    @ <thead><tr>
    @ <th>File</th>
    @ <th>From</th>
    @ <th>Merge?</th>
    @ <th>New</th>

Changes to src/foci.c.

11
12
13
14
15
16
17
18
19

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

40








41
42
43
44
45
46
47
..
49
50
51
52
53
54
55
56

57
58







59
60
61
62
63
64
65
...
101
102
103
104
105
106
107
108

109
110
111
112
113


114

115
116



117
118
119
120
121
122
123
...
164
165
166
167
168
169
170


171




172
173
174
175
176
177
178
...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207


208
209
210
211
212
213
214
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This routine implements an SQLite virtual table that gives all of the
** files associated with a single check-in.

**
** The filename "foci" is short for "Files of Check-in".
**
** Usage example:
**
**    CREATE VIRTUAL TABLE temp.foci USING files_of_checkin;
**                      -- ^^^^--- important!
**    SELECT * FROM foci WHERE checkinID=symbolic_name_to_rid('trunk');
**
** The symbolic_name_to_rid('trunk') function finds the BLOB.RID value
** corresponding to the 'trunk' tag.  Then the files_of_checkin virtual table
** decodes the manifest defined by that BLOB and returns all files described
** by that manifest.  The "schema" for the temp.foci table is:
**
**     CREATE TABLE files_of_checkin(
**       checkinID    INTEGER,    -- RID for the check-in manifest
**       filename     TEXT,       -- Name of a file
**       uuid         TEXT,       -- SHA1 hash of the file
**       previousName TEXT,       -- Name of the file in previous check-in
**       perm         TEXT        -- Permissions on the file

**     );








**
*/
#include "config.h"
#include "foci.h"
#include <assert.h>

/*
................................................................................
*/
static const char zFociSchema[] =
@ CREATE TABLE files_of_checkin(
@  checkinID    INTEGER,    -- RID for the check-in manifest
@  filename     TEXT,       -- Name of a file
@  uuid         TEXT,       -- SHA1 hash of the file
@  previousName TEXT,       -- Name of the file in previous check-in
@  perm         TEXT        -- Permissions on the file

@ );
;








#if INTERFACE
/*
** The subclasses of sqlite3_vtab  and sqlite3_vtab_cursor tables
** that implement the files_of_checkin virtual table.
*/
struct FociTable {
................................................................................
  return SQLITE_OK;
}

/*
** Available scan methods:
**
**   (0)     A full scan.  Visit every manifest in the repo.  (Slow)
**   (1)     checkinID=?.  visit only the single manifest specifed.

*/
static int fociBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
  int i;
  pIdxInfo->estimatedCost = 10000.0;
  for(i=0; i<pIdxInfo->nConstraint; i++){


    if( pIdxInfo->aConstraint[i].iColumn==0

     && pIdxInfo->aConstraint[i].op==SQLITE_INDEX_CONSTRAINT_EQ ){
      pIdxInfo->idxNum = 1;



      pIdxInfo->estimatedCost = 1.0;
      pIdxInfo->aConstraintUsage[i].argvIndex = 1;
      pIdxInfo->aConstraintUsage[i].omit = 1;
      break;
    }
  }
  return SQLITE_OK;
................................................................................
  sqlite3_vtab_cursor *pCursor,
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  FociCursor *pCur = (FociCursor *)pCursor;
  manifest_destroy(pCur->pMan);
  if( idxNum ){


    pCur->pMan = manifest_get(sqlite3_value_int(argv[0]), CFTYPE_MANIFEST, 0);




    if( pCur->pMan ){
      manifest_file_rewind(pCur->pMan);
      pCur->pFile = manifest_file_next(pCur->pMan, 0);
    }
  }else{
    pCur->pMan = 0;
  }
................................................................................
static int fociColumn(
  sqlite3_vtab_cursor *pCursor,
  sqlite3_context *ctx,
  int i
){
  FociCursor *pCsr = (FociCursor *)pCursor;
  switch( i ){
    case 0:            /* checkinID */
      sqlite3_result_int(ctx, pCsr->pMan->rid);
      break;
    case 1:            /* filename */
      sqlite3_result_text(ctx, pCsr->pFile->zName, -1,
                          SQLITE_TRANSIENT);
      break;
    case 2:            /* uuid */
      sqlite3_result_text(ctx, pCsr->pFile->zUuid, -1,
                          SQLITE_TRANSIENT);
      break;
    case 3:            /* previousName */
      sqlite3_result_text(ctx, pCsr->pFile->zPrior, -1,
                          SQLITE_TRANSIENT);
      break;
    case 4:            /* perm */
      sqlite3_result_text(ctx, pCsr->pFile->zPerm, -1,
                          SQLITE_TRANSIENT);


      break;
  }
  return SQLITE_OK;
}

static int fociRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
  FociCursor *pCsr = (FociCursor *)pCursor;







|
|
>

|



<
<
|

<
<
<
|






|
>

>
>
>
>
>
>
>
>







 







|
>


>
>
>
>
>
>
>







 







|
>





>
>
|
>
|
|
>
>
>







 







>
>
|
>
>
>
>







 







|


|



|



|



|


>
>







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25


26
27



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
..
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This routine implements eponymous virtual table for SQLite that gives
** all of the files associated with a single check-in.  The table works
** as a table-valued function.
**
** The source code filename "foci" is short for "Files of Check-in".
**
** Usage example:
**


**    SELECT * FROM files_of_checkin('trunk');
**



** The "schema" for the temp.foci table is:
**
**     CREATE TABLE files_of_checkin(
**       checkinID    INTEGER,    -- RID for the check-in manifest
**       filename     TEXT,       -- Name of a file
**       uuid         TEXT,       -- SHA1 hash of the file
**       previousName TEXT,       -- Name of the file in previous check-in
**       perm         TEXT,       -- Permissions on the file
**       symname      TEXT HIDDEN -- Symbolic name of the check-in.
**     );
**
** The hidden symname column is (optionally) used as a query parameter to
** identify the particular check-in to parse.  The checkinID parameter
** (such is a unique numeric RID rather than symbolic name) can also be used
** to identify the check-in.  Example:
**
**    SELECT * FROM files_of_checkin
**     WHERE checkinID=symbolic_name_to_rid('trunk');
**
*/
#include "config.h"
#include "foci.h"
#include <assert.h>

/*
................................................................................
*/
static const char zFociSchema[] =
@ CREATE TABLE files_of_checkin(
@  checkinID    INTEGER,    -- RID for the check-in manifest
@  filename     TEXT,       -- Name of a file
@  uuid         TEXT,       -- SHA1 hash of the file
@  previousName TEXT,       -- Name of the file in previous check-in
@  perm         TEXT,       -- Permissions on the file
@  symname      TEXT HIDDEN -- Symbolic name of the check-in
@ );
;

#define FOCI_CHECKINID   0
#define FOCI_FILENAME    1
#define FOCI_UUID        2
#define FOCI_PREVNAME    3
#define FOCI_PERM        4
#define FOCI_SYMNAME     5

#if INTERFACE
/*
** The subclasses of sqlite3_vtab  and sqlite3_vtab_cursor tables
** that implement the files_of_checkin virtual table.
*/
struct FociTable {
................................................................................
  return SQLITE_OK;
}

/*
** Available scan methods:
**
**   (0)     A full scan.  Visit every manifest in the repo.  (Slow)
**   (1)     checkinID=?.  visit only the single manifest specified.
**   (2)     symName=?     visit only the single manifest specified.
*/
static int fociBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
  int i;
  pIdxInfo->estimatedCost = 10000.0;
  for(i=0; i<pIdxInfo->nConstraint; i++){
    if( pIdxInfo->aConstraint[i].op==SQLITE_INDEX_CONSTRAINT_EQ
     && (pIdxInfo->aConstraint[i].iColumn==FOCI_CHECKINID
            || pIdxInfo->aConstraint[i].iColumn==FOCI_SYMNAME)
    ){
      if( pIdxInfo->aConstraint[i].iColumn==FOCI_CHECKINID ){
        pIdxInfo->idxNum = 1;
      }else{
        pIdxInfo->idxNum = 2;
      }
      pIdxInfo->estimatedCost = 1.0;
      pIdxInfo->aConstraintUsage[i].argvIndex = 1;
      pIdxInfo->aConstraintUsage[i].omit = 1;
      break;
    }
  }
  return SQLITE_OK;
................................................................................
  sqlite3_vtab_cursor *pCursor,
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  FociCursor *pCur = (FociCursor *)pCursor;
  manifest_destroy(pCur->pMan);
  if( idxNum ){
    int rid;
    if( idxNum==1 ){
      rid = sqlite3_value_int(argv[0]);
    }else{
      rid = symbolic_name_to_rid((const char*)sqlite3_value_text(argv[0]),"ci");
    }
    pCur->pMan = manifest_get(rid, CFTYPE_MANIFEST, 0);
    if( pCur->pMan ){
      manifest_file_rewind(pCur->pMan);
      pCur->pFile = manifest_file_next(pCur->pMan, 0);
    }
  }else{
    pCur->pMan = 0;
  }
................................................................................
static int fociColumn(
  sqlite3_vtab_cursor *pCursor,
  sqlite3_context *ctx,
  int i
){
  FociCursor *pCsr = (FociCursor *)pCursor;
  switch( i ){
    case FOCI_CHECKINID:
      sqlite3_result_int(ctx, pCsr->pMan->rid);
      break;
    case FOCI_FILENAME:
      sqlite3_result_text(ctx, pCsr->pFile->zName, -1,
                          SQLITE_TRANSIENT);
      break;
    case FOCI_UUID:
      sqlite3_result_text(ctx, pCsr->pFile->zUuid, -1,
                          SQLITE_TRANSIENT);
      break;
    case FOCI_PREVNAME:
      sqlite3_result_text(ctx, pCsr->pFile->zPrior, -1,
                          SQLITE_TRANSIENT);
      break;
    case FOCI_PERM:
      sqlite3_result_text(ctx, pCsr->pFile->zPerm, -1,
                          SQLITE_TRANSIENT);
      break;
    case FOCI_SYMNAME:
      break;
  }
  return SQLITE_OK;
}

static int fociRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
  FociCursor *pCsr = (FociCursor *)pCursor;

Added src/fshell.c.













































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/*
** Copyright (c) 2016 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)

** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This module contains the code that implements the "fossil shell" command.
**
** The fossil shell prompts for lines of user input, then parses each line
** after the fashion of a standard Bourne shell and forks a child process
** to run the corresponding Fossil command.  This only works on Unix.
**
** The "fossil shell" command is intended for use with SEE-enabled fossil.
** It allows multiple commands to be issued without having to reenter the
** crypto phasephrase for each command.
*/
#include "config.h"
#include "fshell.h"
#include <ctype.h>

#ifndef _WIN32
#include <sys/types.h>
#include <sys/wait.h>
#endif


/*
** COMMAND: shell*
**
** Usage: %fossil shell
**
** Prompt for lines of input from stdin.  Parse each line and evaluate
** it as a separate fossil command, in a child process.  The initial
** "fossil" is omitted from each line.
**
** This command only works on unix-like platforms that support fork().
** It is non-functional on Windows.
*/
void shell_cmd(void){
#ifdef _WIN32
  fossil_fatal("the 'shell' command is not supported on windows");
#else
  int nArg;
  int mxArg = 0;
  int n, i;
  char **azArg = 0;
  int fDebug;
  pid_t childPid;
  char zLine[10000];
  fDebug = find_option("debug", 0, 0)!=0;
  db_find_and_open_repository(OPEN_ANY_SCHEMA|OPEN_OK_NOT_FOUND, 0);
  db_close(0);
  sqlite3_shutdown();
  while( printf("fossil> "),fflush(stdout),fgets(zLine, sizeof(zLine), stdin) ){
    /* Parse the line of input */
    n = (int)strlen(zLine);
    for(i=0, nArg=1; i<n; i++){
      while( fossil_isspace(zLine[i]) ){ i++; }
      if( i>=n ) break;
      if( nArg>=mxArg ){
        mxArg = nArg+10;
        azArg = fossil_realloc(azArg, sizeof(char*)*mxArg);
        if( nArg==1 ) azArg[0] = g.argv[0];
      }
      if( zLine[i]=='"' || zLine[i]=='\'' ){
        char cQuote = zLine[i];
        i++;
        azArg[nArg++] = &zLine[i];
        for(i++; i<n && zLine[i]!=cQuote; i++){}
      }else{
        azArg[nArg++] = &zLine[i];
        while( i<n && !isspace(zLine[i]) ){ i++; }
      }
      zLine[i] = 0;
    }

    /* If the --debug flag was used, display the parsed arguments */
    if( fDebug ){
      for(i=1; i<nArg; i++){
        fossil_print("argv[%d] = [%s]\n", i, azArg[i]);
      }
    }

    /* Special cases */
    if( nArg<2 ) continue;
    if( fossil_strcmp(azArg[1],"exit")==0 ) break;

    /* Fork a process to handle the command */
    childPid = fork();
    if( childPid<0 ){
      printf("could not fork a child process to handle the command\n");
      fflush(stdout);
      continue;
    }
    if( childPid==0 ){
      /* This is the child process */
      int main(int, char**);
      main(nArg, azArg);
      exit(0);
    }else{
      /* The parent process */
      int status;
      waitpid(childPid, &status, 0);
    }
  }
#endif
}

Changes to src/fusefs.c.

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  ManifestFile *pFile;      /* Name of a cached file */
  Blob content;             /* Content of the cached file */
  /* Parsed path */
  char *az[3];              /* 0=type, 1=id, 2=path */
} fusefs;

/*
** Clear the fusefs.sz[] array.
*/
static void fusefs_clear_path(void){
  int i;
  for(i=0; i<count(fusefs.az); i++){
    fossil_free(fusefs.az[i]);
    fusefs.az[i] = 0;
  }







|







49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  ManifestFile *pFile;      /* Name of a cached file */
  Blob content;             /* Content of the cached file */
  /* Parsed path */
  char *az[3];              /* 0=type, 1=id, 2=path */
} fusefs;

/*
** Clear the fusefs.az[] array.
*/
static void fusefs_clear_path(void){
  int i;
  for(i=0; i<count(fusefs.az); i++){
    fossil_free(fusefs.az[i]);
    fusefs.az[i] = 0;
  }

Changes to src/graph.c.

472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
...
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
  /* Assign rails to all rows that are still unassigned.
  */
  for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
    int parentRid;

    if( pRow->iRail>=0 ){
      if( pRow->pChild==0 && !pRow->timeWarp ){
        if( omitDescenders || pRow->isLeaf ){
          /* no-op */
        }else{
          riser_to_top(pRow);
        }
      }
      continue;
    }
    if( pRow->isDup ){
      continue;
................................................................................
        }
      }
    }
    mask = BIT(pRow->iRail);
    pRow->railInUse |= mask;
    if( pRow->pChild ){
      assignChildrenToRail(pRow);
    }else if( !pRow->isLeaf && !omitDescenders ){
      riser_to_top(pRow);
    }
    if( pParent ){
      for(pLoop=pParent->pPrev; pLoop && pLoop!=pRow; pLoop=pLoop->pPrev){
        pLoop->railInUse |= mask;
      }
    }







|
<
<







 







|







472
473
474
475
476
477
478
479


480
481
482
483
484
485
486
...
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
  /* Assign rails to all rows that are still unassigned.
  */
  for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
    int parentRid;

    if( pRow->iRail>=0 ){
      if( pRow->pChild==0 && !pRow->timeWarp ){
        if( !omitDescenders && count_nonbranch_children(pRow->rid)!=0 ){


          riser_to_top(pRow);
        }
      }
      continue;
    }
    if( pRow->isDup ){
      continue;
................................................................................
        }
      }
    }
    mask = BIT(pRow->iRail);
    pRow->railInUse |= mask;
    if( pRow->pChild ){
      assignChildrenToRail(pRow);
    }else if( !omitDescenders && count_nonbranch_children(pRow->rid)!=0 ){
      riser_to_top(pRow);
    }
    if( pParent ){
      for(pLoop=pParent->pPrev; pLoop && pLoop!=pRow; pLoop=pLoop->pPrev){
        pLoop->railInUse |= mask;
      }
    }

Changes to src/http_ssl.c.

292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
...
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
  }
#endif

  SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

  if( !pUrlData->useProxy ){
    BIO_set_conn_hostname(iBio, pUrlData->name);
    BIO_set_conn_int_port(iBio, &pUrlData->port);
    if( BIO_do_connect(iBio)<=0 ){
      ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
          pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error()));
      ssl_close();
      return 1;
    }
  }
................................................................................

  /* Set the Global.zIpAddr variable to the server we are talking to.
  ** This is used to populate the ipaddr column of the rcvfrom table,
  ** if any files are received from the server.
  */
  {
    /* IPv4 only code */
    const unsigned char *ip = (const unsigned char *) BIO_get_conn_ip(iBio);
    g.zIpAddr = mprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
  }

  X509_free(cert);
  return 0;
}








|







 







|







292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
...
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
  }
#endif

  SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

  if( !pUrlData->useProxy ){
    BIO_set_conn_hostname(iBio, pUrlData->name);
    BIO_ctrl(iBio,BIO_C_SET_CONNECT,3,(char *)&pUrlData->port);
    if( BIO_do_connect(iBio)<=0 ){
      ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
          pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error()));
      ssl_close();
      return 1;
    }
  }
................................................................................

  /* Set the Global.zIpAddr variable to the server we are talking to.
  ** This is used to populate the ipaddr column of the rcvfrom table,
  ** if any files are received from the server.
  */
  {
    /* IPv4 only code */
    const unsigned char *ip = (const unsigned char *) BIO_ptr_ctrl(iBio,BIO_C_GET_CONNECT,2);
    g.zIpAddr = mprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
  }

  X509_free(cert);
  return 0;
}

Changes to src/import.c.

32
33
34
35
36
37
38










39
40
41
42
43
44
45
...
196
197
198
199
200
201
202
203

204
205
206
207
208
209
210
...
273
274
275
276
277
278
279
280

281

282
283

284
285
286
287
288
289
290
291
292
293
294
...
311
312
313
314
315
316
317
318

319
320
321
322
323
324
325
...
747
748
749
750
751
752
753
754



755
756
757
758
759
760
761
...
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
...
967
968
969
970
971
972
973

974
975
976
977
978

979
980
981
982
983
984
985
....
1016
1017
1018
1019
1020
1021
1022
1023

1024

1025
1026

1027
1028

1029
1030
1031
1032
1033
1034

1035
1036
1037
1038
1039
1040

1041

1042
1043

1044
1045
1046
1047
1048
1049
1050

1051
1052
1053
1054
1055
1056
1057
1058
1059

1060
1061
1062
1063
1064
1065
1066
....
1151
1152
1153
1154
1155
1156
1157

1158
1159
1160
1161












1162
1163
1164
1165
1166
1167
1168
....
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
....
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507

1508
1509
1510
1511
1512
1513




1514
1515
1516
1517
1518
1519
1520



1521
1522
1523









1524
1525
1526
1527
1528
1529
1530
....
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547


































1548
1549
1550

1551










1552
1553
1554
1555
1556
1557

1558
1559
1560
1561
1562
1563
1564
....
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
....
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694

1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
  char *zPrior;          /* Prior name if the name was changed */
  char isFrom;           /* True if obtained from the parent */
  char isExe;            /* True if executable */
  char isLink;           /* True if symlink */
};
#endif












/*
** State information about an on-going fast-import parse.
*/
static struct {
  void (*xFinish)(void);      /* Function to finish a prior record */
  int nData;                  /* Bytes of data */
................................................................................
** control artifact to the BLOB table.
*/
static void finish_tag(void){
  Blob record, cksum;
  if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){
    blob_zero(&record);
    blob_appendf(&record, "D %s\n", gg.zDate);
    blob_appendf(&record, "T +%F %s\n", gg.zTag, gg.zFrom);

    blob_appendf(&record, "U %F\n", gg.zUser);
    md5sum_blob(&record, &cksum);
    blob_appendf(&record, "Z %b\n", &cksum);
    fast_insert_content(&record, 0, 0, 1);
    blob_reset(&cksum);
  }
  import_reset(0);
................................................................................
    zFromBranch = 0;
  }

  /* Add the required "T" cards to the manifest. Make sure they are added
  ** in sorted order and without any duplicates. Otherwise, fossil will not
  ** recognize the document as a valid manifest. */
  if( !gg.tagCommit && fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){
    aTCard[nTCard++] = mprintf("T *branch * %F\n", gg.zBranch);

    aTCard[nTCard++] = mprintf("T *sym-%F *\n", gg.zBranch);

    if( zFromBranch ){
      aTCard[nTCard++] = mprintf("T -sym-%F *\n", zFromBranch);

    }
  }
  if( gg.zFrom==0 ){
    aTCard[nTCard++] = mprintf("T *sym-trunk *\n");
  }
  qsort(aTCard, nTCard, sizeof(char *), string_cmp);
  for(i=0; i<nTCard; i++){
    if( i==0 || fossil_strcmp(aTCard[i-1], aTCard[i]) ){
      blob_appendf(&record, "%s", aTCard[i]);
    }
  }
................................................................................
  ** but overwrite that entry if a later instance of the same tag appears.
  **
  ** This behavior seems like a bug in git-fast-export, but it is easier
  ** to work around the problem than to fix git-fast-export.
  */
  if( gg.tagCommit && gg.zDate && gg.zUser && gg.zFrom ){
    blob_appendf(&record, "D %s\n", gg.zDate);
    blob_appendf(&record, "T +sym-%F %s\n", gg.zBranch, gg.zPrevCheckin);

    blob_appendf(&record, "U %F\n", gg.zUser);
    md5sum_blob(&record, &cksum);
    blob_appendf(&record, "Z %b\n", &cksum);
    db_multi_exec(
       "INSERT OR REPLACE INTO xtag(tname, tcontent)"
       " VALUES(%Q,%Q)", gg.zBranch, blob_str(&record)
    );
................................................................................
  const char *zTrunk;         /* Name of trunk folder in repo root */
  int lenTrunk;               /* String length of zTrunk */
  const char *zBranches;      /* Name of branches folder in repo root */
  int lenBranches;            /* String length of zBranches */
  const char *zTags;          /* Name of tags folder in repo root */
  int lenTags;                /* String length of zTags */
  Bag newBranches;            /* Branches that were created in this revision */
  int incrFlag;               /* Add svn-rev-nn tags on every checkin */



} gsvn;
typedef struct {
  char *zKey;
  char *zVal;
} KeyVal;
typedef struct {
  KeyVal *aHeaders;
................................................................................
}

/*
** Returns the UUID for the RID, or NULL if not found.
** The returned string is allocated via db_text() and must be
** free()d by the caller.
*/
char * rid_to_uuid(int rid)
{
  return db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
}

#define SVN_UNKNOWN   0
#define SVN_TRUNK     1
#define SVN_BRANCH    2
#define SVN_TAG       3
................................................................................
    if( !bag_find(&gsvn.newBranches, branchId) ){
      parentRid = db_int(0, "SELECT trid, max(trev) FROM xrevisions"
                            " WHERE trev<%d AND tbranch=%d",
                         gsvn.rev, branchId);
    }
    if( parentRid>0 ){
      pParentManifest = manifest_get(parentRid, CFTYPE_MANIFEST, 0);

      pParentFile = manifest_file_next(pParentManifest, 0);
      parentBranch = db_int(0, "SELECT tbranch FROM xrevisions WHERE trid=%d",
                            parentRid);
      if( parentBranch!=branchId && branchType!=SVN_TAG ){
        sameAsParent = 0;

      }
    }
    if( mergeRid<MAX_INT_32 ){
      if( gsvn.zComment ){
        blob_appendf(&manifest, "C %F\n", gsvn.zComment);
      }else{
        blob_append(&manifest, "C (no\\scomment)\n", 16);
................................................................................
          char *zParentUuid = rid_to_uuid(parentRid);
          if( parentRid==mergeRid || mergeRid==0){
            char *zParentBranch =
              db_text(0, "SELECT tname FROM xbranches WHERE tid=%d",
                      parentBranch
              );
            blob_appendf(&manifest, "P %s\n", zParentUuid);
            blob_appendf(&manifest, "T *branch * %F\n", zBranch);

            blob_appendf(&manifest, "T *sym-%F *\n", zBranch);

            if( gsvn.incrFlag ){
              blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev);

            }
            blob_appendf(&manifest, "T -sym-%F *\n", zParentBranch);

            fossil_free(zParentBranch);
          }else{
            char *zMergeUuid = rid_to_uuid(mergeRid);
            blob_appendf(&manifest, "P %s %s\n", zParentUuid, zMergeUuid);
            if( gsvn.incrFlag ){
              blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev);

            }
            fossil_free(zMergeUuid);
          }
          fossil_free(zParentUuid);
        }else{
          blob_appendf(&manifest, "T *branch * %F\n", zBranch);

          blob_appendf(&manifest, "T *sym-%F *\n", zBranch);

          if( gsvn.incrFlag ){
            blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev);

          }
        }
      }else if( branchType==SVN_TAG ){
        char *zParentUuid = rid_to_uuid(parentRid);
        blob_reset(&manifest);
        blob_appendf(&manifest, "D %s\n", gsvn.zDate);
        blob_appendf(&manifest, "T +sym-%F %s\n", zBranch, zParentUuid);

        fossil_free(zParentUuid);
      }
    }else{
      char *zParentUuid = rid_to_uuid(parentRid);
      blob_appendf(&manifest, "D %s\n", gsvn.zDate);
      if( branchType!=SVN_TAG ){
        blob_appendf(&manifest, "T +closed %s\n", zParentUuid);
      }else{
        blob_appendf(&manifest, "T -sym-%F %s\n", zBranch, zParentUuid);

      }
      fossil_free(zParentUuid);
    }
    if( gsvn.zUser ){
      blob_appendf(&manifest, "U %F\n", gsvn.zUser);
    }else{
      const char *zUserOvrd = find_option("user-override",0,1);
................................................................................
    }
    zDiff += lenData;
  }
}

/*
** Extract the branch or tag that the given path is on. Return the branch ID.

 */
static int svn_parse_path(char *zPath, char **zFile, int *type){
  char *zBranch = 0;
  int branchId = 0;












  *type = SVN_UNKNOWN;
  *zFile = 0;
  if( gsvn.lenTrunk==0 ){
    zBranch = "trunk";
    *zFile = zPath;
    *type = SVN_TRUNK;
  }else
................................................................................
        }
      }else
      if( strncmp(zAction, "change", 6)==0 ){
        int rid = 0;
        if( zKind==0 ){
          fossil_fatal("Missing Node-kind");
        }
        if( strncmp(zKind, "dir", 3)!=0 ){
          if( deltaFlag ){
            Blob deltaSrc;
            Blob target;
            rid = db_int(0, "SELECT rid FROM blob WHERE uuid=("
                            " SELECT tuuid FROM xfiles"
                            "  WHERE tpath=%Q AND tbranch=%d"
                            ")", zFile, branchId);
................................................................................
** The following formats are currently understood by this command
**
**   --git        Import from the git-fast-export file format (default)
**                Options:
**                  --import-marks FILE Restore marks table from FILE
**                  --export-marks FILE Save marks table to FILE
**
**   --svn        Import from the svnadmin-dump file format. The default
**                behaviour (unless overridden by --flat) is to treat 3
**                folders in the SVN root as special, following the
**                common layout of SVN repositories. These are (by
**                default) trunk/, branches/ and tags/

**                Options:
**                  --trunk FOLDER     Name of trunk folder
**                  --branches FOLDER  Name of branches folder
**                  --tags FOLDER      Name of tags folder
**                  --base PATH        Path to project root in repository
**                  --flat             The whole dump is a single branch




**
** Common Options:
**   -i|--incremental   allow importing into an existing repository
**   -f|--force         overwrite repository if already exists
**   -q|--quiet         omit progress output
**   --no-rebuild       skip the "rebuilding metadata" step
**   --no-vacuum        skip the final VACUUM of the database file



**
** The --incremental option allows an existing repository to be extended
** with new content.









**
** See also: export
*/
void import_cmd(void){
  char *zPassword;
  FILE *pIn;
  Stmt q;
................................................................................
  int omitRebuild = find_option("no-rebuild",0,0)!=0;
  int omitVacuum = find_option("no-vacuum",0,0)!=0;

  /* Options common to all input formats */
  int incrFlag = find_option("incremental", "i", 0)!=0;

  /* Options for --svn only */
  const char *zBase="";
  int flatFlag=0;

  /* Options for --git only */
  const char *markfile_in;
  const char *markfile_out;



































  if( svnFlag ){
    /* Get --svn related options here, so verify_all_options() fail when svn
     * only option are specify with --git

     */










    zBase = find_option("base", 0, 1);
    flatFlag = find_option("flat", 0, 0)!=0;
    gsvn.zTrunk = find_option("trunk", 0, 1);
    gsvn.zBranches = find_option("branches", 0, 1);
    gsvn.zTags = find_option("tags", 0, 1);
    gsvn.incrFlag = incrFlag;

  }else if( gitFlag ){
    markfile_in = find_option("import-marks", 0, 1);
    markfile_out = find_option("export-marks", 0, 1);
  }
  verify_all_options();

  if( g.argc!=3 && g.argc!=4 ){
................................................................................
    */
    db_multi_exec(
       "CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT, tuuid TEXT);"
       "CREATE TEMP TABLE xbranch(tname TEXT UNIQUE, brnm TEXT);"
       "CREATE TEMP TABLE xtag(tname TEXT UNIQUE, tcontent TEXT);"
    );

    if(markfile_in){
      FILE *f = fossil_fopen(markfile_in, "r");
      if(!f){
        fossil_fatal("cannot open %s for reading\n", markfile_in);
      }
      if(import_marks(f, &blobs, NULL)<0){
        fossil_fatal("error importing marks from file: %s\n", markfile_in);
      }
      fclose(f);
    }
................................................................................
    while( db_step(&q)==SQLITE_ROW ){
      Blob record;
      db_ephemeral_blob(&q, 0, &record);
      fast_insert_content(&record, 0, 0, 1);
      import_reset(0);
    }
    db_finalize(&q);
    if(markfile_out){
      int rid;
      Stmt q_marks;
      FILE *f;
      db_prepare(&q_marks, "SELECT DISTINCT trid FROM xmark");
      while( db_step(&q_marks)==SQLITE_ROW){
        rid = db_column_int(&q_marks, 0);
        if(db_int(0, "SELECT count(objid) FROM event WHERE objid=%d AND type='ci'", rid)==0){

          if(bag_find(&blobs, rid)==0){
            bag_insert(&blobs, rid);
          }
        }else{
          bag_insert(&vers, rid);
        }
      }
      db_finalize(&q_marks);
      f = fossil_fopen(markfile_out, "w");
      if(!f){
        fossil_fatal("cannot open %s for writing\n", markfile_out);
      }
      export_marks(f, &blobs, &vers);
      fclose(f);
      bag_clear(&blobs);
      bag_clear(&vers);
    }







>
>
>
>
>
>
>
>
>
>







 







|
>







 







|
>
|
>

|
>



|







 







|
>







 







|
>
>
>







 







|
<







 







>
|
|
|
|
|
>







 







|
>
|
>
|
|
>

|
>




|
|
>





|
>
|
>
|
|
>






|
>








|
>







 







>
|



>
>
>
>
>
>
>
>
>
>
>
>







 







|







 







|


|
|
>






>
>
>
>


|
|
|
|
|
>
>
>


|
>
>
>
>
>
>
>
>
>







 







|
|


|
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|
<
>

>
>
>
>
>
>
>
>
>
>





|
>







 







|

|







 







|




|

|
>
|








|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
...
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
...
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
...
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
...
941
942
943
944
945
946
947
948

949
950
951
952
953
954
955
...
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
....
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
....
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
....
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
....
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
....
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642

1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
....
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
....
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
  char *zPrior;          /* Prior name if the name was changed */
  char isFrom;           /* True if obtained from the parent */
  char isExe;            /* True if executable */
  char isLink;           /* True if symlink */
};
#endif

/*
** State information common to all import types.
*/
static struct {
  const char *zTrunkName;     /* Name of trunk branch */
  const char *zBranchPre;     /* Prepended to non-trunk branch names */
  const char *zBranchSuf;     /* Appended to non-trunk branch names */
  const char *zTagPre;        /* Prepended to non-trunk tag names */
  const char *zTagSuf;        /* Appended to non-trunk tag names */
} gimport;

/*
** State information about an on-going fast-import parse.
*/
static struct {
  void (*xFinish)(void);      /* Function to finish a prior record */
  int nData;                  /* Bytes of data */
................................................................................
** control artifact to the BLOB table.
*/
static void finish_tag(void){
  Blob record, cksum;
  if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){
    blob_zero(&record);
    blob_appendf(&record, "D %s\n", gg.zDate);
    blob_appendf(&record, "T +%F%F%F %s\n", gimport.zTagPre, gg.zTag,
        gimport.zTagSuf, gg.zFrom);
    blob_appendf(&record, "U %F\n", gg.zUser);
    md5sum_blob(&record, &cksum);
    blob_appendf(&record, "Z %b\n", &cksum);
    fast_insert_content(&record, 0, 0, 1);
    blob_reset(&cksum);
  }
  import_reset(0);
................................................................................
    zFromBranch = 0;
  }

  /* Add the required "T" cards to the manifest. Make sure they are added
  ** in sorted order and without any duplicates. Otherwise, fossil will not
  ** recognize the document as a valid manifest. */
  if( !gg.tagCommit && fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){
    aTCard[nTCard++] = mprintf("T *branch * %F%F%F\n", gimport.zBranchPre,
        gg.zBranch, gimport.zBranchSuf);
    aTCard[nTCard++] = mprintf("T *sym-%F%F%F *\n", gimport.zBranchPre,
        gg.zBranch, gimport.zBranchSuf);
    if( zFromBranch ){
      aTCard[nTCard++] = mprintf("T -sym-%F%F%F *\n", gimport.zBranchPre,
          zFromBranch, gimport.zBranchSuf);
    }
  }
  if( gg.zFrom==0 ){
    aTCard[nTCard++] = mprintf("T *sym-%F *\n", gimport.zTrunkName);
  }
  qsort(aTCard, nTCard, sizeof(char *), string_cmp);
  for(i=0; i<nTCard; i++){
    if( i==0 || fossil_strcmp(aTCard[i-1], aTCard[i]) ){
      blob_appendf(&record, "%s", aTCard[i]);
    }
  }
................................................................................
  ** but overwrite that entry if a later instance of the same tag appears.
  **
  ** This behavior seems like a bug in git-fast-export, but it is easier
  ** to work around the problem than to fix git-fast-export.
  */
  if( gg.tagCommit && gg.zDate && gg.zUser && gg.zFrom ){
    blob_appendf(&record, "D %s\n", gg.zDate);
    blob_appendf(&record, "T +sym-%F%F%F %s\n", gimport.zBranchPre, gg.zBranch,
        gimport.zBranchSuf, gg.zPrevCheckin);
    blob_appendf(&record, "U %F\n", gg.zUser);
    md5sum_blob(&record, &cksum);
    blob_appendf(&record, "Z %b\n", &cksum);
    db_multi_exec(
       "INSERT OR REPLACE INTO xtag(tname, tcontent)"
       " VALUES(%Q,%Q)", gg.zBranch, blob_str(&record)
    );
................................................................................
  const char *zTrunk;         /* Name of trunk folder in repo root */
  int lenTrunk;               /* String length of zTrunk */
  const char *zBranches;      /* Name of branches folder in repo root */
  int lenBranches;            /* String length of zBranches */
  const char *zTags;          /* Name of tags folder in repo root */
  int lenTags;                /* String length of zTags */
  Bag newBranches;            /* Branches that were created in this revision */
  int revFlag;                /* Add svn-rev-nn tags on every checkin */
  const char *zRevPre;        /* Prepended to revision tag names */
  const char *zRevSuf;        /* Appended to revision tag names */
  const char **azIgnTree;     /* NULL-terminated list of dirs to ignore */
} gsvn;
typedef struct {
  char *zKey;
  char *zVal;
} KeyVal;
typedef struct {
  KeyVal *aHeaders;
................................................................................
}

/*
** Returns the UUID for the RID, or NULL if not found.
** The returned string is allocated via db_text() and must be
** free()d by the caller.
*/
char *rid_to_uuid(int rid){

  return db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
}

#define SVN_UNKNOWN   0
#define SVN_TRUNK     1
#define SVN_BRANCH    2
#define SVN_TAG       3
................................................................................
    if( !bag_find(&gsvn.newBranches, branchId) ){
      parentRid = db_int(0, "SELECT trid, max(trev) FROM xrevisions"
                            " WHERE trev<%d AND tbranch=%d",
                         gsvn.rev, branchId);
    }
    if( parentRid>0 ){
      pParentManifest = manifest_get(parentRid, CFTYPE_MANIFEST, 0);
      if( pParentManifest ){
        pParentFile = manifest_file_next(pParentManifest, 0);
        parentBranch = db_int(0, "SELECT tbranch FROM xrevisions WHERE trid=%d",
                              parentRid);
        if( parentBranch!=branchId && branchType!=SVN_TAG ){
          sameAsParent = 0;
        }
      }
    }
    if( mergeRid<MAX_INT_32 ){
      if( gsvn.zComment ){
        blob_appendf(&manifest, "C %F\n", gsvn.zComment);
      }else{
        blob_append(&manifest, "C (no\\scomment)\n", 16);
................................................................................
          char *zParentUuid = rid_to_uuid(parentRid);
          if( parentRid==mergeRid || mergeRid==0){
            char *zParentBranch =
              db_text(0, "SELECT tname FROM xbranches WHERE tid=%d",
                      parentBranch
              );
            blob_appendf(&manifest, "P %s\n", zParentUuid);
            blob_appendf(&manifest, "T *branch * %F%F%F\n", gimport.zBranchPre,
                zBranch, gimport.zBranchSuf);
            blob_appendf(&manifest, "T *sym-%F%F%F *\n", gimport.zBranchPre,
                zBranch, gimport.zBranchSuf);
            if( gsvn.revFlag ){
              blob_appendf(&manifest, "T +sym-%Fr%d%F *\n", gimport.zTagPre,
                  gsvn.rev, gimport.zTagSuf);
            }
            blob_appendf(&manifest, "T -sym-%F%F%F *\n", gimport.zBranchPre,
                zParentBranch, gimport.zBranchSuf);
            fossil_free(zParentBranch);
          }else{
            char *zMergeUuid = rid_to_uuid(mergeRid);
            blob_appendf(&manifest, "P %s %s\n", zParentUuid, zMergeUuid);
            if( gsvn.revFlag ){
              blob_appendf(&manifest, "T +sym-%F%d%F *\n", gsvn.zRevPre,
                  gsvn.rev, gsvn.zRevSuf);
            }
            fossil_free(zMergeUuid);
          }
          fossil_free(zParentUuid);
        }else{
          blob_appendf(&manifest, "T *branch * %F%F%F\n",
              gimport.zBranchPre, zBranch, gimport.zBranchSuf);
          blob_appendf(&manifest, "T *sym-%F%F%F *\n", gimport.zBranchPre,
              zBranch, gimport.zBranchSuf);
          if( gsvn.revFlag ){
            blob_appendf(&manifest, "T +sym-%F%d%F *\n", gsvn.zRevPre, gsvn.rev,
                gsvn.zRevSuf);
          }
        }
      }else if( branchType==SVN_TAG ){
        char *zParentUuid = rid_to_uuid(parentRid);
        blob_reset(&manifest);
        blob_appendf(&manifest, "D %s\n", gsvn.zDate);
        blob_appendf(&manifest, "T +sym-%F%F%F %s\n", gimport.zTagPre, zBranch,
            gimport.zTagSuf, zParentUuid);
        fossil_free(zParentUuid);
      }
    }else{
      char *zParentUuid = rid_to_uuid(parentRid);
      blob_appendf(&manifest, "D %s\n", gsvn.zDate);
      if( branchType!=SVN_TAG ){
        blob_appendf(&manifest, "T +closed %s\n", zParentUuid);
      }else{
        blob_appendf(&manifest, "T -sym-%F%F%F %s\n", gimport.zBranchPre,
            zBranch, gimport.zBranchSuf, zParentUuid);
      }
      fossil_free(zParentUuid);
    }
    if( gsvn.zUser ){
      blob_appendf(&manifest, "U %F\n", gsvn.zUser);
    }else{
      const char *zUserOvrd = find_option("user-override",0,1);
................................................................................
    }
    zDiff += lenData;
  }
}

/*
** Extract the branch or tag that the given path is on. Return the branch ID.
** Return 0 if not a branch, tag, or trunk, or if ignored by --ignore-tree.
*/
static int svn_parse_path(char *zPath, char **zFile, int *type){
  char *zBranch = 0;
  int branchId = 0;
  if( gsvn.azIgnTree ){
    const char **pzIgnTree;
    unsigned nPath = strlen(zPath);
    for( pzIgnTree = gsvn.azIgnTree; *pzIgnTree; ++pzIgnTree ){
      const char *zIgn = *pzIgnTree;
      int nIgn = strlen(zIgn);
      if( strncmp(zPath, zIgn, nIgn) == 0
       && ( nPath == nIgn || (nPath > nIgn && zPath[nIgn] == '/')) ){
        return 0;
      }
    }
  }
  *type = SVN_UNKNOWN;
  *zFile = 0;
  if( gsvn.lenTrunk==0 ){
    zBranch = "trunk";
    *zFile = zPath;
    *type = SVN_TRUNK;
  }else
................................................................................
        }
      }else
      if( strncmp(zAction, "change", 6)==0 ){
        int rid = 0;
        if( zKind==0 ){
          fossil_fatal("Missing Node-kind");
        }
        if( rec.contentFlag && strncmp(zKind, "dir", 3)!=0 ){
          if( deltaFlag ){
            Blob deltaSrc;
            Blob target;
            rid = db_int(0, "SELECT rid FROM blob WHERE uuid=("
                            " SELECT tuuid FROM xfiles"
                            "  WHERE tpath=%Q AND tbranch=%d"
                            ")", zFile, branchId);
................................................................................
** The following formats are currently understood by this command
**
**   --git        Import from the git-fast-export file format (default)
**                Options:
**                  --import-marks FILE Restore marks table from FILE
**                  --export-marks FILE Save marks table to FILE
**
**   --svn        Import from the svnadmin-dump file format.  The default
**                behaviour (unless overridden by --flat) is to treat 3
**                folders in the SVN root as special, following the
**                common layout of SVN repositories.  These are (by
**                default) trunk/, branches/ and tags/.  The SVN --deltas
**                format is supported but not required.
**                Options:
**                  --trunk FOLDER     Name of trunk folder
**                  --branches FOLDER  Name of branches folder
**                  --tags FOLDER      Name of tags folder
**                  --base PATH        Path to project root in repository
**                  --flat             The whole dump is a single branch
**                  --rev-tags         Tag each revision, implied by -i
**                  --no-rev-tags      Disables tagging effect of -i
**                  --rename-rev PAT   Rev tag names, default "svn-rev-%"
**                  --ignore-tree DIR  Ignores subtree rooted at DIR
**
** Common Options:
**   -i|--incremental     allow importing into an existing repository
**   -f|--force           overwrite repository if already exists
**   -q|--quiet           omit progress output
**   --no-rebuild         skip the "rebuilding metadata" step
**   --no-vacuum          skip the final VACUUM of the database file
**   --rename-trunk NAME  use NAME as name of imported trunk branch
**   --rename-branch PAT  rename all branch names using PAT pattern
**   --rename-tag PAT     rename all tag names using PAT pattern
**
** The --incremental option allows an existing repository to be extended
** with new content.  The --rename-* options may be useful to avoid name
** conflicts when using the --incremental option.
**
** The argument to --rename-* contains one "%" character to be replaced
** with the original name.  For example, "--rename-tag svn-%-tag" renames
** the tag called "release" to "svn-release-tag".
**
** --ignore-tree is useful for importing Subversion repositories which
** move branches to subdirectories of "branches/deleted" instead of
** deleting them.  It can be supplied multiple times if necessary.
**
** See also: export
*/
void import_cmd(void){
  char *zPassword;
  FILE *pIn;
  Stmt q;
................................................................................
  int omitRebuild = find_option("no-rebuild",0,0)!=0;
  int omitVacuum = find_option("no-vacuum",0,0)!=0;

  /* Options common to all input formats */
  int incrFlag = find_option("incremental", "i", 0)!=0;

  /* Options for --svn only */
  const char *zBase = "";
  int flatFlag = 0;

  /* Options for --git only */
  const char *markfile_in = 0;
  const char *markfile_out = 0;

  /* Interpret --rename-* options.  Use a table to avoid code duplication. */
  const struct {
    const char *zOpt, **varPre, *zDefaultPre, **varSuf, *zDefaultSuf;
    int format; /* 1=git, 2=svn, 3=any */
  } renOpts[] = {
    {"rename-branch", &gimport.zBranchPre,   "", &gimport.zBranchSuf, "", 3},
    {"rename-tag"   , &gimport.zTagPre   ,   "", &gimport.zTagSuf   , "", 3},
    {"rename-rev"   , &gsvn.zRevPre, "svn-rev-", &gsvn.zRevSuf      , "", 2},
  }, *renOpt = renOpts;
  int i;
  for( i = 0; i < sizeof(renOpts) / sizeof(*renOpts); ++i, ++renOpt ){
    if( 1 << svnFlag & renOpt->format ){
      const char *zArgument = find_option(renOpt->zOpt, 0, 1);
      if( zArgument ){
         const char *sep = strchr(zArgument, '%');
         if( !sep ){
           fossil_fatal("missing '%%' in argument to --%s", renOpt->zOpt);
         }else if( strchr(sep + 1, '%') ){
           fossil_fatal("multiple '%%' in argument to --%s", renOpt->zOpt);
         }
         *renOpt->varPre = fossil_malloc(sep - zArgument + 1);
         memcpy((char *)*renOpt->varPre, zArgument, sep - zArgument);
         ((char *)*renOpt->varPre)[sep - zArgument] = 0;
         *renOpt->varSuf = sep + 1;
       }else{
         *renOpt->varPre = renOpt->zDefaultPre;
         *renOpt->varSuf = renOpt->zDefaultSuf;
       }
    }
  }
  if( !(gimport.zTrunkName = find_option("rename-trunk", 0, 1)) ){
    gimport.zTrunkName = "trunk";
  }

  if( svnFlag ){
    /* Get --svn related options here, so verify_all_options() fails when

     * svn-only options are specified with --git
     */
    const char *zIgnTree;
    unsigned nIgnTree = 0;
    while( (zIgnTree = find_option("ignore-tree", 0, 1)) ){
      if ( *zIgnTree ){
        gsvn.azIgnTree = fossil_realloc(gsvn.azIgnTree,
            sizeof(*gsvn.azIgnTree) * (nIgnTree + 2));
        gsvn.azIgnTree[nIgnTree++] = zIgnTree;
        gsvn.azIgnTree[nIgnTree] = 0;
      }
    }
    zBase = find_option("base", 0, 1);
    flatFlag = find_option("flat", 0, 0)!=0;
    gsvn.zTrunk = find_option("trunk", 0, 1);
    gsvn.zBranches = find_option("branches", 0, 1);
    gsvn.zTags = find_option("tags", 0, 1);
    gsvn.revFlag = find_option("rev-tags", 0, 0)
                || (incrFlag && !find_option("no-rev-tags", 0, 0));
  }else if( gitFlag ){
    markfile_in = find_option("import-marks", 0, 1);
    markfile_out = find_option("export-marks", 0, 1);
  }
  verify_all_options();

  if( g.argc!=3 && g.argc!=4 ){
................................................................................
    */
    db_multi_exec(
       "CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT, tuuid TEXT);"
       "CREATE TEMP TABLE xbranch(tname TEXT UNIQUE, brnm TEXT);"
       "CREATE TEMP TABLE xtag(tname TEXT UNIQUE, tcontent TEXT);"
    );

    if( markfile_in ){
      FILE *f = fossil_fopen(markfile_in, "r");
      if( !f ){
        fossil_fatal("cannot open %s for reading\n", markfile_in);
      }
      if(import_marks(f, &blobs, NULL)<0){
        fossil_fatal("error importing marks from file: %s\n", markfile_in);
      }
      fclose(f);
    }
................................................................................
    while( db_step(&q)==SQLITE_ROW ){
      Blob record;
      db_ephemeral_blob(&q, 0, &record);
      fast_insert_content(&record, 0, 0, 1);
      import_reset(0);
    }
    db_finalize(&q);
    if( markfile_out ){
      int rid;
      Stmt q_marks;
      FILE *f;
      db_prepare(&q_marks, "SELECT DISTINCT trid FROM xmark");
      while( db_step(&q_marks)==SQLITE_ROW ){
        rid = db_column_int(&q_marks, 0);
        if( db_int(0, "SELECT count(objid) FROM event"
                      " WHERE objid=%d AND type='ci'", rid)==0 ){
          if( bag_find(&blobs, rid)==0 ){
            bag_insert(&blobs, rid);
          }
        }else{
          bag_insert(&vers, rid);
        }
      }
      db_finalize(&q_marks);
      f = fossil_fopen(markfile_out, "w");
      if( !f ){
        fossil_fatal("cannot open %s for writing\n", markfile_out);
      }
      export_marks(f, &blobs, &vers);
      fclose(f);
      bag_clear(&blobs);
      bag_clear(&vers);
    }

Changes to src/info.c.

152
153
154
155
156
157
158











159
160
161
162
163
164
165
...
187
188
189
190
191
192
193

194
195
196
197
198
199
200
...
206
207
208
209
210
211
212

213
214
215
216
217
218
219
....
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
....
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
....
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
....
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837






2838
2839
2840
2841
2842
2843
2844
  while( db_step(&s)==SQLITE_ROW ){
    fossil_print("access-url:   %-54s %s\n", db_column_text(&s, 0),
                 db_column_text(&s, 1));
  }
  db_finalize(&s);
}













/*
** COMMAND: info
**
** Usage: %fossil info ?VERSION | REPOSITORY_FILENAME? ?OPTIONS?
**
** With no arguments, provide information about the current tree.
................................................................................

  if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){
    db_open_config(0, 0);
    db_open_repository(g.argv[2]);
    db_record_repository_filename(g.argv[2]);
    fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
    fossil_print("project-code: %s\n", db_get("project-code", "<none>"));

    extraRepoInfo();
    return;
  }
  db_find_and_open_repository(0,0);
  verify_all_options();
  if( g.argc==2 ){
    int vid;
................................................................................
      fossil_print("local-root:   %s\n", g.zLocalRoot);
    }
    if( verboseFlag ) extraRepoInfo();
    if( g.zConfigDbName ){
      fossil_print("config-db:    %s\n", g.zConfigDbName);
    }
    fossil_print("project-code: %s\n", db_get("project-code", ""));

    vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
    if( vid ){
      show_common_info(vid, "checkout:", 1, 1);
    }
    fossil_print("check-ins:    %d\n",
             db_int(-1, "SELECT count(*) FROM event WHERE type='ci' /*scan*/"));
  }else{
................................................................................
        objType |= OBJTYPE_CHECKIN;
      }else if( zType[0]=='e' ){
        if( eventTagId != 0) {
          @ Instance of technote
          objType |= OBJTYPE_EVENT;
          hyperlink_to_event_tagid(db_column_int(&q, 5));
        }else{
          @ Attachment to technote 
        }
      }else{
        @ Tag referencing
      }
      if( zType[0]!='e' || eventTagId == 0){
        hyperlink_to_uuid(zUuid);
      }
................................................................................
          output_text_with_line_numbers(z, zLn);
        }else{
          @ <pre>
          @ %h(z)
          @ </pre>
        }
      }else if( strncmp(zMime, "image/", 6)==0 ){
        @ <i>(file is %d(blob_size(&content)) bytes of image data)</i><br>
        @ <img src="%R/raw/%s(zUuid)?m=%s(zMime)" />
        style_submenu_element("Image", "Image",
                              "%R/raw/%s?m=%s", zUuid, zMime);
      }else{
        @ <i>(file is %d(blob_size(&content)) bytes of binary data)</i>
      }
      @ </blockquote>
................................................................................
  modPending = moderation_pending(rid);
  if( modPending ){
    @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
  }
  @ <tr><th>Ticket:</th>
  @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a>
  if( zTktTitle ){
        @<br>%h(zTktTitle)
  }
  @</td></tr>
  @ <tr><th>Date:</th><td>
  hyperlink_to_date(zDate, "</td></tr>");
  @ <tr><th>User:</th><td>
  hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>");
  @ </table>
................................................................................
**
** Options:
**
**    --author USER           Make USER the author for check-in
**    -m|--comment COMMENT    Make COMMENT the check-in comment
**    -M|--message-file FILE  Read the amended comment from FILE
**    -e|--edit-comment       Launch editor to revise comment
**    --date DATE             Make DATE the check-in time
**    --bgcolor COLOR         Apply COLOR to this check-in
**    --branchcolor COLOR     Apply and propagate COLOR to the branch
**    --tag TAG               Add new TAG to this check-in
**    --cancel TAG            Cancel TAG from this check-in
**    --branch NAME           Make this check-in the start of branch NAME
**    --hide                  Hide branch starting from this check-in
**    --close                 Mark this "leaf" as closed






*/
void ci_amend_cmd(void){
  int rid;
  const char *zComment;         /* Current comment on the check-in */
  const char *zNewComment;      /* Revised check-in comment */
  const char *zComFile;         /* Filename from which to read comment */
  const char *zUser;            /* Current user for the check-in */







>
>
>
>
>
>
>
>
>
>
>







 







>







 







>







 







|







 







|







 







|







 







|







>
>
>
>
>
>







152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
...
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
....
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
....
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
....
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
....
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
  while( db_step(&s)==SQLITE_ROW ){
    fossil_print("access-url:   %-54s %s\n", db_column_text(&s, 0),
                 db_column_text(&s, 1));
  }
  db_finalize(&s);
}

/*
** Show the parent project, if any
*/
static void showParentProject(void){
  const char *zParentCode;
  zParentCode = db_get("parent-project-code",0);
  if( zParentCode ){
    fossil_print("derived-from: %s %s\n", zParentCode, db_get("parent-project-name",""));
  }
}


/*
** COMMAND: info
**
** Usage: %fossil info ?VERSION | REPOSITORY_FILENAME? ?OPTIONS?
**
** With no arguments, provide information about the current tree.
................................................................................

  if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){
    db_open_config(0, 0);
    db_open_repository(g.argv[2]);
    db_record_repository_filename(g.argv[2]);
    fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
    fossil_print("project-code: %s\n", db_get("project-code", "<none>"));
    showParentProject();
    extraRepoInfo();
    return;
  }
  db_find_and_open_repository(0,0);
  verify_all_options();
  if( g.argc==2 ){
    int vid;
................................................................................
      fossil_print("local-root:   %s\n", g.zLocalRoot);
    }
    if( verboseFlag ) extraRepoInfo();
    if( g.zConfigDbName ){
      fossil_print("config-db:    %s\n", g.zConfigDbName);
    }
    fossil_print("project-code: %s\n", db_get("project-code", ""));
    showParentProject();
    vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
    if( vid ){
      show_common_info(vid, "checkout:", 1, 1);
    }
    fossil_print("check-ins:    %d\n",
             db_int(-1, "SELECT count(*) FROM event WHERE type='ci' /*scan*/"));
  }else{
................................................................................
        objType |= OBJTYPE_CHECKIN;
      }else if( zType[0]=='e' ){
        if( eventTagId != 0) {
          @ Instance of technote
          objType |= OBJTYPE_EVENT;
          hyperlink_to_event_tagid(db_column_int(&q, 5));
        }else{
          @ Attachment to technote
        }
      }else{
        @ Tag referencing
      }
      if( zType[0]!='e' || eventTagId == 0){
        hyperlink_to_uuid(zUuid);
      }
................................................................................
          output_text_with_line_numbers(z, zLn);
        }else{
          @ <pre>
          @ %h(z)
          @ </pre>
        }
      }else if( strncmp(zMime, "image/", 6)==0 ){
        @ <i>(file is %d(blob_size(&content)) bytes of image data)</i><br />
        @ <img src="%R/raw/%s(zUuid)?m=%s(zMime)" />
        style_submenu_element("Image", "Image",
                              "%R/raw/%s?m=%s", zUuid, zMime);
      }else{
        @ <i>(file is %d(blob_size(&content)) bytes of binary data)</i>
      }
      @ </blockquote>
................................................................................
  modPending = moderation_pending(rid);
  if( modPending ){
    @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
  }
  @ <tr><th>Ticket:</th>
  @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a>
  if( zTktTitle ){
        @<br />%h(zTktTitle)
  }
  @</td></tr>
  @ <tr><th>Date:</th><td>
  hyperlink_to_date(zDate, "</td></tr>");
  @ <tr><th>User:</th><td>
  hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>");
  @ </table>
................................................................................
**
** Options:
**
**    --author USER           Make USER the author for check-in
**    -m|--comment COMMENT    Make COMMENT the check-in comment
**    -M|--message-file FILE  Read the amended comment from FILE
**    -e|--edit-comment       Launch editor to revise comment
**    --date DATETIME         Make DATETIME the check-in time
**    --bgcolor COLOR         Apply COLOR to this check-in
**    --branchcolor COLOR     Apply and propagate COLOR to the branch
**    --tag TAG               Add new TAG to this check-in
**    --cancel TAG            Cancel TAG from this check-in
**    --branch NAME           Make this check-in the start of branch NAME
**    --hide                  Hide branch starting from this check-in
**    --close                 Mark this "leaf" as closed
**
** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
** year-month-day form, it may be truncated, the "T" may be replaced by
** a space, and it may also name a timezone offset from UTC as "-HH:MM"
** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z"
** means UTC.
*/
void ci_amend_cmd(void){
  int rid;
  const char *zComment;         /* Current comment on the check-in */
  const char *zNewComment;      /* Revised check-in comment */
  const char *zComFile;         /* Filename from which to read comment */
  const char *zUser;            /* Current user for the check-in */

Changes to src/json.c.

1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
....
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
....
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
....
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007

#define INT(OBJ,K) cson_object_set(o, #K, json_new_int(OBJ.K))
#define CSTR(OBJ,K) cson_object_set(o, #K, OBJ.K ? json_new_string(OBJ.K) : cson_value_null())
#define VAL(K,V) cson_object_set(o, #K, (V) ? (V) : cson_value_null())
  VAL(capabilities, json_cap_value());
  INT(g, argc);
  INT(g, isConst);
  INT(g, useAttach);
  CSTR(g, zConfigDbName);
  INT(g, repositoryOpen);
  INT(g, localOpen);
  INT(g, minPrefix);
  INT(g, fSqlTrace);
  INT(g, fSqlStats);
  INT(g, fSqlPrint);
................................................................................
  INT(g, rcvid);
  INT(g, okCsrf);
  INT(g, thTrace);
  INT(g, isHome);
  INT(g, nAux);
  INT(g, allowSymlinks);

  CSTR(g, zMainDbType);
  CSTR(g, zConfigDbType);
  CSTR(g, zOpenRevision);
  CSTR(g, zLocalRoot);
  CSTR(g, zPath);
  CSTR(g, zExtra);
  CSTR(g, zBaseURL);
  CSTR(g, zTop);
  CSTR(g, zContentType);
................................................................................
** Implementation of the /json/stat page/command.
**
*/
cson_value * json_page_stat(){
  i64 t, fsize;
  int n, m;
  int full;
  const char *zDb;
  enum { BufLen = 1000 };
  char zBuf[BufLen];
  cson_value * jv = NULL;
  cson_object * jo = NULL;
  cson_value * jv2 = NULL;
  cson_object * jo2 = NULL;
  char * zTmp = NULL;
................................................................................

  jv2 = cson_value_new_object();
  jo2 = cson_value_get_object(jv2);
  cson_object_set(jo, "sqlite", jv2);
  sqlite3_snprintf(BufLen, zBuf, "%.19s [%.10s] (%s)",
                   sqlite3_sourceid(), &sqlite3_sourceid()[20], sqlite3_libversion());
  SETBUF(jo2, "version");
  zDb = db_name("repository");
  cson_object_set(jo2, "pageCount", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA \"%w\".page_count", zDb)));
  cson_object_set(jo2, "pageSize", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA \"%w\".page_size", zDb)));
  cson_object_set(jo2, "freeList", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA \"%w\".freelist_count", zDb)));
  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA \"%w\".encoding", zDb));
  SETBUF(jo2, "encoding");
  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA \"%w\".journal_mode", zDb));
  cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
  return jv;
#undef SETBUF
}










<







 







<
<







 







<







 







<
|
|
|
|

|







1257
1258
1259
1260
1261
1262
1263

1264
1265
1266
1267
1268
1269
1270
....
1293
1294
1295
1296
1297
1298
1299


1300
1301
1302
1303
1304
1305
1306
....
1900
1901
1902
1903
1904
1905
1906

1907
1908
1909
1910
1911
1912
1913
....
1983
1984
1985
1986
1987
1988
1989

1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002

#define INT(OBJ,K) cson_object_set(o, #K, json_new_int(OBJ.K))
#define CSTR(OBJ,K) cson_object_set(o, #K, OBJ.K ? json_new_string(OBJ.K) : cson_value_null())
#define VAL(K,V) cson_object_set(o, #K, (V) ? (V) : cson_value_null())
  VAL(capabilities, json_cap_value());
  INT(g, argc);
  INT(g, isConst);

  CSTR(g, zConfigDbName);
  INT(g, repositoryOpen);
  INT(g, localOpen);
  INT(g, minPrefix);
  INT(g, fSqlTrace);
  INT(g, fSqlStats);
  INT(g, fSqlPrint);
................................................................................
  INT(g, rcvid);
  INT(g, okCsrf);
  INT(g, thTrace);
  INT(g, isHome);
  INT(g, nAux);
  INT(g, allowSymlinks);



  CSTR(g, zOpenRevision);
  CSTR(g, zLocalRoot);
  CSTR(g, zPath);
  CSTR(g, zExtra);
  CSTR(g, zBaseURL);
  CSTR(g, zTop);
  CSTR(g, zContentType);
................................................................................
** Implementation of the /json/stat page/command.
**
*/
cson_value * json_page_stat(){
  i64 t, fsize;
  int n, m;
  int full;

  enum { BufLen = 1000 };
  char zBuf[BufLen];
  cson_value * jv = NULL;
  cson_object * jo = NULL;
  cson_value * jv2 = NULL;
  cson_object * jo2 = NULL;
  char * zTmp = NULL;
................................................................................

  jv2 = cson_value_new_object();
  jo2 = cson_value_get_object(jv2);
  cson_object_set(jo, "sqlite", jv2);
  sqlite3_snprintf(BufLen, zBuf, "%.19s [%.10s] (%s)",
                   sqlite3_sourceid(), &sqlite3_sourceid()[20], sqlite3_libversion());
  SETBUF(jo2, "version");

  cson_object_set(jo2, "pageCount", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA repository.page_count")));
  cson_object_set(jo2, "pageSize", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA repository.page_size")));
  cson_object_set(jo2, "freeList", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA repository.freelist_count")));
  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA repository.encoding"));
  SETBUF(jo2, "encoding");
  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA repository.journal_mode"));
  cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
  return jv;
#undef SETBUF
}



Changes to src/loadctrl.c.

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  }
#endif
  return 0.0;
}

/*
** COMMAND: test-loadavg
** 
** %fossil test-loadavg
**
** Print the load average on the host machine.
*/
void loadavg_test_cmd(void){
  fossil_print("load-average: %f\n", load_average());
}







|







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  }
#endif
  return 0.0;
}

/*
** COMMAND: test-loadavg
**
** %fossil test-loadavg
**
** Print the load average on the host machine.
*/
void loadavg_test_cmd(void){
  fossil_print("load-average: %f\n", load_average());
}

Changes to src/login.c.

172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
...
391
392
393
394
395
396
397







398
399
400
401
402
403
404
...
986
987
988
989
990
991
992



993



994
995
996
997
998
999
1000
1001
1002
1003


1004
1005
1006
1007
1008
1009
1010
1011
1012
1013










1014
1015
1016
1017
1018
1019
1020
....
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
....
1107
1108
1109
1110
1111
1112
1113

1114
1115
1116
1117
1118
1119
1120
....
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
....
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
}

/*
** Make sure the accesslog table exists.  Create it if it does not
*/
void create_accesslog_table(void){
  db_multi_exec(
    "CREATE TABLE IF NOT EXISTS %s.accesslog("
    "  uname TEXT,"
    "  ipaddr TEXT,"
    "  success BOOLEAN,"
    "  mtime TIMESTAMP"
    ");", db_name("repository")
  );
}

/*
** Make a record of a login attempt, if login record keeping is enabled.
*/
static void record_login_attempt(
................................................................................
    if( prefix_match("spider", zAgent+i) ) return 0;
    if( prefix_match("crawl", zAgent+i) ) return 0;
    /* If a URI appears in the User-Agent, it is probably a bot */
    if( strncmp("http", zAgent+i,4)==0 ) return 0;
  }
  if( strncmp(zAgent, "Mozilla/", 8)==0 ){
    if( atoi(&zAgent[8])<4 ) return 0;  /* Many bots advertise as Mozilla/3 */







    if( sqlite3_strglob("*Firefox/[1-9]*", zAgent)==0 ) return 1;
    if( sqlite3_strglob("*Chrome/[1-9]*", zAgent)==0 ) return 1;
    if( sqlite3_strglob("*(compatible;?MSIE?[1789]*", zAgent)==0 ) return 1;
    if( sqlite3_strglob("*Trident/[1-9]*;?rv:[1-9]*", zAgent)==0 ) return 1; /* IE11+ */
    if( sqlite3_strglob("*AppleWebKit/[1-9]*(KHTML*", zAgent)==0 ) return 1;
    return 0;
  }
................................................................................
  /* Set the global variables recording the userid and login.  The
  ** "nobody" user is a special case in that g.zLogin==0.
  */
  g.userUid = uid;
  if( fossil_strcmp(g.zLogin,"nobody")==0 ){
    g.zLogin = 0;
  }



  g.isHuman = g.zLogin==0 ? isHuman(P("HTTP_USER_AGENT")) : 1;




  /* Set the capabilities */
  login_replace_capabilities(zCap, 0);
  login_set_anon_nobody_capabilities();

  /* The auto-hyperlink setting allows hyperlinks to be displayed for users
  ** who do not have the "h" permission as long as their UserAgent string
  ** makes it appear that they are human.  Check to see if auto-hyperlink is
  ** enabled for this repository and make appropriate adjustments to the
  ** permission flags if it is.


  */
  if( zCap[0]
   && !g.perm.Hyperlink
   && g.isHuman
   && db_get_boolean("auto-hyperlink",1)
  ){
    g.perm.Hyperlink = 1;
    g.javascriptHyperlink = 1;
  }











  /* If the public-pages glob pattern is defined and REQUEST_URI matches
  ** one of the globs in public-pages, then also add in all default-perms
  ** permissions.
  */
  zPublicPages = db_get("public-pages",0);
  if( zPublicPages!=0 ){
    Glob *pGlob = glob_create(zPublicPages);
................................................................................
      case 's':   p->Setup = 1; /* Fall thru into Admin */
      case 'a':   p->Admin = p->RdTkt = p->WrTkt = p->Zip =
                             p->RdWiki = p->WrWiki = p->NewWiki =
                             p->ApndWiki = p->Hyperlink = p->Clone =
                             p->NewTkt = p->Password = p->RdAddr =
                             p->TktFmt = p->Attach = p->ApndTkt =
                             p->ModWiki = p->ModTkt = p->Delete =
                             p->Private = 1;
                             /* Fall thru into Read/Write */
      case 'i':   p->Read = p->Write = 1;                      break;
      case 'o':   p->Read = 1;                                 break;
      case 'z':   p->Zip = 1;                                  break;

      case 'd':   p->Delete = 1;                               break;
      case 'h':   p->Hyperlink = 1;                            break;
................................................................................
      case 'w':   p->WrTkt = p->RdTkt = p->NewTkt =
                  p->ApndTkt = 1;                              break;
      case 'c':   p->ApndTkt = 1;                              break;
      case 'q':   p->ModTkt = 1;                               break;
      case 't':   p->TktFmt = 1;                               break;
      case 'b':   p->Attach = 1;                               break;
      case 'x':   p->Private = 1;                              break;


      /* The "u" privileges is a little different.  It recursively
      ** inherits all privileges of the user named "reader" */
      case 'u': {
        if( (flags & LOGIN_IGNORE_UV)==0 ){
          const char *zUser;
          zUser = db_text("", "SELECT cap FROM user WHERE login='reader'");
................................................................................
      case 'r':  rc = p->RdTkt;     break;
      case 's':  rc = p->Setup;     break;
      case 't':  rc = p->TktFmt;    break;
      /* case 'u': READER    */
      /* case 'v': DEVELOPER */
      case 'w':  rc = p->WrTkt;     break;
      case 'x':  rc = p->Private;   break;
      /* case 'y': */
      case 'z':  rc = p->Zip;       break;
      default:   rc = 0;            break;
    }
  }
  return rc;
}

................................................................................
  char *zSelfRepo;           /* Name of our repository */
  char *zSelfLabel;          /* Project-name for our repository */
  char *zSelfProjCode;       /* Our project-code */
  char *zSql;                /* SQL to run on all peers */
  const char *zSelf;         /* The ATTACH name of our repository */

  *pzErrMsg = 0;   /* Default to no errors */
  zSelf = db_name("repository");

  /* Get the full pathname of the other repository */
  file_canonical_name(zRepo, &fullName, 0);
  zRepo = fossil_strdup(blob_str(&fullName));
  blob_reset(&fullName);

  /* Get the full pathname for our repository.  Also the project code







|




|







 







>
>
>
>
>
>
>







 







>
>
>
|
>
>
>



<





|
>
>










>
>
>
>
>
>
>
>
>
>







 







|







 







>







 







|







 







|







172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
...
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
...
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009

1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
....
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
....
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
....
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
....
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
}

/*
** Make sure the accesslog table exists.  Create it if it does not
*/
void create_accesslog_table(void){
  db_multi_exec(
    "CREATE TABLE IF NOT EXISTS repository.accesslog("
    "  uname TEXT,"
    "  ipaddr TEXT,"
    "  success BOOLEAN,"
    "  mtime TIMESTAMP"
    ");"
  );
}

/*
** Make a record of a login attempt, if login record keeping is enabled.
*/
static void record_login_attempt(
................................................................................
    if( prefix_match("spider", zAgent+i) ) return 0;
    if( prefix_match("crawl", zAgent+i) ) return 0;
    /* If a URI appears in the User-Agent, it is probably a bot */
    if( strncmp("http", zAgent+i,4)==0 ) return 0;
  }
  if( strncmp(zAgent, "Mozilla/", 8)==0 ){
    if( atoi(&zAgent[8])<4 ) return 0;  /* Many bots advertise as Mozilla/3 */

    /* 2016-05-30:  A pernicious spider that likes to walk Fossil timelines has
    ** been detected on the SQLite website.  The spider changes its user-agent
    ** string frequently, but it always seems to include the following text:
    */
    if( sqlite3_strglob("*Safari/537.36Mozilla/5.0*", zAgent)==0 ) return 0;

    if( sqlite3_strglob("*Firefox/[1-9]*", zAgent)==0 ) return 1;
    if( sqlite3_strglob("*Chrome/[1-9]*", zAgent)==0 ) return 1;
    if( sqlite3_strglob("*(compatible;?MSIE?[1789]*", zAgent)==0 ) return 1;
    if( sqlite3_strglob("*Trident/[1-9]*;?rv:[1-9]*", zAgent)==0 ) return 1; /* IE11+ */
    if( sqlite3_strglob("*AppleWebKit/[1-9]*(KHTML*", zAgent)==0 ) return 1;
    return 0;
  }
................................................................................
  /* Set the global variables recording the userid and login.  The
  ** "nobody" user is a special case in that g.zLogin==0.
  */
  g.userUid = uid;
  if( fossil_strcmp(g.zLogin,"nobody")==0 ){
    g.zLogin = 0;
  }
  if( PB("isrobot") ){
    g.isHuman = 0;
  }else if( g.zLogin==0 ){
    g.isHuman = isHuman(P("HTTP_USER_AGENT"));
  }else{
    g.isHuman = 1;
  }

  /* Set the capabilities */
  login_replace_capabilities(zCap, 0);


  /* The auto-hyperlink setting allows hyperlinks to be displayed for users
  ** who do not have the "h" permission as long as their UserAgent string
  ** makes it appear that they are human.  Check to see if auto-hyperlink is
  ** enabled for this repository and make appropriate adjustments to the
  ** permission flags if it is.  This should be done before the permissions
  ** are (potentially) copied to the anonymous permission set; otherwise,
  ** those will be out-of-sync.
  */
  if( zCap[0]
   && !g.perm.Hyperlink
   && g.isHuman
   && db_get_boolean("auto-hyperlink",1)
  ){
    g.perm.Hyperlink = 1;
    g.javascriptHyperlink = 1;
  }

  /*
  ** At this point, the capabilities for the logged in user are not going
  ** to be modified anymore; therefore, we can copy them over to the ones
  ** for the anonymous user.
  **
  ** WARNING: In the future, please do not add code after this point that
  **          modifies the capabilities for the logged in user.
  */
  login_set_anon_nobody_capabilities();

  /* If the public-pages glob pattern is defined and REQUEST_URI matches
  ** one of the globs in public-pages, then also add in all default-perms
  ** permissions.
  */
  zPublicPages = db_get("public-pages",0);
  if( zPublicPages!=0 ){
    Glob *pGlob = glob_create(zPublicPages);
................................................................................
      case 's':   p->Setup = 1; /* Fall thru into Admin */
      case 'a':   p->Admin = p->RdTkt = p->WrTkt = p->Zip =
                             p->RdWiki = p->WrWiki = p->NewWiki =
                             p->ApndWiki = p->Hyperlink = p->Clone =
                             p->NewTkt = p->Password = p->RdAddr =
                             p->TktFmt = p->Attach = p->ApndTkt =
                             p->ModWiki = p->ModTkt = p->Delete =
                             p->WrUnver = p->Private = 1;
                             /* Fall thru into Read/Write */
      case 'i':   p->Read = p->Write = 1;                      break;
      case 'o':   p->Read = 1;                                 break;
      case 'z':   p->Zip = 1;                                  break;

      case 'd':   p->Delete = 1;                               break;
      case 'h':   p->Hyperlink = 1;                            break;
................................................................................
      case 'w':   p->WrTkt = p->RdTkt = p->NewTkt =
                  p->ApndTkt = 1;                              break;
      case 'c':   p->ApndTkt = 1;                              break;
      case 'q':   p->ModTkt = 1;                               break;
      case 't':   p->TktFmt = 1;                               break;
      case 'b':   p->Attach = 1;                               break;
      case 'x':   p->Private = 1;                              break;
      case 'y':   p->WrUnver = 1;                              break;

      /* The "u" privileges is a little different.  It recursively
      ** inherits all privileges of the user named "reader" */
      case 'u': {
        if( (flags & LOGIN_IGNORE_UV)==0 ){
          const char *zUser;
          zUser = db_text("", "SELECT cap FROM user WHERE login='reader'");
................................................................................
      case 'r':  rc = p->RdTkt;     break;
      case 's':  rc = p->Setup;     break;
      case 't':  rc = p->TktFmt;    break;
      /* case 'u': READER    */
      /* case 'v': DEVELOPER */
      case 'w':  rc = p->WrTkt;     break;
      case 'x':  rc = p->Private;   break;
      case 'y':  rc = p->WrUnver;   break;
      case 'z':  rc = p->Zip;       break;
      default:   rc = 0;            break;
    }
  }
  return rc;
}

................................................................................
  char *zSelfRepo;           /* Name of our repository */
  char *zSelfLabel;          /* Project-name for our repository */
  char *zSelfProjCode;       /* Our project-code */
  char *zSql;                /* SQL to run on all peers */
  const char *zSelf;         /* The ATTACH name of our repository */

  *pzErrMsg = 0;   /* Default to no errors */
  zSelf = "repository";

  /* Get the full pathname of the other repository */
  file_canonical_name(zRepo, &fullName, 0);
  zRepo = fossil_strdup(blob_str(&fullName));
  blob_reset(&fullName);

  /* Get the full pathname for our repository.  Also the project code

Changes to src/lookslike.c.

48
49
50
51
52
53
54































55
56
57
58
59
60
61
...
130
131
132
133
134
135
136
137
138
139
140
141
142
143


144
145
146

147
148
149
150

151

152
153
154
155
156
157
158
159
160
161


162
163

164
165
166



167
168
169
170
171
172
173
174
175
176
177
178
...
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
#define LOOK_ODD     ((int)0x00000080) /* An odd number of bytes was found. */
#define LOOK_SHORT   ((int)0x00000100) /* Unable to perform full check. */
#define LOOK_INVALID ((int)0x00000200) /* Invalid sequence was found. */
#define LOOK_BINARY  (LOOK_NUL | LOOK_LONG | LOOK_SHORT) /* May be binary. */
#define LOOK_EOL     (LOOK_LONE_CR | LOOK_LONE_LF | LOOK_CRLF) /* Line seps. */
#endif /* INTERFACE */

































/*
** This function attempts to scan each logical line within the blob to
** determine the type of content it appears to contain.  The return value
** is a combination of one or more of the LOOK_XXX flags (see above):
**
** !LOOK_BINARY -- The content appears to consist entirely of text; however,
................................................................................
  }
  if( j>LENGTH_MASK ){
    flags |= LOOK_LONG;  /* Very long line -> binary */
  }
  return flags;
}


/*
** Checks for proper UTF-8. It uses the method described in:
**   http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences
** except for the "overlong form" of \u0000 which is not considered invalid
** here: Some languages like Java and Tcl use it. For UTF-8 characters
** > 7f, the variable 'c2' not necessary means the previous character.


** It's number of higher 1-bits indicate the number of continuation bytes
** that are expected to be followed. E.g. when 'c2' has a value in the range
** 0xc0..0xdf it means that 'c' is expected to contain the last continuation

** byte of a UTF-8 character. A value 0xe0..0xef means that after 'c' one
** more continuation byte is expected.
*/


int invalid_utf8(const Blob *pContent){

  const unsigned char *z = (unsigned char *) blob_buffer(pContent);
  unsigned int n = blob_size(pContent);
  unsigned char c, c2;

  if( n==0 ) return 0;  /* Empty file -> OK */
  c = *z;
  while( --n>0 ){
    c2 = c;
    c = *++z;
    if( c2>=0x80 ){


      if( ((c2<0xc2) || (c2>=0xf4) || ((c&0xc0)!=0x80)) &&
          (((c2!=0xf4) || (c>=0x90)) && ((c2!=0xc0) || (c!=0x80))) ){

        return LOOK_INVALID; /* Invalid UTF-8 */
      }
      c = (c2 >= 0xe0) ? (c2<<1)+1 : ' ';



    }
  }
  return (c>=0x80) ? LOOK_INVALID : 0; /* Last byte must be ASCII. */
}


/*
** Define the type needed to represent a Unicode (UTF-16) character.
*/
#ifndef WCHAR_T
#  ifdef _WIN32
#    define WCHAR_T wchar_t
................................................................................
      fUnicode = 0;
    }else{
      fUnicode = could_be_utf16(&blob, 0) || fForceUtf16;
    }
    if( fUnicode ){
      lookFlags = looks_like_utf16(&blob, bRevUtf16, 0);
    }else{
      lookFlags = looks_like_utf8(&blob, 0)|invalid_utf8(&blob);
    }
  }
  fossil_print("File \"%s\" has %d bytes.\n",g.argv[2],blob_size(&blob));
  fossil_print("Starts with UTF-8 BOM: %s\n",fUtf8?"yes":"no");
  fossil_print("Starts with UTF-16 BOM: %s\n",
               fUtf16?(bRevUtf16?"reversed":"yes"):"no");
  fossil_print("Looks like UTF-%s: %s\n",fUnicode?"16":"8",







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







<



|
|
|
>
>
|
|
<
>
|
|


>
|
>


|




<
<
|
>
>
|
|
>


<
>
>
>


|

<







 







|







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
...
161
162
163
164
165
166
167

168
169
170
171
172
173
174
175
176
177

178
179
180
181
182
183
184
185
186
187
188
189
190
191
192


193
194
195
196
197
198
199
200

201
202
203
204
205
206
207

208
209
210
211
212
213
214
...
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
#define LOOK_ODD     ((int)0x00000080) /* An odd number of bytes was found. */
#define LOOK_SHORT   ((int)0x00000100) /* Unable to perform full check. */
#define LOOK_INVALID ((int)0x00000200) /* Invalid sequence was found. */
#define LOOK_BINARY  (LOOK_NUL | LOOK_LONG | LOOK_SHORT) /* May be binary. */
#define LOOK_EOL     (LOOK_LONE_CR | LOOK_LONE_LF | LOOK_CRLF) /* Line seps. */
#endif /* INTERFACE */

/* definitions for various UTF-8 sequence lengths, encoded as start value
 * and size of each valid range belonging to some lead byte*/
#define US2A  0x80, 0x01 /* for lead byte 0xC0 */
#define US2B  0x80, 0x40 /* for lead bytes 0xC2-0xDF */
#define US3A  0xA0, 0x20 /* for lead byte 0xE0 */
#define US3B  0x80, 0x40 /* for lead bytes 0xE1-0xEF */
#define US4A  0x90, 0x30 /* for lead byte 0xF0 */
#define US4B  0x80, 0x40 /* for lead bytes 0xF1-0xF3 */
#define US4C  0x80, 0x10 /* for lead byte 0xF4 */
#define US0A  0x00, 0x00 /* for any other lead byte */

/* a table used for quick lookup of the definition that goes with a
 * particular lead byte */
static const unsigned char lb_tab[] = {
  US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A,
  US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A,
  US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A,
  US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A,
  US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A,
  US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A,
  US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A,
  US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A,
  US2A, US0A, US2B, US2B, US2B, US2B, US2B, US2B,
  US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B,
  US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B,
  US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B,
  US3A, US3B, US3B, US3B, US3B, US3B, US3B, US3B,
  US3B, US3B, US3B, US3B, US3B, US3B, US3B, US3B,
  US4A, US4B, US4B, US4B, US4C, US0A, US0A, US0A,
  US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A
};

/*
** This function attempts to scan each logical line within the blob to
** determine the type of content it appears to contain.  The return value
** is a combination of one or more of the LOOK_XXX flags (see above):
**
** !LOOK_BINARY -- The content appears to consist entirely of text; however,
................................................................................
  }
  if( j>LENGTH_MASK ){
    flags |= LOOK_LONG;  /* Very long line -> binary */
  }
  return flags;
}


/*
** Checks for proper UTF-8. It uses the method described in:
**   http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences
** except for the "overlong form" of \u0000 which is not considered
** invalid here: Some languages like Java and Tcl use it. This function
** also considers valid the derivatives CESU-8 & WTF-8 (as described in
** the same wikipedia article referenced previously). For UTF-8 characters
** > 0x7f, the variable 'c' not necessary means the real lead byte.
** It's number of higher 1-bits indicate the number of continuation
** bytes that are expected to be followed. E.g. when 'c' has a value

** in the range 0xc0..0xdf it means that after 'c' a single continuation
** byte is expected. A value 0xe0..0xef means that after 'c' two more
** continuation bytes are expected.
*/

int invalid_utf8(
  const Blob *pContent
){
  const unsigned char *z = (unsigned char *) blob_buffer(pContent);
  unsigned int n = blob_size(pContent);
  unsigned char c; /* lead byte to be handled. */

  if( n==0 ) return 0;  /* Empty file -> OK */
  c = *z;
  while( --n>0 ){


    if( c>=0x80 ){
      const unsigned char *def; /* pointer to range table*/

      c <<= 1; /* multiply by 2 and get rid of highest bit */
      def = &lb_tab[c]; /* search fb's valid range in table */
      if( (unsigned int)(*++z-def[0])>=def[1] ){
        return LOOK_INVALID; /* Invalid UTF-8 */
      }

      c = (c>=0xC0) ? (c|3) : ' '; /* determine next lead byte */
    } else {
      c = *++z;
    }
  }
  return (c>=0x80) ? LOOK_INVALID : 0; /* Final lead byte must be ASCII. */
}


/*
** Define the type needed to represent a Unicode (UTF-16) character.
*/
#ifndef WCHAR_T
#  ifdef _WIN32
#    define WCHAR_T wchar_t
................................................................................
      fUnicode = 0;
    }else{
      fUnicode = could_be_utf16(&blob, 0) || fForceUtf16;
    }
    if( fUnicode ){
      lookFlags = looks_like_utf16(&blob, bRevUtf16, 0);
    }else{
      lookFlags = looks_like_utf8(&blob, 0) | invalid_utf8(&blob);
    }
  }
  fossil_print("File \"%s\" has %d bytes.\n",g.argv[2],blob_size(&blob));
  fossil_print("Starts with UTF-8 BOM: %s\n",fUtf8?"yes":"no");
  fossil_print("Starts with UTF-16 BOM: %s\n",
               fUtf16?(bRevUtf16?"reversed":"yes"):"no");
  fossil_print("Looks like UTF-%s: %s\n",fUnicode?"16":"8",

Changes to src/main.c.

89
90
91
92
93
94
95

96
97
98
99
100
101
102
...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
...
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
...
556
557
558
559
560
561
562

563
564
565
566
567
568
569
...
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
...
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770

771
772
773
774
775
776
777
...
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
...
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
....
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
....
1639
1640
1641
1642
1643
1644
1645
1646
1647

1648
1649
1650
1651
1652
1653
1654
....
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
....
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
....
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
....
2016
2017
2018
2019
2020
2021
2022
2023
2024


2025
2026
2027
2028

2029
2030
2031
2032
2033
2034
2035
2036



















































2037
2038
2039
2040
2041
2042
2043
....
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143

2144
2145

2146

2147
2148
2149
2150
2151
2152
2153
....
2300
2301
2302
2303
2304
2305
2306

2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318


2319
2320
2321
2322
2323
2324
2325
  char WrTkt;            /* w: make changes to tickets via web */
  char ModTkt;           /* q: approve and publish ticket changes (Moderator) */
  char Attach;           /* b: add attachments */
  char TktFmt;           /* t: create new ticket report formats */
  char RdAddr;           /* e: read email addresses or other private data */
  char Zip;              /* z: download zipped artifact via /zip URL */
  char Private;          /* x: can send and receive private content */

};

#ifdef FOSSIL_ENABLE_TCL
/*
** All Tcl related context information is in this structure.  This structure
** definition has been copied from and should be kept in sync with the one in
** "th_tcl.c".
................................................................................
  char *nameOfExe;        /* Full path of executable. */
  const char *zErrlog;    /* Log errors to this file, if not NULL */
  int isConst;            /* True if the output is unchanging & cacheable */
  const char *zVfsName;   /* The VFS to use for database connections */
  sqlite3 *db;            /* The connection to the databases */
  sqlite3 *dbConfig;      /* Separate connection for global_config table */
  char *zAuxSchema;       /* Main repository aux-schema */
  int useAttach;          /* True if global_config is attached to repository */
  const char *zConfigDbName;/* Path of the config database. NULL if not open */
  sqlite3_int64 now;      /* Seconds since 1970 */
  int repositoryOpen;     /* True if the main repository database is open */
  char *zRepositoryOption; /* Most recent cached repository option value */
  char *zRepositoryName;  /* Name of the repository database */
  char *zLocalDbName;     /* Name of the local database */
  const char *zMainDbType;/* "configdb", "localdb", or "repository" */
  const char *zConfigDbType;  /* "configdb", "localdb", or "repository" */
  char *zOpenRevision;    /* Check-in version to use during database open */
  int localOpen;          /* True if the local database is open */
  char *zLocalRoot;       /* The directory holding the  local database */
  int minPrefix;          /* Number of digits needed for a distinct UUID */
  int fSqlTrace;          /* True if --sqltrace flag is present */
  int fSqlStats;          /* True if --sqltrace or --sqlstats are present */
  int fSqlPrint;          /* True if -sqlprint flag is present */
................................................................................
*/
#define CGIDEBUG(X)  if( g.fDebug ) cgi_debug X

#endif

Global g;

/*
** The table of web pages supported by this application is generated
** automatically by the "mkindex" program and written into a file
** named "page_index.h".  We include that file here to get access
** to the table.
*/
#include "page_index.h"

/*
** Search for a function whose name matches zName.  Write a pointer to
** that function into *pxFunc and return 0.  If no match is found,
** return 1.  If the command is ambiguous return 2;
**
** The NameMap structure and the tables we are searching against are
** defined in the page_index.h header file which is automatically
** generated by mkindex.c program.
*/
static int name_search(
  const char *zName,       /* The name we are looking for */
  const NameMap *aMap,     /* Search in this array */
  int nMap,                /* Number of slots in aMap[] */
  int iBegin,              /* Lower bound on the array search */
  int *pIndex              /* OUT: The index in aMap[] of the match */
){
  int upr, lwr, cnt, m, i;
  int n = strlen(zName);
  lwr = iBegin;
  upr = nMap-1;
  while( lwr<=upr ){
    int mid, c;
    mid = (upr+lwr)/2;
    c = fossil_strcmp(zName, aMap[mid].zName);
    if( c==0 ){
      *pIndex = mid;
      return 0;
    }else if( c<0 ){
      upr = mid - 1;
    }else{
      lwr = mid + 1;
    }
  }
  for(m=cnt=0, i=upr-2; cnt<2 && i<=upr+3 && i<nMap; i++){
    if( i<iBegin ) continue;
    if( strncmp(zName, aMap[i].zName, n)==0 ){
      m = i;
      cnt++;
    }
  }
  if( cnt==1 ){
    *pIndex = m;
    return 0;
  }
  return 1+(cnt>1);
}

/*
** atexit() handler which frees up "some" of the resources
** used by fossil.
*/
static void fossil_atexit(void) {
#if defined(_WIN32) && !defined(_WIN64) && defined(FOSSIL_ENABLE_TCL) && \
    defined(USE_TCL_STUBS)
................................................................................
static void fossil_sqlite_log(void *notUsed, int iCode, const char *zErrmsg){
#ifdef __APPLE__
  /* Disable the file alias warning on apple products because Time Machine
  ** creates lots of aliases and the warning alarms people. */
  if( iCode==SQLITE_WARNING ) return;
#endif
  if( iCode==SQLITE_SCHEMA ) return;

  fossil_warning("%s: %s", fossil_sqlite_return_code_name(iCode), zErrmsg);
}

/*
** This function attempts to find command line options known to contain
** bitwise flags and initializes the associated global variables.  After
** this function executes, all global variables (i.e. in the "g" struct)
................................................................................
#if defined(_WIN32)
int _CRT_glob = 0x0001; /* See MinGW bug #2062 */
#endif
int main(int argc, char **argv)
#endif
{
  const char *zCmdName = "unknown";
  int idx;
  int rc;
  if( sqlite3_libversion_number()<3010000 ){
    fossil_fatal("Unsuitable SQLite version %s, must be at least 3.10.0",
                 sqlite3_libversion());
  }
  sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
  sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
  memset(&g, 0, sizeof(g));
  g.now = time(0);
  g.httpHeader = empty_blob;
................................................................................
      g.httpOut = stdout;
      g.fullHttpReply = !g.isHTTP;
      fossil_fatal("file descriptor 2 is not open. (fd=%d, errno=%d)",
                   fd, x);
    }
  }
#endif
  rc = name_search(zCmdName, aCommand, count(aCommand), FOSSIL_FIRST_CMD, &idx);
  if( rc==1 ){
#ifdef FOSSIL_ENABLE_TH1_HOOKS
    if( !g.isHTTP && !g.fNoThHook ){
      rc = Th_CommandHook(zCmdName, 0);
    }else{
      rc = TH_OK;
    }
    if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
      if( rc==TH_OK || rc==TH_RETURN ){
#endif
        fossil_fatal("%s: unknown command: %s\n"
                     "%s: use \"help\" for more information\n",
                     g.argv[0], zCmdName, g.argv[0]);
#ifdef FOSSIL_ENABLE_TH1_HOOKS
      }
      if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
        Th_CommandNotify(zCmdName, 0);
      }
    }
    fossil_exit(0);
#endif
  }else if( rc==2 ){
    int i, n;
    Blob couldbe;
    blob_zero(&couldbe);
    n = strlen(zCmdName);
    for(i=0; i<count(aCommand); i++){
      if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){
        blob_appendf(&couldbe, " %s", aCommand[i].zName);
      }
    }

    fossil_print("%s: ambiguous command prefix: %s\n"
                 "%s: could be any of:%s\n"
                 "%s: use \"help\" for more information\n",
                 g.argv[0], zCmdName, g.argv[0], blob_str(&couldbe), g.argv[0]);
    fossil_exit(1);
  }
  atexit( fossil_atexit );
................................................................................
  ** TH_RETURN: The xFunc() will be executed, the TH1 notification will be
  **            skipped.
  **
  ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be
  **              executed.
  */
  if( !g.isHTTP && !g.fNoThHook ){
    rc = Th_CommandHook(aCommand[idx].zName, aCommand[idx].cmdFlags);
  }else{
    rc = TH_OK;
  }
  if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
    if( rc==TH_OK || rc==TH_RETURN ){
#endif
      aCommand[idx].xFunc();
#ifdef FOSSIL_ENABLE_TH1_HOOKS
    }
    if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
      Th_CommandNotify(aCommand[idx].zName, aCommand[idx].cmdFlags);
    }
  }
#endif
  fossil_exit(0);
  /*NOT_REACHED*/
  return 0;
}
................................................................................
    }
  }
}

/*
** Print a list of words in multiple columns.
*/
static void multi_column_list(const char **azWord, int nWord){
  int i, j, len;
  int mxLen = 0;
  int nCol;
  int nRow;
  for(i=0; i<nWord; i++){
    len = strlen(azWord[i]);
    if( len>mxLen ) mxLen = len;
  }
  nCol = 80/(mxLen+2);
  if( nCol==0 ) nCol = 1;
  nRow = (nWord + nCol - 1)/nCol;
  for(i=0; i<nRow; i++){
    const char *zSpacer = "";
    for(j=i; j<nWord; j+=nRow){
      fossil_print("%s%-*s", zSpacer, mxLen, azWord[j]);
      zSpacer = "  ";
    }
    fossil_print("\n");
  }
}

/*
** List of commands starting with zPrefix, or all commands if zPrefix is NULL.
*/
static void command_list(const char *zPrefix, int cmdMask){
  int i, nCmd;
  int nPrefix = zPrefix ? strlen(zPrefix) : 0;
  const char *aCmd[count(aCommand)];
  for(i=nCmd=0; i<count(aCommand); i++){
    const char *z = aCommand[i].zName;
    if( (aCommand[i].cmdFlags & cmdMask)==0 ) continue;
    if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue;
    aCmd[nCmd++] = aCommand[i].zName;
  }
  multi_column_list(aCmd, nCmd);
}

/*
** COMMAND: test-list-webpage
**
** List all web pages.
*/
void cmd_test_webpage_list(void){
  int i, nCmd;
  const char *aCmd[count(aCommand)];
  for(i=nCmd=0; i<count(aCommand); i++){
    if(0x08 & aCommand[i].cmdFlags){
      aCmd[nCmd++] = aWebpage[i].zName;
    }
  }
  assert(nCmd && "page list is empty?");
  multi_column_list(aCmd, nCmd);
}



/*
** This function returns a human readable version string.
*/
const char *get_version(){
  static const char version[] = RELEASE_VERSION " " MANIFEST_VERSION " "
................................................................................
  @ <blockquote><pre>
  @ %h(blob_str(&versionInfo))
  @ </pre></blockquote>
  style_footer();
}


/*
** COMMAND: help
**
** Usage: %fossil help COMMAND
**    or: %fossil COMMAND --help
**
** Display information on how to use COMMAND.  To display a list of
** available commands use one of:
**
**    %fossil help              Show common commands
**    %fossil help -a|--all     Show both common and auxiliary commands
**    %fossil help -t|--test    Show test commands only
**    %fossil help -x|--aux     Show auxiliary commands only
**    %fossil help -w|--www     Show list of WWW pages
*/
void help_cmd(void){
  int rc, idx, isPage = 0;
  const char *z;
  const char *zCmdOrPage;
  const char *zCmdOrPagePlural;
  if( g.argc<3 ){
    z = g.argv[0];
    fossil_print(
      "Usage: %s help COMMAND\n"
      "Common COMMANDs:  (use \"%s help -a|--all\" for a complete list)\n",
      z, z);
    command_list(0, CMDFLAG_1ST_TIER);
    version_cmd();
    return;
  }
  if( find_option("all","a",0) ){
    command_list(0, CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER);
    return;
  }
  else if( find_option("www","w",0) ){
    command_list(0, CMDFLAG_WEBPAGE);
    return;
  }
  else if( find_option("aux","x",0) ){
    command_list(0, CMDFLAG_2ND_TIER);
    return;
  }
  else if( find_option("test","t",0) ){
    command_list(0, CMDFLAG_TEST);
    return;
  }
  isPage = ('/' == *g.argv[2]) ? 1 : 0;
  if(isPage){
    zCmdOrPage = "page";
    zCmdOrPagePlural = "pages";
  }else{
    zCmdOrPage = "command";
    zCmdOrPagePlural = "commands";
  }
  rc = name_search(g.argv[2], aCommand, count(aCommand), 0, &idx);
  if( rc==1 ){
    fossil_print("unknown %s: %s\nAvailable %s:\n",
                 zCmdOrPage, g.argv[2], zCmdOrPagePlural);
    command_list(0, isPage ? CMDFLAG_WEBPAGE : (0xff & ~CMDFLAG_WEBPAGE));
    fossil_exit(1);
  }else if( rc==2 ){
    fossil_print("ambiguous %s prefix: %s\nMatching %s:\n",
                 zCmdOrPage, g.argv[2], zCmdOrPagePlural);
    command_list(g.argv[2], 0xff);
    fossil_exit(1);
  }
  z = aCmdHelp[idx].zText;
  if( z==0 ){
    fossil_fatal("no help available for the %s %s",
                 aCommand[idx].zName, zCmdOrPage);
  }
  while( *z ){
    if( *z=='%' && strncmp(z, "%fossil", 7)==0 ){
      fossil_print("%s", g.argv[0]);
      z += 7;
    }else{
      putchar(*z);
      z++;
    }
  }
  putchar('\n');
}

/*
** COMMAND: test-all-help
** 
** Usage: %fossil test-all-help ?OPTIONS?
**
** Show help text for commands and pages.  Useful for proof-reading.
** Defaults to just the CLI commands.  Specify --www to see only the
** web pages, or --everything to see both commands and pages.
**
** Options:
**    -e|--everything   Show all commands and pages.
**    -t|--test         Include test- commands
**    -w|--www          Show WWW pages.
*/
void test_all_help_cmd(void){
  int i;
  int mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER;

  if( find_option("www","w",0) ){
    mask = CMDFLAG_WEBPAGE;
  }
  if( find_option("everything","e",0) ){
    mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE;
  }
  if( find_option("test","t",0) ){
    mask |= CMDFLAG_TEST;
  }
  fossil_print("Help text for:\n");
  if( mask & CMDFLAG_1ST_TIER ) fossil_print(" * Commands\n");
  if( mask & CMDFLAG_2ND_TIER ) fossil_print(" * Auxiliary commands\n");
  if( mask & CMDFLAG_TEST )     fossil_print(" * Test commands\n");
  if( mask & CMDFLAG_WEBPAGE )  fossil_print(" * Web pages\n");
  fossil_print("---\n");
  for(i=0; i<count(aCommand); i++){
    if( (aCommand[i].cmdFlags & mask)==0 ) continue;
    fossil_print("# %s\n", aCommand[i].zName);
    fossil_print("%s\n\n", aCmdHelp[i].zText);
  }
  fossil_print("---\n");
  version_cmd();
}

/*
** WEBPAGE: help
** URL: /help?name=CMD
**
** Show the built-in help text for CMD.  CMD can be a command-line interface
** command or a page name from the web interface.
*/
void help_page(void){
  const char *zCmd = P("cmd");

  if( zCmd==0 ) zCmd = P("name");
  style_header("Command-line Help");
  if( zCmd ){
    int rc, idx;
    char *z, *s, *d;
    const char *zCmdOrPage = ('/'==*zCmd) ? "page" : "command";
    style_submenu_element("Command-List", "Command-List", "%s/help", g.zTop);
    @ <h1>The "%s(zCmd)" %s(zCmdOrPage):</h1>
    rc = name_search(zCmd, aCommand, count(aCommand), 0, &idx);
    if( rc==1 ){
      @ unknown command: %s(zCmd)
    }else if( rc==2 ){
      @ ambiguous command prefix: %s(zCmd)
    }else{
      z = (char*)aCmdHelp[idx].zText;
      if( z==0 ){
        @ no help available for the %s(aCommand[idx].zName) command
      }else{
        z=s=d=mprintf("%s",z);
        while( *s ){
          if( *s=='%' && strncmp(s, "%fossil", 7)==0 ){
            s++;
          }else{
            *d++ = *s++;
          }
        }
        *d = 0;
        @ <blockquote><pre>
        @ %h(z)
        @ </pre></blockquote>
        fossil_free(z);
      }
    }
  }else{
    int i, j, n;

    @ <h1>Available commands:</h1>
    @ <table border="0"><tr>
    for(i=j=0; i<count(aCommand); i++){
      const char *z = aCommand[i].zName;
      if( '/'==*z || strncmp(z,"test",4)==0 ) continue;
      j++;
    }
    n = (j+6)/7;
    for(i=j=0; i<count(aCommand); i++){
      const char *z = aCommand[i].zName;
      if( '/'==*z || strncmp(z,"test",4)==0 ) continue;
      if( j==0 ){
        @ <td valign="top"><ul>
      }
      @ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li>
      j++;
      if( j>=n ){
        @ </ul></td>
        j = 0;
      }
    }
    if( j>0 ){
      @ </ul></td>
    }
    @ </tr></table>

    @ <h1>Available web UI pages:</h1>
    @ <table border="0"><tr>
    for(i=j=0; i<count(aCommand); i++){
      const char *z = aCommand[i].zName;
      if( '/'!=*z ) continue;
      j++;
    }
    n = (j+4)/5;
    for(i=j=0; i<count(aCommand); i++){
      const char *z = aCommand[i].zName;
      if( '/'!=*z ) continue;
      if( j==0 ){
        @ <td valign="top"><ul>
      }
      if( aCmdHelp[i].zText && *aCmdHelp[i].zText ){
        @ <li><a href="%R/help?cmd=%s(z)">%s(z+1)</a></li>
      }else{
        @ <li>%s(z+1)</li>
      }
      j++;
      if( j>=n ){
        @ </ul></td>
        j = 0;
      }
    }
    if( j>0 ){
      @ </ul></td>
    }
    @ </tr></table>

    @ <h1>Unsupported commands:</h1>
    @ <table border="0"><tr>
    for(i=j=0; i<count(aCommand); i++){
      const char *z = aCommand[i].zName;
      if( strncmp(z,"test",4)!=0 ) continue;
      j++;
    }
    n = (j+3)/4;
    for(i=j=0; i<count(aCommand); i++){
      const char *z = aCommand[i].zName;
      if( strncmp(z,"test",4)!=0 ) continue;
      if( j==0 ){
        @ <td valign="top"><ul>
      }
      if( aCmdHelp[i].zText && *aCmdHelp[i].zText ){
        @ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li>
      }else{
        @ <li>%s(z)</li>
      }
      j++;
      if( j>=n ){
        @ </ul></td>
        j = 0;
      }
    }
    if( j>0 ){
      @ </ul></td>
    }
    @ </tr></table>

  }
  style_footer();
}

/*
** WEBPAGE: test-all-help
**
** Show all help text on a single page.  Useful for proof-reading.
*/
void test_all_help_page(void){
  int i;
  style_header("Testpage: All Help Text");
  for(i=0; i<count(aCommand); i++){
    if( memcmp(aCommand[i].zName, "test", 4)==0 ) continue;
    @ <h2>%s(aCommand[i].zName):</h2>
    @ <blockquote><pre>
    @ %h(aCmdHelp[i].zText)
    @ </pre></blockquote>
  }
  style_footer();
}

/*
** Set the g.zBaseURL value to the full URL for the toplevel of
** the fossil tree.  Set g.zTop to g.zBaseURL without the
** leading "http://" and the host and port.
**
** The g.zBaseURL is normally set based on HTTP_HOST and SCRIPT_NAME
** environment variables.  However, if zAltBase is not NULL then it
................................................................................
  const char *zNotFound,      /* Redirect here on a 404 if not NULL */
  Glob *pFileGlob,            /* Deliver static files matching */
  int allowRepoList           /* Send repo list for "/" URL */
){
  const char *zPathInfo;
  const char *zDirPathInfo;
  char *zPath = NULL;
  int idx;
  int i;


  /* Handle universal query parameters */
  if( PB("utc") ){
    g.fTimeFormat = 1;
  }else if( PB("localtime") ){
    g.fTimeFormat = 2;
  }
................................................................................
    }
#endif
  }

  /* Locate the method specified by the path and execute the function
  ** that implements that method.
  */
  if( name_search(g.zPath, aWebpage, count(aWebpage), 0, &idx) ){
#ifdef FOSSIL_ENABLE_JSON
    if(g.json.isJsonMode){
      json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,0);
    }else
#endif
    {
#ifdef FOSSIL_ENABLE_TH1_HOOKS
................................................................................
        }
        if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
          Th_WebpageNotify(g.zPath, 0);
        }
      }
#endif
    }
  }else if( aWebpage[idx].xFunc!=page_xfer && db_schema_is_outofdate() ){
#ifdef FOSSIL_ENABLE_JSON
    if(g.json.isJsonMode){
      json_err(FSL_JSON_E_DB_NEEDS_REBUILD,NULL,0);
    }else
#endif
    {
      @ <h1>Server Configuration Error</h1>
................................................................................
    **            skipped.
    **
    ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be
    **              executed.
    */
    int rc;
    if( !g.fNoThHook ){
      rc = Th_WebpageHook(aWebpage[idx].zName, aWebpage[idx].cmdFlags);
    }else{
      rc = TH_OK;
    }
    if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
      if( rc==TH_OK || rc==TH_RETURN ){
#endif
        aWebpage[idx].xFunc();
#ifdef FOSSIL_ENABLE_TH1_HOOKS
      }
      if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
        Th_WebpageNotify(aWebpage[idx].zName, aWebpage[idx].cmdFlags);
      }
    }
#endif
  }

  /* Return the result.
  */
................................................................................
    cgi_reply();
  }
}

/*
** COMMAND: cgi*
**
** Usage: %fossil ?cgi? SCRIPT
**


** The SCRIPT argument is the name of a file that is the CGI script
** that is being run.  The command name, "cgi", may be omitted if
** the GATEWAY_INTERFACE environment variable is set to "CGI" (which
** should always be the case for CGI scripts run by a webserver.)  The

** SCRIPT file should look something like this:
**
**      #!/usr/bin/fossil
**      repository: /home/somebody/project.db
**
** The second line defines the name of the repository.  After locating
** the repository, fossil will generate a webpage on stdout based on
** the values of standard CGI environment variables.



















































**
** See also: http, server, winsrv
*/
void cmd_cgi(void){
  const char *zFile;
  const char *zNotFound = 0;
  char **azRedirect = 0;             /* List of repositories to redirect to */
................................................................................
      ** found it is returned verbatim.  This feature allows "fossil server"
      ** to function as a primitive web-server delivering arbitrary content.
      */
      pFileGlob = glob_create(blob_str(&value));
      blob_reset(&value);
      continue;
    }
    if( blob_eq(&key, "setenv:") && blob_token(&line, &value)
            && blob_token(&line, &value2) ){
      /* setenv: NAME VALUE

      **
      ** Sets environment variable NAME to VALUE

      */

      fossil_setenv(blob_str(&value), blob_str(&value2));
      blob_reset(&value);
      blob_reset(&value2);
      continue;
    }
    if( blob_eq(&key, "debug:") && blob_token(&line, &value) ){
      /* debug: FILENAME
................................................................................
**   --https          signal a request coming in via https
**   --nojail         drop root privilege but do not enter the chroot jail
**   --nossl          signal that no SSL connections are available
**   --notfound URL   use URL as "HTTP 404, object not found" page.
**   --repolist       If REPOSITORY is directory, URL "/" lists all repos
**   --scgi           Interpret input as SCGI rather than HTTP
**   --skin LABEL     Use override skin LABEL

**
** See also: cgi, server, winsrv
*/
void cmd_http(void){
  const char *zIpAddr = 0;
  const char *zNotFound;
  const char *zHost;
  const char *zAltBase;
  const char *zFileGlob;
  int useSCGI;
  int noJail;
  int allowRepoList;



  /* The winhttp module passes the --files option as --files-urlenc with
  ** the argument being URL encoded, to avoid wildcard expansion in the
  ** shell.  This option is for internal use and is undocumented.
  */
  zFileGlob = find_option("files-urlenc",0,1);
  if( zFileGlob ){







>







 







|




|
|
<
<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







>







 







|

|
|







 







|











|










<

|
<
<
<
<
<
<
>







 







|






|



|







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







<

>







 







|







 







|







 







|






|



|







 







|

>
>
|
<
<
|
>
|




<
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
<

>

|
>

>







 







>












>
>







89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
...
126
127
128
129
130
131
132
133
134
135
136
137
138
139


140
141
142
143
144
145
146
...
292
293
294
295
296
297
298























































299
300
301
302
303
304
305
...
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
...
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
...
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706

707
708






709
710
711
712
713
714
715
716
...
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
...
881
882
883
884
885
886
887























































888
889
890
891
892
893
894
....
1025
1026
1027
1028
1029
1030
1031























































































































































































































































































1032
1033
1034
1035
1036
1037
1038
....
1244
1245
1246
1247
1248
1249
1250

1251
1252
1253
1254
1255
1256
1257
1258
1259
....
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
....
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
....
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
....
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632


1633
1634
1635
1636
1637
1638
1639

1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
....
1790
1791
1792
1793
1794
1795
1796
1797

1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
....
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
  char WrTkt;            /* w: make changes to tickets via web */
  char ModTkt;           /* q: approve and publish ticket changes (Moderator) */
  char Attach;           /* b: add attachments */
  char TktFmt;           /* t: create new ticket report formats */
  char RdAddr;           /* e: read email addresses or other private data */
  char Zip;              /* z: download zipped artifact via /zip URL */
  char Private;          /* x: can send and receive private content */
  char WrUnver;          /* y: can push unversioned content */
};

#ifdef FOSSIL_ENABLE_TCL
/*
** All Tcl related context information is in this structure.  This structure
** definition has been copied from and should be kept in sync with the one in
** "th_tcl.c".
................................................................................
  char *nameOfExe;        /* Full path of executable. */
  const char *zErrlog;    /* Log errors to this file, if not NULL */
  int isConst;            /* True if the output is unchanging & cacheable */
  const char *zVfsName;   /* The VFS to use for database connections */
  sqlite3 *db;            /* The connection to the databases */
  sqlite3 *dbConfig;      /* Separate connection for global_config table */
  char *zAuxSchema;       /* Main repository aux-schema */
  int dbIgnoreErrors;     /* Ignore database errors if true */
  const char *zConfigDbName;/* Path of the config database. NULL if not open */
  sqlite3_int64 now;      /* Seconds since 1970 */
  int repositoryOpen;     /* True if the main repository database is open */
  char *zRepositoryOption; /* Most recent cached repository option value */
  char *zRepositoryName;  /* Name of the repository database file */
  char *zLocalDbName;     /* Name of the local database file */


  char *zOpenRevision;    /* Check-in version to use during database open */
  int localOpen;          /* True if the local database is open */
  char *zLocalRoot;       /* The directory holding the  local database */
  int minPrefix;          /* Number of digits needed for a distinct UUID */
  int fSqlTrace;          /* True if --sqltrace flag is present */
  int fSqlStats;          /* True if --sqltrace or --sqlstats are present */
  int fSqlPrint;          /* True if -sqlprint flag is present */
................................................................................
*/
#define CGIDEBUG(X)  if( g.fDebug ) cgi_debug X

#endif

Global g;
























































/*
** atexit() handler which frees up "some" of the resources
** used by fossil.
*/
static void fossil_atexit(void) {
#if defined(_WIN32) && !defined(_WIN64) && defined(FOSSIL_ENABLE_TCL) && \
    defined(USE_TCL_STUBS)
................................................................................
static void fossil_sqlite_log(void *notUsed, int iCode, const char *zErrmsg){
#ifdef __APPLE__
  /* Disable the file alias warning on apple products because Time Machine
  ** creates lots of aliases and the warning alarms people. */
  if( iCode==SQLITE_WARNING ) return;
#endif
  if( iCode==SQLITE_SCHEMA ) return;
  if( g.dbIgnoreErrors ) return;
  fossil_warning("%s: %s", fossil_sqlite_return_code_name(iCode), zErrmsg);
}

/*
** This function attempts to find command line options known to contain
** bitwise flags and initializes the associated global variables.  After
** this function executes, all global variables (i.e. in the "g" struct)
................................................................................
#if defined(_WIN32)
int _CRT_glob = 0x0001; /* See MinGW bug #2062 */
#endif
int main(int argc, char **argv)
#endif
{
  const char *zCmdName = "unknown";
  const CmdOrPage *pCmd = 0;
  int rc;
  if( sqlite3_libversion_number()<3014000 ){
    fossil_fatal("Unsuitable SQLite version %s, must be at least 3.14.0",
                 sqlite3_libversion());
  }
  sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
  sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
  memset(&g, 0, sizeof(g));
  g.now = time(0);
  g.httpHeader = empty_blob;
................................................................................
      g.httpOut = stdout;
      g.fullHttpReply = !g.isHTTP;
      fossil_fatal("file descriptor 2 is not open. (fd=%d, errno=%d)",
                   fd, x);
    }
  }
#endif
  rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd);
  if( rc==1 ){
#ifdef FOSSIL_ENABLE_TH1_HOOKS
    if( !g.isHTTP && !g.fNoThHook ){
      rc = Th_CommandHook(zCmdName, 0);
    }else{
      rc = TH_OK;
    }
    if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
      if( rc==TH_OK || rc==TH_RETURN ){
#endif
        fossil_fatal("%s: unknown command: %s\n"
                     "%s: use \"help\" for more information",
                     g.argv[0], zCmdName, g.argv[0]);
#ifdef FOSSIL_ENABLE_TH1_HOOKS
      }
      if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
        Th_CommandNotify(zCmdName, 0);
      }
    }
    fossil_exit(0);
#endif
  }else if( rc==2 ){

    Blob couldbe;
    blob_init(&couldbe,0,0);






    dispatch_matching_names(zCmdName, &couldbe);
    fossil_print("%s: ambiguous command prefix: %s\n"
                 "%s: could be any of:%s\n"
                 "%s: use \"help\" for more information\n",
                 g.argv[0], zCmdName, g.argv[0], blob_str(&couldbe), g.argv[0]);
    fossil_exit(1);
  }
  atexit( fossil_atexit );
................................................................................
  ** TH_RETURN: The xFunc() will be executed, the TH1 notification will be
  **            skipped.
  **
  ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be
  **              executed.
  */
  if( !g.isHTTP && !g.fNoThHook ){
    rc = Th_CommandHook(pCmd->zName, pCmd->eCmdFlags);
  }else{
    rc = TH_OK;
  }
  if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
    if( rc==TH_OK || rc==TH_RETURN ){
#endif
      pCmd->xFunc();
#ifdef FOSSIL_ENABLE_TH1_HOOKS
    }
    if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
      Th_CommandNotify(pCmd->zName, pCmd->eCmdFlags);
    }
  }
#endif
  fossil_exit(0);
  /*NOT_REACHED*/
  return 0;
}
................................................................................
    }
  }
}

/*
** Print a list of words in multiple columns.
*/

























































/*
** This function returns a human readable version string.
*/
const char *get_version(){
  static const char version[] = RELEASE_VERSION " " MANIFEST_VERSION " "
................................................................................
  @ <blockquote><pre>
  @ %h(blob_str(&versionInfo))
  @ </pre></blockquote>
  style_footer();
}

























































































































































































































































































/*
** Set the g.zBaseURL value to the full URL for the toplevel of
** the fossil tree.  Set g.zTop to g.zBaseURL without the
** leading "http://" and the host and port.
**
** The g.zBaseURL is normally set based on HTTP_HOST and SCRIPT_NAME
** environment variables.  However, if zAltBase is not NULL then it
................................................................................
  const char *zNotFound,      /* Redirect here on a 404 if not NULL */
  Glob *pFileGlob,            /* Deliver static files matching */
  int allowRepoList           /* Send repo list for "/" URL */
){
  const char *zPathInfo;
  const char *zDirPathInfo;
  char *zPath = NULL;

  int i;
  const CmdOrPage *pCmd = 0;

  /* Handle universal query parameters */
  if( PB("utc") ){
    g.fTimeFormat = 1;
  }else if( PB("localtime") ){
    g.fTimeFormat = 2;
  }
................................................................................
    }
#endif
  }

  /* Locate the method specified by the path and execute the function
  ** that implements that method.
  */
  if( dispatch_name_search(g.zPath-1, CMDFLAG_WEBPAGE, &pCmd) ){
#ifdef FOSSIL_ENABLE_JSON
    if(g.json.isJsonMode){
      json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,0);
    }else
#endif
    {
#ifdef FOSSIL_ENABLE_TH1_HOOKS
................................................................................
        }
        if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
          Th_WebpageNotify(g.zPath, 0);
        }
      }
#endif
    }
  }else if( pCmd->xFunc!=page_xfer && db_schema_is_outofdate() ){
#ifdef FOSSIL_ENABLE_JSON
    if(g.json.isJsonMode){
      json_err(FSL_JSON_E_DB_NEEDS_REBUILD,NULL,0);
    }else
#endif
    {
      @ <h1>Server Configuration Error</h1>
................................................................................
    **            skipped.
    **
    ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be
    **              executed.
    */
    int rc;
    if( !g.fNoThHook ){
      rc = Th_WebpageHook(pCmd->zName+1, pCmd->eCmdFlags);
    }else{
      rc = TH_OK;
    }
    if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
      if( rc==TH_OK || rc==TH_RETURN ){
#endif
        pCmd->xFunc();
#ifdef FOSSIL_ENABLE_TH1_HOOKS
      }
      if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
        Th_WebpageNotify(pCmd->zName+1, pCmd->eCmdFlags);
      }
    }
#endif
  }

  /* Return the result.
  */
................................................................................
    cgi_reply();
  }
}

/*
** COMMAND: cgi*
**
** Usage: %fossil ?cgi? FILE
**
** This command causes Fossil to generate reply to a CGI request.
**
** The FILE argument is the name of a control file that provides Fossil


** with important information such as where to find its repository.  In
** a typical CGI deployment, FILE is the name of the CGI script and will
** typically look something like this:
**
**      #!/usr/bin/fossil
**      repository: /home/somebody/project.db
**

** The command name, "cgi", may be omitted if the GATEWAY_INTERFACE
** environment variable is set to "CGI", which should always be the
** case for CGI scripts run by a webserver.  Fossil ignores any lines
** that begin with "#".
**
** The following control lines are recognized:
**
**    repository: PATH         Name of the Fossil repository
**
**    directory:  PATH         Name of a directory containing many Fossil
**                             repositories whose names all end with ".fossil".
**                             There should only be one of "repository:"
**                             or "directory:"
**
**    notfound: URL            When in "directory:" mode, redirect to
**                             URL if no suitable repository is found.
**
**    repolist                 When in "directory:" mode, display a page
**                             showing a list of available repositories if
**                             the URL is "/".
**
**    localauth                Grant administrator privileges to connections
**                             from 127.0.0.1 or ::1.
**
**    skin: LABEL              Use the built-in skin called LABEL rather than
**                             the default.  If there are no skins called LABEL
**                             then this line is a no-op.
**
**    files: GLOBLIST          GLOBLIST is a comma-separated list of GLOB
**                             patterns that specify files that can be
**                             returned verbatim.  This feature allows Fossil
**                             to act as a web server returning static
**                             content.
**
**    setenv: NAME VALUE       Set environment variable NAME to VALUE.  Or
**                             if VALUE is omitted, unset NAME.
**
**    HOME: PATH               Shorthand for "setenv: HOME PATH"
**
**    debug: FILE              Causing debugging information to be written
**                             into FILE.
**
**    errorlog: FILE           Warnings, errors, and panics written to FILE.
**
**    redirect: REPO URL       Extract the "name" query parameter and search
**                             REPO for a check-in or ticket that matches the
**                             value of "name", then redirect to URL.  There
**                             can be multiple "redirect:" lines that are
**                             processed in order.  If the REPO is "*", then
**                             an unconditional redirect to URL is taken.
**
** Most CGI files contain only a "repository:" line.  It is uncommon to
** use any other option.
**
** See also: http, server, winsrv
*/
void cmd_cgi(void){
  const char *zFile;
  const char *zNotFound = 0;
  char **azRedirect = 0;             /* List of repositories to redirect to */
................................................................................
      ** found it is returned verbatim.  This feature allows "fossil server"
      ** to function as a primitive web-server delivering arbitrary content.
      */
      pFileGlob = glob_create(blob_str(&value));
      blob_reset(&value);
      continue;
    }
    if( blob_eq(&key, "setenv:") && blob_token(&line, &value) ){

      /* setenv: NAME VALUE
      ** setenv: NAME
      **
      ** Sets environment variable NAME to VALUE.  If VALUE is omitted, then
      ** the environment variable is unset.
      */
      blob_token(&line,&value2);
      fossil_setenv(blob_str(&value), blob_str(&value2));
      blob_reset(&value);
      blob_reset(&value2);
      continue;
    }
    if( blob_eq(&key, "debug:") && blob_token(&line, &value) ){
      /* debug: FILENAME
................................................................................
**   --https          signal a request coming in via https
**   --nojail         drop root privilege but do not enter the chroot jail
**   --nossl          signal that no SSL connections are available
**   --notfound URL   use URL as "HTTP 404, object not found" page.
**   --repolist       If REPOSITORY is directory, URL "/" lists all repos
**   --scgi           Interpret input as SCGI rather than HTTP
**   --skin LABEL     Use override skin LABEL
**   --th-trace       trace TH1 execution (for debugging purposes)
**
** See also: cgi, server, winsrv
*/
void cmd_http(void){
  const char *zIpAddr = 0;
  const char *zNotFound;
  const char *zHost;
  const char *zAltBase;
  const char *zFileGlob;
  int useSCGI;
  int noJail;
  int allowRepoList;

  Th_InitTraceLog();

  /* The winhttp module passes the --files option as --files-urlenc with
  ** the argument being URL encoded, to avoid wildcard expansion in the
  ** shell.  This option is for internal use and is undocumented.
  */
  zFileGlob = find_option("files-urlenc",0,1);
  if( zFileGlob ){

Changes to src/main.mk.

36
37
38
39
40
41
42

43
44
45
46
47
48
49

50
51
52
53
54
55
56
...
114
115
116
117
118
119
120

121
122
123
124
125
126
127
...
208
209
210
211
212
213
214

215
216
217
218
219
220
221

222
223
224
225
226
227
228
...
286
287
288
289
290
291
292

293
294
295
296
297
298
299
...
329
330
331
332
333
334
335

336
337
338
339
340
341
342

343
344
345
346
347
348
349
...
407
408
409
410
411
412
413

414
415
416
417
418
419
420
...
479
480
481
482
483
484
485

486

487
488
489
490
491
492
493
...
535
536
537
538
539
540
541




542
543
544
545
546
547
548
...
599
600
601
602
603
604
605

606
607
608
609
610
611
612

613
614
615
616
617
618
619
...
677
678
679
680
681
682
683

684
685
686
687
688
689
690
...
906
907
908
909
910
911
912








913
914
915
916
917
918
919
...
962
963
964
965
966
967
968








969
970
971
972
973
974
975
....
1530
1531
1532
1533
1534
1535
1536








1537
1538
1539
1540
1541
1542
1543
....
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
  $(SRCDIR)/content.c \
  $(SRCDIR)/db.c \
  $(SRCDIR)/delta.c \
  $(SRCDIR)/deltacmd.c \
  $(SRCDIR)/descendants.c \
  $(SRCDIR)/diff.c \
  $(SRCDIR)/diffcmd.c \

  $(SRCDIR)/doc.c \
  $(SRCDIR)/encode.c \
  $(SRCDIR)/event.c \
  $(SRCDIR)/export.c \
  $(SRCDIR)/file.c \
  $(SRCDIR)/finfo.c \
  $(SRCDIR)/foci.c \

  $(SRCDIR)/fusefs.c \
  $(SRCDIR)/glob.c \
  $(SRCDIR)/graph.c \
  $(SRCDIR)/gzip.c \
  $(SRCDIR)/http.c \
  $(SRCDIR)/http_socket.c \
  $(SRCDIR)/http_ssl.c \
................................................................................
  $(SRCDIR)/tar.c \
  $(SRCDIR)/th_main.c \
  $(SRCDIR)/timeline.c \
  $(SRCDIR)/tkt.c \
  $(SRCDIR)/tktsetup.c \
  $(SRCDIR)/undo.c \
  $(SRCDIR)/unicode.c \

  $(SRCDIR)/update.c \
  $(SRCDIR)/url.c \
  $(SRCDIR)/user.c \
  $(SRCDIR)/utf8.c \
  $(SRCDIR)/util.c \
  $(SRCDIR)/verify.c \
  $(SRCDIR)/vfile.c \
................................................................................
  $(OBJDIR)/content_.c \
  $(OBJDIR)/db_.c \
  $(OBJDIR)/delta_.c \
  $(OBJDIR)/deltacmd_.c \
  $(OBJDIR)/descendants_.c \
  $(OBJDIR)/diff_.c \
  $(OBJDIR)/diffcmd_.c \

  $(OBJDIR)/doc_.c \
  $(OBJDIR)/encode_.c \
  $(OBJDIR)/event_.c \
  $(OBJDIR)/export_.c \
  $(OBJDIR)/file_.c \
  $(OBJDIR)/finfo_.c \
  $(OBJDIR)/foci_.c \

  $(OBJDIR)/fusefs_.c \
  $(OBJDIR)/glob_.c \
  $(OBJDIR)/graph_.c \
  $(OBJDIR)/gzip_.c \
  $(OBJDIR)/http_.c \
  $(OBJDIR)/http_socket_.c \
  $(OBJDIR)/http_ssl_.c \
................................................................................
  $(OBJDIR)/tar_.c \
  $(OBJDIR)/th_main_.c \
  $(OBJDIR)/timeline_.c \
  $(OBJDIR)/tkt_.c \
  $(OBJDIR)/tktsetup_.c \
  $(OBJDIR)/undo_.c \
  $(OBJDIR)/unicode_.c \

  $(OBJDIR)/update_.c \
  $(OBJDIR)/url_.c \
  $(OBJDIR)/user_.c \
  $(OBJDIR)/utf8_.c \
  $(OBJDIR)/util_.c \
  $(OBJDIR)/verify_.c \
  $(OBJDIR)/vfile_.c \
................................................................................
 $(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)/foci.o \

 $(OBJDIR)/fusefs.o \
 $(OBJDIR)/glob.o \
 $(OBJDIR)/graph.o \
 $(OBJDIR)/gzip.o \
 $(OBJDIR)/http.o \
 $(OBJDIR)/http_socket.o \
 $(OBJDIR)/http_ssl.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)/util.o \
 $(OBJDIR)/verify.o \
 $(OBJDIR)/vfile.o \
................................................................................

$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
	$(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid  $(SRCDIR)/../manifest  $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h

# Setup the options used to compile the included SQLite library.
SQLITE_OPTIONS = -DNDEBUG=1 \
                 -DSQLITE_OMIT_LOAD_EXTENSION=1 \

                 -DSQLITE_ENABLE_LOCKING_STYLE=0 \

                 -DSQLITE_THREADSAFE=0 \
                 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
                 -DSQLITE_OMIT_DEPRECATED \
                 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
                 -DSQLITE_ENABLE_FTS4 \
                 -DSQLITE_ENABLE_FTS3_PARENTHESIS \
                 -DSQLITE_ENABLE_DBSTAT_VTAB \
................................................................................
# 0, ordinary SQLite is used.  If 1, then sqlite3-see.c (not part of
# the source tree) is used and extra flags are provided to enable
# the SQLite Encryption Extension.
SQLITE3_SRC.0 = sqlite3.c
SQLITE3_SRC.1 = sqlite3-see.c
SQLITE3_SRC. = sqlite3.c
SQLITE3_SRC = $(SRCDIR)/$(SQLITE3_SRC.$(USE_SEE))




SEE_FLAGS.0 =
SEE_FLAGS.1 = -DSQLITE_HAS_CODEC
SEE_FLAGS. =
SEE_FLAGS = $(SEE_FLAGS.$(USE_SEE))


EXTRAOBJ = \
................................................................................
	$(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)/foci_.c:$(OBJDIR)/foci.h \

	$(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.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)/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)/util_.c:$(OBJDIR)/util.h \
	$(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
	$(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
................................................................................
$(OBJDIR)/diffcmd_.c:	$(SRCDIR)/diffcmd.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/diffcmd.c >$@

$(OBJDIR)/diffcmd.o:	$(OBJDIR)/diffcmd_.c $(OBJDIR)/diffcmd.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/diffcmd.o -c $(OBJDIR)/diffcmd_.c

$(OBJDIR)/diffcmd.h:	$(OBJDIR)/headers









$(OBJDIR)/doc_.c:	$(SRCDIR)/doc.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/doc.c >$@

$(OBJDIR)/doc.o:	$(OBJDIR)/doc_.c $(OBJDIR)/doc.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c

................................................................................
$(OBJDIR)/foci_.c:	$(SRCDIR)/foci.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/foci.c >$@

$(OBJDIR)/foci.o:	$(OBJDIR)/foci_.c $(OBJDIR)/foci.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/foci.o -c $(OBJDIR)/foci_.c

$(OBJDIR)/foci.h:	$(OBJDIR)/headers









$(OBJDIR)/fusefs_.c:	$(SRCDIR)/fusefs.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/fusefs.c >$@

$(OBJDIR)/fusefs.o:	$(OBJDIR)/fusefs_.c $(OBJDIR)/fusefs.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/fusefs.o -c $(OBJDIR)/fusefs_.c

................................................................................
$(OBJDIR)/unicode_.c:	$(SRCDIR)/unicode.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/unicode.c >$@

$(OBJDIR)/unicode.o:	$(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c

$(OBJDIR)/unicode.h:	$(OBJDIR)/headers









$(OBJDIR)/update_.c:	$(SRCDIR)/update.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/update.c >$@

$(OBJDIR)/update.o:	$(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/update.o -c $(OBJDIR)/update_.c

................................................................................
	$(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c

$(OBJDIR)/zip.h:	$(OBJDIR)/headers

$(OBJDIR)/sqlite3.o:	$(SQLITE3_SRC)
	$(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SEE_FLAGS) \
		-c $(SQLITE3_SRC) -o $@
$(OBJDIR)/shell.o:	$(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h
	$(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) $(LINENOISE_DEF.$(USE_LINENOISE)) -c $(SRCDIR)/shell.c -o $@

$(OBJDIR)/linenoise.o:	$(SRCDIR)/linenoise.c $(SRCDIR)/linenoise.h
	$(XTCC) -c $(SRCDIR)/linenoise.c -o $@

$(OBJDIR)/th.o:	$(SRCDIR)/th.c
	$(XTCC) -c $(SRCDIR)/th.c -o $@








>







>







 







>







 







>







>







 







>







 







>







>







 







>







 







>

>







 







>
>
>
>







 







>







>







 







>







 







>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>







 







|
|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
...
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
...
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
...
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
...
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
...
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
...
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
...
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
...
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
...
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
...
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
....
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
....
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
  $(SRCDIR)/content.c \
  $(SRCDIR)/db.c \
  $(SRCDIR)/delta.c \
  $(SRCDIR)/deltacmd.c \
  $(SRCDIR)/descendants.c \
  $(SRCDIR)/diff.c \
  $(SRCDIR)/diffcmd.c \
  $(SRCDIR)/dispatch.c \
  $(SRCDIR)/doc.c \
  $(SRCDIR)/encode.c \
  $(SRCDIR)/event.c \
  $(SRCDIR)/export.c \
  $(SRCDIR)/file.c \
  $(SRCDIR)/finfo.c \
  $(SRCDIR)/foci.c \
  $(SRCDIR)/fshell.c \
  $(SRCDIR)/fusefs.c \
  $(SRCDIR)/glob.c \
  $(SRCDIR)/graph.c \
  $(SRCDIR)/gzip.c \
  $(SRCDIR)/http.c \
  $(SRCDIR)/http_socket.c \
  $(SRCDIR)/http_ssl.c \
................................................................................
  $(SRCDIR)/tar.c \
  $(SRCDIR)/th_main.c \
  $(SRCDIR)/timeline.c \
  $(SRCDIR)/tkt.c \
  $(SRCDIR)/tktsetup.c \
  $(SRCDIR)/undo.c \
  $(SRCDIR)/unicode.c \
  $(SRCDIR)/unversioned.c \
  $(SRCDIR)/update.c \
  $(SRCDIR)/url.c \
  $(SRCDIR)/user.c \
  $(SRCDIR)/utf8.c \
  $(SRCDIR)/util.c \
  $(SRCDIR)/verify.c \
  $(SRCDIR)/vfile.c \
................................................................................
  $(OBJDIR)/content_.c \
  $(OBJDIR)/db_.c \
  $(OBJDIR)/delta_.c \
  $(OBJDIR)/deltacmd_.c \
  $(OBJDIR)/descendants_.c \
  $(OBJDIR)/diff_.c \
  $(OBJDIR)/diffcmd_.c \
  $(OBJDIR)/dispatch_.c \
  $(OBJDIR)/doc_.c \
  $(OBJDIR)/encode_.c \
  $(OBJDIR)/event_.c \
  $(OBJDIR)/export_.c \
  $(OBJDIR)/file_.c \
  $(OBJDIR)/finfo_.c \
  $(OBJDIR)/foci_.c \
  $(OBJDIR)/fshell_.c \
  $(OBJDIR)/fusefs_.c \
  $(OBJDIR)/glob_.c \
  $(OBJDIR)/graph_.c \
  $(OBJDIR)/gzip_.c \
  $(OBJDIR)/http_.c \
  $(OBJDIR)/http_socket_.c \
  $(OBJDIR)/http_ssl_.c \
................................................................................
  $(OBJDIR)/tar_.c \
  $(OBJDIR)/th_main_.c \
  $(OBJDIR)/timeline_.c \
  $(OBJDIR)/tkt_.c \
  $(OBJDIR)/tktsetup_.c \
  $(OBJDIR)/undo_.c \
  $(OBJDIR)/unicode_.c \
  $(OBJDIR)/unversioned_.c \
  $(OBJDIR)/update_.c \
  $(OBJDIR)/url_.c \
  $(OBJDIR)/user_.c \
  $(OBJDIR)/utf8_.c \
  $(OBJDIR)/util_.c \
  $(OBJDIR)/verify_.c \
  $(OBJDIR)/vfile_.c \
................................................................................
 $(OBJDIR)/content.o \
 $(OBJDIR)/db.o \
 $(OBJDIR)/delta.o \
 $(OBJDIR)/deltacmd.o \
 $(OBJDIR)/descendants.o \
 $(OBJDIR)/diff.o \
 $(OBJDIR)/diffcmd.o \
 $(OBJDIR)/dispatch.o \
 $(OBJDIR)/doc.o \
 $(OBJDIR)/encode.o \
 $(OBJDIR)/event.o \
 $(OBJDIR)/export.o \
 $(OBJDIR)/file.o \
 $(OBJDIR)/finfo.o \
 $(OBJDIR)/foci.o \
 $(OBJDIR)/fshell.o \
 $(OBJDIR)/fusefs.o \
 $(OBJDIR)/glob.o \
 $(OBJDIR)/graph.o \
 $(OBJDIR)/gzip.o \
 $(OBJDIR)/http.o \
 $(OBJDIR)/http_socket.o \
 $(OBJDIR)/http_ssl.o \
................................................................................
 $(OBJDIR)/tar.o \
 $(OBJDIR)/th_main.o \
 $(OBJDIR)/timeline.o \
 $(OBJDIR)/tkt.o \
 $(OBJDIR)/tktsetup.o \
 $(OBJDIR)/undo.o \
 $(OBJDIR)/unicode.o \
 $(OBJDIR)/unversioned.o \
 $(OBJDIR)/update.o \
 $(OBJDIR)/url.o \
 $(OBJDIR)/user.o \
 $(OBJDIR)/utf8.o \
 $(OBJDIR)/util.o \
 $(OBJDIR)/verify.o \
 $(OBJDIR)/vfile.o \
................................................................................

$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
	$(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid  $(SRCDIR)/../manifest  $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h

# Setup the options used to compile the included SQLite library.
SQLITE_OPTIONS = -DNDEBUG=1 \
                 -DSQLITE_OMIT_LOAD_EXTENSION=1 \
                 -DSQLITE_OMIT_SHARED_CACHE \
                 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 \
                 -DSQLITE_THREADSAFE=0 \
                 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
                 -DSQLITE_OMIT_DEPRECATED \
                 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
                 -DSQLITE_ENABLE_FTS4 \
                 -DSQLITE_ENABLE_FTS3_PARENTHESIS \
                 -DSQLITE_ENABLE_DBSTAT_VTAB \
................................................................................
# 0, ordinary SQLite is used.  If 1, then sqlite3-see.c (not part of
# the source tree) is used and extra flags are provided to enable
# the SQLite Encryption Extension.
SQLITE3_SRC.0 = sqlite3.c
SQLITE3_SRC.1 = sqlite3-see.c
SQLITE3_SRC. = sqlite3.c
SQLITE3_SRC = $(SRCDIR)/$(SQLITE3_SRC.$(USE_SEE))
SQLITE3_SHELL_SRC.0 = shell.c
SQLITE3_SHELL_SRC.1 = shell-see.c
SQLITE3_SHELL_SRC. = shell.c
SQLITE3_SHELL_SRC = $(SRCDIR)/$(SQLITE3_SHELL_SRC.$(USE_SEE))
SEE_FLAGS.0 =
SEE_FLAGS.1 = -DSQLITE_HAS_CODEC
SEE_FLAGS. =
SEE_FLAGS = $(SEE_FLAGS.$(USE_SEE))


EXTRAOBJ = \
................................................................................
	$(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)/dispatch_.c:$(OBJDIR)/dispatch.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)/foci_.c:$(OBJDIR)/foci.h \
	$(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \
	$(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.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)/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)/unversioned_.c:$(OBJDIR)/unversioned.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)/util_.c:$(OBJDIR)/util.h \
	$(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
	$(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
................................................................................
$(OBJDIR)/diffcmd_.c:	$(SRCDIR)/diffcmd.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/diffcmd.c >$@

$(OBJDIR)/diffcmd.o:	$(OBJDIR)/diffcmd_.c $(OBJDIR)/diffcmd.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/diffcmd.o -c $(OBJDIR)/diffcmd_.c

$(OBJDIR)/diffcmd.h:	$(OBJDIR)/headers

$(OBJDIR)/dispatch_.c:	$(SRCDIR)/dispatch.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/dispatch.c >$@

$(OBJDIR)/dispatch.o:	$(OBJDIR)/dispatch_.c $(OBJDIR)/dispatch.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/dispatch.o -c $(OBJDIR)/dispatch_.c

$(OBJDIR)/dispatch.h:	$(OBJDIR)/headers

$(OBJDIR)/doc_.c:	$(SRCDIR)/doc.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/doc.c >$@

$(OBJDIR)/doc.o:	$(OBJDIR)/doc_.c $(OBJDIR)/doc.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c

................................................................................
$(OBJDIR)/foci_.c:	$(SRCDIR)/foci.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/foci.c >$@

$(OBJDIR)/foci.o:	$(OBJDIR)/foci_.c $(OBJDIR)/foci.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/foci.o -c $(OBJDIR)/foci_.c

$(OBJDIR)/foci.h:	$(OBJDIR)/headers

$(OBJDIR)/fshell_.c:	$(SRCDIR)/fshell.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/fshell.c >$@

$(OBJDIR)/fshell.o:	$(OBJDIR)/fshell_.c $(OBJDIR)/fshell.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/fshell.o -c $(OBJDIR)/fshell_.c

$(OBJDIR)/fshell.h:	$(OBJDIR)/headers

$(OBJDIR)/fusefs_.c:	$(SRCDIR)/fusefs.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/fusefs.c >$@

$(OBJDIR)/fusefs.o:	$(OBJDIR)/fusefs_.c $(OBJDIR)/fusefs.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/fusefs.o -c $(OBJDIR)/fusefs_.c

................................................................................
$(OBJDIR)/unicode_.c:	$(SRCDIR)/unicode.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/unicode.c >$@

$(OBJDIR)/unicode.o:	$(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c

$(OBJDIR)/unicode.h:	$(OBJDIR)/headers

$(OBJDIR)/unversioned_.c:	$(SRCDIR)/unversioned.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/unversioned.c >$@

$(OBJDIR)/unversioned.o:	$(OBJDIR)/unversioned_.c $(OBJDIR)/unversioned.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/unversioned.o -c $(OBJDIR)/unversioned_.c

$(OBJDIR)/unversioned.h:	$(OBJDIR)/headers

$(OBJDIR)/update_.c:	$(SRCDIR)/update.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/update.c >$@

$(OBJDIR)/update.o:	$(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/update.o -c $(OBJDIR)/update_.c

................................................................................
	$(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c

$(OBJDIR)/zip.h:	$(OBJDIR)/headers

$(OBJDIR)/sqlite3.o:	$(SQLITE3_SRC)
	$(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SEE_FLAGS) \
		-c $(SQLITE3_SRC) -o $@
$(OBJDIR)/shell.o:	$(SQLITE3_SHELL_SRC) $(SRCDIR)/sqlite3.h
	$(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) $(LINENOISE_DEF.$(USE_LINENOISE)) -c $(SQLITE3_SHELL_SRC) -o $@

$(OBJDIR)/linenoise.o:	$(SRCDIR)/linenoise.c $(SRCDIR)/linenoise.h
	$(XTCC) -c $(SRCDIR)/linenoise.c -o $@

$(OBJDIR)/th.o:	$(SRCDIR)/th.c
	$(XTCC) -c $(SRCDIR)/th.c -o $@

Changes to src/makeheaders.c.

1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
....
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
....
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
** If pTable is not NULL, then insert every identifier seen into the
** IdentTable.  This includes any identifiers seen inside of {...}.
**
** The number of errors encountered is returned.  An error is an
** unterminated token.
*/
static int GetBigToken(InStream *pIn, Token *pToken, IdentTable *pTable){
  const char *z, *zStart;
  int iStart;
  int nBrace;
  int c;
  int nLine;
  int nErr;

  nErr = GetNonspaceToken(pIn,pToken);
................................................................................
      if( pToken->zText[0]=='{' ) break;
      return nErr;

    default:
      return nErr;
  }

  z = pIn->z;
  iStart = pIn->i;
  zStart = pToken->zText;
  nLine = pToken->nLine;
  nBrace = 1;
  while( nBrace ){
    nErr += GetNonspaceToken(pIn,pToken);
    /* printf("%04d: nBrace=%d [%.*s]\n",pToken->nLine,nBrace,
................................................................................

/*
** This routine is called when we see a method for a class that begins
** with the PUBLIC, PRIVATE, or PROTECTED keywords.  Such methods are
** added to their class definitions.
*/
static int ProcessMethodDef(Token *pFirst, Token *pLast, int flags){
  Token *pCode;
  Token *pClass;
  char *zDecl;
  Decl *pDecl;
  String str;
  int type;

  pCode = pLast;
  pLast = pLast->pPrev;
  while( pFirst->zText[0]=='P' ){
    int rc = 1;
    switch( pFirst->nText ){
      case 6:  rc = strncmp(pFirst->zText,"PUBLIC",6); break;
      case 7:  rc = strncmp(pFirst->zText,"PRIVATE",7); break;
      case 9:  rc = strncmp(pFirst->zText,"PROTECTED",9); break;







|







 







<







 







<






<







1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
....
1133
1134
1135
1136
1137
1138
1139

1140
1141
1142
1143
1144
1145
1146
....
1678
1679
1680
1681
1682
1683
1684

1685
1686
1687
1688
1689
1690

1691
1692
1693
1694
1695
1696
1697
** If pTable is not NULL, then insert every identifier seen into the
** IdentTable.  This includes any identifiers seen inside of {...}.
**
** The number of errors encountered is returned.  An error is an
** unterminated token.
*/
static int GetBigToken(InStream *pIn, Token *pToken, IdentTable *pTable){
  const char *zStart;
  int iStart;
  int nBrace;
  int c;
  int nLine;
  int nErr;

  nErr = GetNonspaceToken(pIn,pToken);
................................................................................
      if( pToken->zText[0]=='{' ) break;
      return nErr;

    default:
      return nErr;
  }


  iStart = pIn->i;
  zStart = pToken->zText;
  nLine = pToken->nLine;
  nBrace = 1;
  while( nBrace ){
    nErr += GetNonspaceToken(pIn,pToken);
    /* printf("%04d: nBrace=%d [%.*s]\n",pToken->nLine,nBrace,
................................................................................

/*
** This routine is called when we see a method for a class that begins
** with the PUBLIC, PRIVATE, or PROTECTED keywords.  Such methods are
** added to their class definitions.
*/
static int ProcessMethodDef(Token *pFirst, Token *pLast, int flags){

  Token *pClass;
  char *zDecl;
  Decl *pDecl;
  String str;
  int type;


  pLast = pLast->pPrev;
  while( pFirst->zText[0]=='P' ){
    int rc = 1;
    switch( pFirst->nText ){
      case 6:  rc = strncmp(pFirst->zText,"PUBLIC",6); break;
      case 7:  rc = strncmp(pFirst->zText,"PRIVATE",7); break;
      case 9:  rc = strncmp(pFirst->zText,"PROTECTED",9); break;

Changes to src/makeheaders.html.

141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
...
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
...
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
...
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
...
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
...
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
...
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
...
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
...
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
...
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
...
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
....
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
files change, the entire program must be recompiled.
It also happens that those important .h files tend to be the ones that
change most frequently.
This means that the entire program must be recompiled frequently,
leading to a lengthy modify-compile-test cycle and a corresponding
decrease in programmer productivity.
<p><li>
The C programming language requires that declarations depending upon 
each other must occur in a particular order.
In a program with complex, interwoven data structures, the correct
declaration order can become very difficult to determine manually, 
especially when the declarations involved are spread out over several
files.
</ol>
</p>

<a name="H0004"></a>
<h3>1.2 The Makeheaders Solution</h3>

<p>
The makeheaders program is designed to ameliorate the problems associated
with the traditional C programming model by automatically generating
the interface information in the .h files from 
interface information contained in other .h files and
from implementation information in the .c files.
When the makeheaders program is run, it scans the source
files for a project,
then generates a series of new .h files, one for each .c file.
The generated .h files contain exactly those declarations required by the
corresponding .c files, no more and no less.
................................................................................
a problem.
Simply rerun makeheaders to resynchronize everything.
<p><li>
The generated .h file contains the minimal set of declarations needed
by the .c file.
This means that when something changes, a minimal amount of recompilation
is required to produce an updated executable.
Experience has shown that this gives a dramatic improvement 
in programmer productivity by facilitating a rapid modify-compile-test
cycle during development.
<p><li>
The makeheaders program automatically sorts declarations into the
correct order, completely eliminating the wearisome and error-prone
task of sorting declarations by hand.
</ol>
................................................................................
but manually entered .h files
that contain structure declarations and so forth will be scanned and
the declarations will be copied into the generated .h files as
appropriate.
But if makeheaders sees that the .h file that it has generated is no
different from the .h file it generated last time, it doesn't update
the file.
This prevents the corresponding .c files from having to 
be needlessly recompiled.
</p>

<p>
There are several options to the makeheaders program that can
be used to alter its behavior.
The default behavior is to write a single .h file for each .c file and
................................................................................
In this example, makeheaders will scan the three files named
``alpha.c'',
``beta.c'' and
``gamma.c''
but because of the colon on the end of third filename
it will only generate headers for the first two files.
Unfortunately,
it is not possible to get makeheaders to process any file whose 
name contains a colon.
</p>

<p>
In a large project, the length of the command line for makeheaders
can become very long.
If the operating system doesn't support long command lines
................................................................................
Such prototypes are normally omitted.
</p>

<p>
Finally, makeheaders also includes a ``-doc'' option.
This command line option prevents makeheaders from generating any
headers at all.
Instead, makeheaders will write to standard output 
information about every definition and declaration that it encounters
in its scan of source files.
The information output includes the type of the definition or
declaration and any comment that preceeds the definition or
declaration.
The output is in a format that can be easily parsed, and is
intended to be read by another program that will generate
................................................................................
<a name="H0007"></a>
<h3>3.1 The Basic Setup</h3>

<p>
The simpliest way to use makeheaders is to put all definitions in
one or more .c files and all structure and type declarations in
separate .h files.
The only restriction is that you should take care to chose basenames 
for your .h files that are different from the basenames for you
.c files.
Recall that if your .c file is named (for example) ``alpha.c''
makeheaders will attempt to generate a corresponding header file
named ``alpha.h''.
For that reason, you don't want to use that name for
any of the .h files you write since that will prevent makeheaders
................................................................................

<pre>
   makeheaders *.[ch]
</pre>

The makeheaders program will scan all of the .c files and all of the
manually written .h files and then automatically generate .h files
corresponding to all .c files.  
</p>

<p>
Note that
the wildcard expression used in the above example, 
``<code>*.[ch]</code>'',
will expand to include all .h files in the current directory, both
those entered manually be the programmer and others generated automatically
by a prior run of makeheaders.
But that is not a problem.
The makeheaders program will recognize and ignore any files it 
has previously generated that show up on its input list.
</p>

<a name="H0008"></a>
<h3>3.2 What Declarations Get Copied</h3>

<p>
................................................................................
declaration of that variable is placed in the header of every
.c file that uses the variable.
</p>

<p><li>
When a structure, union or enumeration declaration or a
function prototype or a C++ class declaration appears in a
manually produced .h file, that declaration is copied into the 
automatically generated
.h files of all .c files that use the structure, union, enumeration,
function or class.
But declarations that appear in a
.c file are considered private to that .c file and are not copied into
any automatically generated files.
</p>
................................................................................
<p>
You can instruct makeheaders to treat any part of a .c file as if
it were a .h file by enclosing that part of the .c file within:
<pre>
   #if INTERFACE
   #endif
</pre>
Thus any structure definitions that appear after the 
``#if INTERFACE'' but before the corresponding 
``#endif'' are eligable to be copied into the 
automatically generated
.h files of other .c files.
</p>

<p>
If you use the ``#if INTERFACE'' mechanism in a .c file,
then the generated header for that .c file will contain a line
................................................................................
<pre>
   #if EXPORT_INTERFACE
   #endif
</pre>
will become part of the exported interface.
The ``#if EXPORT_INTERFACE'' mechanism can be used in either
.c or .h files.
(The ``#if INTERFACE'' can also be used in both .h and .c files, 
but since it's use in a .h file would be redundant, we haven't mentioned
it before.)
</p>

<a name="H0011"></a>
<h3>3.5 Local declarations processed by makeheaders</h3>

................................................................................

<p>
Sometimes it is convenient to have makeheaders sort a sequence
of private declarations into the correct order for us automatically.
Or, we could have static functions and procedures for which we would like
makeheaders to generate prototypes, but the arguments to these
functions and procedures uses private declarations.
In both of these cases, we want makeheaders to be aware of the 
private declarations and copy them into the local header file,
but we don't want makeheaders to propagate the
declarations outside of the file in which they are declared.
</p>

<p>
When this situation arises, enclose the private declarations
................................................................................
file ``alpha.cpp'' will induce makeheaders to
generate a header file named ``alpha.hpp''.
</p>

<p>
Makeheaders augments class definitions by inserting prototypes to
methods were appropriate.  If a method definition begins with one
of the special keywords <b>PUBLIC</b>, <b>PROTECTED</b>, or 
<b>PRIVATE</b> (in upper-case to distinguish them from the regular
C++ keywords with the same meaning) then a prototype for that
method will be inserted into the class definition.  If none of
these keywords appear, then the prototype is not inserted.  For
example, in the following code, the constructor is not explicitly
declared in the class definition but makeheaders will add it there
because of the PUBLIC keyword that appears before the constructor
................................................................................
Perhaps these issued will be addressed in future revisions.
</p>

<a name="H0013"></a>
<h3>3.7 Conditional Compilation</h3>

<p>
The makeheaders program understands and tracks the conditional 
compilation constructs in the source code files it scans.
Hence, if the following code appears in a source file
<pre>
  #ifdef UNIX
  #  define WORKS_WELL  1
  #else
  #  define WORKS_WELL  0
................................................................................
as well as Cygwin32 and MSVC 5.0 for Win32.
</p>

<a name="H0017"></a>
<h2>6.0 Summary And Conclusion</h2>

<p>
The makeheaders program will automatically generate a minimal header file 
for each of a set of C source and header files, and will
generate a composite header file for the entire source file suite,
for either internal or external use.
It can also be used as the parser in an automated program
documentation system.
</p>








|


|











|







 







|







 







|







 







|







 







|







 







|







 







|




|





|







 







|







 







|
|
|







 







|







 







|







 







|







 







|







 







|







141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
...
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
...
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
...
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
...
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
...
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
...
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
...
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
...
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
...
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
...
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
....
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
files change, the entire program must be recompiled.
It also happens that those important .h files tend to be the ones that
change most frequently.
This means that the entire program must be recompiled frequently,
leading to a lengthy modify-compile-test cycle and a corresponding
decrease in programmer productivity.
<p><li>
The C programming language requires that declarations depending upon
each other must occur in a particular order.
In a program with complex, interwoven data structures, the correct
declaration order can become very difficult to determine manually,
especially when the declarations involved are spread out over several
files.
</ol>
</p>

<a name="H0004"></a>
<h3>1.2 The Makeheaders Solution</h3>

<p>
The makeheaders program is designed to ameliorate the problems associated
with the traditional C programming model by automatically generating
the interface information in the .h files from
interface information contained in other .h files and
from implementation information in the .c files.
When the makeheaders program is run, it scans the source
files for a project,
then generates a series of new .h files, one for each .c file.
The generated .h files contain exactly those declarations required by the
corresponding .c files, no more and no less.
................................................................................
a problem.
Simply rerun makeheaders to resynchronize everything.
<p><li>
The generated .h file contains the minimal set of declarations needed
by the .c file.
This means that when something changes, a minimal amount of recompilation
is required to produce an updated executable.
Experience has shown that this gives a dramatic improvement
in programmer productivity by facilitating a rapid modify-compile-test
cycle during development.
<p><li>
The makeheaders program automatically sorts declarations into the
correct order, completely eliminating the wearisome and error-prone
task of sorting declarations by hand.
</ol>
................................................................................
but manually entered .h files
that contain structure declarations and so forth will be scanned and
the declarations will be copied into the generated .h files as
appropriate.
But if makeheaders sees that the .h file that it has generated is no
different from the .h file it generated last time, it doesn't update
the file.
This prevents the corresponding .c files from having to
be needlessly recompiled.
</p>

<p>
There are several options to the makeheaders program that can
be used to alter its behavior.
The default behavior is to write a single .h file for each .c file and
................................................................................
In this example, makeheaders will scan the three files named
``alpha.c'',
``beta.c'' and
``gamma.c''
but because of the colon on the end of third filename
it will only generate headers for the first two files.
Unfortunately,
it is not possible to get makeheaders to process any file whose
name contains a colon.
</p>

<p>
In a large project, the length of the command line for makeheaders
can become very long.
If the operating system doesn't support long command lines
................................................................................
Such prototypes are normally omitted.
</p>

<p>
Finally, makeheaders also includes a ``-doc'' option.
This command line option prevents makeheaders from generating any
headers at all.
Instead, makeheaders will write to standard output
information about every definition and declaration that it encounters
in its scan of source files.
The information output includes the type of the definition or
declaration and any comment that preceeds the definition or
declaration.
The output is in a format that can be easily parsed, and is
intended to be read by another program that will generate
................................................................................
<a name="H0007"></a>
<h3>3.1 The Basic Setup</h3>

<p>
The simpliest way to use makeheaders is to put all definitions in
one or more .c files and all structure and type declarations in
separate .h files.
The only restriction is that you should take care to chose basenames
for your .h files that are different from the basenames for you
.c files.
Recall that if your .c file is named (for example) ``alpha.c''
makeheaders will attempt to generate a corresponding header file
named ``alpha.h''.
For that reason, you don't want to use that name for
any of the .h files you write since that will prevent makeheaders
................................................................................

<pre>
   makeheaders *.[ch]
</pre>

The makeheaders program will scan all of the .c files and all of the
manually written .h files and then automatically generate .h files
corresponding to all .c files.
</p>

<p>
Note that
the wildcard expression used in the above example,
``<code>*.[ch]</code>'',
will expand to include all .h files in the current directory, both
those entered manually be the programmer and others generated automatically
by a prior run of makeheaders.
But that is not a problem.
The makeheaders program will recognize and ignore any files it
has previously generated that show up on its input list.
</p>

<a name="H0008"></a>
<h3>3.2 What Declarations Get Copied</h3>

<p>
................................................................................
declaration of that variable is placed in the header of every
.c file that uses the variable.
</p>

<p><li>
When a structure, union or enumeration declaration or a
function prototype or a C++ class declaration appears in a
manually produced .h file, that declaration is copied into the
automatically generated
.h files of all .c files that use the structure, union, enumeration,
function or class.
But declarations that appear in a
.c file are considered private to that .c file and are not copied into
any automatically generated files.
</p>
................................................................................
<p>
You can instruct makeheaders to treat any part of a .c file as if
it were a .h file by enclosing that part of the .c file within:
<pre>
   #if INTERFACE
   #endif
</pre>
Thus any structure definitions that appear after the
``#if INTERFACE'' but before the corresponding
``#endif'' are eligable to be copied into the
automatically generated
.h files of other .c files.
</p>

<p>
If you use the ``#if INTERFACE'' mechanism in a .c file,
then the generated header for that .c file will contain a line
................................................................................
<pre>
   #if EXPORT_INTERFACE
   #endif
</pre>
will become part of the exported interface.
The ``#if EXPORT_INTERFACE'' mechanism can be used in either
.c or .h files.
(The ``#if INTERFACE'' can also be used in both .h and .c files,
but since it's use in a .h file would be redundant, we haven't mentioned
it before.)
</p>

<a name="H0011"></a>
<h3>3.5 Local declarations processed by makeheaders</h3>

................................................................................

<p>
Sometimes it is convenient to have makeheaders sort a sequence
of private declarations into the correct order for us automatically.
Or, we could have static functions and procedures for which we would like
makeheaders to generate prototypes, but the arguments to these
functions and procedures uses private declarations.
In both of these cases, we want makeheaders to be aware of the
private declarations and copy them into the local header file,
but we don't want makeheaders to propagate the
declarations outside of the file in which they are declared.
</p>

<p>
When this situation arises, enclose the private declarations
................................................................................
file ``alpha.cpp'' will induce makeheaders to
generate a header file named ``alpha.hpp''.
</p>

<p>
Makeheaders augments class definitions by inserting prototypes to
methods were appropriate.  If a method definition begins with one
of the special keywords <b>PUBLIC</b>, <b>PROTECTED</b>, or
<b>PRIVATE</b> (in upper-case to distinguish them from the regular
C++ keywords with the same meaning) then a prototype for that
method will be inserted into the class definition.  If none of
these keywords appear, then the prototype is not inserted.  For
example, in the following code, the constructor is not explicitly
declared in the class definition but makeheaders will add it there
because of the PUBLIC keyword that appears before the constructor
................................................................................
Perhaps these issued will be addressed in future revisions.
</p>

<a name="H0013"></a>
<h3>3.7 Conditional Compilation</h3>

<p>
The makeheaders program understands and tracks the conditional
compilation constructs in the source code files it scans.
Hence, if the following code appears in a source file
<pre>
  #ifdef UNIX
  #  define WORKS_WELL  1
  #else
  #  define WORKS_WELL  0
................................................................................
as well as Cygwin32 and MSVC 5.0 for Win32.
</p>

<a name="H0017"></a>
<h2>6.0 Summary And Conclusion</h2>

<p>
The makeheaders program will automatically generate a minimal header file
for each of a set of C source and header files, and will
generate a composite header file for the entire source file suite,
for either internal or external use.
It can also be used as the parser in an automated program
documentation system.
</p>

Changes to src/makemake.tcl.

43
44
45
46
47
48
49

50
51
52
53
54
55
56

57
58
59
60
61
62
63
...
120
121
122
123
124
125
126

127
128
129
130
131
132
133
...
151
152
153
154
155
156
157

158

159
160
161
162
163
164
165
...
336
337
338
339
340
341
342
343
344

345
346
347
348
349
350
351
...
365
366
367
368
369
370
371




372
373
374
375
376
377
378
...
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
...
514
515
516
517
518
519
520








521
522
523
524
525
526
527
528
529
530
531
532
533
534
...
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
...
685
686
687
688
689
690
691








692
693
694
695
696
697
698
699
700
701
702
703
704
705
...
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
....
1010
1011
1012
1013
1014
1015
1016
1017
1018

1019
1020
1021
1022
1023
1024
1025
....
1029
1030
1031
1032
1033
1034
1035




1036
1037
1038
1039
1040
1041
1042
....
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065


1066



1067
1068


1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
....
1140
1141
1142
1143
1144
1145
1146



1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
....
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
....
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
....
1803
1804
1805
1806
1807
1808
1809



1810



1811
1812
1813
1814
1815
1816
1817
1818
  content
  db
  delta
  deltacmd
  descendants
  diff
  diffcmd

  doc
  encode
  event
  export
  file
  finfo
  foci

  fusefs
  glob
  graph
  gzip
  http
  http_socket
  http_transport
................................................................................
  tar
  th_main
  timeline
  tkt
  tktsetup
  undo
  unicode

  update
  url
  user
  utf8
  util
  verify
  vfile
................................................................................
}

# Options used to compile the included SQLite library.
#
set SQLITE_OPTIONS {
  -DNDEBUG=1
  -DSQLITE_OMIT_LOAD_EXTENSION=1

  -DSQLITE_ENABLE_LOCKING_STYLE=0

  -DSQLITE_THREADSAFE=0
  -DSQLITE_DEFAULT_FILE_FORMAT=4
  -DSQLITE_OMIT_DEPRECATED
  -DSQLITE_ENABLE_EXPLAIN_COMMENTS
  -DSQLITE_ENABLE_FTS4
  -DSQLITE_ENABLE_FTS3_PARENTHESIS
  -DSQLITE_ENABLE_DBSTAT_VTAB
................................................................................
# Setup the options used to compile the included miniz library.
MINIZ_OPTIONS = <<<MINIZ_OPTIONS>>>

# The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
# to 1. If it is set to 1, then there is no need to build or link
# the sqlite3.o object. Instead, the system SQLite will be linked
# using -lsqlite3.
SQLITE3_OBJ.1 =
SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o

SQLITE3_OBJ.  = $(SQLITE3_OBJ.0)

# The FOSSIL_ENABLE_MINIZ variable may be undefined, set to 0, or
# set to 1.  If it is set to 1, the miniz library included in the
# source tree should be used; otherwise, it should not.
MINIZ_OBJ.0 =
MINIZ_OBJ.1 = $(OBJDIR)/miniz.o
................................................................................
# 0, ordinary SQLite is used.  If 1, then sqlite3-see.c (not part of
# the source tree) is used and extra flags are provided to enable
# the SQLite Encryption Extension.
SQLITE3_SRC.0 = sqlite3.c
SQLITE3_SRC.1 = sqlite3-see.c
SQLITE3_SRC. = sqlite3.c
SQLITE3_SRC = $(SRCDIR)/$(SQLITE3_SRC.$(USE_SEE))




SEE_FLAGS.0 =
SEE_FLAGS.1 = -DSQLITE_HAS_CODEC
SEE_FLAGS. =
SEE_FLAGS = $(SEE_FLAGS.$(USE_SEE))
}]

writeln [string map [list <<<NEXT_LINE>>> \\] {
................................................................................
  writeln "\$(OBJDIR)/$s.h:\t\$(OBJDIR)/headers\n"
}

writeln "\$(OBJDIR)/sqlite3.o:\t\$(SQLITE3_SRC)"
writeln "\t\$(XTCC) \$(SQLITE_OPTIONS) \$(SQLITE_CFLAGS) \$(SEE_FLAGS) \\"
writeln "\t\t-c \$(SQLITE3_SRC) -o \$@"

writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h"
writeln "\t\$(XTCC) \$(SHELL_OPTIONS) \$(SHELL_CFLAGS) \$(LINENOISE_DEF.\$(USE_LINENOISE)) -c \$(SRCDIR)/shell.c -o \$@\n"

writeln "\$(OBJDIR)/linenoise.o:\t\$(SRCDIR)/linenoise.c \$(SRCDIR)/linenoise.h"
writeln "\t\$(XTCC) -c \$(SRCDIR)/linenoise.c -o \$@\n"

writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$@\n"

................................................................................
#    the following to point from the build directory to the src/ folder.
#
SRCDIR = src

#### The directory into which object code files should be written.
#
OBJDIR = wbld









#### C Compiler and options for use in building executables that
#    will run on the platform that is doing the build.  This is used
#    to compile code-generator programs as part of the build process.
#    See TCC below for the C compiler for building the finished binary.
#
BCC = gcc

#### Enable compiling with debug symbols (much larger binary)
#
# FOSSIL_ENABLE_SYMBOLS = 1

#### Enable JSON (http://www.json.org) support using "cson"
#
................................................................................
#    used, taking into account whether zlib is actually enabled and the target
#    processor architecture.
#
ifndef X64
SSLCONFIG = mingw
ifndef FOSSIL_ENABLE_MINIZ
ZLIBCONFIG = LOC="-DASMV -DASMINF" OBJA="inffas86.o match.o"
LIBTARGETS = $(ZLIBDIR)/inffas86.o $(ZLIBDIR)/match.o
else
ZLIBCONFIG =
LIBTARGETS =
endif
else
SSLCONFIG = mingw64
ZLIBCONFIG =
LIBTARGETS =
endif

#### Disable creation of the OpenSSL shared libraries.  Also, disable support
#    for both SSLv2 and SSLv3 (i.e. thereby forcing the use of TLS).
#
SSLCONFIG += no-ssl2 no-ssl3 no-shared

................................................................................
endif
TCLTARGET = libtclstub86.a
else
LIBTCL = -ltcl86
TCLTARGET = binaries
endif









#### C Compile and options for use in building executables that
#    will run on the target platform.  This is usually the same
#    as BCC, unless you are cross-compiling.  This C compiler builds
#    the finished binary for fossil.  The BCC compiler above is used
#    for building intermediate code-generator tools.
#
TCC = $(PREFIX)gcc -Wall

#### Add the necessary command line options to build with debugging
#    symbols, if enabled.
#
ifdef FOSSIL_ENABLE_SYMBOLS
TCC += -g
else
................................................................................
ifdef USE_SYSTEM_SQLITE
LIB += -lsqlite3
endif

#### OpenSSL: Add the necessary libraries required, if enabled.
#
ifdef FOSSIL_ENABLE_SSL
LIB += -lssl -lcrypto -lgdi32
endif

#### Tcl: Add the necessary libraries required, if enabled.
#
ifdef FOSSIL_ENABLE_TCL
LIB += $(LIBTCL)
endif
................................................................................
$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION)
	$(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@

# The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
# to 1. If it is set to 1, then there is no need to build or link
# the sqlite3.o object. Instead, the system SQLite will be linked
# using -lsqlite3.
SQLITE3_OBJ.1 =
SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o

SQLITE3_OBJ.  = $(SQLITE3_OBJ.0)

# The FOSSIL_ENABLE_MINIZ variable may be undefined, set to 0, or
# set to 1.  If it is set to 1, the miniz library included in the
# source tree should be used; otherwise, it should not.
MINIZ_OBJ.0 =
MINIZ_OBJ.1 = $(OBJDIR)/miniz.o
................................................................................
# 0, ordinary SQLite is used.  If 1, then sqlite3-see.c (not part of
# the source tree) is used and extra flags are provided to enable
# the SQLite Encryption Extension.
SQLITE3_SRC.0 = sqlite3.c
SQLITE3_SRC.1 = sqlite3-see.c
SQLITE3_SRC. = sqlite3.c
SQLITE3_SRC = $(SRCDIR)/$(SQLITE3_SRC.$(USE_SEE))




SEE_FLAGS.0 =
SEE_FLAGS.1 = -DSQLITE_HAS_CODEC
SEE_FLAGS. =
SEE_FLAGS = $(SEE_FLAGS.$(USE_SEE))
}

writeln [string map [list <<<NEXT_LINE>>> \\] {
................................................................................
 $(OBJDIR)/th.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_lang.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_tcl.o <<<NEXT_LINE>>>
 $(OBJDIR)/cson_amalgamation.o
}]

writeln {
zlib:
	$(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) $(ZLIBCONFIG) -f win32/Makefile.gcc libz.a

clean-zlib:
	$(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc clean

$(ZLIBDIR)/inffas86.o:
	$(TCC) -c -o $@ -DASMINF -I$(ZLIBDIR) -O3 $(ZLIBDIR)/contrib/inflate86/inffas86.c

$(ZLIBDIR)/match.o:
	$(TCC) -c -o $@ -DASMV $(ZLIBDIR)/contrib/asm686/match.S







ifndef FOSSIL_ENABLE_MINIZ
LIBTARGETS += zlib


endif

openssl:	$(LIBTARGETS)
	cd $(OPENSSLLIBDIR);./Configure --cross-compile-prefix=$(PREFIX) $(SSLCONFIG)
	$(MAKE) -C $(OPENSSLLIBDIR) build_libs

clean-openssl:
	$(MAKE) -C $(OPENSSLLIBDIR) clean

tcl:
	cd $(TCLSRCDIR)/win;./configure
	$(MAKE) -C $(TCLSRCDIR)/win $(TCLTARGET)

clean-tcl:
	$(MAKE) -C $(TCLSRCDIR)/win distclean

APPTARGETS += $(LIBTARGETS)

ifdef FOSSIL_BUILD_SSL
APPTARGETS += openssl
endif

$(APPNAME):	$(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o
	$(CODECHECK1) $(TRANS_SRC)
................................................................................
foreach s [lsort $src] {
  writeln "\$(OBJDIR)/${s}_.c:\t\$(SRCDIR)/$s.c \$(TRANSLATE)"
  writeln "\t\$(TRANSLATE) \$(SRCDIR)/$s.c >\$@\n"
  writeln "\$(OBJDIR)/$s.o:\t\$(OBJDIR)/${s}_.c \$(OBJDIR)/$s.h$extra_h($s)\$(SRCDIR)/config.h"
  writeln "\t\$(XTCC) -o \$(OBJDIR)/$s.o -c \$(OBJDIR)/${s}_.c\n"
  writeln "\$(OBJDIR)/${s}.h:\t\$(OBJDIR)/headers\n"
}




set SQLITE_WIN32_OPTIONS $SQLITE_OPTIONS
lappend SQLITE_WIN32_OPTIONS -DSQLITE_WIN32_NO_ANSI

set MINGW_SQLITE_OPTIONS $SQLITE_WIN32_OPTIONS
lappend MINGW_SQLITE_OPTIONS -D_HAVE__MINGW_H
lappend MINGW_SQLITE_OPTIONS -DSQLITE_USE_MALLOC_H
lappend MINGW_SQLITE_OPTIONS -DSQLITE_USE_MSIZE

set MINIZ_WIN32_OPTIONS $MINIZ_OPTIONS

set j " \\\n                 "
writeln "SQLITE_OPTIONS = [join $MINGW_SQLITE_OPTIONS $j]\n"
................................................................................
writeln "\t\$(XTCC) \$(SQLITE_OPTIONS) \$(SQLITE_CFLAGS) \$(SEE_FLAGS) \\"
writeln "\t\t-c \$(SQLITE3_SRC) -o \$@\n"

writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/cson_amalgamation.c -o \$@\n"
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_status.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n"

writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h \$(SRCDIR)/../win/Makefile.mingw"
writeln "\t\$(XTCC) \$(SHELL_OPTIONS) \$(SHELL_CFLAGS) -c \$(SRCDIR)/shell.c -o \$@\n"

writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$@\n"

writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n"

................................................................................
SSLINCDIR = $(SSLDIR)\inc32
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLLIBDIR = $(SSLDIR)\out32dll
!else
SSLLIBDIR = $(SSLDIR)\out32
!endif
SSLLFLAGS = /nologo /opt:ref /debug
SSLLIB    = ssleay32.lib libeay32.lib user32.lib gdi32.lib
!if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64"
!message Using 'x64' platform for OpenSSL...
# BUGBUG (OpenSSL): Using "no-ssl*" here breaks the build.
# SSLCONFIG = VC-WIN64A no-asm no-ssl2 no-ssl3
SSLCONFIG = VC-WIN64A no-asm
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLCONFIG = $(SSLCONFIG) shared
................................................................................

mkversion$E: $(SRCDIR)\mkversion.c
	$(BCC) $**

codecheck1$E: $(SRCDIR)\codecheck1.c
	$(BCC) $**




$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc



	$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c

!if $(USE_SEE)!=0
SQLITE3_SRC = $(SRCDIR)\sqlite3-see.c
!else
SQLITE3_SRC = $(SRCDIR)\sqlite3.c
!endif








>







>







 







>







 







>

>







 







<

>







 







>
>
>
>







 







|
|







 







>
>
>
>
>
>
>
>






|







 







|


|




|







 







>
>
>
>
>
>
>
>
|
|




|







 







|







 







<

>







 







>
>
>
>







 







<
<
<
<
<
<






>
>

>
>
>
|
|
>
>


|

|


|



|


|

|







 







>
>
>





|







 







|
|







 







|







 







>
>
>
|
>
>
>
|







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
...
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
...
341
342
343
344
345
346
347

348
349
350
351
352
353
354
355
356
...
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
...
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
...
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
...
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
...
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
...
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
....
1035
1036
1037
1038
1039
1040
1041

1042
1043
1044
1045
1046
1047
1048
1049
1050
....
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
....
1076
1077
1078
1079
1080
1081
1082






1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
....
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
....
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
....
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
....
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
  content
  db
  delta
  deltacmd
  descendants
  diff
  diffcmd
  dispatch
  doc
  encode
  event
  export
  file
  finfo
  foci
  fshell
  fusefs
  glob
  graph
  gzip
  http
  http_socket
  http_transport
................................................................................
  tar
  th_main
  timeline
  tkt
  tktsetup
  undo
  unicode
  unversioned
  update
  url
  user
  utf8
  util
  verify
  vfile
................................................................................
}

# Options used to compile the included SQLite library.
#
set SQLITE_OPTIONS {
  -DNDEBUG=1
  -DSQLITE_OMIT_LOAD_EXTENSION=1
  -DSQLITE_OMIT_SHARED_CACHE
  -DSQLITE_ENABLE_LOCKING_STYLE=0
  -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1
  -DSQLITE_THREADSAFE=0
  -DSQLITE_DEFAULT_FILE_FORMAT=4
  -DSQLITE_OMIT_DEPRECATED
  -DSQLITE_ENABLE_EXPLAIN_COMMENTS
  -DSQLITE_ENABLE_FTS4
  -DSQLITE_ENABLE_FTS3_PARENTHESIS
  -DSQLITE_ENABLE_DBSTAT_VTAB
................................................................................
# Setup the options used to compile the included miniz library.
MINIZ_OPTIONS = <<<MINIZ_OPTIONS>>>

# The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
# to 1. If it is set to 1, then there is no need to build or link
# the sqlite3.o object. Instead, the system SQLite will be linked
# using -lsqlite3.

SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
SQLITE3_OBJ.1 =
SQLITE3_OBJ.  = $(SQLITE3_OBJ.0)

# The FOSSIL_ENABLE_MINIZ variable may be undefined, set to 0, or
# set to 1.  If it is set to 1, the miniz library included in the
# source tree should be used; otherwise, it should not.
MINIZ_OBJ.0 =
MINIZ_OBJ.1 = $(OBJDIR)/miniz.o
................................................................................
# 0, ordinary SQLite is used.  If 1, then sqlite3-see.c (not part of
# the source tree) is used and extra flags are provided to enable
# the SQLite Encryption Extension.
SQLITE3_SRC.0 = sqlite3.c
SQLITE3_SRC.1 = sqlite3-see.c
SQLITE3_SRC. = sqlite3.c
SQLITE3_SRC = $(SRCDIR)/$(SQLITE3_SRC.$(USE_SEE))
SQLITE3_SHELL_SRC.0 = shell.c
SQLITE3_SHELL_SRC.1 = shell-see.c
SQLITE3_SHELL_SRC. = shell.c
SQLITE3_SHELL_SRC = $(SRCDIR)/$(SQLITE3_SHELL_SRC.$(USE_SEE))
SEE_FLAGS.0 =
SEE_FLAGS.1 = -DSQLITE_HAS_CODEC
SEE_FLAGS. =
SEE_FLAGS = $(SEE_FLAGS.$(USE_SEE))
}]

writeln [string map [list <<<NEXT_LINE>>> \\] {
................................................................................
  writeln "\$(OBJDIR)/$s.h:\t\$(OBJDIR)/headers\n"
}

writeln "\$(OBJDIR)/sqlite3.o:\t\$(SQLITE3_SRC)"
writeln "\t\$(XTCC) \$(SQLITE_OPTIONS) \$(SQLITE_CFLAGS) \$(SEE_FLAGS) \\"
writeln "\t\t-c \$(SQLITE3_SRC) -o \$@"

writeln "\$(OBJDIR)/shell.o:\t\$(SQLITE3_SHELL_SRC) \$(SRCDIR)/sqlite3.h"
writeln "\t\$(XTCC) \$(SHELL_OPTIONS) \$(SHELL_CFLAGS) \$(LINENOISE_DEF.\$(USE_LINENOISE)) -c \$(SQLITE3_SHELL_SRC) -o \$@\n"

writeln "\$(OBJDIR)/linenoise.o:\t\$(SRCDIR)/linenoise.c \$(SRCDIR)/linenoise.h"
writeln "\t\$(XTCC) -c \$(SRCDIR)/linenoise.c -o \$@\n"

writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$@\n"

................................................................................
#    the following to point from the build directory to the src/ folder.
#
SRCDIR = src

#### The directory into which object code files should be written.
#
OBJDIR = wbld

#### C compiler for use in building executables that will run on
#    the platform that is doing the build.  This is used to compile
#    code-generator programs as part of the build process.  See TCC
#    and TCCEXE below for the C compiler for building the finished
#    binary.
#
BCCEXE = gcc

#### C Compiler and options for use in building executables that
#    will run on the platform that is doing the build.  This is used
#    to compile code-generator programs as part of the build process.
#    See TCC below for the C compiler for building the finished binary.
#
BCC = $(BCCEXE)

#### Enable compiling with debug symbols (much larger binary)
#
# FOSSIL_ENABLE_SYMBOLS = 1

#### Enable JSON (http://www.json.org) support using "cson"
#
................................................................................
#    used, taking into account whether zlib is actually enabled and the target
#    processor architecture.
#
ifndef X64
SSLCONFIG = mingw
ifndef FOSSIL_ENABLE_MINIZ
ZLIBCONFIG = LOC="-DASMV -DASMINF" OBJA="inffas86.o match.o"
ZLIBTARGETS = $(ZLIBDIR)/inffas86.o $(ZLIBDIR)/match.o
else
ZLIBCONFIG =
ZLIBTARGETS =
endif
else
SSLCONFIG = mingw64
ZLIBCONFIG =
ZLIBTARGETS =
endif

#### Disable creation of the OpenSSL shared libraries.  Also, disable support
#    for both SSLv2 and SSLv3 (i.e. thereby forcing the use of TLS).
#
SSLCONFIG += no-ssl2 no-ssl3 no-shared

................................................................................
endif
TCLTARGET = libtclstub86.a
else
LIBTCL = -ltcl86
TCLTARGET = binaries
endif

#### C compiler for use in building executables that will run on the
#    target platform.  This is usually the same as BCCEXE, unless you
#    are cross-compiling.  This C compiler builds the finished binary
#    for fossil.  See BCC and BCCEXE above for the C compiler for
#    building intermediate code-generator tools.
#
TCCEXE = gcc

#### C compiler and options for use in building executables that will
#    run on the target platform.  This is usually the almost the same
#    as BCC, unless you are cross-compiling.  This C compiler builds
#    the finished binary for fossil.  The BCC compiler above is used
#    for building intermediate code-generator tools.
#
TCC = $(PREFIX)$(TCCEXE) -Wall

#### Add the necessary command line options to build with debugging
#    symbols, if enabled.
#
ifdef FOSSIL_ENABLE_SYMBOLS
TCC += -g
else
................................................................................
ifdef USE_SYSTEM_SQLITE
LIB += -lsqlite3
endif

#### OpenSSL: Add the necessary libraries required, if enabled.
#
ifdef FOSSIL_ENABLE_SSL
LIB += -lssl -lcrypto -lgdi32 -lcrypt32
endif

#### Tcl: Add the necessary libraries required, if enabled.
#
ifdef FOSSIL_ENABLE_TCL
LIB += $(LIBTCL)
endif
................................................................................
$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION)
	$(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@

# The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
# to 1. If it is set to 1, then there is no need to build or link
# the sqlite3.o object. Instead, the system SQLite will be linked
# using -lsqlite3.

SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
SQLITE3_OBJ.1 =
SQLITE3_OBJ.  = $(SQLITE3_OBJ.0)

# The FOSSIL_ENABLE_MINIZ variable may be undefined, set to 0, or
# set to 1.  If it is set to 1, the miniz library included in the
# source tree should be used; otherwise, it should not.
MINIZ_OBJ.0 =
MINIZ_OBJ.1 = $(OBJDIR)/miniz.o
................................................................................
# 0, ordinary SQLite is used.  If 1, then sqlite3-see.c (not part of
# the source tree) is used and extra flags are provided to enable
# the SQLite Encryption Extension.
SQLITE3_SRC.0 = sqlite3.c
SQLITE3_SRC.1 = sqlite3-see.c
SQLITE3_SRC. = sqlite3.c
SQLITE3_SRC = $(SRCDIR)/$(SQLITE3_SRC.$(USE_SEE))
SQLITE3_SHELL_SRC.0 = shell.c
SQLITE3_SHELL_SRC.1 = shell-see.c
SQLITE3_SHELL_SRC. = shell.c
SQLITE3_SHELL_SRC = $(SRCDIR)/$(SQLITE3_SHELL_SRC.$(USE_SEE))
SEE_FLAGS.0 =
SEE_FLAGS.1 = -DSQLITE_HAS_CODEC
SEE_FLAGS. =
SEE_FLAGS = $(SEE_FLAGS.$(USE_SEE))
}

writeln [string map [list <<<NEXT_LINE>>> \\] {
................................................................................
 $(OBJDIR)/th.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_lang.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_tcl.o <<<NEXT_LINE>>>
 $(OBJDIR)/cson_amalgamation.o
}]

writeln {






$(ZLIBDIR)/inffas86.o:
	$(TCC) -c -o $@ -DASMINF -I$(ZLIBDIR) -O3 $(ZLIBDIR)/contrib/inflate86/inffas86.c

$(ZLIBDIR)/match.o:
	$(TCC) -c -o $@ -DASMV $(ZLIBDIR)/contrib/asm686/match.S

zlib:	$(ZLIBTARGETS)
	$(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) $(ZLIBCONFIG) -f win32/Makefile.gcc libz.a

clean-zlib:
	$(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) -f win32/Makefile.gcc clean

ifdef FOSSIL_ENABLE_MINIZ
BLDTARGETS =
else
BLDTARGETS = zlib
endif

openssl:	$(BLDTARGETS)
	cd $(OPENSSLLIBDIR);./Configure --cross-compile-prefix=$(PREFIX) $(SSLCONFIG)
	$(MAKE) -C $(OPENSSLLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) build_libs

clean-openssl:
	$(MAKE) -C $(OPENSSLLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) clean

tcl:
	cd $(TCLSRCDIR)/win;./configure
	$(MAKE) -C $(TCLSRCDIR)/win PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) $(TCLTARGET)

clean-tcl:
	$(MAKE) -C $(TCLSRCDIR)/win PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) distclean

APPTARGETS += $(BLDTARGETS)

ifdef FOSSIL_BUILD_SSL
APPTARGETS += openssl
endif

$(APPNAME):	$(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o
	$(CODECHECK1) $(TRANS_SRC)
................................................................................
foreach s [lsort $src] {
  writeln "\$(OBJDIR)/${s}_.c:\t\$(SRCDIR)/$s.c \$(TRANSLATE)"
  writeln "\t\$(TRANSLATE) \$(SRCDIR)/$s.c >\$@\n"
  writeln "\$(OBJDIR)/$s.o:\t\$(OBJDIR)/${s}_.c \$(OBJDIR)/$s.h$extra_h($s)\$(SRCDIR)/config.h"
  writeln "\t\$(XTCC) -o \$(OBJDIR)/$s.o -c \$(OBJDIR)/${s}_.c\n"
  writeln "\$(OBJDIR)/${s}.h:\t\$(OBJDIR)/headers\n"
}

writeln {MINGW_OPTIONS = -D_HAVE__MINGW_H
}

set SQLITE_WIN32_OPTIONS $SQLITE_OPTIONS
lappend SQLITE_WIN32_OPTIONS -DSQLITE_WIN32_NO_ANSI

set MINGW_SQLITE_OPTIONS $SQLITE_WIN32_OPTIONS
lappend MINGW_SQLITE_OPTIONS {$(MINGW_OPTIONS)}
lappend MINGW_SQLITE_OPTIONS -DSQLITE_USE_MALLOC_H
lappend MINGW_SQLITE_OPTIONS -DSQLITE_USE_MSIZE

set MINIZ_WIN32_OPTIONS $MINIZ_OPTIONS

set j " \\\n                 "
writeln "SQLITE_OPTIONS = [join $MINGW_SQLITE_OPTIONS $j]\n"
................................................................................
writeln "\t\$(XTCC) \$(SQLITE_OPTIONS) \$(SQLITE_CFLAGS) \$(SEE_FLAGS) \\"
writeln "\t\t-c \$(SQLITE3_SRC) -o \$@\n"

writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/cson_amalgamation.c -o \$@\n"
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_status.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n"

writeln "\$(OBJDIR)/shell.o:\t\$(SQLITE3_SHELL_SRC) \$(SRCDIR)/sqlite3.h \$(SRCDIR)/../win/Makefile.mingw"
writeln "\t\$(XTCC) \$(SHELL_OPTIONS) \$(SHELL_CFLAGS) -c \$(SQLITE3_SHELL_SRC) -o \$@\n"

writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$@\n"

writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n"

................................................................................
SSLINCDIR = $(SSLDIR)\inc32
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLLIBDIR = $(SSLDIR)\out32dll
!else
SSLLIBDIR = $(SSLDIR)\out32
!endif
SSLLFLAGS = /nologo /opt:ref /debug
SSLLIB    = ssleay32.lib libeay32.lib user32.lib gdi32.lib crypt32.lib
!if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64"
!message Using 'x64' platform for OpenSSL...
# BUGBUG (OpenSSL): Using "no-ssl*" here breaks the build.
# SSLCONFIG = VC-WIN64A no-asm no-ssl2 no-ssl3
SSLCONFIG = VC-WIN64A no-asm
!if $(FOSSIL_DYNAMIC_BUILD)!=0
SSLCONFIG = $(SSLCONFIG) shared
................................................................................

mkversion$E: $(SRCDIR)\mkversion.c
	$(BCC) $**

codecheck1$E: $(SRCDIR)\codecheck1.c
	$(BCC) $**

!if $(USE_SEE)!=0
SQLITE3_SHELL_SRC = $(SRCDIR)\shell-see.c
!else
SQLITE3_SHELL_SRC = $(SRCDIR)\shell.c
!endif

$(OX)\shell$O : $(SQLITE3_SHELL_SRC) $B\win\Makefile.msc
	$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SQLITE3_SHELL_SRC)

!if $(USE_SEE)!=0
SQLITE3_SRC = $(SRCDIR)\sqlite3-see.c
!else
SQLITE3_SRC = $(SRCDIR)\sqlite3.c
!endif

Changes to src/manifest.c.

1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
....
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548












































































































1549
1550





1551
1552
1553
1554
1555
1556
1557
....
1590
1591
1592
1593
1594
1595
1596











1597
1598
1599
1600
1601
1602
1603
....
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
....
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
....
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
....
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
....
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
....
2246
2247
2248
2249
2250
2251
2252


2253
2254
2255
2256


2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
    if( *ppOther==0 ) return;
  }
  if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){
    manifest_destroy(*ppOther);
    return;
  }
  isPublic = !content_is_private(mid);
  
  /* If pParent is not the primary parent of pChild, and the primary
  ** parent of pChild is a phantom, then abort this routine without
  ** doing any work.  The mlink entries will be computed when the
  ** primary parent dephantomizes.
  */
  if( !isPrim && otherRid==mid
   && !db_exists("SELECT 1 FROM blob WHERE uuid=%Q AND size>0",
................................................................................
      if( pChildFile==0 && pParentFile->zUuid!=0 ){
        add_one_mlink(pmid, pParentFile->zUuid, mid, 0, pParentFile->zName, 0,
                      isPublic, isPrim, 0);
      }
    }
  }
  manifest_cache_insert(*ppOther);
  
  /* If pParent is the primary parent of pChild, also run this analysis
  ** for all merge parents of pChild
  */
  if( isPrim ){
    for(i=1; i<pChild->nParent; i++){
      pmid = uuid_to_rid(pChild->azParent[i], 0);
      if( pmid<=0 ) continue;
      add_mlink(pmid, 0, mid, pChild, 0);
    }
  }
}

/*












































































































** Setup to do multiple manifest_crosslink() calls.
** This is only required if processing ticket changes.





*/
void manifest_crosslink_begin(void){
  assert( manifest_crosslink_busy==0 );
  manifest_crosslink_busy = 1;
  db_begin_transaction();
  db_multi_exec(
     "CREATE TEMP TABLE pending_tkt(uuid TEXT UNIQUE);"
................................................................................
  assert( manifest_crosslink_busy==1 );
  if( permitHooks ){
    rc = xfer_run_common_script();
    if( rc==TH_OK ){
      zScript = xfer_ticket_code();
    }
  }











  db_prepare(&q, "SELECT uuid FROM pending_tkt");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q, 0);
    ticket_rebuild_entry(zUuid);
    if( permitHooks && rc==TH_OK ){
      rc = xfer_run_script(zScript, zUuid, 0);
    }
................................................................................
** Processing for other control artifacts was added later.  The name
** of the routine, "manifest_crosslink", and the name of this source
** file, is a legacy of its original use.
*/
int manifest_crosslink(int rid, Blob *pContent, int flags){
  int i, rc = TH_OK;
  Manifest *p;
  Stmt q;
  int parentid = 0;
  int permitHooks = (flags & MC_PERMIT_HOOKS);
  const char *zScript = 0;
  const char *zUuid = 0;

  if( (p = manifest_cache_find(rid))!=0 ){
    blob_reset(pContent);
................................................................................
  if( p->type==CFTYPE_MANIFEST ){
    if( permitHooks ){
      zScript = xfer_commit_code();
      zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
    }
    if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
      char *zCom;
      char zBaseId[30];
      if( p->zBaseline ){
        sqlite3_snprintf(sizeof(zBaseId), zBaseId, "%d",
                         uuid_to_rid(p->zBaseline,1));
      }else{
        sqlite3_snprintf(sizeof(zBaseId), zBaseId, "NULL");
      }
      for(i=0; i<p->nParent; i++){
        int pid = uuid_to_rid(p->azParent[i], 1);
        db_multi_exec(
           "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime, baseid)"
           "VALUES(%d, %d, %d, %.17g, %s)",
           pid, rid, i==0, p->rDate, zBaseId/*safe-for-%s*/);
        if( i==0 ) parentid = pid;
      }
      add_mlink(parentid, 0, rid, p, 1);
      if( p->nParent>1 ){
        /* Change MLINK.PID from 0 to -1 for files that are added by merge. */
        db_multi_exec(
           "UPDATE mlink SET pid=-1"
           " WHERE mid=%d"
           "   AND pid=0"
           "   AND fnid IN "
           "  (SELECT fnid FROM mlink WHERE mid=%d GROUP BY fnid"
           "    HAVING count(*)<%d)",
           rid, rid, p->nParent
        );
      }
      db_prepare(&q, "SELECT cid, isprim FROM plink WHERE pid=%d", rid);
      while( db_step(&q)==SQLITE_ROW ){
        int cid = db_column_int(&q, 0);
        int isprim = db_column_int(&q, 1);
        add_mlink(rid, p, cid, 0, isprim);
      }
      db_finalize(&q);
      if( p->nParent==0 ){
        /* For root files (files without parents) add mlink entries
        ** showing all content as new. */
        int isPublic = !content_is_private(rid);
        for(i=0; i<p->nFile; i++){
          add_one_mlink(0, 0, rid, p->aFile[i].zUuid, p->aFile[i].zName, 0,
                        isPublic, 1, manifest_file_mperm(&p->aFile[i]));
        }
      }
      search_doc_touch('c', rid, 0);
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment,"
                           "bgcolor,euser,ecomment,omtime)"
        "VALUES('ci',"
        "  coalesce("
        "    (SELECT julianday(value) FROM tagxref WHERE tagid=%d AND rid=%d),"
................................................................................
      const char *zName = db_column_text(&qatt, 3);
      const char isAdd = (zSrc && zSrc[0]) ? 1 : 0;
      char *zComment;
      if( isAdd ){
        zComment = mprintf(
             "Add attachment [/artifact/%!S|%h] to"
             " tech note [/technote/%!S|%S]",
             zSrc, zName, zTarget, zTarget); 
      }else{
        zComment = mprintf(
             "Delete attachment \"%h\" from"
             " tech note [/technote/%!S|%S]",
             zName, zTarget, zTarget);
      }
      db_multi_exec("UPDATE event SET comment=%Q, type='e'"
                       " WHERE objid=%Q",
                    zComment, zAttachId);
      fossil_free(zComment);      
    }
    db_finalize(&qatt);
  }
  if( p->type==CFTYPE_TICKET ){
    char *zTag;
    Stmt qatt;
    assert( manifest_crosslink_busy==1 );
................................................................................
      }else{
        zComment = mprintf("Delete attachment \"%h\" from ticket [%!S|%S]",
             zName, zTarget, zTarget);
      }
      db_multi_exec("UPDATE event SET comment=%Q, type='t'"
                       " WHERE objid=%Q",
                    zComment, zAttachId);
      fossil_free(zComment);      
    }
    db_finalize(&qatt);
  }
  if( p->type==CFTYPE_ATTACHMENT ){
    char *zComment = 0;
    const char isAdd = (p->zAttachSrc && p->zAttachSrc[0]) ? 1 : 0;
    /* We assume that we're attaching to a wiki page until we
    ** prove otherwise (which could on a later artifact if we
    ** process the attachment artifact before the artifact to
    ** which it is attached!) */
    char attachToType = 'w';       
    if( fossil_is_uuid(p->zAttachTarget) ){
      if( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'",
            p->zAttachTarget)
        ){
        attachToType = 't';          /* Attaching to known ticket */
      }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'",
                  p->zAttachTarget) 
            ){
        attachToType = 'e';          /* Attaching to known tech note */
      }
    }
    db_multi_exec(
       "INSERT INTO attachment(attachid, mtime, src, target,"
                              "filename, comment, user)"
................................................................................
        zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
             p->zAttachName, p->zAttachTarget);
      }
    }else if( 'e' == attachToType ){
      if( isAdd ){
        zComment = mprintf(
          "Add attachment [/artifact/%!S|%h] to tech note [/technote/%!S|%S]",
          p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget); 
      }else{
        zComment = mprintf(
             "Delete attachment \"/artifact/%!S|%h\" from"
             " tech note [/technote/%!S|%S]",
             p->zAttachName, p->zAttachName,
             p->zAttachTarget,p->zAttachTarget);
      }      
    }else{
      if( isAdd ){
        zComment = mprintf(
             "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
             p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
      }else{
        zComment = mprintf("Delete attachment \"%h\" from ticket [%!S|%S]",
................................................................................
        continue;
      }else if( strcmp(zName, "+date")==0 ){
        blob_appendf(&comment, " Timestamp %h.", zValue);
        continue;
      }else if( memcmp(zName, "-sym-",5)==0 ){
        if( !branchMove ){
          blob_appendf(&comment, " Cancel tag \"%h\"", &zName[5]);


        }
      }else if( memcmp(zName, "*sym-",5)==0 ){
        if( !branchMove ){
          blob_appendf(&comment, " Add propagating tag \"%h\"", &zName[5]);


        }
      }else if( memcmp(zName, "+sym-",5)==0 ){
        blob_appendf(&comment, " Add tag \"%h\"", &zName[5]);
      }else if( strcmp(zName, "+closed")==0 ){
        blob_append(&comment, " Marked \"Closed\"", -1);
      }else if( strcmp(zName, "-closed")==0 ){
        blob_append(&comment, " Removed the \"Closed\" mark", -1);
      }else {
        if( zName[0]=='-' ){
          blob_appendf(&comment, " Cancel \"%h\"", &zName[1]);
        }else if( zName[0]=='+' ){
          blob_appendf(&comment, " Add \"%h\"", &zName[1]);
        }else{
          blob_appendf(&comment, " Add propagating \"%h\"", &zName[1]);







|







 







|













>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>







 







<







 







|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|









|







 







|










|






|







 







|






|







 







>
>




>
>




|

|







1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
....
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657

1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
....
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
....
1919
1920
1921
1922
1923
1924
1925

1926
1927
1928
1929
1930
1931
1932
....
1957
1958
1959
1960
1961
1962
1963
1964











































1965
1966
1967
1968
1969
1970
1971
....
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
....
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
....
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
....
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
    if( *ppOther==0 ) return;
  }
  if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){
    manifest_destroy(*ppOther);
    return;
  }
  isPublic = !content_is_private(mid);

  /* If pParent is not the primary parent of pChild, and the primary
  ** parent of pChild is a phantom, then abort this routine without
  ** doing any work.  The mlink entries will be computed when the
  ** primary parent dephantomizes.
  */
  if( !isPrim && otherRid==mid
   && !db_exists("SELECT 1 FROM blob WHERE uuid=%Q AND size>0",
................................................................................
      if( pChildFile==0 && pParentFile->zUuid!=0 ){
        add_one_mlink(pmid, pParentFile->zUuid, mid, 0, pParentFile->zName, 0,
                      isPublic, isPrim, 0);
      }
    }
  }
  manifest_cache_insert(*ppOther);

  /* If pParent is the primary parent of pChild, also run this analysis
  ** for all merge parents of pChild
  */
  if( isPrim ){
    for(i=1; i<pChild->nParent; i++){
      pmid = uuid_to_rid(pChild->azParent[i], 0);
      if( pmid<=0 ) continue;
      add_mlink(pmid, 0, mid, pChild, 0);
    }
  }
}

/*
** For a check-in with RID "rid" that has nParent parent check-ins given
** by the UUIDs in azParent[], create all appropriate plink and mlink table
** entries.
**
** The primary parent is the first UUID on the azParent[] list.
**
** Return the RID of the primary parent.
*/
static int manifest_add_checkin_linkages(
  int rid,                   /* The RID of the check-in */
  Manifest *p,               /* Manifest for this check-in */
  int nParent,               /* Number of parents for this check-in */
  char **azParent            /* UUIDs for each parent */
){
  int i;
  int parentid = 0;
  char zBaseId[30];    /* Baseline manifest RID for deltas.  "NULL" otherwise */
  Stmt q;

  if( p->zBaseline ){
     sqlite3_snprintf(sizeof(zBaseId), zBaseId, "%d",
                      uuid_to_rid(p->zBaseline,1));
  }else{
     sqlite3_snprintf(sizeof(zBaseId), zBaseId, "NULL");
  }
  for(i=0; i<nParent; i++){
    int pid = uuid_to_rid(azParent[i], 1);
    db_multi_exec(
       "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime, baseid)"
        "VALUES(%d, %d, %d, %.17g, %s)",
       pid, rid, i==0, p->rDate, zBaseId/*safe-for-%s*/);
    if( i==0 ) parentid = pid;
  }
  add_mlink(parentid, 0, rid, p, 1);
  if( nParent>1 ){
    /* Change MLINK.PID from 0 to -1 for files that are added by merge. */
    db_multi_exec(
      "UPDATE mlink SET pid=-1"
      " WHERE mid=%d"
      "   AND pid=0"
      "   AND fnid IN "
      "  (SELECT fnid FROM mlink WHERE mid=%d GROUP BY fnid"
      "    HAVING count(*)<%d)",
      rid, rid, nParent
    );
  }
  db_prepare(&q, "SELECT cid, isprim FROM plink WHERE pid=%d", rid);
  while( db_step(&q)==SQLITE_ROW ){
    int cid = db_column_int(&q, 0);
    int isprim = db_column_int(&q, 1);
    add_mlink(rid, p, cid, 0, isprim);
  }
  db_finalize(&q);
  if( nParent==0 ){
    /* For root files (files without parents) add mlink entries
    ** showing all content as new. */
    int isPublic = !content_is_private(rid);
    for(i=0; i<p->nFile; i++){
      add_one_mlink(0, 0, rid, p->aFile[i].zUuid, p->aFile[i].zName, 0,
                    isPublic, 1, manifest_file_mperm(&p->aFile[i]));
    }
  }
  return parentid;
}

/*
** There exists a "parent" tag against checkin rid that has value zValue.
** If value is well-formed (meaning that it is a list of UUIDs), then use
** zValue to reparent check-in rid.
*/
void manifest_reparent_checkin(int rid, const char *zValue){
  int nParent;
  char *zCopy = 0;
  char **azParent = 0;
  Manifest *p = 0;
  int i;
  int n = (int)strlen(zValue);
  nParent = (n+1)/(UUID_SIZE+1);
  if( nParent*(UUID_SIZE+1) - 1 !=n ) return;
  if( nParent<1 ) return;
  zCopy = fossil_strdup(zValue);
  azParent = fossil_malloc( sizeof(azParent[0])*nParent );
  for(i=0; i<nParent; i++){
    azParent[i] = &zCopy[i*(UUID_SIZE+1)];
    if( i<nParent-1 && azParent[i][UUID_SIZE]!=' ' ) break;
    azParent[i][UUID_SIZE] = 0;
    if( !validate16(azParent[i],UUID_SIZE) ) break;
  }
  if( i==nParent
   && !db_exists("SELECT 1 FROM plink WHERE cid=%d AND pid=%d",
                 rid, uuid_to_rid(azParent[0],0))
  ){
    p = manifest_get(rid, CFTYPE_MANIFEST, 0);
  }
  if( p!=0 ){
    db_multi_exec(
       "DELETE FROM plink WHERE cid=%d;"
       "DELETE FROM mlink WHERE mid=%d;",
       rid, rid
    );
    manifest_add_checkin_linkages(rid,p,nParent,azParent);
  }
  manifest_destroy(p);
  fossil_free(azParent);
  fossil_free(zCopy);
}

/*
** Setup to do multiple manifest_crosslink() calls.

**
** This routine creates TEMP tables for holding information for
** processing that must be deferred until all artifacts have been
** seen at least once.  The deferred processing is accomplished
** by the call to manifest_crosslink_end().
*/
void manifest_crosslink_begin(void){
  assert( manifest_crosslink_busy==0 );
  manifest_crosslink_busy = 1;
  db_begin_transaction();
  db_multi_exec(
     "CREATE TEMP TABLE pending_tkt(uuid TEXT UNIQUE);"
................................................................................
  assert( manifest_crosslink_busy==1 );
  if( permitHooks ){
    rc = xfer_run_common_script();
    if( rc==TH_OK ){
      zScript = xfer_ticket_code();
    }
  }
  db_prepare(&q,
     "SELECT rid, value FROM tagxref"
     " WHERE tagid=%d AND tagtype=1",
     TAG_PARENT
  );
  while( db_step(&q)==SQLITE_ROW ){
    int rid = db_column_int(&q,0);
    const char *zValue = db_column_text(&q,1);
    manifest_reparent_checkin(rid, zValue);
  }
  db_finalize(&q);
  db_prepare(&q, "SELECT uuid FROM pending_tkt");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q, 0);
    ticket_rebuild_entry(zUuid);
    if( permitHooks && rc==TH_OK ){
      rc = xfer_run_script(zScript, zUuid, 0);
    }
................................................................................
** Processing for other control artifacts was added later.  The name
** of the routine, "manifest_crosslink", and the name of this source
** file, is a legacy of its original use.
*/
int manifest_crosslink(int rid, Blob *pContent, int flags){
  int i, rc = TH_OK;
  Manifest *p;

  int parentid = 0;
  int permitHooks = (flags & MC_PERMIT_HOOKS);
  const char *zScript = 0;
  const char *zUuid = 0;

  if( (p = manifest_cache_find(rid))!=0 ){
    blob_reset(pContent);
................................................................................
  if( p->type==CFTYPE_MANIFEST ){
    if( permitHooks ){
      zScript = xfer_commit_code();
      zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
    }
    if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
      char *zCom;
      parentid = manifest_add_checkin_linkages(rid,p,p->nParent,p->azParent);











































      search_doc_touch('c', rid, 0);
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment,"
                           "bgcolor,euser,ecomment,omtime)"
        "VALUES('ci',"
        "  coalesce("
        "    (SELECT julianday(value) FROM tagxref WHERE tagid=%d AND rid=%d),"
................................................................................
      const char *zName = db_column_text(&qatt, 3);
      const char isAdd = (zSrc && zSrc[0]) ? 1 : 0;
      char *zComment;
      if( isAdd ){
        zComment = mprintf(
             "Add attachment [/artifact/%!S|%h] to"
             " tech note [/technote/%!S|%S]",
             zSrc, zName, zTarget, zTarget);
      }else{
        zComment = mprintf(
             "Delete attachment \"%h\" from"
             " tech note [/technote/%!S|%S]",
             zName, zTarget, zTarget);
      }
      db_multi_exec("UPDATE event SET comment=%Q, type='e'"
                       " WHERE objid=%Q",
                    zComment, zAttachId);
      fossil_free(zComment);
    }
    db_finalize(&qatt);
  }
  if( p->type==CFTYPE_TICKET ){
    char *zTag;
    Stmt qatt;
    assert( manifest_crosslink_busy==1 );
................................................................................
      }else{
        zComment = mprintf("Delete attachment \"%h\" from ticket [%!S|%S]",
             zName, zTarget, zTarget);
      }
      db_multi_exec("UPDATE event SET comment=%Q, type='t'"
                       " WHERE objid=%Q",
                    zComment, zAttachId);
      fossil_free(zComment);
    }
    db_finalize(&qatt);
  }
  if( p->type==CFTYPE_ATTACHMENT ){
    char *zComment = 0;
    const char isAdd = (p->zAttachSrc && p->zAttachSrc[0]) ? 1 : 0;
    /* We assume that we're attaching to a wiki page until we
    ** prove otherwise (which could on a later artifact if we
    ** process the attachment artifact before the artifact to
    ** which it is attached!) */
    char attachToType = 'w';
    if( fossil_is_uuid(p->zAttachTarget) ){
      if( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'",
            p->zAttachTarget)
        ){
        attachToType = 't';          /* Attaching to known ticket */
      }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'",
                  p->zAttachTarget)
            ){
        attachToType = 'e';          /* Attaching to known tech note */
      }
    }
    db_multi_exec(
       "INSERT INTO attachment(attachid, mtime, src, target,"
                              "filename, comment, user)"
................................................................................
        zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
             p->zAttachName, p->zAttachTarget);
      }
    }else if( 'e' == attachToType ){
      if( isAdd ){
        zComment = mprintf(
          "Add attachment [/artifact/%!S|%h] to tech note [/technote/%!S|%S]",
          p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
      }else{
        zComment = mprintf(
             "Delete attachment \"/artifact/%!S|%h\" from"
             " tech note [/technote/%!S|%S]",
             p->zAttachName, p->zAttachName,
             p->zAttachTarget,p->zAttachTarget);
      }
    }else{
      if( isAdd ){
        zComment = mprintf(
             "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
             p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
      }else{
        zComment = mprintf("Delete attachment \"%h\" from ticket [%!S|%S]",
................................................................................
        continue;
      }else if( strcmp(zName, "+date")==0 ){
        blob_appendf(&comment, " Timestamp %h.", zValue);
        continue;
      }else if( memcmp(zName, "-sym-",5)==0 ){
        if( !branchMove ){
          blob_appendf(&comment, " Cancel tag \"%h\"", &zName[5]);
        }else{
          continue;
        }
      }else if( memcmp(zName, "*sym-",5)==0 ){
        if( !branchMove ){
          blob_appendf(&comment, " Add propagating tag \"%h\"", &zName[5]);
        }else{
          continue;
        }
      }else if( memcmp(zName, "+sym-",5)==0 ){
        blob_appendf(&comment, " Add tag \"%h\"", &zName[5]);
      }else if( strcmp(zName, "+closed")==0 ){
        blob_append(&comment, " Mark \"Closed\"", -1);
      }else if( strcmp(zName, "-closed")==0 ){
        blob_append(&comment, " Remove the \"Closed\" mark", -1);
      }else {
        if( zName[0]=='-' ){
          blob_appendf(&comment, " Cancel \"%h\"", &zName[1]);
        }else if( zName[0]=='+' ){
          blob_appendf(&comment, " Add \"%h\"", &zName[1]);
        }else{
          blob_appendf(&comment, " Add propagating \"%h\"", &zName[1]);

Changes to src/markdown.c.

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
...
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
...
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
...
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
...
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
...
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
...
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
....
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
....
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
....
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
....
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
....
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
....
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
....
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
....
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
 ********************/

#if INTERFACE

/* mkd_autolink -- type of autolink */
enum mkd_autolink {
  MKDA_NOT_AUTOLINK,    /* used internally when it is not an autolink*/
  MKDA_NORMAL,          /* normal http/http/ftp/etc link */
  MKDA_EXPLICIT_EMAIL,  /* e-mail link with explit mailto: */
  MKDA_IMPLICIT_EMAIL   /* e-mail link without mailto: */
};

/* mkd_renderer -- functions for rendering parsed data */
struct mkd_renderer {
  /* document level callbacks */
  void (*prolog)(struct Blob *ob, void *opaque);
................................................................................
    i++;
  }
  if( i>=size || data[i]!='>' || nb!=1 ) return 0;
  return i+1;
}


/* tag_length -- returns the length of the given tag, or 0 is it's not valid */
static size_t tag_length(char *data, size_t size, enum mkd_autolink *autolink){
  size_t i, j;

  /* a valid tag can't be shorter than 3 chars */
  if( size<3 ) return 0;

  /* begins with a '<' optionally followed by '/', followed by letter */
................................................................................
    /* one of the forbidden chars has been found */
    *autolink = MKDA_NOT_AUTOLINK;
  }else if( (j = is_mail_autolink(data+i, size-i))!=0 ){
    *autolink = (i==8) ? MKDA_EXPLICIT_EMAIL : MKDA_IMPLICIT_EMAIL;
    return i+j;
  }

  /* looking for sometinhg looking like a tag end */
  while( i<size && data[i]!='>' ){ i++; }
  if( i>=size ) return 0;
  return i+1;
}


/* parse_inline -- parses inline markdown elements */
................................................................................
      i++;
    }
  }
  return 0;
}


/* parse_emph1 -- parsing single emphase */
/* closed by a symbol not preceded by whitespace and not followed by symbol */
static size_t parse_emph1(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size,
  char c
................................................................................
      return r ? i+1 : 0;
    }
  }
  return 0;
}


/* parse_emph2 -- parsing single emphase */
static size_t parse_emph2(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size,
  char c
){
................................................................................
    }
    i++;
  }
  return 0;
}


/* parse_emph3 -- parsing single emphase */
/* finds the first closing tag, and delegates to the other emph */
static size_t parse_emph3(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size,
  char c
................................................................................
    }
  }
  return 2;
}


/* char_entity -- '&' escaped when it doesn't belong to an entity */
/* valid entities are assumed to be anything mathing &#?[A-Za-z0-9]+; */
static size_t char_entity(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t offset,
  size_t size
){
................................................................................
    if( id_end>=size ) goto char_link_cleanup;

    if( i+1==id_end ){
      /* implicit id - use the contents */
      id_data = data+1;
      id_size = txt_e-1;
    }else{
      /* explici id - between brackets */
      id_data = data+i+1;
      id_size = id_end-(i+1);
    }

    if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
      goto char_link_cleanup;
    }
................................................................................
    return (i>=size || data[i]=='\n') ? 2 : 0;
  }

  return 0;
}


/* is_table_sep -- returns wether there is a table separator at the given pos */
static int is_table_sep(char *data, size_t pos){
  return data[pos]=='|' && (pos==0 || data[pos-1]!='\\');
}


/* is_tableline -- returns the number of column tables in the given line */
static int is_tableline(char *data, size_t size){
................................................................................
    }
  }else{
    return 0;
  }
}


/* prefix_code -- returns prefix length for block code*/
static size_t prefix_code(char *data, size_t size){
  if( size>0 && data[0]=='\t' ) return 1;
  if( size>3 && data[0]==' ' && data[1]==' ' && data[2]==' ' && data[3]==' ' ){
    return 4;
  }
  return 0;
}
................................................................................
static void parse_block(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size);


/* parse_blockquote -- hanldes parsing of a blockquote fragment */
static size_t parse_blockquote(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size
){
  size_t beg, end = 0, pre, work_size = 0;
................................................................................
    rndr->make.blockquote(ob, out ? out : &fallback, rndr->make.opaque);
  }
  release_work_buffer(rndr, out);
  return end;
}


/* parse_blockquote -- hanldes parsing of a regular paragraph */
static size_t parse_paragraph(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size
){
  size_t i = 0, end = 0;
................................................................................
      release_work_buffer(rndr, span);
    }
  }
  return end;
}


/* parse_blockquote -- hanldes parsing of a block-level code fragment */
static size_t parse_blockcode(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size
){
  size_t beg, end, pre;
................................................................................
  int flags               /* table flags */
){
  size_t i = 0, col = 0;
  size_t beg, end, total = 0;
  struct Blob *cells = new_work_buffer(rndr);
  int align;

  /* skip leading blanks and sperator */
  while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; }
  if( i<size && data[i]=='|' ) i++;

  /* go over all the cells */
  while( i<size && total==0 ){
    /* check optional left/center align marker */
    align = 0;
................................................................................

/* is_ref -- returns whether a line is a reference or not */
static int is_ref(
  char *data,         /* input text */
  size_t beg,         /* offset of the beginning of the line */
  size_t end,         /* offset of the end of the text */
  size_t *last,       /* last character of the link */
  struct Blob *refs   /* arry of link references */
){
  size_t i = 0;
  size_t id_offset, id_end;
  size_t link_offset, link_end;
  size_t title_offset, title_end;
  size_t line_end;
  struct link_ref lr = {







|
|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
...
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
...
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
...
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
...
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
...
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
...
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
....
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
....
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
....
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
....
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
....
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
....
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
....
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
....
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
 ********************/

#if INTERFACE

/* mkd_autolink -- type of autolink */
enum mkd_autolink {
  MKDA_NOT_AUTOLINK,    /* used internally when it is not an autolink*/
  MKDA_NORMAL,          /* normal http/http/ftp link */
  MKDA_EXPLICIT_EMAIL,  /* e-mail link with explicit mailto: */
  MKDA_IMPLICIT_EMAIL   /* e-mail link without mailto: */
};

/* mkd_renderer -- functions for rendering parsed data */
struct mkd_renderer {
  /* document level callbacks */
  void (*prolog)(struct Blob *ob, void *opaque);
................................................................................
    i++;
  }
  if( i>=size || data[i]!='>' || nb!=1 ) return 0;
  return i+1;
}


/* tag_length -- returns the length of the given tag, or 0 if it's not valid */
static size_t tag_length(char *data, size_t size, enum mkd_autolink *autolink){
  size_t i, j;

  /* a valid tag can't be shorter than 3 chars */
  if( size<3 ) return 0;

  /* begins with a '<' optionally followed by '/', followed by letter */
................................................................................
    /* one of the forbidden chars has been found */
    *autolink = MKDA_NOT_AUTOLINK;
  }else if( (j = is_mail_autolink(data+i, size-i))!=0 ){
    *autolink = (i==8) ? MKDA_EXPLICIT_EMAIL : MKDA_IMPLICIT_EMAIL;
    return i+j;
  }

  /* looking for something looking like a tag end */
  while( i<size && data[i]!='>' ){ i++; }
  if( i>=size ) return 0;
  return i+1;
}


/* parse_inline -- parses inline markdown elements */
................................................................................
      i++;
    }
  }
  return 0;
}


/* parse_emph1 -- parsing single emphasis */
/* closed by a symbol not preceded by whitespace and not followed by symbol */
static size_t parse_emph1(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size,
  char c
................................................................................
      return r ? i+1 : 0;
    }
  }
  return 0;
}


/* parse_emph2 -- parsing single emphasis */
static size_t parse_emph2(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size,
  char c
){
................................................................................
    }
    i++;
  }
  return 0;
}


/* parse_emph3 -- parsing single emphasis */
/* finds the first closing tag, and delegates to the other emph */
static size_t parse_emph3(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size,
  char c
................................................................................
    }
  }
  return 2;
}


/* char_entity -- '&' escaped when it doesn't belong to an entity */
/* valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; */
static size_t char_entity(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t offset,
  size_t size
){
................................................................................
    if( id_end>=size ) goto char_link_cleanup;

    if( i+1==id_end ){
      /* implicit id - use the contents */
      id_data = data+1;
      id_size = txt_e-1;
    }else{
      /* explicit id - between brackets */
      id_data = data+i+1;
      id_size = id_end-(i+1);
    }

    if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
      goto char_link_cleanup;
    }
................................................................................
    return (i>=size || data[i]=='\n') ? 2 : 0;
  }

  return 0;
}


/* is_table_sep -- returns whether there is a table separator at pos */
static int is_table_sep(char *data, size_t pos){
  return data[pos]=='|' && (pos==0 || data[pos-1]!='\\');
}


/* is_tableline -- returns the number of column tables in the given line */
static int is_tableline(char *data, size_t size){
................................................................................
    }
  }else{
    return 0;
  }
}


/* prefix_code -- returns prefix length for block code */
static size_t prefix_code(char *data, size_t size){
  if( size>0 && data[0]=='\t' ) return 1;
  if( size>3 && data[0]==' ' && data[1]==' ' && data[2]==' ' && data[3]==' ' ){
    return 4;
  }
  return 0;
}
................................................................................
static void parse_block(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size);


/* parse_blockquote -- handles parsing of a blockquote fragment */
static size_t parse_blockquote(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size
){
  size_t beg, end = 0, pre, work_size = 0;
................................................................................
    rndr->make.blockquote(ob, out ? out : &fallback, rndr->make.opaque);
  }
  release_work_buffer(rndr, out);
  return end;
}


/* parse_paragraph -- handles parsing of a regular paragraph */
static size_t parse_paragraph(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size
){
  size_t i = 0, end = 0;
................................................................................
      release_work_buffer(rndr, span);
    }
  }
  return end;
}


/* parse_blockcode -- handles parsing of a block-level code fragment */
static size_t parse_blockcode(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size
){
  size_t beg, end, pre;
................................................................................
  int flags               /* table flags */
){
  size_t i = 0, col = 0;
  size_t beg, end, total = 0;
  struct Blob *cells = new_work_buffer(rndr);
  int align;

  /* skip leading blanks and separator */
  while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; }
  if( i<size && data[i]=='|' ) i++;

  /* go over all the cells */
  while( i<size && total==0 ){
    /* check optional left/center align marker */
    align = 0;
................................................................................

/* is_ref -- returns whether a line is a reference or not */
static int is_ref(
  char *data,         /* input text */
  size_t beg,         /* offset of the beginning of the line */
  size_t end,         /* offset of the end of the text */
  size_t *last,       /* last character of the link */
  struct Blob *refs   /* array of link references */
){
  size_t i = 0;
  size_t id_offset, id_end;
  size_t link_offset, link_end;
  size_t title_offset, title_end;
  size_t line_end;
  struct link_ref lr = {

Changes to src/markdown.md.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74


75
76
77
78


79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100




101
102




103
104
105
106

107
108
109





110
111


112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137




138
139

140
141

142
143
144
145
146
147
148
149
150
151
152

153
154
155

156
157

158
159
160
161






162
163
164

165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241





# Markdown formatting rules

In addition to its native Wiki formatting syntax, Fossil supports Markdown syntax as specified by
[John Gruber's original Markdown implementation](http://daringfireball.net/projects/markdown/).
For lots of examples - not repeated here - please refer to its
[syntax description](http://daringfireball.net/projects/markdown/syntax), of which the page you
are reading is an extract.

This page itself uses Markdown formatting.

## Summary

  - Block elements

      * A **paragraph** is a group of consecutive lines. Paragraphs are separated by blank lines.

      * A **Header** is a line of text underlined with equal signs or hyphens, or prefixed by a
        number of hash marks.

      * **Block quotes** are blocks of text prefixed by '>'.

      * **Ordered list** items are prefixed by a number and a period. **Unordered list** items
        are prefixed by a hyphen, asterisk or plus sign. Prefix and item text are separated by
        whitespace.

      * **Code blocks** are formed by lines of text (possibly including empty lines) prefixed by
        at least 4 spaces or a tab.

      * A **horizontal rule** is a line consisting of 3 or more asterisks, hyphens or underscores,
        with optional whitespace between them.

  - Span elements

      * 3 types of **links** exist:

        - **automatic links** are URLs or email addresses enclosed in angle brackets
          ('<' and '>'), and are displayed as such.

        - **inline links** consist of the displayed link text in square brackets ('[' and ']'),
          followed by the link target in parentheses.

        - **reference links** separate _link instance_ from _link definition_. A link instance
          consists of the displayed link text in square brackets, followed by a link definition name
          in square brackets.
          The corresponding link definition can occur anywhere on the page, and consists
          of the link definition name in square brackets followed by a colon, whitespace and the
          link target.

      * **Emphasis** can be given by wrapping text in one or two asterisks or underscores - use
        one for HTML `<em>`, and two for `<strong>` emphasis.

      * A **code span** is text wrapped in backticks ('`').

      * **Images** use a syntax much like inline or reference links, but with alt attribute text
        ('img alt=...') instead of link text, and the first pair of square
        brackets in an image instance prefixed by an exclamation mark.

  - **Inline HTML** is mostly interpreted automatically.

  - **Escaping** Markdown punctuation characters is done by prefixing them by a backslash ('\\').

## Details

### Paragraphs

To cause an explicit line break within a paragraph, use 2 or more spaces at the end of a line.


Any line containing only whitespace (space or tab characters) is considered a blank line.

### Headers

#### 'Setext' style headers (underlined)

The number of underlining equal signs or hyphens used has no impact on the resulting header.



Underlining using equal sign(s) creates a top level header (corresponding to HTML `<h1>`),
while hyphen(s) create a second level header (HTML `<h2>`). Thus, only 2 levels of headers
can be made this way.



#### 'Atx' style headers (hash prefixed)

1 to 6 hash characters can be used to indicate header levels 1 (HTML `<h1>`) to 6 (`<h6>`).

Headers may optionally be 'closed' for cosmetic reasons, by appending a whitespace and hash
characters to the header. The number of trailing hash characters has no impact on the header
level.

### Block quotes

Not every line in a paragraph needs to be prefixed by '>' in order to make it a block quote,
only the first line.

Block quoted paragraphs can be nested by using multiple '>' characters as prefix.

Within a block quote, Markdown formatting (e.g. lists, emphasis) still works as normal.

### Lists

A list item prefix need not occur first on its line; up to 3 leading spaces are allowed
(4 spaces would make a code block out of the following text).





For unordered lists, asterisks, hyphens and plus signs can be used interchangeably.





For ordered lists, arbitrary numbers can be used as part of an item prefix; the items will be
renumbered during rendering. However, future implementations may demand that the number used
for the first item in a list indicates an offset to be used for subsequent items.


For list items spanning multiple lines, subsequent lines can be indented using an arbitrary amount
of whitespace.






List items will be wrapped in HTML `<p>` tags if they are separated by blank lines.



A list item may span multiple paragraphs. At least the first line of each such paragraph must
be indented using at least 4 spaces or a tab character.

Block quotes within list items must have their '>' delimiters indented using 4 up to 7 spaces.

Code blocks within list items need to be indented _twice_, that is, using 8 spaces or 2 tab
characters.

### Code blocks

Lines within a code block are rendered verbatim using HTML `<pre>` and `<code>` tags, except that
HTML punctuation characters like '<' and '&' are automatically converted to HTML entities. Thus,
there is no need to explicitly escape HTML syntax within a code block.

A code block runs until the first non blank line with indent less than 4 spaces or 1 tab character.


Regular Markdown syntax is not processed within code blocks.

### Links

#### Automatic links

When rendering automatic links to email addresses, HTML encoding obfuscation is used to
prevent some spambots from harvesting.





#### Inline links


Links to resources on the same server can use relative paths (i.e. can start with a '/').


An optional title for the link (e.g. to have mouseover text in the browser) may be given behind
the link target but within the parentheses, in single and double quotes, and separated from the
link target by whitespace.

#### Reference links

> Each reference link consists of
>
>   - one or more _link instances_ at appropriate locations in the page text
>   - a single _link definition_ at an arbitrary location on the page

>
> During rendering, each link instance is resolved, and the corresponding definition is
> filled in. No separate link definition clauses occur in the rendered output.

>
> There are 3 fields involved in link instances and definitions:

>
>   - link text (i.e. the text that is displayed at the resulting link)
>   - link definition name (i.e. an unique ID binding link instances to link definition)
>   - link target (a target URL for the link)







Multiple link instances may reference the same link definition using its link definition
name.


Link definition names are case insensitive, and may contain letters, numbers, spaces and
punctuation.

##### Link instance

A space may be inserted between the bracket pairs for link text and link definition name.

A link instance can use an _implicit link definition name_ shortcut, in which case the link
text is used as the link definition name. The second set of brackets then remains empty, e.g.
'[Google][]' ('Google' being used as both link text and link definition name).

##### Link definition

The first bracket pair containing the link definition name may be indented using up to 3 spaces.

The link target may optionally be surrounded by angle brackets ('<' and '>').

A link target may be followed by an optional title (e.g. to have mouseover text in the browser).
This title may be enclosed in parentheses, single or double quotes.

Link definitions may be split into 2 lines, with the title on the second line, arbitrarily
indented. This may be more visually pleasing when using long link targets.

### Emphasis

The same character(s) used for starting the emphasis must be used to end it; don't mix
asterisks and underscores.

Emphasis can be used in the middle of a word. That is, there need not be whitespace on either
side of emphasis start or end punctuation characters.

### Code spans

To include a literal backtick character in a code span, use multiple backticks as opening and
closing delimiters.

Whitespace may exist immediately after the opening delimiter and before the closing delimiter
of a code span, to allow for code fragments starting or ending with a backtick.

Within a code span - like within a code block - angle brackets and ampersands are automatically encoded to make including
HTML fragments easier.

### Images

If necessary, HTML must be used to specify image dimensions. Markdown has no provision for this.

### Inline HTML

Start and end tags of
a HTML block level construct (`<div>`, `<table>` etc) must be separated from surrounding
context using blank lines, and must both occur at the start of a line.

No extra unwanted `<p>` HTML tags are added around HTML block level tags.

Markdown formatting within HTML block level tags is not processed; however, formatting within
span level tags (e.g. `<mark>`) is processed normally.

### Escaping Markdown punctuation

The following punctuation characters can be escaped using backslash:

  - \\   backslash
  - `   backtick
  - *   asterisk
  - _   underscore
  - {}  curly braces
  - []  square brackets
  - ()  parentheses
  - #   hash mark
  - +   plus sign
  - -   minus sign (hyphen)
  - .   dot
  - !   exclamation mark

To render a literal backslash, use 2 backslashes ('\\\\').






|

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|

<
>

<
<
|

<
|
<
>
>
|
<
<
<
>
>

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|

<
<
>
>
>
>

<
>
>
>
>

<
<
<
>

<
<
>
>
>
>
>

<
>
>

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|

<
|
<
<
>
>
>
>

<
>

<
>

<
<
<
<
<
<
<
|
<
<
>
|
<
<
>
|
<
>
|
<
<
<
>
>
>
>
>
>

<
<
>

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
>
>
>
>
1
2





























































3
4

5
6


7
8

9

10
11
12



13
14
15

















16
17


18
19
20
21
22

23
24
25
26
27



28
29


30
31
32
33
34
35

36
37
38



















39
40

41


42
43
44
45
46

47
48

49
50







51


52
53


54
55

56
57



58
59
60
61
62
63
64


65
66












































































67
68
69
70
71
# Markdown Overview #






























































## Paragraphs ##


> Paragraphs are divided by blank lines.



## Headings ##


>

    # Top-level Heading                         Alternative Top Level Heading
                                                =============================
>



    ## Second-level Heading                     Alternative 2nd Level Heading
                                                -----------------------------


















## Links ##



> 1.  **\[display text\]\(URL\)**
> 2.  **\[display text\]\(URL "Title"\)**
> 3.  **\<URL\>**
> 4.  **\[display text\]\[label\]**


> With link format 4 ("reference links") the label must be resolved by
> including a line of the form **\[label\]:&nbsp;URL** or
> **\[label\]:&nbsp;URL&nbsp;"Title"** somewhere else
> in the document.




## Fonts ##



> *   _\*italic\*_
> *   *\_italic\_*
> *   __\*\*bold\*\*__
> *   **\_\_bold\_\_**
> *   `` `code` ``


> Note that the \`...\` construct disables HTML markup, so one can write,
> for example, **\``<html>`\`** to yield **`<html>`**.




















## Lists ##


>


     *   bullet item
     +   bullet item
     -   bullet item
     1.  numbered item


## Block Quotes ##


> Begin each line of a paragraph with ">" to block quote that paragraph.








> >


    > This paragraph is indented
> >


    > > Double-indented paragraph


## Miscellaneous ##




> *   In-line images using **\!\[alt-text\]\(image-URL\)**
> *   Use HTML for complex formatting issues.
> *   Escape special characters (ex: "\[", "\(", "\*")
>     using backslash (ex: "\\\[", "\\\(", "\\\*").
> *   See [daringfireball.net](http://daringfireball.net/projects/markdown/syntax)
>     for additional information.



## Special Features For Fossil ##













































































> *  In hyperlinks, if the URL begins with "/" then the root of the Fossil
>    repository is prepended.  This allows for repository-relative hyperlinks.
> *  For documents that begin with top-level heading (ex: "# heading #"), the
>    heading is omitted from the body of the document and becomes the document
>    title displayed at the top of the Fossil page.

Changes to src/markdown_html.c.

353
354
355
356
357
358
359

360





361
362
363
364
365
366
367
static int html_link(
  struct Blob *ob,
  struct Blob *link,
  struct Blob *title,
  struct Blob *content,
  void *opaque
){

  BLOB_APPEND_LITERAL(ob, "<a href=\"");





  html_escape(ob, blob_buffer(link), blob_size(link));
  if( title && blob_size(title)>0 ){
    BLOB_APPEND_LITERAL(ob, "\" title=\"");
    html_escape(ob, blob_buffer(title), blob_size(title));
  }
  BLOB_APPEND_LITERAL(ob, "\">");
  BLOB_APPEND_BLOB(ob, content);







>

>
>
>
>
>







353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
static int html_link(
  struct Blob *ob,
  struct Blob *link,
  struct Blob *title,
  struct Blob *content,
  void *opaque
){
  char *zLink = blob_buffer(link);
  BLOB_APPEND_LITERAL(ob, "<a href=\"");
  if( zLink && zLink[0]=='/' ){
    /* For any hyperlink that begins with "/", make it refer to the root
    ** of the Fossil repository */
    blob_append(ob, g.zTop, -1);
  }
  html_escape(ob, blob_buffer(link), blob_size(link));
  if( title && blob_size(title)>0 ){
    BLOB_APPEND_LITERAL(ob, "\" title=\"");
    html_escape(ob, blob_buffer(title), blob_size(title));
  }
  BLOB_APPEND_LITERAL(ob, "\">");
  BLOB_APPEND_BLOB(ob, content);

Changes to src/md5.c.

420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
  DigestToBase16(zResult, blob_buffer(pCksum));
  return 0;
}


/*
** COMMAND: md5sum*
** 
** Usage: %fossil md5sum FILES....
**
** Compute an MD5 checksum of all files named on the command-line.
** If a file is named "-" then content is read from standard input.
*/
void md5sum_test(void){
  int i;







|







420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
  DigestToBase16(zResult, blob_buffer(pCksum));
  return 0;
}


/*
** COMMAND: md5sum*
**
** Usage: %fossil md5sum FILES....
**
** Compute an MD5 checksum of all files named on the command-line.
** If a file is named "-" then content is read from standard input.
*/
void md5sum_test(void){
  int i;

Changes to src/merge.c.

126
127
128
129
130
131
132




































133
134
135
136
137
138
139
...
175
176
177
178
179
180
181
182

183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

198
199
200
201
202
203
204
205

206
207
208
209
210
211
212
...
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
...
289
290
291
292
293
294
295

296
297
298
299
300
301
302
303
304

305
306
307
308
309
310
311
312
313
314
315









316
317
318
319
320
321
322

323
324
325
326
327
328
329
...
341
342
343
344
345
346
347











348
349


350
351
352
353
354
355
356
...
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376

377
378
379
380

381
382








383
384
385
386
387
388






389
390
391
392
393
394

395
396
397
398
399
400
401
402
403
404






405
406
407
408
409
410
411

412
413
414

415
416
417
418
419
420


421
422
423
424
425
426
427
428
429
430
431
432
433

434
435
436
437
438
439
440

441
442
443
444
445
446
447
448




449
450
451
452
453
454
455
456
457
458
459
460
461
462
463

464
465
466
467
468
469
470
471
472
473
474
475
476
477
...
479
480
481
482
483
484
485

486
487
488
489





















490
491
492
493
494
495
496
...
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
...
659
660
661
662
663
664
665










666
667
668
669
670
671
672
673
674
675
676
677
678

679
680
681
682


683
684

685
686



687

688











689
690
691
692
693

694
695
696
697
698
699
700










































701
702
703
704
705
706
707
        fForkSeen = fossil_find_nearest_fork(rid, db_open_local(0))!=0;
      }
    }
  }
  db_finalize(&q);
  return fForkSeen;
}





































/*
** COMMAND: merge
**
** Usage: %fossil merge ?OPTIONS? ?VERSION?
**
** The argument VERSION is a version that should be merged into the
................................................................................
**   -n|--dry-run            If given, display instead of run actions
**
**   -v|--verbose            Show additional details of the merge
*/
void merge_cmd(void){
  int vid;              /* Current version "V" */
  int mid;              /* Version we are merging from "M" */
  int pid;              /* The pivot version - most recent common ancestor P */

  int verboseFlag;      /* True if the -v|--verbose option is present */
  int integrateFlag;    /* True if the --integrate option is present */
  int pickFlag;         /* True if the --cherrypick option is present */
  int backoutFlag;      /* True if the --backout option is present */
  int dryRunFlag;       /* True if the --dry-run or -n option is present */
  int forceFlag;        /* True if the --force or -f option is present */
  int forceMissingFlag; /* True if the --force-missing option is present */
  const char *zBinGlob; /* The value of --binary */
  const char *zPivot;   /* The value of --baseline */
  int debugFlag;        /* True if --debug is present */
  int nChng;            /* Number of file name changes */
  int *aChng;           /* An array of file name changes */
  int i;                /* Loop counter */
  int nConflict = 0;    /* Number of conflicts seen */
  int nOverwrite = 0;   /* Number of unmanaged files overwritten */

  Stmt q;


  /* Notation:
  **
  **      V     The current checkout
  **      M     The version being merged in
  **      P     The "pivot" - the most recent common ancestor of V and M.

  */

  undo_capture_command_line();
  verboseFlag = find_option("verbose","v",0)!=0;
  forceMissingFlag = find_option("force-missing",0,0)!=0;
  if( !verboseFlag ){
    verboseFlag = find_option("detail",0,0)!=0; /* deprecated */
................................................................................
  if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
  vid = db_lget_int("checkout", 0);
  if( vid==0 ){
    fossil_fatal("nothing is checked out");
  }
  if( !dryRunFlag ){
    if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag,
                      db_get_int("autosync-tries", 1)) ){
      fossil_fatal("Cannot proceed with merge");
    }
  }

  /* Find mid, the artifactID of the version to be merged into the current
  ** check-out */
  if( g.argc==3 ){
    /* Mid is specified as an argument on the command-line */
................................................................................
    pid = name_to_typed_rid(zPivot, "ci");
    if( pid==0 || !is_a_version(pid) ){
      fossil_fatal("not a version: %s", zPivot);
    }
    if( pickFlag ){
      fossil_fatal("incompatible options: --cherrypick & --baseline");
    }

  }else if( pickFlag || backoutFlag ){
    if( integrateFlag ){
      fossil_fatal("incompatible options: --integrate & --cherrypick or --backout");
    }
    pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
    if( pid<=0 ){
      fossil_fatal("cannot find an ancestor for %s", g.argv[2]);
    }
  }else{

    pivot_set_primary(mid);
    pivot_set_secondary(vid);
    db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
    while( db_step(&q)==SQLITE_ROW ){
      pivot_set_secondary(db_column_int(&q,0));
    }
    db_finalize(&q);
    pid = pivot_find();
    if( pid<=0 ){
      fossil_fatal("cannot find a common ancestor between the current "
                   "checkout and %s", g.argv[2]);









    }
  }
  if( backoutFlag ){
    int t = pid;
    pid = mid;
    mid = t;
  }

  if( !is_a_version(pid) ){
    fossil_fatal("not a version: record #%d", pid);
  }
  if( !forceFlag && mid==pid ){
    fossil_print("Merge skipped because it is a no-op. "
                 " Use --force to override.\n");
    return;
................................................................................
  if( !dryRunFlag ) undo_begin();
  if( load_vfile_from_rid(mid) && !forceMissingFlag ){
    fossil_fatal("missing content, unable to merge");
  }
  if( load_vfile_from_rid(pid) && !forceMissingFlag ){
    fossil_fatal("missing content, unable to merge");
  }











  if( debugFlag ){
    char *z;


    z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pid);
    fossil_print("P=%d %z\n", pid, z);
    z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
    fossil_print("M=%d %z\n", mid, z);
    z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
    fossil_print("V=%d %z\n", vid, z);
  }
................................................................................
  ** The vfile.pathname field is used to match files against each other.  The
  ** FV table contains one row for each each unique filename in
  ** in the current checkout, the pivot, and the version being merged.
  */
  db_multi_exec(
    "DROP TABLE IF EXISTS fv;"
    "CREATE TEMP TABLE fv("
    "  fn TEXT PRIMARY KEY %s,"   /* The filename */
    "  idv INTEGER,"              /* VFILE entry for current version */
    "  idp INTEGER,"              /* VFILE entry for the pivot */
    "  idm INTEGER,"              /* VFILE entry for version merging in */
    "  chnged BOOLEAN,"           /* True if current version has been edited */
    "  ridv INTEGER,"             /* Record ID for current version */
    "  ridp INTEGER,"             /* Record ID for pivot */
    "  ridm INTEGER,"             /* Record ID for merge */
    "  isexe BOOLEAN,"            /* Execute permission enabled */
    "  fnp TEXT %s,"              /* The filename in the pivot */
    "  fnm TEXT %s,"              /* the filename in the merged version */

    "  islinkv BOOLEAN,"          /* True if current version is a symlink */
    "  islinkm BOOLEAN"           /* True if merged version in is a symlink */
    ");",
    filename_collation(), filename_collation(), filename_collation()

  );









  /* Add files found in V
  */
  db_multi_exec(
    "INSERT OR IGNORE"
    " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
    " SELECT pathname, pathname, pathname, id, 0, 0, rid, 0, 0, isexe, chnged "






    " FROM vfile WHERE vid=%d",
    vid
  );

  /*
  ** Compute name changes from P->V

  */
  find_filename_changes(pid, vid, 0, &nChng, &aChng, debugFlag ? "P->V" : 0);
  if( nChng ){
    for(i=0; i<nChng; i++){
      char *z;
      z = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2]);
      db_multi_exec(
        "UPDATE fv SET fnp=%Q, fnm=%Q"
        " WHERE fn=(SELECT name FROM filename WHERE fnid=%d)",
        z, z, aChng[i*2+1]






      );
      free(z);
    }
    fossil_free(aChng);
    db_multi_exec("UPDATE fv SET fnm=fnp WHERE fnp!=fn");
  }


  /* Add files found in P but not in V
  */
  db_multi_exec(

    "INSERT OR IGNORE"
    " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
    " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, isexe, 0 "
    "   FROM vfile"
    "  WHERE vid=%d AND pathname %s NOT IN (SELECT fnp FROM fv)",
    pid, filename_collation()


  );

  /*
  ** Compute name changes from P->M
  */
  find_filename_changes(pid, mid, 0, &nChng, &aChng, debugFlag ? "P->M" : 0);
  if( nChng ){
    if( nChng>4 ) db_multi_exec("CREATE INDEX fv_fnp ON fv(fnp)");
    for(i=0; i<nChng; i++){
      db_multi_exec(
        "UPDATE fv SET fnm=(SELECT name FROM filename WHERE fnid=%d)"
        " WHERE fnp=(SELECT name FROM filename WHERE fnid=%d)",
        aChng[i*2+1], aChng[i*2]

      );
    }
    fossil_free(aChng);
  }

  /* Add files found in M but not in P or V.
  */

  db_multi_exec(
    "INSERT OR IGNORE"
    " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
    " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, isexe, 0 "
    "   FROM vfile"
    "  WHERE vid=%d"
    "    AND pathname %s NOT IN (SELECT fnp FROM fv UNION SELECT fnm FROM fv)",
    mid, filename_collation()




  );

  /*
  ** Compute the file version ids for P and M.
  */
  db_multi_exec(
    "UPDATE fv SET"
    " idp=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnp=pathname),0),"
    " ridp=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnp=pathname),0),"
    " idm=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnm=pathname),0),"
    " ridm=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnm=pathname),0),"
    " islinkv=coalesce((SELECT islink FROM vfile"
                    " WHERE vid=%d AND fnm=pathname),0),"
    " islinkm=coalesce((SELECT islink FROM vfile"
                    " WHERE vid=%d AND fnm=pathname),0)",

    pid, pid, mid, mid, vid, mid
  );

  if( debugFlag ){
    db_prepare(&q,
       "SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, "
       "       isexe, islinkv, islinkm FROM fv"
    );
    while( db_step(&q)==SQLITE_ROW ){
       fossil_print("%3d: ridv=%-4d ridp=%-4d ridm=%-4d chnged=%d isexe=%d "
                    " islinkv=%d islinkm=%d\n",
          db_column_int(&q, 0),
          db_column_int(&q, 5),
          db_column_int(&q, 6),
................................................................................
          db_column_int(&q, 4),
          db_column_int(&q, 8),
          db_column_int(&q, 9),
          db_column_int(&q, 10));
       fossil_print("     fn  = [%s]\n", db_column_text(&q, 1));
       fossil_print("     fnp = [%s]\n", db_column_text(&q, 2));
       fossil_print("     fnm = [%s]\n", db_column_text(&q, 3));

    }
    db_finalize(&q);
  }






















  /*
  ** Find files in M and V but not in P and report conflicts.
  ** The file in M will be ignored.  It will be treated as if it
  ** does not exist.
  */
  db_prepare(&q,
    "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0"
................................................................................
  while( db_step(&q)==SQLITE_ROW ){
    int idm = db_column_int(&q, 0);
    char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm);
    fossil_warning("WARNING: no common ancestor for %s", zName);
    free(zName);
    db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm);
  }
  db_finalize(&q);

  /*
  ** Add to V files that are not in V or P but are in M
  */
  db_prepare(&q,
    "SELECT idm, rowid, fnm FROM fv AS x"
    " WHERE idp=0 AND idv=0 AND idm>0"
  );
  while( db_step(&q)==SQLITE_ROW ){
    int idm = db_column_int(&q, 0);
    int rowid = db_column_int(&q, 1);
    int idv;
    const char *zName;
    char *zFullName;
    db_multi_exec(
      "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,islink,pathname)"
      "  SELECT %d,%d,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d",
      vid, integrateFlag?5:3, idm
    );
    idv = db_last_insert_rowid();
    db_multi_exec("UPDATE fv SET idv=%d WHERE rowid=%d", idv, rowid);
    zName = db_column_text(&q, 2);
    zFullName = mprintf("%s%s", g.zLocalRoot, zName);
    if( file_wd_isfile_or_link(zFullName) ){
      fossil_print("ADDED %s (overwrites an unmanaged file)\n", zName);
      nOverwrite++;
    }else{
      fossil_print("ADDED %s\n", zName);
    }
    fossil_free(zFullName);
    if( !dryRunFlag ){
      undo_save(zName);
      vfile_to_disk(0, idm, 0, 0);
    }
  }
  db_finalize(&q);

  /*
  ** Find files that have changed from P->M but not P->V.
  ** Copy the M content over into V.
  */
  db_prepare(&q,
................................................................................
      char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
      file_delete(zFullPath);
      free(zFullPath);
    }
  }
  db_finalize(&q);











  /*
  ** Rename files that have taken a rename on P->M but which keep the same
  ** name on P->V.  If a file is renamed on P->V only or on both P->V and
  ** P->M then we retain the V name of the file.
  */
  db_prepare(&q,
    "SELECT idv, fnp, fnm FROM fv"
    " WHERE idv>0 AND idp>0 AND idm>0 AND fnp=fn AND fnm!=fnp"
  );
  while( db_step(&q)==SQLITE_ROW ){
    int idv = db_column_int(&q, 0);
    const char *zOldName = db_column_text(&q, 1);
    const char *zNewName = db_column_text(&q, 2);

    fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
    if( !dryRunFlag ) undo_save(zOldName);
    if( !dryRunFlag ) undo_save(zNewName);
    db_multi_exec(


      "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
      " WHERE id=%d AND vid=%d", zNewName, idv, vid

    );
    if( !dryRunFlag ){



      char *zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName);

      char *zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);











      if( file_wd_islink(zFullOldPath) ){
        symlink_copy(zFullOldPath, zFullNewPath);
      }else{
        file_copy(zFullOldPath, zFullNewPath);
      }

      file_delete(zFullOldPath);
      free(zFullNewPath);
      free(zFullOldPath);
    }
  }
  db_finalize(&q);












































  /* Report on conflicts
  */
  if( nConflict ){
    fossil_warning("WARNING: %d merge conflicts", nConflict);
  }
  if( nOverwrite ){







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
>










<
<
<


>








>







 







|
|







 







>
|








>
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>







>







 







>
>
>
>
>
>
>
>
>
>
>


>
>







 







|
|
|
|

|
|
|

|
|
>



|
>


>
>
>
>
>
>
>
>
|


<
<
<
>
>
>
>
>
>
|
|



<
>

<
<
<
<
<
|
<
<
<
>
>
>
>
>
>
|
<
|
<
<
<
<
>
|


>
|
<
<
<
<
<
>
>



|

|
<
<
<
|
<
<
<
>
|
<
<
<
<
<
<
>
|
<
<
<
<
<
<
<
>
>
>
>
|
|
<
<
<


<
<


|

<
|
>
|





|







 







>




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







>
>
>
>
>
>
>
>
>
>






|






>




>
>

|
>


>
>
>
|
>
|
>
>
>
>
>
>
>
>
>
>
>





>







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
...
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
...
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
...
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
...
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456



457
458
459
460
461
462
463
464
465
466
467

468
469





470



471
472
473
474
475
476
477

478




479
480
481
482
483
484





485
486
487
488
489
490
491
492



493



494
495






496
497







498
499
500
501
502
503



504
505


506
507
508
509

510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
...
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
...
568
569
570
571
572
573
574




































575
576
577
578
579
580
581
...
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
        fForkSeen = fossil_find_nearest_fork(rid, db_open_local(0))!=0;
      }
    }
  }
  db_finalize(&q);
  return fForkSeen;
}

/*
** Add an entry to the FV table for all files renamed between
** version N and the version specified by vid.
*/
static void add_renames(
  const char *zFnCol, /* The FV column for the filename in vid */
  int vid,            /* The desired version's RID */
  int nid,            /* Version N's RID */
  int revOk,          /* Ok to move backwards (child->parent) if true */
  const char *zDebug  /* Generate trace output if not NULL */
){
  int nChng;  /* Number of file name changes */
  int *aChng; /* An array of file name changes */
  int i;      /* Loop counter */
  find_filename_changes(nid, vid, revOk, &nChng, &aChng, zDebug);
  if( nChng==0 ) return;
  for(i=0; i<nChng; i++){
    char *zN, *zV;
    zN = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2]);
    zV = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2+1]);
    db_multi_exec(
      "INSERT OR IGNORE INTO fv(%s,fnn) VALUES(%Q,%Q)",
      zFnCol /*safe-for-%s*/, zV, zN
    );
    if( db_changes()==0 ){
      db_multi_exec(
        "UPDATE fv SET %s=%Q WHERE fnn=%Q",
        zFnCol /*safe-for-%s*/, zV, zN
      );
    }
    free(zN);
    free(zV);
  }
  free(aChng);
}

/*
** COMMAND: merge
**
** Usage: %fossil merge ?OPTIONS? ?VERSION?
**
** The argument VERSION is a version that should be merged into the
................................................................................
**   -n|--dry-run            If given, display instead of run actions
**
**   -v|--verbose            Show additional details of the merge
*/
void merge_cmd(void){
  int vid;              /* Current version "V" */
  int mid;              /* Version we are merging from "M" */
  int pid = 0;          /* The pivot version - most recent common ancestor P */
  int nid = 0;          /* The name pivot version "N" */
  int verboseFlag;      /* True if the -v|--verbose option is present */
  int integrateFlag;    /* True if the --integrate option is present */
  int pickFlag;         /* True if the --cherrypick option is present */
  int backoutFlag;      /* True if the --backout option is present */
  int dryRunFlag;       /* True if the --dry-run or -n option is present */
  int forceFlag;        /* True if the --force or -f option is present */
  int forceMissingFlag; /* True if the --force-missing option is present */
  const char *zBinGlob; /* The value of --binary */
  const char *zPivot;   /* The value of --baseline */
  int debugFlag;        /* True if --debug is present */



  int nConflict = 0;    /* Number of conflicts seen */
  int nOverwrite = 0;   /* Number of unmanaged files overwritten */
  char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */
  Stmt q;


  /* Notation:
  **
  **      V     The current checkout
  **      M     The version being merged in
  **      P     The "pivot" - the most recent common ancestor of V and M.
  **      N     The "name pivot" - for detecting renames
  */

  undo_capture_command_line();
  verboseFlag = find_option("verbose","v",0)!=0;
  forceMissingFlag = find_option("force-missing",0,0)!=0;
  if( !verboseFlag ){
    verboseFlag = find_option("detail",0,0)!=0; /* deprecated */
................................................................................
  if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
  vid = db_lget_int("checkout", 0);
  if( vid==0 ){
    fossil_fatal("nothing is checked out");
  }
  if( !dryRunFlag ){
    if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag,
                      db_get_int("autosync-tries", 1), 1) ){
      fossil_fatal("merge abandoned due to sync failure");
    }
  }

  /* Find mid, the artifactID of the version to be merged into the current
  ** check-out */
  if( g.argc==3 ){
    /* Mid is specified as an argument on the command-line */
................................................................................
    pid = name_to_typed_rid(zPivot, "ci");
    if( pid==0 || !is_a_version(pid) ){
      fossil_fatal("not a version: %s", zPivot);
    }
    if( pickFlag ){
      fossil_fatal("incompatible options: --cherrypick & --baseline");
    }
  }
  if( pickFlag || backoutFlag ){
    if( integrateFlag ){
      fossil_fatal("incompatible options: --integrate & --cherrypick or --backout");
    }
    pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
    if( pid<=0 ){
      fossil_fatal("cannot find an ancestor for %s", g.argv[2]);
    }
  }else{
    if( !zPivot ){
      pivot_set_primary(mid);
      pivot_set_secondary(vid);
      db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
      while( db_step(&q)==SQLITE_ROW ){
        pivot_set_secondary(db_column_int(&q,0));
      }
      db_finalize(&q);
      pid = pivot_find(0);
      if( pid<=0 ){
        fossil_fatal("cannot find a common ancestor between the current "
                     "checkout and %s", g.argv[2]);
      }
    }
    pivot_set_primary(mid);
    pivot_set_secondary(vid);
    nid = pivot_find(1);
    if( nid!=pid ){
      pivot_set_primary(nid);
      pivot_set_secondary(pid);
      nid = pivot_find(1);
    }
  }
  if( backoutFlag ){
    int t = pid;
    pid = mid;
    mid = t;
  }
  if( nid==0 ) nid = pid;
  if( !is_a_version(pid) ){
    fossil_fatal("not a version: record #%d", pid);
  }
  if( !forceFlag && mid==pid ){
    fossil_print("Merge skipped because it is a no-op. "
                 " Use --force to override.\n");
    return;
................................................................................
  if( !dryRunFlag ) undo_begin();
  if( load_vfile_from_rid(mid) && !forceMissingFlag ){
    fossil_fatal("missing content, unable to merge");
  }
  if( load_vfile_from_rid(pid) && !forceMissingFlag ){
    fossil_fatal("missing content, unable to merge");
  }
  if( zPivot ){
    vAncestor = db_exists(
      "WITH RECURSIVE ancestor(id) AS ("
      "  VALUES(%d)"
      "  UNION"
      "  SELECT pid FROM plink, ancestor"
      "   WHERE cid=ancestor.id AND pid!=%d AND cid!=%d)"
      "SELECT 1 FROM ancestor WHERE id=%d LIMIT 1",
      vid, nid, pid, pid
    ) ? 'p' : 'n';
  }
  if( debugFlag ){
    char *z;
    z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nid);
    fossil_print("N=%d %z\n", nid, z);
    z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pid);
    fossil_print("P=%d %z\n", pid, z);
    z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
    fossil_print("M=%d %z\n", mid, z);
    z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
    fossil_print("V=%d %z\n", vid, z);
  }
................................................................................
  ** The vfile.pathname field is used to match files against each other.  The
  ** FV table contains one row for each each unique filename in
  ** in the current checkout, the pivot, and the version being merged.
  */
  db_multi_exec(
    "DROP TABLE IF EXISTS fv;"
    "CREATE TEMP TABLE fv("
    "  fn TEXT UNIQUE %s,"        /* The filename */
    "  idv INTEGER DEFAULT 0,"    /* VFILE entry for current version */
    "  idp INTEGER DEFAULT 0,"    /* VFILE entry for the pivot */
    "  idm INTEGER DEFAULT 0,"    /* VFILE entry for version merging in */
    "  chnged BOOLEAN,"           /* True if current version has been edited */
    "  ridv INTEGER DEFAULT 0,"   /* Record ID for current version */
    "  ridp INTEGER DEFAULT 0,"   /* Record ID for pivot */
    "  ridm INTEGER DEFAULT 0,"   /* Record ID for merge */
    "  isexe BOOLEAN,"            /* Execute permission enabled */
    "  fnp TEXT UNIQUE %s,"       /* The filename in the pivot */
    "  fnm TEXT UNIQUE %s,"       /* The filename in the merged version */
    "  fnn TEXT UNIQUE %s,"       /* The filename in the name pivot */
    "  islinkv BOOLEAN,"          /* True if current version is a symlink */
    "  islinkm BOOLEAN"           /* True if merged version in is a symlink */
    ");",
    filename_collation(), filename_collation(), filename_collation(),
    filename_collation()
  );

  /*
  ** Compute name changes from N to V, P, and M
  */
  add_renames("fn", vid, nid, 0, debugFlag ? "N->V" : 0);
  add_renames("fnp", pid, nid, 0, debugFlag ? "N->P" : 0);
  add_renames("fnm", mid, nid, backoutFlag, debugFlag ? "N->M" : 0);

  /*
  ** Add files found in V
  */
  db_multi_exec(



    "UPDATE OR IGNORE fv SET fn=coalesce(fn%c,fnn) WHERE fn IS NULL;"
    "REPLACE INTO fv(fn,fnp,fnm,fnn,idv,ridv,islinkv,isexe,chnged)"
    " SELECT pathname, fnp, fnm, fnn, id, rid, islink, vf.isexe, vf.chnged"
    "   FROM vfile vf"
    "   LEFT JOIN fv ON fn=coalesce(origname,pathname)"
    "    AND rid>0 AND vf.chnged NOT IN (3,5)"
    "  WHERE vid=%d;",
    vAncestor, vid
  );

  /*

  ** Add files found in P
  */





  db_multi_exec(



    "UPDATE OR IGNORE fv SET fnp=coalesce(fnn,"
    "   (SELECT coalesce(origname,pathname) FROM vfile WHERE id=idv))"
    " WHERE fnp IS NULL;"
    "INSERT OR IGNORE INTO fv(fnp)"
    " SELECT coalesce(origname,pathname) FROM vfile WHERE vid=%d;",
    pid
  );






  /*
  ** Add files found in M
  */
  db_multi_exec(
    "UPDATE OR IGNORE fv SET fnm=fnp WHERE fnm IS NULL;"
    "INSERT OR IGNORE INTO fv(fnm)"





    " SELECT pathname FROM vfile WHERE vid=%d;",
    mid
  );

  /*
  ** Compute the file version ids for P and M
  */
  if( pid==vid ){



    db_multi_exec(



      "UPDATE fv SET idp=idv, ridp=ridv WHERE ridv>0 AND chnged NOT IN (3,5)"
    );






  }else{
    db_multi_exec(







      "UPDATE fv SET"
      " idp=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnp=pathname),0),"
      " ridp=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnp=pathname),0)",
      pid, pid
    );
  }



  db_multi_exec(
    "UPDATE fv SET"


    " idm=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnm=pathname),0),"
    " ridm=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnm=pathname),0),"
    " islinkm=coalesce((SELECT islink FROM vfile"
                    " WHERE vid=%d AND fnm=pathname),0),"

    " isexe=coalesce((SELECT isexe FROM vfile WHERE vid=%d AND fnm=pathname),"
    "   isexe)",
    mid, mid, mid, mid
  );

  if( debugFlag ){
    db_prepare(&q,
       "SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, "
       "       isexe, islinkv, islinkm, fnn FROM fv"
    );
    while( db_step(&q)==SQLITE_ROW ){
       fossil_print("%3d: ridv=%-4d ridp=%-4d ridm=%-4d chnged=%d isexe=%d "
                    " islinkv=%d islinkm=%d\n",
          db_column_int(&q, 0),
          db_column_int(&q, 5),
          db_column_int(&q, 6),
................................................................................
          db_column_int(&q, 4),
          db_column_int(&q, 8),
          db_column_int(&q, 9),
          db_column_int(&q, 10));
       fossil_print("     fn  = [%s]\n", db_column_text(&q, 1));
       fossil_print("     fnp = [%s]\n", db_column_text(&q, 2));
       fossil_print("     fnm = [%s]\n", db_column_text(&q, 3));
       fossil_print("     fnn = [%s]\n", db_column_text(&q, 11));
    }
    db_finalize(&q);
  }

  /*
  ** Update the execute bit on files where it's changed from P->M but not P->V
  */
  db_prepare(&q,
    "SELECT idv, fn, fv.isexe FROM fv, vfile p, vfile v"
    " WHERE p.id=idp AND v.id=idv AND fv.isexe!=p.isexe AND v.isexe=p.isexe"
  );
  while( db_step(&q)==SQLITE_ROW ){
    int idv = db_column_int(&q, 0);
    const char *zName = db_column_text(&q, 1);
    int isExe = db_column_int(&q, 2);
    fossil_print("%s %s\n", isExe ? "EXECUTABLE" : "UNEXEC", zName);
    if( !dryRunFlag ){
      char *zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
      file_wd_setexe(zFullPath, isExe);
      free(zFullPath);
      db_multi_exec("UPDATE vfile SET isexe=%d WHERE id=%d", isExe, idv);
    }
  }
  db_finalize(&q);

  /*
  ** Find files in M and V but not in P and report conflicts.
  ** The file in M will be ignored.  It will be treated as if it
  ** does not exist.
  */
  db_prepare(&q,
    "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0"
................................................................................
  while( db_step(&q)==SQLITE_ROW ){
    int idm = db_column_int(&q, 0);
    char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm);
    fossil_warning("WARNING: no common ancestor for %s", zName);
    free(zName);
    db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm);
  }




































  db_finalize(&q);

  /*
  ** Find files that have changed from P->M but not P->V.
  ** Copy the M content over into V.
  */
  db_prepare(&q,
................................................................................
      char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
      file_delete(zFullPath);
      free(zFullPath);
    }
  }
  db_finalize(&q);

  /* For certain sets of renames (e.g. A -> B and B -> A), a file that is
  ** being renamed must first be moved to a temporary location to avoid
  ** being overwritten by another rename operation. A row is added to the
  ** TMPRN table for each of these temporary renames.
  */
  db_multi_exec(
    "DROP TABLE IF EXISTS tmprn;"
    "CREATE TEMP TABLE tmprn(fn UNIQUE, tmpfn);"
  );

  /*
  ** Rename files that have taken a rename on P->M but which keep the same
  ** name on P->V.  If a file is renamed on P->V only or on both P->V and
  ** P->M then we retain the V name of the file.
  */
  db_prepare(&q,
    "SELECT idv, fnp, fnm, isexe FROM fv"
    " WHERE idv>0 AND idp>0 AND idm>0 AND fnp=fn AND fnm!=fnp"
  );
  while( db_step(&q)==SQLITE_ROW ){
    int idv = db_column_int(&q, 0);
    const char *zOldName = db_column_text(&q, 1);
    const char *zNewName = db_column_text(&q, 2);
    int isExe = db_column_int(&q, 3);
    fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
    if( !dryRunFlag ) undo_save(zOldName);
    if( !dryRunFlag ) undo_save(zNewName);
    db_multi_exec(
      "UPDATE vfile SET pathname=NULL, origname=pathname"
      " WHERE vid=%d AND pathname=%Q;"
      "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
      " WHERE id=%d;",
      vid, zNewName, zNewName, idv
    );
    if( !dryRunFlag ){
      char *zFullOldPath, *zFullNewPath;
      zFullOldPath = db_text(0,"SELECT tmpfn FROM tmprn WHERE fn=%Q", zOldName);
      if( !zFullOldPath ){
        zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName);
      }
      zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
      if( file_wd_size(zFullNewPath)>=0 ){
        char zTmpPath[300];
        file_tempname(sizeof(zTmpPath), zTmpPath);
        db_multi_exec("INSERT INTO tmprn(fn,tmpfn) VALUES(%Q,%Q)",
                      zNewName, zTmpPath);
        if( file_wd_islink(zFullNewPath) ){
          symlink_copy(zFullNewPath, zTmpPath);
        }else{
          file_copy(zFullNewPath, zTmpPath);
        }
      }
      if( file_wd_islink(zFullOldPath) ){
        symlink_copy(zFullOldPath, zFullNewPath);
      }else{
        file_copy(zFullOldPath, zFullNewPath);
      }
      file_wd_setexe(zFullNewPath, isExe);
      file_delete(zFullOldPath);
      free(zFullNewPath);
      free(zFullOldPath);
    }
  }
  db_finalize(&q);

  /* A file that has been deleted and replaced by a renamed file will have a
  ** NULL pathname. Change it to something that makes the output of "status"
  ** and similar commands make sense for such files and that will (most likely)
  ** not be an actual existing pathname.
  */
  db_multi_exec(
    "UPDATE vfile SET pathname=origname || ' (overwritten by rename)'"
    " WHERE pathname IS NULL"
  );

  /*
  ** Add to V files that are not in V or P but are in M
  */
  db_prepare(&q,
    "SELECT idm, fnm FROM fv"
    " WHERE idp=0 AND idv=0 AND idm>0"
  );
  while( db_step(&q)==SQLITE_ROW ){
    int idm = db_column_int(&q, 0);
    const char *zName;
    char *zFullName;
    db_multi_exec(
      "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,islink,pathname)"
      "  SELECT %d,%d,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d",
      vid, integrateFlag?5:3, idm
    );
    zName = db_column_text(&q, 1);
    zFullName = mprintf("%s%s", g.zLocalRoot, zName);
    if( file_wd_isfile_or_link(zFullName)
        && !db_exists("SELECT 1 FROM fv WHERE fn=%Q", zName) ){
      fossil_print("ADDED %s (overwrites an unmanaged file)\n", zName);
      nOverwrite++;
    }else{
      fossil_print("ADDED %s\n", zName);
    }
    fossil_free(zFullName);
    if( !dryRunFlag ){
      undo_save(zName);
      vfile_to_disk(0, idm, 0, 0);
    }
  }
  db_finalize(&q);

  /* Report on conflicts
  */
  if( nConflict ){
    fossil_warning("WARNING: %d merge conflicts", nConflict);
  }
  if( nOverwrite ){

Changes to src/mkindex.c.

20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53








54
55
56
57
58
59











60
61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
..
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109





110
111
112
113
114
115
116
117
118
119
120
121


















122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141




142

143





































144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213


214
215
216
217
218
219
220
221
222
223
...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247

248
249











250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303

304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
...
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
** routine collects information about these entry points and then
** generates (on standard output) C code used by Fossil to dispatch
** to those entry points.
**
** The source code is scanned for comment lines of the form:
**
**       WEBPAGE:  /abc/xyz

**
** This comment should be followed by a function definition of the
** form:
**
**       void function_name(void){
**
** This routine creates C source code for a constant table that maps
** webpage name into pointers to the function.
**
** We also scan for comments lines of this form:
**
**       COMMAND:  cmdname
**
** These entries build a constant table used to map command names into
** functions.  If cmdname ends with "*" then the command is a second-tier
** command that is not displayed by the "fossil help" command.  The
** final "*" is not considered to be part of the command name.
**
** Comment text following COMMAND: through the end of the comment is
** understood to be help text for the command specified.  This help
** text is accumulated and a table containing the text for each command
** is generated.  That table is used implement the "fossil help" command
** and the "/help" HTTP method.
**
** Multiple occurrences of WEBPAGE: or COMMAND: (but not both) can appear
** before each function name.  In this way, webpages and commands can
** have aliases.








*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>












/*
** Each entry looks like this:
*/
typedef struct Entry {
  int eType;
  char *zIf;
  char *zFunc;
  char *zPath;
  char *zHelp;

} Entry;

/*
** Maximum number of entries
*/
#define N_ENTRY 500

/*
** Maximum size of a help message
*/
#define MX_HELP 25000

/*
** Table of entries
*/
Entry aEntry[N_ENTRY];

/*
................................................................................
*/
char zHelp[MX_HELP];
int nHelp;

/*
** Most recently encountered #if
*/
char zIf[200];

/*
** How many entries are used
*/
int nUsed;
int nFixed;

/*
** Current filename and line number
*/
char *zFile;
int nLine;






/*
** Duplicate N characters of a string.
*/
char *string_dup(const char *zSrc, int n){
  char *z;
  if( n<0 ) n = strlen(zSrc);
  z = malloc( n+1 );
  if( z==0 ){ fprintf(stderr,"Out of memory!\n"); exit(1); }
  strncpy(z, zSrc, n);
  z[n] = 0;
  return z;
}



















/*
** Scan a line looking for comments containing zLabel.  Make
** new entries if found.
*/
void scan_for_label(const char *zLabel, char *zLine, int eType){
  int i, j;
  int len = strlen(zLabel);
  if( nUsed>=N_ENTRY ) return;
  for(i=0; isspace(zLine[i]) || zLine[i]=='*'; i++){}
  if( zLine[i]!=zLabel[0] ) return;
  if( strncmp(&zLine[i],zLabel, len)==0 ){
    i += len;
  }else{
    return;
  }
  while( isspace(zLine[i]) ){ i++; }
  if( zLine[i]=='/' ) i++;
  for(j=0; zLine[i+j] && !isspace(zLine[i+j]); j++){}
  aEntry[nUsed].eType = eType;




  aEntry[nUsed].zPath = string_dup(&zLine[i], j);

  aEntry[nUsed].zFunc = 0;





































  nUsed++;
}

/*
** Check to see if the current line is an #if and if it is, add it to
** the zIf[] string.  If the current line is an #endif or #else or #elif
** then cancel the current zIf[] string.
*/
void scan_for_if(const char *zLine){
  int i;
  int len;
  if( zLine[0]!='#' ) return;
  for(i=1; isspace(zLine[i]); i++){}
  if( zLine[i]==0 ) return;
  len = strlen(&zLine[i]);
  if( memcmp(&zLine[i],"if",2)==0 ){
    zIf[0] = '#';
    memcpy(&zIf[1], &zLine[i], len+1);
  }else if( zLine[i]=='e' ){
    zIf[0] = 0;
  }
}

................................................................................
** Scan a line for a function that implements a web page or command.
*/
void scan_for_func(char *zLine){
  int i,j,k;
  char *z;
  if( nUsed<=nFixed ) return;
  if( strncmp(zLine, "**", 2)==0
   && isspace(zLine[2])
   && strlen(zLine)<sizeof(zHelp)-nHelp-1
   && nUsed>nFixed
   && memcmp(zLine,"** COMMAND:",11)!=0
   && memcmp(zLine,"** WEBPAGE:",11)!=0
  ){
    if( zLine[2]=='\n' ){
      zHelp[nHelp++] = '\n';
    }else{
      if( strncmp(&zLine[3], "Usage: ", 6)==0 ) nHelp = 0;
      strcpy(&zHelp[nHelp], &zLine[3]);
      nHelp += strlen(&zHelp[nHelp]);
    }
    return;
  }
  for(i=0; isspace(zLine[i]); i++){}
  if( zLine[i]==0 ) return;
  if( strncmp(&zLine[i],"void",4)!=0 ){
    if( zLine[i]!='*' ) goto page_skip;
    return;
  }
  i += 4;
  if( !isspace(zLine[i]) ) goto page_skip;
  while( isspace(zLine[i]) ){ i++; }
  for(j=0; isalnum(zLine[i+j]) || zLine[i+j]=='_'; j++){}
  if( j==0 ) goto page_skip;
  for(k=nHelp-1; k>=0 && isspace(zHelp[k]); k--){}
  nHelp = k+1;
  zHelp[nHelp] = 0;
  for(k=0; k<nHelp && isspace(zHelp[k]); k++){}
  if( k<nHelp ){
    z = string_dup(&zHelp[k], nHelp-k);
  }else{
    z = 0;
  }
  for(k=nFixed; k<nUsed; k++){
    aEntry[k].zIf = zIf[0] ? string_dup(zIf, -1) : 0;
    aEntry[k].zFunc = string_dup(&zLine[i], j);
    aEntry[k].zHelp = z;


  }
  i+=j;
  while( isspace(zLine[i]) ){ i++; }
  if( zLine[i]!='(' ) goto page_skip;
  nFixed = nUsed;
  nHelp = 0;
  return;

page_skip:
   for(i=nFixed; i<nUsed; i++){
................................................................................

/*
** Compare two entries
*/
int e_compare(const void *a, const void *b){
  const Entry *pA = (const Entry*)a;
  const Entry *pB = (const Entry*)b;
  int x = pA->eType - pB->eType;
  if( x==0 ){
    x = strcmp(pA->zPath, pB->zPath);
  }
  return x;
}

/*
** Build the binary search table.
*/
void build_table(void){
  int i;


  qsort(aEntry, nFixed, sizeof(aEntry[0]), e_compare);











  for(i=0; i<nFixed; i++){
    if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
    printf("extern void %s(void);\n", aEntry[i].zFunc);
    if( aEntry[i].zIf ) printf("#endif\n");
  }
  printf(
    "typedef struct NameMap NameMap;\n"
    "struct NameMap {\n"
    "  const char *zName;\n"
    "  void (*xFunc)(void);\n"
    "  char cmdFlags;\n"
    "};\n"
    "#define CMDFLAG_1ST_TIER  0x01\n"
    "#define CMDFLAG_2ND_TIER  0x02\n"
    "#define CMDFLAG_TEST      0x04\n"
    "#define CMDFLAG_WEBPAGE   0x08\n"
    "static const NameMap aWebpage[] = {\n"
  );
  for(i=0; i<nFixed && aEntry[i].eType==0; i++){
    const char *z = aEntry[i].zPath;
    int n = strlen(z);
    if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
    printf("  { \"%s\",%*s %s,%*s 1 },\n",
      z,
      25-n, "",
      aEntry[i].zFunc,
      (int)(35-strlen(aEntry[i].zFunc)), ""
    );
    if( aEntry[i].zIf ) printf("#endif\n");
  }
  printf("};\n");
  printf(
    "static const NameMap aCommand[] = {\n"
  );
  for(i=0; i<nFixed /*&& aEntry[i].eType==1*/; i++){
    const char *z = aEntry[i].zPath;
    int n = strlen(z);
    int cmdFlags = (1==aEntry[i].eType) ? 0x01 : 0x08;
    if( 0x01==cmdFlags ){
      if( z[n-1]=='*' ){
        n--;
        cmdFlags = 0x02;
      }else if( memcmp(z, "test-", 5)==0 ){
        cmdFlags = 0x04;
      }
    }
    if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
    printf("  { \"%s%.*s\",%*s %s,%*s %d },\n",
      (0x08 & cmdFlags) ? "/" : "",
      n, z,
      25-n, "",
      aEntry[i].zFunc,
      (int)(35-strlen(aEntry[i].zFunc)), "",
      cmdFlags

    );
    if( aEntry[i].zIf ) printf("#endif\n");
  }
  printf("};\n");
  printf("#define FOSSIL_FIRST_CMD (sizeof(aWebpage)/sizeof(aWebpage[0]))\n");
  for(i=0; i<nFixed; i++){
    char *z = aEntry[i].zHelp;
    if( z && z[0] ){
      if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
      printf("static const char zHelp_%s[] = \n", aEntry[i].zFunc);
      printf("  \"");
      while( *z ){
        if( *z=='\n' ){
          printf("\\n\"\n  \"");
        }else if( *z=='"' ){
          printf("\\\"");
        }else{
          putchar(*z);
        }
        z++;
      }
      printf("\";\n");
      if( aEntry[i].zIf ) printf("#endif\n");
      aEntry[i].zHelp[0] = 0;
    }
  }
  puts("struct CmdHelp {"
       "int eType; "
       "const char *zText;"
       "};");
  puts("static struct CmdHelp aCmdHelp[] = {");
  for(i=0; i<nFixed; i++){
    if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
    if( aEntry[i].zHelp==0 ){
      printf("{%d, 0},\n", aEntry[i].eType);
    }else{
      printf("{%d, zHelp_%s},\n", aEntry[i].eType, aEntry[i].zFunc);
    }
    if( aEntry[i].zIf ) printf("#endif\n");
  }
  printf("};\n");
}

/*
** Process a single file of input
*/
void process_file(void){
  FILE *in = fopen(zFile, "r");
................................................................................
    fprintf(stderr,"%s: cannot open\n", zFile);
    return;
  }
  nLine = 0;
  while( fgets(zLine, sizeof(zLine), in) ){
    nLine++;
    scan_for_if(zLine);
    scan_for_label("WEBPAGE:",zLine,0);
    scan_for_label("COMMAND:",zLine,1);
    scan_for_func(zLine);
  }
  fclose(in);
  nUsed = nFixed;
}

int main(int argc, char **argv){
  int i;
  for(i=1; i<argc; i++){
    zFile = argv[i];
    process_file();
  }
  build_table();
  return 0;
}







>

|





|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>



<


>
>
>
>
>
>
>
>
>
>
>





|
|
|
|
|
>





|




|







 







|













>
>
>
>
>












>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>









|






|

|

>
>
>
>
|
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>












|


|







 







|


|
|










|






|
|
|

|


|



|





>
>


|







 







<
<
|
<
<







>


>
>
>
>
>
>
>
>
>
>
>





|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
<
<
<
<
<
<
<
<
<
<



|
|
>




|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|
|













|

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
...
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
...
316
317
318
319
320
321
322


323


324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382











383
384
385
386
387
388
389
390
391
392
393




































394
395
396
397
398
399
400
...
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
** routine collects information about these entry points and then
** generates (on standard output) C code used by Fossil to dispatch
** to those entry points.
**
** The source code is scanned for comment lines of the form:
**
**       WEBPAGE:  /abc/xyz
**       COMMAND:  cmdname
**
** These comment should be followed by a function definition of the
** form:
**
**       void function_name(void){
**
** This routine creates C source code for a constant table that maps
** command and webpage name into pointers to the function.
**
** Command names can divided into three classes:  1st-tier, 2nd-tier,
** and test.  1st-tier commands are the most frequently used and the
** ones that show up with "fossil help".  2nd-tier are seldom-used and/or
** legacy command.  Test commands are unsupported commands used for testing
** and analysis only.
**
** Commands are 1st-tier by default.  If the command name begins with
** "test-" or if the command name has a "test" argument, then it becomes
** a test command.  If the command name has a "2nd-tier" argument or ends
** with a "*" character, it is second tier.  Examples:
**
**        COMMAND:  abcde*
**        COMMAND:  fghij        2nd-tier
**        COMMAND:  test-xyzzy
**        COMMAND:  xyzzy        test
**
** New arguments may be added in future releases that set additional
** bits in the eCmdFlags field.
**
** Additional lines of comment after the COMMAND: or WEBPAGE: become
** the built-in help text for that command or webpage.
**
** Multiple COMMAND: entries can be attached to the same command, thus
** creating multiple aliases for that command.  Similarly, multiple
** WEBPAGE: entries can be attached to the same webpage function, to give
** that page aliases.
*/
#include <stdio.h>
#include <stdlib.h>

#include <assert.h>
#include <string.h>

/***************************************************************************
** These macros must match similar macros in dispatch.c.
**
** Allowed values for CmdOrPage.eCmdFlags. */
#define CMDFLAG_1ST_TIER  0x0001      /* Most important commands */
#define CMDFLAG_2ND_TIER  0x0002      /* Obscure and seldom used commands */
#define CMDFLAG_TEST      0x0004      /* Commands for testing only */
#define CMDFLAG_WEBPAGE   0x0008      /* Web pages */
#define CMDFLAG_COMMAND   0x0010      /* A command */
/**************************************************************************/

/*
** Each entry looks like this:
*/
typedef struct Entry {
  int eType;        /* CMDFLAG_* values */
  char *zIf;        /* Enclose in #if */
  char *zFunc;      /* Name of implementation */
  char *zPath;      /* Webpage or command name */
  char *zHelp;      /* Help text */
  int iHelp;        /* Index of Help text */
} Entry;

/*
** Maximum number of entries
*/
#define N_ENTRY 5000

/*
** Maximum size of a help message
*/
#define MX_HELP 250000

/*
** Table of entries
*/
Entry aEntry[N_ENTRY];

/*
................................................................................
*/
char zHelp[MX_HELP];
int nHelp;

/*
** Most recently encountered #if
*/
char zIf[2000];

/*
** How many entries are used
*/
int nUsed;
int nFixed;

/*
** Current filename and line number
*/
char *zFile;
int nLine;

/*
** Number of errors
*/
int nErr = 0;

/*
** Duplicate N characters of a string.
*/
char *string_dup(const char *zSrc, int n){
  char *z;
  if( n<0 ) n = strlen(zSrc);
  z = malloc( n+1 );
  if( z==0 ){ fprintf(stderr,"Out of memory!\n"); exit(1); }
  strncpy(z, zSrc, n);
  z[n] = 0;
  return z;
}

/*
** Safe isspace macro.  Works with signed characters.
*/
int fossil_isspace(char c){
  return c==' ' || (c<='\r' && c>='\t');
}

/*
** Safe isident macro.  Works with signed characters.
*/
int fossil_isident(char c){
  if( c>='a' && c<='z' ) return 1;
  if( c>='A' && c<='Z' ) return 1;
  if( c>='0' && c<='9' ) return 1;
  if( c=='_' ) return 1;
  return 0;
}

/*
** Scan a line looking for comments containing zLabel.  Make
** new entries if found.
*/
void scan_for_label(const char *zLabel, char *zLine, int eType){
  int i, j;
  int len = strlen(zLabel);
  if( nUsed>=N_ENTRY ) return;
  for(i=0; fossil_isspace(zLine[i]) || zLine[i]=='*'; i++){}
  if( zLine[i]!=zLabel[0] ) return;
  if( strncmp(&zLine[i],zLabel, len)==0 ){
    i += len;
  }else{
    return;
  }
  while( fossil_isspace(zLine[i]) ){ i++; }
  if( zLine[i]=='/' ) i++;
  for(j=0; zLine[i+j] && !fossil_isspace(zLine[i+j]); j++){}
  aEntry[nUsed].eType = eType;
  if( eType & CMDFLAG_WEBPAGE ){
    aEntry[nUsed].zPath = string_dup(&zLine[i-1], j+1);
    aEntry[nUsed].zPath[0] = '/';
  }else{
    aEntry[nUsed].zPath = string_dup(&zLine[i], j);
  }
  aEntry[nUsed].zFunc = 0;
  if( (eType & CMDFLAG_COMMAND)!=0 ){
    if( strncmp(&zLine[i], "test-", 5)==0 ){
      /* Commands that start with "test-" are test-commands */
      aEntry[nUsed].eType |= CMDFLAG_TEST;
    }else if( zLine[i+j-1]=='*' ){
      /* If the command name ends in '*', remove the '*' from the name
      ** but move the command into the second tier */
      aEntry[nUsed].zPath[j-1] = 0;
      aEntry[nUsed].eType |= CMDFLAG_2ND_TIER;
    }else{
      /* Otherwise, this is a first-tier command */
      aEntry[nUsed].eType |= CMDFLAG_1ST_TIER;
    }
  }

  /* Processing additional flags that might following the command name */
  while( zLine[i+j]!=0 ){
    i += j;
    while( fossil_isspace(zLine[i]) ){ i++; }
    if( zLine[i]==0 ) break;
    for(j=0; zLine[i+j] && !fossil_isspace(zLine[i+j]); j++){}
    if( j==8 && strncmp(&zLine[i], "1st-tier", j)==0 ){
      aEntry[nUsed].eType &= ~(CMDFLAG_2ND_TIER|CMDFLAG_TEST);
      aEntry[nUsed].eType |= CMDFLAG_1ST_TIER;
    }else if( j==8 && strncmp(&zLine[i], "2nd-tier", j)==0 ){
      aEntry[nUsed].eType &= ~(CMDFLAG_1ST_TIER|CMDFLAG_TEST);
      aEntry[nUsed].eType |= CMDFLAG_2ND_TIER;
    }else if( j==4 && strncmp(&zLine[i], "test", j)==0 ){
      aEntry[nUsed].eType &= ~(CMDFLAG_1ST_TIER|CMDFLAG_2ND_TIER);
      aEntry[nUsed].eType |= CMDFLAG_TEST;
    }else{
      fprintf(stderr, "%s:%d: unknown option: '%.*s'\n",
              zFile, nLine, j, &zLine[i]);
      nErr++;
    }
  }
 
  nUsed++;
}

/*
** Check to see if the current line is an #if and if it is, add it to
** the zIf[] string.  If the current line is an #endif or #else or #elif
** then cancel the current zIf[] string.
*/
void scan_for_if(const char *zLine){
  int i;
  int len;
  if( zLine[0]!='#' ) return;
  for(i=1; fossil_isspace(zLine[i]); i++){}
  if( zLine[i]==0 ) return;
  len = strlen(&zLine[i]);
  if( strncmp(&zLine[i],"if",2)==0 ){
    zIf[0] = '#';
    memcpy(&zIf[1], &zLine[i], len+1);
  }else if( zLine[i]=='e' ){
    zIf[0] = 0;
  }
}

................................................................................
** Scan a line for a function that implements a web page or command.
*/
void scan_for_func(char *zLine){
  int i,j,k;
  char *z;
  if( nUsed<=nFixed ) return;
  if( strncmp(zLine, "**", 2)==0
   && fossil_isspace(zLine[2])
   && strlen(zLine)<sizeof(zHelp)-nHelp-1
   && nUsed>nFixed
   && strncmp(zLine,"** COMMAND:",11)!=0
   && strncmp(zLine,"** WEBPAGE:",11)!=0
  ){
    if( zLine[2]=='\n' ){
      zHelp[nHelp++] = '\n';
    }else{
      if( strncmp(&zLine[3], "Usage: ", 6)==0 ) nHelp = 0;
      strcpy(&zHelp[nHelp], &zLine[3]);
      nHelp += strlen(&zHelp[nHelp]);
    }
    return;
  }
  for(i=0; fossil_isspace(zLine[i]); i++){}
  if( zLine[i]==0 ) return;
  if( strncmp(&zLine[i],"void",4)!=0 ){
    if( zLine[i]!='*' ) goto page_skip;
    return;
  }
  i += 4;
  if( !fossil_isspace(zLine[i]) ) goto page_skip;
  while( fossil_isspace(zLine[i]) ){ i++; }
  for(j=0; fossil_isident(zLine[i+j]); j++){}
  if( j==0 ) goto page_skip;
  for(k=nHelp-1; k>=0 && fossil_isspace(zHelp[k]); k--){}
  nHelp = k+1;
  zHelp[nHelp] = 0;
  for(k=0; k<nHelp && fossil_isspace(zHelp[k]); k++){}
  if( k<nHelp ){
    z = string_dup(&zHelp[k], nHelp-k);
  }else{
    z = "";
  }
  for(k=nFixed; k<nUsed; k++){
    aEntry[k].zIf = zIf[0] ? string_dup(zIf, -1) : 0;
    aEntry[k].zFunc = string_dup(&zLine[i], j);
    aEntry[k].zHelp = z;
    z = 0;
    aEntry[k].iHelp = nFixed;
  }
  i+=j;
  while( fossil_isspace(zLine[i]) ){ i++; }
  if( zLine[i]!='(' ) goto page_skip;
  nFixed = nUsed;
  nHelp = 0;
  return;

page_skip:
   for(i=nFixed; i<nUsed; i++){
................................................................................

/*
** Compare two entries
*/
int e_compare(const void *a, const void *b){
  const Entry *pA = (const Entry*)a;
  const Entry *pB = (const Entry*)b;


  return strcmp(pA->zPath, pB->zPath);


}

/*
** Build the binary search table.
*/
void build_table(void){
  int i;
  int nWeb = 0;

  qsort(aEntry, nFixed, sizeof(aEntry[0]), e_compare);

  printf(
    "/* Automatically generated code\n"
    "** DO NOT EDIT!\n"
    "**\n"
    "** This file was generated by the mkindex.exe program based on\n"
    "** comments in other Fossil source files.\n"
    "*/\n"
  );

  /* Output declarations for all the action functions */
  for(i=0; i<nFixed; i++){
    if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
    printf("extern void %s(void);\n", aEntry[i].zFunc);
    if( aEntry[i].zIf ) printf("#endif\n");
  }

  /* Output strings for all the help text */
  for(i=0; i<nFixed; i++){
    char *z = aEntry[i].zHelp;
    if( z==0 ) continue;
    if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
    printf("static const char zHelp%03d[] = \n", aEntry[i].iHelp);
    printf("  \"");
    while( *z ){
      if( *z=='\n' ){
        printf("\\n\"\n  \"");
      }else if( *z=='"' ){
        printf("\\\"");
      }else{
        putchar(*z);
      }
      z++;
    }
    printf("\";\n");
    if( aEntry[i].zIf ) printf("#endif\n");
  }

  /* Generate the aCommand[] table */
  printf("static const CmdOrPage aCommand[] = {\n");
  for(i=0; i<nFixed; i++){
    const char *z = aEntry[i].zPath;
    int n = strlen(z);
    if( aEntry[i].zIf ){
      printf("%s", aEntry[i].zIf);
    }else if( (aEntry[i].eType & CMDFLAG_WEBPAGE)!=0 ){
      nWeb++;
    }
    printf("  { \"%.*s\",%*s%s,%*szHelp%03d, 0x%02x },\n",











      n, z,
      25-n, "",
      aEntry[i].zFunc,
      (int)(30-strlen(aEntry[i].zFunc)), "",
      aEntry[i].iHelp,
      aEntry[i].eType
    );
    if( aEntry[i].zIf ) printf("#endif\n");
  }
  printf("};\n");
  printf("#define FOSSIL_FIRST_CMD %d\n", nWeb);




































}

/*
** Process a single file of input
*/
void process_file(void){
  FILE *in = fopen(zFile, "r");
................................................................................
    fprintf(stderr,"%s: cannot open\n", zFile);
    return;
  }
  nLine = 0;
  while( fgets(zLine, sizeof(zLine), in) ){
    nLine++;
    scan_for_if(zLine);
    scan_for_label("WEBPAGE:",zLine,CMDFLAG_WEBPAGE);
    scan_for_label("COMMAND:",zLine,CMDFLAG_COMMAND);
    scan_for_func(zLine);
  }
  fclose(in);
  nUsed = nFixed;
}

int main(int argc, char **argv){
  int i;
  for(i=1; i<argc; i++){
    zFile = argv[i];
    process_file();
  }
  build_table();
  return nErr;
}

Changes to src/moderate.c.

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

/*
** Create a table to represent pending moderation requests, if the
** table does not already exist.
*/
void moderation_table_create(void){
  db_multi_exec(
     "CREATE TABLE IF NOT EXISTS %s.modreq(\n"
     "  objid INTEGER PRIMARY KEY,\n"        /* Record pending approval */
     "  attachRid INT,\n"                    /* Object attached */
     "  tktid TEXT\n"                        /* Associated ticket id */
     ");\n", db_name("repository")
  );
}

/*
** Return TRUE if the modreq table exists
*/
int moderation_table_exists(void){







|



|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

/*
** Create a table to represent pending moderation requests, if the
** table does not already exist.
*/
void moderation_table_create(void){
  db_multi_exec(
     "CREATE TABLE IF NOT EXISTS repository.modreq(\n"
     "  objid INTEGER PRIMARY KEY,\n"        /* Record pending approval */
     "  attachRid INT,\n"                    /* Object attached */
     "  tktid TEXT\n"                        /* Associated ticket id */
     ");\n"
  );
}

/*
** Return TRUE if the modreq table exists
*/
int moderation_table_exists(void){

Changes to src/name.c.

331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
...
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
...
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
...
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
...
932
933
934
935
936
937
938
939





940
941
942
943
944
945
946
947
948
949
950
951
...
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
/*
** This routine is similar to name_to_uuid() except in the form it
** takes its parameters and returns its value, and in that it does not
** treat errors as fatal. zName must be a UUID, as described for
** name_to_uuid(). zType is also as described for that function. If
** zName does not resolve, 0 is returned. If it is ambiguous, a
** negative value is returned. On success the rid is returned and
** pUuid (if it is not NULL) is set to the a newly-allocated string,
** the full UUID, which must eventually be free()d by the caller.
*/
int name_to_uuid2(const char *zName, const char *zType, char **pUuid){
  int rid = symbolic_name_to_rid(zName, zType);
  if((rid>0) && pUuid){
    *pUuid = db_text(NULL, "SELECT uuid FROM blob WHERE rid=%d", rid);
  }
................................................................................
    comment_print(db_column_text(&q,1), 0, 12, -1, g.comFmtFlags);
  }
  db_finalize(&q);
}

/*
** COMMAND: whatis*
** 
** Usage: %fossil whatis NAME
**
** Resolve the symbol NAME into its canonical 40-character SHA1-hash
** artifact name and provide a description of what role that artifact
** plays.
**
** Options:
................................................................................
      whatis_rid(rid, verboseFlag);
    }
  }
}

/*
** COMMAND: test-whatis-all
** 
** Usage: %fossil test-whatis-all
**
** Show "whatis" information about every artifact in the repository
*/
void test_whatis_all_cmd(void){
  Stmt q;
  int cnt = 0;
................................................................................
  }
  db_finalize(&q);
}


/*
** COMMAND: test-ambiguous
** 
** Usage: %fossil test-ambiguous [--minsize N]
**
** Show a list of ambiguous SHA1-hash abbreviations of N characters or
** more where N defaults to 4.  Change N to a different value using
** the "--minsize N" command-line option.
*/
void test_ambiguous_cmd(void){
................................................................................
  /* Mark private elements */
  db_multi_exec(
   "UPDATE description SET isPrivate=1 WHERE rid IN private"
  );
}

/*
** Print the content of the description table on stdout





*/
int describe_artifacts_to_stdout(const char *zWhere, const char *zLabel){
  Stmt q;
  int cnt = 0;
  describe_artifacts(zWhere);
  db_prepare(&q,
    "SELECT uuid, summary, isPrivate\n"
    "  FROM description\n"
    " ORDER BY ctime, type;"
  );
  while( db_step(&q)==SQLITE_ROW ){
    if( zLabel ){
................................................................................
    }
    fossil_print("  %.16s %s", db_column_text(&q,0), db_column_text(&q,1));
    if( db_column_int(&q,2) ) fossil_print(" (unpublished)");
    fossil_print("\n");
    cnt++;
  }
  db_finalize(&q);
  db_multi_exec("DELETE FROM description;");
  return cnt;
}

/*
** COMMAND: test-describe-artifacts
**
** Usage: %fossil test-describe-artifacts [--from S] [--count N]







|







 







|







 







|







 







|







 







|
>
>
>
>
>




|







 







|







331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
...
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
...
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
...
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
...
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
...
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
/*
** This routine is similar to name_to_uuid() except in the form it
** takes its parameters and returns its value, and in that it does not
** treat errors as fatal. zName must be a UUID, as described for
** name_to_uuid(). zType is also as described for that function. If
** zName does not resolve, 0 is returned. If it is ambiguous, a
** negative value is returned. On success the rid is returned and
** pUuid (if it is not NULL) is set to a newly-allocated string,
** the full UUID, which must eventually be free()d by the caller.
*/
int name_to_uuid2(const char *zName, const char *zType, char **pUuid){
  int rid = symbolic_name_to_rid(zName, zType);
  if((rid>0) && pUuid){
    *pUuid = db_text(NULL, "SELECT uuid FROM blob WHERE rid=%d", rid);
  }
................................................................................
    comment_print(db_column_text(&q,1), 0, 12, -1, g.comFmtFlags);
  }
  db_finalize(&q);
}

/*
** COMMAND: whatis*
**
** Usage: %fossil whatis NAME
**
** Resolve the symbol NAME into its canonical 40-character SHA1-hash
** artifact name and provide a description of what role that artifact
** plays.
**
** Options:
................................................................................
      whatis_rid(rid, verboseFlag);
    }
  }
}

/*
** COMMAND: test-whatis-all
**
** Usage: %fossil test-whatis-all
**
** Show "whatis" information about every artifact in the repository
*/
void test_whatis_all_cmd(void){
  Stmt q;
  int cnt = 0;
................................................................................
  }
  db_finalize(&q);
}


/*
** COMMAND: test-ambiguous
**
** Usage: %fossil test-ambiguous [--minsize N]
**
** Show a list of ambiguous SHA1-hash abbreviations of N characters or
** more where N defaults to 4.  Change N to a different value using
** the "--minsize N" command-line option.
*/
void test_ambiguous_cmd(void){
................................................................................
  /* Mark private elements */
  db_multi_exec(
   "UPDATE description SET isPrivate=1 WHERE rid IN private"
  );
}

/*
** Print the content of the description table on stdout.
**
** The description table is computed using the WHERE clause zWhere if
** the zWhere parameter is not NULL.  If zWhere is NULL, then this
** routine assumes that the description table already exists and is
** populated and merely prints the contents.
*/
int describe_artifacts_to_stdout(const char *zWhere, const char *zLabel){
  Stmt q;
  int cnt = 0;
  if( zWhere!=0 ) describe_artifacts(zWhere);
  db_prepare(&q,
    "SELECT uuid, summary, isPrivate\n"
    "  FROM description\n"
    " ORDER BY ctime, type;"
  );
  while( db_step(&q)==SQLITE_ROW ){
    if( zLabel ){
................................................................................
    }
    fossil_print("  %.16s %s", db_column_text(&q,0), db_column_text(&q,1));
    if( db_column_int(&q,2) ) fossil_print(" (unpublished)");
    fossil_print("\n");
    cnt++;
  }
  db_finalize(&q);
  if( zWhere!=0 ) db_multi_exec("DELETE FROM description;");
  return cnt;
}

/*
** COMMAND: test-describe-artifacts
**
** Usage: %fossil test-describe-artifacts [--from S] [--count N]

Changes to src/path.c.

450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
  }
  db_finalize(&q1);
  if( nChng ){
    aChng = *aiChng = fossil_malloc( nChng*2*sizeof(int) );
    for(pChng=pAll, i=0; pChng; pChng=pChng->pNext){
      if( pChng->newName==0 ) continue;
      if( pChng->origName==0 ) continue;
      if( pChng->newName==pChng->origName ) continue;
      aChng[i] = pChng->origName;
      aChng[i+1] = pChng->newName;
      if( zDebug ){
        fossil_print("%s summary %d[%z] -> %d[%z]\n",
           zDebug,
           aChng[i],
           db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i]),







<







450
451
452
453
454
455
456

457
458
459
460
461
462
463
  }
  db_finalize(&q1);
  if( nChng ){
    aChng = *aiChng = fossil_malloc( nChng*2*sizeof(int) );
    for(pChng=pAll, i=0; pChng; pChng=pChng->pNext){
      if( pChng->newName==0 ) continue;
      if( pChng->origName==0 ) continue;

      aChng[i] = pChng->origName;
      aChng[i+1] = pChng->newName;
      if( zDebug ){
        fossil_print("%s summary %d[%z] -> %d[%z]\n",
           zDebug,
           aChng[i],
           db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i]),

Changes to src/piechart.c.

240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
...
265
266
267
268
269
270
271
272
273
274
275
276

277
278
279
280
281
282
283
284
285
286
287
288








289
290
291




292
293
294
295
296

297
298
299
300
301
302
303
304
305
306
307
308


309
310
311
312
313
314
315
316
317
318
319
320
321
322

323





324
325
326
      }else{
        if( y4<rLwrLeft ){
          y4 = rLwrLeft;
        }
        rLwrLeft = y4 + TEXT_HEIGHT;
      }
    }
    if( x4<=cx ){
      x5 = x4 - 1.0;
      zAnc = "end";
    }else{
      x5 = x4 + 1.0;
      zAnc = "start";
    }
    y5 = y4 - 3.0 + 6.0*(1.0 - p->rCos);
    @ <line stroke-width='1' stroke='%s(zFg)' class='piechartLine'
    @  x1='%g(x3)' y1='%g(y3)' x2='%g(x4)' y2='%g(y4)''/>
    @ <text text-anchor="%s(zAnc)" fill='%s(zFg)' class="piechartLabel"
    @  x='%g(x5)' y='%g(y5)'>%h(p->z)</text>
    fossil_free(p->z);
  }
  db_finalize(&q);
  fossil_free(aWedge);
}
................................................................................
/*
** WEBPAGE: test-piechart
**
** Generate a pie-chart based on data input from a form.
*/
void piechart_test_page(void){
  const char *zData;
  Stmt ins, q;
  Blob all, line, token1, token2;
  int n = 0;
  int width;
  int height;


  login_check_credentials();
  style_header("Pie Chart Test");
  db_multi_exec("CREATE TEMP TABLE piechart(amt REAL, label TEXT);");
  db_prepare(&ins, "INSERT INTO piechart(amt,label) VALUES(:amt,:label)");
  zData = PD("data","");
  width = atoi(PD("width","800"));
  height = atoi(PD("height","400"));
  blob_init(&all, zData, -1);
  while( blob_line(&all, &line) ){
    double rAmt;
    if( blob_token(&line, &token1)==0 ) continue;








    rAmt = atof(blob_str(&token1));
    if( rAmt<=0.0 ) continue;
    blob_tail(&line, &token2);




    db_bind_double(&ins, ":amt", rAmt);
    db_bind_text(&ins, ":label", blob_str(&token2));
    db_step(&ins);
    db_reset(&ins);
    n++;

  }
  db_finalize(&ins);
  blob_reset(&all);
  if( n>0 ){
    @ <svg width=%d(width) height=%d(height) style="border:1px solid #d3d3d3;">
    piechart_render(width,height, PIE_OTHER);
    @ </svg>
    @ <hr>
  }
  @ <form method="post" action='%R/test-piechart'>
  @ <p>One slice per line.  Value and then Label.<p>
  @ <textarea name='data' rows='20' cols='80'>%h(zData)</textarea><br/>


  @ Width: <input type='text' size='8' name='width' value='%d(width)'/>
  @ Height: <input type='text' size='8' name='height' value='%d(height)'/><br/>
  @ <input type='hidden' name='width' value='%d(width)'/>
  @ <input type='hidden' name='height' value='%d(height)'/>
  @ <input type='submit' value='Draw The Pie Chart'/>
  @ </form>
  @ <hr><p>Previous Data:</p>
  @ <table border="1">
  db_prepare(&q, "SELECT rowid, amt, label FROM piechart");
  while( db_step(&q)==SQLITE_ROW ){
     @ <tr><td>%d(db_column_int(&q,0))</td>
     @ <td>%g(db_column_double(&q,1))</td>
     @ <td>%h(db_column_text(&q,2))</td></tr>
  }

  db_finalize(&q);





  @ </table>
  style_footer();
}







|








|







 







|
<



>








|
|

|
>
>
>
>
>
>
>
>
|
<
<
>
>
>
>

|


<
>


<
|

|

|

|
<
<
>
>

|
<
<


<
<
<
<
<
<
<
<
>
|
>
>
>
>
>
|


240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
...
265
266
267
268
269
270
271
272

273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297


298
299
300
301
302
303
304
305

306
307
308

309
310
311
312
313
314
315


316
317
318
319


320
321








322
323
324
325
326
327
328
329
330
331
      }else{
        if( y4<rLwrLeft ){
          y4 = rLwrLeft;
        }
        rLwrLeft = y4 + TEXT_HEIGHT;
      }
    }
    if( x4<cx ){
      x5 = x4 - 1.0;
      zAnc = "end";
    }else{
      x5 = x4 + 1.0;
      zAnc = "start";
    }
    y5 = y4 - 3.0 + 6.0*(1.0 - p->rCos);
    @ <line stroke-width='1' stroke='%s(zFg)' class='piechartLine'
    @  x1='%g(x3)' y1='%g(y3)' x2='%g(x4)' y2='%g(y4)'/>
    @ <text text-anchor="%s(zAnc)" fill='%s(zFg)' class="piechartLabel"
    @  x='%g(x5)' y='%g(y5)'>%h(p->z)</text>
    fossil_free(p->z);
  }
  db_finalize(&q);
  fossil_free(aWedge);
}
................................................................................
/*
** WEBPAGE: test-piechart
**
** Generate a pie-chart based on data input from a form.
*/
void piechart_test_page(void){
  const char *zData;
  Stmt ins;

  int n = 0;
  int width;
  int height;
  int i, j;

  login_check_credentials();
  style_header("Pie Chart Test");
  db_multi_exec("CREATE TEMP TABLE piechart(amt REAL, label TEXT);");
  db_prepare(&ins, "INSERT INTO piechart(amt,label) VALUES(:amt,:label)");
  zData = PD("data","");
  width = atoi(PD("width","800"));
  height = atoi(PD("height","400"));
  i = 0;
  while( zData[i] ){
    double rAmt;
    char *zLabel;
    while( fossil_isspace(zData[i]) ){ i++; }
    j = i;
    while( fossil_isdigit(zData[j]) ){ j++; }
    if( zData[j]=='.' ){
      j++;
      while( fossil_isdigit(zData[j]) ){ j++; }
    }
    if( i==j ) break;
    rAmt = atof(&zData[i]);


    i = j;
    while( zData[i]==',' || fossil_isspace(zData[i]) ){ i++; }
    n++;
    zLabel = mprintf("label%02d-%g", n, rAmt);
    db_bind_double(&ins, ":amt", rAmt);
    db_bind_text(&ins, ":label", zLabel);
    db_step(&ins);
    db_reset(&ins);

    fossil_free(zLabel);
  }
  db_finalize(&ins);

  if( n>1 ){
    @ <svg width=%d(width) height=%d(height) style="border:1px solid #d3d3d3;">
    piechart_render(width,height, PIE_OTHER|PIE_PERCENT);
    @ </svg>
    @ <hr />
  }
  @ <form method="POST" action='%R/test-piechart'>


  @ <p>Comma-separated list of slice widths:<br />
  @ <input type='text' name='data' size='80' value='%h(zData)'/><br />
  @ Width: <input type='text' size='8' name='width' value='%d(width)'/>
  @ Height: <input type='text' size='8' name='height' value='%d(height)'/><br />


  @ <input type='submit' value='Draw The Pie Chart'/>
  @ </form>








  @ <p>Interesting test cases:
  @ <ul>
  @ <li> <a href='test-piechart?data=44,2,2,2,2,2,3,2,2,2,2,2,44'>Case 1</a>
  @ <li> <a href='test-piechart?data=2,2,2,2,2,44,44,2,2,2,2,2'>Case 2</a>
  @ <li> <a href='test-piechart?data=20,2,2,2,2,2,2,2,2,2,2,80'>Case 3</a>
  @ <li> <a href='test-piechart?data=80,2,2,2,2,2,2,2,2,2,2,20'>Case 4</a>
  @ <li> <a href='test-piechart?data=2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2'>Case 5</a>
  @ </ul>
  style_footer();
}

Changes to src/pivot.c.

71
72
73
74
75
76
77


78
79
80
81
82
83
84
85
86
...
100
101
102
103
104
105
106
107

108
109
110
111
112
113
114
...
120
121
122
123
124
125
126
127

128
129
130
131
132
133
134
...
159
160
161
162
163
164
165
166
167
168
169
170
  );
}

/*
** Find the most recent common ancestor of the primary and one of
** the secondaries.  Return its rid.  Return 0 if no common ancestor
** can be found.


*/
int pivot_find(void){
  Stmt q1, q2, u1, i1;
  int rid = 0;

  /* aqueue must contain at least one primary and one other.  Otherwise
  ** we abort early
  */
  if( db_int(0, "SELECT count(distinct src) FROM aqueue")<2 ){
................................................................................
  ** is not.
  */
  db_prepare(&q2,
    "SELECT 1 FROM aqueue A, plink, aqueue B"
    " WHERE plink.pid=:rid"
    "   AND plink.cid=B.rid"
    "   AND A.rid=:rid"
    "   AND A.src!=B.src"

  );

  /* Mark the :rid record has having been checked.  It is not the
  ** common ancestor.
  */
  db_prepare(&u1,
    "UPDATE aqueue SET pending=0 WHERE rid=:rid"
................................................................................
    "INSERT OR IGNORE INTO aqueue "
    "SELECT plink.pid,"
    "       coalesce((SELECT mtime FROM plink X WHERE X.cid=plink.pid), 0.0),"
    "       1,"
    "       aqueue.src "
    "  FROM plink, aqueue"
    " WHERE plink.cid=:rid"
    "   AND aqueue.rid=:rid"

  );

  while( db_step(&q1)==SQLITE_ROW ){
    rid = db_column_int(&q1, 0);
    db_reset(&q1);
    db_bind_int(&q2, ":rid", rid);
    if( db_step(&q2)==SQLITE_ROW ){
................................................................................
    usage("PRIMARY SECONDARY ...");
  }
  db_must_be_within_tree();
  pivot_set_primary(name_to_rid(g.argv[2]));
  for(i=3; i<g.argc; i++){
    pivot_set_secondary(name_to_rid(g.argv[i]));
  }
  rid = pivot_find();
  printf("pivot=%s\n",
         db_text("?","SELECT uuid FROM blob WHERE rid=%d",rid)
  );
}







>
>

|







 







|
>







 







|
>







 







|




71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
...
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
...
163
164
165
166
167
168
169
170
171
172
173
174
  );
}

/*
** Find the most recent common ancestor of the primary and one of
** the secondaries.  Return its rid.  Return 0 if no common ancestor
** can be found.
**
** If ignoreMerges is true, follow only "primary" parent links.
*/
int pivot_find(int ignoreMerges){
  Stmt q1, q2, u1, i1;
  int rid = 0;

  /* aqueue must contain at least one primary and one other.  Otherwise
  ** we abort early
  */
  if( db_int(0, "SELECT count(distinct src) FROM aqueue")<2 ){
................................................................................
  ** is not.
  */
  db_prepare(&q2,
    "SELECT 1 FROM aqueue A, plink, aqueue B"
    " WHERE plink.pid=:rid"
    "   AND plink.cid=B.rid"
    "   AND A.rid=:rid"
    "   AND A.src!=B.src %s",
    ignoreMerges ? "AND plink.isprim" : ""
  );

  /* Mark the :rid record has having been checked.  It is not the
  ** common ancestor.
  */
  db_prepare(&u1,
    "UPDATE aqueue SET pending=0 WHERE rid=:rid"
................................................................................
    "INSERT OR IGNORE INTO aqueue "
    "SELECT plink.pid,"
    "       coalesce((SELECT mtime FROM plink X WHERE X.cid=plink.pid), 0.0),"
    "       1,"
    "       aqueue.src "
    "  FROM plink, aqueue"
    " WHERE plink.cid=:rid"
    "   AND aqueue.rid=:rid %s",
    ignoreMerges ? "AND plink.isprim" : ""
  );

  while( db_step(&q1)==SQLITE_ROW ){
    rid = db_column_int(&q1, 0);
    db_reset(&q1);
    db_bind_int(&q2, ":rid", rid);
    if( db_step(&q2)==SQLITE_ROW ){
................................................................................
    usage("PRIMARY SECONDARY ...");
  }
  db_must_be_within_tree();
  pivot_set_primary(name_to_rid(g.argv[2]));
  for(i=3; i<g.argc; i++){
    pivot_set_secondary(name_to_rid(g.argv[i]));
  }
  rid = pivot_find(0);
  printf("pivot=%s\n",
         db_text("?","SELECT uuid FROM blob WHERE rid=%d",rid)
  );
}

Changes to src/purge.c.

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
..
50
51
52
53
54
55
56









57
58
59
60
61
62
63
..
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102










103
104
105
106
107
108
109
...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
...
187
188
189
190
191
192
193







194
195
196
197
198
199
200
...
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
...
420
421
422
423
424
425
426
427
428
429
430
431













432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447



448
449

450
451
452
453
454
455
456
457
458
459





460
461
462
463
464









465

466
467

468
469

470

471
472

473
474

475

476
477
478
479














480
481
482
483
484
485
486
487
488
489
490
491
492
493

































494
495
496
497
498
499
500
...
501
502
503
504
505
506
507

508











509
510
511
512
513
514
515
516
517
518
519
520
521


522
523
524
525

526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573


574
575
576
577
578



579
580
581
582
** manages the graveyard of purged content.
*/
#include "config.h"
#include "purge.h"
#include <assert.h>

/*
** SQL code used to initialize the schema of a bundle.
**
** The purgeevent table contains one entry for each purge event.  For each
** purge event, multiple artifacts might have been removed.  Each removed
** artifact is stored as an entry in the purgeitem table.
**
** The purgeevent and purgeitem tables are not synced, even by the
** "fossil config" command.  They exist only as a backup in case of a
................................................................................
@   isPrivate BOOLEAN,         -- True if artifact was originally private
@   sz INT NOT NULL,           -- Uncompressed size of the purged artifact
@   desc TEXT,                 -- Brief description of this artifact
@   data BLOB                  -- Compressed artifact content
@ );
;










/*
** This routine purges multiple artifacts from the repository, transfering
** those artifacts into the PURGEITEM table.
**
** Prior to invoking this routine, the caller must create a (TEMP) table
** named zTab that contains the RID of every artifact to be purged.
**
................................................................................
**         (h) BACKLINK
**         (i) ATTACHMENT
**         (j) TICKETCHNG
**    (7) If any ticket artifacts were removed (6j) then rebuild the
**        corresponding ticket entries.  Possibly remove entries from
**        the ticket table.
**
** Stops 1-4 (saving the purged artifacts into the graveyard) are only
** undertaken if the moveToGraveyard flag is true.
*/
int purge_artifact_list(
  const char *zTab,       /* TEMP table containing list of RIDS to be purged */
  const char *zNote,      /* Text of the purgeevent.pnotes field */
  int moveToGraveyard     /* Move purged artifacts into the graveyard */
){
  int peid = 0;                 /* New purgeevent ID */
  Stmt q;                       /* General-use prepared statement */
  char *z;

  assert( g.repositoryOpen );   /* Main database must already be open */
  db_begin_transaction();
  z = sqlite3_mprintf("IN \"%w\"", zTab);
  describe_artifacts(z);
  sqlite3_free(z);











  /* Make sure we are not removing a manifest that is the baseline of some
  ** manifest that is being left behind.  This step is not strictly necessary.
  ** is is just a safety check. */
  if( purge_baseline_out_from_under_delta(zTab) ){
    fossil_fatal("attempt to purge a baseline manifest without also purging "
                 "all of its deltas");
................................................................................
    content_undelta(rid);
    verify_before_commit(rid);
  }
  db_finalize(&q);

  /* Construct the graveyard and copy the artifacts to be purged into the
  ** graveyard */
  if( moveToGraveyard ){
    db_multi_exec(zPurgeInit /*works-like:"%w%w"*/,
                  db_name("repository"), db_name("repository"));
    db_multi_exec(
      "INSERT INTO purgeevent(ctime,pnotes) VALUES(now(),%Q)", zNote
    );
    peid = db_last_insert_rowid();
    db_prepare(&q, "SELECT rid FROM delta WHERE rid IN \"%w\""
                   " AND srcid NOT IN \"%w\"", zTab, zTab);
    while( db_step(&q)==SQLITE_ROW ){
................................................................................
    ticket_rebuild_entry(db_column_text(&q, 0));
  }
  db_finalize(&q);
  /* db_multi_exec("DROP TABLE \"%w_tickets\"", zTab); */

  /* Mission accomplished */
  db_end_transaction(0);







  return peid;
}

/*
** The TEMP table named zTab contains RIDs for a set of check-ins.
**
** Check to see if any check-in in zTab is a baseline manifest for some
................................................................................
  }
}


/*
** The TEMP table named zTab contains the RIDs for a set of check-in
** artifacts.  Expand this set (by adding new entries to zTab) to include
** all other artifacts that are used the set of check-ins in
** the original list.
**
** If the bExclusive flag is true, then the set is only expanded by
** artifacts that are used exclusively by the check-ins in the set.
** When bExclusive is false, then all artifacts used by the check-ins
** are added even if those artifacts are also used by other check-ins
** not in the set.
................................................................................
    blob_reset(&c2);
  }
  db_finalize(&q);
  if( iSrc>0 ) bag_remove(&busy, iSrc);
}

/*
** COMMAND: purge
**
** The purge command removes content from a repository and stores that content
** in a "graveyard".  The graveyard exists so that content can be recovered
** using the "fossil purge undo" command.













**
**   fossil purge cat UUID...
**
**      Write the content of one or more artifacts in the graveyard onto
**      standard output.
**
**   fossil purge ?checkins? TAGS... ?OPTIONS?
**
**      Move the check-ins identified by TAGS and all of their descendants
**      out of the repository and into the graveyard.  The "checkins"
**      subcommand keyword is optional and can be omitted as long as TAGS
**      does not conflict with any other subcommand.
**
**      If TAGS includes a branch name then it means all the check-ins
**      on the most recent occurrance of that branch.
**



**           --explain         Make no changes, but show what would happen.
**           --dry-run         Make no changes.

**
**   fossil purge list|ls ?-l?
**
**      Show the graveyard of prior purges.  The -l option gives more
**      detail in the output.
**
**   fossil purge obliterate ID...
**
**      Remove one or more purge events from the graveyard.  Once a purge
**      event is obliterated, it can no longer be undone.





**
**   fossil purge undo ID
**
**      Restore the content previously removed by purge ID.
**









** SUMMARY:

**   fossil purge cat UUID...
**   fossil purge [checkins] TAGS... [--explain]

**   fossil purge list
**   fossil purge obliterate ID...

**   fossil purge undo ID

*/
void purge_cmd(void){

  const char *zSubcmd;
  int n;

  Stmt q;

  if( g.argc<3 ) usage("SUBCOMMAND ?ARGS?");
  zSubcmd = g.argv[2];
  db_find_and_open_repository(0,0);
  n = (int)strlen(zSubcmd);














  if( strncmp(zSubcmd, "cat", n)==0 ){
    int i, piid;
    Blob content;
    if( g.argc<4 ) usage("cat UUID...");
    for(i=3; i<g.argc; i++){
      piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'",
                       g.argv[i]);
      if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]);
      purge_extract_item(piid, &content);
      blob_write_to_file(&content, "-");
      blob_reset(&content);
    }
  /* The "checkins" subcommand goes here in alphabetical order, but it must
  ** be moved to the end since it is the default case */

































  }else if( strncmp(zSubcmd, "list", n)==0 || strcmp(zSubcmd,"ls")==0 ){
    int showDetail = find_option("l","l",0)!=0;
    if( !db_table_exists("repository","purgeevent") ) return;
    db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch',toLocal())"
                   " FROM purgeevent");
    while( db_step(&q)==SQLITE_ROW ){
      fossil_print("%4d on %s\n", db_column_int(&q,0), db_column_text(&q,1));
................................................................................
      if( showDetail ){
        purge_list_event_content(db_column_int(&q,0));
      }
    }
    db_finalize(&q);
  }else if( strncmp(zSubcmd, "obliterate", n)==0 ){
    int i;

    if( g.argc<4 ) usage("obliterate ID...");











    db_begin_transaction();
    for(i=3; i<g.argc; i++){
      int peid = atoi(g.argv[i]);
      if( !db_exists("SELECT 1 FROM purgeevent WHERE peid=%d",peid) ){
        fossil_fatal("no such purge event: %s", g.argv[i]);
      }
      db_multi_exec(
        "DELETE FROM purgeevent WHERE peid=%d;"
        "DELETE FROM purgeitem WHERE peid=%d;",
        peid, peid
      );
    }
    db_end_transaction(0);


  }else if( strncmp(zSubcmd, "undo", n)==0 ){
    int peid;
    if( g.argc!=4 ) usage("undo ID");
    peid = atoi(g.argv[3]);

    db_begin_transaction();
    db_multi_exec(
      "CREATE TEMP TABLE ix("
      "  piid INTEGER PRIMARY KEY,"
      "  srcid INTEGER"
      ");"
      "CREATE INDEX ixsrcid ON ix(srcid);"
      "INSERT INTO ix(piid,srcid) "
      "  SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;",
      peid
    );
    db_multi_exec(
      "DELETE FROM shun"
      " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);",
      peid
    );
    manifest_crosslink_begin();
    purge_item_resurrect(0, 0);
    manifest_crosslink_end(0);
    db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid);
    db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid);
    db_end_transaction(0);
  }else{
    /* The "checkins" command is the default and so must occur last */
    int explainOnly = find_option("explain",0,0)!=0;
    int dryRun = find_option("dry-run",0,0)!=0;
    int i;
    int vid;
    int nCkin;
    int nArtifact;
    verify_all_options();
    db_begin_transaction();
    i = strncmp(zSubcmd,"checkins",n)==0 ? 3 : 2;
    if( i>=g.argc ) usage("[checkin] TAGS... [--explain]");
    db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
    for(; i<g.argc; i++){
      int r = name_to_typed_rid(g.argv[i], "br");
      compute_descendants(r, 1000000000);
    }
    vid = db_lget_int("checkout",0);
    if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){
      fossil_fatal("cannot purge the current checkout");
    }
    nCkin = db_int(0, "SELECT count(*) FROM ok");
    find_checkin_associates("ok", 1);
    nArtifact = db_int(0, "SELECT count(*) FROM ok");
    if( explainOnly ){
      describe_artifacts_to_stdout("IN ok", 0);


    }else{
      int peid = purge_artifact_list("ok","",1);
      fossil_print("%d check-ins and %d artifacts purged.\n", nCkin, nArtifact);
      fossil_print("undoable using \"%s purge undo %d\".\n",
                    g.nameOfExe, peid);



    }
    db_end_transaction(explainOnly||dryRun);
  }
}







|







 







>
>
>
>
>
>
>
>
>







 







|





|










>
>
>
>
>
>
>
>
>
>







 







|

|







 







>
>
>
>
>
>
>







 







|







 







|



|
>
>
>
>
>
>
>
>
>
>
>
>
>






|

|
|
<
<
<

|

>
>
>
|
|
>






|


|
>
>
>
>
>





>
>
>
>
>
>
>
>
>

>

|
>


>

>


>


>

>




>
>
>
>
>
>
>
>
>
>
>
>
>
>
|











|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>

>
>
>
>
>
>
>
>
>
>
>













>
>




>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

<
<
<
<
<
<
<
<
<
>
>
|
<
<
<
<
>
>
>
|
<
|
<
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
..
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
..
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
...
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
...
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480



481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
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
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
















671









672
673
674




675
676
677
678

679

** manages the graveyard of purged content.
*/
#include "config.h"
#include "purge.h"
#include <assert.h>

/*
** SQL code used to initialize the schema of the graveyard.
**
** The purgeevent table contains one entry for each purge event.  For each
** purge event, multiple artifacts might have been removed.  Each removed
** artifact is stored as an entry in the purgeitem table.
**
** The purgeevent and purgeitem tables are not synced, even by the
** "fossil config" command.  They exist only as a backup in case of a
................................................................................
@   isPrivate BOOLEAN,         -- True if artifact was originally private
@   sz INT NOT NULL,           -- Uncompressed size of the purged artifact
@   desc TEXT,                 -- Brief description of this artifact
@   data BLOB                  -- Compressed artifact content
@ );
;

/*
** Flags for the purge_artifact_list() function.
*/
#if INTERFACE
#define PURGE_MOVETO_GRAVEYARD  0x0001    /* Move artifacts in graveyard */
#define PURGE_EXPLAIN_ONLY      0x0002    /* Show what would have happened */
#define PURGE_PRINT_SUMMARY     0x0004    /* Print a summary report at end */
#endif

/*
** This routine purges multiple artifacts from the repository, transfering
** those artifacts into the PURGEITEM table.
**
** Prior to invoking this routine, the caller must create a (TEMP) table
** named zTab that contains the RID of every artifact to be purged.
**
................................................................................
**         (h) BACKLINK
**         (i) ATTACHMENT
**         (j) TICKETCHNG
**    (7) If any ticket artifacts were removed (6j) then rebuild the
**        corresponding ticket entries.  Possibly remove entries from
**        the ticket table.
**
** Steps 1-4 (saving the purged artifacts into the graveyard) are only
** undertaken if the moveToGraveyard flag is true.
*/
int purge_artifact_list(
  const char *zTab,       /* TEMP table containing list of RIDS to be purged */
  const char *zNote,      /* Text of the purgeevent.pnotes field */
  unsigned purgeFlags     /* zero or more PURGE_* flags */
){
  int peid = 0;                 /* New purgeevent ID */
  Stmt q;                       /* General-use prepared statement */
  char *z;

  assert( g.repositoryOpen );   /* Main database must already be open */
  db_begin_transaction();
  z = sqlite3_mprintf("IN \"%w\"", zTab);
  describe_artifacts(z);
  sqlite3_free(z);
  describe_artifacts_to_stdout(0, 0);

  /* The explain-only flags causes this routine to list the artifacts
  ** that would have been purged but to not actually make any changes
  ** to the repository.
  */
  if( purgeFlags & PURGE_EXPLAIN_ONLY ){
    db_end_transaction(0);
    return 0;
  }

  /* Make sure we are not removing a manifest that is the baseline of some
  ** manifest that is being left behind.  This step is not strictly necessary.
  ** is is just a safety check. */
  if( purge_baseline_out_from_under_delta(zTab) ){
    fossil_fatal("attempt to purge a baseline manifest without also purging "
                 "all of its deltas");
................................................................................
    content_undelta(rid);
    verify_before_commit(rid);
  }
  db_finalize(&q);

  /* Construct the graveyard and copy the artifacts to be purged into the
  ** graveyard */
  if( purgeFlags & PURGE_MOVETO_GRAVEYARD ){
    db_multi_exec(zPurgeInit /*works-like:"%w%w"*/,
                  "repository", "repository");
    db_multi_exec(
      "INSERT INTO purgeevent(ctime,pnotes) VALUES(now(),%Q)", zNote
    );
    peid = db_last_insert_rowid();
    db_prepare(&q, "SELECT rid FROM delta WHERE rid IN \"%w\""
                   " AND srcid NOT IN \"%w\"", zTab, zTab);
    while( db_step(&q)==SQLITE_ROW ){
................................................................................
    ticket_rebuild_entry(db_column_text(&q, 0));
  }
  db_finalize(&q);
  /* db_multi_exec("DROP TABLE \"%w_tickets\"", zTab); */

  /* Mission accomplished */
  db_end_transaction(0);

  if( purgeFlags & PURGE_PRINT_SUMMARY ){
    fossil_print("%d artifacts purged\n",
                  db_int(0, "SELECT count(*) FROM \"%w\";", zTab));
    fossil_print("undoable using \"%s purge undo %d\".\n",
                  g.nameOfExe, peid);
  }
  return peid;
}

/*
** The TEMP table named zTab contains RIDs for a set of check-ins.
**
** Check to see if any check-in in zTab is a baseline manifest for some
................................................................................
  }
}


/*
** The TEMP table named zTab contains the RIDs for a set of check-in
** artifacts.  Expand this set (by adding new entries to zTab) to include
** all other artifacts that are used by the check-ins in
** the original list.
**
** If the bExclusive flag is true, then the set is only expanded by
** artifacts that are used exclusively by the check-ins in the set.
** When bExclusive is false, then all artifacts used by the check-ins
** are added even if those artifacts are also used by other check-ins
** not in the set.
................................................................................
    blob_reset(&c2);
  }
  db_finalize(&q);
  if( iSrc>0 ) bag_remove(&busy, iSrc);
}

/*
** COMMAND: purge*
**
** The purge command removes content from a repository and stores that content
** in a "graveyard".  The graveyard exists so that content can be recovered
** using the "fossil purge undo" command.  The "fossil purge obliterate"
** command empties the graveyard, making the content unrecoverable.
**
** ==== WARNING: This command can potentially destroy historical data and ====
** ==== leave your repository in a goofy state. Know what you are doing!  ====
** ==== Make a backup of your repository before using this command!       ====
**
** ==== FURTHER WARNING: This command is a work-in-progress and may yet   ====
** ==== contain bugs.                                                     ====
**
**   fossil purge artifacts UUID... ?OPTIONS?
**
**      Move arbitrary artifacts identified by the UUID list into the
**      graveyard.
**
**   fossil purge cat UUID...
**
**      Write the content of one or more artifacts in the graveyard onto
**      standard output.
**
**   fossil purge checkins TAGS... ?OPTIONS?
**
**      Move the check-ins or branches identified by TAGS and all of
**      their descendants out of the repository and into the graveyard.



**      If TAGS includes a branch name then it means all the check-ins
**      on the most recent occurrence of that branch.
**
**   fossil purge files NAME ... ?OPTIONS?
**
**      Move all instances of files called NAME into the graveyard.
**      NAME should be the name of the file relative to the root of the
**      repository.  If NAME is a directory, then all files within that
**      directory are moved.
**
**   fossil purge list|ls ?-l?
**
**      Show the graveyard of prior purges.  The -l option gives more
**      detail in the output.
**
**   fossil purge obliterate ID... ?--force?
**
**      Remove one or more purge events from the graveyard.  Once a purge
**      event is obliterated, it can no longer be undone.  The --force
**      option suppresses the confirmation prompt.
**
**   fossil purge tickets NAME ... ?OPTIONS?
**
**      TBD...
**
**   fossil purge undo ID
**
**      Restore the content previously removed by purge ID.
**
**   fossil purge wiki NAME ... ?OPTIONS?
**
**      TBD...
**
** COMMON OPTIONS:
**
**   --explain         Make no changes, but show what would happen.
**   --dry-run         An alias for --explain
**
** SUMMARY:
**   fossil purge artifacts UUID.. [OPTIONS]
**   fossil purge cat UUID...
**   fossil purge checkins TAGS... [OPTIONS]
**   fossil purge files FILENAME... [OPTIONS]
**   fossil purge list
**   fossil purge obliterate ID...
**   fossil purge tickets NAME... [OPTIONS]
**   fossil purge undo ID
**   fossil purge wiki NAME... [OPTIONS]
*/
void purge_cmd(void){
  int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY;
  const char *zSubcmd;
  int n;
  int i;
  Stmt q;

  if( g.argc<3 ) usage("SUBCOMMAND ?ARGS?");
  zSubcmd = g.argv[2];
  db_find_and_open_repository(0,0);
  n = (int)strlen(zSubcmd);
  if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){
    purgeFlags |= PURGE_EXPLAIN_ONLY;
  }
  if( strncmp(zSubcmd, "artifacts", n)==0 ){
    verify_all_options();
    db_begin_transaction();
    db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
    for(i=3; i<g.argc; i++){
      int r = name_to_typed_rid(g.argv[i], "");
      db_multi_exec("INSERT OR IGNORE INTO ok(rid) VALUES(%d);", r);
    }
    describe_artifacts_to_stdout("IN ok", 0);
    purge_artifact_list("ok", "", purgeFlags);
    db_end_transaction(0);
  }else if( strncmp(zSubcmd, "cat", n)==0 ){
    int i, piid;
    Blob content;
    if( g.argc<4 ) usage("cat UUID...");
    for(i=3; i<g.argc; i++){
      piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'",
                       g.argv[i]);
      if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]);
      purge_extract_item(piid, &content);
      blob_write_to_file(&content, "-");
      blob_reset(&content);
    }
  }else if( strncmp(zSubcmd, "checkins", n)==0 ){
    int vid;
    if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){
      purgeFlags |= PURGE_EXPLAIN_ONLY;
    }
    verify_all_options();
    db_begin_transaction();
    if( g.argc<=3 ) usage("checkins TAGS... [OPTIONS]");
    db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
    for(i=3; i<g.argc; i++){
      int r = name_to_typed_rid(g.argv[i], "br");
      compute_descendants(r, 1000000000);
    }
    vid = db_lget_int("checkout",0);
    if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){
      fossil_fatal("cannot purge the current checkout");
    }
    find_checkin_associates("ok", 1);
    purge_artifact_list("ok", "", purgeFlags);
    db_end_transaction(0);
  }else if( strncmp(zSubcmd, "files", n)==0 ){
    verify_all_options();
    db_begin_transaction();
    db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
    for(i=3; i<g.argc; i++){
      db_multi_exec(
         "INSERT OR IGNORE INTO ok(rid) "
         "  SELECT fid FROM mlink, filename"
         "   WHERE mlink.fnid=filename.fnid"
         "     AND (filename.name=%Q OR filename.name GLOB '%q/*')",
         g.argv[i], g.argv[i]
      );
    }
    purge_artifact_list("ok", "", purgeFlags);
    db_end_transaction(0);
  }else if( strncmp(zSubcmd, "list", n)==0 || strcmp(zSubcmd,"ls")==0 ){
    int showDetail = find_option("l","l",0)!=0;
    if( !db_table_exists("repository","purgeevent") ) return;
    db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch',toLocal())"
                   " FROM purgeevent");
    while( db_step(&q)==SQLITE_ROW ){
      fossil_print("%4d on %s\n", db_column_int(&q,0), db_column_text(&q,1));
................................................................................
      if( showDetail ){
        purge_list_event_content(db_column_int(&q,0));
      }
    }
    db_finalize(&q);
  }else if( strncmp(zSubcmd, "obliterate", n)==0 ){
    int i;
    int bForce = find_option("force","f",0)!=0;
    if( g.argc<4 ) usage("obliterate ID...");
    if( !bForce ){
      Blob ans;
      char cReply;
      prompt_user(
         "Obliterating the graveyard will permanently delete information.\n"
         "Changes cannot be undone.  Continue (y/N)? ", &ans);
      cReply = blob_str(&ans)[0];
      if( cReply!='y' && cReply!='Y' ){
        fossil_exit(1);
      }
    }
    db_begin_transaction();
    for(i=3; i<g.argc; i++){
      int peid = atoi(g.argv[i]);
      if( !db_exists("SELECT 1 FROM purgeevent WHERE peid=%d",peid) ){
        fossil_fatal("no such purge event: %s", g.argv[i]);
      }
      db_multi_exec(
        "DELETE FROM purgeevent WHERE peid=%d;"
        "DELETE FROM purgeitem WHERE peid=%d;",
        peid, peid
      );
    }
    db_end_transaction(0);
  }else if( strncmp(zSubcmd, "tickets", n)==0 ){
    fossil_fatal("not yet implemented....");
  }else if( strncmp(zSubcmd, "undo", n)==0 ){
    int peid;
    if( g.argc!=4 ) usage("undo ID");
    peid = atoi(g.argv[3]);
    if( (purgeFlags & PURGE_EXPLAIN_ONLY)==0 ){
      db_begin_transaction();
      db_multi_exec(
        "CREATE TEMP TABLE ix("
        "  piid INTEGER PRIMARY KEY,"
        "  srcid INTEGER"
        ");"
        "CREATE INDEX ixsrcid ON ix(srcid);"
        "INSERT INTO ix(piid,srcid) "
        "  SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;",
        peid
      );
      db_multi_exec(
        "DELETE FROM shun"
        " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);",
        peid
      );
      manifest_crosslink_begin();
      purge_item_resurrect(0, 0);
      manifest_crosslink_end(0);
      db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid);
      db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid);
      db_end_transaction(0);
















    }









  }else if( strncmp(zSubcmd, "wiki", n)==0 ){
    fossil_fatal("not yet implemented....");
  }else{




    fossil_fatal("unknown subcommand \"%s\".\n"
                 "should be one of:  cat, checkins, files, list, obliterate,"
                 " tickets, undo, wiki", zSubcmd);
  }

}

Changes to src/rebuild.c.

347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
...
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
...
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
  for(;;){
    zTable = db_text(0,
       "SELECT name FROM sqlite_master /*scan*/"
       " WHERE type='table'"
       " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user',"
                         "'config','shun','private','reportfmt',"
                         "'concealed','accesslog','modreq',"
                         "'purgeevent','purgeitem')"
       " AND name NOT GLOB 'sqlite_*'"
       " AND name NOT GLOB 'fx_*'"
    );
    if( zTable==0 ) break;
    db_multi_exec("DROP TABLE %Q", zTable);
    free(zTable);
  }
................................................................................
    }
    fossil_print("%-15s %6d\n", "Other:", g.parseCnt[CFTYPE_ANY] - subtotal);
  }
}

/*
** COMMAND: test-detach
** 
** Usage: %fossil test-detach  ?REPOSITORY?
**
** Change the project-code and make other changes in order to prevent
** the repository from ever again pushing or pulling to other
** repositories.  Used to create a "test" repository for development
** testing by cloning a working project repository.
*/
................................................................................
    }
    db_finalize(&q);
  }
}

/*
** COMMAND: scrub*
** 
** Usage: %fossil scrub ?OPTIONS? ?REPOSITORY?
**
** The command removes sensitive information (such as passwords) from a
** repository so that the repository can be sent to an untrusted reader.
**
** By default, only passwords are removed.  However, if the --verily option
** is added, then private branches, concealed email addresses, IP







|







 







|







 







|







347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
...
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
...
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
  for(;;){
    zTable = db_text(0,
       "SELECT name FROM sqlite_master /*scan*/"
       " WHERE type='table'"
       " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user',"
                         "'config','shun','private','reportfmt',"
                         "'concealed','accesslog','modreq',"
                         "'purgeevent','purgeitem','unversioned')"
       " AND name NOT GLOB 'sqlite_*'"
       " AND name NOT GLOB 'fx_*'"
    );
    if( zTable==0 ) break;
    db_multi_exec("DROP TABLE %Q", zTable);
    free(zTable);
  }
................................................................................
    }
    fossil_print("%-15s %6d\n", "Other:", g.parseCnt[CFTYPE_ANY] - subtotal);
  }
}

/*
** COMMAND: test-detach
**
** Usage: %fossil test-detach  ?REPOSITORY?
**
** Change the project-code and make other changes in order to prevent
** the repository from ever again pushing or pulling to other
** repositories.  Used to create a "test" repository for development
** testing by cloning a working project repository.
*/
................................................................................
    }
    db_finalize(&q);
  }
}

/*
** COMMAND: scrub*
**
** Usage: %fossil scrub ?OPTIONS? ?REPOSITORY?
**
** The command removes sensitive information (such as passwords) from a
** repository so that the repository can be sent to an untrusted reader.
**
** By default, only passwords are removed.  However, if the --verily option
** is added, then private branches, concealed email addresses, IP

Changes to src/report.c.

171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
...
190
191
192
193
194
195
196

197
198
199
200
201
202
203
...
893
894
895
896
897
898
899

900
901
902
903
904
905
906
...
909
910
911
912
913
914
915
916






917


918

919
920
921
922
923
924
925
....
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
  const char *zArg2,
  const char *zArg3,
  const char *zArg4
){
  int rc = SQLITE_OK;
  if( *(char**)pError ){
    /* We've already seen an error.  No need to continue. */
    return SQLITE_OK;
  }
  switch( code ){
    case SQLITE_SELECT:
    case SQLITE_RECURSIVE:
    case SQLITE_FUNCTION: {
      break;
    }
................................................................................
         "blob",
         "filename",
         "mlink",
         "plink",
         "event",
         "tag",
         "tagxref",

      };
      int i;
      if( fossil_strncmp(zArg1, "fx_", 3)==0 ){
        break;
      }
      for(i=0; i<sizeof(azAllowed)/sizeof(azAllowed[0]); i++){
        if( fossil_stricmp(zArg1, azAllowed[i])==0 ) break;
................................................................................
  int rc = SQLITE_OK;         /* Return code */
  const char *zLeftover;      /* Tail of unprocessed SQL */
  sqlite3_stmt *pStmt = 0;    /* The current SQL statement */
  const char **azCols = 0;    /* Names of result columns */
  int nCol;                   /* Number of columns of output */
  const char **azVals = 0;    /* Text of all output columns */
  int i;                      /* Loop counter */


  pStmt = 0;
  rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
  assert( rc==SQLITE_OK || pStmt==0 );
  if( rc!=SQLITE_OK ){
    return rc;
  }
................................................................................
    return SQLITE_OK;
  }
  if( !sqlite3_stmt_readonly(pStmt) ){
    sqlite3_finalize(pStmt);
    return SQLITE_ERROR;
  }

  i = sqlite3_bind_parameter_index(pStmt, "$login");






  if( i ) sqlite3_bind_text(pStmt, i, g.zLogin, -1, SQLITE_TRANSIENT);




  nCol = sqlite3_column_count(pStmt);
  azVals = fossil_malloc(2*nCol*sizeof(const char*) + 1);
  while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){
    if( azCols==0 ){
      azCols = &azVals[nCol];
      for(i=0; i<nCol; i++){
        azCols[i] = sqlite3_column_name(pStmt, i);
................................................................................
      zDir = !strcmp("ASC",zDir) ? "ASC" : "DESC";
      zSql = mprintf("SELECT * FROM (%s) ORDER BY %d %s", zSql, nField, zDir);
    }
  }

  count = 0;
  if( !tabs ){
    struct GenerateHTML sState;

    db_multi_exec("PRAGMA empty_result_callbacks=ON");
    style_submenu_element("Raw", "Raw",
      "rptview?tablist=1&%h", PD("QUERY_STRING",""));
    if( g.perm.Admin
       || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
      style_submenu_element("Edit", "Edit", "rptedit?rn=%d", rn);







|







 







>







 







>







 







|
>
>
>
>
>
>
|
>
>
|
>







 







|







171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
...
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
...
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
...
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
....
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
  const char *zArg2,
  const char *zArg3,
  const char *zArg4
){
  int rc = SQLITE_OK;
  if( *(char**)pError ){
    /* We've already seen an error.  No need to continue. */
    return SQLITE_DENY;
  }
  switch( code ){
    case SQLITE_SELECT:
    case SQLITE_RECURSIVE:
    case SQLITE_FUNCTION: {
      break;
    }
................................................................................
         "blob",
         "filename",
         "mlink",
         "plink",
         "event",
         "tag",
         "tagxref",
         "unversioned",
      };
      int i;
      if( fossil_strncmp(zArg1, "fx_", 3)==0 ){
        break;
      }
      for(i=0; i<sizeof(azAllowed)/sizeof(azAllowed[0]); i++){
        if( fossil_stricmp(zArg1, azAllowed[i])==0 ) break;
................................................................................
  int rc = SQLITE_OK;         /* Return code */
  const char *zLeftover;      /* Tail of unprocessed SQL */
  sqlite3_stmt *pStmt = 0;    /* The current SQL statement */
  const char **azCols = 0;    /* Names of result columns */
  int nCol;                   /* Number of columns of output */
  const char **azVals = 0;    /* Text of all output columns */
  int i;                      /* Loop counter */
  int nVar;                   /* Number of parameters */

  pStmt = 0;
  rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
  assert( rc==SQLITE_OK || pStmt==0 );
  if( rc!=SQLITE_OK ){
    return rc;
  }
................................................................................
    return SQLITE_OK;
  }
  if( !sqlite3_stmt_readonly(pStmt) ){
    sqlite3_finalize(pStmt);
    return SQLITE_ERROR;
  }

  nVar = sqlite3_bind_parameter_count(pStmt);
  for(i=1; i<=nVar; i++){
    const char *zVar = sqlite3_bind_parameter_name(pStmt, i);
    if( zVar==0 ) continue;
    if( zVar[0]!='$' && zVar[0]!='$' && zVar[0]!=':' ) continue;
    if( !fossil_islower(zVar[1]) ) continue;
    if( strcmp(zVar, "$login")==0 ){
      sqlite3_bind_text(pStmt, i, g.zLogin, -1, SQLITE_TRANSIENT);
    }else{
      sqlite3_bind_text(pStmt, i, P(zVar+1), -1, SQLITE_TRANSIENT);
    }
  }
  nCol = sqlite3_column_count(pStmt);
  azVals = fossil_malloc(2*nCol*sizeof(const char*) + 1);
  while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){
    if( azCols==0 ){
      azCols = &azVals[nCol];
      for(i=0; i<nCol; i++){
        azCols[i] = sqlite3_column_name(pStmt, i);
................................................................................
      zDir = !strcmp("ASC",zDir) ? "ASC" : "DESC";
      zSql = mprintf("SELECT * FROM (%s) ORDER BY %d %s", zSql, nField, zDir);
    }
  }

  count = 0;
  if( !tabs ){
    struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    db_multi_exec("PRAGMA empty_result_callbacks=ON");
    style_submenu_element("Raw", "Raw",
      "rptview?tablist=1&%h", PD("QUERY_STRING",""));
    if( g.perm.Admin
       || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
      style_submenu_element("Edit", "Edit", "rptedit?rn=%d", rn);

Changes to src/search.c.

11
12
13
14
15
16
17
18
19
20
21
22
23
24









25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
..
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
..
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
...
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
...
316
317
318
319
320
321
322



323
324
325
326
327
328
329
...
349
350
351
352
353
354
355
356
357
358
359
360




361
362
363
364
365
366
367
...
384
385
386
387
388
389
390

391
392


393
394
395
396
397
398
399
400
...
405
406
407
408
409
410
411
412
413
414

415
416
417
418
419
420
421
422





423
424
425
426
427
428
429
430
431
432
433

434
435
436
437
438
439
440
441
...
443
444
445
446
447
448
449





450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465





466
467
468
469
470
471
472
473
474
475
476
477
478

479
480

481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
...
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
...
634
635
636
637
638
639
640
641
642






643
644
645
646
647
648
649
650
651
652
653
...
803
804
805
806
807
808
809
810
811






812
813
814
815
816
817
818
819
820
821
822
...
908
909
910
911
912
913
914




915
916
917
918
919
920
921
...
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
955
956
957
958
959
960
961
...
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
....
1171
1172
1173
1174
1175
1176
1177
1178



1179
1180
1181
1182
1183
1184
1185
....
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
....
1306
1307
1308
1309
1310
1311
1312
1313





1314
1315
1316
1317
1318
1319
1320
....
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
....
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
....
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
....
1731
1732
1733
1734
1735
1736
1737



























































































**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code to implement a very simple search function
** against timeline comments, check-in content, wiki pages, and/or tickets.
**
** The search is full-text like in that it is looking for words and ignores
** punctuation and capitalization.  But it is more akin to "grep" in that
** it scans the entire corpus for the search, and it does not support the
** full functionality of FTS4.









*/
#include "config.h"
#include "search.h"
#include <assert.h>

#if INTERFACE

/* Maximum number of search terms */
#define SEARCH_MAX_TERM   8

/*
** A compiled search pattern
*/
struct Search {
  int nTerm;            /* Number of search terms */
  struct srchTerm {     /* For each search term */
    char *z;               /* Text */
    int n;                 /* length */
  } a[SEARCH_MAX_TERM];
................................................................................
  0, 0, 0, 0, 0, 0, 0, 0,     0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,     0, 0, 0, 0, 0, 0, 0, 0,
};
#define ISALNUM(x)  (!isBoundary[(x)&0xff])


/*
** Destroy a search context.
*/
void search_end(Search *p){
  if( p ){
    fossil_free(p->zPattern);
    fossil_free(p->zMarkBegin);
    fossil_free(p->zMarkEnd);
    fossil_free(p->zMarkGap);
................................................................................
    if( p->iScore ) blob_reset(&p->snip);
    memset(p, 0, sizeof(*p));
    if( p!=&gSearch ) fossil_free(p);
  }
}

/*
** Compile a search pattern
*/
static Search *search_init(
  const char *zPattern,       /* The search pattern */
  const char *zMarkBegin,     /* Start of a match */
  const char *zMarkEnd,       /* End of a match */
  const char *zMarkGap,       /* A gap between two matches */
  unsigned fSrchFlg           /* Flags */
................................................................................
      blob_appendf(pSnip, "%#h", n, zTxt);
    }else{
      blob_append(pSnip, zTxt, n);
    }
  }
}


/*
** Compare a search pattern against one or more input strings which
** collectively comprise a document.  Return a match score.  Any
** postive value means there was a match.  Zero means that one or
** more terms are missing.
**
** The score and a snippet are record for future use.
**
................................................................................
  return score;
}

/*
** COMMAND: test-match
**
** Usage: %fossil test-match SEARCHSTRING FILE1 FILE2 ...



*/
void test_match_cmd(void){
  Search *p;
  int i;
  Blob x;
  int score;
  char *zDoc;
................................................................................
      fossil_print("%.78c\n%s\n%.78c\n\n", '=', blob_str(&p->snip), '=');
    }
  }
  search_end(p);
}

/*
** An SQL function to initialize the global search pattern:
**
**     search_init(PATTERN,BEGIN,END,GAP,FLAGS)
**
** All arguments are optional.




*/
static void search_init_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  const char *zPattern = 0;
................................................................................
  if( zPattern && zPattern[0] ){
    search_init(zPattern, zBegin, zEnd, zGap, flg | SRCHFLG_STATIC);
  }else{
    search_end(&gSearch);
  }
}


/*
** Try to match the input text against the search parameters set up


** by the previous search_init() call.  Remember the results globally.
** Return non-zero on a match and zero on a miss.
*/
static void search_match_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
................................................................................
    azDoc[nDoc] = (const char*)sqlite3_value_text(argv[nDoc]);
    if( azDoc[nDoc]==0 ) azDoc[nDoc] = "";
  }
  rc = search_match(&gSearch, nDoc, azDoc);
  sqlite3_result_int(context, rc);
}

/*
** These SQL functions return the results of the last
** call to the search_match() SQL function.

*/
static void search_score_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  sqlite3_result_int(context, gSearch.iScore);
}





static void search_snippet_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  if( blob_size(&gSearch.snip)>0 ){
    sqlite3_result_text(context, blob_str(&gSearch.snip), -1, fossil_free);
    blob_init(&gSearch.snip, 0, 0);
  }
}


/*
** This is an SQLite function that computes the searchable text.
** It is a wrapper around the search_stext() routine.  See the
** search_stext() routine for further detail.
*/
static void search_stext_sqlfunc(
  sqlite3_context *context,
  int argc,
................................................................................
){
  const char *zType = (const char*)sqlite3_value_text(argv[0]);
  int rid = sqlite3_value_int(argv[1]);
  const char *zName = (const char*)sqlite3_value_text(argv[2]);
  sqlite3_result_text(context, search_stext_cached(zType[0],rid,zName,0), -1,
                      SQLITE_TRANSIENT);
}





static void search_title_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  const char *zType = (const char*)sqlite3_value_text(argv[0]);
  int rid = sqlite3_value_int(argv[1]);
  const char *zName = (const char*)sqlite3_value_text(argv[2]);
  int nHdr;
  char *z = search_stext_cached(zType[0], rid, zName, &nHdr);
  if( nHdr || zType[0]!='d' ){
    sqlite3_result_text(context, z, nHdr, SQLITE_TRANSIENT);
  }else{
    sqlite3_result_value(context, argv[2]);
  }
}





static void search_body_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  const char *zType = (const char*)sqlite3_value_text(argv[0]);
  int rid = sqlite3_value_int(argv[1]);
  const char *zName = (const char*)sqlite3_value_text(argv[2]);
  int nHdr;
  char *z = search_stext_cached(zType[0], rid, zName, &nHdr);
  sqlite3_result_text(context, z+nHdr+1, -1, SQLITE_TRANSIENT);
}


/*
** Encode a string for use as a query parameter in a URL

*/
static void search_urlencode_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  char *z = mprintf("%T",sqlite3_value_text(argv[0]));
  sqlite3_result_text(context, z, -1, fossil_free);
}

/*
** Register the "score()" SQL function to score its input text
** using the given Search object.  Once this function is registered,
** do not delete the Search object.
*/
void search_sql_setup(sqlite3 *db){
  static int once = 0;
  if( once++ ) return;
  sqlite3_create_function(db, "search_match", -1, SQLITE_UTF8, 0,
     search_match_sqlfunc, 0, 0);
  sqlite3_create_function(db, "search_score", 0, SQLITE_UTF8, 0,
................................................................................
     search_urlencode_sqlfunc, 0, 0);
}

/*
** Testing the search function.
**
** COMMAND: search*
** 
** Usage: %fossil search [-all|-a] [-limit|-n #] [-width|-W #] pattern...
**
** Search for timeline entries matching all words provided on the
** command line. Whole-word matches scope more highly than partial
** matches.
**
** Outputs, by default, some top-N fraction of the results. The -all
................................................................................
  }
  return srchFlags & ~knownBad;
}

/*
** When this routine is called, there already exists a table
**
**       x(label,url,score,date,snip).
**






** And the srchFlags parameter has been validated.  This routine
** fills the X table with search results using a full-text scan.
**
** The companion indexed scan routine is search_indexed().
*/
static void search_fullscan(
  const char *zPattern,       /* The query pattern */
  unsigned int srchFlags      /* What to search over */
){
  search_init(zPattern, "<mark>", "</mark>", " ... ",
          SRCHFLG_STATIC|SRCHFLG_HTML);
................................................................................
  sqlite3_result_double(context, r);
#endif
}

/*
** When this routine is called, there already exists a table
**
**       x(label,url,score,date,snip).
**






** And the srchFlags parameter has been validated.  This routine
** fills the X table with search results using a index scan.
**
** The companion full-text scan routine is search_fullscan().
*/
static void search_indexed(
  const char *zPattern,       /* The query pattern */
  unsigned int srchFlags      /* What to search over */
){
  Blob sql;
  if( srchFlags==0 ) return;
................................................................................
}


/*
** This routine generates web-page output for a search operation.
** Other web-pages can invoke this routine to add search results
** in the middle of the page.




**
** Return the number of rows.
*/
int search_run_and_output(
  const char *zPattern,       /* The query pattern */
  unsigned int srchFlags,     /* What to search over */
  int fDebug                  /* Extra debugging output */
................................................................................
  if( srchFlags==0 ) return 0;
  search_sql_setup(g.db);
  add_content_sql_commands(g.db);
  db_multi_exec(
    "CREATE TEMP TABLE x(label,url,score,id,date,snip);"
  );
  if( !search_index_exists() ){
    search_fullscan(zPattern, srchFlags);
  }else{
    search_update_index(srchFlags);
    search_indexed(zPattern, srchFlags);
  }
  db_prepare(&q, "SELECT url, snip, label, score, id"
                 "  FROM x"
                 " ORDER BY score DESC, date DESC;");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUrl = db_column_text(&q, 0);
    const char *zSnippet = db_column_text(&q, 1);
................................................................................
    const char *zLabel = db_column_text(&q, 2);
    if( nRow==0 ){
      @ <ol>
    }
    nRow++;
    @ <li><p><a href='%R%s(zUrl)'>%h(zLabel)</a>
    if( fDebug ){
      @ (%e(db_column_double(&q,3)), %s(db_column_text(&q,4)))
    }
    @ <br><span class='snippet'>%z(cleanSnippet(zSnippet))</span></li>
  }
  db_finalize(&q);
  if( nRow ){
    @ </ol>
  }
  return nRow;
}
................................................................................
    zDisable2 = " disabled";
    zPattern = "";
  }else{
    zDisable1 = " autofocus";
    zDisable2 = "";
    zPattern = PD("s","");
  }
  @ <form method='GET' action='%R/%t(g.zPath)'>
  if( zClass ){
    @ <div class='searchForm searchForm%s(zClass)'>
  }else{
    @ <div class='searchForm'>
  }
  @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable1)>
  if( useYparam && (srchFlags & (srchFlags-1))!=0 && useYparam ){
................................................................................
**                      w      Wiki page
**                      c      Check-in comment
**                      t      Ticket text
**
**    rid               The RID of an artifact that defines the object
**                      being searched.
**
**    zName             Name of the object being searched.



*/
void search_stext(
  char cType,            /* Type of document */
  int rid,               /* BLOB.RID or TAG.TAGID value for document */
  const char *zName,     /* Auxiliary information */
  Blob *pOut             /* OUT: Initialize to the search text */
){
................................................................................
** for the same document return the same pointer.  The returned pointer
** is valid until the next invocation of this routine.  Call this routine
** with an eType of 0 to clear the cache.
*/
char *search_stext_cached(
  char cType,            /* Type of document */
  int rid,               /* BLOB.RID or TAG.TAGID value for document */
  const char *zName,     /* Auxiliary information */
  int *pnTitle           /* OUT: length of title in bytes excluding \n */
){
  static struct {
    Blob stext;          /* Cached search text */
    char cType;          /* The type */
    int rid;             /* The RID */
    int nTitle;          /* Number of bytes in title */
................................................................................
  if( pnTitle ) *pnTitle = cache.nTitle;
  return blob_str(&cache.stext);
}

/*
** COMMAND: test-search-stext
**
** Usage: fossil test-search-stext TYPE ARG1 ARG2





*/
void test_search_stext(void){
  Blob out;
  db_find_and_open_repository(0,0);
  if( g.argc!=5 ) usage("TYPE RID NAME");
  search_stext(g.argv[2][0], atoi(g.argv[3]), g.argv[4], &out);
  fossil_print("%s\n",blob_str(&out));
................................................................................
  blob_reset(&out);
}

/* The schema for the full-text index
*/
static const char zFtsSchema[] =
@ -- One entry for each possible search result
@ CREATE TABLE IF NOT EXISTS "%w".ftsdocs(
@   rowid INTEGER PRIMARY KEY, -- Maps to the ftsidx.docid
@   type CHAR(1),              -- Type of document
@   rid INTEGER,               -- BLOB.RID or TAG.TAGID for the document
@   name TEXT,                 -- Additional document description
@   idxed BOOLEAN,             -- True if currently in the index
@   label TEXT,                -- Label to print on search results
@   url TEXT,                  -- URL to access this document
@   mtime DATE,                -- Date when document created
@   bx TEXT,                   -- Temporary "body" content cache
@   UNIQUE(type,rid)
@ );
@ CREATE INDEX "%w".ftsdocIdxed ON ftsdocs(type,rid,name) WHERE idxed==0;
@ CREATE INDEX "%w".ftsdocName ON ftsdocs(name) WHERE type='w';
@ CREATE VIEW IF NOT EXISTS "%w".ftscontent AS
@   SELECT rowid, type, rid, name, idxed, label, url, mtime,
@          title(type,rid,name) AS 'title', body(type,rid,name) AS 'body'
@     FROM ftsdocs;
@ CREATE VIRTUAL TABLE IF NOT EXISTS "%w".ftsidx
@   USING fts4(content="ftscontent", title, body%s);
;
static const char zFtsDrop[] =
@ DROP TABLE IF EXISTS "%w".ftsidx;
@ DROP VIEW IF EXISTS "%w".ftscontent;
@ DROP TABLE IF EXISTS "%w".ftsdocs;
;

/*
** Create or drop the tables associated with a full-text index.
*/
static int searchIdxExists = -1;
void search_create_index(void){
  const char *zDb = db_name("repository");
  int useStemmer = db_get_boolean("search-stemmer",0);
  const char *zExtra = useStemmer ? ",tokenize=porter" : "";
  search_sql_setup(g.db);
  db_multi_exec(zFtsSchema/*works-like:"%w%w%w%w%w%s"*/,
       zDb, zDb, zDb, zDb, zDb, zExtra/*safe-for-%s*/);
  searchIdxExists = 1;
}
void search_drop_index(void){
  const char *zDb = db_name("repository");
  db_multi_exec(zFtsDrop/*works-like:"%w%w%w"*/, zDb, zDb, zDb);
  searchIdxExists = 0;
}

/*
** Return true if the full-text search index exists
*/
int search_index_exists(void){
................................................................................
** check-ins, then update all 'd' entries in FTSDOCS that have
** changed.
*/
static void search_update_doc_index(void){
  const char *zDocBr = db_get("doc-branch","trunk");
  int ckid = zDocBr ? symbolic_name_to_rid(zDocBr,"ci") : 0;
  double rTime;
  char *zBrUuid;
  if( ckid==0 ) return;
  if( !db_exists("SELECT 1 FROM ftsdocs WHERE type='c' AND rid=%d"
                 "   AND NOT idxed", ckid) ) return;

  /* If we get this far, it means that changes to 'd' entries are
  ** required. */
  rTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", ckid);
  zBrUuid = db_text("","SELECT substr(uuid,1,20) FROM blob WHERE rid=%d",ckid);
  db_multi_exec(
    "CREATE TEMP TABLE current_docs(rid INTEGER PRIMARY KEY, name);"
    "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
    "INSERT OR IGNORE INTO current_docs(rid, name)"
    "  SELECT blob.rid, foci.filename FROM foci, blob"
    "   WHERE foci.checkinID=%d AND blob.uuid=foci.uuid"
    "     AND %z",
................................................................................
    "      AND rid NOT IN (SELECT rid FROM current_docs)"
  );
  db_multi_exec(
    "INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed,label,bx,url,mtime)"
    "  SELECT 'd', rid, name, 0,"
    "         title('d',rid,name),"
    "         body('d',rid,name),"
    "         printf('/doc/%q/%%s',urlencode(name)),"
    "         %.17g"
    " FROM current_docs",
    zBrUuid, rTime
  );
  db_multi_exec(
    "INSERT INTO ftsidx(docid,title,body)"
    "  SELECT rowid, label, bx FROM ftsdocs WHERE type='d' AND NOT idxed"
  );
  db_multi_exec(
    "UPDATE ftsdocs SET"
................................................................................
    fossil_print("%-16s %d\n", "documents:",
       db_int(0, "SELECT count(*) FROM ftsdocs"));
  }else{
    fossil_print("%-16s disabled\n", "full-text index:");
  }
  db_end_transaction(0);
}


































































































|


|
|
|
|
>
>
>
>
>
>
>
>
>







|



|







 







|







 







|







 







>
|







 







>
>
>







 







|



|
>
>
>
>







 







>
|
<
>
>
|







 







|
|
|
>








>
>
>
>
>











>
|







 







>
>
>
>
>








|







>
>
>
>
>








|




>
|
|
>











|
|
<







 







|







 







|

>
>
>
>
>
>

|

|







 







|

>
>
>
>
>
>

|

|







 







>
>
>
>







 







|

|
|







 







|

|







 







|







 







|
>
>
>







 







|







 







|
>
>
>
>
>







 







|











|
|
|



|



|
|
|







<



|
<



<
|







 







<







<







 







|


|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
..
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
...
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
...
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
...
401
402
403
404
405
406
407
408
409

410
411
412
413
414
415
416
417
418
419
...
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
...
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531

532
533
534
535
536
537
538
...
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
...
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
...
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
...
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
...
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
...
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
....
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
....
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
....
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
....
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
....
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440

1441
1442
1443
1444

1445
1446
1447

1448
1449
1450
1451
1452
1453
1454
1455
....
1530
1531
1532
1533
1534
1535
1536

1537
1538
1539
1540
1541
1542
1543

1544
1545
1546
1547
1548
1549
1550
....
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
....
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code to implement a search functions
** against timeline comments, check-in content, wiki pages, and/or tickets.
**
** The search can be either a per-query "grep"-like search that scans
** the entire corpus.  Or it can use the FTS4 or FTS5 search engine of
** SQLite.  The choice is a administrator configuration option.
**
** The first option is referred to as "full-scan search".  The second
** option is called "indexed search".
**
** The code in this file is ordered approximately as follows:
**
**    (1) The full-scan search engine
**    (2) The indexed search engine
**    (3) Higher level interfaces that use either (1) or (b2) according
**        to the current search configuration settings
*/
#include "config.h"
#include "search.h"
#include <assert.h>

#if INTERFACE

/* Maximum number of search terms for full-scan search */
#define SEARCH_MAX_TERM   8

/*
** A compiled search pattern used for full-scan search.
*/
struct Search {
  int nTerm;            /* Number of search terms */
  struct srchTerm {     /* For each search term */
    char *z;               /* Text */
    int n;                 /* length */
  } a[SEARCH_MAX_TERM];
................................................................................
  0, 0, 0, 0, 0, 0, 0, 0,     0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,     0, 0, 0, 0, 0, 0, 0, 0,
};
#define ISALNUM(x)  (!isBoundary[(x)&0xff])


/*
** Destroy a full-scan search context.
*/
void search_end(Search *p){
  if( p ){
    fossil_free(p->zPattern);
    fossil_free(p->zMarkBegin);
    fossil_free(p->zMarkEnd);
    fossil_free(p->zMarkGap);
................................................................................
    if( p->iScore ) blob_reset(&p->snip);
    memset(p, 0, sizeof(*p));
    if( p!=&gSearch ) fossil_free(p);
  }
}

/*
** Compile a full-scan search pattern
*/
static Search *search_init(
  const char *zPattern,       /* The search pattern */
  const char *zMarkBegin,     /* Start of a match */
  const char *zMarkEnd,       /* End of a match */
  const char *zMarkGap,       /* A gap between two matches */
  unsigned fSrchFlg           /* Flags */
................................................................................
      blob_appendf(pSnip, "%#h", n, zTxt);
    }else{
      blob_append(pSnip, zTxt, n);
    }
  }
}

/* This the core search engine for full-scan search.
**
** Compare a search pattern against one or more input strings which
** collectively comprise a document.  Return a match score.  Any
** postive value means there was a match.  Zero means that one or
** more terms are missing.
**
** The score and a snippet are record for future use.
**
................................................................................
  return score;
}

/*
** COMMAND: test-match
**
** Usage: %fossil test-match SEARCHSTRING FILE1 FILE2 ...
**
** Run the full-scan search algorithm using SEARCHSTRING against
** the text of the files listed.  Output matches and snippets.
*/
void test_match_cmd(void){
  Search *p;
  int i;
  Blob x;
  int score;
  char *zDoc;
................................................................................
      fossil_print("%.78c\n%s\n%.78c\n\n", '=', blob_str(&p->snip), '=');
    }
  }
  search_end(p);
}

/*
** An SQL function to initialize the full-scan search pattern:
**
**     search_init(PATTERN,BEGIN,END,GAP,FLAGS)
**
** All arguments are optional.  PATTERN is the search pattern.  If it
** is omitted, then the global search pattern is reset.  BEGIN and END
** and GAP are the strings used to construct snippets.  FLAGS is an
** integer bit pattern containing the various SRCH_CKIN, SRCH_DOC,
** SRCH_TKT, or SRCH_ALL bits to determine what is to be searched.
*/
static void search_init_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  const char *zPattern = 0;
................................................................................
  if( zPattern && zPattern[0] ){
    search_init(zPattern, zBegin, zEnd, zGap, flg | SRCHFLG_STATIC);
  }else{
    search_end(&gSearch);
  }
}

/*     search_match(TEXT, TEXT, ....)
**

** Using the full-scan search engine created by the most recent call
** to search_init(), match the input the TEXT arguments.
** Remember the results global full-scan search object. 
** Return non-zero on a match and zero on a miss.
*/
static void search_match_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
................................................................................
    azDoc[nDoc] = (const char*)sqlite3_value_text(argv[nDoc]);
    if( azDoc[nDoc]==0 ) azDoc[nDoc] = "";
  }
  rc = search_match(&gSearch, nDoc, azDoc);
  sqlite3_result_int(context, rc);
}


/*      search_score()
**
** Return the match score for the last successful search_match call.
*/
static void search_score_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  sqlite3_result_int(context, gSearch.iScore);
}

/*      search_snippet()
**
** Return a snippet for the last successful search_match() call.
*/
static void search_snippet_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  if( blob_size(&gSearch.snip)>0 ){
    sqlite3_result_text(context, blob_str(&gSearch.snip), -1, fossil_free);
    blob_init(&gSearch.snip, 0, 0);
  }
}

/*       stext(TYPE, RID, ARG)
**
** This is an SQLite function that computes the searchable text.
** It is a wrapper around the search_stext() routine.  See the
** search_stext() routine for further detail.
*/
static void search_stext_sqlfunc(
  sqlite3_context *context,
  int argc,
................................................................................
){
  const char *zType = (const char*)sqlite3_value_text(argv[0]);
  int rid = sqlite3_value_int(argv[1]);
  const char *zName = (const char*)sqlite3_value_text(argv[2]);
  sqlite3_result_text(context, search_stext_cached(zType[0],rid,zName,0), -1,
                      SQLITE_TRANSIENT);
}

/*       title(TYPE, RID, ARG)
**
** Return the title of the document to be search.
*/
static void search_title_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  const char *zType = (const char*)sqlite3_value_text(argv[0]);
  int rid = sqlite3_value_int(argv[1]);
  const char *zName = (const char*)sqlite3_value_text(argv[2]);
  int nHdr = 0;
  char *z = search_stext_cached(zType[0], rid, zName, &nHdr);
  if( nHdr || zType[0]!='d' ){
    sqlite3_result_text(context, z, nHdr, SQLITE_TRANSIENT);
  }else{
    sqlite3_result_value(context, argv[2]);
  }
}

/*       body(TYPE, RID, ARG)
**
** Return the body of the document to be search.
*/
static void search_body_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  const char *zType = (const char*)sqlite3_value_text(argv[0]);
  int rid = sqlite3_value_int(argv[1]);
  const char *zName = (const char*)sqlite3_value_text(argv[2]);
  int nHdr = 0;
  char *z = search_stext_cached(zType[0], rid, zName, &nHdr);
  sqlite3_result_text(context, z+nHdr+1, -1, SQLITE_TRANSIENT);
}

/*      urlencode(X)
**
** Encode a string for use as a query parameter in a URL.  This is
** the equivalent of printf("%T",X).
*/
static void search_urlencode_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  char *z = mprintf("%T",sqlite3_value_text(argv[0]));
  sqlite3_result_text(context, z, -1, fossil_free);
}

/*
** Register the various SQL functions (defined above) needed to implement
** full-scan search.

*/
void search_sql_setup(sqlite3 *db){
  static int once = 0;
  if( once++ ) return;
  sqlite3_create_function(db, "search_match", -1, SQLITE_UTF8, 0,
     search_match_sqlfunc, 0, 0);
  sqlite3_create_function(db, "search_score", 0, SQLITE_UTF8, 0,
................................................................................
     search_urlencode_sqlfunc, 0, 0);
}

/*
** Testing the search function.
**
** COMMAND: search*
**
** Usage: %fossil search [-all|-a] [-limit|-n #] [-width|-W #] pattern...
**
** Search for timeline entries matching all words provided on the
** command line. Whole-word matches scope more highly than partial
** matches.
**
** Outputs, by default, some top-N fraction of the results. The -all
................................................................................
  }
  return srchFlags & ~knownBad;
}

/*
** When this routine is called, there already exists a table
**
**       x(label,url,score,id,snip).
**
** label:  The "name" of the document containing the match
** url:    A URL for the document
** score:  How well the document matched
** id:     The document id.  Format: xNNNNN, x: type, N: number
** snip:   A snippet for the match
**
** And the srchFlags parameter has been validated.  This routine
** fills the X table with search results using a full-scan search.
**
** The companion indexed search routine is search_indexed().
*/
static void search_fullscan(
  const char *zPattern,       /* The query pattern */
  unsigned int srchFlags      /* What to search over */
){
  search_init(zPattern, "<mark>", "</mark>", " ... ",
          SRCHFLG_STATIC|SRCHFLG_HTML);
................................................................................
  sqlite3_result_double(context, r);
#endif
}

/*
** When this routine is called, there already exists a table
**
**       x(label,url,score,id,snip).
**
** label:  The "name" of the document containing the match
** url:    A URL for the document
** score:  How well the document matched
** id:     The document id.  Format: xNNNNN, x: type, N: number
** snip:   A snippet for the match
**
** And the srchFlags parameter has been validated.  This routine
** fills the X table with search results using FTS indexed search.
**
** The companion full-scan search routine is search_fullscan().
*/
static void search_indexed(
  const char *zPattern,       /* The query pattern */
  unsigned int srchFlags      /* What to search over */
){
  Blob sql;
  if( srchFlags==0 ) return;
................................................................................
}


/*
** This routine generates web-page output for a search operation.
** Other web-pages can invoke this routine to add search results
** in the middle of the page.
**
** This routine works for both full-scan and indexed search.  The
** appropriate low-level search routine is called according to the
** current configuration.
**
** Return the number of rows.
*/
int search_run_and_output(
  const char *zPattern,       /* The query pattern */
  unsigned int srchFlags,     /* What to search over */
  int fDebug                  /* Extra debugging output */
................................................................................
  if( srchFlags==0 ) return 0;
  search_sql_setup(g.db);
  add_content_sql_commands(g.db);
  db_multi_exec(
    "CREATE TEMP TABLE x(label,url,score,id,date,snip);"
  );
  if( !search_index_exists() ){
    search_fullscan(zPattern, srchFlags);  /* Full-scan search */
  }else{
    search_update_index(srchFlags);        /* Update the index, if necessary */
    search_indexed(zPattern, srchFlags);   /* Indexed search */
  }
  db_prepare(&q, "SELECT url, snip, label, score, id"
                 "  FROM x"
                 " ORDER BY score DESC, date DESC;");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUrl = db_column_text(&q, 0);
    const char *zSnippet = db_column_text(&q, 1);
................................................................................
    const char *zLabel = db_column_text(&q, 2);
    if( nRow==0 ){
      @ <ol>
    }
    nRow++;
    @ <li><p><a href='%R%s(zUrl)'>%h(zLabel)</a>
    if( fDebug ){
      @ (%e(db_column_double(&q,3)), %s(db_column_text(&q,4))
    }
    @ <br /><span class='snippet'>%z(cleanSnippet(zSnippet))</span></li>
  }
  db_finalize(&q);
  if( nRow ){
    @ </ol>
  }
  return nRow;
}
................................................................................
    zDisable2 = " disabled";
    zPattern = "";
  }else{
    zDisable1 = " autofocus";
    zDisable2 = "";
    zPattern = PD("s","");
  }
  @ <form method='GET' action='%R/%T(g.zPath)'>
  if( zClass ){
    @ <div class='searchForm searchForm%s(zClass)'>
  }else{
    @ <div class='searchForm'>
  }
  @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable1)>
  if( useYparam && (srchFlags & (srchFlags-1))!=0 && useYparam ){
................................................................................
**                      w      Wiki page
**                      c      Check-in comment
**                      t      Ticket text
**
**    rid               The RID of an artifact that defines the object
**                      being searched.
**
**    zName             Name of the object being searched.  This is used
**                      only to help figure out the mimetype (text/plain,
**                      test/html, test/x-fossil-wiki, or text/x-markdown)
**                      so that the code can know how to simplify the text.
*/
void search_stext(
  char cType,            /* Type of document */
  int rid,               /* BLOB.RID or TAG.TAGID value for document */
  const char *zName,     /* Auxiliary information */
  Blob *pOut             /* OUT: Initialize to the search text */
){
................................................................................
** for the same document return the same pointer.  The returned pointer
** is valid until the next invocation of this routine.  Call this routine
** with an eType of 0 to clear the cache.
*/
char *search_stext_cached(
  char cType,            /* Type of document */
  int rid,               /* BLOB.RID or TAG.TAGID value for document */
  const char *zName,     /* Auxiliary information, for mimetype */
  int *pnTitle           /* OUT: length of title in bytes excluding \n */
){
  static struct {
    Blob stext;          /* Cached search text */
    char cType;          /* The type */
    int rid;             /* The RID */
    int nTitle;          /* Number of bytes in title */
................................................................................
  if( pnTitle ) *pnTitle = cache.nTitle;
  return blob_str(&cache.stext);
}

/*
** COMMAND: test-search-stext
**
** Usage: fossil test-search-stext TYPE RID NAME
**
** Compute the search text for document TYPE-RID whose name is NAME.
** The TYPE is one of "c", "d", "t", or "w".  The RID is the document
** ID.  The NAME is used to figure out a mimetype to use for formatting
** the raw document text.
*/
void test_search_stext(void){
  Blob out;
  db_find_and_open_repository(0,0);
  if( g.argc!=5 ) usage("TYPE RID NAME");
  search_stext(g.argv[2][0], atoi(g.argv[3]), g.argv[4], &out);
  fossil_print("%s\n",blob_str(&out));
................................................................................
  blob_reset(&out);
}

/* The schema for the full-text index
*/
static const char zFtsSchema[] =
@ -- One entry for each possible search result
@ CREATE TABLE IF NOT EXISTS repository.ftsdocs(
@   rowid INTEGER PRIMARY KEY, -- Maps to the ftsidx.docid
@   type CHAR(1),              -- Type of document
@   rid INTEGER,               -- BLOB.RID or TAG.TAGID for the document
@   name TEXT,                 -- Additional document description
@   idxed BOOLEAN,             -- True if currently in the index
@   label TEXT,                -- Label to print on search results
@   url TEXT,                  -- URL to access this document
@   mtime DATE,                -- Date when document created
@   bx TEXT,                   -- Temporary "body" content cache
@   UNIQUE(type,rid)
@ );
@ CREATE INDEX repository.ftsdocIdxed ON ftsdocs(type,rid,name) WHERE idxed==0;
@ CREATE INDEX repository.ftsdocName ON ftsdocs(name) WHERE type='w';
@ CREATE VIEW IF NOT EXISTS repository.ftscontent AS
@   SELECT rowid, type, rid, name, idxed, label, url, mtime,
@          title(type,rid,name) AS 'title', body(type,rid,name) AS 'body'
@     FROM ftsdocs;
@ CREATE VIRTUAL TABLE IF NOT EXISTS repository.ftsidx
@   USING fts4(content="ftscontent", title, body%s);
;
static const char zFtsDrop[] =
@ DROP TABLE IF EXISTS repository.ftsidx;
@ DROP VIEW IF EXISTS repository.ftscontent;
@ DROP TABLE IF EXISTS repository.ftsdocs;
;

/*
** Create or drop the tables associated with a full-text index.
*/
static int searchIdxExists = -1;
void search_create_index(void){

  int useStemmer = db_get_boolean("search-stemmer",0);
  const char *zExtra = useStemmer ? ",tokenize=porter" : "";
  search_sql_setup(g.db);
  db_multi_exec(zFtsSchema/*works-like:"%s"*/, zExtra/*safe-for-%s*/);

  searchIdxExists = 1;
}
void search_drop_index(void){

  db_multi_exec(zFtsDrop/*works-like:""*/);
  searchIdxExists = 0;
}

/*
** Return true if the full-text search index exists
*/
int search_index_exists(void){
................................................................................
** check-ins, then update all 'd' entries in FTSDOCS that have
** changed.
*/
static void search_update_doc_index(void){
  const char *zDocBr = db_get("doc-branch","trunk");
  int ckid = zDocBr ? symbolic_name_to_rid(zDocBr,"ci") : 0;
  double rTime;

  if( ckid==0 ) return;
  if( !db_exists("SELECT 1 FROM ftsdocs WHERE type='c' AND rid=%d"
                 "   AND NOT idxed", ckid) ) return;

  /* If we get this far, it means that changes to 'd' entries are
  ** required. */
  rTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", ckid);

  db_multi_exec(
    "CREATE TEMP TABLE current_docs(rid INTEGER PRIMARY KEY, name);"
    "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
    "INSERT OR IGNORE INTO current_docs(rid, name)"
    "  SELECT blob.rid, foci.filename FROM foci, blob"
    "   WHERE foci.checkinID=%d AND blob.uuid=foci.uuid"
    "     AND %z",
................................................................................
    "      AND rid NOT IN (SELECT rid FROM current_docs)"
  );
  db_multi_exec(
    "INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed,label,bx,url,mtime)"
    "  SELECT 'd', rid, name, 0,"
    "         title('d',rid,name),"
    "         body('d',rid,name),"
    "         printf('/doc/%T/%%s',urlencode(name)),"
    "         %.17g"
    " FROM current_docs",
    zDocBr, rTime
  );
  db_multi_exec(
    "INSERT INTO ftsidx(docid,title,body)"
    "  SELECT rowid, label, bx FROM ftsdocs WHERE type='d' AND NOT idxed"
  );
  db_multi_exec(
    "UPDATE ftsdocs SET"
................................................................................
    fossil_print("%-16s %d\n", "documents:",
       db_int(0, "SELECT count(*) FROM ftsdocs"));
  }else{
    fossil_print("%-16s disabled\n", "full-text index:");
  }
  db_end_transaction(0);
}

/*
** WEBPAGE: test-ftsdocs
**
** Show a table of all documents currently in the search index.
*/
void search_data_page(void){
  Stmt q;
  const char *zId = P("id");
  const char *zType = P("y");
  const char *zIdxed = P("ixed");
  int id;
  int cnt = 0;
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }
  if( !search_index_exists() ){
    @ <p>Indexed search is disabled
    style_footer();
    return;
  }
  if( zId!=0 && (id = atoi(zId))>0 ){
    /* Show information about a single ftsdocs entry */
    style_header("Information about ftsdoc entry %d", id);
    db_prepare(&q,
      "SELECT type||rid, name, idxed, label, url, datetime(mtime)"
      "  FROM ftsdocs WHERE rowid=%d", id
    );
    if( db_step(&q)==SQLITE_ROW ){
      const char *zUrl = db_column_text(&q,4);
      @ <table border=0>
      @ <tr><td align='right'>rowid:<td>&nbsp;&nbsp;<td>%d(id)
      @ <tr><td align='right'>id:<td><td>%s(db_column_text(&q,0))
      @ <tr><td align='right'>name:<td><td>%h(db_column_text(&q,1))
      @ <tr><td align='right'>idxed:<td><td>%d(db_column_int(&q,2))
      @ <tr><td align='right'>label:<td><td>%h(db_column_text(&q,3))
      @ <tr><td align='right'>url:<td><td>
      @ <a href='%R%s(zUrl)'>%h(zUrl)</a>
      @ <tr><td align='right'>mtime:<td><td>%s(db_column_text(&q,5))
      @ </table>
    }
    db_finalize(&q);
    style_footer();
    return;
  }
  if( zType!=0 && zType[0]!=0 && zType[1]==0 &&
      zIdxed!=0 && (zIdxed[0]=='1' || zIdxed[0]=='0') && zIdxed[1]==0
  ){
    int ixed = zIdxed[0]=='1';
    style_header("List of '%c' documents that are%s indexed",
                 zType[0], ixed ? "" : " not");
    db_prepare(&q,
      "SELECT rowid, type||rid ||' '|| coalesce(label,'')"
      "  FROM ftsdocs WHERE type='%c' AND %s idxed",
      zType[0], ixed ? "" : "NOT"
    );
    @ <ul>
    while( db_step(&q)==SQLITE_ROW ){
      @ <li> <a href='test-ftsdocs?id=%d(db_column_int(&q,0))'>
      @ %h(db_column_text(&q,1))</a>
    }
    @ </ul>
    db_finalize(&q);
    style_footer();
    return;
  }
  style_header("Summary of ftsdocs");
  db_prepare(&q,
     "SELECT type, idxed, count(*) FROM ftsdocs"
     " GROUP BY 1, 2 ORDER BY 3 DESC"
  );
  @ <table border=1 cellpadding=3 cellspacing=0>
  @ <thead>
  @ <tr><th>Type<th>Indexed?<th>Count<th>Link
  @ </thead>
  @ <tbody>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zType = db_column_text(&q,0);
    int idxed = db_column_int(&q,1);
    int n = db_column_int(&q,2);
    @ <tr><td>%h(zType)<td>%d(idxed)
    @ <td>%d(n)
    @ <td><a href='test-ftsdocs?y=%s(zType)&ixed=%d(idxed)'>listing</a>
    @ </tr>
    cnt += n;
  }
  @ </tbody><tfooter>
  @ <tr><th>Total<th><th>%d(cnt)<th>
  @ </tfooter>
  @ </table>
  style_footer();
}

Changes to src/setup.c.

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
...
113
114
115
116
117
118
119


120
121
122
123
124
125
126
127
128
129


130
131
132
133
134
135
136
...
156
157
158
159
160
161
162



163
164
165
166
167
168
169
170
...
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
...
295
296
297
298
299
300
301


302
303
304
305

306











307
308
309
310
311
312
313
...
314
315
316
317
318
319
320







321
322
323
324
325
326
327



328
329
330
331











332
333
334
335
336
337
338
...
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
...
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
...
683
684
685
686
687
688
689



690
691
692
693
694
695
696
...
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
....
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
....
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
....
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
....
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
....
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
....
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
....
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
#include <assert.h>
#include "setup.h"

#if INTERFACE
#define ArraySize(x) (sizeof(x)/sizeof(x[0]))
#endif

/*
** The table of web pages supported by this application is generated
** automatically by the "mkindex" program and written into a file
** named "page_index.h".  We include that file here to get access
** to the table.
*/
#include "page_index.h"

/*
** Output a single entry for a menu generated using an HTML table.
** If zLink is not NULL or an empty string, then it is the page that
** the menu entry will hyperlink to.  If zLink is NULL or "", then
** the menu entry has no hyperlink - it is disabled.
*/
void setup_menu_entry(
................................................................................
  setup_menu_entry("Skins", "setup_skin",
    "Select and/or modify the web interface \"skins\"");
  setup_menu_entry("Moderation", "setup_modreq",
    "Enable/Disable requiring moderator approval of Wiki and/or Ticket"
    " changes and attachments.");
  setup_menu_entry("Ad-Unit", "setup_adunit",
    "Edit HTML text for an ad unit inserted after the menu bar");


  setup_menu_entry("Logo", "setup_logo",
    "Change the logo and background images for the server");
  setup_menu_entry("Shunned", "shun",
    "Show artifacts that are shunned by this repository");
  setup_menu_entry("Artifact Receipts Log", "rcvfromlist",
    "A record of received artifacts and their sources");
  setup_menu_entry("User Log", "access_log",
    "A record of login attempts");
  setup_menu_entry("Administrative Log", "admin_log",
    "View the admin_log entries");


  setup_menu_entry("Stats", "stat",
    "Repository Status Reports");
  setup_menu_entry("Sitemap", "sitemap",
    "Links to miscellaneous pages");
  setup_menu_entry("SQL", "admin_sql",
    "Enter raw SQL commands");
  setup_menu_entry("TH1", "admin_th1",
................................................................................
  }

  style_submenu_element("Add", "Add User", "setup_uedit");
  style_submenu_element("Log", "Access Log", "access_log");
  style_submenu_element("Help", "Help", "setup_ulist_notes");
  style_header("User List");
  @ <table border=1 cellpadding=2 cellspacing=0 class='userTable'>



  @ <thead><tr><th>UID <th>Category <th>Capabilities <th>Info <th>Last Change</tr></thead>
  @ <tbody>
  db_prepare(&s,
     "SELECT uid, login, cap, date(mtime,'unixepoch')"
     "  FROM user"
     " WHERE login IN ('anonymous','nobody','developer','reader')"
     " ORDER BY login"
  );
................................................................................
    const char *zLogin = db_column_text(&s, 1);
    const char *zCap = db_column_text(&s, 2);
    const char *zDate = db_column_text(&s, 4);
    @ <tr>
    @ <td><a href='setup_uedit?id=%d(uid)'>%d(uid)</a>
    @ <td><a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a>
    @ <td>%h(zCap)
    
    if( fossil_strcmp(zLogin,"anonymous")==0 ){
      @ <td>All logged-in users
    }else if( fossil_strcmp(zLogin,"developer")==0 ){
      @ <td>Users with '<b>v</b>' capability
    }else if( fossil_strcmp(zLogin,"nobody")==0 ){
      @ <td>All users without login
    }else if( fossil_strcmp(zLogin,"reader")==0 ){
................................................................................
  @ </tbody></table>
  db_finalize(&s);
  output_table_sorting_javascript("userlist","nktxTT",2);
  style_footer();
}

/*
** WEBPAGE: setup_ulist_notes
**
** A documentation page showing notes about user configuration.  This information
** used to be a side-bar on the user list page, but has been factored out for
** improved presentation.
*/
void setup_ulist_notes(void){
  style_header("User Configuration Notes");
  @ <h1>User Configuration Notes:</h1>
  @ <ol>
  @ <li><p>The permission flags are as follows:</p>
  @ <table>
     @ <tr><th valign="top">a</th>
     @   <td><i>Admin:</i> Create and delete users</td></tr>
     @ <tr><th valign="top">b</th>
     @   <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr>
     @ <tr><th valign="top">c</th>
     @   <td><i>Append-Tkt:</i> Append to tickets</td></tr>
................................................................................
     @ <tr><th valign="top">v</th>
     @   <td><i>Developer:</i> Inherit privileges of
     @   user <tt>developer</tt></td></tr>
     @ <tr><th valign="top">w</th>
     @   <td><i>Write-Tkt:</i> Edit tickets</td></tr>
     @ <tr><th valign="top">x</th>
     @   <td><i>Private:</i> Push and/or pull private branches</td></tr>


     @ <tr><th valign="top">z</th>
     @   <td><i>Zip download:</i> Download a ZIP archive or tarball</td></tr>
  @ </table>
  @ </li>

  @











  @ <li><p>
  @ Every user, logged in or not, inherits the privileges of
  @ <span class="usertype">nobody</span>.
  @ </p></li>
  @
  @ <li><p>
  @ Any human can login as <span class="usertype">anonymous</span> since the
................................................................................
  @ password is clearly displayed on the login page for them to type. The
  @ purpose of requiring anonymous to log in is to prevent access by spiders.
  @ Every logged-in user inherits the combined privileges of
  @ <span class="usertype">anonymous</span> and
  @ <span class="usertype">nobody</span>.
  @ </p></li>
  @







  @ <li><p>
  @ Users with privilege <span class="capability">v</span> inherit the combined
  @ privileges of <span class="usertype">developer</span>,
  @ <span class="usertype">anonymous</span>, and
  @ <span class="usertype">nobody</span>.
  @ </p></li>
  @



  @ </ol>
  style_footer();
}













/*
** Return true if zPw is a valid password string.  A valid
** password string is:
**
**  (1)  A zero-length string, or
**  (2)  a string that contains a character other than '*'.
................................................................................
    for(i=0; zCap[i]; i++){
      char c = zCap[i];
      if( c>='a' && c<='z' ) oa[c&0x7f] = " checked=\"checked\"";
    }
  }

  /* figure out inherited permissions */
  memset(inherit, 0, sizeof(inherit));
  if( fossil_strcmp(zLogin, "developer") ){
    char *z1, *z2;
    z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='developer'");
    while( z1 && *z1 ){
      inherit[0x7f & *(z1++)] =
         "<span class=\"ueditInheritDeveloper\"><sub>[D]</sub></span>";
    }
................................................................................
  @ <form action="%s(g.zPath)" method="post"><div>
  login_insert_csrf_secret();
  if( login_is_special(zLogin) ){
    @ <input type="hidden" name="login" value="%s(zLogin)">
    @ <input type="hidden" name="info" value="">
    @ <input type="hidden" name="pw" value="*">
  }
  @ <script type='text/javascript'>
  @ function updateCapabilityString(){
  @   /*
  @   ** This function updates the "#usetupEditCapability" span content
  @   ** with the capabilities selected by the interactive user, based
  @   ** upon the state of the capability checkboxes.
  @   */
  @   try {
................................................................................
  @  Moderate Tickets%s(B('q'))</label><br />
  @  <label><input type="checkbox" name="at"%s(oa['t'])
  @                onchange="updateCapabilityString()" />
  @  Ticket Report%s(B('t'))</label><br />
  @  <label><input type="checkbox" name="ax"%s(oa['x'])
  @                onchange="updateCapabilityString()" />
  @  Private%s(B('x'))</label><br />



  @  <label><input type="checkbox" name="az"%s(oa['z'])
  @                onchange="updateCapabilityString()" />
  @  Download Zip%s(B('z'))</label>
  @ </td></tr>
  @ </table>
  @   </td>
  @ </tr>
................................................................................
    @   <td>&nbsp;</td>
    @   <td><input type="submit" name="submit" value="Apply Changes" /></td>
    @ </tr>
  }
  @ </table>
  @ </div></form>
  @ </div>
  @ <script type='text/javascript'>updateCapabilityString();</script>
  @ <h2>Privileges And Capabilities:</h2>
  @ <ul>
  if( higherUser ){
    @ <li><p class="missingPriv">
    @ User %h(zLogin) has Setup privileges and you only have Admin privileges
    @ so you are not permitted to make changes to %h(zLogin).
    @ </p></li>
................................................................................
  @ <hr />
  onoff_attribute(
      "Enable hyperlinks for \"nobody\" based on User-Agent and Javascript",
      "auto-hyperlink", "autohyperlink", 1, 0);
  @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users
  @ including user "nobody", as long as (1) the User-Agent string in the
  @ HTTP header indicates that the request is coming from an actual human
  @ being and not a a robot or spider and (2) the user agent is able to
  @ run Javascript in order to set the href= attribute of hyperlinks.  Bots
  @ and spiders can forge a User-Agent string that makes them seem to be a
  @ normal browser and they can run javascript just like browsers.  But most
  @ bots do not go to that much trouble so this is normally an effective
  @ defense.</p>
  @
  @ <p>You do not normally want a bot to walk your entire repository because
................................................................................
  @ even for relatively small projects.</p>
  @
  @ <p>Additional parameters that control this behavior:</p>
  @ <blockquote>
  onoff_attribute("Enable hyperlinks for humans (as deduced from the UserAgent "
                  " HTTP header string)",
                  "auto-hyperlink-ishuman", "ahis", 0, 0);
  @ <br>
  onoff_attribute("Require mouse movement before enabling hyperlinks",
                  "auto-hyperlink-mouseover", "ahmo", 0, 0);
  @ <br>
  entry_attribute("Delay before enabling hyperlinks (milliseconds)", 5,
                  "auto-hyperlink-delay", "ah-delay", "10", 0);
  @ </blockquote>
  @ <p>Hyperlinks for user "nobody" are normally enabled as soon as the page
  @ finishes loading.  But the first check-box below can be set to require mouse
  @ movement before enabling the links. One can also set a delay prior to enabling
  @ links by enter a positive number of milliseconds in the entry box above.</p>
................................................................................
    @ </table>
    @
    @ <p><form action="%s(g.zTop)/setup_login_group" method="post"><div>
    login_insert_csrf_secret();
    @ To leave this login group press
    @ <input type="submit" value="Leave Login Group" name="leave">
    @ </form></p>
    @ <hr><h2>Implementation Details</h2>
    @ <p>The following are fields from the CONFIG table related to login-groups,
    @ provided here for instructional and debugging purposes:</p>
    @ <table border='1' id='configTab'>
    @ <thead><tr><th>Config.Name<th>Config.Value<th>Config.mtime</tr></thead><tbody>
    db_prepare(&q, "SELECT name, value, datetime(mtime,'unixepoch') FROM config"
                   " WHERE name GLOB 'peer-*'"
                   "    OR name GLOB 'project-*'"
................................................................................

  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }

  (void) aCmdHelp; /* NOTE: Silence compiler warning. */
  style_header("Settings");
  if(!g.repositoryOpen){
    /* Provide read-only access to versioned settings,
       but only if no repo file was explicitly provided. */
    db_open_local(0);
  }
  db_begin_transaction();
................................................................................
  @ </td></tr></table>
  @ </div></form>
  @ <p>Settings marked with (v) are 'versionable' and will be overridden
  @ by the contents of files named <tt>.fossil-settings/PROPERTY</tt>
  @ in the check-out root.
  @ If such a file is present, the corresponding field above is not
  @ editable.</p><hr /><p>
  @ These settings work in the same way, as the <kbd>set</kbd>
  @ commandline:<br />
  @ </p><pre>%s(zHelp_setting_cmd)</pre>
  db_end_transaction(0);
  style_footer();
}

/*
** WEBPAGE: setup_config
**
................................................................................
  @ <p><b>Caution:</b> There are no restrictions on the SQL that can be
  @ run by this page.  You can do serious and irrepairable damage to the
  @ repository.  Proceed with extreme caution.</p>
  @
  @ <p>Only the first statement in the entry box will be run.
  @ Any subsequent statements will be silently ignored.</p>
  @
  @ <p>Database names:<ul><li>repository &rarr; %s(db_name("repository"))
  if( g.zConfigDbName ){
    @ <li>config &rarr; %s(db_name("configdb"))
  }
  if( g.localOpen ){
    @ <li>local-checkout &rarr; %s(db_name("localdb"))
  }
  @ </ul></p>
  @
  @ <form method="post" action="%s(g.zTop)/admin_sql">
  login_insert_csrf_secret();
  @ SQL:<br />
  @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br />
  @ <input type="submit" name="go" value="Run SQL">
  @ <input type="submit" name="schema" value="Show Schema">
  @ <input type="submit" name="tablelist" value="List Tables">
  @ </form>
  if( P("schema") ){
    zQ = sqlite3_mprintf(
            "SELECT sql FROM %s.sqlite_master WHERE sql IS NOT NULL",
            db_name("repository"));
    go = 1;
  }else if( P("tablelist") ){
    zQ = sqlite3_mprintf(
            "SELECT name FROM %s.sqlite_master WHERE type='table'"
            " ORDER BY name",
            db_name("repository"));
    go = 1;
  }
  if( go ){
    sqlite3_stmt *pStmt;
    int rc;
    const char *zTail;
    int nCol;
................................................................................
  @ <td>Search nothing. (Disables document search).</tr>
  @ </table>
  @ <hr />
  entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0);
  @ <p>When searching documents, use the versions of the files found at the
  @ type of the "Document Branch" branch.  Recommended value: "trunk".
  @ Document search is disabled if blank.
  @ <hr/>
  onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0);
  @ <br>
  onoff_attribute("Search Documents", "search-doc", "sd", 0, 0);
  @ <br>
  onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0);
  @ <br>
  onoff_attribute("Search Wiki","search-wiki", "sw", 0, 0);
  @ <hr/>
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
  @ <hr/>
  if( P("fts0") ){
    search_drop_index();
  }else if( P("fts1") ){
    search_drop_index();
    search_create_index();
    search_fill_index();
    search_update_index(search_restrict(SRCH_ALL));







<
<
<
<
<
<
<
<







 







>
>










>
>







 







>
>
>
|







 







|







 







|
<
<
<
<

|
<
<
<
<







 







>
>



<
>
|
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>







>
>
>




>
>
>
>
>
>
>
>
>
>
>







 







|







 







|







 







>
>
>







 







|







 







|







 







|


|







 







|







 







<







 







|
|
<







 







|

|


|













|
<



|
|
<







 







|

|

|

|

|

|







21
22
23
24
25
26
27








28
29
30
31
32
33
34
...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
...
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
...
231
232
233
234
235
236
237
238




239
240




241
242
243
244
245
246
247
...
286
287
288
289
290
291
292
293
294
295
296
297

298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
...
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
...
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
...
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
...
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
...
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
....
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
....
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
....
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
....
1471
1472
1473
1474
1475
1476
1477

1478
1479
1480
1481
1482
1483
1484
....
1535
1536
1537
1538
1539
1540
1541
1542
1543

1544
1545
1546
1547
1548
1549
1550
....
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942

1943
1944
1945
1946
1947

1948
1949
1950
1951
1952
1953
1954
....
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
#include <assert.h>
#include "setup.h"

#if INTERFACE
#define ArraySize(x) (sizeof(x)/sizeof(x[0]))
#endif









/*
** Output a single entry for a menu generated using an HTML table.
** If zLink is not NULL or an empty string, then it is the page that
** the menu entry will hyperlink to.  If zLink is NULL or "", then
** the menu entry has no hyperlink - it is disabled.
*/
void setup_menu_entry(
................................................................................
  setup_menu_entry("Skins", "setup_skin",
    "Select and/or modify the web interface \"skins\"");
  setup_menu_entry("Moderation", "setup_modreq",
    "Enable/Disable requiring moderator approval of Wiki and/or Ticket"
    " changes and attachments.");
  setup_menu_entry("Ad-Unit", "setup_adunit",
    "Edit HTML text for an ad unit inserted after the menu bar");
  setup_menu_entry("Web-Cache", "cachestat",
    "View the status of the expensive-page cache");
  setup_menu_entry("Logo", "setup_logo",
    "Change the logo and background images for the server");
  setup_menu_entry("Shunned", "shun",
    "Show artifacts that are shunned by this repository");
  setup_menu_entry("Artifact Receipts Log", "rcvfromlist",
    "A record of received artifacts and their sources");
  setup_menu_entry("User Log", "access_log",
    "A record of login attempts");
  setup_menu_entry("Administrative Log", "admin_log",
    "View the admin_log entries");
  setup_menu_entry("Unversioned Files", "uvlist?byage=1",
    "Show all unversioned files held");
  setup_menu_entry("Stats", "stat",
    "Repository Status Reports");
  setup_menu_entry("Sitemap", "sitemap",
    "Links to miscellaneous pages");
  setup_menu_entry("SQL", "admin_sql",
    "Enter raw SQL commands");
  setup_menu_entry("TH1", "admin_th1",
................................................................................
  }

  style_submenu_element("Add", "Add User", "setup_uedit");
  style_submenu_element("Log", "Access Log", "access_log");
  style_submenu_element("Help", "Help", "setup_ulist_notes");
  style_header("User List");
  @ <table border=1 cellpadding=2 cellspacing=0 class='userTable'>
  @ <thead><tr>
  @   <th>UID <th>Category
  @   <th>Capabilities (<a href='%R/setup_ucap_list'>key</a>)
  @   <th>Info <th>Last Change</tr></thead>
  @ <tbody>
  db_prepare(&s,
     "SELECT uid, login, cap, date(mtime,'unixepoch')"
     "  FROM user"
     " WHERE login IN ('anonymous','nobody','developer','reader')"
     " ORDER BY login"
  );
................................................................................
    const char *zLogin = db_column_text(&s, 1);
    const char *zCap = db_column_text(&s, 2);
    const char *zDate = db_column_text(&s, 4);
    @ <tr>
    @ <td><a href='setup_uedit?id=%d(uid)'>%d(uid)</a>
    @ <td><a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a>
    @ <td>%h(zCap)

    if( fossil_strcmp(zLogin,"anonymous")==0 ){
      @ <td>All logged-in users
    }else if( fossil_strcmp(zLogin,"developer")==0 ){
      @ <td>Users with '<b>v</b>' capability
    }else if( fossil_strcmp(zLogin,"nobody")==0 ){
      @ <td>All users without login
    }else if( fossil_strcmp(zLogin,"reader")==0 ){
................................................................................
  @ </tbody></table>
  db_finalize(&s);
  output_table_sorting_javascript("userlist","nktxTT",2);
  style_footer();
}

/*
** Render the user-capability table




*/
static void setup_usercap_table(void){




  @ <table>
     @ <tr><th valign="top">a</th>
     @