Fossil

Check-in [1014ffb553]
Login

Check-in [1014ffb553]

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

Overview
Comment:Merge trunk into the merge-info-html branch.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | merge-info-html
Files: files | file ages | folders
SHA3-256: 1014ffb553117ca4113dcc5071d3389fe3c664eb5522dd634aab91d69b0e876e
User & Date: stephan 2025-03-27 12:42:00.821
Original Comment: Merge trunk into the merge-info-trunk branch.
Context
2025-03-27
12:42
Merge trunk into the merge-info-html branch. ... (Leaf check-in: 1014ffb553 user: stephan tags: merge-info-html)
2025-03-26
22:56
Updates to the change log. ... (check-in: 0db5c15990 user: drh tags: trunk)
2025-02-20
14:46
Bring the merge-info-html branch up to date with trunk, step 2 of 2. ... (check-in: c2b1c2c9fa user: stephan tags: merge-info-html)
Changes
Unified Diff Ignore Whitespace Patch
Changes to .fossil-settings/crlf-glob.
1
2
3
4


5
compat/zlib/*
setup/fossil.iss
test/th1-docs-input.txt
test/th1-hooks-input.txt


win/buildmsvc.bat




>
>

1
2
3
4
5
6
7
compat/zlib/*
setup/fossil.iss
test/th1-docs-input.txt
test/th1-hooks-input.txt
win/build32.bat
win/build64.bat
win/buildmsvc.bat
Changes to .fossil-settings/ignore-glob.
1
2
3
4
5
6
7
8
9

compat/openssl*
compat/tcl*
compat/zlib/contrib/ada/*
compat/zlib/doc/*
fossil
fossil.exe
win/fossil.exe
*shell-see.*
*sqlite3-see.*










>
1
2
3
4
5
6
7
8
9
10
compat/openssl*
compat/tcl*
compat/zlib/contrib/ada/*
compat/zlib/doc/*
fossil
fossil.exe
win/fossil.exe
*shell-see.*
*sqlite3-see.*
bld
Makefile.in became a regular file.
Changes to auto.def.
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
    compile-commands=0 =>
      "Check for compile_commands.json support."
}

# Update the minimum required SQLite version number here, and also
# in src/main.c near the sqlite3_libversion_number() call.  Take care
# that both places agree!
define MINIMUM_SQLITE_VERSION "3.46.0"

# This is useful for people wanting Fossil to use an external SQLite library
# to compare the one they have against the minimum required
if {[opt-bool print-minimum-sqlite-version]} {
    puts [get-define MINIMUM_SQLITE_VERSION]
    exit 0
}

# Space characters have never been allowed in either the source
# tree nor the build directory.  But the resulting error messages
# could be confusing.  The following checks make the reason for the
# failure clear.
#







|




|
|







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
    compile-commands=0 =>
      "Check for compile_commands.json support."
}

# Update the minimum required SQLite version number here, and also
# in src/main.c near the sqlite3_libversion_number() call.  Take care
# that both places agree!
define MINIMUM_SQLITE_VERSION "3.49.0"

# This is useful for people wanting Fossil to use an external SQLite library
# to compare the one they have against the minimum required
if {[opt-bool print-minimum-sqlite-version]} {
  puts [get-define MINIMUM_SQLITE_VERSION]
  exit 0
}

# Space characters have never been allowed in either the source
# tree nor the build directory.  But the resulting error messages
# could be confusing.  The following checks make the reason for the
# failure clear.
#
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
if {![file exists fossil.1]} {
  puts "This appears to be an out-of-tree build."
  set outOfTreeBuild 1
}

# sqlite wants these types if possible
cc-with {-includes {stdint.h inttypes.h}} {
    cc-check-types uint32_t uint16_t int16_t uint8_t
}

# Use pread/pwrite system calls in place of seek + read/write if possible
define USE_PREAD [cc-check-functions pread]

# If we have cscope here, we'll use it in the "tags" target
if {[cc-check-progs cscope]} {
    define COLLECT_CSCOPE_DATA "cscope -bR $::autosetup(srcdir)/src/*.\[ch\]"
} else {
    define COLLECT_CSCOPE_DATA ""
}

# Find tclsh for the test suite.
#
# We can't use jimsh for this: the test suite uses features of Tcl that
# Jim doesn't support, either statically or due to the way it's built by
# autosetup.  For example, Jim supports `file normalize`, but only if
# you build it with HAVE_REALPATH, which won't ever be defined in this
# context because autosetup doesn't try to discover platform-specific
# details like that before it decides to build jimsh0.  Besides which,
# autosetup won't build jimsh0 at all if it can find tclsh itself.
# Ironically, this means we may right now be running under either jimsh0
# or a version of tclsh that we find unsuitable below!
cc-check-progs tclsh
set hbtd /usr/local/Cellar/tcl-tk
if {[string equal false [get-define TCLSH]]} {
    msg-result "WARNING: 'make test' will not run here."
} else {
    set v [exec sh -c "echo 'puts \$tcl_version' | tclsh"]
    if {[expr {$v >= 8.6}]} {
        msg-result "Found Tclsh version $v in the PATH."
        define TCLSH tclsh
    } elseif {[file isdirectory $hbtd]} {
        # This is a macOS system with the Homebrew version of Tcl/Tk
        # installed.  Select the newest version.  It won't normally be
        # in the PATH to avoid shadowing /usr/bin/tclsh, and even if it
        # were in the PATH, it's bad practice to put /usr/local/bin (the
        # Homebrew default) ahead of /usr/bin, especially given that
        # it's user-writeable by default with Homebrew.  Thus, we can be
        # pretty sure the only way to call it is with an absolute path.
        set v [exec ls -tr $hbtd | tail -1]
        set path "$hbtd/$v/bin/tclsh"
        define TCLSH $path
        msg-result "Using Homebrew Tcl/Tk version $path."
    } else {
        msg-result "WARNING: tclsh $v found; need >= 8.6 for 'make test'."
        define TCLSH false     ;# force "make test" failure via /usr/bin/false
    }
}

define CFLAGS [get-env CFLAGS "-g -Os"]
define EXTRA_CFLAGS "-Wall"
define EXTRA_LDFLAGS ""
define USE_SYSTEM_SQLITE 0
define USE_LINENOISE 0
define USE_MMAN_H 0
define USE_SEE 0
define SQLITE3_ORIGIN 0
# SQLITE3_ORIGIN 0 = src/sqlite3, 1=src/sqlite3-see.c, 2=client-provided
define SQLITE_OPTIONS_EXT ""
# SQLITE_OPTIONS_EXT => build-dependent CFLAGS for sqlite3.c and shell.c

# Maintain the C89/C90-style order of variable declarations before statements.
# Check if the compiler supports the respective warning flag.
if {[cctest -cflags -Wdeclaration-after-statement]} {
    define-append EXTRA_CFLAGS -Wdeclaration-after-statement
}


# This procedure is a customized version of "cc-check-function-in-lib",
# that does not modify the LIBS variable.  Its use prevents prematurely
# pulling in libraries that will be added later anyhow (e.g. "-ldl").
proc check-function-in-lib {function libs {otherlibs {}}} {
    if {[string length $otherlibs]} {
        msg-checking "Checking for $function in $libs with $otherlibs..."
    } else {
        msg-checking "Checking for $function in $libs..."
    }
    set found 0
    cc-with [list -libs $otherlibs] {
        if {[cctest_function $function]} {
            msg-result "none needed"
            define lib_$function ""
            incr found
        } else {
            foreach lib $libs {
                cc-with [list -libs -l$lib] {
                    if {[cctest_function $function]} {
                        msg-result -l$lib
                        define lib_$function -l$lib
                        incr found
                        break
                    }
                }
            }
        }
    }
    if {$found} {
        define [feature-define-name $function]
    } else {
        msg-result "no"
    }
    return $found
}

if {![opt-bool internal-sqlite]} {
  proc find_system_sqlite {} {

    # On some systems (slackware), libsqlite3 requires -ldl to link. So
    # search for the system SQLite once with -ldl, and once without. If







|







|

|
















|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

















|







|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|







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
if {![file exists fossil.1]} {
  puts "This appears to be an out-of-tree build."
  set outOfTreeBuild 1
}

# sqlite wants these types if possible
cc-with {-includes {stdint.h inttypes.h}} {
  cc-check-types uint32_t uint16_t int16_t uint8_t
}

# Use pread/pwrite system calls in place of seek + read/write if possible
define USE_PREAD [cc-check-functions pread]

# If we have cscope here, we'll use it in the "tags" target
if {[cc-check-progs cscope]} {
  define COLLECT_CSCOPE_DATA "cscope -bR $::autosetup(srcdir)/src/*.\[ch\]"
} else {
  define COLLECT_CSCOPE_DATA ""
}

# Find tclsh for the test suite.
#
# We can't use jimsh for this: the test suite uses features of Tcl that
# Jim doesn't support, either statically or due to the way it's built by
# autosetup.  For example, Jim supports `file normalize`, but only if
# you build it with HAVE_REALPATH, which won't ever be defined in this
# context because autosetup doesn't try to discover platform-specific
# details like that before it decides to build jimsh0.  Besides which,
# autosetup won't build jimsh0 at all if it can find tclsh itself.
# Ironically, this means we may right now be running under either jimsh0
# or a version of tclsh that we find unsuitable below!
cc-check-progs tclsh
set hbtd /usr/local/Cellar/tcl-tk
if {[string equal false [get-define TCLSH]]} {
  msg-result "WARNING: 'make test' will not run here."
} else {
  set v [exec sh -c "echo 'puts \$tcl_version' | tclsh"]
  if {[expr {$v >= 8.6}]} {
    msg-result "Found Tclsh version $v in the PATH."
    define TCLSH tclsh
  } elseif {[file isdirectory $hbtd]} {
    # This is a macOS system with the Homebrew version of Tcl/Tk
    # installed.  Select the newest version.  It won't normally be
    # in the PATH to avoid shadowing /usr/bin/tclsh, and even if it
    # were in the PATH, it's bad practice to put /usr/local/bin (the
    # Homebrew default) ahead of /usr/bin, especially given that
    # it's user-writeable by default with Homebrew.  Thus, we can be
    # pretty sure the only way to call it is with an absolute path.
    set v [exec ls -tr $hbtd | tail -1]
    set path "$hbtd/$v/bin/tclsh"
    define TCLSH $path
    msg-result "Using Homebrew Tcl/Tk version $path."
  } else {
    msg-result "WARNING: tclsh $v found; need >= 8.6 for 'make test'."
    define TCLSH false     ;# force "make test" failure via /usr/bin/false
  }
}

define CFLAGS [get-env CFLAGS "-g -Os"]
define EXTRA_CFLAGS "-Wall"
define EXTRA_LDFLAGS ""
define USE_SYSTEM_SQLITE 0
define USE_LINENOISE 0
define USE_MMAN_H 0
define USE_SEE 0
define SQLITE3_ORIGIN 0
# SQLITE3_ORIGIN 0 = src/sqlite3, 1=src/sqlite3-see.c, 2=client-provided
define SQLITE_OPTIONS_EXT ""
# SQLITE_OPTIONS_EXT => build-dependent CFLAGS for sqlite3.c and shell.c

# Maintain the C89/C90-style order of variable declarations before statements.
# Check if the compiler supports the respective warning flag.
if {[cctest -cflags -Wdeclaration-after-statement]} {
  define-append EXTRA_CFLAGS -Wdeclaration-after-statement
}


# This procedure is a customized version of "cc-check-function-in-lib",
# that does not modify the LIBS variable.  Its use prevents prematurely
# pulling in libraries that will be added later anyhow (e.g. "-ldl").
proc check-function-in-lib {function libs {otherlibs {}}} {
  if {[string length $otherlibs]} {
    msg-checking "Checking for $function in $libs with $otherlibs..."
  } else {
    msg-checking "Checking for $function in $libs..."
  }
  set found 0
  cc-with [list -libs $otherlibs] {
    if {[cctest_function $function]} {
      msg-result "none needed"
      define lib_$function ""
      incr found
    } else {
      foreach lib $libs {
        cc-with [list -libs -l$lib] {
          if {[cctest_function $function]} {
            msg-result -l$lib
            define lib_$function -l$lib
            incr found
            break
          }
        }
      }
    }
  }
  if {$found} {
    define [feature-define-name $function]
  } else {
    msg-result "no"
  }
  return $found
}

if {![opt-bool internal-sqlite]} {
  proc find_system_sqlite {} {

    # On some systems (slackware), libsqlite3 requires -ldl to link. So
    # search for the system SQLite once with -ldl, and once without. If
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
    lappend cmdline {*}[get-define LDFLAGS]
    lappend cmdline {*}[get-define LIBS]
    set sqlite-version [string cat "-D MINIMUM_SQLITE_VERSION=" [get-define MINIMUM_SQLITE_VERSION]]
    lappend cmdline {*}[set sqlite-version]
    set ok 1
    set err [catch {exec-with-stderr {*}$cmdline} result errinfo]
    if {$err} {
       configlog "Failed: [join $cmdline]"
       if {[string length $result]>0} {configlog $result}
       configlog "============"
       set ok 0
    } elseif {$::autosetup(debug)} {
       configlog "Compiled OK: [join $cmdline]"
       configlog "============"
    }
    if {!$ok} {
      user-error "unable to compile SQLite compatibility test program"
    }
    set err [catch {exec-with-stderr ./conftest__} result errinfo]
    if {[get-define build] eq [get-define host]} {
      set err [catch {exec-with-stderr ./conftest__} result errinfo]







|
|
|
|

|
|







217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
    lappend cmdline {*}[get-define LDFLAGS]
    lappend cmdline {*}[get-define LIBS]
    set sqlite-version [string cat "-D MINIMUM_SQLITE_VERSION=" [get-define MINIMUM_SQLITE_VERSION]]
    lappend cmdline {*}[set sqlite-version]
    set ok 1
    set err [catch {exec-with-stderr {*}$cmdline} result errinfo]
    if {$err} {
      configlog "Failed: [join $cmdline]"
      if {[string length $result]>0} {configlog $result}
      configlog "============"
      set ok 0
    } elseif {$::autosetup(debug)} {
      configlog "Compiled OK: [join $cmdline]"
      configlog "============"
    }
    if {!$ok} {
      user-error "unable to compile SQLite compatibility test program"
    }
    set err [catch {exec-with-stderr ./conftest__} result errinfo]
    if {[get-define build] eq [get-define host]} {
      set err [catch {exec-with-stderr ./conftest__} result errinfo]
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
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
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
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
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
  return [expr {
    [string match *mingw* [get-define host]] &&
   ![file exists "/dev/null"]
  }]
}

if {[is_mingw]} {
    define-append EXTRA_CFLAGS -DBROKEN_MINGW_CMDLINE
    define-append LIBS -lkernel32 -lws2_32
} else {
    #
    # NOTE: All platforms except MinGW should use the linenoise
    #       package.  It is currently unsupported on Win32.
    #
    define USE_LINENOISE 1
}

if {[string match *-solaris* [get-define host]]} {
    define-append EXTRA_CFLAGS {-D__EXTENSIONS__}
}

if {[opt-bool fossil-debug]} {
    define CFLAGS {-g -O0 -Wall}
    define-append CFLAGS -DFOSSIL_DEBUG
    msg-result "Debugging support enabled"
}

if {[opt-bool no-opt]} {
    define CFLAGS {-g -O0 -Wall}
    msg-result "Builting without compiler optimization"
    if {[opt-bool fossil-debug]} {
        define-append CFLAGS -DFOSSIL_DEBUG
    }
}

if {[opt-bool with-mman]} {
    define-append EXTRA_CFLAGS -DUSE_MMAN_H
    define USE_MMAN_H 1
    msg-result "Enabling \"sys/mman.h\" support"
}

if {[opt-bool with-see]} {
    define-append EXTRA_CFLAGS -DUSE_SEE
    define USE_SEE 1
    define SQLITE3_ORIGIN 1
    msg-result "Enabling encryption support"
}

if {[opt-bool json]} {
    # Reminder/FIXME (stephan): FOSSIL_ENABLE_JSON
    # is required in the CFLAGS because json*.c
    # have #ifdef guards around the whole file without
    # reading config.h first.
    define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON
    define FOSSIL_ENABLE_JSON
    msg-result "JSON support enabled"
}

if {[opt-bool with-exec-rel-paths]} {
    define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_EXEC_REL_PATHS
    define FOSSIL_ENABLE_EXEC_REL_PATHS
    msg-result "Relative paths in external diff/gdiff enabled"
}

if {[opt-bool with-th1-docs]} {
    define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_DOCS
    define FOSSIL_ENABLE_TH1_DOCS
    msg-result "TH1 embedded documentation support enabled"
}

if {[opt-bool with-th1-hooks]} {
    define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_HOOKS
    define FOSSIL_ENABLE_TH1_HOOKS
    msg-result "TH1 hooks support enabled"
}

#if {[opt-bool markdown]} {
#    # no-op.  Markdown is now enabled by default.
#    msg-result "Markdown support enabled"
#}

if {[opt-bool static]} {
    # XXX: This will not work on all systems.
    define-append EXTRA_LDFLAGS -static
    msg-result "Trying to link statically"
} else {
    define-append EXTRA_CFLAGS -DFOSSIL_DYNAMIC_BUILD=1
    define FOSSIL_DYNAMIC_BUILD
}

# Check for libraries that need to be sorted out early
cc-check-function-in-lib iconv iconv




# Helper for OpenSSL checking
proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto -lpthread}}} {
    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]} {
            incr rc
        }
    }
    if {!$rc && ![is_mingw]} {
        # On some systems, OpenSSL appears to require -ldl to link.
        lappend libs -ldl
        msg-quiet cc-with [list -cflags $cflags -libs $libs] {
            if {[cc-check-includes openssl/ssl.h] && \
                    [cc-check-functions SSL_new]} {
                incr rc
            }
        }
    }
    if {$rc} {
        msg-result "ok"
        return 1
    } else {
        msg-result "no"
        return 0
    }
}


# Check for zlib, using the given location if specified


set zlibpath [opt-val with-zlib]
if {$zlibpath eq "tree"} {
  set zlibdir [file dirname $autosetup(dir)]/compat/zlib
  if {![file isdirectory $zlibdir]} {
    user-error "The zlib in source tree directory does not exist"
  } elseif { ([llength [glob -nocomplain -directory $zlibdir libz*]] == 0) } {
    user-error "With --with-zlib=tree, $zlibdir must be configured and built first."
  }
  cc-with [list -cflags "-I$zlibdir -L$zlibdir"]
  define-append EXTRA_CFLAGS -I$zlibdir
  define-append LIBS $zlibdir/libz.a
  set ::zlib_lib $zlibdir/libz.a
  msg-result "Using zlib in source tree"
} else {
  set cftry {""}
  set ldtry {""}
  if {$zlibpath ni {auto ""}} {
    lappend cftry "-I$zlibpath"
    lappend cftry "-I$zlibpath/include"
    lappend ldtry "-L$zlibpath"
    lappend ldtry "-L$zlibpath/lib"
  }

  # Reverse the list of tests so we check most-specific to least, else
  # platform devel files will shadow local --with-zlib overrides.
  foreach c [lreverse $cftry] {
    if {[cc-with [list -cflags $c] {cc-check-includes zlib.h}]} {
      if {$c eq ""} {
        msg-result "Found zlib.h in default include path"
      } else {
        define-append EXTRA_CFLAGS "$c"
        msg-result "Found zlib.h via $c"
      }
      set cfound $c
      break
    }
  }
  if {![info exists cfound]} {
    user-error "zlib.h not found; either install it or specify its location via --with-zlib"
  }
  foreach lcheck [lreverse $ldtry] {
    if {[cc-with [list -cflags "$cfound $lcheck"] {check-function-in-lib inflateEnd z}]} {
      if {$lcheck eq ""} {
        msg-result "Linked to zlib via default library path"
      } else {
        define-append EXTRA_LDFLAGS "$lcheck"
        msg-result "Linked to zlib via $lcheck"
      }
      if {![check-function-in-lib compressBound z]} {
        puts "Notice: disabling zlib compression in the SQL shell"
        define-append SQLITE_OPTIONS_EXT {-USQLITE_HAVE_ZLIB}
      }
      break
    }
  }
  set ::zlib_lib -lz
}













set ssldirs [opt-val with-openssl]
if {$ssldirs ne "none"} {
    set found 0
    if {$ssldirs eq "tree"} {
        set ssldir [file dirname $autosetup(dir)]/compat/openssl
        if {![file isdirectory $ssldir]} {
            user-error "The OpenSSL in source tree directory does not exist"
        }
        set msg "openssl in $ssldir"
        set cflags "-I$ssldir/include"
        set ldflags "-L$ssldir"
        set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a -lpthread"
        set found [check-for-openssl "openssl in source tree" "$cflags $ldflags" $ssllibs]
    } else {
        if {$ssldirs in {auto ""}} {
            catch {

                set cflags [exec pkg-config openssl --cflags-only-I]
                set ldflags [exec pkg-config openssl --libs-only-L]
                set found [check-for-openssl "ssl via pkg-config" "$cflags $ldflags"]
            } msg
            if {!$found} {
                set ssldirs "{} /usr/sfw /usr/local/ssl /usr/lib/ssl /usr/ssl \
                             /usr/pkg /usr/local /usr /usr/local/opt/openssl \
                             /opt/homebrew/opt/openssl"
            }
        }
        if {!$found} {
            foreach dir $ssldirs {
                if {$dir eq ""} {
                    set msg "system openssl"
                    set cflags ""
                    set ldflags ""
                } else {
                    set msg "openssl in $dir"
                    set cflags "-I$dir/include"





                    set ldflags "-L$dir/lib"


                }

                if {[check-for-openssl $msg "$cflags $ldflags"]} {
                    incr found
                    break
                }
                if {$dir ne ""} {
                    set ldflags ""
                    set msg "static build of openssl in $dir"
                    set ssllibs "$dir/libssl.a $dir/libcrypto.a -lpthread"
                    if {[check-for-openssl $msg "$cflags $ldflags" $ssllibs]} {
                        incr found
                        break
                    }


                }
            }
        }
    }
    if {$found} {
        define FOSSIL_ENABLE_SSL
        define-append EXTRA_CFLAGS $cflags
        define-append EXTRA_LDFLAGS $ldflags
        define-append CFLAGS $cflags
        define-append LDFLAGS $ldflags
        if {[info exists ssllibs]} {
            define-append LIBS $ssllibs
        } 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
            }
        }
    } else {
        user-error "OpenSSL not found. Consider --with-openssl=none to disable HTTPS support"
    }
} else {
    if {[info exists ::zlib_lib]} {
        define-append LIBS $::zlib_lib
    }
}













########################################################################
# --with-sqlite=PATH checks for the first it finds of the following...
# - PATH/sqlite3.c and PATH/sqlite3.h
# - PATH/sqlite3.o (and assumes sqlite3.h is with it)
# - PATH/lib/libsqlite3* and PATH/include/sqlite3.h
define CFLAGS_INCLUDE {}
# ^^^ CFLAGS_INCLUDE is ONLY for -I... flags and their order is
# significant so that --with-sqlite=PATH's header can shadow our
# own. One caveat with this is that we cannot point --with-sqlite=PATH
# to the root of sqlite3's own build tree because that dir has a
# config.h which ends up shadowing src/config.h, breaking our build.
set sq3path [opt-val with-sqlite]
define SQLITE3_SRC.2 {}
define SQLITE3_OBJ.2 {}
define SQLITE3_SHELL_SRC.2 {$(SQLITE3_SHELL_SRC.0)}
if {$sq3path in {tree ""}} {
  msg-result "Using sqlite3.c from this source tree."
} else {
  # SQLITE3_ORIGIN:
  #   0 = local source tree
  #   1 = use external lib or sqlite3.o
  #   2 = use external sqlite3.c and (if found) shell.c
  define USE_SYSTEM_SQLITE 1
  define SQLITE3_ORIGIN 2
  if {$sq3path != "auto"} {
    if {([file exists $sq3path/sqlite3.c]) && \
        ([file exists $sq3path/sqlite3.h]) } {
      # Prefer sqlite3.[ch] if found.
      define SQLITE3_SRC.2 $sq3path/sqlite3.c
      define SQLITE3_OBJ.2 {$(SQLITE3_OBJ.0)}
      define USE_SYSTEM_SQLITE 2
      define SQLITE3_ORIGIN 2
      if {[file exists $sq3path/shell.c]} {
        define SQLITE3_SHELL_SRC.2 $sq3path/shell.c
      }
      define-append CFLAGS_INCLUDE -I$sq3path
      define-append EXTRA_LDFLAGS -lpthread
      # ^^^ additional -lXXX flags are conservative estimates
      msg-result "Using sqlite3.c and sqlite3.h from $sq3path"
    } elseif {[file exists $sq3path/sqlite3.o]} {
      # Use sqlite3.o if found.
      define SQLITE3_OBJ.2 $sq3path/sqlite3.o
      define-append CFLAGS_INCLUDE -I$sq3path
      define-append EXTRA_LDFLAGS $sq3path/sqlite3.o -lpthread
      # ^^^ additional -lXXX flags are conservative estimates
      msg-result "Using sqlite3.o from $sq3path"
    } elseif { ([llength [glob -nocomplain -directory $sq3path/lib libsqlite3*]] != 0) \
                 && ([file exists $sq3path/include/sqlite3.h]) } {
      # e.g. --with-sqlite=/usr/local. Try $sq3path/lib/libsqlite3*
      # and $sq3path/include/sqlite3.h
      define-append CFLAGS_INCLUDE -I$sq3path/include
      define-append EXTRA_LDFLAGS -L$sq3path/lib -lsqlite3 -lpthread
      # ^^^ additional -lXXX flags are conservative estimates
      msg-result "Using -lsqlite3 from $sq3path"
    } else {
      # Assume $sq3path holds both the lib and header
      cc-with [list -cflags "-I$sq3path -L$sq3path"]
      define-append CFLAGS_INCLUDE -I$sq3path
      define-append EXTRA_LDFLAGS -L$sq3path -lsqlite3 -lpthread
      # ^^^ additional -lXXX flags are conservative estimates
      msg-result "Using -lsqlite3 from $sq3path"
    }
  } elseif {![cc-check-includes sqlite3.h] || ![check-function-in-lib sqlite3_open_v2 sqlite3]} {
    user-error "libsqlite3 not found please install it or specify the location with --with-sqlite"
  }
}


define-append CFLAGS_INCLUDE {-I. -I$(SRCDIR) -I$(SRCDIR_extsrc)}





set tclpath [opt-val with-tcl]
if {$tclpath ne ""} {


    set tclprivatestubs [opt-bool with-tcl-private-stubs]
    # Note parse-tclconfig-sh is in autosetup/local.tcl
    if {$tclpath eq "1"} {
        set tcldir [file dirname $autosetup(dir)]/compat/tcl-8.6
        if {$tclprivatestubs} {
            set tclconfig(TCL_INCLUDE_SPEC) -I$tcldir/generic
            set tclconfig(TCL_VERSION) {Private Stubs}
            set tclconfig(TCL_PATCH_LEVEL) {}
            set tclconfig(TCL_PREFIX) $tcldir
            set tclconfig(TCL_LD_FLAGS) { }
        } else {
            # Use the system Tcl. Look in some likely places.
            array set tclconfig [parse-tclconfig-sh \
                $tcldir/unix $tcldir/win \
                /usr /usr/local /usr/share /opt/local]
            set msg "on your system"
        }
    } else {
        array set tclconfig [parse-tclconfig-sh $tclpath]
        set msg "at $tclpath"
    }
    if {[opt-bool static]} {
        set tclconfig(TCL_LD_FLAGS) { }
    }
    if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} {
        user-error "Cannot find Tcl $msg"
    }
    set tclstubs [opt-bool with-tcl-stubs]
    if {$tclprivatestubs} {
        define FOSSIL_ENABLE_TCL_PRIVATE_STUBS
        define USE_TCL_STUBS
    } elseif {$tclstubs && $tclconfig(TCL_SUPPORTS_STUBS)} {
        set libs "$tclconfig(TCL_STUB_LIB_SPEC)"
        define FOSSIL_ENABLE_TCL_STUBS
        define USE_TCL_STUBS
    } else {
        set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)"
    }
    set cflags $tclconfig(TCL_INCLUDE_SPEC)
    if {!$tclprivatestubs} {
        set foundtcl 0; # Did we find a working Tcl library?
        cc-with [list -cflags $cflags -libs $libs] {
            if {$tclstubs} {
                if {[cc-check-functions Tcl_InitStubs]} {
                    set foundtcl 1
                }
            } else {
                if {[cc-check-functions Tcl_CreateInterp]} {
                    set foundtcl 1
                }
            }
        }
        if {!$foundtcl && [string match *-lieee* $libs]} {
            # On some systems, using "-lieee" from TCL_LIB_SPEC appears
            # to cause issues.
            msg-result "Removing \"-lieee\" and retrying for Tcl..."
            set libs [string map [list -lieee ""] $libs]
            cc-with [list -cflags $cflags -libs $libs] {
                if {$tclstubs} {
                    if {[cc-check-functions Tcl_InitStubs]} {
                        set foundtcl 1
                    }
                } else {
                    if {[cc-check-functions Tcl_CreateInterp]} {
                        set foundtcl 1
                    }
                }
            }
        }
        if {!$foundtcl && ![string match *-lpthread* $libs]} {
            # On some systems, TCL_LIB_SPEC appears to be missing
            # "-lpthread".  Try adding it.
            msg-result "Adding \"-lpthread\" and retrying for Tcl..."
            set libs "$libs -lpthread"
            cc-with [list -cflags $cflags -libs $libs] {
                if {$tclstubs} {
                    if {[cc-check-functions Tcl_InitStubs]} {
                        set foundtcl 1
                    }
                } else {
                    if {[cc-check-functions Tcl_CreateInterp]} {
                        set foundtcl 1
                    }
                }
            }
        }
        if {!$foundtcl} {
            if {$tclstubs} {
                user-error "Cannot find a usable Tcl stubs library $msg"
            } else {
                user-error "Cannot find a usable Tcl library $msg"
            }
        }
    }
    set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL)
    msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)"
    if {!$tclprivatestubs} {
        define-append LIBS $libs
    }
    define-append EXTRA_CFLAGS $cflags
    define-append CFLAGS $cflags
    if {[info exists zlibpath] && $zlibpath eq "tree"} {
      #
      # NOTE: When using zlib in the source tree, prevent Tcl from
      #       pulling in the system one.
      #
      set tclconfig(TCL_LD_FLAGS) [string map [list -lz ""] \
          $tclconfig(TCL_LD_FLAGS)]
    }
    #
    # NOTE: Remove "-ldl" from the TCL_LD_FLAGS because it will be
    #       be checked for near the bottom of this file.
    #
    set tclconfig(TCL_LD_FLAGS) [string map [list -ldl ""] \
        $tclconfig(TCL_LD_FLAGS)]
    define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS)
    define FOSSIL_ENABLE_TCL
}


# Network functions require libraries on some systems
cc-check-function-in-lib gethostbyname nsl
if {![cc-check-function-in-lib socket {socket network}]} {
    # Last resort, may be Windows
    if {[is_mingw]} {
        define-append LIBS -lwsock32
    }
}

# Some systems (ex: SunOS) require -lrt in order to use nanosleep
cc-check-function-in-lib nanosleep rt

# The SMTP module requires special libraries and headers for MX DNS
# record lookups and such.
cc-check-includes arpa/nameser.h
cc-include-needs bind/resolv.h netinet/in.h
cc-check-includes bind/resolv.h
cc-check-includes resolv.h
if {    !(([cc-check-function-in-lib dn_expand resolv] ||
           [cc-check-function-in-lib   ns_name_uncompress {bind resolv}] ||
           [cc-check-function-in-lib __ns_name_uncompress {bind resolv}]) &&
          ([cc-check-function-in-lib   ns_parserr {bind resolv}] ||
           [cc-check-function-in-lib __ns_parserr {bind resolv}]) &&
          ([cc-check-function-in-lib   res_query {bind resolv}] ||
           [cc-check-function-in-lib __res_query {bind resolv}]))} {
    msg-result "WARNING: SMTP feature will not be able to look up local MX."
}
cc-check-function-in-lib res_9_ns_initparse resolv

# Other nonstandard function checks
cc-check-functions utime
cc-check-functions usleep
cc-check-functions strchrnul
cc-check-functions pledge
cc-check-functions backtrace

# Termux on Android adds "getpass(char *)" to unistd.h, so check this so we
# guard against including it again; use cctest as cc-check-functions and
# cctest_function check for "getpass()" with no args and fail
if {[cctest -link 1 -includes {unistd.h} -code "getpass(0);"]} {
    define FOSSIL_HAVE_GETPASS 1
    msg-result "Found getpass() with unistd.h"
}

# Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE
if {![cc-check-functions getloadavg] ||
    ![cctest -link 1 -includes {unistd.h} -code "double a\[3\]; getloadavg(a,3);"]} {
  define FOSSIL_OMIT_LOAD_AVERAGE 1
  msg-result "Load average support unavailable"
}

# Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars
if {![cc-check-functions getpassphrase]} {
    # Haiku needs this
    cc-check-function-in-lib getpass bsd
}
cc-check-function-in-lib sin m

# Check for the FuseFS library
if {[opt-bool fusefs]} {
  if {[opt-bool static]} {
     msg-result "FuseFS support disabled due to -static"
  } elseif {[cc-check-function-in-lib fuse_mount fuse]} {
     define-append EXTRA_CFLAGS -DFOSSIL_HAVE_FUSEFS
     define FOSSIL_HAVE_FUSEFS 1
     msg-result "FuseFS support enabled"
  }
}

########################################################################
# Checks the compiler for compile_commands.json support.
#
# Returns 1 if supported, else 0. Defines MAKE_COMPILATION_DB to "yes"
# if supported, "no" if not.
proc check-compile-commands {} {
    msg-checking "compile_commands.json support... "
    if {[cctest -lang c -cflags {/dev/null -MJ} -source {}]} {
        # This test reportedly incorrectly succeeds on one of
        # Martin G.'s older systems.
        msg-result "compiler supports compile_commands.json"
        define MAKE_COMPILATION_DB yes
        return 1
    } else {
        msg-result "compiler does not support compile_commands.json"
        define MAKE_COMPILATION_DB no
        return 0
    }
}

define MAKE_COMPILATION_DB no
if {!$outOfTreeBuild} {
  if {[opt-bool compile-commands]} {
    check-compile-commands
  } else {
    puts "Use --compile-commands to enable check for compile-commands-capable compiler."
  }
} else {
  puts "Disabling compile_commands.json check for out-of-tree build."
  # This is an attempt to resolve the problem reported at
  # https://fossil-scm.org/forum/forumpost/d19061d09a8179d0
}

# Add -fsanitize compile and link options late: we don't want the C
# checks above to run with those sanitizers enabled.  It can not only
# be pointless, it can actually break correct tests.
set fsan [opt-val with-sanitizer]
if {[string length $fsan]} {
    define-append  EXTRA_CFLAGS -fsanitize=$fsan
    define-append EXTRA_LDFLAGS -fsanitize=$fsan
    if {[string first "undefined" $fsan] != -1} {
        # We need to link with libubsan if we're compiling under
        # GCC with -fsanitize=undefined.
        cc-check-function-in-lib __ubsan_handle_add_overflow ubsan
    }
}

# Finally, append libraries that must be last. This matters more on some
# OSes than others, but is most broadly required for static linking.
if {[check-function-in-lib dlopen dl]} {
    # Some platforms (*BSD) have the dl functions already in libc and no libdl.
    # In such case we can link directly without -ldl.
    define-append LIBS [get-define lib_dlopen]
}
if {[opt-bool static]} {
    # Linux can only infer the dependency on pthread from OpenSSL when
    # doing dynamic linkage.
    define-append LIBS -lpthread
}


########################################################################
# @proj-check-emsdk
#
# Emscripten is used for doing in-tree builds of web-based WASM stuff,
# as opposed to WASI-based WASM or WASM binaries we import from other
# places. This is only set up for Unix-style OSes and is untested







|
|

|
|
|
|
|



|



|
|
|



|
|
|
|
|



|
|
|



|
|
|
|



|
|
|
|
|
|
|



|
|
|



|
|
|



|
|
|








|
|
|

|
|





>
>
>


|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|


>

>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>

>
>
>
>
>
>
>
>
>
>
|
|


|
|
|
|
|
|
|
|
|

|
|
>
|
|
|
|
|
|


|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
|
>
>
|
>
|
|
|
|
|
|
|
|
|
|
|
|
>
>
|
|
|


|
|
|
<
<
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|

|

|

|

|
>

|
>
>
>
>
>
>
>
>
>






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

>
>
>
>
|
|
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>




|
|
|
|


















|














|
|











|
|

<




|

|
|
|









|
|
|
|
|
|
|
|
|
|
|
|




















|
|
|
|
|
|
|

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







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
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
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
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
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
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
872
873
874
875
  return [expr {
    [string match *mingw* [get-define host]] &&
   ![file exists "/dev/null"]
  }]
}

if {[is_mingw]} {
  define-append EXTRA_CFLAGS -DBROKEN_MINGW_CMDLINE
  define-append LIBS -lkernel32 -lws2_32
} else {
  #
  # NOTE: All platforms except MinGW should use the linenoise
  #       package.  It is currently unsupported on Win32.
  #
  define USE_LINENOISE 1
}

if {[string match *-solaris* [get-define host]]} {
  define-append EXTRA_CFLAGS {-D__EXTENSIONS__}
}

if {[opt-bool fossil-debug]} {
  define CFLAGS {-g -O0 -Wall}
  define-append CFLAGS -DFOSSIL_DEBUG
  msg-result "Debugging support enabled"
}

if {[opt-bool no-opt]} {
  define CFLAGS {-g -O0 -Wall}
  msg-result "Builting without compiler optimization"
  if {[opt-bool fossil-debug]} {
    define-append CFLAGS -DFOSSIL_DEBUG
  }
}

if {[opt-bool with-mman]} {
  define-append EXTRA_CFLAGS -DUSE_MMAN_H
  define USE_MMAN_H 1
  msg-result "Enabling \"sys/mman.h\" support"
}

if {[opt-bool with-see]} {
  define-append EXTRA_CFLAGS -DUSE_SEE
  define USE_SEE 1
  define SQLITE3_ORIGIN 1
  msg-result "Enabling encryption support"
}

if {[opt-bool json]} {
  # Reminder/FIXME (stephan): FOSSIL_ENABLE_JSON
  # is required in the CFLAGS because json*.c
  # have #ifdef guards around the whole file without
  # reading config.h first.
  define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON
  define FOSSIL_ENABLE_JSON
  msg-result "JSON support enabled"
}

if {[opt-bool with-exec-rel-paths]} {
  define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_EXEC_REL_PATHS
  define FOSSIL_ENABLE_EXEC_REL_PATHS
  msg-result "Relative paths in external diff/gdiff enabled"
}

if {[opt-bool with-th1-docs]} {
  define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_DOCS
  define FOSSIL_ENABLE_TH1_DOCS
  msg-result "TH1 embedded documentation support enabled"
}

if {[opt-bool with-th1-hooks]} {
  define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_HOOKS
  define FOSSIL_ENABLE_TH1_HOOKS
  msg-result "TH1 hooks support enabled"
}

#if {[opt-bool markdown]} {
#    # no-op.  Markdown is now enabled by default.
#    msg-result "Markdown support enabled"
#}

if {[opt-bool static]} {
  # XXX: This will not work on all systems.
  define-append EXTRA_LDFLAGS -static
  msg-result "Trying to link statically"
} else {
  define-append EXTRA_CFLAGS -DFOSSIL_DYNAMIC_BUILD=1
  define FOSSIL_DYNAMIC_BUILD
}

# Check for libraries that need to be sorted out early
cc-check-function-in-lib iconv iconv

cc-check-function-in-lib sin m
cc-check-function-in-lib dlopen dl

# Helper for OpenSSL checking
proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto -lpthread}}} {
  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]} {
      incr rc
    }
  }
  if {!$rc && ![is_mingw]} {
    # On some systems, OpenSSL appears to require -ldl to link.
    lappend libs -ldl
    msg-quiet cc-with [list -cflags $cflags -libs $libs] {
      if {[cc-check-includes openssl/ssl.h] && \
            [cc-check-functions SSL_new]} {
        incr rc
      }
    }
  }
  if {$rc} {
    msg-result "ok"
    return 1
  } else {
    msg-result "no"
    return 0
  }
}

#
# Check for zlib, using the given location if specified
#
proc handle-zlib {} {
  set ::zlibpath [opt-val with-zlib]; # used by downstream tcl tests
  if {$::zlibpath eq "tree"} {
    set ::zlibdir [file dirname $::autosetup(dir)]/compat/zlib
    if {![file isdirectory $::zlibdir]} {
      user-error "The zlib in source tree directory does not exist"
    } elseif { ([llength [glob -nocomplain -directory $::zlibdir libz*]] == 0) } {
      user-error "With --with-zlib=tree, $::zlibdir must be configured and built first."
    }
    cc-with [list -cflags "-I$::zlibdir -L$::zlibdir"]
    define-append EXTRA_CFLAGS -I$::zlibdir
    define-append LIBS $::zlibdir/libz.a
    set ::zlib_lib $::zlibdir/libz.a
    msg-result "Using zlib in source tree"
  } else {
    set cftry {""}
    set ldtry {""}
    if {$::zlibpath ni {auto ""}} {
      lappend cftry "-I$::zlibpath"
      lappend cftry "-I$::zlibpath/include"
      lappend ldtry "-L$::zlibpath"
      lappend ldtry "-L$::zlibpath/lib"
    }

    # Reverse the list of tests so we check most-specific to least, else
    # platform devel files will shadow local --with-zlib overrides.
    foreach c [lreverse $cftry] {
      if {[cc-with [list -cflags $c] {cc-check-includes zlib.h}]} {
        if {$c eq ""} {
          msg-result "Found zlib.h in default include path"
        } else {
          define-append EXTRA_CFLAGS "$c"
          msg-result "Found zlib.h via $c"
        }
        set cfound $c
        break
      }
    }
    if {![info exists cfound]} {
      user-error "zlib.h not found; either install it or specify its location via --with-zlib"
    }
    foreach lcheck [lreverse $ldtry] {
      if {[cc-with [list -cflags "$cfound $lcheck"] {check-function-in-lib inflateEnd z}]} {
        if {$lcheck eq ""} {
          msg-result "Linked to zlib via default library path"
        } else {
          define-append EXTRA_LDFLAGS "$lcheck"
          msg-result "Linked to zlib via $lcheck"
        }
        if {![check-function-in-lib compressBound z]} {
          puts "Notice: disabling zlib compression in the SQL shell"
          define-append SQLITE_OPTIONS_EXT {-USQLITE_HAVE_ZLIB}
        }
        break
      }
    }
    set ::zlib_lib -lz
  }
}; # handle-zlib
handle-zlib

#
# Handle the --with-openssl flag and, incidentally, update @LIBS@ for
# zlib if openssl is _not_ used (if it is, we get zlib via libssl).
#
# This function should be called as late as possible in the configure
# script to avoid that its updates to @LIBS@ break tests which follow
# it when a custom local build of openssl is used, as discussed in
# <https://fossil-scm.org/forum/forumpost/15e3d9cdc137030c>.
#
proc handle-with-openssl {} {
  set ssldirs [opt-val with-openssl]
  if {$ssldirs ne "none"} {
    set found 0
    if {$ssldirs eq "tree"} {
      set ssldir [file dirname $::autosetup(dir)]/compat/openssl
      if {![file isdirectory $ssldir]} {
        user-error "The OpenSSL in source tree directory does not exist"
      }
      set msg "openssl in $ssldir"
      set cflags "-I$ssldir/include"
      set ldflags "-L$ssldir"
      set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a -lpthread"
      set found [check-for-openssl "openssl in source tree" "$cflags $ldflags" $ssllibs]
    } else {
      if {$ssldirs in {auto ""}} {
        catch {
          # TODO?: use autosetup's pkg-config support
          set cflags [exec pkg-config openssl --cflags-only-I]
          set ldflags [exec pkg-config openssl --libs-only-L]
          set found [check-for-openssl "ssl via pkg-config" "$cflags $ldflags"]
        } msg
        if {!$found} {
          set ssldirs "{} /usr/sfw /usr/local/ssl /usr/lib/ssl /usr/ssl \
                             /usr/pkg /usr/local /usr /usr/local/opt/openssl \
                             /opt/homebrew/opt/openssl"
        }
      }
      if {!$found} {
        foreach dir $ssldirs {
          if {$dir eq ""} {
            set msg "system openssl"
            set cflags ""
            set ldflags ""
          } else {
            set msg "openssl in $dir"
            set cflags "-I$dir/include"
            if {[file readable $dir/libssl.a]} {
              set ldflags -L$dir
            } elseif {[file readable $dir/lib/libssl.a]} {
              set ldflags -L$dir/lib
            } elseif {[file isdir $dir/lib]} {
              set ldflags "-L$dir -L$dir/lib"
            } else {
              set ldflags -L$dir
            }
          }
          if {[check-for-openssl $msg "$cflags $ldflags"]} {
            incr found
            break
          }
          if {$dir ne ""} {
            set ldflags ""
            set msg "static build of openssl in $dir"
            set ssllibs "$dir/libssl.a $dir/libcrypto.a -lpthread"
            if {[check-for-openssl $msg "$cflags $ldflags" $ssllibs]} {
              incr found
              break
            }
            # This test should arguably fail here if --with-openssl=X
            # points to an invalid X.
          }
        }
      }
    }
    if {$found} {
      define FOSSIL_ENABLE_SSL
      define-append EXTRA_CFLAGS $cflags
      define-append EXTRA_LDFLAGS $ldflags


      if {[info exists ssllibs]} {
        define-append LIBS $ssllibs
      } 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
        }
      }
    } else {
      user-error "OpenSSL not found. Consider --with-openssl=none to disable HTTPS support"
    }
  } else {
    if {[info exists ::zlib_lib]} {
      define-append LIBS $::zlib_lib
    }
  }
}; # handle-with-openssl

#
# CFLAGS_INCLUDE is ONLY for -I... flags and their order is
# significant so that --with-sqlite=PATH's header can shadow our
# own. <s>One caveat with this is that we cannot point
# --with-sqlite=PATH to the root of sqlite3's own build tree because
# that dir has a config.h which ends up shadowing src/config.h,
# breaking our build.</s> (That is no longer true: that that config.h
# was renamed to sqlite_cfg.h at some point.)
#
define CFLAGS_INCLUDE {}

########################################################################
# --with-sqlite=PATH checks for the first it finds of the following...
# - PATH/sqlite3.c and PATH/sqlite3.h
# - PATH/sqlite3.o (and assumes sqlite3.h is with it)
# - PATH/lib/libsqlite3* and PATH/include/sqlite3.h


proc handle-with-sqlite {} {



  set sq3path [opt-val with-sqlite]
  define SQLITE3_SRC.2 {}
  define SQLITE3_OBJ.2 {}
  define SQLITE3_SHELL_SRC.2 {$(SQLITE3_SHELL_SRC.0)}
  if {$sq3path in {tree ""}} {
    msg-result "Using sqlite3.c from this source tree."
  } else {
    # SQLITE3_ORIGIN:
    #   0 = local source tree
    #   1 = use external lib or sqlite3.o
    #   2 = use external sqlite3.c and (if found) shell.c
    define USE_SYSTEM_SQLITE 1
    define SQLITE3_ORIGIN 2
    if {$sq3path != "auto"} {
      if {([file exists $sq3path/sqlite3.c]) &&
          ([file exists $sq3path/sqlite3.h]) } {
        # Prefer sqlite3.[ch] if found.
        define SQLITE3_SRC.2 $sq3path/sqlite3.c
        define SQLITE3_OBJ.2 {$(SQLITE3_OBJ.0)}
        define USE_SYSTEM_SQLITE 2
        define SQLITE3_ORIGIN 2
        if {[file exists $sq3path/shell.c]} {
          define SQLITE3_SHELL_SRC.2 $sq3path/shell.c
        }
        define-append CFLAGS_INCLUDE -I$sq3path
        define-append EXTRA_LDFLAGS -lpthread
        # ^^^ additional -lXXX flags are conservative estimates
        msg-result "Using sqlite3.c and sqlite3.h from $sq3path"
      } elseif {[file exists $sq3path/sqlite3.o]} {
        # Use sqlite3.o if found.
        define SQLITE3_OBJ.2 $sq3path/sqlite3.o
        define-append CFLAGS_INCLUDE -I$sq3path
        define-append EXTRA_LDFLAGS $sq3path/sqlite3.o -lpthread
        # ^^^ additional -lXXX flags are conservative estimates
        msg-result "Using sqlite3.o from $sq3path"
      } elseif { ([llength [glob -nocomplain -directory $sq3path/lib libsqlite3*]] != 0) \
                   && ([file exists $sq3path/include/sqlite3.h]) } {
        # e.g. --with-sqlite=/usr/local. Try $sq3path/lib/libsqlite3*
        # and $sq3path/include/sqlite3.h
        define-append CFLAGS_INCLUDE -I$sq3path/include
        define-append EXTRA_LDFLAGS -L$sq3path/lib -lsqlite3 -lpthread
        # ^^^ additional -lXXX flags are conservative estimates
        msg-result "Using -lsqlite3 from $sq3path"
      } else {
        # Assume $sq3path holds both the lib and header
        cc-with [list -cflags "-I$sq3path -L$sq3path"]
        define-append CFLAGS_INCLUDE -I$sq3path
        define-append EXTRA_LDFLAGS -L$sq3path -lsqlite3 -lpthread
        # ^^^ additional -lXXX flags are conservative estimates
        msg-result "Using -lsqlite3 from $sq3path"
      }
    } elseif {![cc-check-includes sqlite3.h] || ![check-function-in-lib sqlite3_open_v2 sqlite3]} {
      user-error "libsqlite3 not found please install it or specify the location with --with-sqlite"
    }
  }
}; # handle-with-sqlite
handle-with-sqlite
define-append CFLAGS_INCLUDE {-I. -I$(SRCDIR) -I$(SRCDIR_extsrc)}; # must be after handle-with-sqlite

#
# Handle the --with-tcl flag.
#
proc handle-with-tcl {} {
  set tclpath [opt-val with-tcl]
  if {$tclpath eq ""} {
    return
  }
  set tclprivatestubs [opt-bool with-tcl-private-stubs]
  # Note parse-tclconfig-sh is in autosetup/local.tcl
  if {$tclpath eq "1"} {
    set tcldir [file dirname $::autosetup(dir)]/compat/tcl-8.6
    if {$tclprivatestubs} {
      set tclconfig(TCL_INCLUDE_SPEC) -I$tcldir/generic
      set tclconfig(TCL_VERSION) {Private Stubs}
      set tclconfig(TCL_PATCH_LEVEL) {}
      set tclconfig(TCL_PREFIX) $tcldir
      set tclconfig(TCL_LD_FLAGS) { }
    } else {
      # Use the system Tcl. Look in some likely places.
      array set tclconfig [parse-tclconfig-sh \
                             $tcldir/unix $tcldir/win \
                             /usr /usr/local /usr/share /opt/local]
      set msg "on your system"
    }
  } else {
    array set tclconfig [parse-tclconfig-sh $tclpath]
    set msg "at $tclpath"
  }
  if {[opt-bool static]} {
    set tclconfig(TCL_LD_FLAGS) { }
  }
  if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} {
    user-error "Cannot find Tcl $msg"
  }
  set tclstubs [opt-bool with-tcl-stubs]
  if {$tclprivatestubs} {
    define FOSSIL_ENABLE_TCL_PRIVATE_STUBS
    define USE_TCL_STUBS
  } elseif {$tclstubs && $tclconfig(TCL_SUPPORTS_STUBS)} {
    set libs "$tclconfig(TCL_STUB_LIB_SPEC)"
    define FOSSIL_ENABLE_TCL_STUBS
    define USE_TCL_STUBS
  } else {
    set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)"
  }
  set cflags $tclconfig(TCL_INCLUDE_SPEC)
  if {!$tclprivatestubs} {
    set foundtcl 0; # Did we find a working Tcl library?
    cc-with [list -cflags $cflags -libs $libs] {
      if {$tclstubs} {
        if {[cc-check-functions Tcl_InitStubs]} {
          set foundtcl 1
        }
      } else {
        if {[cc-check-functions Tcl_CreateInterp]} {
          set foundtcl 1
        }
      }
    }
    if {!$foundtcl && [string match *-lieee* $libs]} {
      # On some systems, using "-lieee" from TCL_LIB_SPEC appears
      # to cause issues.
      msg-result "Removing \"-lieee\" and retrying for Tcl..."
      set libs [string map [list -lieee ""] $libs]
      cc-with [list -cflags $cflags -libs $libs] {
        if {$tclstubs} {
          if {[cc-check-functions Tcl_InitStubs]} {
            set foundtcl 1
          }
        } else {
          if {[cc-check-functions Tcl_CreateInterp]} {
            set foundtcl 1
          }
        }
      }
    }
    if {!$foundtcl && ![string match *-lpthread* $libs]} {
      # On some systems, TCL_LIB_SPEC appears to be missing
      # "-lpthread".  Try adding it.
      msg-result "Adding \"-lpthread\" and retrying for Tcl..."
      set libs "$libs -lpthread"
      cc-with [list -cflags $cflags -libs $libs] {
        if {$tclstubs} {
          if {[cc-check-functions Tcl_InitStubs]} {
            set foundtcl 1
          }
        } else {
          if {[cc-check-functions Tcl_CreateInterp]} {
            set foundtcl 1
          }
        }
      }
    }
    if {!$foundtcl} {
      if {$tclstubs} {
        user-error "Cannot find a usable Tcl stubs library $msg"
      } else {
        user-error "Cannot find a usable Tcl library $msg"
      }
    }
  }
  set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL)
  msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)"
  if {!$tclprivatestubs} {
    define-append LIBS $libs
  }
  define-append EXTRA_CFLAGS $cflags
  define-append CFLAGS $cflags
  if {[info exists ::zlibpath] && $::zlibpath eq "tree"} {
    #
    # NOTE: When using zlib in the source tree, prevent Tcl from
    #       pulling in the system one.
    #
    set tclconfig(TCL_LD_FLAGS) [string map [list -lz ""] \
                                   $tclconfig(TCL_LD_FLAGS)]
  }
  #
  # NOTE: Remove "-ldl" from the TCL_LD_FLAGS because it will be
  #       be checked for near the bottom of this file.
  #
  set tclconfig(TCL_LD_FLAGS) [string map [list -ldl ""] \
                                 $tclconfig(TCL_LD_FLAGS)]
  define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS)
  define FOSSIL_ENABLE_TCL
}; # handle-with-tcl
handle-with-tcl

# Network functions require libraries on some systems
cc-check-function-in-lib gethostbyname nsl
if {![cc-check-function-in-lib socket {socket network}]} {
  # Last resort, may be Windows
  if {[is_mingw]} {
    define-append LIBS -lwsock32
  }
}

# Some systems (ex: SunOS) require -lrt in order to use nanosleep
cc-check-function-in-lib nanosleep rt

# The SMTP module requires special libraries and headers for MX DNS
# record lookups and such.
cc-check-includes arpa/nameser.h
cc-include-needs bind/resolv.h netinet/in.h
cc-check-includes bind/resolv.h
cc-check-includes resolv.h
if {    !(([cc-check-function-in-lib dn_expand resolv] ||
           [cc-check-function-in-lib   ns_name_uncompress {bind resolv}] ||
           [cc-check-function-in-lib __ns_name_uncompress {bind resolv}]) &&
          ([cc-check-function-in-lib   ns_parserr {bind resolv}] ||
           [cc-check-function-in-lib __ns_parserr {bind resolv}]) &&
          ([cc-check-function-in-lib   res_query {bind resolv}] ||
           [cc-check-function-in-lib __res_query {bind resolv}]))} {
  msg-result "WARNING: SMTP feature will not be able to look up local MX."
}
cc-check-function-in-lib res_9_ns_initparse resolv

# Other nonstandard function checks
cc-check-functions utime
cc-check-functions usleep
cc-check-functions strchrnul
cc-check-functions pledge
cc-check-functions backtrace

# Termux on Android adds "getpass(char *)" to unistd.h, so check this so we
# guard against including it again; use cctest as cc-check-functions and
# cctest_function check for "getpass()" with no args and fail
if {[cctest -link 1 -includes {unistd.h} -code "getpass(0);"]} {
  define FOSSIL_HAVE_GETPASS 1
  msg-result "Found getpass() with unistd.h"
}

# Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE
if {![cc-check-functions getloadavg] ||
    ![cctest -link 1 -includes {unistd.h} -code "double a\[3\]; getloadavg(a,3);"]} {
  define FOSSIL_OMIT_LOAD_AVERAGE 1
  msg-result "Load average support unavailable"
}

# Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars
if {![cc-check-functions getpassphrase]} {
  # Haiku needs this
  cc-check-function-in-lib getpass bsd
}


# Check for the FuseFS library
if {[opt-bool fusefs]} {
  if {[opt-bool static]} {
    msg-result "FuseFS support disabled due to -static"
  } elseif {[cc-check-function-in-lib fuse_mount fuse]} {
    define-append EXTRA_CFLAGS -DFOSSIL_HAVE_FUSEFS
    define FOSSIL_HAVE_FUSEFS 1
    msg-result "FuseFS support enabled"
  }
}

########################################################################
# Checks the compiler for compile_commands.json support.
#
# Returns 1 if supported, else 0. Defines MAKE_COMPILATION_DB to "yes"
# if supported, "no" if not.
proc check-compile-commands {} {
  msg-checking "compile_commands.json support... "
  if {[cctest -lang c -cflags {/dev/null -MJ} -source {}]} {
    # This test reportedly incorrectly succeeds on one of
    # Martin G.'s older systems.
    msg-result "compiler supports compile_commands.json"
    define MAKE_COMPILATION_DB yes
    return 1
  } else {
    msg-result "compiler does not support compile_commands.json"
    define MAKE_COMPILATION_DB no
    return 0
  }
}

define MAKE_COMPILATION_DB no
if {!$outOfTreeBuild} {
  if {[opt-bool compile-commands]} {
    check-compile-commands
  } else {
    puts "Use --compile-commands to enable check for compile-commands-capable compiler."
  }
} else {
  puts "Disabling compile_commands.json check for out-of-tree build."
  # This is an attempt to resolve the problem reported at
  # https://fossil-scm.org/forum/forumpost/d19061d09a8179d0
}

# Add -fsanitize compile and link options late: we don't want the C
# checks above to run with those sanitizers enabled.  It can not only
# be pointless, it can actually break correct tests.
set fsan [opt-val with-sanitizer]
if {[string length $fsan]} {
  define-append  EXTRA_CFLAGS -fsanitize=$fsan
  define-append EXTRA_LDFLAGS -fsanitize=$fsan
  if {[string first "undefined" $fsan] != -1} {
    # We need to link with libubsan if we're compiling under
    # GCC with -fsanitize=undefined.
    cc-check-function-in-lib __ubsan_handle_add_overflow ubsan
  }
}















########################################################################
# @proj-check-emsdk
#
# Emscripten is used for doing in-tree builds of web-based WASM stuff,
# as opposed to WASI-based WASM or WASM binaries we import from other
# places. This is only set up for Unix-style OSes and is untested
916
917
918
919
920
921
922




































923
924
925
926
927
928
929
930
931
932
933
934
  make-template tools/emcc.sh.in
  catch {exec chmod u+x tools/emcc.sh}
} else {
  define EMCC_WRAPPER ""
  define EMCC_OPT ""
  catch {exec rm -f tools/emcc.sh}
}





































# Tag container builds with a prefix of the checkin ID of the version
# of Fossil each one contains.  This not only allows multiple images
# to coexist and multiple containers to be created unamgiguosly from
# them, it also changes the URL we fetch the source tarball from, so
# repeated builds of a given version generate and fetch the source
# tarball once only, keeping it in the local Docker/Podman cache.
set ci [readfile "$::autosetup(srcdir)/manifest.uuid"]
define FOSSIL_CI_PFX [string range $ci 0 11]

make-template Makefile.in
make-config-header autoconfig.h -auto {USE_* FOSSIL_*}







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












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
  make-template tools/emcc.sh.in
  catch {exec chmod u+x tools/emcc.sh}
} else {
  define EMCC_WRAPPER ""
  define EMCC_OPT ""
  catch {exec rm -f tools/emcc.sh}
}

handle-with-openssl

# Finally, append libraries that must be last. This matters more on some
# OSes than others, but is most broadly required for static linking.
if {[opt-bool static]} {
  # Linux can only infer the dependency on pthread from OpenSSL when
  # doing dynamic linkage.
  define-append LIBS -lpthread
}

apply {{} {
  # This started out as a workaround for getting the ordering of -ldl
  # correct in conjunction with openssl in some environments. Then it
  # evolved into a more generic preemptive portability workaround to
  # ensure that certain libraries are always appended to the global
  # LIBS list if they exist on the system. Based on a /chat discussion
  # on 2025-02-27 in the context of check-in [8d3b9bf4d4].
  #
  # Note that [move-lib-to-end] and [lib-actually-exists] are in
  # autosetup/local.tcl.
  set libs [get-define LIBS]
  #puts "**** 1 LIBS: $libs"
  foreach ll {-ldl -lpthread -lm} {
    if {![move-lib-to-end $ll $libs libs]} {
      # $ll was not in the list
      if {[lib-actually-exists $ll]} {
        # Add it to the list "just in case." This will be a no-op on
        # systems where the lib is not actually used.
        lappend libs $ll
      }
    }
  }
  #puts "**** 2 LIBS: $libs"
  define LIBS [join $libs " "]
}}

# Tag container builds with a prefix of the checkin ID of the version
# of Fossil each one contains.  This not only allows multiple images
# to coexist and multiple containers to be created unamgiguosly from
# them, it also changes the URL we fetch the source tarball from, so
# repeated builds of a given version generate and fetch the source
# tarball once only, keeping it in the local Docker/Podman cache.
set ci [readfile "$::autosetup(srcdir)/manifest.uuid"]
define FOSSIL_CI_PFX [string range $ci 0 11]

make-template Makefile.in
make-config-header autoconfig.h -auto {USE_* FOSSIL_*}
Changes to autosetup/local.tcl.
25
26
27
28
29
30
31






























		if {[regexp {^(TCL_[^=]*)=(.*)$} $line -> name value]} {
			set value [regsub -all {\$\{.*\}} $value ""]
			set tclconfig($name) [string trim $value ']
		}
	}
	return [array get tclconfig]
}





































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
		if {[regexp {^(TCL_[^=]*)=(.*)$} $line -> name value]} {
			set value [regsub -all {\$\{.*\}} $value ""]
			set tclconfig($name) [string trim $value ']
		}
	}
	return [array get tclconfig]
}

#
# Given a library link flag, e.g. -lfoo, returns 1 if that library can
# actually be linked to, else returns 0.
proc lib-actually-exists {linkFlag} {
  cctest -link 1 -code "void libActuallyExists(void){}" -libs $linkFlag
}

#
# Given a library flag, e.g. -lfoo, a list of libs, e.g. {-lfoo -lbar
# -lbaz}, and a target variable name, this function appends all
# entries of $libList which do not match $flag to $tgtVar, then
# appends $flag to the end of $tgtVar. Returns the number of matches
# found.
proc move-lib-to-end {flag libList tgtVar} {
  upvar $tgtVar tgt
  set tgt {}
  set found 0
  foreach e $libList {
    if {$flag eq $e} {
      incr found
    } else {
      lappend tgt $e
    }
  }
  if {$found} {
    lappend tgt $flag
  }
  return $found
}
Added extsrc/linenoise-win32.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

/* this code is not standalone
 * it is included into linenoise.c
 * for windows.
 * It is deliberately kept separate so that
 * applications that have no need for windows
 * support can omit this
 */
static DWORD orig_consolemode = 0;

static int flushOutput(struct current *current);
static void outputNewline(struct current *current);

static void refreshStart(struct current *current)
{
    (void)current;
}

static void refreshEnd(struct current *current)
{
    (void)current;
}

static void refreshStartChars(struct current *current)
{
    assert(current->output == NULL);
    /* We accumulate all output here */
    current->output = sb_alloc();
#ifdef USE_UTF8
    current->ubuflen = 0;
#endif
}

static void refreshNewline(struct current *current)
{
    DRL("<nl>");
    outputNewline(current);
}

static void refreshEndChars(struct current *current)
{
    assert(current->output);
    flushOutput(current);
    sb_free(current->output);
    current->output = NULL;
}

static int enableRawMode(struct current *current) {
    DWORD n;
    INPUT_RECORD irec;

    current->outh = GetStdHandle(STD_OUTPUT_HANDLE);
    current->inh = GetStdHandle(STD_INPUT_HANDLE);

    if (!PeekConsoleInput(current->inh, &irec, 1, &n)) {
        return -1;
    }
    if (getWindowSize(current) != 0) {
        return -1;
    }
    if (GetConsoleMode(current->inh, &orig_consolemode)) {
        SetConsoleMode(current->inh, ENABLE_PROCESSED_INPUT);
    }
#ifdef USE_UTF8
    /* XXX is this the right thing to do? */
    SetConsoleCP(65001);
#endif
    return 0;
}

static void disableRawMode(struct current *current)
{
    SetConsoleMode(current->inh, orig_consolemode);
}

void linenoiseClearScreen(void)
{
    /* XXX: This is ugly. Should just have the caller pass a handle */
    struct current current;

    current.outh = GetStdHandle(STD_OUTPUT_HANDLE);

    if (getWindowSize(&current) == 0) {
        COORD topleft = { 0, 0 };
        DWORD n;

        FillConsoleOutputCharacter(current.outh, ' ',
            current.cols * current.rows, topleft, &n);
        FillConsoleOutputAttribute(current.outh,
            FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN,
            current.cols * current.rows, topleft, &n);
        SetConsoleCursorPosition(current.outh, topleft);
    }
}

static void cursorToLeft(struct current *current)
{
    COORD pos;
    DWORD n;

    pos.X = 0;
    pos.Y = (SHORT)current->y;

    FillConsoleOutputAttribute(current->outh,
        FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, current->cols, pos, &n);
    current->x = 0;
}

#ifdef USE_UTF8
static void flush_ubuf(struct current *current)
{
    COORD pos;
    DWORD nwritten;
    pos.Y = (SHORT)current->y;
    pos.X = (SHORT)current->x;
    SetConsoleCursorPosition(current->outh, pos);
    WriteConsoleW(current->outh, current->ubuf, current->ubuflen, &nwritten, 0);
    current->x += current->ubufcols;
    current->ubuflen = 0;
    current->ubufcols = 0;
}

static void add_ubuf(struct current *current, int ch)
{
    /* This code originally by: Author: Mark E. Davis, 1994. */
    static const int halfShift  = 10; /* used for shifting by 10 bits */

    static const DWORD halfBase = 0x0010000UL;
    static const DWORD halfMask = 0x3FFUL;

    #define UNI_SUR_HIGH_START  0xD800
    #define UNI_SUR_HIGH_END    0xDBFF
    #define UNI_SUR_LOW_START   0xDC00
    #define UNI_SUR_LOW_END     0xDFFF

    #define UNI_MAX_BMP 0x0000FFFF

    if (ch > UNI_MAX_BMP) {
        /* convert from unicode to utf16 surrogate pairs
         * There is always space for one extra word in ubuf
         */
        ch -= halfBase;
        current->ubuf[current->ubuflen++] = (WORD)((ch >> halfShift) + UNI_SUR_HIGH_START);
        current->ubuf[current->ubuflen++] = (WORD)((ch & halfMask) + UNI_SUR_LOW_START);
    }
    else {
        current->ubuf[current->ubuflen++] = ch;
    }
    current->ubufcols += utf8_width(ch);
    if (current->ubuflen >= UBUF_MAX_CHARS) {
        flush_ubuf(current);
    }
}
#endif

static int flushOutput(struct current *current)
{
    const char *pt = sb_str(current->output);
    int len = sb_len(current->output);

#ifdef USE_UTF8
    /* convert utf8 in current->output into utf16 in current->ubuf
     */
    while (len) {
        int ch;
        int n = utf8_tounicode(pt, &ch);

        pt += n;
        len -= n;

        add_ubuf(current, ch);
    }
    flush_ubuf(current);
#else
    DWORD nwritten;
    COORD pos;

    pos.Y = (SHORT)current->y;
    pos.X = (SHORT)current->x;

    SetConsoleCursorPosition(current->outh, pos);
    WriteConsoleA(current->outh, pt, len, &nwritten, 0);

    current->x += len;
#endif

    sb_clear(current->output);

    return 0;
}

static int outputChars(struct current *current, const char *buf, int len)
{
    if (len < 0) {
        len = strlen(buf);
    }
    assert(current->output);

    sb_append_len(current->output, buf, len);

    return 0;
}

static void outputNewline(struct current *current)
{
    /* On the last row output a newline to force a scroll */
    if (current->y + 1 == current->rows) {
        outputChars(current, "\n", 1);
    }
    flushOutput(current);
    current->x = 0;
    current->y++;
}

static void setOutputHighlight(struct current *current, const int *props, int nprops)
{
    int colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
    int bold = 0;
    int reverse = 0;
    int i;

    for (i = 0; i < nprops; i++) {
        switch (props[i]) {
            case 0:
               colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
               bold = 0;
               reverse = 0;
               break;
            case 1:
               bold = FOREGROUND_INTENSITY;
               break;
            case 7:
               reverse = 1;
               break;
            case 30:
               colour = 0;
               break;
            case 31:
               colour = FOREGROUND_RED;
               break;
            case 32:
               colour = FOREGROUND_GREEN;
               break;
            case 33:
               colour = FOREGROUND_RED | FOREGROUND_GREEN;
               break;
            case 34:
               colour = FOREGROUND_BLUE;
               break;
            case 35:
               colour = FOREGROUND_RED | FOREGROUND_BLUE;
               break;
            case 36:
               colour = FOREGROUND_BLUE | FOREGROUND_GREEN;
               break;
            case 37:
               colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
               break;
        }
    }

    flushOutput(current);

    if (reverse) {
        SetConsoleTextAttribute(current->outh, BACKGROUND_INTENSITY);
    }
    else {
        SetConsoleTextAttribute(current->outh, colour | bold);
    }
}

static void eraseEol(struct current *current)
{
    COORD pos;
    DWORD n;

    pos.X = (SHORT) current->x;
    pos.Y = (SHORT) current->y;

    FillConsoleOutputCharacter(current->outh, ' ', current->cols - current->x, pos, &n);
}

static void setCursorXY(struct current *current)
{
    COORD pos;

    pos.X = (SHORT) current->x;
    pos.Y = (SHORT) current->y;

    SetConsoleCursorPosition(current->outh, pos);
}


static void setCursorPos(struct current *current, int x)
{
    current->x = x;
    setCursorXY(current);
}

static void cursorUp(struct current *current, int n)
{
    current->y -= n;
    setCursorXY(current);
}

static void cursorDown(struct current *current, int n)
{
    current->y += n;
    setCursorXY(current);
}

static int fd_read(struct current *current)
{
    while (1) {
        INPUT_RECORD irec;
        DWORD n;
        if (WaitForSingleObject(current->inh, INFINITE) != WAIT_OBJECT_0) {
            break;
        }
        if (!ReadConsoleInputW(current->inh, &irec, 1, &n)) {
            break;
        }
        if (irec.EventType == KEY_EVENT) {
            KEY_EVENT_RECORD *k = &irec.Event.KeyEvent;
            if (k->bKeyDown || k->wVirtualKeyCode == VK_MENU) {
                if (k->dwControlKeyState & ENHANCED_KEY) {
                    switch (k->wVirtualKeyCode) {
                     case VK_LEFT:
                        return SPECIAL_LEFT;
                     case VK_RIGHT:
                        return SPECIAL_RIGHT;
                     case VK_UP:
                        return SPECIAL_UP;
                     case VK_DOWN:
                        return SPECIAL_DOWN;
                     case VK_INSERT:
                        return SPECIAL_INSERT;
                     case VK_DELETE:
                        return SPECIAL_DELETE;
                     case VK_HOME:
                        return SPECIAL_HOME;
                     case VK_END:
                        return SPECIAL_END;
                     case VK_PRIOR:
                        return SPECIAL_PAGE_UP;
                     case VK_NEXT:
                        return SPECIAL_PAGE_DOWN;
                     case VK_RETURN:
                        return k->uChar.UnicodeChar;
                    }
                }
                /* Note that control characters are already translated in AsciiChar */
                else if (k->wVirtualKeyCode == VK_CONTROL)
                    continue;
                else {
                    return k->uChar.UnicodeChar;
                }
            }
        }
    }
    return -1;
}

static int getWindowSize(struct current *current)
{
    CONSOLE_SCREEN_BUFFER_INFO info;
    if (!GetConsoleScreenBufferInfo(current->outh, &info)) {
        return -1;
    }
    current->cols = info.dwSize.X;
    current->rows = info.dwSize.Y;
    if (current->cols <= 0 || current->rows <= 0) {
        current->cols = 80;
        return -1;
    }
    current->y = info.dwCursorPosition.Y;
    current->x = info.dwCursorPosition.X;
    return 0;
}
Changes to extsrc/linenoise.c.
890
891
892
893
894
895
896

897
898
899
900
901
902
903
    int colsright; /* refreshLine() cached cols for insert_char() optimisation */
    int colsleft;  /* refreshLine() cached cols for remove_char() optimisation */
    const char *prompt;
    stringbuf *capture; /* capture buffer, or NULL for none. Always null terminated */
    stringbuf *output;  /* used only during refreshLine() - output accumulator */
#if defined(USE_TERMIOS)
    int fd;     /* Terminal fd */

#elif defined(USE_WINCONSOLE)
    HANDLE outh; /* Console output handle */
    HANDLE inh; /* Console input handle */
    int rows;   /* Screen rows */
    int x;      /* Current column during output */
    int y;      /* Current row */
#ifdef USE_UTF8







>







890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
    int colsright; /* refreshLine() cached cols for insert_char() optimisation */
    int colsleft;  /* refreshLine() cached cols for remove_char() optimisation */
    const char *prompt;
    stringbuf *capture; /* capture buffer, or NULL for none. Always null terminated */
    stringbuf *output;  /* used only during refreshLine() - output accumulator */
#if defined(USE_TERMIOS)
    int fd;     /* Terminal fd */
    int pending; /* pending char fd_read_char() */
#elif defined(USE_WINCONSOLE)
    HANDLE outh; /* Console output handle */
    HANDLE inh; /* Console input handle */
    int rows;   /* Screen rows */
    int x;      /* Current column during output */
    int y;      /* Current row */
#ifdef USE_UTF8
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
    /* local modes - choing off, canonical off, no extended functions,
     * no signal chars (^Z,^C) */
    raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
    /* control chars - set return condition: min number of bytes and timer.
     * We want read to return every single byte, without timeout. */
    raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */

    /* put terminal in raw mode after flushing */



    if (tcsetattr(current->fd,TCSADRAIN,&raw) < 0) {
        goto fatal;
    }
    rawmode = 1;
    return 0;
}

static void disableRawMode(struct current *current) {
    /* Don't even check the return value as it's too late. */
    if (rawmode && tcsetattr(current->fd,TCSADRAIN,&orig_termios) != -1)
        rawmode = 0;
}

/* At exit we'll try to fix the terminal to the initial conditions. */
static void linenoiseAtExit(void) {
    if (rawmode) {
        tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios);
    }
    linenoiseHistoryFree();
}

/* gcc/glibc insists that we care about the return code of write!
 * Clarification: This means that a void-cast like "(void) (EXPR)"
 * does not work.







|
>
>
>
|








|






|







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
    /* local modes - choing off, canonical off, no extended functions,
     * no signal chars (^Z,^C) */
    raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
    /* control chars - set return condition: min number of bytes and timer.
     * We want read to return every single byte, without timeout. */
    raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */

    /* put terminal in raw mode. Because we aren't changing any output
     * settings we don't need to use TCSADRAIN and I have seen that hang on
     * OpenBSD when running under a pty
     */
    if (tcsetattr(current->fd,TCSANOW,&raw) < 0) {
        goto fatal;
    }
    rawmode = 1;
    return 0;
}

static void disableRawMode(struct current *current) {
    /* Don't even check the return value as it's too late. */
    if (rawmode && tcsetattr(current->fd,TCSANOW,&orig_termios) != -1)
        rawmode = 0;
}

/* At exit we'll try to fix the terminal to the initial conditions. */
static void linenoiseAtExit(void) {
    if (rawmode) {
        tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
    }
    linenoiseHistoryFree();
}

/* gcc/glibc insists that we care about the return code of write!
 * Clarification: This means that a void-cast like "(void) (EXPR)"
 * does not work.
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

void linenoiseClearScreen(void)
{
    IGNORE_RC(write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7));
}

/**
 * Reads a char from 'fd', waiting at most 'timeout' milliseconds.
 *
 * A timeout of -1 means to wait forever.
 *
 * Returns -1 if no char is received within the time or an error occurs.
 */
static int fd_read_char(int fd, int timeout)
{
    struct pollfd p;
    unsigned char c;







    p.fd = fd;
    p.events = POLLIN;

    if (poll(&p, 1, timeout) == 0) {
        /* timeout */
        return -1;
    }
    if (read(fd, &c, 1) != 1) {
        return -1;
    }
    return c;
}

/**
 * Reads a complete utf-8 character
 * and returns the unicode value, or -1 on error.
 */
static int fd_read(struct current *current)
{
#ifdef USE_UTF8
    char buf[MAX_UTF8_LEN];
    int n;
    int i;
    int c;





    if (read(current->fd, &buf[0], 1) != 1) {
        return -1;
    }
    n = utf8_charlen(buf[0]);
    if (n < 1) {
        return -1;
    }
    for (i = 1; i < n; i++) {
        if (read(current->fd, &buf[i], 1) != 1) {
            return -1;
        }
    }
    /* decode and return the character */
    utf8_tounicode(buf, &c);
    return c;
#else
    return fd_read_char(current->fd, -1);
#endif
}


/**
 * Stores the current cursor column in '*cols'.
 * Returns 1 if OK, or 0 if failed to determine cursor pos.
 */
static int queryCursor(struct current *current, int* cols)
{
    struct esc_parser parser;
    int ch;










    /* Should not be buffering this output, it needs to go immediately */
    assert(current->output == NULL);

    /* control sequence - report cursor location */
    outputChars(current, "\x1b[6n", -1);

    /* Parse the response: ESC [ rows ; cols R */
    initParseEscapeSeq(&parser, 'R');
    while ((ch = fd_read_char(current->fd, 100)) > 0) {
        switch (parseEscapeSequence(&parser, ch)) {
            default:
                continue;
            case EP_END:
                if (parser.numprops == 2 && parser.props[1] < 1000) {
                    *cols = parser.props[1];
                    return 1;
                }
                break;
            case EP_ERROR:


                break;
        }
        /* failed */
        break;
    }

    return 0;
}

/**
 * Updates current->cols with the current window size (width)
 */
static int getWindowSize(struct current *current)







|





|




>
>
>
>
>
>
|






|

















>
>
>
>
|















|












>
>
>
>
>
>
>
>
>









|










>
>





>







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

void linenoiseClearScreen(void)
{
    IGNORE_RC(write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7));
}

/**
 * Reads a char from 'current->fd', waiting at most 'timeout' milliseconds.
 *
 * A timeout of -1 means to wait forever.
 *
 * Returns -1 if no char is received within the time or an error occurs.
 */
static int fd_read_char(struct current *current, int timeout)
{
    struct pollfd p;
    unsigned char c;

    if (current->pending) {
        c = current->pending;
        current->pending = 0;
        return c;
    }

    p.fd = current->fd;
    p.events = POLLIN;

    if (poll(&p, 1, timeout) == 0) {
        /* timeout */
        return -1;
    }
    if (read(current->fd, &c, 1) != 1) {
        return -1;
    }
    return c;
}

/**
 * Reads a complete utf-8 character
 * and returns the unicode value, or -1 on error.
 */
static int fd_read(struct current *current)
{
#ifdef USE_UTF8
    char buf[MAX_UTF8_LEN];
    int n;
    int i;
    int c;

    if (current->pending) {
        buf[0] = current->pending;
        current->pending = 0;
    }
    else if (read(current->fd, &buf[0], 1) != 1) {
        return -1;
    }
    n = utf8_charlen(buf[0]);
    if (n < 1) {
        return -1;
    }
    for (i = 1; i < n; i++) {
        if (read(current->fd, &buf[i], 1) != 1) {
            return -1;
        }
    }
    /* decode and return the character */
    utf8_tounicode(buf, &c);
    return c;
#else
    return fd_read_char(current, -1);
#endif
}


/**
 * Stores the current cursor column in '*cols'.
 * Returns 1 if OK, or 0 if failed to determine cursor pos.
 */
static int queryCursor(struct current *current, int* cols)
{
    struct esc_parser parser;
    int ch;
    /* Unfortunately we don't have any persistent state, so assume
     * a process will only ever interact with one terminal at a time.
     */
    static int query_cursor_failed;

    if (query_cursor_failed) {
        /* If it ever fails, don't try again */
        return 0;
    }

    /* Should not be buffering this output, it needs to go immediately */
    assert(current->output == NULL);

    /* control sequence - report cursor location */
    outputChars(current, "\x1b[6n", -1);

    /* Parse the response: ESC [ rows ; cols R */
    initParseEscapeSeq(&parser, 'R');
    while ((ch = fd_read_char(current, 100)) > 0) {
        switch (parseEscapeSequence(&parser, ch)) {
            default:
                continue;
            case EP_END:
                if (parser.numprops == 2 && parser.props[1] < 1000) {
                    *cols = parser.props[1];
                    return 1;
                }
                break;
            case EP_ERROR:
                /* Push back the character that caused the error */
                current->pending = ch;
                break;
        }
        /* failed */
        break;
    }
    query_cursor_failed = 1;
    return 0;
}

/**
 * Updates current->cols with the current window size (width)
 */
static int getWindowSize(struct current *current)
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
 * chars to determine if this is a known special key.
 *
 * Returns SPECIAL_NONE if unrecognised, or -1 if EOF.
 *
 * If no additional char is received within a short time,
 * CHAR_ESCAPE is returned.
 */
static int check_special(int fd)
{
    int c = fd_read_char(fd, 50);
    int c2;

    if (c < 0) {
        return CHAR_ESCAPE;
    }
    else if (c >= 'a' && c <= 'z') {
        /* esc-a => meta-a */
        return meta(c);
    }

    c2 = fd_read_char(fd, 50);
    if (c2 < 0) {
        return c2;
    }
    if (c == '[' || c == 'O') {
        /* Potential arrow key */
        switch (c2) {
            case 'A':







|

|










|







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
 * chars to determine if this is a known special key.
 *
 * Returns SPECIAL_NONE if unrecognised, or -1 if EOF.
 *
 * If no additional char is received within a short time,
 * CHAR_ESCAPE is returned.
 */
static int check_special(struct current *current)
{
    int c = fd_read_char(current, 50);
    int c2;

    if (c < 0) {
        return CHAR_ESCAPE;
    }
    else if (c >= 'a' && c <= 'z') {
        /* esc-a => meta-a */
        return meta(c);
    }

    c2 = fd_read_char(current, 50);
    if (c2 < 0) {
        return c2;
    }
    if (c == '[' || c == 'O') {
        /* Potential arrow key */
        switch (c2) {
            case 'A':
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
                return SPECIAL_END;
            case 'H':
                return SPECIAL_HOME;
        }
    }
    if (c == '[' && c2 >= '1' && c2 <= '8') {
        /* extended escape */
        c = fd_read_char(fd, 50);
        if (c == '~') {
            switch (c2) {
                case '2':
                    return SPECIAL_INSERT;
                case '3':
                    return SPECIAL_DELETE;
                case '5':
                    return SPECIAL_PAGE_UP;
                case '6':
                    return SPECIAL_PAGE_DOWN;
                case '7':
                    return SPECIAL_HOME;
                case '8':
                    return SPECIAL_END;
            }
        }
        while (c != -1 && c != '~') {
            /* .e.g \e[12~ or '\e[11;2~   discard the complete sequence */
            c = fd_read_char(fd, 50);
        }
    }

    return SPECIAL_NONE;
}
#endif








|


















|







1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
                return SPECIAL_END;
            case 'H':
                return SPECIAL_HOME;
        }
    }
    if (c == '[' && c2 >= '1' && c2 <= '8') {
        /* extended escape */
        c = fd_read_char(current, 50);
        if (c == '~') {
            switch (c2) {
                case '2':
                    return SPECIAL_INSERT;
                case '3':
                    return SPECIAL_DELETE;
                case '5':
                    return SPECIAL_PAGE_UP;
                case '6':
                    return SPECIAL_PAGE_DOWN;
                case '7':
                    return SPECIAL_HOME;
                case '8':
                    return SPECIAL_END;
            }
        }
        while (c != -1 && c != '~') {
            /* .e.g \e[12~ or '\e[11;2~   discard the complete sequence */
            c = fd_read_char(current, 50);
        }
    }

    return SPECIAL_NONE;
}
#endif

2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
                rbuf[p_ind] = 0;
                rlen = strlen(rbuf);
            }
            continue;
        }
#ifdef USE_TERMIOS
        if (c == CHAR_ESCAPE) {
            c = check_special(current->fd);
        }
#endif
        if (c == ctrl('R')) {
            /* Search for the previous (earlier) match */
            if (searchpos > 0) {
                searchpos--;
            }







|







2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
                rbuf[p_ind] = 0;
                rlen = strlen(rbuf);
            }
            continue;
        }
#ifdef USE_TERMIOS
        if (c == CHAR_ESCAPE) {
            c = check_special(current);
        }
#endif
        if (c == ctrl('R')) {
            /* Search for the previous (earlier) match */
            if (searchpos > 0) {
                searchpos--;
            }
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
            /* reverse incremental search will provide an alternative keycode or 0 for none */
            c = reverseIncrementalSearch(current);
            /* go on to process the returned char normally */
        }

#ifdef USE_TERMIOS
        if (c == CHAR_ESCAPE) {   /* escape sequence */
            c = check_special(current->fd);
        }
#endif
        if (c == -1) {
            /* Return on errors */
            return sb_len(current->buf);
        }








|







2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
            /* reverse incremental search will provide an alternative keycode or 0 for none */
            c = reverseIncrementalSearch(current);
            /* go on to process the returned char normally */
        }

#ifdef USE_TERMIOS
        if (c == CHAR_ESCAPE) {   /* escape sequence */
            c = check_special(current);
        }
#endif
        if (c == -1) {
            /* Return on errors */
            return sb_len(current->buf);
        }

Changes to extsrc/pikchr-worker.js.
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

  {
    pikchr: source code for the pikchr,
    darkMode: boolean true to adjust colors for a dark color scheme,
    cssClass: CSS class name to add to the SVG
  }

  Workers-to-Main types

  - stdout, stderr: indicate stdout/stderr output from the wasm
  layer. The data property is the string of the output, noting
  that the emscripten binding emits these one line at a time. Thus,
  if a C-side puts() emits multiple lines in a single call, the JS
  side will see that as multiple calls. Example:

  {type:'stdout', data: 'Hi, world.'}

  - module: Status text. This is intended to alert the main thread
  about module loading status so that, e.g., the main thread can
  update a progress widget and DTRT when the module is finished
  loading and available for work. Status messages come in the form
  
  {type:'module', data:{
  type:'status',
  data: {text:string|null, step:1-based-integer}
  }

  with an incrementing step value for each subsequent message. When
  the module loading is complete, a message with a text value of
  null is posted.

  - pikchr: 

  {type: 'pikchr',
    data:{
      pikchr: input text,
      result: rendered result (SVG on success, HTML on error),
      isError: bool, true if .pikchr holds an error report,
      flags: integer: flags used to configure the pikchr rendering,







|













|









|







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

  {
    pikchr: source code for the pikchr,
    darkMode: boolean true to adjust colors for a dark color scheme,
    cssClass: CSS class name to add to the SVG
  }

  Workers-to-Main message types:

  - stdout, stderr: indicate stdout/stderr output from the wasm
  layer. The data property is the string of the output, noting
  that the emscripten binding emits these one line at a time. Thus,
  if a C-side puts() emits multiple lines in a single call, the JS
  side will see that as multiple calls. Example:

  {type:'stdout', data: 'Hi, world.'}

  - module: Status text. This is intended to alert the main thread
  about module loading status so that, e.g., the main thread can
  update a progress widget and DTRT when the module is finished
  loading and available for work. Status messages come in the form

  {type:'module', data:{
  type:'status',
  data: {text:string|null, step:1-based-integer}
  }

  with an incrementing step value for each subsequent message. When
  the module loading is complete, a message with a text value of
  null is posted.

  - pikchr:

  {type: 'pikchr',
    data:{
      pikchr: input text,
      result: rendered result (SVG on success, HTML on error),
      isError: bool, true if .pikchr holds an error report,
      flags: integer: flags used to configure the pikchr rendering,
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
            pikchrModule.stackRestore(stack);
            wMsg('working','end');
          }
          return;
    };
    console.warn("Unknown pikchr-worker message type:",ev);
  };
  
  /**
     emscripten module for use with build mode -sMODULARIZE.
  */
  const pikchrModule = {
    print: function(){wMsg('stdout', Array.prototype.slice.call(arguments));},
    printErr: stderr,
    /**







|







160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
            pikchrModule.stackRestore(stack);
            wMsg('working','end');
          }
          return;
    };
    console.warn("Unknown pikchr-worker message type:",ev);
  };

  /**
     emscripten module for use with build mode -sMODULARIZE.
  */
  const pikchrModule = {
    print: function(){wMsg('stdout', Array.prototype.slice.call(arguments));},
    printErr: stderr,
    /**
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218

219
220
221
      wMsg('module',{
        type:'status',
        data:{step: ++f.last.step, text: text||null}
      });
    }
  };

  importScripts('pikchr.js');
  /**
     initPikchrModule() is installed via pikchr.js due to
     building with:

     emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initPikchrModule
  */
  initPikchrModule(pikchrModule).then(function(thisModule){

    wMsg('pikchr-ready');
  });
})();







|







>
|


204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
      wMsg('module',{
        type:'status',
        data:{step: ++f.last.step, text: text||null}
      });
    }
  };

  importScripts('pikchr-v2813665466.js');
  /**
     initPikchrModule() is installed via pikchr.js due to
     building with:

     emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initPikchrModule
  */
  initPikchrModule(pikchrModule).then(function(thisModule){
    //globalThis.M = pikchrModule; console.warn("pikchrModule=globalThis.M=",globalThis.M);
    wMsg('pikchr-ready', pikchrModule.ccall('pikchr_version','string'));
  });
})();
Changes to extsrc/pikchr.c.
1
2
3







































4
5
6
7
8
9
10
/* This file is automatically generated by Lemon from input grammar
** source file "pikchr.y".
*/







































/*
** Zero-Clause BSD license:
**
** Copyright (C) 2020-09-01 by D. Richard Hipp <drh@sqlite.org>
**
** Permission to use, copy, modify, and/or distribute this software for
** any purpose with or without fee is hereby granted.



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







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
/* This file is automatically generated by Lemon from input grammar
** source file "pikchr.y".
*/
/*
** 2000-05-29
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Driver template for the LEMON parser generator.
**
** The "lemon" program processes an LALR(1) input grammar file, then uses
** this template to construct a parser.  The "lemon" program inserts text
** at each "%%" line.  Also, any "P-a-r-s-e" identifier prefix (without the
** interstitial "-" characters) contained in this template is changed into
** the value of the %name directive from the grammar.  Otherwise, the content
** of this template is copied straight through into the generate parser
** source file.
**
** The following is the concatenation of all %include directives from the
** input grammar file:
*/
/************ Begin %include sections from the grammar ************************/
#line 1 "VERSION.h"
#define MANIFEST_UUID "8a43b020141f772a0ac45291a7fd73041d2efba5e3665c6bd2f334ad9b2e9845"
#define MANIFEST_VERSION "[8a43b02014]"
#define MANIFEST_DATE "2025-03-19 16:19:43"
#define MANIFEST_YEAR "2025"
#define MANIFEST_ISODATE "20250319161943"
#define MANIFEST_NUMERIC_DATE 20250319
#define MANIFEST_NUMERIC_TIME 161943
#define RELEASE_VERSION "1.0"
#define RELEASE_VERSION_NUMBER 10000
#define RELEASE_RESOURCE_VERSION 1,0,0,0
#define COMPILER "gcc-13.3.0"
#line 2 "pikchr.y"

/*
** Zero-Clause BSD license:
**
** Copyright (C) 2020-09-01 by D. Richard Hipp <drh@sqlite.org>
**
** Permission to use, copy, modify, and/or distribute this software for
** any purpose with or without fee is hereby granted.
123
124
125
126
127
128
129












130
131
132
133
134
135
136
#include <ctype.h>
#include <math.h>
#include <assert.h>
#define count(X) (sizeof(X)/sizeof(X[0]))
#ifndef M_PI
# define M_PI 3.1415926535897932385
#endif













/* Limit the number of tokens in a single script to avoid run-away
** macro expansion attacks.  See forum post
**    https://pikchr.org/home/forumpost/ef8684c6955a411a
*/
#ifndef PIKCHR_TOKEN_LIMIT
# define PIKCHR_TOKEN_LIMIT 100000







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







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
#include <ctype.h>
#include <math.h>
#include <assert.h>
#define count(X) (sizeof(X)/sizeof(X[0]))
#ifndef M_PI
# define M_PI 3.1415926535897932385
#endif

/*
** Typesafe version of ctype.h macros.  Cygwin requires this, I'm told.
*/
#define IsUpper(X)  isupper((unsigned char)(X))
#define IsLower(X)  islower((unsigned char)(X))
#define ToLower(X)  tolower((unsigned char)(X))
#define IsDigit(X)  isdigit((unsigned char)(X))
#define IsXDigit(X) isxdigit((unsigned char)(X))
#define IsSpace(X)  isspace((unsigned char)(X))
#define IsAlnum(X)  isalnum((unsigned char)(X))


/* Limit the number of tokens in a single script to avoid run-away
** macro expansion attacks.  See forum post
**    https://pikchr.org/home/forumpost/ef8684c6955a411a
*/
#ifndef PIKCHR_TOKEN_LIMIT
# define PIKCHR_TOKEN_LIMIT 100000
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
static int pik_bbox_contains_point(PBox*,PPoint*);
static void pik_bbox_init(PBox*);
static void pik_bbox_addbox(PBox*,PBox*);
static void pik_bbox_add_xy(PBox*,PNum,PNum);
static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry);
static void pik_add_txt(Pik*,PToken*,int);
static int pik_text_length(const PToken *pToken, const int isMonospace);
static void pik_size_to_fit(Pik*,PToken*,int);
static int pik_text_position(int,PToken*);
static PNum pik_property_of(PObj*,PToken*);
static PNum pik_func(Pik*,PToken*,PNum,PNum);
static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2);
static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt);
static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt);
static void pik_same(Pik *p, PObj*, PToken*);
static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PObj *pObj);
static PToken pik_next_semantic_token(PToken *pThis);
static void pik_compute_layout_settings(Pik*);
static void pik_behind(Pik*,PObj*);
static PObj *pik_assert(Pik*,PNum,PToken*,PNum);
static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*);
static PNum pik_dist(PPoint*,PPoint*);
static void pik_add_macro(Pik*,PToken *pId,PToken *pCode);


#line 523 "pikchr.c"
/**************** End of %include directives **********************************/
/* These constants specify the various numeric values for terminal symbols.
***************** Begin token definitions *************************************/
#ifndef T_ID
#define T_ID                              1
#define T_EDGEPT                          2
#define T_OF                              3







|

















|







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
static int pik_bbox_contains_point(PBox*,PPoint*);
static void pik_bbox_init(PBox*);
static void pik_bbox_addbox(PBox*,PBox*);
static void pik_bbox_add_xy(PBox*,PNum,PNum);
static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry);
static void pik_add_txt(Pik*,PToken*,int);
static int pik_text_length(const PToken *pToken, const int isMonospace);
static void pik_size_to_fit(Pik*,PObj*,PToken*,int);
static int pik_text_position(int,PToken*);
static PNum pik_property_of(PObj*,PToken*);
static PNum pik_func(Pik*,PToken*,PNum,PNum);
static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2);
static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt);
static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt);
static void pik_same(Pik *p, PObj*, PToken*);
static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PObj *pObj);
static PToken pik_next_semantic_token(PToken *pThis);
static void pik_compute_layout_settings(Pik*);
static void pik_behind(Pik*,PObj*);
static PObj *pik_assert(Pik*,PNum,PToken*,PNum);
static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*);
static PNum pik_dist(PPoint*,PPoint*);
static void pik_add_macro(Pik*,PToken *pId,PToken *pCode);


#line 549 "pikchr.c"
/**************** End of %include directives **********************************/
/* These constants specify the various numeric values for terminal symbols.
***************** Begin token definitions *************************************/
#ifndef T_ID
#define T_ID                              1
#define T_EDGEPT                          2
#define T_OF                              3
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
#define T_CODEBLOCK                      19
#define T_FILL                           20
#define T_COLOR                          21
#define T_THICKNESS                      22
#define T_PRINT                          23
#define T_STRING                         24
#define T_COMMA                          25

#define T_CLASSNAME                      26
#define T_LB                             27
#define T_RB                             28
#define T_UP                             29
#define T_DOWN                           30
#define T_LEFT                           31
#define T_RIGHT                          32
#define T_CLOSE                          33
#define T_CHOP                           34
#define T_FROM                           35
#define T_TO                             36
#define T_THEN                           37
#define T_HEADING                        38
#define T_GO                             39
#define T_AT                             40
#define T_WITH                           41
#define T_SAME                           42
#define T_AS                             43
#define T_FIT                            44
#define T_BEHIND                         45
#define T_UNTIL                          46
#define T_EVEN                           47
#define T_DOT_E                          48
#define T_HEIGHT                         49
#define T_WIDTH                          50
#define T_RADIUS                         51
#define T_DIAMETER                       52
#define T_DOTTED                         53
#define T_DASHED                         54
#define T_CW                             55
#define T_CCW                            56
#define T_LARROW                         57
#define T_RARROW                         58
#define T_LRARROW                        59
#define T_INVIS                          60
#define T_THICK                          61
#define T_THIN                           62
#define T_SOLID                          63
#define T_CENTER                         64
#define T_LJUST                          65
#define T_RJUST                          66
#define T_ABOVE                          67
#define T_BELOW                          68
#define T_ITALIC                         69
#define T_BOLD                           70
#define T_MONO                           71
#define T_ALIGNED                        72
#define T_BIG                            73
#define T_SMALL                          74
#define T_AND                            75
#define T_LT                             76
#define T_GT                             77
#define T_ON                             78
#define T_WAY                            79
#define T_BETWEEN                        80
#define T_THE                            81
#define T_NTH                            82
#define T_VERTEX                         83
#define T_TOP                            84
#define T_BOTTOM                         85
#define T_START                          86
#define T_END                            87
#define T_IN                             88
#define T_THIS                           89
#define T_DOT_U                          90
#define T_LAST                           91
#define T_NUMBER                         92
#define T_FUNC1                          93
#define T_FUNC2                          94
#define T_DIST                           95
#define T_DOT_XY                         96
#define T_X                              97
#define T_Y                              98
#define T_DOT_L                          99
#endif
/**************** End token definitions ***************************************/

/* The next sections is a series of control #defines.
** various aspects of the generated parser.
**    YYCODETYPE         is the data type used to store the integer codes
**                       that represent terminal and non-terminal symbols.







>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|







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
#define T_CODEBLOCK                      19
#define T_FILL                           20
#define T_COLOR                          21
#define T_THICKNESS                      22
#define T_PRINT                          23
#define T_STRING                         24
#define T_COMMA                          25
#define T_ISODATE                        26
#define T_CLASSNAME                      27
#define T_LB                             28
#define T_RB                             29
#define T_UP                             30
#define T_DOWN                           31
#define T_LEFT                           32
#define T_RIGHT                          33
#define T_CLOSE                          34
#define T_CHOP                           35
#define T_FROM                           36
#define T_TO                             37
#define T_THEN                           38
#define T_HEADING                        39
#define T_GO                             40
#define T_AT                             41
#define T_WITH                           42
#define T_SAME                           43
#define T_AS                             44
#define T_FIT                            45
#define T_BEHIND                         46
#define T_UNTIL                          47
#define T_EVEN                           48
#define T_DOT_E                          49
#define T_HEIGHT                         50
#define T_WIDTH                          51
#define T_RADIUS                         52
#define T_DIAMETER                       53
#define T_DOTTED                         54
#define T_DASHED                         55
#define T_CW                             56
#define T_CCW                            57
#define T_LARROW                         58
#define T_RARROW                         59
#define T_LRARROW                        60
#define T_INVIS                          61
#define T_THICK                          62
#define T_THIN                           63
#define T_SOLID                          64
#define T_CENTER                         65
#define T_LJUST                          66
#define T_RJUST                          67
#define T_ABOVE                          68
#define T_BELOW                          69
#define T_ITALIC                         70
#define T_BOLD                           71
#define T_MONO                           72
#define T_ALIGNED                        73
#define T_BIG                            74
#define T_SMALL                          75
#define T_AND                            76
#define T_LT                             77
#define T_GT                             78
#define T_ON                             79
#define T_WAY                            80
#define T_BETWEEN                        81
#define T_THE                            82
#define T_NTH                            83
#define T_VERTEX                         84
#define T_TOP                            85
#define T_BOTTOM                         86
#define T_START                          87
#define T_END                            88
#define T_IN                             89
#define T_THIS                           90
#define T_DOT_U                          91
#define T_LAST                           92
#define T_NUMBER                         93
#define T_FUNC1                          94
#define T_FUNC2                          95
#define T_DIST                           96
#define T_DOT_XY                         97
#define T_X                              98
#define T_Y                              99
#define T_DOT_L                          100
#endif
/**************** End token definitions ***************************************/

/* The next sections is a series of control #defines.
** various aspects of the generated parser.
**    YYCODETYPE         is the data type used to store the integer codes
**                       that represent terminal and non-terminal symbols.
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
**    YY_MAX_DSTRCTR     Maximum symbol value that has a destructor
*/
#ifndef INTERFACE
# define INTERFACE 1
#endif
/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned char
#define YYNOCODE 136
#define YYACTIONTYPE unsigned short int
#define pik_parserTOKENTYPE PToken
typedef union {
  int yyinit;
  pik_parserTOKENTYPE yy0;
  PNum yy21;
  PPoint yy63;
  PRel yy72;
  PObj* yy162;


  short int yy188;
  PList* yy235;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
#endif
#define pik_parserARG_SDECL
#define pik_parserARG_PDECL
#define pik_parserARG_PARAM







|





|
<
|
|
>
>
|
<







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
**    YY_MAX_DSTRCTR     Maximum symbol value that has a destructor
*/
#ifndef INTERFACE
# define INTERFACE 1
#endif
/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned char
#define YYNOCODE 138
#define YYACTIONTYPE unsigned short int
#define pik_parserTOKENTYPE PToken
typedef union {
  int yyinit;
  pik_parserTOKENTYPE yy0;
  PList* yy23;

  PRel yy28;
  PObj* yy54;
  PNum yy129;
  PPoint yy187;
  short int yy272;

} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
#endif
#define pik_parserARG_SDECL
#define pik_parserARG_PDECL
#define pik_parserARG_PARAM
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
#define pik_parserCTX_PARAM ,p
#define pik_parserCTX_FETCH Pik *p=yypParser->p;
#define pik_parserCTX_STORE yypParser->p=p;
#define YYFALLBACK 1
#define YYNSTATE             164
#define YYNRULE              156
#define YYNRULE_WITH_ACTION  116
#define YYNTOKEN             100
#define YY_MAX_SHIFT         163
#define YY_MIN_SHIFTREDUCE   287
#define YY_MAX_SHIFTREDUCE   442
#define YY_ERROR_ACTION      443
#define YY_ACCEPT_ACTION     444
#define YY_NO_ACTION         445
#define YY_MIN_REDUCE        446
#define YY_MAX_REDUCE        601
#define YY_MIN_DSTRCTR       100
#define YY_MAX_DSTRCTR       103
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))

/* Define the yytestcase() macro to be a no-op if is not already defined
** otherwise.
**
** Applications can choose to define yytestcase() in the %include section







|








|
|







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
#define pik_parserCTX_PARAM ,p
#define pik_parserCTX_FETCH Pik *p=yypParser->p;
#define pik_parserCTX_STORE yypParser->p=p;
#define YYFALLBACK 1
#define YYNSTATE             164
#define YYNRULE              156
#define YYNRULE_WITH_ACTION  116
#define YYNTOKEN             101
#define YY_MAX_SHIFT         163
#define YY_MIN_SHIFTREDUCE   287
#define YY_MAX_SHIFTREDUCE   442
#define YY_ERROR_ACTION      443
#define YY_ACCEPT_ACTION     444
#define YY_NO_ACTION         445
#define YY_MIN_REDUCE        446
#define YY_MAX_REDUCE        601
#define YY_MIN_DSTRCTR       101
#define YY_MAX_DSTRCTR       104
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))

/* Define the yytestcase() macro to be a no-op if is not already defined
** otherwise.
**
** Applications can choose to define yytestcase() in the %include section
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
814
815
816
817
818
819
820
821
822
823

824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
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
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894

895
896
897
898
899
900
901
902
903
904
905
906
907
908
909

910
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
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
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
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
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
**  yy_shift_ofst[]    For each state, the offset into yy_action for
**                     shifting terminals.
**  yy_reduce_ofst[]   For each state, the offset into yy_action for
**                     shifting non-terminals after a reduce.
**  yy_default[]       Default action for each state.
**
*********** Begin parsing tables **********************************************/
#define YY_ACTTAB_COUNT (1313)
static const YYACTIONTYPE yy_action[] = {
 /*     0 */   575,  495,  161,  119,   25,  452,   29,   74,  129,  148,
 /*    10 */   575,  492,  161,  119,  453,  113,  120,  161,  119,  530,
 /*    20 */   427,  428,  339,  559,   81,   30,  560,  561,  575,   64,
 /*    30 */    63,   62,   61,  322,  323,    9,    8,   33,  149,   32,
 /*    40 */     7,   71,  127,   38,  335,   66,   48,   37,   28,  339,
 /*    50 */   339,  339,  339,  425,  426,  340,  341,  342,  343,  344,
 /*    60 */   345,  346,  347,  348,  474,  528,  161,  119,  577,   77,
 /*    70 */   577,   73,  306,  148,  474,  533,  161,  119,  112,  113,
 /*    80 */   120,  161,  119,  128,  427,  428,  339,   31,   81,  531,
 /*    90 */   161,  119,  474,   35,  330,  378,  158,  322,  323,    9,
 /*   100 */     8,   33,  149,   32,    7,   71,  127,  328,  335,   66,
 /*   110 */   579,  378,  158,  339,  339,  339,  339,  425,  426,  340,
 /*   120 */   341,  342,  343,  344,  345,  346,  347,  348,  394,  435,
 /*   130 */    46,   59,   60,   64,   63,   62,   61,  357,   36,  376,
 /*   140 */    54,   51,    2,   47,  403,   13,  297,  411,  412,  413,
 /*   150 */   414,   80,  162,  308,   79,  133,  310,  126,  441,  440,
 /*   160 */   118,  123,   83,  404,  405,  406,  408,   80,   84,  308,
 /*   170 */    79,  299,  411,  412,  413,  414,  118,   69,  350,  350,


 /*   180 */   350,  350,  350,  350,  350,  350,  350,  350,  350,   62,
 /*   190 */    61,  434,   64,   63,   62,   61,  313,  398,  399,  427,
 /*   200 */   428,  339,  380,  157,   64,   63,   62,   61,  122,  106,
 /*   210 */   535,  436,  437,  438,  439,  298,  375,  391,  117,  393,
 /*   220 */   155,  154,  153,  394,  435,   49,   59,   60,  339,  339,
 /*   230 */   339,  339,  425,  426,  376,    3,    4,    2,   64,   63,
 /*   240 */    62,   61,  156,  156,  156,  394,  379,  159,   59,   60,
 /*   250 */    76,   67,  535,  441,  440,    5,  102,    6,  535,   42,
 /*   260 */   131,  535,   69,  107,  301,  302,  303,  394,  305,   15,
 /*   270 */    59,   60,  120,  161,  119,  446,  463,  424,  376,  423,
 /*   280 */     1,   42,  397,   78,   78,   36,  434,   11,  394,  435,
 /*   290 */   356,   59,   60,   12,  152,  139,  432,   14,   16,  376,
 /*   300 */    18,   65,    2,  138,  106,  430,  436,  437,  438,  439,

 /*   310 */    44,  375,   19,  117,  393,  155,  154,  153,  441,  440,
 /*   320 */   142,  140,   64,   63,   62,   61,  106,   20,   68,  376,
 /*   330 */   359,  107,   23,  375,   45,  117,  393,  155,  154,  153,
 /*   340 */   120,  161,  119,   55,  463,  114,   26,   57,  106,  147,
 /*   350 */   146,  434,  569,   58,  392,  375,   43,  117,  393,  155,
 /*   360 */   154,  153,  152,  384,   64,   63,   62,   61,  382,  106,
 /*   370 */   383,  436,  437,  438,  439,  377,  375,   70,  117,  393,
 /*   380 */   155,  154,  153,  160,   39,   22,   21,  445,  142,  140,
 /*   390 */    64,   63,   62,   61,   24,   17,  145,  141,  431,  108,
 /*   400 */   445,  445,  445,  391,  445,  445,  375,  445,  117,  445,
 /*   410 */   445,   55,   74,  445,  148,  445,  445,  147,  146,  124,
 /*   420 */   113,  120,  161,  119,   43,  445,  445,  142,  140,   64,
 /*   430 */    63,   62,   61,  445,  394,  445,  445,   59,   60,   64,
 /*   440 */    63,   62,   61,  149,  445,  376,  445,  445,   42,  445,
 /*   450 */    55,  445,  391,   22,   21,  445,  147,  146,  445,  445,
 /*   460 */    52,  445,   24,   43,  145,  141,  431,  394,  445,  445,
 /*   470 */    59,   60,   64,   63,   62,   61,  445,  445,  376,  132,
 /*   480 */   130,   42,  445,  445,  445,  355,  156,  156,  156,  445,
 /*   490 */   445,  445,   22,   21,  445,  394,  473,  445,   59,   60,
 /*   500 */   445,   24,  445,  145,  141,  431,  376,  445,  107,   42,
 /*   510 */    64,   63,   62,   61,  445,  106,  445,  120,  161,  119,
 /*   520 */   445,  478,  375,  354,  117,  393,  155,  154,  153,  445,
 /*   530 */   394,  143,  473,   59,   60,   64,   63,   62,   61,  152,
 /*   540 */   445,  376,  445,  445,   42,  445,  445,  445,  106,   64,
 /*   550 */    63,   62,   61,  445,  445,  375,   50,  117,  393,  155,
 /*   560 */   154,  153,  445,  394,  144,  445,   59,   60,  445,  445,
 /*   570 */    53,   72,  445,  148,  376,  445,  106,   42,  125,  113,
 /*   580 */   120,  161,  119,  375,  445,  117,  393,  155,  154,  153,
 /*   590 */   394,  445,  445,   59,   60,  445,  445,  445,  445,  445,
 /*   600 */   445,  102,  149,  445,   42,  445,   74,  445,  148,  445,
 /*   610 */   445,  106,  445,  497,  113,  120,  161,  119,  375,  445,

 /*   620 */   117,  393,  155,  154,  153,  394,  445,  445,   59,   60,
 /*   630 */   445,  445,   88,  445,  445,  445,  376,  149,  445,   40,
 /*   640 */   445,  120,  161,  119,  106,  445,  445,  435,  110,  110,
 /*   650 */   445,  375,  445,  117,  393,  155,  154,  153,  394,  445,
 /*   660 */   445,   59,   60,  152,   85,  445,  445,  445,  445,  376,
 /*   670 */   445,  106,   41,  120,  161,  119,  441,  440,  375,  445,
 /*   680 */   117,  393,  155,  154,  153,  448,  454,   29,  445,  445,
 /*   690 */    74,  450,  148,   75,   88,  152,  445,  496,  113,  120,
 /*   700 */   161,  119,  163,  120,  161,  119,  106,   27,  445,  434,
 /*   710 */   111,  111,  445,  375,  445,  117,  393,  155,  154,  153,
 /*   720 */   445,  149,  445,  445,  445,  152,   74,  445,  148,  436,
 /*   730 */   437,  438,  439,  490,  113,  120,  161,  119,  445,  106,
 /*   740 */   121,  447,  454,   29,  445,  445,  375,  450,  117,  393,
 /*   750 */   155,  154,  153,  445,  445,  445,  445,  149,  163,   74,
 /*   760 */   445,  148,  444,   27,  445,  445,  484,  113,  120,  161,
 /*   770 */   119,  445,  445,  445,   74,  445,  148,  445,  445,  445,
 /*   780 */   445,  483,  113,  120,  161,  119,   74,  445,  148,   86,
 /*   790 */   149,  445,  445,  480,  113,  120,  161,  119,  120,  161,
 /*   800 */   119,  445,   74,  445,  148,  149,  445,  445,  445,  134,
 /*   810 */   113,  120,  161,  119,   74,  445,  148,  149,  445,  445,
 /*   820 */   152,  517,  113,  120,  161,  119,   88,   64,   63,   62,
 /*   830 */    61,  445,  445,  149,  445,  120,  161,  119,  445,   74,
 /*   840 */   396,  148,  475,  445,  445,  149,  137,  113,  120,  161,
 /*   850 */   119,   74,  445,  148,  445,  445,  445,  152,  525,  113,
 /*   860 */   120,  161,  119,  445,   74,  445,  148,  445,  445,  445,
 /*   870 */   149,  527,  113,  120,  161,  119,  445,  445,  445,   74,
 /*   880 */   445,  148,  149,  445,  445,  445,  524,  113,  120,  161,
 /*   890 */   119,   74,  445,  148,   98,  149,  445,  445,  526,  113,
 /*   900 */   120,  161,  119,  120,  161,  119,  445,   74,  445,  148,
 /*   910 */   149,  445,  445,  445,  523,  113,  120,  161,  119,   74,
 /*   920 */   445,  148,  149,  445,  445,  152,  522,  113,  120,  161,
 /*   930 */   119,   89,   64,   63,   62,   61,  445,  445,  149,  445,
 /*   940 */   120,  161,  119,  445,   74,  395,  148,  445,  445,  445,
 /*   950 */   149,  521,  113,  120,  161,  119,   74,  445,  148,  445,
 /*   960 */   445,  445,  152,  520,  113,  120,  161,  119,  445,   74,
 /*   970 */   445,  148,  445,  445,  445,  149,  519,  113,  120,  161,
 /*   980 */   119,  445,  445,  445,   74,  445,  148,  149,  445,  445,
 /*   990 */   445,  150,  113,  120,  161,  119,   74,  445,  148,   90,
 /*  1000 */   149,  445,  445,  151,  113,  120,  161,  119,  120,  161,
 /*  1010 */   119,  445,   74,  445,  148,  149,  445,  435,  445,  136,

 /*  1020 */   113,  120,  161,  119,   74,  445,  148,  149,  445,  445,
 /*  1030 */   152,  135,  113,  120,  161,  119,   64,   63,   62,   61,
 /*  1040 */   445,  445,  445,  149,  445,  445,  441,  440,  445,   88,
 /*  1050 */   445,  445,  445,  445,  445,  149,  445,   56,  120,  161,
 /*  1060 */   119,   88,  445,  445,   10,  479,  479,  445,  445,  445,
 /*  1070 */   120,  161,  119,  445,  445,  445,  445,   82,  445,  434,
 /*  1080 */   152,  445,  445,  445,  466,  445,   34,  109,  447,  454,
 /*  1090 */    29,  445,  152,  445,  450,  445,  445,  445,  107,  436,
 /*  1100 */   437,  438,  439,   87,  445,  163,  445,  120,  161,  119,
 /*  1110 */    27,  451,  120,  161,  119,   99,  445,   64,   63,   62,
 /*  1120 */    61,  445,  100,  445,  120,  161,  119,  101,  445,  152,
 /*  1130 */   391,  120,  161,  119,  152,  445,  120,  161,  119,   91,
 /*  1140 */   445,  445,  445,  445,  445,  445,  152,  445,  120,  161,
 /*  1150 */   119,  103,  445,  152,   92,  445,  445,  445,  152,  445,
 /*  1160 */   120,  161,  119,  120,  161,  119,   93,  445,  445,  104,

 /*  1170 */   152,  445,  445,  445,  445,  120,  161,  119,  120,  161,
 /*  1180 */   119,  445,  152,  445,   94,  152,  445,  445,  445,  445,
 /*  1190 */   445,  445,  105,  120,  161,  119,  445,  152,  445,   95,
 /*  1200 */   152,  120,  161,  119,  445,  445,  445,   96,  120,  161,
 /*  1210 */   119,  445,  445,  445,  445,  152,  120,  161,  119,  445,



 /*  1220 */   445,  445,  445,  152,  445,  445,  445,  445,  445,  445,
 /*  1230 */   152,   97,  445,  445,  549,  445,  445,  548,  152,  445,
 /*  1240 */   120,  161,  119,  120,  161,  119,  120,  161,  119,  445,
 /*  1250 */   445,  445,  445,  445,  445,  445,  445,  445,  445,  445,
 /*  1260 */   445,  445,  152,  547,  445,  152,  546,  445,  152,  115,
 /*  1270 */   445,  445,  120,  161,  119,  120,  161,  119,  120,  161,
 /*  1280 */   119,  116,  445,  445,  445,  445,  445,  445,  445,  445,
 /*  1290 */   120,  161,  119,  445,  152,  445,  445,  152,  445,  445,
 /*  1300 */   152,  445,  445,  445,  445,  445,  445,  445,  445,  445,
 /*  1310 */   445,  445,  152,
};
static const YYCODETYPE yy_lookahead[] = {
 /*     0 */     0,  113,  114,  115,  134,  102,  103,  104,  106,  106,
 /*    10 */    10,  113,  114,  115,  111,  112,  113,  114,  115,  106,
 /*    20 */    20,   21,   22,  105,   24,  126,  108,  109,   28,    4,
 /*    30 */     5,    6,    7,   33,   34,   35,   36,   37,  135,   39,
 /*    40 */    40,   41,   42,  105,   44,   45,  108,  109,  107,   49,
 /*    50 */    50,   51,   52,   53,   54,   55,   56,   57,   58,   59,
 /*    60 */    60,   61,   62,   63,    0,  113,  114,  115,  130,  131,
 /*    70 */   132,  104,   25,  106,   10,  113,  114,  115,  111,  112,
 /*    80 */   113,  114,  115,  106,   20,   21,   22,  128,   24,  113,
 /*    90 */   114,  115,   28,  129,    2,   26,   27,   33,   34,   35,
 /*   100 */    36,   37,  135,   39,   40,   41,   42,    2,   44,   45,
 /*   110 */   133,   26,   27,   49,   50,   51,   52,   53,   54,   55,
 /*   120 */    56,   57,   58,   59,   60,   61,   62,   63,    1,    2,
 /*   130 */    38,    4,    5,    4,    5,    6,    7,   17,   10,   12,
 /*   140 */     4,    5,   15,   38,    1,   25,   17,   29,   30,   31,
 /*   150 */    32,   24,   83,   26,   27,   12,   28,   14,   31,   32,
 /*   160 */    91,   18,  116,   20,   21,   22,   23,   24,  116,   26,
 /*   170 */    27,   19,   29,   30,   31,   32,   91,    3,   64,   65,
 /*   180 */    66,   67,   68,   69,   70,   71,   72,   73,   74,    6,
 /*   190 */     7,   64,    4,    5,    6,    7,    8,   97,   98,   20,
 /*   200 */    21,   22,   26,   27,    4,    5,    6,    7,    1,   82,
 /*   210 */    48,   84,   85,   86,   87,   17,   89,   17,   91,   92,
 /*   220 */    93,   94,   95,    1,    2,   25,    4,    5,   49,   50,
 /*   230 */    51,   52,   53,   54,   12,   16,   15,   15,    4,    5,
 /*   240 */     6,    7,   20,   21,   22,    1,   26,   27,    4,    5,
 /*   250 */    48,   43,   90,   31,   32,   40,   12,   40,   96,   15,
 /*   260 */    47,   99,   88,  104,   20,   21,   22,    1,   24,   35,
 /*   270 */     4,    5,  113,  114,  115,    0,  117,   41,   12,   41,
 /*   280 */    13,   15,   17,  124,  125,   10,   64,   25,    1,    2,
 /*   290 */    17,    4,    5,   75,  135,   81,   80,    3,    3,   12,
 /*   300 */     3,   99,   15,   79,   82,   80,   84,   85,   86,   87,

 /*   310 */    38,   89,    3,   91,   92,   93,   94,   95,   31,   32,
 /*   320 */     2,    3,    4,    5,    6,    7,   82,    3,    3,   12,
 /*   330 */    77,  104,   25,   89,   16,   91,   92,   93,   94,   95,
 /*   340 */   113,  114,  115,   25,  117,   96,   15,   15,   82,   31,
 /*   350 */    32,   64,  125,   15,   17,   89,   38,   91,   92,   93,
 /*   360 */    94,   95,  135,   28,    4,    5,    6,    7,   28,   82,
 /*   370 */    28,   84,   85,   86,   87,   12,   89,    3,   91,   92,
 /*   380 */    93,   94,   95,   90,   11,   67,   68,  136,    2,    3,

 /*   390 */     4,    5,    6,    7,   76,   35,   78,   79,   80,   82,
 /*   400 */   136,  136,  136,   17,  136,  136,   89,  136,   91,  136,
 /*   410 */   136,   25,  104,  136,  106,  136,  136,   31,   32,  111,
 /*   420 */   112,  113,  114,  115,   38,  136,  136,    2,    3,    4,
 /*   430 */     5,    6,    7,  136,    1,  136,  136,    4,    5,    4,
 /*   440 */     5,    6,    7,  135,  136,   12,  136,  136,   15,  136,
 /*   450 */    25,  136,   17,   67,   68,  136,   31,   32,  136,  136,
 /*   460 */    25,  136,   76,   38,   78,   79,   80,    1,  136,  136,
 /*   470 */     4,    5,    4,    5,    6,    7,  136,  136,   12,   46,
 /*   480 */    47,   15,  136,  136,  136,   17,   20,   21,   22,  136,
 /*   490 */   136,  136,   67,   68,  136,    1,    2,  136,    4,    5,
 /*   500 */   136,   76,  136,   78,   79,   80,   12,  136,  104,   15,
 /*   510 */     4,    5,    6,    7,  136,   82,  136,  113,  114,  115,
 /*   520 */   136,  117,   89,   17,   91,   92,   93,   94,   95,  136,
 /*   530 */     1,    2,   38,    4,    5,    4,    5,    6,    7,  135,
 /*   540 */   136,   12,  136,  136,   15,  136,  136,  136,   82,    4,
 /*   550 */     5,    6,    7,  136,  136,   89,   25,   91,   92,   93,
 /*   560 */    94,   95,  136,    1,    2,  136,    4,    5,  136,  136,
 /*   570 */    25,  104,  136,  106,   12,  136,   82,   15,  111,  112,
 /*   580 */   113,  114,  115,   89,  136,   91,   92,   93,   94,   95,
 /*   590 */     1,  136,  136,    4,    5,  136,  136,  136,  136,  136,
 /*   600 */   136,   12,  135,  136,   15,  136,  104,  136,  106,  136,
 /*   610 */   136,   82,  136,  111,  112,  113,  114,  115,   89,  136,
 /*   620 */    91,   92,   93,   94,   95,    1,  136,  136,    4,    5,
 /*   630 */   136,  136,  104,  136,  136,  136,   12,  135,  136,   15,
 /*   640 */   136,  113,  114,  115,   82,  136,  136,    2,  120,  121,
 /*   650 */   136,   89,  136,   91,   92,   93,   94,   95,    1,  136,
 /*   660 */   136,    4,    5,  135,  104,  136,  136,  136,  136,   12,
 /*   670 */   136,   82,   15,  113,  114,  115,   31,   32,   89,  136,
 /*   680 */    91,   92,   93,   94,   95,  101,  102,  103,  136,  136,
 /*   690 */   104,  107,  106,   48,  104,  135,  136,  111,  112,  113,
 /*   700 */   114,  115,  118,  113,  114,  115,   82,  123,  136,   64,
 /*   710 */   120,  121,  136,   89,  136,   91,   92,   93,   94,   95,
 /*   720 */   136,  135,  136,  136,  136,  135,  104,  136,  106,   84,
 /*   730 */    85,   86,   87,  111,  112,  113,  114,  115,  136,   82,
 /*   740 */   100,  101,  102,  103,  136,  136,   89,  107,   91,   92,
 /*   750 */    93,   94,   95,  136,  136,  136,  136,  135,  118,  104,
 /*   760 */   136,  106,  122,  123,  136,  136,  111,  112,  113,  114,
 /*   770 */   115,  136,  136,  136,  104,  136,  106,  136,  136,  136,
 /*   780 */   136,  111,  112,  113,  114,  115,  104,  136,  106,  104,
 /*   790 */   135,  136,  136,  111,  112,  113,  114,  115,  113,  114,
 /*   800 */   115,  136,  104,  136,  106,  135,  136,  136,  136,  111,
 /*   810 */   112,  113,  114,  115,  104,  136,  106,  135,  136,  136,
 /*   820 */   135,  111,  112,  113,  114,  115,  104,    4,    5,    6,
 /*   830 */     7,  136,  136,  135,  136,  113,  114,  115,  136,  104,
 /*   840 */    17,  106,  120,  136,  136,  135,  111,  112,  113,  114,
 /*   850 */   115,  104,  136,  106,  136,  136,  136,  135,  111,  112,
 /*   860 */   113,  114,  115,  136,  104,  136,  106,  136,  136,  136,
 /*   870 */   135,  111,  112,  113,  114,  115,  136,  136,  136,  104,
 /*   880 */   136,  106,  135,  136,  136,  136,  111,  112,  113,  114,
 /*   890 */   115,  104,  136,  106,  104,  135,  136,  136,  111,  112,
 /*   900 */   113,  114,  115,  113,  114,  115,  136,  104,  136,  106,
 /*   910 */   135,  136,  136,  136,  111,  112,  113,  114,  115,  104,
 /*   920 */   136,  106,  135,  136,  136,  135,  111,  112,  113,  114,
 /*   930 */   115,  104,    4,    5,    6,    7,  136,  136,  135,  136,
 /*   940 */   113,  114,  115,  136,  104,   17,  106,  136,  136,  136,
 /*   950 */   135,  111,  112,  113,  114,  115,  104,  136,  106,  136,
 /*   960 */   136,  136,  135,  111,  112,  113,  114,  115,  136,  104,
 /*   970 */   136,  106,  136,  136,  136,  135,  111,  112,  113,  114,
 /*   980 */   115,  136,  136,  136,  104,  136,  106,  135,  136,  136,
 /*   990 */   136,  111,  112,  113,  114,  115,  104,  136,  106,  104,
 /*  1000 */   135,  136,  136,  111,  112,  113,  114,  115,  113,  114,
 /*  1010 */   115,  136,  104,  136,  106,  135,  136,    2,  136,  111,
 /*  1020 */   112,  113,  114,  115,  104,  136,  106,  135,  136,  136,
 /*  1030 */   135,  111,  112,  113,  114,  115,    4,    5,    6,    7,
 /*  1040 */   136,  136,  136,  135,  136,  136,   31,   32,  136,  104,
 /*  1050 */   136,  136,  136,  136,  136,  135,  136,   25,  113,  114,
 /*  1060 */   115,  104,  136,  136,  119,  120,  121,  136,  136,  136,
 /*  1070 */   113,  114,  115,  136,  136,  136,  136,  120,  136,   64,
 /*  1080 */   135,  136,  136,  136,  127,  136,  129,  100,  101,  102,
 /*  1090 */   103,  136,  135,  136,  107,  136,  136,  136,  104,   84,
 /*  1100 */    85,   86,   87,  104,  136,  118,  136,  113,  114,  115,
 /*  1110 */   123,  117,  113,  114,  115,  104,  136,    4,    5,    6,
 /*  1120 */     7,  136,  104,  136,  113,  114,  115,  104,  136,  135,
 /*  1130 */    17,  113,  114,  115,  135,  136,  113,  114,  115,  104,
 /*  1140 */   136,  136,  136,  136,  136,  136,  135,  136,  113,  114,
 /*  1150 */   115,  104,  136,  135,  104,  136,  136,  136,  135,  136,
 /*  1160 */   113,  114,  115,  113,  114,  115,  104,  136,  136,  104,
 /*  1170 */   135,  136,  136,  136,  136,  113,  114,  115,  113,  114,
 /*  1180 */   115,  136,  135,  136,  104,  135,  136,  136,  136,  136,
 /*  1190 */   136,  136,  104,  113,  114,  115,  136,  135,  136,  104,
 /*  1200 */   135,  113,  114,  115,  136,  136,  136,  104,  113,  114,
 /*  1210 */   115,  136,  136,  136,  136,  135,  113,  114,  115,  136,
 /*  1220 */   136,  136,  136,  135,  136,  136,  136,  136,  136,  136,
 /*  1230 */   135,  104,  136,  136,  104,  136,  136,  104,  135,  136,
 /*  1240 */   113,  114,  115,  113,  114,  115,  113,  114,  115,  136,
 /*  1250 */   136,  136,  136,  136,  136,  136,  136,  136,  136,  136,
 /*  1260 */   136,  136,  135,  104,  136,  135,  104,  136,  135,  104,
 /*  1270 */   136,  136,  113,  114,  115,  113,  114,  115,  113,  114,
 /*  1280 */   115,  104,  136,  136,  136,  136,  136,  136,  136,  136,
 /*  1290 */   113,  114,  115,  136,  135,  136,  136,  135,  136,  136,
 /*  1300 */   135,  136,  136,  136,  136,  136,  136,  136,  136,  136,
 /*  1310 */   136,  136,  135,  100,  100,  100,  100,  100,  100,  100,
 /*  1320 */   100,  100,  100,  100,  100,  100,  100,  100,  100,  100,
 /*  1330 */   100,  100,  100,  100,  100,  100,  100,  100,  100,  100,
 /*  1340 */   100,  100,  100,  100,  100,  100,  100,  100,  100,  100,
 /*  1350 */   100,  100,  100,  100,  100,  100,  100,  100,  100,  100,
 /*  1360 */   100,  100,  100,  100,  100,  100,  100,  100,  100,  100,
 /*  1370 */   100,  100,  100,  100,  100,  100,  100,  100,  100,  100,
 /*  1380 */   100,  100,  100,  100,  100,  100,  100,  100,  100,  100,
 /*  1390 */   100,  100,  100,  100,  100,  100,  100,  100,  100,  100,
 /*  1400 */   100,  100,  100,  100,  100,  100,  100,  100,  100,  100,
 /*  1410 */   100,  100,  100,
};
#define YY_SHIFT_COUNT    (163)
#define YY_SHIFT_MIN      (0)
#define YY_SHIFT_MAX      (1113)
static const unsigned short int yy_shift_ofst[] = {
 /*     0 */   143,  127,  222,  287,  287,  287,  287,  287,  287,  287,
 /*    10 */   287,  287,  287,  287,  287,  287,  287,  287,  287,  287,
 /*    20 */   287,  287,  287,  287,  287,  287,  287,  244,  433,  266,
 /*    30 */   244,  143,  494,  494,    0,   64,  143,  589,  266,  589,
 /*    40 */   466,  466,  466,  529,  562,  266,  266,  266,  266,  266,
 /*    50 */   266,  624,  266,  266,  657,  266,  266,  266,  266,  266,
 /*    60 */   266,  266,  266,  266,  266,  179,  317,  317,  317,  317,
 /*    70 */   317,  645,  318,  386,  425, 1015, 1015,  118,   47, 1313,
 /*    80 */  1313, 1313, 1313,  114,  114,  200,  435,  129,  188,  234,
 /*    90 */   360,  468,  531,  506,  545,  823, 1032,  928, 1113,   25,
 /*   100 */    25,   25,  162,   25,   25,   25,   69,   25,   85,  128,
 /*   110 */    92,  105,  120,  136,  100,  183,  183,  176,  220,  174,
 /*   120 */   202,  275,  152,  207,  198,  219,  221,  208,  215,  217,
 /*   130 */   236,  238,  213,  267,  265,  262,  218,  273,  216,  224,
 /*   140 */   214,  225,  294,  295,  297,  272,  309,  324,  325,  249,
 /*   150 */   253,  307,  249,  331,  332,  338,  337,  335,  340,  342,
 /*   160 */   363,  293,  374,  373,
};
#define YY_REDUCE_COUNT (82)
#define YY_REDUCE_MIN   (-130)
#define YY_REDUCE_MAX   (1177)
static const short yy_reduce_ofst[] = {
 /*     0 */   640,  -97,  -33,  308,  467,  502,  586,  622,  655,  670,
 /*    10 */   682,  698,  710,  735,  747,  760,  775,  787,  803,  815,
 /*    20 */   840,  852,  865,  880,  892,  908,  920,  159,  945,  957,
 /*    30 */   227,  987,  528,  590,  -62,  -62,  584,  404,  722,  994,
 /*    40 */   560,  685,  790,  827,  895,  999, 1011, 1018, 1023, 1035,
 /*    50 */  1047, 1050, 1062, 1065, 1080, 1088, 1095, 1103, 1127, 1130,
 /*    60 */  1133, 1159, 1162, 1165, 1177,  -82, -112, -102,  -48,  -38,
 /*    70 */   -24,  -23, -130, -130, -130,  -98,  -87,  -59, -101,  -41,
 /*    80 */    46,   52,  -36,
};
static const YYACTIONTYPE yy_default[] = {
 /*     0 */   449,  443,  443,  443,  443,  443,  443,  443,  443,  443,
 /*    10 */   443,  443,  443,  443,  443,  443,  443,  443,  443,  443,
 /*    20 */   443,  443,  443,  443,  443,  443,  443,  443,  473,  576,
 /*    30 */   443,  449,  580,  485,  581,  581,  449,  443,  443,  443,
 /*    40 */   443,  443,  443,  443,  443,  443,  443,  443,  477,  443,







|


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


|
|
|
|
|

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



|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|


|
|

|
|
|
|
|
|
|
|
|







836
837
838
839
840
841
842
843
844
845
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
872
873
874
875
876
877
878

879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910

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
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
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
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
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
1154
1155
1156
1157
1158
1159
1160
1161
**  yy_shift_ofst[]    For each state, the offset into yy_action for
**                     shifting terminals.
**  yy_reduce_ofst[]   For each state, the offset into yy_action for
**                     shifting non-terminals after a reduce.
**  yy_default[]       Default action for each state.
**
*********** Begin parsing tables **********************************************/
#define YY_ACTTAB_COUNT (1305)
static const YYACTIONTYPE yy_action[] = {
 /*     0 */   575,  495,  161,  119,   25,  452,   29,   74,  129,  148,
 /*    10 */   575,   64,   63,   62,   61,  453,  113,  120,  161,  119,
 /*    20 */   427,  428,  339,  357,   81,  121,  447,  454,   29,  575,
 /*    30 */   530,   13,   50,  450,  322,  323,    9,    8,   33,  149,
 /*    40 */    32,    7,   71,  127,  163,  335,   66,   28,  444,   27,
 /*    50 */   339,  339,  339,  339,  425,  426,  340,  341,  342,  343,
 /*    60 */   344,  345,  346,  347,  348,  474,   64,   63,   62,   61,
 /*    70 */    54,   51,   73,  306,  148,  474,  492,  161,  119,  297,
 /*    80 */   112,  113,  120,  161,  119,  427,  428,  339,   30,   81,
 /*    90 */   109,  447,  454,   29,  474,  528,  161,  119,  450,  322,
 /*   100 */   323,    9,    8,   33,  149,   32,    7,   71,  127,  163,
 /*   110 */   335,   66,  535,   36,   27,  339,  339,  339,  339,  425,
 /*   120 */   426,  340,  341,  342,  343,  344,  345,  346,  347,  348,
 /*   130 */   394,  435,  310,   59,   60,   64,   63,   62,   61,  313,
 /*   140 */    74,  376,  148,   69,    2,  533,  161,  119,  124,  113,
 /*   150 */   120,  161,  119,   80,  535,   31,  308,   79,   83,  107,
 /*   160 */   535,  441,  440,  535,  394,  435,  299,   59,   60,  120,
 /*   170 */   161,  119,  149,  463,  376,  376,  330,   84,    2,  122,
 /*   180 */    78,   78,   38,  156,  156,  156,   48,   37,  559,  328,
 /*   190 */   128,  152,  560,  561,  434,  441,  440,  350,  350,  350,
 /*   200 */   350,  350,  350,  350,  350,  350,  350,  350,  577,   77,


 /*   210 */   577,   35,  106,   46,  436,  437,  438,  439,  579,  375,
 /*   220 */   298,  117,  393,  155,  154,  153,   47,    4,  434,   69,
 /*   230 */   394,  435,    3,   59,   60,  411,  412,  413,  414,  398,
 /*   240 */   399,  376,   62,   61,    2,  108,  106,    5,  436,  437,
 /*   250 */   438,  439,  375,  375,  117,  117,  393,  155,  154,  153,
 /*   260 */    76,  441,  440,   67,    6,  142,  140,   64,   63,   62,
 /*   270 */    61,  380,  157,  424,  427,  428,  339,  379,  159,   45,
 /*   280 */   423,   72,  131,  148,  531,  161,  119,    1,   55,  125,
 /*   290 */   113,  120,  161,  119,  434,  147,  146,   64,   63,   62,
 /*   300 */    61,  397,   43,   11,  339,  339,  339,  339,  425,  426,
 /*   310 */   355,   65,  106,  149,  436,  437,  438,  439,   74,  375,
 /*   320 */   148,  117,  393,  155,  154,  153,  497,  113,  120,  161,
 /*   330 */   119,   22,   21,   12,  142,  140,   64,   63,   62,   61,

 /*   340 */    24,  356,  145,  141,  431,   64,   63,   62,   61,  391,
 /*   350 */   149,  448,  454,   29,  378,  158,   85,   55,  450,  394,
 /*   360 */   432,  138,   59,   60,  147,  146,  120,  161,  119,  163,
 /*   370 */   102,   43,  139,   42,   27,  430,   14,   15,  301,  302,
 /*   380 */   303,  446,  305,   16,   44,   74,   18,  148,  152,   19,
 /*   390 */    20,   36,   68,  496,  113,  120,  161,  119,  114,  359,
 /*   400 */    22,   21,   23,  142,  140,   64,   63,   62,   61,   24,
 /*   410 */   107,  145,  141,  431,   26,   57,  377,  149,   58,  118,
 /*   420 */   120,  161,  119,  392,  463,  384,   55,   64,   63,   62,
 /*   430 */    61,  382,  569,  147,  146,  160,  383,  435,   39,   70,
 /*   440 */    43,  106,  152,  445,  445,   88,  445,  445,  375,  445,
 /*   450 */   117,  393,  155,  154,  153,  120,  161,  119,  445,   17,
 /*   460 */   445,   10,  479,  479,  445,  445,  435,  441,  440,   22,
 /*   470 */    21,  445,  403,   64,   63,   62,   61,  152,   24,  445,
 /*   480 */   145,  141,  431,  133,   75,  126,  354,  445,  445,  123,
 /*   490 */   445,  404,  405,  406,  408,   80,  441,  440,  308,   79,
 /*   500 */   434,  411,  412,  413,  414,  394,  445,  445,   59,   60,
 /*   510 */    64,   63,   62,   61,  445,  445,  376,  445,  445,   42,
 /*   520 */   436,  437,  438,  439,  156,  156,  156,  394,  445,  434,
 /*   530 */    59,   60,   64,   63,   62,   61,  445,  445,  376,  445,
 /*   540 */   445,   42,  445,  394,  473,  391,   59,   60,  445,  436,
 /*   550 */   437,  438,  439,   49,  376,  445,   74,   42,  148,  445,
 /*   560 */    88,  445,  445,  445,  490,  113,  120,  161,  119,  445,
 /*   570 */   120,  161,  119,  132,  130,  394,  143,  475,   59,   60,
 /*   580 */   445,  473,   64,   63,   62,   61,  376,  106,  149,   42,
 /*   590 */   445,  445,  152,  445,  375,  391,  117,  393,  155,  154,
 /*   600 */   153,  394,  144,   52,   59,   60,  445,  445,  445,  106,
 /*   610 */   445,  445,  376,  445,  445,   42,  375,  445,  117,  393,
 /*   620 */   155,  154,  153,  445,  445,  106,   64,   63,   62,   61,
 /*   630 */   445,  445,  375,  445,  117,  393,  155,  154,  153,  394,
 /*   640 */   445,  445,   59,   60,   88,  445,  445,   53,  445,  445,
 /*   650 */   376,  445,  445,   42,  120,  161,  119,  106,  445,  445,

 /*   660 */   445,  110,  110,  445,  375,  445,  117,  393,  155,  154,
 /*   670 */   153,  394,  445,  445,   59,   60,  152,  107,  445,  445,
 /*   680 */   445,  445,  102,  106,  445,   42,  445,  120,  161,  119,
 /*   690 */   375,  451,  117,  393,  155,  154,  153,  394,  445,  445,
 /*   700 */    59,   60,   64,   63,   62,   61,  445,  445,  376,  152,
 /*   710 */   445,   40,  445,  394,  445,  396,   59,   60,  445,  445,
 /*   720 */   445,  106,  445,  445,  376,   88,  445,   41,  375,  445,
 /*   730 */   117,  393,  155,  154,  153,  120,  161,  119,   74,  445,
 /*   740 */   148,  445,  111,  111,  107,  445,  484,  113,  120,  161,
 /*   750 */   119,  445,  445,  106,  120,  161,  119,  152,  478,  445,
 /*   760 */   375,   86,  117,  393,  155,  154,  153,  445,  445,  445,
 /*   770 */   149,  120,  161,  119,  445,  445,  152,  445,  445,  106,
 /*   780 */   445,   64,   63,   62,   61,  445,  375,  445,  117,  393,
 /*   790 */   155,  154,  153,  152,  395,  106,   64,   63,   62,   61,
 /*   800 */    98,  445,  375,  445,  117,  393,  155,  154,  153,  445,
 /*   810 */   120,  161,  119,  445,   74,  445,  148,   56,  445,   74,
 /*   820 */   445,  148,  483,  113,  120,  161,  119,  480,  113,  120,
 /*   830 */   161,  119,  152,   74,  445,  148,  445,   89,  445,  445,
 /*   840 */   445,  134,  113,  120,  161,  119,  149,  120,  161,  119,
 /*   850 */   445,  149,   74,  445,  148,  445,  445,  445,  378,  158,

 /*   860 */   517,  113,  120,  161,  119,  149,   74,  445,  148,  152,

 /*   870 */   445,   74,  445,  148,  137,  113,  120,  161,  119,  525,
 /*   880 */   113,  120,  161,  119,  149,   74,  445,  148,   64,   63,
 /*   890 */    62,   61,  445,  527,  113,  120,  161,  119,  149,  445,

 /*   900 */   445,  391,  445,  149,  445,  445,  445,  445,  445,  445,
 /*   910 */    74,  445,  148,  445,  445,  162,  445,  149,  524,  113,
 /*   920 */   120,  161,  119,  118,  445,   74,  445,  148,  445,  445,
 /*   930 */   445,  445,  445,  526,  113,  120,  161,  119,  445,   74,

 /*   940 */   445,  148,  149,  445,  445,  445,  445,  523,  113,  120,
 /*   950 */   161,  119,   74,  445,  148,  445,  445,  149,  445,  445,
 /*   960 */   522,  113,  120,  161,  119,  445,   74,  445,  148,  445,
 /*   970 */   445,  149,  445,  445,  521,  113,  120,  161,  119,   74,
 /*   980 */   445,  148,  445,  445,  149,  445,  445,  520,  113,  120,
 /*   990 */   161,  119,  445,   74,  445,  148,  445,  445,  149,  445,
 /*  1000 */   445,  519,  113,  120,  161,  119,  445,  445,  445,  445,
 /*  1010 */   445,  149,  445,  445,  445,  445,  445,  445,   74,  445,
 /*  1020 */   148,  445,  445,  445,  445,  149,  150,  113,  120,  161,
 /*  1030 */   119,   74,  445,  148,  445,  445,  445,  445,  445,  151,
 /*  1040 */   113,  120,  161,  119,  445,   74,  445,  148,  445,  445,
 /*  1050 */   149,  445,  445,  136,  113,  120,  161,  119,   74,  445,
 /*  1060 */   148,  445,  445,  149,  445,  445,  135,  113,  120,  161,

 /*  1070 */   119,  445,   88,  445,  445,  445,  445,  149,  445,  445,
 /*  1080 */   445,   90,  120,  161,  119,  445,  445,  445,  445,   82,
 /*  1090 */   149,  120,  161,  119,  445,   87,  466,  445,   34,   99,
 /*  1100 */   445,  445,  445,  445,  152,  120,  161,  119,  100,  120,
 /*  1110 */   161,  119,  445,  152,  445,  445,  445,  445,  120,  161,
 /*  1120 */   119,  445,  445,  445,  101,  445,  445,  152,  445,  445,
 /*  1130 */   445,  152,   91,  445,  120,  161,  119,  103,  445,  445,
 /*  1140 */   152,  445,  120,  161,  119,  445,  445,  120,  161,  119,
 /*  1150 */   445,   92,  445,  445,  445,  445,  152,  445,  445,  445,
 /*  1160 */    93,  120,  161,  119,  152,  445,  104,  445,  445,  152,
 /*  1170 */   120,  161,  119,  445,   94,  445,  120,  161,  119,  445,
 /*  1180 */   445,  445,  445,  152,  120,  161,  119,  445,  445,  105,
 /*  1190 */   445,  445,  152,  445,  445,  445,  445,   95,  152,  120,
 /*  1200 */   161,  119,   96,  445,  445,   97,  152,  120,  161,  119,
 /*  1210 */   445,  445,  120,  161,  119,  120,  161,  119,  445,  445,
 /*  1220 */   445,  152,  445,  445,  445,  445,  445,  445,  445,  152,
 /*  1230 */   549,  445,  445,  548,  152,  445,  445,  152,  547,  445,
 /*  1240 */   120,  161,  119,  120,  161,  119,  546,  445,  120,  161,
 /*  1250 */   119,  445,  445,  445,  445,  445,  120,  161,  119,  445,
 /*  1260 */   445,  445,  152,  445,  445,  152,  445,  445,  445,  115,

 /*  1270 */   152,  445,  116,  445,  445,  445,  445,  445,  152,  120,
 /*  1280 */   161,  119,  120,  161,  119,  445,  445,  445,  445,  445,
 /*  1290 */   445,  445,  445,  445,  445,  445,  445,  445,  445,  445,
 /*  1300 */   445,  152,  445,  445,  152,
};
static const YYCODETYPE yy_lookahead[] = {
 /*     0 */     0,  115,  116,  117,  136,  103,  104,  105,  107,  107,
 /*    10 */    10,    4,    5,    6,    7,  113,  114,  115,  116,  117,
 /*    20 */    20,   21,   22,   17,   24,  101,  102,  103,  104,   29,
 /*    30 */   107,   25,   25,  109,   34,   35,   36,   37,   38,  137,
 /*    40 */    40,   41,   42,   43,  120,   45,   46,  109,  124,  125,
 /*    50 */    50,   51,   52,   53,   54,   55,   56,   57,   58,   59,
 /*    60 */    60,   61,   62,   63,   64,    0,    4,    5,    6,    7,
 /*    70 */     4,    5,  105,   25,  107,   10,  115,  116,  117,   17,
 /*    80 */   113,  114,  115,  116,  117,   20,   21,   22,  128,   24,
 /*    90 */   101,  102,  103,  104,   29,  115,  116,  117,  109,   34,
 /*   100 */    35,   36,   37,   38,  137,   40,   41,   42,   43,  120,
 /*   110 */    45,   46,   49,   10,  125,   50,   51,   52,   53,   54,
 /*   120 */    55,   56,   57,   58,   59,   60,   61,   62,   63,   64,
 /*   130 */     1,    2,   29,    4,    5,    4,    5,    6,    7,    8,
 /*   140 */   105,   12,  107,    3,   15,  115,  116,  117,  113,  114,
 /*   150 */   115,  116,  117,   24,   91,  130,   27,   28,  118,  105,
 /*   160 */    97,   32,   33,  100,    1,    2,   19,    4,    5,  115,
 /*   170 */   116,  117,  137,  119,   12,   12,    2,  118,   15,    1,
 /*   180 */   126,  127,  106,   20,   21,   22,  110,  111,  106,    2,
 /*   190 */   107,  137,  110,  111,   65,   32,   33,   65,   66,   67,
 /*   200 */    68,   69,   70,   71,   72,   73,   74,   75,  132,  133,
 /*   210 */   134,  131,   83,   39,   85,   86,   87,   88,  135,   90,
 /*   220 */    17,   92,   93,   94,   95,   96,   39,   15,   65,   89,
 /*   230 */     1,    2,   16,    4,    5,   30,   31,   32,   33,   98,
 /*   240 */    99,   12,    6,    7,   15,   83,   83,   41,   85,   86,
 /*   250 */    87,   88,   90,   90,   92,   92,   93,   94,   95,   96,
 /*   260 */    49,   32,   33,   44,   41,    2,    3,    4,    5,    6,
 /*   270 */     7,   27,   28,   42,   20,   21,   22,   27,   28,   16,
 /*   280 */    42,  105,   48,  107,  115,  116,  117,   13,   25,  113,
 /*   290 */   114,  115,  116,  117,   65,   32,   33,    4,    5,    6,
 /*   300 */     7,   17,   39,   25,   50,   51,   52,   53,   54,   55,
 /*   310 */    17,  100,   83,  137,   85,   86,   87,   88,  105,   90,
 /*   320 */   107,   92,   93,   94,   95,   96,  113,  114,  115,  116,
 /*   330 */   117,   68,   69,   76,    2,    3,    4,    5,    6,    7,

 /*   340 */    77,   17,   79,   80,   81,    4,    5,    6,    7,   17,
 /*   350 */   137,  102,  103,  104,   27,   28,  105,   25,  109,    1,
 /*   360 */    81,   80,    4,    5,   32,   33,  115,  116,  117,  120,
 /*   370 */    12,   39,   82,   15,  125,   81,    3,   36,   20,   21,
 /*   380 */    22,    0,   24,    3,   39,  105,    3,  107,  137,    3,
 /*   390 */     3,   10,    3,  113,  114,  115,  116,  117,   97,   78,
 /*   400 */    68,   69,   25,    2,    3,    4,    5,    6,    7,   77,

 /*   410 */   105,   79,   80,   81,   15,   15,   12,  137,   15,   92,
 /*   420 */   115,  116,  117,   17,  119,   29,   25,    4,    5,    6,
 /*   430 */     7,   29,  127,   32,   33,   91,   29,    2,   11,    3,
 /*   440 */    39,   83,  137,  138,  138,  105,  138,  138,   90,  138,
 /*   450 */    92,   93,   94,   95,   96,  115,  116,  117,  138,   36,
 /*   460 */   138,  121,  122,  123,  138,  138,    2,   32,   33,   68,
 /*   470 */    69,  138,    1,    4,    5,    6,    7,  137,   77,  138,
 /*   480 */    79,   80,   81,   12,   49,   14,   17,  138,  138,   18,
 /*   490 */   138,   20,   21,   22,   23,   24,   32,   33,   27,   28,
 /*   500 */    65,   30,   31,   32,   33,    1,  138,  138,    4,    5,
 /*   510 */     4,    5,    6,    7,  138,  138,   12,  138,  138,   15,
 /*   520 */    85,   86,   87,   88,   20,   21,   22,    1,  138,   65,
 /*   530 */     4,    5,    4,    5,    6,    7,  138,  138,   12,  138,
 /*   540 */   138,   15,  138,    1,    2,   17,    4,    5,  138,   85,
 /*   550 */    86,   87,   88,   25,   12,  138,  105,   15,  107,  138,
 /*   560 */   105,  138,  138,  138,  113,  114,  115,  116,  117,  138,
 /*   570 */   115,  116,  117,   47,   48,    1,    2,  122,    4,    5,
 /*   580 */   138,   39,    4,    5,    6,    7,   12,   83,  137,   15,
 /*   590 */   138,  138,  137,  138,   90,   17,   92,   93,   94,   95,
 /*   600 */    96,    1,    2,   25,    4,    5,  138,  138,  138,   83,
 /*   610 */   138,  138,   12,  138,  138,   15,   90,  138,   92,   93,
 /*   620 */    94,   95,   96,  138,  138,   83,    4,    5,    6,    7,
 /*   630 */   138,  138,   90,  138,   92,   93,   94,   95,   96,    1,
 /*   640 */   138,  138,    4,    5,  105,  138,  138,   25,  138,  138,
 /*   650 */    12,  138,  138,   15,  115,  116,  117,   83,  138,  138,
 /*   660 */   138,  122,  123,  138,   90,  138,   92,   93,   94,   95,
 /*   670 */    96,    1,  138,  138,    4,    5,  137,  105,  138,  138,
 /*   680 */   138,  138,   12,   83,  138,   15,  138,  115,  116,  117,
 /*   690 */    90,  119,   92,   93,   94,   95,   96,    1,  138,  138,
 /*   700 */     4,    5,    4,    5,    6,    7,  138,  138,   12,  137,
 /*   710 */   138,   15,  138,    1,  138,   17,    4,    5,  138,  138,
 /*   720 */   138,   83,  138,  138,   12,  105,  138,   15,   90,  138,
 /*   730 */    92,   93,   94,   95,   96,  115,  116,  117,  105,  138,
 /*   740 */   107,  138,  122,  123,  105,  138,  113,  114,  115,  116,
 /*   750 */   117,  138,  138,   83,  115,  116,  117,  137,  119,  138,
 /*   760 */    90,  105,   92,   93,   94,   95,   96,  138,  138,  138,
 /*   770 */   137,  115,  116,  117,  138,  138,  137,  138,  138,   83,
 /*   780 */   138,    4,    5,    6,    7,  138,   90,  138,   92,   93,
 /*   790 */    94,   95,   96,  137,   17,   83,    4,    5,    6,    7,
 /*   800 */   105,  138,   90,  138,   92,   93,   94,   95,   96,  138,
 /*   810 */   115,  116,  117,  138,  105,  138,  107,   25,  138,  105,
 /*   820 */   138,  107,  113,  114,  115,  116,  117,  113,  114,  115,
 /*   830 */   116,  117,  137,  105,  138,  107,  138,  105,  138,  138,
 /*   840 */   138,  113,  114,  115,  116,  117,  137,  115,  116,  117,
 /*   850 */   138,  137,  105,  138,  107,  138,  138,  138,   27,   28,
 /*   860 */   113,  114,  115,  116,  117,  137,  105,  138,  107,  137,
 /*   870 */   138,  105,  138,  107,  113,  114,  115,  116,  117,  113,
 /*   880 */   114,  115,  116,  117,  137,  105,  138,  107,    4,    5,
 /*   890 */     6,    7,  138,  113,  114,  115,  116,  117,  137,  138,
 /*   900 */   138,   17,  138,  137,  138,  138,  138,  138,  138,  138,
 /*   910 */   105,  138,  107,  138,  138,   84,  138,  137,  113,  114,
 /*   920 */   115,  116,  117,   92,  138,  105,  138,  107,  138,  138,
 /*   930 */   138,  138,  138,  113,  114,  115,  116,  117,  138,  105,
 /*   940 */   138,  107,  137,  138,  138,  138,  138,  113,  114,  115,
 /*   950 */   116,  117,  105,  138,  107,  138,  138,  137,  138,  138,
 /*   960 */   113,  114,  115,  116,  117,  138,  105,  138,  107,  138,
 /*   970 */   138,  137,  138,  138,  113,  114,  115,  116,  117,  105,
 /*   980 */   138,  107,  138,  138,  137,  138,  138,  113,  114,  115,
 /*   990 */   116,  117,  138,  105,  138,  107,  138,  138,  137,  138,
 /*  1000 */   138,  113,  114,  115,  116,  117,  138,  138,  138,  138,
 /*  1010 */   138,  137,  138,  138,  138,  138,  138,  138,  105,  138,
 /*  1020 */   107,  138,  138,  138,  138,  137,  113,  114,  115,  116,
 /*  1030 */   117,  105,  138,  107,  138,  138,  138,  138,  138,  113,
 /*  1040 */   114,  115,  116,  117,  138,  105,  138,  107,  138,  138,
 /*  1050 */   137,  138,  138,  113,  114,  115,  116,  117,  105,  138,
 /*  1060 */   107,  138,  138,  137,  138,  138,  113,  114,  115,  116,
 /*  1070 */   117,  138,  105,  138,  138,  138,  138,  137,  138,  138,
 /*  1080 */   138,  105,  115,  116,  117,  138,  138,  138,  138,  122,
 /*  1090 */   137,  115,  116,  117,  138,  105,  129,  138,  131,  105,
 /*  1100 */   138,  138,  138,  138,  137,  115,  116,  117,  105,  115,
 /*  1110 */   116,  117,  138,  137,  138,  138,  138,  138,  115,  116,
 /*  1120 */   117,  138,  138,  138,  105,  138,  138,  137,  138,  138,
 /*  1130 */   138,  137,  105,  138,  115,  116,  117,  105,  138,  138,
 /*  1140 */   137,  138,  115,  116,  117,  138,  138,  115,  116,  117,
 /*  1150 */   138,  105,  138,  138,  138,  138,  137,  138,  138,  138,
 /*  1160 */   105,  115,  116,  117,  137,  138,  105,  138,  138,  137,
 /*  1170 */   115,  116,  117,  138,  105,  138,  115,  116,  117,  138,
 /*  1180 */   138,  138,  138,  137,  115,  116,  117,  138,  138,  105,
 /*  1190 */   138,  138,  137,  138,  138,  138,  138,  105,  137,  115,
 /*  1200 */   116,  117,  105,  138,  138,  105,  137,  115,  116,  117,
 /*  1210 */   138,  138,  115,  116,  117,  115,  116,  117,  138,  138,
 /*  1220 */   138,  137,  138,  138,  138,  138,  138,  138,  138,  137,
 /*  1230 */   105,  138,  138,  105,  137,  138,  138,  137,  105,  138,
 /*  1240 */   115,  116,  117,  115,  116,  117,  105,  138,  115,  116,
 /*  1250 */   117,  138,  138,  138,  138,  138,  115,  116,  117,  138,
 /*  1260 */   138,  138,  137,  138,  138,  137,  138,  138,  138,  105,
 /*  1270 */   137,  138,  105,  138,  138,  138,  138,  138,  137,  115,
 /*  1280 */   116,  117,  115,  116,  117,  138,  138,  138,  138,  138,
 /*  1290 */   138,  138,  138,  138,  138,  138,  138,  138,  138,  138,
 /*  1300 */   138,  137,  138,  138,  137,  101,  101,  101,  101,  101,
 /*  1310 */   101,  101,  101,  101,  101,  101,  101,  101,  101,  101,
 /*  1320 */   101,  101,  101,  101,  101,  101,  101,  101,  101,  101,
 /*  1330 */   101,  101,  101,  101,  101,  101,  101,  101,  101,  101,
 /*  1340 */   101,  101,  101,  101,  101,  101,  101,  101,  101,  101,
 /*  1350 */   101,  101,  101,  101,  101,  101,  101,  101,  101,  101,
 /*  1360 */   101,  101,  101,  101,  101,  101,  101,  101,  101,  101,
 /*  1370 */   101,  101,  101,  101,  101,  101,  101,  101,  101,  101,
 /*  1380 */   101,  101,  101,  101,  101,  101,  101,  101,  101,  101,
 /*  1390 */   101,  101,  101,  101,  101,  101,  101,  101,  101,  101,
 /*  1400 */   101,  101,  101,  101,  101,  101,

};
#define YY_SHIFT_COUNT    (163)
#define YY_SHIFT_MIN      (0)
#define YY_SHIFT_MAX      (884)
static const unsigned short int yy_shift_ofst[] = {
 /*     0 */   471,  129,  163,  229,  229,  229,  229,  229,  229,  229,
 /*    10 */   229,  229,  229,  229,  229,  229,  229,  229,  229,  229,
 /*    20 */   229,  229,  229,  229,  229,  229,  229,  358,  526,  638,
 /*    30 */   358,  471,  542,  542,    0,   65,  471,  670,  638,  670,
 /*    40 */   504,  504,  504,  574,  600,  638,  638,  638,  638,  638,
 /*    50 */   638,  696,  638,  638,  712,  638,  638,  638,  638,  638,
 /*    60 */   638,  638,  638,  638,  638,  254,  162,  162,  162,  162,
 /*    70 */   162,  435,  263,  332,  401,  464,  464,  205,   48, 1305,
 /*    80 */  1305, 1305, 1305,  132,  132,  528,  578,   62,  131,  341,
 /*    90 */   423,  293,    7,  469,  622,  698,  792,  777,  884,  506,
 /*   100 */   506,  506,   63,  506,  506,  506,  831,  506,  327,  103,
 /*   110 */   174,  187,    6,   66,  141,  236,  236,  244,  250,  140,
 /*   120 */   211,  381,  147,  178,  203,  216,  212,  219,  206,  223,
 /*   130 */   231,  238,  234,  274,  284,  278,  257,  324,  279,  281,
 /*   140 */   290,  294,  373,  380,  383,  345,  386,  387,  389,  301,
 /*   150 */   321,  377,  301,  399,  400,  403,  406,  396,  402,  407,
 /*   160 */   404,  344,  436,  427,
};
#define YY_REDUCE_COUNT (82)
#define YY_REDUCE_MIN   (-132)
#define YY_REDUCE_MAX   (1167)
static const short yy_reduce_ofst[] = {
 /*     0 */   -76,  -98,  -33,   35,  176,  213,  280,  451,  633,  709,
 /*    10 */   714,  728,  747,  761,  766,  780,  805,  820,  834,  847,
 /*    20 */   861,  874,  888,  913,  926,  940,  953,   54,  340,  967,
 /*    30 */   305,  -11,  539,  620,   76,   76,  249,  639,  455,  572,
 /*    40 */   251,  656,  695,  732,  976,  990,  994, 1003, 1019, 1027,
 /*    50 */  1032, 1046, 1055, 1061, 1069, 1084, 1092, 1097, 1100, 1125,
 /*    60 */  1128, 1133, 1141, 1164, 1167,   82, -114,  -39,  -20,   30,
 /*    70 */   169,   83, -132, -132, -132,  -99,  -77,  -62,  -40,   25,
 /*    80 */    40,   59,   80,
};
static const YYACTIONTYPE yy_default[] = {
 /*     0 */   449,  443,  443,  443,  443,  443,  443,  443,  443,  443,
 /*    10 */   443,  443,  443,  443,  443,  443,  443,  443,  443,  443,
 /*    20 */   443,  443,  443,  443,  443,  443,  443,  443,  473,  576,
 /*    30 */   443,  449,  580,  485,  581,  581,  449,  443,  443,  443,
 /*    40 */   443,  443,  443,  443,  443,  443,  443,  443,  477,  443,
1162
1163
1164
1165
1166
1167
1168

1169
1170
1171
1172
1173
1174
1175
    0,  /*  CODEBLOCK => nothing */
    0,  /*       FILL => nothing */
    0,  /*      COLOR => nothing */
    0,  /*  THICKNESS => nothing */
    0,  /*      PRINT => nothing */
    0,  /*     STRING => nothing */
    0,  /*      COMMA => nothing */

    0,  /*  CLASSNAME => nothing */
    0,  /*         LB => nothing */
    0,  /*         RB => nothing */
    0,  /*         UP => nothing */
    0,  /*       DOWN => nothing */
    0,  /*       LEFT => nothing */
    0,  /*      RIGHT => nothing */







>







1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
    0,  /*  CODEBLOCK => nothing */
    0,  /*       FILL => nothing */
    0,  /*      COLOR => nothing */
    0,  /*  THICKNESS => nothing */
    0,  /*      PRINT => nothing */
    0,  /*     STRING => nothing */
    0,  /*      COMMA => nothing */
    0,  /*    ISODATE => nothing */
    0,  /*  CLASSNAME => nothing */
    0,  /*         LB => nothing */
    0,  /*         RB => nothing */
    0,  /*         UP => nothing */
    0,  /*       DOWN => nothing */
    0,  /*       LEFT => nothing */
    0,  /*      RIGHT => nothing */
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


1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461


1462
1463
1464
1465
1466
1467
1468
  /*   19 */ "CODEBLOCK",
  /*   20 */ "FILL",
  /*   21 */ "COLOR",
  /*   22 */ "THICKNESS",
  /*   23 */ "PRINT",
  /*   24 */ "STRING",
  /*   25 */ "COMMA",
  /*   26 */ "CLASSNAME",
  /*   27 */ "LB",
  /*   28 */ "RB",
  /*   29 */ "UP",
  /*   30 */ "DOWN",
  /*   31 */ "LEFT",
  /*   32 */ "RIGHT",
  /*   33 */ "CLOSE",
  /*   34 */ "CHOP",
  /*   35 */ "FROM",
  /*   36 */ "TO",
  /*   37 */ "THEN",
  /*   38 */ "HEADING",
  /*   39 */ "GO",
  /*   40 */ "AT",
  /*   41 */ "WITH",
  /*   42 */ "SAME",
  /*   43 */ "AS",
  /*   44 */ "FIT",
  /*   45 */ "BEHIND",
  /*   46 */ "UNTIL",
  /*   47 */ "EVEN",
  /*   48 */ "DOT_E",
  /*   49 */ "HEIGHT",
  /*   50 */ "WIDTH",
  /*   51 */ "RADIUS",
  /*   52 */ "DIAMETER",
  /*   53 */ "DOTTED",
  /*   54 */ "DASHED",
  /*   55 */ "CW",
  /*   56 */ "CCW",
  /*   57 */ "LARROW",
  /*   58 */ "RARROW",
  /*   59 */ "LRARROW",
  /*   60 */ "INVIS",
  /*   61 */ "THICK",
  /*   62 */ "THIN",
  /*   63 */ "SOLID",
  /*   64 */ "CENTER",
  /*   65 */ "LJUST",
  /*   66 */ "RJUST",
  /*   67 */ "ABOVE",
  /*   68 */ "BELOW",
  /*   69 */ "ITALIC",
  /*   70 */ "BOLD",
  /*   71 */ "MONO",
  /*   72 */ "ALIGNED",
  /*   73 */ "BIG",
  /*   74 */ "SMALL",
  /*   75 */ "AND",
  /*   76 */ "LT",
  /*   77 */ "GT",
  /*   78 */ "ON",
  /*   79 */ "WAY",
  /*   80 */ "BETWEEN",
  /*   81 */ "THE",
  /*   82 */ "NTH",
  /*   83 */ "VERTEX",
  /*   84 */ "TOP",
  /*   85 */ "BOTTOM",
  /*   86 */ "START",
  /*   87 */ "END",
  /*   88 */ "IN",
  /*   89 */ "THIS",
  /*   90 */ "DOT_U",
  /*   91 */ "LAST",
  /*   92 */ "NUMBER",
  /*   93 */ "FUNC1",
  /*   94 */ "FUNC2",
  /*   95 */ "DIST",
  /*   96 */ "DOT_XY",
  /*   97 */ "X",
  /*   98 */ "Y",
  /*   99 */ "DOT_L",
  /*  100 */ "statement_list",
  /*  101 */ "statement",
  /*  102 */ "unnamed_statement",
  /*  103 */ "basetype",
  /*  104 */ "expr",
  /*  105 */ "numproperty",
  /*  106 */ "edge",
  /*  107 */ "direction",


  /*  108 */ "dashproperty",
  /*  109 */ "colorproperty",
  /*  110 */ "locproperty",
  /*  111 */ "position",
  /*  112 */ "place",
  /*  113 */ "object",
  /*  114 */ "objectname",
  /*  115 */ "nth",
  /*  116 */ "textposition",
  /*  117 */ "rvalue",
  /*  118 */ "lvalue",
  /*  119 */ "even",
  /*  120 */ "relexpr",
  /*  121 */ "optrelexpr",
  /*  122 */ "document",
  /*  123 */ "print",
  /*  124 */ "prlist",
  /*  125 */ "pritem",
  /*  126 */ "prsep",
  /*  127 */ "attribute_list",
  /*  128 */ "savelist",
  /*  129 */ "alist",
  /*  130 */ "attribute",
  /*  131 */ "go",
  /*  132 */ "boolproperty",
  /*  133 */ "withclause",
  /*  134 */ "between",
  /*  135 */ "place2",


};
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */

#ifndef NDEBUG
/* For tracing reduce actions, the names of all rules are required.
*/
static const char *const yyRuleName[] = {







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







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
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487


1488
1489
1490
1491
1492
1493
1494
1495
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
  /*   19 */ "CODEBLOCK",
  /*   20 */ "FILL",
  /*   21 */ "COLOR",
  /*   22 */ "THICKNESS",
  /*   23 */ "PRINT",
  /*   24 */ "STRING",
  /*   25 */ "COMMA",
  /*   26 */ "ISODATE",
  /*   27 */ "CLASSNAME",
  /*   28 */ "LB",
  /*   29 */ "RB",
  /*   30 */ "UP",
  /*   31 */ "DOWN",
  /*   32 */ "LEFT",
  /*   33 */ "RIGHT",
  /*   34 */ "CLOSE",
  /*   35 */ "CHOP",
  /*   36 */ "FROM",
  /*   37 */ "TO",
  /*   38 */ "THEN",
  /*   39 */ "HEADING",
  /*   40 */ "GO",
  /*   41 */ "AT",
  /*   42 */ "WITH",
  /*   43 */ "SAME",
  /*   44 */ "AS",
  /*   45 */ "FIT",
  /*   46 */ "BEHIND",
  /*   47 */ "UNTIL",
  /*   48 */ "EVEN",
  /*   49 */ "DOT_E",
  /*   50 */ "HEIGHT",
  /*   51 */ "WIDTH",
  /*   52 */ "RADIUS",
  /*   53 */ "DIAMETER",
  /*   54 */ "DOTTED",
  /*   55 */ "DASHED",
  /*   56 */ "CW",
  /*   57 */ "CCW",
  /*   58 */ "LARROW",
  /*   59 */ "RARROW",
  /*   60 */ "LRARROW",
  /*   61 */ "INVIS",
  /*   62 */ "THICK",
  /*   63 */ "THIN",
  /*   64 */ "SOLID",
  /*   65 */ "CENTER",
  /*   66 */ "LJUST",
  /*   67 */ "RJUST",
  /*   68 */ "ABOVE",
  /*   69 */ "BELOW",
  /*   70 */ "ITALIC",
  /*   71 */ "BOLD",
  /*   72 */ "MONO",
  /*   73 */ "ALIGNED",
  /*   74 */ "BIG",
  /*   75 */ "SMALL",
  /*   76 */ "AND",
  /*   77 */ "LT",
  /*   78 */ "GT",
  /*   79 */ "ON",
  /*   80 */ "WAY",
  /*   81 */ "BETWEEN",
  /*   82 */ "THE",
  /*   83 */ "NTH",
  /*   84 */ "VERTEX",
  /*   85 */ "TOP",
  /*   86 */ "BOTTOM",
  /*   87 */ "START",
  /*   88 */ "END",
  /*   89 */ "IN",
  /*   90 */ "THIS",
  /*   91 */ "DOT_U",
  /*   92 */ "LAST",
  /*   93 */ "NUMBER",
  /*   94 */ "FUNC1",
  /*   95 */ "FUNC2",
  /*   96 */ "DIST",
  /*   97 */ "DOT_XY",
  /*   98 */ "X",
  /*   99 */ "Y",
  /*  100 */ "DOT_L",
  /*  101 */ "statement_list",
  /*  102 */ "statement",
  /*  103 */ "unnamed_statement",
  /*  104 */ "basetype",
  /*  105 */ "expr",
  /*  106 */ "numproperty",
  /*  107 */ "edge",
  /*  108 */ "isodate",
  /*  109 */ "direction",
  /*  110 */ "dashproperty",


  /*  111 */ "colorproperty",
  /*  112 */ "locproperty",
  /*  113 */ "position",
  /*  114 */ "place",
  /*  115 */ "object",
  /*  116 */ "objectname",
  /*  117 */ "nth",
  /*  118 */ "textposition",
  /*  119 */ "rvalue",
  /*  120 */ "lvalue",
  /*  121 */ "even",
  /*  122 */ "relexpr",
  /*  123 */ "optrelexpr",
  /*  124 */ "document",
  /*  125 */ "print",
  /*  126 */ "prlist",
  /*  127 */ "pritem",
  /*  128 */ "prsep",
  /*  129 */ "attribute_list",
  /*  130 */ "savelist",
  /*  131 */ "alist",
  /*  132 */ "attribute",
  /*  133 */ "go",
  /*  134 */ "boolproperty",
  /*  135 */ "withclause",
  /*  136 */ "between",
  /*  137 */ "place2",
};
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */

#ifndef NDEBUG
/* For tracing reduce actions, the names of all rules are required.
*/
static const char *const yyRuleName[] = {
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
    ** being destroyed before it is finished parsing.
    **
    ** Note: during a reduce, the only symbols destroyed are those
    ** which appear on the RHS of the rule, but which are *not* used
    ** inside the C code.
    */
/********* Begin destructor definitions ***************************************/
    case 100: /* statement_list */
{
#line 511 "pikchr.y"
pik_elist_free(p,(yypminor->yy235));
#line 1777 "pikchr.c"
}
      break;
    case 101: /* statement */
    case 102: /* unnamed_statement */
    case 103: /* basetype */
{
#line 513 "pikchr.y"
pik_elem_free(p,(yypminor->yy162));
#line 1786 "pikchr.c"
}
      break;
/********* End destructor definitions *****************************************/
    default:  break;   /* If no destructor action specified: do nothing */
  }
}








|

|
|
|


|
|
|

|
|
|







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
    ** being destroyed before it is finished parsing.
    **
    ** Note: during a reduce, the only symbols destroyed are those
    ** which appear on the RHS of the rule, but which are *not* used
    ** inside the C code.
    */
/********* Begin destructor definitions ***************************************/
    case 101: /* statement_list */
{
#line 524 "pikchr.y"
pik_elist_free(p,(yypminor->yy23));
#line 1805 "pikchr.c"
}
      break;
    case 102: /* statement */
    case 103: /* unnamed_statement */
    case 104: /* basetype */
{
#line 526 "pikchr.y"
pik_elem_free(p,(yypminor->yy54));
#line 1814 "pikchr.c"
}
      break;
/********* End destructor definitions *****************************************/
    default:  break;   /* If no destructor action specified: do nothing */
  }
}

1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
     fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
   }
#endif
   while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
   /* Here code is inserted which will execute if the parser
   ** stack every overflows */
/******** Begin %stack_overflow code ******************************************/
#line 545 "pikchr.y"

  pik_error(p, 0, "parser stack overflow");
#line 2024 "pikchr.c"
/******** End %stack_overflow code ********************************************/
   pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */
   pik_parserCTX_STORE
}

/*
** Print tracing information for a SHIFT action







|


|







2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
     fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
   }
#endif
   while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
   /* Here code is inserted which will execute if the parser
   ** stack every overflows */
/******** Begin %stack_overflow code ******************************************/
#line 559 "pikchr.y"

  pik_error(p, 0, "parser stack overflow");
#line 2052 "pikchr.c"
/******** End %stack_overflow code ********************************************/
   pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */
   pik_parserCTX_STORE
}

/*
** Print tracing information for a SHIFT action
2058
2059
2060
2061
2062
2063
2064
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
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
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
2145
2146
2147
2148
2149
2150
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
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
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
2224
2225
2226
2227
  yytos->minor.yy0 = yyMinor;
  yyTraceShift(yypParser, yyNewState, "Shift");
}

/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
** of that rule */
static const YYCODETYPE yyRuleInfoLhs[] = {
   122,  /* (0) document ::= statement_list */
   100,  /* (1) statement_list ::= statement */
   100,  /* (2) statement_list ::= statement_list EOL statement */
   101,  /* (3) statement ::= */
   101,  /* (4) statement ::= direction */
   101,  /* (5) statement ::= lvalue ASSIGN rvalue */
   101,  /* (6) statement ::= PLACENAME COLON unnamed_statement */
   101,  /* (7) statement ::= PLACENAME COLON position */
   101,  /* (8) statement ::= unnamed_statement */
   101,  /* (9) statement ::= print prlist */
   101,  /* (10) statement ::= ASSERT LP expr EQ expr RP */
   101,  /* (11) statement ::= ASSERT LP position EQ position RP */
   101,  /* (12) statement ::= DEFINE ID CODEBLOCK */
   117,  /* (13) rvalue ::= PLACENAME */
   125,  /* (14) pritem ::= FILL */
   125,  /* (15) pritem ::= COLOR */
   125,  /* (16) pritem ::= THICKNESS */
   125,  /* (17) pritem ::= rvalue */
   125,  /* (18) pritem ::= STRING */
   126,  /* (19) prsep ::= COMMA */
   102,  /* (20) unnamed_statement ::= basetype attribute_list */
   103,  /* (21) basetype ::= CLASSNAME */
   103,  /* (22) basetype ::= STRING textposition */
   103,  /* (23) basetype ::= LB savelist statement_list RB */
   128,  /* (24) savelist ::= */
   120,  /* (25) relexpr ::= expr */
   120,  /* (26) relexpr ::= expr PERCENT */
   121,  /* (27) optrelexpr ::= */
   127,  /* (28) attribute_list ::= relexpr alist */
   130,  /* (29) attribute ::= numproperty relexpr */
   130,  /* (30) attribute ::= dashproperty expr */
   130,  /* (31) attribute ::= dashproperty */
   130,  /* (32) attribute ::= colorproperty rvalue */
   130,  /* (33) attribute ::= go direction optrelexpr */
   130,  /* (34) attribute ::= go direction even position */
   130,  /* (35) attribute ::= CLOSE */
   130,  /* (36) attribute ::= CHOP */
   130,  /* (37) attribute ::= FROM position */
   130,  /* (38) attribute ::= TO position */
   130,  /* (39) attribute ::= THEN */
   130,  /* (40) attribute ::= THEN optrelexpr HEADING expr */
   130,  /* (41) attribute ::= THEN optrelexpr EDGEPT */
   130,  /* (42) attribute ::= GO optrelexpr HEADING expr */
   130,  /* (43) attribute ::= GO optrelexpr EDGEPT */
   130,  /* (44) attribute ::= AT position */
   130,  /* (45) attribute ::= SAME */
   130,  /* (46) attribute ::= SAME AS object */
   130,  /* (47) attribute ::= STRING textposition */
   130,  /* (48) attribute ::= FIT */
   130,  /* (49) attribute ::= BEHIND object */
   133,  /* (50) withclause ::= DOT_E edge AT position */
   133,  /* (51) withclause ::= edge AT position */
   105,  /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
   132,  /* (53) boolproperty ::= CW */
   132,  /* (54) boolproperty ::= CCW */
   132,  /* (55) boolproperty ::= LARROW */
   132,  /* (56) boolproperty ::= RARROW */
   132,  /* (57) boolproperty ::= LRARROW */
   132,  /* (58) boolproperty ::= INVIS */
   132,  /* (59) boolproperty ::= THICK */
   132,  /* (60) boolproperty ::= THIN */
   132,  /* (61) boolproperty ::= SOLID */
   116,  /* (62) textposition ::= */
   116,  /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */
   111,  /* (64) position ::= expr COMMA expr */
   111,  /* (65) position ::= place PLUS expr COMMA expr */
   111,  /* (66) position ::= place MINUS expr COMMA expr */
   111,  /* (67) position ::= place PLUS LP expr COMMA expr RP */
   111,  /* (68) position ::= place MINUS LP expr COMMA expr RP */
   111,  /* (69) position ::= LP position COMMA position RP */
   111,  /* (70) position ::= LP position RP */
   111,  /* (71) position ::= expr between position AND position */
   111,  /* (72) position ::= expr LT position COMMA position GT */
   111,  /* (73) position ::= expr ABOVE position */
   111,  /* (74) position ::= expr BELOW position */
   111,  /* (75) position ::= expr LEFT OF position */
   111,  /* (76) position ::= expr RIGHT OF position */
   111,  /* (77) position ::= expr ON HEADING EDGEPT OF position */
   111,  /* (78) position ::= expr HEADING EDGEPT OF position */
   111,  /* (79) position ::= expr EDGEPT OF position */
   111,  /* (80) position ::= expr ON HEADING expr FROM position */
   111,  /* (81) position ::= expr HEADING expr FROM position */
   112,  /* (82) place ::= edge OF object */
   135,  /* (83) place2 ::= object */
   135,  /* (84) place2 ::= object DOT_E edge */
   135,  /* (85) place2 ::= NTH VERTEX OF object */
   113,  /* (86) object ::= nth */
   113,  /* (87) object ::= nth OF|IN object */
   114,  /* (88) objectname ::= THIS */
   114,  /* (89) objectname ::= PLACENAME */
   114,  /* (90) objectname ::= objectname DOT_U PLACENAME */
   115,  /* (91) nth ::= NTH CLASSNAME */
   115,  /* (92) nth ::= NTH LAST CLASSNAME */
   115,  /* (93) nth ::= LAST CLASSNAME */
   115,  /* (94) nth ::= LAST */
   115,  /* (95) nth ::= NTH LB RB */
   115,  /* (96) nth ::= NTH LAST LB RB */
   115,  /* (97) nth ::= LAST LB RB */
   104,  /* (98) expr ::= expr PLUS expr */
   104,  /* (99) expr ::= expr MINUS expr */
   104,  /* (100) expr ::= expr STAR expr */
   104,  /* (101) expr ::= expr SLASH expr */
   104,  /* (102) expr ::= MINUS expr */
   104,  /* (103) expr ::= PLUS expr */
   104,  /* (104) expr ::= LP expr RP */
   104,  /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */
   104,  /* (106) expr ::= NUMBER */
   104,  /* (107) expr ::= ID */
   104,  /* (108) expr ::= FUNC1 LP expr RP */
   104,  /* (109) expr ::= FUNC2 LP expr COMMA expr RP */
   104,  /* (110) expr ::= DIST LP position COMMA position RP */
   104,  /* (111) expr ::= place2 DOT_XY X */
   104,  /* (112) expr ::= place2 DOT_XY Y */
   104,  /* (113) expr ::= object DOT_L numproperty */
   104,  /* (114) expr ::= object DOT_L dashproperty */
   104,  /* (115) expr ::= object DOT_L colorproperty */
   118,  /* (116) lvalue ::= ID */
   118,  /* (117) lvalue ::= FILL */
   118,  /* (118) lvalue ::= COLOR */
   118,  /* (119) lvalue ::= THICKNESS */
   117,  /* (120) rvalue ::= expr */
   123,  /* (121) print ::= PRINT */
   124,  /* (122) prlist ::= pritem */
   124,  /* (123) prlist ::= prlist prsep pritem */
   107,  /* (124) direction ::= UP */
   107,  /* (125) direction ::= DOWN */
   107,  /* (126) direction ::= LEFT */
   107,  /* (127) direction ::= RIGHT */
   121,  /* (128) optrelexpr ::= relexpr */
   127,  /* (129) attribute_list ::= alist */
   129,  /* (130) alist ::= */
   129,  /* (131) alist ::= alist attribute */
   130,  /* (132) attribute ::= boolproperty */
   130,  /* (133) attribute ::= WITH withclause */
   131,  /* (134) go ::= GO */
   131,  /* (135) go ::= */
   119,  /* (136) even ::= UNTIL EVEN WITH */
   119,  /* (137) even ::= EVEN WITH */
   108,  /* (138) dashproperty ::= DOTTED */
   108,  /* (139) dashproperty ::= DASHED */
   109,  /* (140) colorproperty ::= FILL */
   109,  /* (141) colorproperty ::= COLOR */
   111,  /* (142) position ::= place */
   134,  /* (143) between ::= WAY BETWEEN */
   134,  /* (144) between ::= BETWEEN */
   134,  /* (145) between ::= OF THE WAY BETWEEN */
   112,  /* (146) place ::= place2 */
   106,  /* (147) edge ::= CENTER */
   106,  /* (148) edge ::= EDGEPT */
   106,  /* (149) edge ::= TOP */
   106,  /* (150) edge ::= BOTTOM */
   106,  /* (151) edge ::= START */
   106,  /* (152) edge ::= END */
   106,  /* (153) edge ::= RIGHT */
   106,  /* (154) edge ::= LEFT */
   113,  /* (155) object ::= objectname */
};

/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
** of symbols on the right-hand side of that rule. */
static const signed char yyRuleInfoNRhs[] = {
   -1,  /* (0) document ::= statement_list */
   -1,  /* (1) statement_list ::= statement */







|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|







2111
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
2145
2146
2147
2148
2149
2150
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
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
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
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
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
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
  yytos->minor.yy0 = yyMinor;
  yyTraceShift(yypParser, yyNewState, "Shift");
}

/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
** of that rule */
static const YYCODETYPE yyRuleInfoLhs[] = {
   124,  /* (0) document ::= statement_list */
   101,  /* (1) statement_list ::= statement */
   101,  /* (2) statement_list ::= statement_list EOL statement */
   102,  /* (3) statement ::= */
   102,  /* (4) statement ::= direction */
   102,  /* (5) statement ::= lvalue ASSIGN rvalue */
   102,  /* (6) statement ::= PLACENAME COLON unnamed_statement */
   102,  /* (7) statement ::= PLACENAME COLON position */
   102,  /* (8) statement ::= unnamed_statement */
   102,  /* (9) statement ::= print prlist */
   102,  /* (10) statement ::= ASSERT LP expr EQ expr RP */
   102,  /* (11) statement ::= ASSERT LP position EQ position RP */
   102,  /* (12) statement ::= DEFINE ID CODEBLOCK */
   119,  /* (13) rvalue ::= PLACENAME */
   127,  /* (14) pritem ::= FILL */
   127,  /* (15) pritem ::= COLOR */
   127,  /* (16) pritem ::= THICKNESS */
   127,  /* (17) pritem ::= rvalue */
   127,  /* (18) pritem ::= STRING */
   128,  /* (19) prsep ::= COMMA */
   103,  /* (20) unnamed_statement ::= basetype attribute_list */
   104,  /* (21) basetype ::= CLASSNAME */
   104,  /* (22) basetype ::= STRING textposition */
   104,  /* (23) basetype ::= LB savelist statement_list RB */
   130,  /* (24) savelist ::= */
   122,  /* (25) relexpr ::= expr */
   122,  /* (26) relexpr ::= expr PERCENT */
   123,  /* (27) optrelexpr ::= */
   129,  /* (28) attribute_list ::= relexpr alist */
   132,  /* (29) attribute ::= numproperty relexpr */
   132,  /* (30) attribute ::= dashproperty expr */
   132,  /* (31) attribute ::= dashproperty */
   132,  /* (32) attribute ::= colorproperty rvalue */
   132,  /* (33) attribute ::= go direction optrelexpr */
   132,  /* (34) attribute ::= go direction even position */
   132,  /* (35) attribute ::= CLOSE */
   132,  /* (36) attribute ::= CHOP */
   132,  /* (37) attribute ::= FROM position */
   132,  /* (38) attribute ::= TO position */
   132,  /* (39) attribute ::= THEN */
   132,  /* (40) attribute ::= THEN optrelexpr HEADING expr */
   132,  /* (41) attribute ::= THEN optrelexpr EDGEPT */
   132,  /* (42) attribute ::= GO optrelexpr HEADING expr */
   132,  /* (43) attribute ::= GO optrelexpr EDGEPT */
   132,  /* (44) attribute ::= AT position */
   132,  /* (45) attribute ::= SAME */
   132,  /* (46) attribute ::= SAME AS object */
   132,  /* (47) attribute ::= STRING textposition */
   132,  /* (48) attribute ::= FIT */
   132,  /* (49) attribute ::= BEHIND object */
   135,  /* (50) withclause ::= DOT_E edge AT position */
   135,  /* (51) withclause ::= edge AT position */
   106,  /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
   134,  /* (53) boolproperty ::= CW */
   134,  /* (54) boolproperty ::= CCW */
   134,  /* (55) boolproperty ::= LARROW */
   134,  /* (56) boolproperty ::= RARROW */
   134,  /* (57) boolproperty ::= LRARROW */
   134,  /* (58) boolproperty ::= INVIS */
   134,  /* (59) boolproperty ::= THICK */
   134,  /* (60) boolproperty ::= THIN */
   134,  /* (61) boolproperty ::= SOLID */
   118,  /* (62) textposition ::= */
   118,  /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */
   113,  /* (64) position ::= expr COMMA expr */
   113,  /* (65) position ::= place PLUS expr COMMA expr */
   113,  /* (66) position ::= place MINUS expr COMMA expr */
   113,  /* (67) position ::= place PLUS LP expr COMMA expr RP */
   113,  /* (68) position ::= place MINUS LP expr COMMA expr RP */
   113,  /* (69) position ::= LP position COMMA position RP */
   113,  /* (70) position ::= LP position RP */
   113,  /* (71) position ::= expr between position AND position */
   113,  /* (72) position ::= expr LT position COMMA position GT */
   113,  /* (73) position ::= expr ABOVE position */
   113,  /* (74) position ::= expr BELOW position */
   113,  /* (75) position ::= expr LEFT OF position */
   113,  /* (76) position ::= expr RIGHT OF position */
   113,  /* (77) position ::= expr ON HEADING EDGEPT OF position */
   113,  /* (78) position ::= expr HEADING EDGEPT OF position */
   113,  /* (79) position ::= expr EDGEPT OF position */
   113,  /* (80) position ::= expr ON HEADING expr FROM position */
   113,  /* (81) position ::= expr HEADING expr FROM position */
   114,  /* (82) place ::= edge OF object */
   137,  /* (83) place2 ::= object */
   137,  /* (84) place2 ::= object DOT_E edge */
   137,  /* (85) place2 ::= NTH VERTEX OF object */
   115,  /* (86) object ::= nth */
   115,  /* (87) object ::= nth OF|IN object */
   116,  /* (88) objectname ::= THIS */
   116,  /* (89) objectname ::= PLACENAME */
   116,  /* (90) objectname ::= objectname DOT_U PLACENAME */
   117,  /* (91) nth ::= NTH CLASSNAME */
   117,  /* (92) nth ::= NTH LAST CLASSNAME */
   117,  /* (93) nth ::= LAST CLASSNAME */
   117,  /* (94) nth ::= LAST */
   117,  /* (95) nth ::= NTH LB RB */
   117,  /* (96) nth ::= NTH LAST LB RB */
   117,  /* (97) nth ::= LAST LB RB */
   105,  /* (98) expr ::= expr PLUS expr */
   105,  /* (99) expr ::= expr MINUS expr */
   105,  /* (100) expr ::= expr STAR expr */
   105,  /* (101) expr ::= expr SLASH expr */
   105,  /* (102) expr ::= MINUS expr */
   105,  /* (103) expr ::= PLUS expr */
   105,  /* (104) expr ::= LP expr RP */
   105,  /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */
   105,  /* (106) expr ::= NUMBER */
   105,  /* (107) expr ::= ID */
   105,  /* (108) expr ::= FUNC1 LP expr RP */
   105,  /* (109) expr ::= FUNC2 LP expr COMMA expr RP */
   105,  /* (110) expr ::= DIST LP position COMMA position RP */
   105,  /* (111) expr ::= place2 DOT_XY X */
   105,  /* (112) expr ::= place2 DOT_XY Y */
   105,  /* (113) expr ::= object DOT_L numproperty */
   105,  /* (114) expr ::= object DOT_L dashproperty */
   105,  /* (115) expr ::= object DOT_L colorproperty */
   120,  /* (116) lvalue ::= ID */
   120,  /* (117) lvalue ::= FILL */
   120,  /* (118) lvalue ::= COLOR */
   120,  /* (119) lvalue ::= THICKNESS */
   119,  /* (120) rvalue ::= expr */
   125,  /* (121) print ::= PRINT */
   126,  /* (122) prlist ::= pritem */
   126,  /* (123) prlist ::= prlist prsep pritem */
   109,  /* (124) direction ::= UP */
   109,  /* (125) direction ::= DOWN */
   109,  /* (126) direction ::= LEFT */
   109,  /* (127) direction ::= RIGHT */
   123,  /* (128) optrelexpr ::= relexpr */
   129,  /* (129) attribute_list ::= alist */
   131,  /* (130) alist ::= */
   131,  /* (131) alist ::= alist attribute */
   132,  /* (132) attribute ::= boolproperty */
   132,  /* (133) attribute ::= WITH withclause */
   133,  /* (134) go ::= GO */
   133,  /* (135) go ::= */
   121,  /* (136) even ::= UNTIL EVEN WITH */
   121,  /* (137) even ::= EVEN WITH */
   110,  /* (138) dashproperty ::= DOTTED */
   110,  /* (139) dashproperty ::= DASHED */
   111,  /* (140) colorproperty ::= FILL */
   111,  /* (141) colorproperty ::= COLOR */
   113,  /* (142) position ::= place */
   136,  /* (143) between ::= WAY BETWEEN */
   136,  /* (144) between ::= BETWEEN */
   136,  /* (145) between ::= OF THE WAY BETWEEN */
   114,  /* (146) place ::= place2 */
   107,  /* (147) edge ::= CENTER */
   107,  /* (148) edge ::= EDGEPT */
   107,  /* (149) edge ::= TOP */
   107,  /* (150) edge ::= BOTTOM */
   107,  /* (151) edge ::= START */
   107,  /* (152) edge ::= END */
   107,  /* (153) edge ::= RIGHT */
   107,  /* (154) edge ::= LEFT */
   115,  /* (155) object ::= objectname */
};

/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
** of symbols on the right-hand side of that rule. */
static const signed char yyRuleInfoNRhs[] = {
   -1,  /* (0) document ::= statement_list */
   -1,  /* (1) statement_list ::= statement */
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
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
2864
2865
2866
2867
2868
2869
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
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
  **     { ... }           // User supplied code
  **  #line <lineno> <thisfile>
  **     break;
  */
/********** Begin reduce actions **********************************************/
        YYMINORTYPE yylhsminor;
      case 0: /* document ::= statement_list */
#line 549 "pikchr.y"
{pik_render(p,yymsp[0].minor.yy235);}
#line 2451 "pikchr.c"
        break;
      case 1: /* statement_list ::= statement */
#line 552 "pikchr.y"
{ yylhsminor.yy235 = pik_elist_append(p,0,yymsp[0].minor.yy162); }
#line 2456 "pikchr.c"
  yymsp[0].minor.yy235 = yylhsminor.yy235;
        break;
      case 2: /* statement_list ::= statement_list EOL statement */
#line 554 "pikchr.y"
{ yylhsminor.yy235 = pik_elist_append(p,yymsp[-2].minor.yy235,yymsp[0].minor.yy162); }
#line 2462 "pikchr.c"
  yymsp[-2].minor.yy235 = yylhsminor.yy235;
        break;
      case 3: /* statement ::= */
#line 557 "pikchr.y"
{ yymsp[1].minor.yy162 = 0; }
#line 2468 "pikchr.c"
        break;
      case 4: /* statement ::= direction */
#line 558 "pikchr.y"
{ pik_set_direction(p,yymsp[0].minor.yy0.eCode);  yylhsminor.yy162=0; }
#line 2473 "pikchr.c"
  yymsp[0].minor.yy162 = yylhsminor.yy162;
        break;
      case 5: /* statement ::= lvalue ASSIGN rvalue */
#line 559 "pikchr.y"
{pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy21,&yymsp[-1].minor.yy0); yylhsminor.yy162=0;}
#line 2479 "pikchr.c"
  yymsp[-2].minor.yy162 = yylhsminor.yy162;
        break;
      case 6: /* statement ::= PLACENAME COLON unnamed_statement */
#line 561 "pikchr.y"
{ yylhsminor.yy162 = yymsp[0].minor.yy162;  pik_elem_setname(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0); }
#line 2485 "pikchr.c"
  yymsp[-2].minor.yy162 = yylhsminor.yy162;
        break;
      case 7: /* statement ::= PLACENAME COLON position */
#line 563 "pikchr.y"
{ yylhsminor.yy162 = pik_elem_new(p,0,0,0);
                 if(yylhsminor.yy162){ yylhsminor.yy162->ptAt = yymsp[0].minor.yy63; pik_elem_setname(p,yylhsminor.yy162,&yymsp[-2].minor.yy0); }}
#line 2492 "pikchr.c"
  yymsp[-2].minor.yy162 = yylhsminor.yy162;
        break;
      case 8: /* statement ::= unnamed_statement */
#line 565 "pikchr.y"
{yylhsminor.yy162 = yymsp[0].minor.yy162;}
#line 2498 "pikchr.c"
  yymsp[0].minor.yy162 = yylhsminor.yy162;
        break;
      case 9: /* statement ::= print prlist */
#line 566 "pikchr.y"
{pik_append(p,"<br>\n",5); yymsp[-1].minor.yy162=0;}
#line 2504 "pikchr.c"
        break;
      case 10: /* statement ::= ASSERT LP expr EQ expr RP */
#line 571 "pikchr.y"
{yymsp[-5].minor.yy162=pik_assert(p,yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy21);}
#line 2509 "pikchr.c"
        break;
      case 11: /* statement ::= ASSERT LP position EQ position RP */
#line 573 "pikchr.y"
{yymsp[-5].minor.yy162=pik_position_assert(p,&yymsp[-3].minor.yy63,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy63);}
#line 2514 "pikchr.c"
        break;
      case 12: /* statement ::= DEFINE ID CODEBLOCK */
#line 574 "pikchr.y"
{yymsp[-2].minor.yy162=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
#line 2519 "pikchr.c"
        break;
      case 13: /* rvalue ::= PLACENAME */
#line 585 "pikchr.y"
{yylhsminor.yy21 = pik_lookup_color(p,&yymsp[0].minor.yy0);}
#line 2524 "pikchr.c"
  yymsp[0].minor.yy21 = yylhsminor.yy21;
        break;
      case 14: /* pritem ::= FILL */
      case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15);
      case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16);
#line 590 "pikchr.y"
{pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));}
#line 2532 "pikchr.c"
        break;
      case 17: /* pritem ::= rvalue */
#line 593 "pikchr.y"
{pik_append_num(p,"",yymsp[0].minor.yy21);}
#line 2537 "pikchr.c"
        break;
      case 18: /* pritem ::= STRING */
#line 594 "pikchr.y"
{pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);}
#line 2542 "pikchr.c"
        break;
      case 19: /* prsep ::= COMMA */
#line 595 "pikchr.y"
{pik_append(p, " ", 1);}
#line 2547 "pikchr.c"
        break;
      case 20: /* unnamed_statement ::= basetype attribute_list */
#line 598 "pikchr.y"
{yylhsminor.yy162 = yymsp[-1].minor.yy162; pik_after_adding_attributes(p,yylhsminor.yy162);}
#line 2552 "pikchr.c"
  yymsp[-1].minor.yy162 = yylhsminor.yy162;
        break;
      case 21: /* basetype ::= CLASSNAME */
#line 600 "pikchr.y"
{yylhsminor.yy162 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); }
#line 2558 "pikchr.c"
  yymsp[0].minor.yy162 = yylhsminor.yy162;
        break;
      case 22: /* basetype ::= STRING textposition */
#line 602 "pikchr.y"
{yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy188; yylhsminor.yy162 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); }
#line 2564 "pikchr.c"
  yymsp[-1].minor.yy162 = yylhsminor.yy162;
        break;
      case 23: /* basetype ::= LB savelist statement_list RB */
#line 604 "pikchr.y"
{ p->list = yymsp[-2].minor.yy235; yymsp[-3].minor.yy162 = pik_elem_new(p,0,0,yymsp[-1].minor.yy235); if(yymsp[-3].minor.yy162) yymsp[-3].minor.yy162->errTok = yymsp[0].minor.yy0; }
#line 2570 "pikchr.c"
        break;
      case 24: /* savelist ::= */
#line 609 "pikchr.y"
{yymsp[1].minor.yy235 = p->list; p->list = 0;}
#line 2575 "pikchr.c"
        break;
      case 25: /* relexpr ::= expr */
#line 616 "pikchr.y"
{yylhsminor.yy72.rAbs = yymsp[0].minor.yy21; yylhsminor.yy72.rRel = 0;}
#line 2580 "pikchr.c"
  yymsp[0].minor.yy72 = yylhsminor.yy72;
        break;
      case 26: /* relexpr ::= expr PERCENT */
#line 617 "pikchr.y"
{yylhsminor.yy72.rAbs = 0; yylhsminor.yy72.rRel = yymsp[-1].minor.yy21/100;}
#line 2586 "pikchr.c"
  yymsp[-1].minor.yy72 = yylhsminor.yy72;
        break;
      case 27: /* optrelexpr ::= */
#line 619 "pikchr.y"
{yymsp[1].minor.yy72.rAbs = 0; yymsp[1].minor.yy72.rRel = 1.0;}
#line 2592 "pikchr.c"
        break;
      case 28: /* attribute_list ::= relexpr alist */
#line 621 "pikchr.y"
{pik_add_direction(p,0,&yymsp[-1].minor.yy72);}
#line 2597 "pikchr.c"
        break;
      case 29: /* attribute ::= numproperty relexpr */
#line 625 "pikchr.y"
{ pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72); }
#line 2602 "pikchr.c"
        break;
      case 30: /* attribute ::= dashproperty expr */
#line 626 "pikchr.y"
{ pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy21); }
#line 2607 "pikchr.c"
        break;
      case 31: /* attribute ::= dashproperty */
#line 627 "pikchr.y"
{ pik_set_dashed(p,&yymsp[0].minor.yy0,0);  }
#line 2612 "pikchr.c"
        break;
      case 32: /* attribute ::= colorproperty rvalue */
#line 628 "pikchr.y"
{ pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21); }
#line 2617 "pikchr.c"
        break;
      case 33: /* attribute ::= go direction optrelexpr */
#line 629 "pikchr.y"
{ pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72);}
#line 2622 "pikchr.c"
        break;
      case 34: /* attribute ::= go direction even position */
#line 630 "pikchr.y"
{pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63);}
#line 2627 "pikchr.c"
        break;
      case 35: /* attribute ::= CLOSE */
#line 631 "pikchr.y"
{ pik_close_path(p,&yymsp[0].minor.yy0); }
#line 2632 "pikchr.c"
        break;
      case 36: /* attribute ::= CHOP */
#line 632 "pikchr.y"
{ p->cur->bChop = 1; }
#line 2637 "pikchr.c"
        break;
      case 37: /* attribute ::= FROM position */
#line 633 "pikchr.y"
{ pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); }
#line 2642 "pikchr.c"
        break;
      case 38: /* attribute ::= TO position */
#line 634 "pikchr.y"
{ pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); }
#line 2647 "pikchr.c"
        break;
      case 39: /* attribute ::= THEN */
#line 635 "pikchr.y"
{ pik_then(p, &yymsp[0].minor.yy0, p->cur); }
#line 2652 "pikchr.c"
        break;
      case 40: /* attribute ::= THEN optrelexpr HEADING expr */
      case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42);
#line 637 "pikchr.y"
{pik_move_hdg(p,&yymsp[-2].minor.yy72,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21,0,&yymsp[-3].minor.yy0);}
#line 2658 "pikchr.c"
        break;
      case 41: /* attribute ::= THEN optrelexpr EDGEPT */
      case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43);
#line 638 "pikchr.y"
{pik_move_hdg(p,&yymsp[-1].minor.yy72,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);}
#line 2664 "pikchr.c"
        break;
      case 44: /* attribute ::= AT position */
#line 643 "pikchr.y"
{ pik_set_at(p,0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); }
#line 2669 "pikchr.c"
        break;
      case 45: /* attribute ::= SAME */
#line 645 "pikchr.y"
{pik_same(p,0,&yymsp[0].minor.yy0);}
#line 2674 "pikchr.c"
        break;
      case 46: /* attribute ::= SAME AS object */
#line 646 "pikchr.y"
{pik_same(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);}
#line 2679 "pikchr.c"
        break;
      case 47: /* attribute ::= STRING textposition */
#line 647 "pikchr.y"
{pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy188);}
#line 2684 "pikchr.c"
        break;
      case 48: /* attribute ::= FIT */
#line 648 "pikchr.y"
{pik_size_to_fit(p,&yymsp[0].minor.yy0,3); }
#line 2689 "pikchr.c"
        break;
      case 49: /* attribute ::= BEHIND object */
#line 649 "pikchr.y"
{pik_behind(p,yymsp[0].minor.yy162);}
#line 2694 "pikchr.c"
        break;
      case 50: /* withclause ::= DOT_E edge AT position */
      case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51);
#line 657 "pikchr.y"
{ pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); }
#line 2700 "pikchr.c"
        break;
      case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
#line 661 "pikchr.y"
{yylhsminor.yy0 = yymsp[0].minor.yy0;}
#line 2705 "pikchr.c"
  yymsp[0].minor.yy0 = yylhsminor.yy0;
        break;
      case 53: /* boolproperty ::= CW */
#line 672 "pikchr.y"
{p->cur->cw = 1;}
#line 2711 "pikchr.c"
        break;
      case 54: /* boolproperty ::= CCW */
#line 673 "pikchr.y"
{p->cur->cw = 0;}
#line 2716 "pikchr.c"
        break;
      case 55: /* boolproperty ::= LARROW */
#line 674 "pikchr.y"
{p->cur->larrow=1; p->cur->rarrow=0; }
#line 2721 "pikchr.c"
        break;
      case 56: /* boolproperty ::= RARROW */
#line 675 "pikchr.y"
{p->cur->larrow=0; p->cur->rarrow=1; }
#line 2726 "pikchr.c"
        break;
      case 57: /* boolproperty ::= LRARROW */
#line 676 "pikchr.y"
{p->cur->larrow=1; p->cur->rarrow=1; }
#line 2731 "pikchr.c"
        break;
      case 58: /* boolproperty ::= INVIS */
#line 677 "pikchr.y"
{p->cur->sw = -0.00001;}
#line 2736 "pikchr.c"
        break;
      case 59: /* boolproperty ::= THICK */
#line 678 "pikchr.y"
{p->cur->sw *= 1.5;}
#line 2741 "pikchr.c"
        break;
      case 60: /* boolproperty ::= THIN */
#line 679 "pikchr.y"
{p->cur->sw *= 0.67;}
#line 2746 "pikchr.c"
        break;
      case 61: /* boolproperty ::= SOLID */
#line 680 "pikchr.y"
{p->cur->sw = pik_value(p,"thickness",9,0);
                               p->cur->dotted = p->cur->dashed = 0.0;}
#line 2752 "pikchr.c"
        break;
      case 62: /* textposition ::= */
#line 683 "pikchr.y"
{yymsp[1].minor.yy188 = 0;}
#line 2757 "pikchr.c"
        break;
      case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */
#line 686 "pikchr.y"
{yylhsminor.yy188 = (short int)pik_text_position(yymsp[-1].minor.yy188,&yymsp[0].minor.yy0);}
#line 2762 "pikchr.c"
  yymsp[-1].minor.yy188 = yylhsminor.yy188;
        break;
      case 64: /* position ::= expr COMMA expr */
#line 689 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[0].minor.yy21;}
#line 2768 "pikchr.c"
  yymsp[-2].minor.yy63 = yylhsminor.yy63;
        break;
      case 65: /* position ::= place PLUS expr COMMA expr */
#line 691 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-4].minor.yy63.x+yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y+yymsp[0].minor.yy21;}
#line 2774 "pikchr.c"
  yymsp[-4].minor.yy63 = yylhsminor.yy63;
        break;
      case 66: /* position ::= place MINUS expr COMMA expr */
#line 692 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-4].minor.yy63.x-yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y-yymsp[0].minor.yy21;}
#line 2780 "pikchr.c"
  yymsp[-4].minor.yy63 = yylhsminor.yy63;
        break;
      case 67: /* position ::= place PLUS LP expr COMMA expr RP */
#line 694 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-6].minor.yy63.x+yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y+yymsp[-1].minor.yy21;}
#line 2786 "pikchr.c"
  yymsp[-6].minor.yy63 = yylhsminor.yy63;
        break;
      case 68: /* position ::= place MINUS LP expr COMMA expr RP */
#line 696 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-6].minor.yy63.x-yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y-yymsp[-1].minor.yy21;}
#line 2792 "pikchr.c"
  yymsp[-6].minor.yy63 = yylhsminor.yy63;
        break;
      case 69: /* position ::= LP position COMMA position RP */
#line 697 "pikchr.y"
{yymsp[-4].minor.yy63.x=yymsp[-3].minor.yy63.x; yymsp[-4].minor.yy63.y=yymsp[-1].minor.yy63.y;}
#line 2798 "pikchr.c"
        break;
      case 70: /* position ::= LP position RP */
#line 698 "pikchr.y"
{yymsp[-2].minor.yy63=yymsp[-1].minor.yy63;}
#line 2803 "pikchr.c"
        break;
      case 71: /* position ::= expr between position AND position */
#line 700 "pikchr.y"
{yylhsminor.yy63 = pik_position_between(yymsp[-4].minor.yy21,yymsp[-2].minor.yy63,yymsp[0].minor.yy63);}
#line 2808 "pikchr.c"
  yymsp[-4].minor.yy63 = yylhsminor.yy63;
        break;
      case 72: /* position ::= expr LT position COMMA position GT */
#line 702 "pikchr.y"
{yylhsminor.yy63 = pik_position_between(yymsp[-5].minor.yy21,yymsp[-3].minor.yy63,yymsp[-1].minor.yy63);}
#line 2814 "pikchr.c"
  yymsp[-5].minor.yy63 = yylhsminor.yy63;
        break;
      case 73: /* position ::= expr ABOVE position */
#line 703 "pikchr.y"
{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y += yymsp[-2].minor.yy21;}
#line 2820 "pikchr.c"
  yymsp[-2].minor.yy63 = yylhsminor.yy63;
        break;
      case 74: /* position ::= expr BELOW position */
#line 704 "pikchr.y"
{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y -= yymsp[-2].minor.yy21;}
#line 2826 "pikchr.c"
  yymsp[-2].minor.yy63 = yylhsminor.yy63;
        break;
      case 75: /* position ::= expr LEFT OF position */
#line 705 "pikchr.y"
{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x -= yymsp[-3].minor.yy21;}
#line 2832 "pikchr.c"
  yymsp[-3].minor.yy63 = yylhsminor.yy63;
        break;
      case 76: /* position ::= expr RIGHT OF position */
#line 706 "pikchr.y"
{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x += yymsp[-3].minor.yy21;}
#line 2838 "pikchr.c"
  yymsp[-3].minor.yy63 = yylhsminor.yy63;
        break;
      case 77: /* position ::= expr ON HEADING EDGEPT OF position */
#line 708 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-5].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);}
#line 2844 "pikchr.c"
  yymsp[-5].minor.yy63 = yylhsminor.yy63;
        break;
      case 78: /* position ::= expr HEADING EDGEPT OF position */
#line 710 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-4].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);}
#line 2850 "pikchr.c"
  yymsp[-4].minor.yy63 = yylhsminor.yy63;
        break;
      case 79: /* position ::= expr EDGEPT OF position */
#line 712 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);}
#line 2856 "pikchr.c"
  yymsp[-3].minor.yy63 = yylhsminor.yy63;
        break;
      case 80: /* position ::= expr ON HEADING expr FROM position */
#line 714 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_angle(yymsp[-5].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);}
#line 2862 "pikchr.c"
  yymsp[-5].minor.yy63 = yylhsminor.yy63;
        break;
      case 81: /* position ::= expr HEADING expr FROM position */
#line 716 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_angle(yymsp[-4].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);}
#line 2868 "pikchr.c"
  yymsp[-4].minor.yy63 = yylhsminor.yy63;
        break;
      case 82: /* place ::= edge OF object */
#line 728 "pikchr.y"
{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);}
#line 2874 "pikchr.c"
  yymsp[-2].minor.yy63 = yylhsminor.yy63;
        break;
      case 83: /* place2 ::= object */
#line 729 "pikchr.y"
{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,0);}
#line 2880 "pikchr.c"
  yymsp[0].minor.yy63 = yylhsminor.yy63;
        break;
      case 84: /* place2 ::= object DOT_E edge */
#line 730 "pikchr.y"
{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);}
#line 2886 "pikchr.c"
  yymsp[-2].minor.yy63 = yylhsminor.yy63;
        break;
      case 85: /* place2 ::= NTH VERTEX OF object */
#line 731 "pikchr.y"
{yylhsminor.yy63 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy162);}
#line 2892 "pikchr.c"
  yymsp[-3].minor.yy63 = yylhsminor.yy63;
        break;
      case 86: /* object ::= nth */
#line 743 "pikchr.y"
{yylhsminor.yy162 = pik_find_nth(p,0,&yymsp[0].minor.yy0);}
#line 2898 "pikchr.c"
  yymsp[0].minor.yy162 = yylhsminor.yy162;
        break;
      case 87: /* object ::= nth OF|IN object */
#line 744 "pikchr.y"
{yylhsminor.yy162 = pik_find_nth(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);}
#line 2904 "pikchr.c"
  yymsp[-2].minor.yy162 = yylhsminor.yy162;
        break;
      case 88: /* objectname ::= THIS */
#line 746 "pikchr.y"
{yymsp[0].minor.yy162 = p->cur;}
#line 2910 "pikchr.c"
        break;
      case 89: /* objectname ::= PLACENAME */
#line 747 "pikchr.y"
{yylhsminor.yy162 = pik_find_byname(p,0,&yymsp[0].minor.yy0);}
#line 2915 "pikchr.c"
  yymsp[0].minor.yy162 = yylhsminor.yy162;
        break;
      case 90: /* objectname ::= objectname DOT_U PLACENAME */
#line 749 "pikchr.y"
{yylhsminor.yy162 = pik_find_byname(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);}
#line 2921 "pikchr.c"
  yymsp[-2].minor.yy162 = yylhsminor.yy162;
        break;
      case 91: /* nth ::= NTH CLASSNAME */
#line 751 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); }
#line 2927 "pikchr.c"
  yymsp[-1].minor.yy0 = yylhsminor.yy0;
        break;
      case 92: /* nth ::= NTH LAST CLASSNAME */
#line 752 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); }
#line 2933 "pikchr.c"
  yymsp[-2].minor.yy0 = yylhsminor.yy0;
        break;
      case 93: /* nth ::= LAST CLASSNAME */
#line 753 "pikchr.y"
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;}
#line 2939 "pikchr.c"
        break;
      case 94: /* nth ::= LAST */
#line 754 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;}
#line 2944 "pikchr.c"
  yymsp[0].minor.yy0 = yylhsminor.yy0;
        break;
      case 95: /* nth ::= NTH LB RB */
#line 755 "pikchr.y"
{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);}
#line 2950 "pikchr.c"
  yymsp[-2].minor.yy0 = yylhsminor.yy0;
        break;
      case 96: /* nth ::= NTH LAST LB RB */
#line 756 "pikchr.y"
{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);}
#line 2956 "pikchr.c"
  yymsp[-3].minor.yy0 = yylhsminor.yy0;
        break;
      case 97: /* nth ::= LAST LB RB */
#line 757 "pikchr.y"
{yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; }
#line 2962 "pikchr.c"
        break;
      case 98: /* expr ::= expr PLUS expr */
#line 759 "pikchr.y"
{yylhsminor.yy21=yymsp[-2].minor.yy21+yymsp[0].minor.yy21;}
#line 2967 "pikchr.c"
  yymsp[-2].minor.yy21 = yylhsminor.yy21;
        break;
      case 99: /* expr ::= expr MINUS expr */
#line 760 "pikchr.y"
{yylhsminor.yy21=yymsp[-2].minor.yy21-yymsp[0].minor.yy21;}
#line 2973 "pikchr.c"
  yymsp[-2].minor.yy21 = yylhsminor.yy21;
        break;
      case 100: /* expr ::= expr STAR expr */
#line 761 "pikchr.y"
{yylhsminor.yy21=yymsp[-2].minor.yy21*yymsp[0].minor.yy21;}
#line 2979 "pikchr.c"
  yymsp[-2].minor.yy21 = yylhsminor.yy21;
        break;
      case 101: /* expr ::= expr SLASH expr */
#line 762 "pikchr.y"
{
  if( yymsp[0].minor.yy21==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy21 = 0.0; }
  else{ yylhsminor.yy21 = yymsp[-2].minor.yy21/yymsp[0].minor.yy21; }
}
#line 2988 "pikchr.c"
  yymsp[-2].minor.yy21 = yylhsminor.yy21;
        break;
      case 102: /* expr ::= MINUS expr */
#line 766 "pikchr.y"
{yymsp[-1].minor.yy21=-yymsp[0].minor.yy21;}
#line 2994 "pikchr.c"
        break;
      case 103: /* expr ::= PLUS expr */
#line 767 "pikchr.y"
{yymsp[-1].minor.yy21=yymsp[0].minor.yy21;}
#line 2999 "pikchr.c"
        break;
      case 104: /* expr ::= LP expr RP */
#line 768 "pikchr.y"
{yymsp[-2].minor.yy21=yymsp[-1].minor.yy21;}
#line 3004 "pikchr.c"
        break;
      case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */
#line 769 "pikchr.y"
{yymsp[-2].minor.yy21=pik_get_var(p,&yymsp[-1].minor.yy0);}
#line 3009 "pikchr.c"
        break;
      case 106: /* expr ::= NUMBER */
#line 770 "pikchr.y"
{yylhsminor.yy21=pik_atof(&yymsp[0].minor.yy0);}
#line 3014 "pikchr.c"
  yymsp[0].minor.yy21 = yylhsminor.yy21;
        break;
      case 107: /* expr ::= ID */
#line 771 "pikchr.y"
{yylhsminor.yy21=pik_get_var(p,&yymsp[0].minor.yy0);}
#line 3020 "pikchr.c"
  yymsp[0].minor.yy21 = yylhsminor.yy21;
        break;
      case 108: /* expr ::= FUNC1 LP expr RP */
#line 772 "pikchr.y"
{yylhsminor.yy21 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy21,0.0);}
#line 3026 "pikchr.c"
  yymsp[-3].minor.yy21 = yylhsminor.yy21;
        break;
      case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */
#line 773 "pikchr.y"
{yylhsminor.yy21 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy21,yymsp[-1].minor.yy21);}
#line 3032 "pikchr.c"
  yymsp[-5].minor.yy21 = yylhsminor.yy21;
        break;
      case 110: /* expr ::= DIST LP position COMMA position RP */
#line 774 "pikchr.y"
{yymsp[-5].minor.yy21 = pik_dist(&yymsp[-3].minor.yy63,&yymsp[-1].minor.yy63);}
#line 3038 "pikchr.c"
        break;
      case 111: /* expr ::= place2 DOT_XY X */
#line 775 "pikchr.y"
{yylhsminor.yy21 = yymsp[-2].minor.yy63.x;}
#line 3043 "pikchr.c"
  yymsp[-2].minor.yy21 = yylhsminor.yy21;
        break;
      case 112: /* expr ::= place2 DOT_XY Y */
#line 776 "pikchr.y"
{yylhsminor.yy21 = yymsp[-2].minor.yy63.y;}
#line 3049 "pikchr.c"
  yymsp[-2].minor.yy21 = yylhsminor.yy21;
        break;
      case 113: /* expr ::= object DOT_L numproperty */
      case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114);
      case 115: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==115);
#line 777 "pikchr.y"
{yylhsminor.yy21=pik_property_of(yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);}
#line 3057 "pikchr.c"
  yymsp[-2].minor.yy21 = yylhsminor.yy21;
        break;
      default:
      /* (116) lvalue ::= ID */ yytestcase(yyruleno==116);
      /* (117) lvalue ::= FILL */ yytestcase(yyruleno==117);
      /* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118);
      /* (119) lvalue ::= THICKNESS */ yytestcase(yyruleno==119);
      /* (120) rvalue ::= expr */ yytestcase(yyruleno==120);







|
|
|


|
|
|
|


|
|
|
|


|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|
|


|
|
|
|


|
|
|


|
|
|


|
|
|


|
|
|


|
|
|
|




|

|


|
|
|


|

|


|

|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|


|
|
|


|
|
|
|


|
|
|
|


|
|
|


|
|
|


|
|
|


|
|
|


|

|


|
|
|


|
|
|


|
|
|


|

|


|

|


|
|
|


|
|
|


|

|



|
|
|



|
|
|


|
|
|


|

|


|
|
|


|
|
|


|
|
|


|
|
|



|
|
|


|

|



|

|


|

|


|

|


|

|


|

|


|

|


|

|


|

|


|


|


|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|


|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|


|
|
|
|


|
|
|
|


|

|



|

|



|

|


|

|



|

|



|

|



|

|


|
|
|
|


|
|
|
|


|
|
|
|


|

|
|

|
|


|
|
|


|
|
|


|
|
|


|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|
|


|
|
|


|
|
|
|


|
|
|
|




|
|
|
|







2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
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
2864
2865
2866
2867
2868
2869
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
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
  **     { ... }           // User supplied code
  **  #line <lineno> <thisfile>
  **     break;
  */
/********** Begin reduce actions **********************************************/
        YYMINORTYPE yylhsminor;
      case 0: /* document ::= statement_list */
#line 563 "pikchr.y"
{pik_render(p,yymsp[0].minor.yy23);}
#line 2479 "pikchr.c"
        break;
      case 1: /* statement_list ::= statement */
#line 566 "pikchr.y"
{ yylhsminor.yy23 = pik_elist_append(p,0,yymsp[0].minor.yy54); }
#line 2484 "pikchr.c"
  yymsp[0].minor.yy23 = yylhsminor.yy23;
        break;
      case 2: /* statement_list ::= statement_list EOL statement */
#line 568 "pikchr.y"
{ yylhsminor.yy23 = pik_elist_append(p,yymsp[-2].minor.yy23,yymsp[0].minor.yy54); }
#line 2490 "pikchr.c"
  yymsp[-2].minor.yy23 = yylhsminor.yy23;
        break;
      case 3: /* statement ::= */
#line 571 "pikchr.y"
{ yymsp[1].minor.yy54 = 0; }
#line 2496 "pikchr.c"
        break;
      case 4: /* statement ::= direction */
#line 572 "pikchr.y"
{ pik_set_direction(p,yymsp[0].minor.yy0.eCode);  yylhsminor.yy54=0; }
#line 2501 "pikchr.c"
  yymsp[0].minor.yy54 = yylhsminor.yy54;
        break;
      case 5: /* statement ::= lvalue ASSIGN rvalue */
#line 573 "pikchr.y"
{pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy129,&yymsp[-1].minor.yy0); yylhsminor.yy54=0;}
#line 2507 "pikchr.c"
  yymsp[-2].minor.yy54 = yylhsminor.yy54;
        break;
      case 6: /* statement ::= PLACENAME COLON unnamed_statement */
#line 575 "pikchr.y"
{ yylhsminor.yy54 = yymsp[0].minor.yy54;  pik_elem_setname(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0); }
#line 2513 "pikchr.c"
  yymsp[-2].minor.yy54 = yylhsminor.yy54;
        break;
      case 7: /* statement ::= PLACENAME COLON position */
#line 577 "pikchr.y"
{ yylhsminor.yy54 = pik_elem_new(p,0,0,0);
                 if(yylhsminor.yy54){ yylhsminor.yy54->ptAt = yymsp[0].minor.yy187; pik_elem_setname(p,yylhsminor.yy54,&yymsp[-2].minor.yy0); }}
#line 2520 "pikchr.c"
  yymsp[-2].minor.yy54 = yylhsminor.yy54;
        break;
      case 8: /* statement ::= unnamed_statement */
#line 579 "pikchr.y"
{yylhsminor.yy54 = yymsp[0].minor.yy54;}
#line 2526 "pikchr.c"
  yymsp[0].minor.yy54 = yylhsminor.yy54;
        break;
      case 9: /* statement ::= print prlist */
#line 580 "pikchr.y"
{pik_append(p,"<br>\n",5); yymsp[-1].minor.yy54=0;}
#line 2532 "pikchr.c"
        break;
      case 10: /* statement ::= ASSERT LP expr EQ expr RP */
#line 585 "pikchr.y"
{yymsp[-5].minor.yy54=pik_assert(p,yymsp[-3].minor.yy129,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy129);}
#line 2537 "pikchr.c"
        break;
      case 11: /* statement ::= ASSERT LP position EQ position RP */
#line 587 "pikchr.y"
{yymsp[-5].minor.yy54=pik_position_assert(p,&yymsp[-3].minor.yy187,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy187);}
#line 2542 "pikchr.c"
        break;
      case 12: /* statement ::= DEFINE ID CODEBLOCK */
#line 588 "pikchr.y"
{yymsp[-2].minor.yy54=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
#line 2547 "pikchr.c"
        break;
      case 13: /* rvalue ::= PLACENAME */
#line 599 "pikchr.y"
{yylhsminor.yy129 = pik_lookup_color(p,&yymsp[0].minor.yy0);}
#line 2552 "pikchr.c"
  yymsp[0].minor.yy129 = yylhsminor.yy129;
        break;
      case 14: /* pritem ::= FILL */
      case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15);
      case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16);
#line 604 "pikchr.y"
{pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));}
#line 2560 "pikchr.c"
        break;
      case 17: /* pritem ::= rvalue */
#line 607 "pikchr.y"
{pik_append_num(p,"",yymsp[0].minor.yy129);}
#line 2565 "pikchr.c"
        break;
      case 18: /* pritem ::= STRING */
#line 608 "pikchr.y"
{pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);}
#line 2570 "pikchr.c"
        break;
      case 19: /* prsep ::= COMMA */
#line 609 "pikchr.y"
{pik_append(p, " ", 1);}
#line 2575 "pikchr.c"
        break;
      case 20: /* unnamed_statement ::= basetype attribute_list */
#line 614 "pikchr.y"
{yylhsminor.yy54 = yymsp[-1].minor.yy54; pik_after_adding_attributes(p,yylhsminor.yy54);}
#line 2580 "pikchr.c"
  yymsp[-1].minor.yy54 = yylhsminor.yy54;
        break;
      case 21: /* basetype ::= CLASSNAME */
#line 616 "pikchr.y"
{yylhsminor.yy54 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); }
#line 2586 "pikchr.c"
  yymsp[0].minor.yy54 = yylhsminor.yy54;
        break;
      case 22: /* basetype ::= STRING textposition */
#line 618 "pikchr.y"
{yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy272; yylhsminor.yy54 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); }
#line 2592 "pikchr.c"
  yymsp[-1].minor.yy54 = yylhsminor.yy54;
        break;
      case 23: /* basetype ::= LB savelist statement_list RB */
#line 620 "pikchr.y"
{ p->list = yymsp[-2].minor.yy23; yymsp[-3].minor.yy54 = pik_elem_new(p,0,0,yymsp[-1].minor.yy23); if(yymsp[-3].minor.yy54) yymsp[-3].minor.yy54->errTok = yymsp[0].minor.yy0; }
#line 2598 "pikchr.c"
        break;
      case 24: /* savelist ::= */
#line 625 "pikchr.y"
{yymsp[1].minor.yy23 = p->list; p->list = 0;}
#line 2603 "pikchr.c"
        break;
      case 25: /* relexpr ::= expr */
#line 632 "pikchr.y"
{yylhsminor.yy28.rAbs = yymsp[0].minor.yy129; yylhsminor.yy28.rRel = 0;}
#line 2608 "pikchr.c"
  yymsp[0].minor.yy28 = yylhsminor.yy28;
        break;
      case 26: /* relexpr ::= expr PERCENT */
#line 633 "pikchr.y"
{yylhsminor.yy28.rAbs = 0; yylhsminor.yy28.rRel = yymsp[-1].minor.yy129/100;}
#line 2614 "pikchr.c"
  yymsp[-1].minor.yy28 = yylhsminor.yy28;
        break;
      case 27: /* optrelexpr ::= */
#line 635 "pikchr.y"
{yymsp[1].minor.yy28.rAbs = 0; yymsp[1].minor.yy28.rRel = 1.0;}
#line 2620 "pikchr.c"
        break;
      case 28: /* attribute_list ::= relexpr alist */
#line 637 "pikchr.y"
{pik_add_direction(p,0,&yymsp[-1].minor.yy28);}
#line 2625 "pikchr.c"
        break;
      case 29: /* attribute ::= numproperty relexpr */
#line 641 "pikchr.y"
{ pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy28); }
#line 2630 "pikchr.c"
        break;
      case 30: /* attribute ::= dashproperty expr */
#line 642 "pikchr.y"
{ pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy129); }
#line 2635 "pikchr.c"
        break;
      case 31: /* attribute ::= dashproperty */
#line 643 "pikchr.y"
{ pik_set_dashed(p,&yymsp[0].minor.yy0,0);  }
#line 2640 "pikchr.c"
        break;
      case 32: /* attribute ::= colorproperty rvalue */
#line 644 "pikchr.y"
{ pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy129); }
#line 2645 "pikchr.c"
        break;
      case 33: /* attribute ::= go direction optrelexpr */
#line 645 "pikchr.y"
{ pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy28);}
#line 2650 "pikchr.c"
        break;
      case 34: /* attribute ::= go direction even position */
#line 646 "pikchr.y"
{pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy187);}
#line 2655 "pikchr.c"
        break;
      case 35: /* attribute ::= CLOSE */
#line 647 "pikchr.y"
{ pik_close_path(p,&yymsp[0].minor.yy0); }
#line 2660 "pikchr.c"
        break;
      case 36: /* attribute ::= CHOP */
#line 648 "pikchr.y"
{ p->cur->bChop = 1; }
#line 2665 "pikchr.c"
        break;
      case 37: /* attribute ::= FROM position */
#line 649 "pikchr.y"
{ pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy187); }
#line 2670 "pikchr.c"
        break;
      case 38: /* attribute ::= TO position */
#line 650 "pikchr.y"
{ pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy187); }
#line 2675 "pikchr.c"
        break;
      case 39: /* attribute ::= THEN */
#line 651 "pikchr.y"
{ pik_then(p, &yymsp[0].minor.yy0, p->cur); }
#line 2680 "pikchr.c"
        break;
      case 40: /* attribute ::= THEN optrelexpr HEADING expr */
      case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42);
#line 653 "pikchr.y"
{pik_move_hdg(p,&yymsp[-2].minor.yy28,&yymsp[-1].minor.yy0,yymsp[0].minor.yy129,0,&yymsp[-3].minor.yy0);}
#line 2686 "pikchr.c"
        break;
      case 41: /* attribute ::= THEN optrelexpr EDGEPT */
      case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43);
#line 654 "pikchr.y"
{pik_move_hdg(p,&yymsp[-1].minor.yy28,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);}
#line 2692 "pikchr.c"
        break;
      case 44: /* attribute ::= AT position */
#line 659 "pikchr.y"
{ pik_set_at(p,0,&yymsp[0].minor.yy187,&yymsp[-1].minor.yy0); }
#line 2697 "pikchr.c"
        break;
      case 45: /* attribute ::= SAME */
#line 661 "pikchr.y"
{pik_same(p,0,&yymsp[0].minor.yy0);}
#line 2702 "pikchr.c"
        break;
      case 46: /* attribute ::= SAME AS object */
#line 662 "pikchr.y"
{pik_same(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);}
#line 2707 "pikchr.c"
        break;
      case 47: /* attribute ::= STRING textposition */
#line 663 "pikchr.y"
{pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy272);}
#line 2712 "pikchr.c"
        break;
      case 48: /* attribute ::= FIT */
#line 664 "pikchr.y"
{pik_size_to_fit(p,0,&yymsp[0].minor.yy0,3); }
#line 2717 "pikchr.c"
        break;
      case 49: /* attribute ::= BEHIND object */
#line 665 "pikchr.y"
{pik_behind(p,yymsp[0].minor.yy54);}
#line 2722 "pikchr.c"
        break;
      case 50: /* withclause ::= DOT_E edge AT position */
      case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51);
#line 673 "pikchr.y"
{ pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy187,&yymsp[-1].minor.yy0); }
#line 2728 "pikchr.c"
        break;
      case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
#line 677 "pikchr.y"
{yylhsminor.yy0 = yymsp[0].minor.yy0;}
#line 2733 "pikchr.c"
  yymsp[0].minor.yy0 = yylhsminor.yy0;
        break;
      case 53: /* boolproperty ::= CW */
#line 688 "pikchr.y"
{p->cur->cw = 1;}
#line 2739 "pikchr.c"
        break;
      case 54: /* boolproperty ::= CCW */
#line 689 "pikchr.y"
{p->cur->cw = 0;}
#line 2744 "pikchr.c"
        break;
      case 55: /* boolproperty ::= LARROW */
#line 690 "pikchr.y"
{p->cur->larrow=1; p->cur->rarrow=0; }
#line 2749 "pikchr.c"
        break;
      case 56: /* boolproperty ::= RARROW */
#line 691 "pikchr.y"
{p->cur->larrow=0; p->cur->rarrow=1; }
#line 2754 "pikchr.c"
        break;
      case 57: /* boolproperty ::= LRARROW */
#line 692 "pikchr.y"
{p->cur->larrow=1; p->cur->rarrow=1; }
#line 2759 "pikchr.c"
        break;
      case 58: /* boolproperty ::= INVIS */
#line 693 "pikchr.y"
{p->cur->sw = -0.00001;}
#line 2764 "pikchr.c"
        break;
      case 59: /* boolproperty ::= THICK */
#line 694 "pikchr.y"
{p->cur->sw *= 1.5;}
#line 2769 "pikchr.c"
        break;
      case 60: /* boolproperty ::= THIN */
#line 695 "pikchr.y"
{p->cur->sw *= 0.67;}
#line 2774 "pikchr.c"
        break;
      case 61: /* boolproperty ::= SOLID */
#line 696 "pikchr.y"
{p->cur->sw = pik_value(p,"thickness",9,0);
                               p->cur->dotted = p->cur->dashed = 0.0;}
#line 2780 "pikchr.c"
        break;
      case 62: /* textposition ::= */
#line 699 "pikchr.y"
{yymsp[1].minor.yy272 = 0;}
#line 2785 "pikchr.c"
        break;
      case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */
#line 702 "pikchr.y"
{yylhsminor.yy272 = (short int)pik_text_position(yymsp[-1].minor.yy272,&yymsp[0].minor.yy0);}
#line 2790 "pikchr.c"
  yymsp[-1].minor.yy272 = yylhsminor.yy272;
        break;
      case 64: /* position ::= expr COMMA expr */
#line 705 "pikchr.y"
{yylhsminor.yy187.x=yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[0].minor.yy129;}
#line 2796 "pikchr.c"
  yymsp[-2].minor.yy187 = yylhsminor.yy187;
        break;
      case 65: /* position ::= place PLUS expr COMMA expr */
#line 707 "pikchr.y"
{yylhsminor.yy187.x=yymsp[-4].minor.yy187.x+yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[-4].minor.yy187.y+yymsp[0].minor.yy129;}
#line 2802 "pikchr.c"
  yymsp[-4].minor.yy187 = yylhsminor.yy187;
        break;
      case 66: /* position ::= place MINUS expr COMMA expr */
#line 708 "pikchr.y"
{yylhsminor.yy187.x=yymsp[-4].minor.yy187.x-yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[-4].minor.yy187.y-yymsp[0].minor.yy129;}
#line 2808 "pikchr.c"
  yymsp[-4].minor.yy187 = yylhsminor.yy187;
        break;
      case 67: /* position ::= place PLUS LP expr COMMA expr RP */
#line 710 "pikchr.y"
{yylhsminor.yy187.x=yymsp[-6].minor.yy187.x+yymsp[-3].minor.yy129; yylhsminor.yy187.y=yymsp[-6].minor.yy187.y+yymsp[-1].minor.yy129;}
#line 2814 "pikchr.c"
  yymsp[-6].minor.yy187 = yylhsminor.yy187;
        break;
      case 68: /* position ::= place MINUS LP expr COMMA expr RP */
#line 712 "pikchr.y"
{yylhsminor.yy187.x=yymsp[-6].minor.yy187.x-yymsp[-3].minor.yy129; yylhsminor.yy187.y=yymsp[-6].minor.yy187.y-yymsp[-1].minor.yy129;}
#line 2820 "pikchr.c"
  yymsp[-6].minor.yy187 = yylhsminor.yy187;
        break;
      case 69: /* position ::= LP position COMMA position RP */
#line 713 "pikchr.y"
{yymsp[-4].minor.yy187.x=yymsp[-3].minor.yy187.x; yymsp[-4].minor.yy187.y=yymsp[-1].minor.yy187.y;}
#line 2826 "pikchr.c"
        break;
      case 70: /* position ::= LP position RP */
#line 714 "pikchr.y"
{yymsp[-2].minor.yy187=yymsp[-1].minor.yy187;}
#line 2831 "pikchr.c"
        break;
      case 71: /* position ::= expr between position AND position */
#line 716 "pikchr.y"
{yylhsminor.yy187 = pik_position_between(yymsp[-4].minor.yy129,yymsp[-2].minor.yy187,yymsp[0].minor.yy187);}
#line 2836 "pikchr.c"
  yymsp[-4].minor.yy187 = yylhsminor.yy187;
        break;
      case 72: /* position ::= expr LT position COMMA position GT */
#line 718 "pikchr.y"
{yylhsminor.yy187 = pik_position_between(yymsp[-5].minor.yy129,yymsp[-3].minor.yy187,yymsp[-1].minor.yy187);}
#line 2842 "pikchr.c"
  yymsp[-5].minor.yy187 = yylhsminor.yy187;
        break;
      case 73: /* position ::= expr ABOVE position */
#line 719 "pikchr.y"
{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.y += yymsp[-2].minor.yy129;}
#line 2848 "pikchr.c"
  yymsp[-2].minor.yy187 = yylhsminor.yy187;
        break;
      case 74: /* position ::= expr BELOW position */
#line 720 "pikchr.y"
{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.y -= yymsp[-2].minor.yy129;}
#line 2854 "pikchr.c"
  yymsp[-2].minor.yy187 = yylhsminor.yy187;
        break;
      case 75: /* position ::= expr LEFT OF position */
#line 721 "pikchr.y"
{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.x -= yymsp[-3].minor.yy129;}
#line 2860 "pikchr.c"
  yymsp[-3].minor.yy187 = yylhsminor.yy187;
        break;
      case 76: /* position ::= expr RIGHT OF position */
#line 722 "pikchr.y"
{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.x += yymsp[-3].minor.yy129;}
#line 2866 "pikchr.c"
  yymsp[-3].minor.yy187 = yylhsminor.yy187;
        break;
      case 77: /* position ::= expr ON HEADING EDGEPT OF position */
#line 724 "pikchr.y"
{yylhsminor.yy187 = pik_position_at_hdg(yymsp[-5].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);}
#line 2872 "pikchr.c"
  yymsp[-5].minor.yy187 = yylhsminor.yy187;
        break;
      case 78: /* position ::= expr HEADING EDGEPT OF position */
#line 726 "pikchr.y"
{yylhsminor.yy187 = pik_position_at_hdg(yymsp[-4].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);}
#line 2878 "pikchr.c"
  yymsp[-4].minor.yy187 = yylhsminor.yy187;
        break;
      case 79: /* position ::= expr EDGEPT OF position */
#line 728 "pikchr.y"
{yylhsminor.yy187 = pik_position_at_hdg(yymsp[-3].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);}
#line 2884 "pikchr.c"
  yymsp[-3].minor.yy187 = yylhsminor.yy187;
        break;
      case 80: /* position ::= expr ON HEADING expr FROM position */
#line 730 "pikchr.y"
{yylhsminor.yy187 = pik_position_at_angle(yymsp[-5].minor.yy129,yymsp[-2].minor.yy129,yymsp[0].minor.yy187);}
#line 2890 "pikchr.c"
  yymsp[-5].minor.yy187 = yylhsminor.yy187;
        break;
      case 81: /* position ::= expr HEADING expr FROM position */
#line 732 "pikchr.y"
{yylhsminor.yy187 = pik_position_at_angle(yymsp[-4].minor.yy129,yymsp[-2].minor.yy129,yymsp[0].minor.yy187);}
#line 2896 "pikchr.c"
  yymsp[-4].minor.yy187 = yylhsminor.yy187;
        break;
      case 82: /* place ::= edge OF object */
#line 744 "pikchr.y"
{yylhsminor.yy187 = pik_place_of_elem(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);}
#line 2902 "pikchr.c"
  yymsp[-2].minor.yy187 = yylhsminor.yy187;
        break;
      case 83: /* place2 ::= object */
#line 745 "pikchr.y"
{yylhsminor.yy187 = pik_place_of_elem(p,yymsp[0].minor.yy54,0);}
#line 2908 "pikchr.c"
  yymsp[0].minor.yy187 = yylhsminor.yy187;
        break;
      case 84: /* place2 ::= object DOT_E edge */
#line 746 "pikchr.y"
{yylhsminor.yy187 = pik_place_of_elem(p,yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);}
#line 2914 "pikchr.c"
  yymsp[-2].minor.yy187 = yylhsminor.yy187;
        break;
      case 85: /* place2 ::= NTH VERTEX OF object */
#line 747 "pikchr.y"
{yylhsminor.yy187 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy54);}
#line 2920 "pikchr.c"
  yymsp[-3].minor.yy187 = yylhsminor.yy187;
        break;
      case 86: /* object ::= nth */
#line 759 "pikchr.y"
{yylhsminor.yy54 = pik_find_nth(p,0,&yymsp[0].minor.yy0);}
#line 2926 "pikchr.c"
  yymsp[0].minor.yy54 = yylhsminor.yy54;
        break;
      case 87: /* object ::= nth OF|IN object */
#line 760 "pikchr.y"
{yylhsminor.yy54 = pik_find_nth(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);}
#line 2932 "pikchr.c"
  yymsp[-2].minor.yy54 = yylhsminor.yy54;
        break;
      case 88: /* objectname ::= THIS */
#line 762 "pikchr.y"
{yymsp[0].minor.yy54 = p->cur;}
#line 2938 "pikchr.c"
        break;
      case 89: /* objectname ::= PLACENAME */
#line 763 "pikchr.y"
{yylhsminor.yy54 = pik_find_byname(p,0,&yymsp[0].minor.yy0);}
#line 2943 "pikchr.c"
  yymsp[0].minor.yy54 = yylhsminor.yy54;
        break;
      case 90: /* objectname ::= objectname DOT_U PLACENAME */
#line 765 "pikchr.y"
{yylhsminor.yy54 = pik_find_byname(p,yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);}
#line 2949 "pikchr.c"
  yymsp[-2].minor.yy54 = yylhsminor.yy54;
        break;
      case 91: /* nth ::= NTH CLASSNAME */
#line 767 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); }
#line 2955 "pikchr.c"
  yymsp[-1].minor.yy0 = yylhsminor.yy0;
        break;
      case 92: /* nth ::= NTH LAST CLASSNAME */
#line 768 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); }
#line 2961 "pikchr.c"
  yymsp[-2].minor.yy0 = yylhsminor.yy0;
        break;
      case 93: /* nth ::= LAST CLASSNAME */
#line 769 "pikchr.y"
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;}
#line 2967 "pikchr.c"
        break;
      case 94: /* nth ::= LAST */
#line 770 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;}
#line 2972 "pikchr.c"
  yymsp[0].minor.yy0 = yylhsminor.yy0;
        break;
      case 95: /* nth ::= NTH LB RB */
#line 771 "pikchr.y"
{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);}
#line 2978 "pikchr.c"
  yymsp[-2].minor.yy0 = yylhsminor.yy0;
        break;
      case 96: /* nth ::= NTH LAST LB RB */
#line 772 "pikchr.y"
{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);}
#line 2984 "pikchr.c"
  yymsp[-3].minor.yy0 = yylhsminor.yy0;
        break;
      case 97: /* nth ::= LAST LB RB */
#line 773 "pikchr.y"
{yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; }
#line 2990 "pikchr.c"
        break;
      case 98: /* expr ::= expr PLUS expr */
#line 775 "pikchr.y"
{yylhsminor.yy129=yymsp[-2].minor.yy129+yymsp[0].minor.yy129;}
#line 2995 "pikchr.c"
  yymsp[-2].minor.yy129 = yylhsminor.yy129;
        break;
      case 99: /* expr ::= expr MINUS expr */
#line 776 "pikchr.y"
{yylhsminor.yy129=yymsp[-2].minor.yy129-yymsp[0].minor.yy129;}
#line 3001 "pikchr.c"
  yymsp[-2].minor.yy129 = yylhsminor.yy129;
        break;
      case 100: /* expr ::= expr STAR expr */
#line 777 "pikchr.y"
{yylhsminor.yy129=yymsp[-2].minor.yy129*yymsp[0].minor.yy129;}
#line 3007 "pikchr.c"
  yymsp[-2].minor.yy129 = yylhsminor.yy129;
        break;
      case 101: /* expr ::= expr SLASH expr */
#line 778 "pikchr.y"
{
  if( yymsp[0].minor.yy129==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy129 = 0.0; }
  else{ yylhsminor.yy129 = yymsp[-2].minor.yy129/yymsp[0].minor.yy129; }
}
#line 3016 "pikchr.c"
  yymsp[-2].minor.yy129 = yylhsminor.yy129;
        break;
      case 102: /* expr ::= MINUS expr */
#line 782 "pikchr.y"
{yymsp[-1].minor.yy129=-yymsp[0].minor.yy129;}
#line 3022 "pikchr.c"
        break;
      case 103: /* expr ::= PLUS expr */
#line 783 "pikchr.y"
{yymsp[-1].minor.yy129=yymsp[0].minor.yy129;}
#line 3027 "pikchr.c"
        break;
      case 104: /* expr ::= LP expr RP */
#line 784 "pikchr.y"
{yymsp[-2].minor.yy129=yymsp[-1].minor.yy129;}
#line 3032 "pikchr.c"
        break;
      case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */
#line 785 "pikchr.y"
{yymsp[-2].minor.yy129=pik_get_var(p,&yymsp[-1].minor.yy0);}
#line 3037 "pikchr.c"
        break;
      case 106: /* expr ::= NUMBER */
#line 786 "pikchr.y"
{yylhsminor.yy129=pik_atof(&yymsp[0].minor.yy0);}
#line 3042 "pikchr.c"
  yymsp[0].minor.yy129 = yylhsminor.yy129;
        break;
      case 107: /* expr ::= ID */
#line 787 "pikchr.y"
{yylhsminor.yy129=pik_get_var(p,&yymsp[0].minor.yy0);}
#line 3048 "pikchr.c"
  yymsp[0].minor.yy129 = yylhsminor.yy129;
        break;
      case 108: /* expr ::= FUNC1 LP expr RP */
#line 788 "pikchr.y"
{yylhsminor.yy129 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy129,0.0);}
#line 3054 "pikchr.c"
  yymsp[-3].minor.yy129 = yylhsminor.yy129;
        break;
      case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */
#line 789 "pikchr.y"
{yylhsminor.yy129 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy129,yymsp[-1].minor.yy129);}
#line 3060 "pikchr.c"
  yymsp[-5].minor.yy129 = yylhsminor.yy129;
        break;
      case 110: /* expr ::= DIST LP position COMMA position RP */
#line 790 "pikchr.y"
{yymsp[-5].minor.yy129 = pik_dist(&yymsp[-3].minor.yy187,&yymsp[-1].minor.yy187);}
#line 3066 "pikchr.c"
        break;
      case 111: /* expr ::= place2 DOT_XY X */
#line 791 "pikchr.y"
{yylhsminor.yy129 = yymsp[-2].minor.yy187.x;}
#line 3071 "pikchr.c"
  yymsp[-2].minor.yy129 = yylhsminor.yy129;
        break;
      case 112: /* expr ::= place2 DOT_XY Y */
#line 792 "pikchr.y"
{yylhsminor.yy129 = yymsp[-2].minor.yy187.y;}
#line 3077 "pikchr.c"
  yymsp[-2].minor.yy129 = yylhsminor.yy129;
        break;
      case 113: /* expr ::= object DOT_L numproperty */
      case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114);
      case 115: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==115);
#line 793 "pikchr.y"
{yylhsminor.yy129=pik_property_of(yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);}
#line 3085 "pikchr.c"
  yymsp[-2].minor.yy129 = yylhsminor.yy129;
        break;
      default:
      /* (116) lvalue ::= ID */ yytestcase(yyruleno==116);
      /* (117) lvalue ::= FILL */ yytestcase(yyruleno==117);
      /* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118);
      /* (119) lvalue ::= THICKNESS */ yytestcase(yyruleno==119);
      /* (120) rvalue ::= expr */ yytestcase(yyruleno==120);
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
  int yymajor,                   /* The major type of the error token */
  pik_parserTOKENTYPE yyminor         /* The minor type of the error token */
){
  pik_parserARG_FETCH
  pik_parserCTX_FETCH
#define TOKEN yyminor
/************ Begin %syntax_error code ****************************************/
#line 537 "pikchr.y"

  if( TOKEN.z && TOKEN.z[0] ){
    pik_error(p, &TOKEN, "syntax error");
  }else{
    pik_error(p, 0, "syntax error");
  }
  UNUSED_PARAMETER(yymajor);
#line 3168 "pikchr.c"
/************ End %syntax_error code ******************************************/
  pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
  pik_parserCTX_STORE
}

/*
** The following is executed when the parser accepts







|







|







3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
  int yymajor,                   /* The major type of the error token */
  pik_parserTOKENTYPE yyminor         /* The minor type of the error token */
){
  pik_parserARG_FETCH
  pik_parserCTX_FETCH
#define TOKEN yyminor
/************ Begin %syntax_error code ****************************************/
#line 551 "pikchr.y"

  if( TOKEN.z && TOKEN.z[0] ){
    pik_error(p, &TOKEN, "syntax error");
  }else{
    pik_error(p, 0, "syntax error");
  }
  UNUSED_PARAMETER(yymajor);
#line 3196 "pikchr.c"
/************ End %syntax_error code ******************************************/
  pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
  pik_parserCTX_STORE
}

/*
** The following is executed when the parser accepts
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
  assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) );
  return yyFallback[iToken];
#else
  (void)iToken;
  return 0;
#endif
}
#line 782 "pikchr.y"



/* Chart of the 148 official CSS color names with their
** corresponding RGB values thru Color Module Level 4:
** https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
**







|







3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
  assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) );
  return yyFallback[iToken];
#else
  (void)iToken;
  return 0;
#endif
}
#line 798 "pikchr.y"



/* Chart of the 148 official CSS color names with their
** corresponding RGB values thru Color Module Level 4:
** https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
**
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656


3657
3658
3659
3660


3661










3662

3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
}
/* Hack: Arcs are here rendered as quadratic Bezier curves rather
** than true arcs.  Multiple reasons: (1) the legacy-PIC parameters
** that control arcs are obscure and I could not figure out what they
** mean based on available documentation.  (2) Arcs are rarely used,
** and so do not seem that important.
*/
static PPoint arcControlPoint(int cw, PPoint f, PPoint t, PNum rScale){
  PPoint m;
  PNum dx, dy;
  m.x = 0.5*(f.x+t.x);
  m.y = 0.5*(f.y+t.y);
  dx = t.x - f.x;
  dy = t.y - f.y;
  if( cw ){
    m.x -= 0.5*rScale*dy;
    m.y += 0.5*rScale*dx;
  }else{
    m.x += 0.5*rScale*dy;
    m.y -= 0.5*rScale*dx;
  }
  return m;
}
static void arcCheck(Pik *p, PObj *pObj){
  PPoint m;


  if( p->nTPath>2 ){
    pik_error(p, &pObj->errTok, "arc geometry error");
    return;
  }


  m = arcControlPoint(pObj->cw, p->aTPath[0], p->aTPath[1], 0.5);










  pik_bbox_add_xy(&pObj->bbox, m.x, m.y);

}
static void arcRender(Pik *p, PObj *pObj){
  PPoint f, m, t;
  if( pObj->nPath<2 ) return;
  if( pObj->sw<0.0 ) return;
  f = pObj->aPath[0];
  t = pObj->aPath[1];
  m = arcControlPoint(pObj->cw,f,t,1.0);
  if( pObj->larrow ){
    pik_draw_arrowhead(p,&m,&f,pObj);
  }
  if( pObj->rarrow ){
    pik_draw_arrowhead(p,&m,&t,pObj);
  }
  pik_append_xy(p,"<path d=\"M", f.x, f.y);







|







|
|

|
|




|
>
>




>
>
|
>
>
>
>
>
>
>
>
>
>
|
>







|







3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
}
/* Hack: Arcs are here rendered as quadratic Bezier curves rather
** than true arcs.  Multiple reasons: (1) the legacy-PIC parameters
** that control arcs are obscure and I could not figure out what they
** mean based on available documentation.  (2) Arcs are rarely used,
** and so do not seem that important.
*/
static PPoint arcControlPoint(int cw, PPoint f, PPoint t){
  PPoint m;
  PNum dx, dy;
  m.x = 0.5*(f.x+t.x);
  m.y = 0.5*(f.y+t.y);
  dx = t.x - f.x;
  dy = t.y - f.y;
  if( cw ){
    m.x -= 0.5*dy;
    m.y += 0.5*dx;
  }else{
    m.x += 0.5*dy;
    m.y -= 0.5*dx;
  }
  return m;
}
static void arcCheck(Pik *p, PObj *pObj){
  PPoint f, m, t;
  PNum sw;
  int i;
  if( p->nTPath>2 ){
    pik_error(p, &pObj->errTok, "arc geometry error");
    return;
  }
  f = p->aTPath[0];
  t = p->aTPath[1];
  m = arcControlPoint(pObj->cw, f, t);
  sw = pObj->sw;
  for(i=1; i<16; i++){
    PNum t1, t2, a, b, c, x, y;
    t1 = 0.0625*i;
    t2 = 1.0 - t1;
    a = t2*t2;
    b = 2*t1*t2;
    c = t1*t1;
    x = a*f.x + b*m.x + c*t.x;
    y = a*f.y + b*m.y + c*t.y;
    pik_bbox_addellipse(&pObj->bbox, x, y, sw, sw);
  }
}
static void arcRender(Pik *p, PObj *pObj){
  PPoint f, m, t;
  if( pObj->nPath<2 ) return;
  if( pObj->sw<0.0 ) return;
  f = pObj->aPath[0];
  t = pObj->aPath[1];
  m = arcControlPoint(pObj->cw,f,t);
  if( pObj->larrow ){
    pik_draw_arrowhead(p,&m,&f,pObj);
  }
  if( pObj->rarrow ){
    pik_draw_arrowhead(p,&m,&t,pObj);
  }
  pik_append_xy(p,"<path d=\"M", f.x, f.y);
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
  pObj->sw = 0.0;
}
static PPoint textOffset(Pik *p, PObj *pObj, int cp){
  /* Automatically slim-down the width and height of text
  ** statements so that the bounding box tightly encloses the text,
  ** then get boxOffset() to do the offset computation.
  */
  pik_size_to_fit(p, &pObj->errTok,3);
  return boxOffset(p, pObj, cp);
}
static void textRender(Pik *p, PObj *pObj){
  pik_append_txt(p, pObj, 0);
}









|







4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
  pObj->sw = 0.0;
}
static PPoint textOffset(Pik *p, PObj *pObj, int cp){
  /* Automatically slim-down the width and height of text
  ** statements so that the bounding box tightly encloses the text,
  ** then get boxOffset() to do the offset computation.
  */
  pik_size_to_fit(p, pObj, &pObj->errTok,3);
  return boxOffset(p, pObj, cp);
}
static void textRender(Pik *p, PObj *pObj){
  pik_append_txt(p, pObj, 0);
}


6345
6346
6347
6348
6349
6350
6351
6352
6353
6354
6355
6356
6357
6358
6359
6360
6361
6362
6363
6364
**
** The eWhich parameter is:
**
**    1:   Fit horizontally only
**    2:   Fit vertically only
**    3:   Fit both ways
*/
static void pik_size_to_fit(Pik *p, PToken *pFit, int eWhich){
  PObj *pObj;
  PNum w, h;
  PBox bbox;
  if( p->nErr ) return;
  pObj = p->cur;

  if( pObj->nTxt==0 ){
    pik_error(0, pFit, "no text to fit to");
    return;
  }
  if( pObj->type->xFit==0 ) return;
  pik_bbox_init(&bbox);







|
<



|







6413
6414
6415
6416
6417
6418
6419
6420

6421
6422
6423
6424
6425
6426
6427
6428
6429
6430
6431
**
** The eWhich parameter is:
**
**    1:   Fit horizontally only
**    2:   Fit vertically only
**    3:   Fit both ways
*/
static void pik_size_to_fit(Pik *p, PObj *pObj, PToken *pFit, int eWhich){

  PNum w, h;
  PBox bbox;
  if( p->nErr ) return;
  if( pObj==0 ) pObj = p->cur;

  if( pObj->nTxt==0 ){
    pik_error(0, pFit, "no text to fit to");
    return;
  }
  if( pObj->type->xFit==0 ) return;
  pik_bbox_init(&bbox);
6493
6494
6495
6496
6497
6498
6499
6500
6501
6502
6503
6504
6505
6506
6507
6508
6509
    const char *zClr;
    int c1, c2;
    unsigned int i;
    mid = (first+last)/2;
    zClr = aColor[mid].zName;
    for(i=0; i<pId->n; i++){
      c1 = zClr[i]&0x7f;
      if( isupper(c1) ) c1 = tolower(c1);
      c2 = pId->z[i]&0x7f;
      if( isupper(c2) ) c2 = tolower(c2);
      c = c2 - c1;
      if( c ) break;
    }
    if( c==0 && aColor[mid].zName[pId->n] ) c = -1;
    if( c==0 ) return (double)aColor[mid].val;
    if( c>0 ){
      first = mid+1;







|

|







6560
6561
6562
6563
6564
6565
6566
6567
6568
6569
6570
6571
6572
6573
6574
6575
6576
    const char *zClr;
    int c1, c2;
    unsigned int i;
    mid = (first+last)/2;
    zClr = aColor[mid].zName;
    for(i=0; i<pId->n; i++){
      c1 = zClr[i]&0x7f;
      if( IsUpper(c1) ) c1 = ToLower(c1);
      c2 = pId->z[i]&0x7f;
      if( IsUpper(c2) ) c2 = ToLower(c2);
      c = c2 - c1;
      if( c ) break;
    }
    if( c==0 && aColor[mid].zName[pId->n] ) c = -1;
    if( c==0 ) return (double)aColor[mid].val;
    if( c>0 ){
      first = mid+1;
6894
6895
6896
6897
6898
6899
6900
6901
6902
6903
6904
6905
6906
6907
6908
6909
6910
6911
6912
6913
6914
6915
6916
6917
    /* A height or width less than or equal to zero means "autofit".
    ** Change the height or width to be big enough to contain the text,
    */
    if( pObj->h<=0.0 ){
      if( pObj->nTxt==0 ){
        pObj->h = 0.0;
      }else if( pObj->w<=0.0 ){
        pik_size_to_fit(p, &pObj->errTok, 3);
      }else{
        pik_size_to_fit(p, &pObj->errTok, 2);
      }
    }
    if( pObj->w<=0.0 ){
      if( pObj->nTxt==0 ){
        pObj->w = 0.0;
      }else{
        pik_size_to_fit(p, &pObj->errTok, 1);
      }
    }
    ofst = pik_elem_offset(p, pObj, pObj->eWith);
    dx = (pObj->with.x - ofst.x) - pObj->ptAt.x;
    dy = (pObj->with.y - ofst.y) - pObj->ptAt.y;
    if( dx!=0 || dy!=0 ){
      pik_elem_move(pObj, dx, dy);







|

|






|







6961
6962
6963
6964
6965
6966
6967
6968
6969
6970
6971
6972
6973
6974
6975
6976
6977
6978
6979
6980
6981
6982
6983
6984
    /* A height or width less than or equal to zero means "autofit".
    ** Change the height or width to be big enough to contain the text,
    */
    if( pObj->h<=0.0 ){
      if( pObj->nTxt==0 ){
        pObj->h = 0.0;
      }else if( pObj->w<=0.0 ){
        pik_size_to_fit(p, pObj, &pObj->errTok, 3);
      }else{
        pik_size_to_fit(p, pObj, &pObj->errTok, 2);
      }
    }
    if( pObj->w<=0.0 ){
      if( pObj->nTxt==0 ){
        pObj->w = 0.0;
      }else{
        pik_size_to_fit(p, pObj, &pObj->errTok, 1);
      }
    }
    ofst = pik_elem_offset(p, pObj, pObj->eWith);
    dx = (pObj->with.x - ofst.x) - pObj->ptAt.x;
    dy = (pObj->with.y - ofst.y) - pObj->ptAt.y;
    if( dx!=0 || dy!=0 ){
      pik_elem_move(pObj, dx, dy);
7233
7234
7235
7236
7237
7238
7239
7240

7241
7242
7243
7244
7245
7246
7247
      p->wSVG = pik_round(p->wSVG*pikScale);
      p->hSVG = pik_round(p->hSVG*pikScale);
      pik_append_num(p, " width=\"", p->wSVG);
      pik_append_num(p, "\" height=\"", p->hSVG);
      pik_append(p, "\"", 1);
    }
    pik_append_dis(p, " viewBox=\"0 0 ",w,"");
    pik_append_dis(p, " ",h,"\">\n");

    pik_elist_render(p, pList);
    pik_append(p,"</svg>\n", -1);
  }else{
    p->wSVG = -1;
    p->hSVG = -1;
  }
  pik_elist_free(p, pList);







|
>







7300
7301
7302
7303
7304
7305
7306
7307
7308
7309
7310
7311
7312
7313
7314
7315
      p->wSVG = pik_round(p->wSVG*pikScale);
      p->hSVG = pik_round(p->hSVG*pikScale);
      pik_append_num(p, " width=\"", p->wSVG);
      pik_append_num(p, "\" height=\"", p->hSVG);
      pik_append(p, "\"", 1);
    }
    pik_append_dis(p, " viewBox=\"0 0 ",w,"");
    pik_append_dis(p, " ",h,"\"");
    pik_append(p, " data-pikchr-date=\"" MANIFEST_ISODATE "\">\n", -1);
    pik_elist_render(p, pList);
    pik_append(p,"</svg>\n", -1);
  }else{
    p->wSVG = -1;
    p->hSVG = -1;
  }
  pik_elist_free(p, pList);
7317
7318
7319
7320
7321
7322
7323

7324
7325
7326
7327
7328
7329
7330
  { "mono",       4,   T_MONO,      0,         0        },
  { "monospace",  9,   T_MONO,      0,         0        },
  { "n",          1,   T_EDGEPT,    0,         CP_N     },
  { "ne",         2,   T_EDGEPT,    0,         CP_NE    },
  { "north",      5,   T_EDGEPT,    0,         CP_N     },
  { "nw",         2,   T_EDGEPT,    0,         CP_NW    },
  { "of",         2,   T_OF,        0,         0        },

  { "previous",   8,   T_LAST,      0,         0,       },
  { "print",      5,   T_PRINT,     0,         0        },
  { "rad",        3,   T_RADIUS,    0,         0        },
  { "radius",     6,   T_RADIUS,    0,         0        },
  { "right",      5,   T_RIGHT,     DIR_RIGHT, CP_E     },
  { "rjust",      5,   T_RJUST,     0,         0        },
  { "s",          1,   T_EDGEPT,    0,         CP_S     },







>







7385
7386
7387
7388
7389
7390
7391
7392
7393
7394
7395
7396
7397
7398
7399
  { "mono",       4,   T_MONO,      0,         0        },
  { "monospace",  9,   T_MONO,      0,         0        },
  { "n",          1,   T_EDGEPT,    0,         CP_N     },
  { "ne",         2,   T_EDGEPT,    0,         CP_NE    },
  { "north",      5,   T_EDGEPT,    0,         CP_N     },
  { "nw",         2,   T_EDGEPT,    0,         CP_NW    },
  { "of",         2,   T_OF,        0,         0        },
  { "pikchr_date",11,  T_ISODATE,   0,         0,       },
  { "previous",   8,   T_LAST,      0,         0,       },
  { "print",      5,   T_PRINT,     0,         0        },
  { "rad",        3,   T_RADIUS,    0,         0        },
  { "radius",     6,   T_RADIUS,    0,         0        },
  { "right",      5,   T_RIGHT,     DIR_RIGHT, CP_E     },
  { "rjust",      5,   T_RJUST,     0,         0        },
  { "s",          1,   T_EDGEPT,    0,         CP_S     },
7603
7604
7605
7606
7607
7608
7609
7610
7611
7612
7613
7614
7615
7616
7617
7618
7619
7620
7621
7622
7623
7624
7625
7626
7627
7628
7629
7630
7631
7632
7633
7634
7635
7636
7637
7638
7639
7640
7641
7642
7643
7644
7645
7646
7647
7648
7649
7650
7651
7652
7653
7654
7655
7656
      pToken->eType = T_ERROR;
      return 1;
    }
    default: {
      c = z[0];
      if( c=='.' ){
        unsigned char c1 = z[1];
        if( islower(c1) ){
          const PikWord *pFound;
          for(i=2; (c = z[i])>='a' && c<='z'; i++){}
          pFound = pik_find_word((const char*)z+1, i-1,
                                    pik_keywords, count(pik_keywords));
          if( pFound && (pFound->eEdge>0 ||
                         pFound->eType==T_EDGEPT ||
                         pFound->eType==T_START ||
                         pFound->eType==T_END )
          ){
            /* Dot followed by something that is a 2-D place value */
            pToken->eType = T_DOT_E;
          }else if( pFound && (pFound->eType==T_X || pFound->eType==T_Y) ){
            /* Dot followed by "x" or "y" */
            pToken->eType = T_DOT_XY;
          }else{
            /* Any other "dot" */
            pToken->eType = T_DOT_L;
          }
          return 1;
        }else if( isdigit(c1) ){
          i = 0;
          /* no-op.  Fall through to number handling */
        }else if( isupper(c1) ){
          for(i=2; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){}
          pToken->eType = T_DOT_U;
          return 1;
        }else{
          pToken->eType = T_ERROR;
          return 1;
        }
      }
      if( (c>='0' && c<='9') || c=='.' ){
        int nDigit;
        int isInt = 1;
        if( c!='.' ){
          nDigit = 1;
          for(i=1; (c = z[i])>='0' && c<='9'; i++){ nDigit++; }
          if( i==1 && (c=='x' || c=='X') ){
            for(i=2; (c = z[i])!=0 && isxdigit(c); i++){}
            pToken->eType = T_NUMBER;
            return i;
          }
        }else{
          isInt = 0;
          nDigit = 0;
          i = 0;







|



















|


|
|














|







7672
7673
7674
7675
7676
7677
7678
7679
7680
7681
7682
7683
7684
7685
7686
7687
7688
7689
7690
7691
7692
7693
7694
7695
7696
7697
7698
7699
7700
7701
7702
7703
7704
7705
7706
7707
7708
7709
7710
7711
7712
7713
7714
7715
7716
7717
7718
7719
7720
7721
7722
7723
7724
7725
      pToken->eType = T_ERROR;
      return 1;
    }
    default: {
      c = z[0];
      if( c=='.' ){
        unsigned char c1 = z[1];
        if( IsLower(c1) ){
          const PikWord *pFound;
          for(i=2; (c = z[i])>='a' && c<='z'; i++){}
          pFound = pik_find_word((const char*)z+1, i-1,
                                    pik_keywords, count(pik_keywords));
          if( pFound && (pFound->eEdge>0 ||
                         pFound->eType==T_EDGEPT ||
                         pFound->eType==T_START ||
                         pFound->eType==T_END )
          ){
            /* Dot followed by something that is a 2-D place value */
            pToken->eType = T_DOT_E;
          }else if( pFound && (pFound->eType==T_X || pFound->eType==T_Y) ){
            /* Dot followed by "x" or "y" */
            pToken->eType = T_DOT_XY;
          }else{
            /* Any other "dot" */
            pToken->eType = T_DOT_L;
          }
          return 1;
        }else if( IsDigit(c1) ){
          i = 0;
          /* no-op.  Fall through to number handling */
        }else if( IsUpper(c1) ){
          for(i=2; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){}
          pToken->eType = T_DOT_U;
          return 1;
        }else{
          pToken->eType = T_ERROR;
          return 1;
        }
      }
      if( (c>='0' && c<='9') || c=='.' ){
        int nDigit;
        int isInt = 1;
        if( c!='.' ){
          nDigit = 1;
          for(i=1; (c = z[i])>='0' && c<='9'; i++){ nDigit++; }
          if( i==1 && (c=='x' || c=='X') ){
            for(i=2; (c = z[i])!=0 && IsXDigit(c); i++){}
            pToken->eType = T_NUMBER;
            return i;
          }
        }else{
          isInt = 0;
          nDigit = 0;
          i = 0;
7698
7699
7700
7701
7702
7703
7704
7705
7706
7707
7708
7709
7710
7711
7712
7713
7714
7715
7716
7717
7718
7719
7720
7721
7722
7723
7724
7725
7726
7727
7728
7729
7730
7731
7732
7733
7734
7735
7736
7737
7738
7739
         || (c=='p' && c2=='x')
         || (c=='p' && c2=='c')
        ){
          i += 2;
        }
        pToken->eType = T_NUMBER;
        return i;
      }else if( islower(c) ){
        const PikWord *pFound;
        for(i=1; (c =  z[i])!=0 && (isalnum(c) || c=='_'); i++){}
        pFound = pik_find_word((const char*)z, i,
                               pik_keywords, count(pik_keywords));
        if( pFound ){
          pToken->eType = pFound->eType;
          pToken->eCode = pFound->eCode;
          pToken->eEdge = pFound->eEdge;
          return i;
        }
        pToken->n = i;
        if( pik_find_class(pToken)!=0 ){
          pToken->eType = T_CLASSNAME;
        }else{
          pToken->eType = T_ID;
        }
        return i;
      }else if( c>='A' && c<='Z' ){
        for(i=1; (c =  z[i])!=0 && (isalnum(c) || c=='_'); i++){}
        pToken->eType = T_PLACENAME;
        return i;
      }else if( c=='$' && z[1]>='1' && z[1]<='9' && !isdigit(z[2]) ){
        pToken->eType = T_PARAMETER;
        pToken->eCode = z[1] - '1';
        return 2;
      }else if( c=='_' || c=='$' || c=='@' ){
        for(i=1; (c =  z[i])!=0 && (isalnum(c) || c=='_'); i++){}
        pToken->eType = T_ID;
        return i;
      }else{
        pToken->eType = T_ERROR;
        return 1;
      }
    }







|

|
















|


|




|







7767
7768
7769
7770
7771
7772
7773
7774
7775
7776
7777
7778
7779
7780
7781
7782
7783
7784
7785
7786
7787
7788
7789
7790
7791
7792
7793
7794
7795
7796
7797
7798
7799
7800
7801
7802
7803
7804
7805
7806
7807
7808
         || (c=='p' && c2=='x')
         || (c=='p' && c2=='c')
        ){
          i += 2;
        }
        pToken->eType = T_NUMBER;
        return i;
      }else if( IsLower(c) ){
        const PikWord *pFound;
        for(i=1; (c =  z[i])!=0 && (IsAlnum(c) || c=='_'); i++){}
        pFound = pik_find_word((const char*)z, i,
                               pik_keywords, count(pik_keywords));
        if( pFound ){
          pToken->eType = pFound->eType;
          pToken->eCode = pFound->eCode;
          pToken->eEdge = pFound->eEdge;
          return i;
        }
        pToken->n = i;
        if( pik_find_class(pToken)!=0 ){
          pToken->eType = T_CLASSNAME;
        }else{
          pToken->eType = T_ID;
        }
        return i;
      }else if( c>='A' && c<='Z' ){
        for(i=1; (c =  z[i])!=0 && (IsAlnum(c) || c=='_'); i++){}
        pToken->eType = T_PLACENAME;
        return i;
      }else if( c=='$' && z[1]>='1' && z[1]<='9' && !IsDigit(z[2]) ){
        pToken->eType = T_PARAMETER;
        pToken->eCode = z[1] - '1';
        return 2;
      }else if( c=='_' || c=='$' || c=='@' ){
        for(i=1; (c =  z[i])!=0 && (IsAlnum(c) || c=='_'); i++){}
        pToken->eType = T_ID;
        return i;
      }else{
        pToken->eType = T_ERROR;
        return 1;
      }
    }
7812
7813
7814
7815
7816
7817
7818
7819
7820
7821
7822
7823
7824
7825
7826
7827
  if( z[i]==')' ){
    args[nArg].n = i - iStart;
    /* Remove leading and trailing whitespace from each argument.
    ** If what remains is one of $1, $2, ... $9 then transfer the
    ** corresponding argument from the outer context */
    for(j=0; j<=nArg; j++){
      PToken *t = &args[j];
      while( t->n>0 && isspace(t->z[0]) ){ t->n--; t->z++; }
      while( t->n>0 && isspace(t->z[t->n-1]) ){ t->n--; }
      if( t->n==2 && t->z[0]=='$' && t->z[1]>='1' && t->z[1]<='9' ){
        if( pOuter ) *t = pOuter[t->z[1]-'1'];
        else t->n = 0;
      }
    }
    return i+1;
  }







|
|







7881
7882
7883
7884
7885
7886
7887
7888
7889
7890
7891
7892
7893
7894
7895
7896
  if( z[i]==')' ){
    args[nArg].n = i - iStart;
    /* Remove leading and trailing whitespace from each argument.
    ** If what remains is one of $1, $2, ... $9 then transfer the
    ** corresponding argument from the outer context */
    for(j=0; j<=nArg; j++){
      PToken *t = &args[j];
      while( t->n>0 && IsSpace(t->z[0]) ){ t->n--; t->z++; }
      while( t->n>0 && IsSpace(t->z[t->n-1]) ){ t->n--; }
      if( t->n==2 && t->z[0]=='$' && t->z[1]>='1' && t->z[1]<='9' ){
        if( pOuter ) *t = pOuter[t->z[1]-'1'];
        else t->n = 0;
      }
    }
    return i+1;
  }
7894
7895
7896
7897
7898
7899
7900
7901
7902
7903
7904
7905
7906





7907
7908
7909
7910
7911










7912
7913
7914
7915
7916
7917
7918
      pik_tokenize(p, &pMac->macroBody, pParser, args);
      p->nCtx--;
      pMac->inUse = 0;
    }else{
#if 0
      printf("******** Token %s (%d): \"%.*s\" **************\n",
             yyTokenName[token.eType], token.eType,
             (int)(isspace(token.z[0]) ? 0 : sz), token.z);
#endif
      token.n = (unsigned short)(sz & 0xffff);
      if( p->nToken++ > PIKCHR_TOKEN_LIMIT ){
        pik_error(p, &token, "script is too complex");
        break;





      }
      pik_parser(pParser, token.eType, token);
    }
  }
}











/*
** Parse the PIKCHR script contained in zText[].  Return a rendering.  Or
** if an error is encountered, return the error text.  The error message
** is HTML formatted.  So regardless of what happens, the return text
** is safe to be insertd into an HTML output stream.
**







|





>
>
>
>
>





>
>
>
>
>
>
>
>
>
>







7963
7964
7965
7966
7967
7968
7969
7970
7971
7972
7973
7974
7975
7976
7977
7978
7979
7980
7981
7982
7983
7984
7985
7986
7987
7988
7989
7990
7991
7992
7993
7994
7995
7996
7997
7998
7999
8000
8001
8002
      pik_tokenize(p, &pMac->macroBody, pParser, args);
      p->nCtx--;
      pMac->inUse = 0;
    }else{
#if 0
      printf("******** Token %s (%d): \"%.*s\" **************\n",
             yyTokenName[token.eType], token.eType,
             (int)(IsSpace(token.z[0]) ? 0 : sz), token.z);
#endif
      token.n = (unsigned short)(sz & 0xffff);
      if( p->nToken++ > PIKCHR_TOKEN_LIMIT ){
        pik_error(p, &token, "script is too complex");
        break;
      }
      if( token.eType==T_ISODATE ){
        token.z = "\"" MANIFEST_ISODATE "\"";
        token.n = sizeof(MANIFEST_ISODATE)+1;
        token.eType = T_STRING;
      }
      pik_parser(pParser, token.eType, token);
    }
  }
}

/*
** Return the version name.
*/
const char *pikchr_version(void)
  /* Emscripten workaround, else it chokes on the inlined version */;

const char *pikchr_version(void){
  return RELEASE_VERSION " " MANIFEST_ISODATE;
}

/*
** Parse the PIKCHR script contained in zText[].  Return a rendering.  Or
** if an error is encountered, return the error text.  The error message
** is HTML formatted.  So regardless of what happens, the return text
** is safe to be insertd into an HTML output stream.
**
8128
8129
8130
8131
8132
8133
8134




8135
8136
8137
8138
8139
8140
8141
        if( zHtmlHdr==0 ){
          fprintf(stderr, "the \"%s\" option must come first\n",argv[i]);
          exit(1);
        }
        bSvgOnly = 1;
        mFlags |= PIKCHR_PLAINTEXT_ERRORS;
      }else




      {
        fprintf(stderr,"unknown option: \"%s\"\n", argv[i]);
        usage(argv[0]);
      }
      continue;
    }
    zIn = readFile(argv[i]);







>
>
>
>







8212
8213
8214
8215
8216
8217
8218
8219
8220
8221
8222
8223
8224
8225
8226
8227
8228
8229
        if( zHtmlHdr==0 ){
          fprintf(stderr, "the \"%s\" option must come first\n",argv[i]);
          exit(1);
        }
        bSvgOnly = 1;
        mFlags |= PIKCHR_PLAINTEXT_ERRORS;
      }else
      if( strcmp(z,"version")==0 || strcmp(z,"v")==0 ){
        printf("pikchr %s\n", pikchr_version());
        return 0;
      }else
      {
        fprintf(stderr,"unknown option: \"%s\"\n", argv[i]);
        usage(argv[0]);
      }
      continue;
    }
    zIn = readFile(argv[i]);
8240
8241
8242
8243
8244
8245
8246
8247
  return TCL_OK;
}


#endif /* PIKCHR_TCL */


#line 8272 "pikchr.c"







|
8328
8329
8330
8331
8332
8333
8334
8335
  return TCL_OK;
}


#endif /* PIKCHR_TCL */


#line 8335 "pikchr.c"
Changes to extsrc/pikchr.js.
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

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
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

var initPikchrModule = (() => {
  var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
  
  return (
function(config) {
  var initPikchrModule = config || {};















var Module = typeof initPikchrModule != "undefined" ? initPikchrModule : {};


var readyPromiseResolve, readyPromiseReject;

Module["ready"] = new Promise(function(resolve, reject) {
 readyPromiseResolve = resolve;
 readyPromiseReject = reject;
});














var moduleOverrides = Object.assign({}, Module);

var arguments_ = [];

var thisProgram = "./this.program";

var quit_ = (status, toThrow) => {
 throw toThrow;
};

var ENVIRONMENT_IS_WEB = true;

var ENVIRONMENT_IS_WORKER = false;

var scriptDirectory = "";

function locateFile(path) {
 if (Module["locateFile"]) {
  return Module["locateFile"](path, scriptDirectory);
 }
 return scriptDirectory + path;
}


var read_, readAsync, readBinary, setWindowTitle;




if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
 if (ENVIRONMENT_IS_WORKER) {

  scriptDirectory = self.location.href;
 } else if (typeof document != "undefined" && document.currentScript) {

  scriptDirectory = document.currentScript.src;
 }


 if (_scriptDir) {
  scriptDirectory = _scriptDir;
 }






 if (scriptDirectory.indexOf("blob:") !== 0) {
  scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf("/") + 1);
 } else {
  scriptDirectory = "";
 }
 {
  read_ = url => {
   var xhr = new XMLHttpRequest();
   xhr.open("GET", url, false);
   xhr.send(null);
   return xhr.responseText;
  };
  if (ENVIRONMENT_IS_WORKER) {
   readBinary = url => {
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url, false);
    xhr.responseType = "arraybuffer";
    xhr.send(null);
    return new Uint8Array(xhr.response);
   };
  }
  readAsync = (url, onload, onerror) => {
   var xhr = new XMLHttpRequest();
   xhr.open("GET", url, true);
   xhr.responseType = "arraybuffer";
   xhr.onload = () => {
    if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
     onload(xhr.response);
     return;
    }
    onerror();

   };
   xhr.onerror = onerror;
   xhr.send(null);
  };
 }
 setWindowTitle = title => document.title = title;

} else {}

var out = Module["print"] || console.log.bind(console);

var err = Module["printErr"] || console.warn.bind(console);


Object.assign(Module, moduleOverrides);



moduleOverrides = null;





if (Module["arguments"]) arguments_ = Module["arguments"];

if (Module["thisProgram"]) thisProgram = Module["thisProgram"];

if (Module["quit"]) quit_ = Module["quit"];

var wasmBinary;

if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"];

var noExitRuntime = Module["noExitRuntime"] || true;

if (typeof WebAssembly != "object") {


 abort("no native wasm support detected");
}

var wasmMemory;






var ABORT = false;




var EXITSTATUS;

var UTF8Decoder = typeof TextDecoder != "undefined" ? new TextDecoder("utf8") : undefined;

function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) {
 var endIdx = idx + maxBytesToRead;
 var endPtr = idx;
 while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr;
 if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) {
  return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr));
 }
 var str = "";
 while (idx < endPtr) {
  var u0 = heapOrArray[idx++];
  if (!(u0 & 128)) {
   str += String.fromCharCode(u0);
   continue;
  }
  var u1 = heapOrArray[idx++] & 63;
  if ((u0 & 224) == 192) {
   str += String.fromCharCode((u0 & 31) << 6 | u1);
   continue;
  }
  var u2 = heapOrArray[idx++] & 63;
  if ((u0 & 240) == 224) {
   u0 = (u0 & 15) << 12 | u1 << 6 | u2;
  } else {
   u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | heapOrArray[idx++] & 63;
  }
  if (u0 < 65536) {
   str += String.fromCharCode(u0);
  } else {
   var ch = u0 - 65536;
   str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023);
  }
 }
 return str;
}

function UTF8ToString(ptr, maxBytesToRead) {
 return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : "";
}

function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) {
 if (!(maxBytesToWrite > 0)) return 0;
 var startIdx = outIdx;
 var endIdx = outIdx + maxBytesToWrite - 1;
 for (var i = 0; i < str.length; ++i) {
  var u = str.charCodeAt(i);
  if (u >= 55296 && u <= 57343) {
   var u1 = str.charCodeAt(++i);
   u = 65536 + ((u & 1023) << 10) | u1 & 1023;
  }
  if (u <= 127) {
   if (outIdx >= endIdx) break;
   heap[outIdx++] = u;
  } else if (u <= 2047) {
   if (outIdx + 1 >= endIdx) break;
   heap[outIdx++] = 192 | u >> 6;
   heap[outIdx++] = 128 | u & 63;
  } else if (u <= 65535) {
   if (outIdx + 2 >= endIdx) break;
   heap[outIdx++] = 224 | u >> 12;
   heap[outIdx++] = 128 | u >> 6 & 63;
   heap[outIdx++] = 128 | u & 63;
  } else {
   if (outIdx + 3 >= endIdx) break;
   heap[outIdx++] = 240 | u >> 18;
   heap[outIdx++] = 128 | u >> 12 & 63;
   heap[outIdx++] = 128 | u >> 6 & 63;
   heap[outIdx++] = 128 | u & 63;
  }
 }
 heap[outIdx] = 0;
 return outIdx - startIdx;
}

function stringToUTF8(str, outPtr, maxBytesToWrite) {
 return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite);
}

var HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64;

function updateMemoryViews() {
 var b = wasmMemory.buffer;
 Module["HEAP8"] = HEAP8 = new Int8Array(b);
 Module["HEAP16"] = HEAP16 = new Int16Array(b);
 Module["HEAP32"] = HEAP32 = new Int32Array(b);
 Module["HEAPU8"] = HEAPU8 = new Uint8Array(b);
 Module["HEAPU16"] = HEAPU16 = new Uint16Array(b);

 Module["HEAPU32"] = HEAPU32 = new Uint32Array(b);
 Module["HEAPF32"] = HEAPF32 = new Float32Array(b);
 Module["HEAPF64"] = HEAPF64 = new Float64Array(b);
}

var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 16777216;

var wasmTable;

var __ATPRERUN__ = [];


var __ATINIT__ = [];


var __ATPOSTRUN__ = [];


var runtimeInitialized = false;

function keepRuntimeAlive() {
 return noExitRuntime;
}

function preRun() {
 if (Module["preRun"]) {
  if (typeof Module["preRun"] == "function") Module["preRun"] = [ Module["preRun"] ];
  while (Module["preRun"].length) {

   addOnPreRun(Module["preRun"].shift());
  }
 }
 callRuntimeCallbacks(__ATPRERUN__);
}

function initRuntime() {
 runtimeInitialized = true;
 callRuntimeCallbacks(__ATINIT__);
}

function postRun() {
 if (Module["postRun"]) {
  if (typeof Module["postRun"] == "function") Module["postRun"] = [ Module["postRun"] ];
  while (Module["postRun"].length) {

   addOnPostRun(Module["postRun"].shift());
  }
 }
 callRuntimeCallbacks(__ATPOSTRUN__);
}

function addOnPreRun(cb) {
 __ATPRERUN__.unshift(cb);
}

function addOnInit(cb) {
 __ATINIT__.unshift(cb);
}

function addOnPostRun(cb) {
 __ATPOSTRUN__.unshift(cb);
}














var runDependencies = 0;

var runDependencyWatcher = null;

var dependenciesFulfilled = null;

function addRunDependency(id) {
 runDependencies++;
 if (Module["monitorRunDependencies"]) {
  Module["monitorRunDependencies"](runDependencies);
 }
}

function removeRunDependency(id) {
 runDependencies--;
 if (Module["monitorRunDependencies"]) {
  Module["monitorRunDependencies"](runDependencies);
 }
 if (runDependencies == 0) {
  if (runDependencyWatcher !== null) {
   clearInterval(runDependencyWatcher);
   runDependencyWatcher = null;
  }
  if (dependenciesFulfilled) {
   var callback = dependenciesFulfilled;
   dependenciesFulfilled = null;
   callback();
  }
 }
}

function abort(what) {
 if (Module["onAbort"]) {
  Module["onAbort"](what);
 }
 what = "Aborted(" + what + ")";


 err(what);
 ABORT = true;
 EXITSTATUS = 1;
 what += ". Build with -sASSERTIONS for more info.";












 var e = new WebAssembly.RuntimeError(what);
 readyPromiseReject(e);



 throw e;
}





var dataURIPrefix = "data:application/octet-stream;base64,";











function isDataURI(filename) {


 return filename.startsWith(dataURIPrefix);
}

var wasmBinaryFile;

wasmBinaryFile = "pikchr.wasm";

if (!isDataURI(wasmBinaryFile)) {
 wasmBinaryFile = locateFile(wasmBinaryFile);
}

function getBinary(file) {
 try {
  if (file == wasmBinaryFile && wasmBinary) {
   return new Uint8Array(wasmBinary);
  }
  if (readBinary) {
   return readBinary(file);
  }
  throw "both async and sync fetching of the wasm failed";
 } catch (err) {
  abort(err);
 }
}

function getBinaryPromise() {

 if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) {
  if (typeof fetch == "function") {

   return fetch(wasmBinaryFile, {

    credentials: "same-origin"
   }).then(function(response) {
    if (!response["ok"]) {
     throw "failed to load wasm binary file at '" + wasmBinaryFile + "'";
    }
    return response["arrayBuffer"]();
   }).catch(function() {
    return getBinary(wasmBinaryFile);


   });
  }
 }
 return Promise.resolve().then(function() {

  return getBinary(wasmBinaryFile);














 });
}












function createWasm() {
 var info = {


  "a": asmLibraryArg
 };
 function receiveInstance(instance, module) {
  var exports = instance.exports;
  Module["asm"] = exports;
  wasmMemory = Module["asm"]["d"];
  updateMemoryViews();
  wasmTable = Module["asm"]["g"];
  addOnInit(Module["asm"]["e"]);
  removeRunDependency("wasm-instantiate");

 }

 addRunDependency("wasm-instantiate");

 function receiveInstantiationResult(result) {




  receiveInstance(result["instance"]);
 }
 function instantiateArrayBuffer(receiver) {
  return getBinaryPromise().then(function(binary) {
   return WebAssembly.instantiate(binary, info);
  }).then(function(instance) {
   return instance;
  }).then(receiver, function(reason) {
   err("failed to asynchronously prepare wasm: " + reason);
   abort(reason);
  });
 }
 function instantiateAsync() {
  if (!wasmBinary && typeof WebAssembly.instantiateStreaming == "function" && !isDataURI(wasmBinaryFile) && typeof fetch == "function") {
   return fetch(wasmBinaryFile, {
    credentials: "same-origin"
   }).then(function(response) {
    var result = WebAssembly.instantiateStreaming(response, info);
    return result.then(receiveInstantiationResult, function(reason) {
     err("wasm streaming compile failed: " + reason);
     err("falling back to ArrayBuffer instantiation");
     return instantiateArrayBuffer(receiveInstantiationResult);
    });
   });

  } else {
   return instantiateArrayBuffer(receiveInstantiationResult);
  }
 }
 if (Module["instantiateWasm"]) {
  try {
   var exports = Module["instantiateWasm"](info, receiveInstance);
   return exports;
  } catch (e) {
   err("Module.instantiateWasm callback failed with error: " + e);

   readyPromiseReject(e);
  }
 }


 instantiateAsync().catch(readyPromiseReject);
 return {};
}


















var tempDouble;

var tempI64;




function ExitStatus(status) {
 this.name = "ExitStatus";
 this.message = "Program terminated with exit(" + status + ")";
 this.status = status;
}



function callRuntimeCallbacks(callbacks) {
 while (callbacks.length > 0) {
  callbacks.shift()(Module);

 }


}



function getValue(ptr, type = "i8") {
 if (type.endsWith("*")) type = "*";
 switch (type) {
 case "i1":
  return HEAP8[ptr >> 0];

 case "i8":
  return HEAP8[ptr >> 0];























 case "i16":
  return HEAP16[ptr >> 1];


 case "i32":
  return HEAP32[ptr >> 2];


 case "i64":
  return HEAP32[ptr >> 2];


 case "float":
  return HEAPF32[ptr >> 2];


 case "double":
  return HEAPF64[ptr >> 3];


 case "*":
  return HEAPU32[ptr >> 2];


 default:
  abort("invalid type for getValue: " + type);
 }
 return null;
}


function setValue(ptr, value, type = "i8") {
 if (type.endsWith("*")) type = "*";
 switch (type) {
 case "i1":
  HEAP8[ptr >> 0] = value;
  break;

 case "i8":
  HEAP8[ptr >> 0] = value;
  break;

 case "i16":
  HEAP16[ptr >> 1] = value;
  break;


 case "i32":
  HEAP32[ptr >> 2] = value;
  break;





 case "i64":
  tempI64 = [ value >>> 0, (tempDouble = value, +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> 0 : 0) ], 
  HEAP32[ptr >> 2] = tempI64[0], HEAP32[ptr + 4 >> 2] = tempI64[1];



  break;












 case "float":
  HEAPF32[ptr >> 2] = value;



  break;








 case "double":



  HEAPF64[ptr >> 3] = value;
  break;




 case "*":
  HEAPU32[ptr >> 2] = value;
  break;




 default:
  abort("invalid type for setValue: " + type);


 }
}

function ___assert_fail(condition, filename, line, func) {
 abort("Assertion failed: " + UTF8ToString(condition) + ", at: " + [ filename ? UTF8ToString(filename) : "unknown filename", line, func ? UTF8ToString(func) : "unknown function" ]);
}

function abortOnCannotGrowMemory(requestedSize) {
 abort("OOM");
}




function _emscripten_resize_heap(requestedSize) {
 var oldSize = HEAPU8.length;
 requestedSize = requestedSize >>> 0;
 abortOnCannotGrowMemory(requestedSize);
}












var SYSCALLS = {
 varargs: undefined,
 get: function() {
  SYSCALLS.varargs += 4;
  var ret = HEAP32[SYSCALLS.varargs - 4 >> 2];
  return ret;

 },
 getStr: function(ptr) {
  var ret = UTF8ToString(ptr);
  return ret;


 }





};





function _proc_exit(code) {
 EXITSTATUS = code;
 if (!keepRuntimeAlive()) {
  if (Module["onExit"]) Module["onExit"](code);
  ABORT = true;
 }
 quit_(code, new ExitStatus(code));
}

function exitJS(status, implicit) {
 EXITSTATUS = status;
 _proc_exit(status);
}

var _exit = exitJS;

function getCFunc(ident) {
 var func = Module["_" + ident];

 return func;
}

function writeArrayToMemory(array, buffer) {
 HEAP8.set(array, buffer);

}





















































































function ccall(ident, returnType, argTypes, args, opts) {

 var toC = {
  "string": str => {
   var ret = 0;
   if (str !== null && str !== undefined && str !== 0) {
    var len = (str.length << 2) + 1;
    ret = stackAlloc(len);
    stringToUTF8(str, ret, len);
   }
   return ret;
  },
  "array": arr => {
   var ret = stackAlloc(arr.length);
   writeArrayToMemory(arr, ret);
   return ret;
  }
 };
 function convertReturnValue(ret) {
  if (returnType === "string") {
   return UTF8ToString(ret);
  }
  if (returnType === "boolean") return Boolean(ret);
  return ret;
 }
 var func = getCFunc(ident);
 var cArgs = [];
 var stack = 0;
 if (args) {
  for (var i = 0; i < args.length; i++) {
   var converter = toC[argTypes[i]];
   if (converter) {
    if (stack === 0) stack = stackSave();
    cArgs[i] = converter(args[i]);
   } else {
    cArgs[i] = args[i];
   }
  }
 }
 var ret = func.apply(null, cArgs);
 function onDone(ret) {
  if (stack !== 0) stackRestore(stack);
  return convertReturnValue(ret);
 }
 ret = onDone(ret);
 return ret;
}





function cwrap(ident, returnType, argTypes, opts) {
 argTypes = argTypes || [];


 var numericArgs = argTypes.every(type => type === "number" || type === "boolean");
 var numericRet = returnType !== "string";
 if (numericRet && numericArgs && !opts) {
  return getCFunc(ident);
 }
 return function() {
  return ccall(ident, returnType, argTypes, arguments, opts);
 };
}

var asmLibraryArg = {
 "a": ___assert_fail,
 "b": _emscripten_resize_heap,
 "c": _exit
};

var asm = createWasm();

var ___wasm_call_ctors = Module["___wasm_call_ctors"] = function() {
 return (___wasm_call_ctors = Module["___wasm_call_ctors"] = Module["asm"]["e"]).apply(null, arguments);


};

var _pikchr = Module["_pikchr"] = function() {
 return (_pikchr = Module["_pikchr"] = Module["asm"]["f"]).apply(null, arguments);
};


var stackSave = Module["stackSave"] = function() {
 return (stackSave = Module["stackSave"] = Module["asm"]["h"]).apply(null, arguments);
};

var stackRestore = Module["stackRestore"] = function() {
 return (stackRestore = Module["stackRestore"] = Module["asm"]["i"]).apply(null, arguments);
};


var stackAlloc = Module["stackAlloc"] = function() {
 return (stackAlloc = Module["stackAlloc"] = Module["asm"]["j"]).apply(null, arguments);
};




Module["stackSave"] = stackSave;

Module["stackRestore"] = stackRestore;





Module["cwrap"] = cwrap;

Module["setValue"] = setValue;

Module["getValue"] = getValue;

var calledRun;



dependenciesFulfilled = function runCaller() {

 if (!calledRun) run();
 if (!calledRun) dependenciesFulfilled = runCaller;
};


function run(args) {
 args = args || arguments_;
 if (runDependencies > 0) {
  return;
 }


 preRun();

 if (runDependencies > 0) {
  return;
 }

 function doRun() {


  if (calledRun) return;
  calledRun = true;
  Module["calledRun"] = true;
  if (ABORT) return;
  initRuntime();
  readyPromiseResolve(Module);
  if (Module["onRuntimeInitialized"]) Module["onRuntimeInitialized"]();
  postRun();
 }
 if (Module["setStatus"]) {
  Module["setStatus"]("Running...");
  setTimeout(function() {
   setTimeout(function() {
    Module["setStatus"]("");
   }, 1);
   doRun();
  }, 1);
 } else {
  doRun();
 }
}

if (Module["preInit"]) {
 if (typeof Module["preInit"] == "function") Module["preInit"] = [ Module["preInit"] ];
 while (Module["preInit"].length > 0) {
  Module["preInit"].pop()();
 }
}

run();









  return initPikchrModule.ready

}
);
})();
if (typeof exports === 'object' && typeof module === 'object')
  module.exports = initPikchrModule;
else if (typeof define === 'function' && define['amd'])
  define([], function() { return initPikchrModule; });
else if (typeof exports === 'object')
  exports["initPikchrModule"] = initPikchrModule;


|


|
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

>


|
|
|


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







|


<
|
<
<



|
|
|
|


>
|

>
>
>

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



|

>


>
>


>
>
>
>




|
|
|
|
|
|
|
|
|
>
>
|
|
|


>
>
>
>
>


>
>
>


<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|
|
|
<
|
|
>
|
|
|


|
|
<
|


>


>


>


<
<
<
<

|
<
|
>
|

<
|



|
|



|
<
|
>
|

<
|



|



|



|


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







|
<
|
<



|
<
|
<
|
|
|
|
|
|
|
|
|
|
|


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


>
>
>
>


>
>
>
>
>
>
>
>
>
>
|
>
>
|




<
<
<
<
<
<
|
<

|


|


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

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

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
>
>
>

<
|
<
|
|
>
>

<
<
|
>
|
>
>
|
>
>

<
<
<
|
|

|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
|
|
>

|
|
>

|
<
>

|
|
>

|
|
>

|
|
>

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

<
<
<
>

<
<
<
>

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

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

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


>
>
>
>
|
|
|
|
|
|
|
|

|
|
|
|



|
|
>
|
|

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

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

|
|
>
>


|
|
<
>

|
<
<

|
|
<
>

|
|
<
>

>
>



>
>
>
>









>
>

>
|
|


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



|
|
|
|




>
>
>
>
>
>
>

|
>






<
<
|
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
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
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
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
814
815
816
817
818
819
820
821
822
823
824
825
826
827

828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
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
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886


887

var initPikchrModule = (() => {
  var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined;
  
  return (
function(moduleArg = {}) {
  var moduleRtn;

// include: shell.js
// The Module object: Our interface to the outside world. We import
// and export values on it. There are various ways Module can be used:
// 1. Not defined. We create it here
// 2. A function parameter, function(moduleArg) => Promise<Module>
// 3. pre-run appended it, var Module = {}; ..generated code..
// 4. External script tag defines var Module.
// We need to check if Module already exists (e.g. case 3 above).
// Substitution will be replaced with actual code on later stage of the build,
// this way Closure Compiler will not mangle it (e.g. case 4. above).
// Note that if you want to run closure, and also to use Module
// after the generated code, you will need to define   var Module = {};
// before the code. Then that object will be used in the code, and you
// can continue to use Module afterwards as well.
var Module = moduleArg;

// Set up the promise that indicates the Module is initialized
var readyPromiseResolve, readyPromiseReject;

var readyPromise = new Promise((resolve, reject) => {
  readyPromiseResolve = resolve;
  readyPromiseReject = reject;
});

// Determine the runtime environment we are in. You can customize this by
// setting the ENVIRONMENT setting at compile time (see settings.js).
var ENVIRONMENT_IS_WEB = true;

var ENVIRONMENT_IS_WORKER = false;

// --pre-jses are emitted after the Module integration code, so that they can
// refer to Module (if they choose; they can also define Module)
// Sometimes an existing Module object exists with properties
// meant to overwrite the default module functionality. Here
// we collect those properties and reapply _after_ we configure
// the current environment's defaults to avoid having to be so
// defensive during initialization.
var moduleOverrides = Object.assign({}, Module);

var arguments_ = [];

var thisProgram = "./this.program";

var quit_ = (status, toThrow) => {
  throw toThrow;
};


// `/` should be present at the end if `scriptDirectory` is not empty


var scriptDirectory = "";

function locateFile(path) {
  if (Module["locateFile"]) {
    return Module["locateFile"](path, scriptDirectory);
  }
  return scriptDirectory + path;
}

// Hooks that are implemented differently in different runtime environments.
var readAsync, readBinary;

// Note that this includes Node.js workers when relevant (pthreads is enabled).
// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and
// ENVIRONMENT_IS_NODE.
if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
  if (ENVIRONMENT_IS_WORKER) {
    // Check worker, not web, since window could be polyfilled
    scriptDirectory = self.location.href;
  } else if (typeof document != "undefined" && document.currentScript) {
    // web
    scriptDirectory = document.currentScript.src;
  }
  // When MODULARIZE, this JS may be executed later, after document.currentScript
  // is gone, so we saved it, and we use it here instead of any other info.
  if (_scriptName) {
    scriptDirectory = _scriptName;
  }
  // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them.
  // otherwise, slice off the final part of the url to find the script directory.
  // if scriptDirectory does not contain a slash, lastIndexOf will return -1,
  // and scriptDirectory will correctly be replaced with an empty string.
  // If scriptDirectory contains a query (starting with ?) or a fragment (starting with #),
  // they are removed because they could contain a slash.
  if (scriptDirectory.startsWith("blob:")) {
    scriptDirectory = "";
  } else {
    scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf("/") + 1);
  }
  {
    // include: web_or_worker_shell_read.js






    readAsync = url => fetch(url, {

      credentials: "same-origin"
    }).then(response => {










      if (response.ok) {
        return response.arrayBuffer();
      }

      return Promise.reject(new Error(response.status + " : " + response.url));
    });



  }

} else // end include: web_or_worker_shell_read.js
{}

var out = Module["print"] || console.log.bind(console);

var err = Module["printErr"] || console.error.bind(console);

// Merge back in the overrides
Object.assign(Module, moduleOverrides);

// Free the object hierarchy contained in the overrides, this lets the GC
// reclaim data used.
moduleOverrides = null;

// Emit code to handle expected values on the Module object. This applies Module.x
// to the proper local x. This has two benefits: first, we only emit it if it is
// expected to arrive, and second, by using a local everywhere else that can be
// minified.
if (Module["arguments"]) arguments_ = Module["arguments"];

if (Module["thisProgram"]) thisProgram = Module["thisProgram"];

// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message
// end include: shell.js
// include: preamble.js
// === Preamble library stuff ===
// Documentation for the public APIs defined in this file must be updated in:
//    site/source/docs/api_reference/preamble.js.rst
// A prebuilt local version of the documentation is available at:
//    site/build/text/docs/api_reference/preamble.js.txt
// You can also build docs locally as HTML or other formats in site/
// An online HTML version (which may be of a different version of Emscripten)
//    is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html
var wasmBinary = Module["wasmBinary"];

// Wasm globals
var wasmMemory;

//========================================
// Runtime essentials
//========================================
// whether we are quitting the application. no code should run after this.
// set in exit() and abort()
var ABORT = false;

// set by exit() and abort().  Passed to 'onExit' handler.
// NOTE: This is also used as the process return code code in shell environments
// but only when noExitRuntime is false.
var EXITSTATUS;


// Memory management


































var /** @type {!Int8Array} */ HEAP8, /** @type {!Uint8Array} */ HEAPU8, /** @type {!Int16Array} */ HEAP16, /** @type {!Uint16Array} */ HEAPU16, /** @type {!Int32Array} */ HEAP32, /** @type {!Uint32Array} */ HEAPU32, /** @type {!Float32Array} */ HEAPF32, /** @type {!Float64Array} */ HEAPF64;



// include: runtime_shared.js








































function updateMemoryViews() {
  var b = wasmMemory.buffer;
  Module["HEAP8"] = HEAP8 = new Int8Array(b);
  Module["HEAP16"] = HEAP16 = new Int16Array(b);

  Module["HEAPU8"] = HEAPU8 = new Uint8Array(b);
  Module["HEAPU16"] = HEAPU16 = new Uint16Array(b);
  Module["HEAP32"] = HEAP32 = new Int32Array(b);
  Module["HEAPU32"] = HEAPU32 = new Uint32Array(b);
  Module["HEAPF32"] = HEAPF32 = new Float32Array(b);
  Module["HEAPF64"] = HEAPF64 = new Float64Array(b);
}

// end include: runtime_shared.js
// include: runtime_stack_check.js

// end include: runtime_stack_check.js
var __ATPRERUN__ = [];

// functions called before the runtime is initialized
var __ATINIT__ = [];

// functions called during shutdown
var __ATPOSTRUN__ = [];

// functions called after the main() is called
var runtimeInitialized = false;





function preRun() {
  var preRuns = Module["preRun"];

  if (preRuns) {
    if (typeof preRuns == "function") preRuns = [ preRuns ];
    preRuns.forEach(addOnPreRun);
  }

  callRuntimeCallbacks(__ATPRERUN__);
}

function initRuntime() {
  runtimeInitialized = true;
  callRuntimeCallbacks(__ATINIT__);
}

function postRun() {
  var postRuns = Module["postRun"];

  if (postRuns) {
    if (typeof postRuns == "function") postRuns = [ postRuns ];
    postRuns.forEach(addOnPostRun);
  }

  callRuntimeCallbacks(__ATPOSTRUN__);
}

function addOnPreRun(cb) {
  __ATPRERUN__.unshift(cb);
}

function addOnInit(cb) {
  __ATINIT__.unshift(cb);
}

function addOnPostRun(cb) {
  __ATPOSTRUN__.unshift(cb);
}

// include: runtime_math.js
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc
// end include: runtime_math.js
// A counter of dependencies for calling run(). If we need to
// do asynchronous work before running, increment this and
// decrement it. Incrementing must happen in a place like
// Module.preRun (used by emcc to add file preloading).
// Note that you can add dependencies in preRun, even though
// it happens right before run - run will be postponed until
// the dependencies are met.
var runDependencies = 0;

var runDependencyWatcher = null;

var dependenciesFulfilled = null;

function addRunDependency(id) {
  runDependencies++;

  Module["monitorRunDependencies"]?.(runDependencies);

}

function removeRunDependency(id) {
  runDependencies--;

  Module["monitorRunDependencies"]?.(runDependencies);

  if (runDependencies == 0) {
    if (runDependencyWatcher !== null) {
      clearInterval(runDependencyWatcher);
      runDependencyWatcher = null;
    }
    if (dependenciesFulfilled) {
      var callback = dependenciesFulfilled;
      dependenciesFulfilled = null;
      callback();
    }
  }
}

/** @param {string|number=} what */ function abort(what) {

  Module["onAbort"]?.(what);

  what = "Aborted(" + what + ")";
  // TODO(sbc): Should we remove printing and leave it up to whoever
  // catches the exception?
  err(what);
  ABORT = true;

  what += ". Build with -sASSERTIONS for more info.";
  // Use a wasm runtime error, because a JS error might be seen as a foreign
  // exception, which means we'd run destructors on it. We need the error to
  // simply make the program stop.
  // FIXME This approach does not work in Wasm EH because it currently does not assume
  // all RuntimeErrors are from traps; it decides whether a RuntimeError is from
  // a trap or not based on a hidden field within the object. So at the moment
  // we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that
  // allows this in the wasm spec.
  // Suppress closure compiler warning here. Closure compiler's builtin extern
  // definition for WebAssembly.RuntimeError claims it takes no arguments even
  // though it can.
  // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed.
  /** @suppress {checkTypes} */ var e = new WebAssembly.RuntimeError(what);
  readyPromiseReject(e);
  // Throw the error whether or not MODULARIZE is set because abort is used
  // in code paths apart from instantiation where an exception is expected
  // to be thrown when abort is called.
  throw e;
}

// include: memoryprofiler.js
// end include: memoryprofiler.js
// include: URIUtils.js
// Prefix of data URIs emitted by SINGLE_FILE and related options.
var dataURIPrefix = "data:application/octet-stream;base64,";

/**
 * Indicates whether filename is a base64 data URI.
 * @noinline
 */ var isDataURI = filename => filename.startsWith(dataURIPrefix);

// end include: URIUtils.js
// include: runtime_exceptions.js
// end include: runtime_exceptions.js
function findWasmBinary() {
  var f = "pikchr-v2813665466.wasm";
  if (!isDataURI(f)) {
    return locateFile(f);
  }
  return f;
}

var wasmBinaryFile;







function getBinarySync(file) {

  if (file == wasmBinaryFile && wasmBinary) {
    return new Uint8Array(wasmBinary);
  }
  if (readBinary) {
    return readBinary(file);
  }
  throw "both async and sync fetching of the wasm failed";


}


function getBinaryPromise(binaryFile) {
  // If we don't have the binary yet, load it asynchronously using readAsync.
  if (!wasmBinary) {
    // Fetch the binary using readAsync
    return readAsync(binaryFile).then(response => new Uint8Array(/** @type{!ArrayBuffer} */ (response)), // Fall back to getBinarySync if readAsync fails
    () => getBinarySync(binaryFile));
  }
  // Otherwise, getBinarySync should be able to get it synchronously
  return Promise.resolve().then(() => getBinarySync(binaryFile));


}

function instantiateArrayBuffer(binaryFile, imports, receiver) {
  return getBinaryPromise(binaryFile).then(binary => WebAssembly.instantiate(binary, imports)).then(receiver, reason => {
    err(`failed to asynchronously prepare wasm: ${reason}`);
    abort(reason);
  });
}

function instantiateAsync(binary, binaryFile, imports, callback) {
  if (!binary && typeof WebAssembly.instantiateStreaming == "function" && !isDataURI(binaryFile) && typeof fetch == "function") {
    return fetch(binaryFile, {
      credentials: "same-origin"
    }).then(response => {
      // Suppress closure warning here since the upstream definition for
      // instantiateStreaming only allows Promise<Repsponse> rather than
      // an actual Response.
      // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure is fixed.
      /** @suppress {checkTypes} */ var result = WebAssembly.instantiateStreaming(response, imports);
      return result.then(callback, function(reason) {
        // We expect the most common failure cause to be a bad MIME type for the binary,
        // in which case falling back to ArrayBuffer instantiation should work.
        err(`wasm streaming compile failed: ${reason}`);
        err("falling back to ArrayBuffer instantiation");
        return instantiateArrayBuffer(binaryFile, imports, callback);
      });
    });
  }
  return instantiateArrayBuffer(binaryFile, imports, callback);
}

function getWasmImports() {
  // prepare imports
  return {
    "a": wasmImports
  };
}

// Create the wasm instance.
// Receives the wasm imports, returns the exports.
function createWasm() {
  var info = getWasmImports();
  // Load the wasm module and create an instance of using native support in the JS engine.
  // handle a generated wasm instance, receiving its exports and
  // performing other necessary setup

  /** @param {WebAssembly.Module=} module*/ function receiveInstance(instance, module) {
    wasmExports = instance.exports;

    wasmMemory = wasmExports["d"];
    updateMemoryViews();

    addOnInit(wasmExports["e"]);
    removeRunDependency("wasm-instantiate");
    return wasmExports;
  }
  // wait for the pthread pool (if any)
  addRunDependency("wasm-instantiate");
  // Prefer streaming instantiation if available.
  function receiveInstantiationResult(result) {
    // 'result' is a ResultObject object which has both the module and instance.
    // receiveInstance() will swap in the exports (to Module.asm) so they can be called
    // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line.
    // When the regression is fixed, can restore the above PTHREADS-enabled path.
    receiveInstance(result["instance"]);
  }














  // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback
  // to manually instantiate the Wasm module themselves. This allows pages to
  // run the instantiation parallel to any other async startup actions they are
  // performing.




  // Also pthreads and wasm workers initialize the wasm instance through this
  // path.



  if (Module["instantiateWasm"]) {
    try {
      return Module["instantiateWasm"](info, receiveInstance);

    } catch (e) {
      err(`Module.instantiateWasm callback failed with error: ${e}`);
      // If instantiation fails, reject the module ready promise.
      readyPromiseReject(e);
    }
  }
  wasmBinaryFile ??= findWasmBinary();
  // If instantiation fails, reject the module ready promise.
  instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult).catch(readyPromiseReject);
  return {};
}

// include: runtime_debug.js
// end include: runtime_debug.js
// === Body ===
// end include: preamble.js
/** @constructor */ function ExitStatus(status) {
  this.name = "ExitStatus";
  this.message = `Program terminated with exit(${status})`;
  this.status = status;
}

var callRuntimeCallbacks = callbacks => {
  // Pass the module as the first argument.
  callbacks.forEach(f => f(Module));
};

/**
     * @param {number} ptr
     * @param {string} type
     */ function getValue(ptr, type = "i8") {
  if (type.endsWith("*")) type = "*";
  switch (type) {
   case "i1":
    return HEAP8[ptr];


   case "i8":

    return HEAP8[ptr];

   case "i16":
    return HEAP16[((ptr) >> 1)];



   case "i32":
    return HEAP32[((ptr) >> 2)];

   case "i64":
    abort("to do getValue(i64) use WASM_BIGINT");

   case "float":
    return HEAPF32[((ptr) >> 2)];




   case "double":
    return HEAPF64[((ptr) >> 3)];

   case "*":
    return HEAPU32[((ptr) >> 2)];

   default:
    abort(`invalid type for getValue: ${type}`);
  }
}

var noExitRuntime = Module["noExitRuntime"] || true;

/**
     * @param {number} ptr
     * @param {number} value
     * @param {string} type
     */ function setValue(ptr, value, type = "i8") {
  if (type.endsWith("*")) type = "*";
  switch (type) {
   case "i1":
    HEAP8[ptr] = value;
    break;

   case "i8":
    HEAP8[ptr] = value;
    break;

   case "i16":
    HEAP16[((ptr) >> 1)] = value;
    break;

   case "i32":
    HEAP32[((ptr) >> 2)] = value;
    break;

   case "i64":

    abort("to do setValue(i64) use WASM_BIGINT");

   case "float":
    HEAPF32[((ptr) >> 2)] = value;
    break;

   case "double":
    HEAPF64[((ptr) >> 3)] = value;
    break;

   case "*":
    HEAPU32[((ptr) >> 2)] = value;
    break;

   default:




    abort(`invalid type for setValue: ${type}`);
  }






}

var stackRestore = val => __emscripten_stack_restore(val);





var stackSave = () => _emscripten_stack_get_current();




var UTF8Decoder = typeof TextDecoder != "undefined" ? new TextDecoder : undefined;

/**
     * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given
     * array that contains uint8 values, returns a copy of that string as a
     * Javascript String object.


     * heapOrArray is either a regular array, or a JavaScript typed array view.
     * @param {number=} idx
     * @param {number=} maxBytesToRead
     * @return {string}
     */ var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead = NaN) => {
  var endIdx = idx + maxBytesToRead;
  var endPtr = idx;
  // TextDecoder needs to know the byte length in advance, it doesn't stop on
  // null terminator by itself.  Also, use the length info to avoid running tiny
  // strings through TextDecoder, since .subarray() allocates garbage.
  // (As a tiny code save trick, compare endPtr against endIdx using a negation,
  // so that undefined/NaN means Infinity)
  while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr;
  if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) {
    return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr));
  }
  var str = "";

  // If building with TextDecoder, we have already computed the string length
  // above, so test loop end condition against that
  while (idx < endPtr) {
    // For UTF8 byte structure, see:
    // http://en.wikipedia.org/wiki/UTF-8#Description
    // https://www.ietf.org/rfc/rfc2279.txt
    // https://tools.ietf.org/html/rfc3629
    var u0 = heapOrArray[idx++];
    if (!(u0 & 128)) {
      str += String.fromCharCode(u0);
      continue;
    }

    var u1 = heapOrArray[idx++] & 63;
    if ((u0 & 224) == 192) {
      str += String.fromCharCode(((u0 & 31) << 6) | u1);
      continue;

    }
    var u2 = heapOrArray[idx++] & 63;
    if ((u0 & 240) == 224) {
      u0 = ((u0 & 15) << 12) | (u1 << 6) | u2;
    } else {


      u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63);
    }
    if (u0 < 65536) {
      str += String.fromCharCode(u0);
    } else {

      var ch = u0 - 65536;
      str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023));
    }
  }
  return str;


};




/**
     * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the
     * emscripten HEAP, returns a copy of that string as a Javascript String object.
     *





     * @param {number} ptr
     * @param {number=} maxBytesToRead - An optional length that specifies the
     *   maximum number of bytes to read. You can omit this parameter to scan the
     *   string until the first 0 byte. If maxBytesToRead is passed, and the string
     *   at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the
     *   string will cut short at that byte index (i.e. maxBytesToRead will not
     *   produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing
     *   frequent uses of UTF8ToString() with and without maxBytesToRead may throw
     *   JS JIT optimizations off, so it is worth to consider consistently using one
     * @return {string}
     */ var UTF8ToString = (ptr, maxBytesToRead) => ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : "";

var ___assert_fail = (condition, filename, line, func) => {





  abort(`Assertion failed: ${UTF8ToString(condition)}, at: ` + [ filename ? UTF8ToString(filename) : "unknown filename", line, func ? UTF8ToString(func) : "unknown function" ]);
};

var abortOnCannotGrowMemory = requestedSize => {

  abort("OOM");
};

var _emscripten_resize_heap = requestedSize => {
  var oldSize = HEAPU8.length;
  // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned.
  requestedSize >>>= 0;
  abortOnCannotGrowMemory(requestedSize);
};

var runtimeKeepaliveCounter = 0;

var keepRuntimeAlive = () => noExitRuntime || runtimeKeepaliveCounter > 0;

var _proc_exit = code => {
  EXITSTATUS = code;
  if (!keepRuntimeAlive()) {
    Module["onExit"]?.(code);
    ABORT = true;
  }
  quit_(code, new ExitStatus(code));
};

/** @suppress {duplicate } */ /** @param {boolean|number=} implicit */ var exitJS = (status, implicit) => {
  EXITSTATUS = status;
  _proc_exit(status);
};

var _exit = exitJS;

var getCFunc = ident => {
  var func = Module["_" + ident];
  // closure exported function
  return func;
};

var writeArrayToMemory = (array, buffer) => {
  HEAP8.set(array, buffer);
};

var lengthBytesUTF8 = str => {
  var len = 0;
  for (var i = 0; i < str.length; ++i) {
    // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code
    // unit, not a Unicode code point of the character! So decode
    // UTF16->UTF32->UTF8.
    // See http://unicode.org/faq/utf_bom.html#utf16-3
    var c = str.charCodeAt(i);
    // possibly a lead surrogate
    if (c <= 127) {
      len++;
    } else if (c <= 2047) {
      len += 2;
    } else if (c >= 55296 && c <= 57343) {
      len += 4;
      ++i;
    } else {
      len += 3;
    }
  }
  return len;
};

var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => {
  // Parameter maxBytesToWrite is not optional. Negative values, 0, null,
  // undefined and false each don't write out any bytes.
  if (!(maxBytesToWrite > 0)) return 0;
  var startIdx = outIdx;
  var endIdx = outIdx + maxBytesToWrite - 1;
  // -1 for string null terminator.
  for (var i = 0; i < str.length; ++i) {
    // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code
    // unit, not a Unicode code point of the character! So decode
    // UTF16->UTF32->UTF8.
    // See http://unicode.org/faq/utf_bom.html#utf16-3
    // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description
    // and https://www.ietf.org/rfc/rfc2279.txt
    // and https://tools.ietf.org/html/rfc3629
    var u = str.charCodeAt(i);
    // possibly a lead surrogate
    if (u >= 55296 && u <= 57343) {
      var u1 = str.charCodeAt(++i);
      u = 65536 + ((u & 1023) << 10) | (u1 & 1023);
    }
    if (u <= 127) {
      if (outIdx >= endIdx) break;
      heap[outIdx++] = u;
    } else if (u <= 2047) {
      if (outIdx + 1 >= endIdx) break;
      heap[outIdx++] = 192 | (u >> 6);
      heap[outIdx++] = 128 | (u & 63);
    } else if (u <= 65535) {
      if (outIdx + 2 >= endIdx) break;
      heap[outIdx++] = 224 | (u >> 12);
      heap[outIdx++] = 128 | ((u >> 6) & 63);
      heap[outIdx++] = 128 | (u & 63);
    } else {
      if (outIdx + 3 >= endIdx) break;
      heap[outIdx++] = 240 | (u >> 18);
      heap[outIdx++] = 128 | ((u >> 12) & 63);
      heap[outIdx++] = 128 | ((u >> 6) & 63);
      heap[outIdx++] = 128 | (u & 63);
    }
  }
  // Null-terminate the pointer to the buffer.
  heap[outIdx] = 0;
  return outIdx - startIdx;
};

var stringToUTF8 = (str, outPtr, maxBytesToWrite) => stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite);

var stackAlloc = sz => __emscripten_stack_alloc(sz);

var stringToUTF8OnStack = str => {
  var size = lengthBytesUTF8(str) + 1;
  var ret = stackAlloc(size);
  stringToUTF8(str, ret, size);
  return ret;
};

/**
     * @param {string|null=} returnType
     * @param {Array=} argTypes
     * @param {Arguments|Array=} args
     * @param {Object=} opts
     */ var ccall = (ident, returnType, argTypes, args, opts) => {
  // For fast lookup of conversion functions
  var toC = {
    "string": str => {
      var ret = 0;
      if (str !== null && str !== undefined && str !== 0) {

        // null string
        ret = stringToUTF8OnStack(str);
      }
      return ret;
    },
    "array": arr => {
      var ret = stackAlloc(arr.length);
      writeArrayToMemory(arr, ret);
      return ret;
    }
  };
  function convertReturnValue(ret) {
    if (returnType === "string") {
      return UTF8ToString(ret);
    }
    if (returnType === "boolean") return Boolean(ret);
    return ret;
  }
  var func = getCFunc(ident);
  var cArgs = [];
  var stack = 0;
  if (args) {
    for (var i = 0; i < args.length; i++) {
      var converter = toC[argTypes[i]];
      if (converter) {
        if (stack === 0) stack = stackSave();
        cArgs[i] = converter(args[i]);
      } else {
        cArgs[i] = args[i];
      }
    }
  }
  var ret = func(...cArgs);
  function onDone(ret) {
    if (stack !== 0) stackRestore(stack);
    return convertReturnValue(ret);
  }
  ret = onDone(ret);
  return ret;
};

/**
     * @param {string=} returnType
     * @param {Array=} argTypes
     * @param {Object=} opts
     */ var cwrap = (ident, returnType, argTypes, opts) => {

  // When the function takes numbers and returns a number, we can just return
  // the original function
  var numericArgs = !argTypes || argTypes.every(type => type === "number" || type === "boolean");
  var numericRet = returnType !== "string";
  if (numericRet && numericArgs && !opts) {
    return getCFunc(ident);
  }

  return (...args) => ccall(ident, returnType, argTypes, args, opts);
};










var wasmImports = {
  /** @export */ a: ___assert_fail,
  /** @export */ b: _emscripten_resize_heap,
  /** @export */ c: _exit
};

var wasmExports = createWasm();


var ___wasm_call_ctors = () => (___wasm_call_ctors = wasmExports["e"])();

var _pikchr_version = Module["_pikchr_version"] = () => (_pikchr_version = Module["_pikchr_version"] = wasmExports["g"])();



var _pikchr = Module["_pikchr"] = (a0, a1, a2, a3, a4) => (_pikchr = Module["_pikchr"] = wasmExports["h"])(a0, a1, a2, a3, a4);


var __emscripten_stack_restore = a0 => (__emscripten_stack_restore = wasmExports["i"])(a0);

var __emscripten_stack_alloc = a0 => (__emscripten_stack_alloc = wasmExports["j"])(a0);


var _emscripten_stack_get_current = () => (_emscripten_stack_get_current = wasmExports["k"])();

// include: postamble.js
// === Auto-generated postamble setup entry stuff ===
Module["stackSave"] = stackSave;

Module["stackRestore"] = stackRestore;

Module["stackAlloc"] = stackAlloc;

Module["ccall"] = ccall;

Module["cwrap"] = cwrap;

Module["setValue"] = setValue;

Module["getValue"] = getValue;

var calledRun;

var calledPrerun;

dependenciesFulfilled = function runCaller() {
  // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false)
  if (!calledRun) run();
  if (!calledRun) dependenciesFulfilled = runCaller;
};

// try this again later, after new deps are fulfilled
function run() {

  if (runDependencies > 0) {
    return;
  }
  if (!calledPrerun) {
    calledPrerun = 1;
    preRun();
    // a preRun added a dependency, run will be called later
    if (runDependencies > 0) {
      return;
    }
  }
  function doRun() {
    // run may have just been called through dependencies being fulfilled just in this very frame,
    // or while the async setStatus time below was happening
    if (calledRun) return;
    calledRun = 1;
    Module["calledRun"] = 1;
    if (ABORT) return;
    initRuntime();
    readyPromiseResolve(Module);
    Module["onRuntimeInitialized"]?.();
    postRun();
  }
  if (Module["setStatus"]) {
    Module["setStatus"]("Running...");
    setTimeout(() => {

      setTimeout(() => Module["setStatus"](""), 1);

      doRun();
    }, 1);
  } else {
    doRun();
  }
}

if (Module["preInit"]) {
  if (typeof Module["preInit"] == "function") Module["preInit"] = [ Module["preInit"] ];
  while (Module["preInit"].length > 0) {
    Module["preInit"].pop()();
  }
}

run();

// end include: postamble.js
// include: postamble_modularize.js
// In MODULARIZE mode we wrap the generated code in a factory function
// and return either the Module itself, or a promise of the module.
// We assign to the `moduleRtn` global here and configure closure to see
// this as and extern so it won't get minified.
moduleRtn = readyPromise;


  return moduleRtn;
}
);
})();
if (typeof exports === 'object' && typeof module === 'object')
  module.exports = initPikchrModule;
else if (typeof define === 'function' && define['amd'])


  define([], () => initPikchrModule);
Changes to extsrc/pikchr.wasm.

cannot compute difference between binary files

Changes to extsrc/shell.c.
232
233
234
235
236
237
238


239
240
241
242
243
244
245
#define isatty(x) 1
#endif

/* ctype macros that work with signed characters */
#define IsSpace(X)  isspace((unsigned char)X)
#define IsDigit(X)  isdigit((unsigned char)X)
#define ToLower(X)  (char)tolower((unsigned char)X)



#if defined(_WIN32) || defined(WIN32)
#if SQLITE_OS_WINRT
#include <intrin.h>
#endif
#undef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN







>
>







232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#define isatty(x) 1
#endif

/* ctype macros that work with signed characters */
#define IsSpace(X)  isspace((unsigned char)X)
#define IsDigit(X)  isdigit((unsigned char)X)
#define ToLower(X)  (char)tolower((unsigned char)X)
#define IsAlnum(X)  isalnum((unsigned char)X)
#define IsAlpha(X)  isalpha((unsigned char)X)

#if defined(_WIN32) || defined(WIN32)
#if SQLITE_OS_WINRT
#include <intrin.h>
#endif
#undef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
*/
static char *Argv0;

/*
** Prompt strings. Initialized in main. Settable with
**   .prompt main continue
*/
#define PROMPT_LEN_MAX 20
/* First line prompt.   default: "sqlite> " */
static char mainPrompt[PROMPT_LEN_MAX];
/* Continuation prompt. default: "   ...> " */
static char continuePrompt[PROMPT_LEN_MAX];

/* This is variant of the standard-library strncpy() routine with the
** one change that the destination string is always zero-terminated, even







|







849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
*/
static char *Argv0;

/*
** Prompt strings. Initialized in main. Settable with
**   .prompt main continue
*/
#define PROMPT_LEN_MAX 128
/* First line prompt.   default: "sqlite> " */
static char mainPrompt[PROMPT_LEN_MAX];
/* Continuation prompt. default: "   ...> " */
static char continuePrompt[PROMPT_LEN_MAX];

/* This is variant of the standard-library strncpy() routine with the
** one change that the destination string is always zero-terminated, even
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
** that quoting is required.
**
** Return '"' if quoting is required.  Return 0 if no quoting is required.
*/
static char quoteChar(const char *zName){
  int i;
  if( zName==0 ) return '"';
  if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"';
  for(i=0; zName[i]; i++){
    if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"';
  }
  return sqlite3_keyword_check(zName, i) ? '"' : 0;
}

/*
** Construct a fake object name and column list to describe the structure
** of the view, virtual table, or table valued function zSchema.zName.







|

|







1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
** that quoting is required.
**
** Return '"' if quoting is required.  Return 0 if no quoting is required.
*/
static char quoteChar(const char *zName){
  int i;
  if( zName==0 ) return '"';
  if( !IsAlpha(zName[0]) && zName[0]!='_' ) return '"';
  for(i=0; zName[i]; i++){
    if( !IsAlnum(zName[i]) && zName[i]!='_' ) return '"';
  }
  return sqlite3_keyword_check(zName, i) ? '"' : 0;
}

/*
** Construct a fake object name and column list to describe the structure
** of the view, virtual table, or table valued function zSchema.zName.
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
**                             Notice the ":" at the end of the prefix, which
**                             is needed to separate the prefix from the
**                             content in cases where the content starts
**                             with a digit.
**
**    typeof(Y)='blob'         The hash is taken over prefix "Bnnn:" followed
**                             by the binary content of the blob.  The "nnn"
**                             in the prefix is the mimimum-length decimal
**                             representation of the byte-length of the blob.
**
** According to the rules above, all of the following SELECT statements
** should return TRUE:
**
**    SELECT sha3(1) = sha3('1');
**







|







2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
**                             Notice the ":" at the end of the prefix, which
**                             is needed to separate the prefix from the
**                             content in cases where the content starts
**                             with a digit.
**
**    typeof(Y)='blob'         The hash is taken over prefix "Bnnn:" followed
**                             by the binary content of the blob.  The "nnn"
**                             in the prefix is the minimum-length decimal
**                             representation of the byte-length of the blob.
**
** According to the rules above, all of the following SELECT statements
** should return TRUE:
**
**    SELECT sha3(1) = sha3('1');
**
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
**
** This SQLite extension implements the UINT collating sequence.
**
** UINT works like BINARY for text, except that embedded strings
** of digits compare in numeric order.
**
**     *   Leading zeros are handled properly, in the sense that
**         they do not mess of the maginitude comparison of embedded
**         strings of digits.  "x00123y" is equal to "x123y".
**
**     *   Only unsigned integers are recognized.  Plus and minus
**         signs are ignored.  Decimal points and exponential notation
**         are ignored.
**
**     *   Embedded integers can be of arbitrary length.  Comparison







|







3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
**
** This SQLite extension implements the UINT collating sequence.
**
** UINT works like BINARY for text, except that embedded strings
** of digits compare in numeric order.
**
**     *   Leading zeros are handled properly, in the sense that
**         they do not mess of the magnitude comparison of embedded
**         strings of digits.  "x00123y" is equal to "x123y".
**
**     *   Only unsigned integers are recognized.  Plus and minus
**         signs are ignored.  Decimal points and exponential notation
**         are ignored.
**
**     *   Embedded integers can be of arbitrary length.  Comparison
3750
3751
3752
3753
3754
3755
3756



3757
3758
3759
3760
3761
3762
3763

/* Mark a function parameter as unused, to suppress nuisance compiler
** warnings. */
#ifndef UNUSED_PARAMETER
# define UNUSED_PARAMETER(X)  (void)(X)
#endif





/* A decimal object */
typedef struct Decimal Decimal;
struct Decimal {
  char sign;        /* 0 for positive, 1 for negative */
  char oom;         /* True if an OOM is encountered */
  char isNull;      /* True if holds a NULL rather than a number */







>
>
>







3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768

/* Mark a function parameter as unused, to suppress nuisance compiler
** warnings. */
#ifndef UNUSED_PARAMETER
# define UNUSED_PARAMETER(X)  (void)(X)
#endif

#ifndef IsSpace
#define IsSpace(X)  isspace((unsigned char)X)
#endif

/* A decimal object */
typedef struct Decimal Decimal;
struct Decimal {
  char sign;        /* 0 for positive, 1 for negative */
  char oom;         /* True if an OOM is encountered */
  char isNull;      /* True if holds a NULL rather than a number */
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
  p->oom = 0;
  p->isInit = 1;
  p->isNull = 0;
  p->nDigit = 0;
  p->nFrac = 0;
  p->a = sqlite3_malloc64( n+1 );
  if( p->a==0 ) goto new_from_text_failed;
  for(i=0; isspace(zIn[i]); i++){}
  if( zIn[i]=='-' ){
    p->sign = 1;
    i++;
  }else if( zIn[i]=='+' ){
    i++;
  }
  while( i<n && zIn[i]=='0' ) i++;







|







3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
  p->oom = 0;
  p->isInit = 1;
  p->isNull = 0;
  p->nDigit = 0;
  p->nFrac = 0;
  p->a = sqlite3_malloc64( n+1 );
  if( p->a==0 ) goto new_from_text_failed;
  for(i=0; IsSpace(zIn[i]); i++){}
  if( zIn[i]=='-' ){
    p->sign = 1;
    i++;
  }else if( zIn[i]=='+' ){
    i++;
  }
  while( i<n && zIn[i]=='0' ) i++;
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
    decimal_add(pA, pB);
    decimal_result(context, pA);
  }
  decimal_free(pA);
  decimal_free(pB);
}

/* Aggregate funcion:   decimal_sum(X)
**
** Works like sum() except that it uses decimal arithmetic for unlimited
** precision.
*/
static void decimalSumStep(
  sqlite3_context *context,
  int argc,







|







4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
    decimal_add(pA, pB);
    decimal_result(context, pA);
  }
  decimal_free(pA);
  decimal_free(pB);
}

/* Aggregate function:   decimal_sum(X)
**
** Works like sum() except that it uses decimal arithmetic for unlimited
** precision.
*/
static void decimalSumStep(
  sqlite3_context *context,
  int argc,
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
  if( bExact ) return -1;
  return iFirst;
}

/*
** Generate an error for a percentile function.
**
** The error format string must have exactly one occurrance of "%%s()"
** (with two '%' characters).  That substring will be replaced by the name
** of the function.
*/
static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){
  PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
  char *zMsg1;
  char *zMsg2;







|







4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
  if( bExact ) return -1;
  return iFirst;
}

/*
** Generate an error for a percentile function.
**
** The error format string must have exactly one occurrence of "%%s()"
** (with two '%' characters).  That substring will be replaced by the name
** of the function.
*/
static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){
  PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
  char *zMsg1;
  char *zMsg2;
5955
5956
5957
5958
5959
5960
5961
5962
5963
5964
5965
5966
5967
5968
5969
**
** Here is a query to show various boundry values for the binary64
** number format:
**
**    WITH c(name,bin) AS (VALUES
**       ('minimum positive value',        x'0000000000000001'),
**       ('maximum subnormal value',       x'000fffffffffffff'),
**       ('mininum positive nornal value', x'0010000000000000'),
**       ('maximum value',                 x'7fefffffffffffff'))
**    SELECT c.name, decimal_mul(ieee754_mantissa(c.bin),pow2.v)
**      FROM pow2, c WHERE pow2.x=ieee754_exponent(c.bin);
**
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1







|







5960
5961
5962
5963
5964
5965
5966
5967
5968
5969
5970
5971
5972
5973
5974
**
** Here is a query to show various boundry values for the binary64
** number format:
**
**    WITH c(name,bin) AS (VALUES
**       ('minimum positive value',        x'0000000000000001'),
**       ('maximum subnormal value',       x'000fffffffffffff'),
**       ('minimum positive normal value', x'0010000000000000'),
**       ('maximum value',                 x'7fefffffffffffff'))
**    SELECT c.name, decimal_mul(ieee754_mantissa(c.bin),pow2.v)
**      FROM pow2, c WHERE pow2.x=ieee754_exponent(c.bin);
**
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
6342
6343
6344
6345
6346
6347
6348
6349
6350
6351
6352
6353
6354
6355
6356
    ix -= mxI64;
    /* With 2's complement ALU, this next can be 1 step, but is split into
     * 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */
    smBase += (mxI64/2) * smStep;
    smBase += (mxI64 - mxI64/2) * smStep;
  }
  /* Under UBSAN (or on 1's complement machines), must do this last term
   * in steps to avoid the dreaded (and harmless) signed multiply overlow. */
  if( ix>=2 ){
    sqlite3_int64 ix2 = (sqlite3_int64)ix/2;
    smBase += ix2*smStep;
    ix -= ix2;
  }
  return smBase + ((sqlite3_int64)ix)*smStep;
}







|







6347
6348
6349
6350
6351
6352
6353
6354
6355
6356
6357
6358
6359
6360
6361
    ix -= mxI64;
    /* With 2's complement ALU, this next can be 1 step, but is split into
     * 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */
    smBase += (mxI64/2) * smStep;
    smBase += (mxI64 - mxI64/2) * smStep;
  }
  /* Under UBSAN (or on 1's complement machines), must do this last term
   * in steps to avoid the dreaded (and harmless) signed multiply overflow. */
  if( ix>=2 ){
    sqlite3_int64 ix2 = (sqlite3_int64)ix/2;
    smBase += ix2*smStep;
    ix -= ix2;
  }
  return smBase + ((sqlite3_int64)ix)*smStep;
}
6722
6723
6724
6725
6726
6727
6728
6729
6730
6731
6732
6733
6734
6735
6736
6737
6738
6739
6740
6741
6742
6743
6744
6745
6746
6747
6748
    if( pCur->ss.iStep>0 ){
      sqlite3_int64 szStep = pCur->ss.iStep;
      if( pCur->ss.iBase<iMin ){
        sqlite3_uint64 d = iMin - pCur->ss.iBase;
        pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep;
      }
      if( pCur->ss.iTerm>iMax ){
        sqlite3_uint64 d = pCur->ss.iTerm - iMax;
        pCur->ss.iTerm -= ((d+szStep-1)/szStep)*szStep;
      }
    }else{
      sqlite3_int64 szStep = -pCur->ss.iStep;
      assert( szStep>0 );
      if( pCur->ss.iBase>iMax ){
        sqlite3_uint64 d = pCur->ss.iBase - iMax;
        pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep;
      }
      if( pCur->ss.iTerm<iMin ){
        sqlite3_uint64 d = iMin - pCur->ss.iTerm;
        pCur->ss.iTerm += ((d+szStep-1)/szStep)*szStep;
      }
    }
  }

  /* Apply LIMIT and OFFSET constraints, if any */
  if( idxNum & 0x20 ){
    if( iOffset>0 ){







<
|









<
|







6727
6728
6729
6730
6731
6732
6733

6734
6735
6736
6737
6738
6739
6740
6741
6742
6743

6744
6745
6746
6747
6748
6749
6750
6751
    if( pCur->ss.iStep>0 ){
      sqlite3_int64 szStep = pCur->ss.iStep;
      if( pCur->ss.iBase<iMin ){
        sqlite3_uint64 d = iMin - pCur->ss.iBase;
        pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep;
      }
      if( pCur->ss.iTerm>iMax ){

        pCur->ss.iTerm = iMax;
      }
    }else{
      sqlite3_int64 szStep = -pCur->ss.iStep;
      assert( szStep>0 );
      if( pCur->ss.iBase>iMax ){
        sqlite3_uint64 d = pCur->ss.iBase - iMax;
        pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep;
      }
      if( pCur->ss.iTerm<iMin ){

        pCur->ss.iTerm = iMin;
      }
    }
  }

  /* Apply LIMIT and OFFSET constraints, if any */
  if( idxNum & 0x20 ){
    if( iOffset>0 ){
9022
9023
9024
9025
9026
9027
9028





9029
9030
9031
9032
9033
9034
9035
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <ctype.h>

#ifndef SQLITE_OMIT_VIRTUALTABLE






/* completion_vtab is a subclass of sqlite3_vtab which will
** serve as the underlying representation of a completion virtual table
*/
typedef struct completion_vtab completion_vtab;
struct completion_vtab {
  sqlite3_vtab base;  /* Base class - must be first */







>
>
>
>
>







9025
9026
9027
9028
9029
9030
9031
9032
9033
9034
9035
9036
9037
9038
9039
9040
9041
9042
9043
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <ctype.h>

#ifndef SQLITE_OMIT_VIRTUALTABLE

#ifndef IsAlnum
#define IsAlnum(X)  isalnum((unsigned char)X)
#endif


/* completion_vtab is a subclass of sqlite3_vtab which will
** serve as the underlying representation of a completion virtual table
*/
typedef struct completion_vtab completion_vtab;
struct completion_vtab {
  sqlite3_vtab base;  /* Base class - must be first */
9359
9360
9361
9362
9363
9364
9365
9366
9367
9368
9369
9370
9371
9372
9373
    if( pCur->nLine>0 ){
      pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
      if( pCur->zLine==0 ) return SQLITE_NOMEM;
    }
  }
  if( pCur->zLine!=0 && pCur->zPrefix==0 ){
    int i = pCur->nLine;
    while( i>0 && (isalnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){
      i--;
    }
    pCur->nPrefix = pCur->nLine - i;
    if( pCur->nPrefix>0 ){
      pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i);
      if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
    }







|







9367
9368
9369
9370
9371
9372
9373
9374
9375
9376
9377
9378
9379
9380
9381
    if( pCur->nLine>0 ){
      pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
      if( pCur->zLine==0 ) return SQLITE_NOMEM;
    }
  }
  if( pCur->zLine!=0 && pCur->zPrefix==0 ){
    int i = pCur->nLine;
    while( i>0 && (IsAlnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){
      i--;
    }
    pCur->nPrefix = pCur->nLine - i;
    if( pCur->nPrefix>0 ){
      pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i);
      if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
    }
14766
14767
14768
14769
14770
14771
14772
14773
14774
14775
14776
14777
14778
14779
14780
  }

  /* Register the auth callback with dbv */
  if( rc==SQLITE_OK ){
    sqlite3_set_authorizer(pNew->dbv, idxAuthCallback, (void*)pNew);
  }

  /* If an error has occurred, free the new object and reutrn NULL. Otherwise,
  ** return the new sqlite3expert handle.  */
  if( rc!=SQLITE_OK ){
    sqlite3_expert_destroy(pNew);
    pNew = 0;
  }
  return pNew;
}







|







14774
14775
14776
14777
14778
14779
14780
14781
14782
14783
14784
14785
14786
14787
14788
  }

  /* Register the auth callback with dbv */
  if( rc==SQLITE_OK ){
    sqlite3_set_authorizer(pNew->dbv, idxAuthCallback, (void*)pNew);
  }

  /* If an error has occurred, free the new object and return NULL. Otherwise,
  ** return the new sqlite3expert handle.  */
  if( rc!=SQLITE_OK ){
    sqlite3_expert_destroy(pNew);
    pNew = 0;
  }
  return pNew;
}
16288
16289
16290
16291
16292
16293
16294
16295
16296
16297
16298
16299
16300
16301
16302
**
** To enable all output (which is the default setting):
**
**    PRAGMA vfstrace('+all');
**
** Individual APIs can be enabled or disabled by name, with or without
** the initial "x" character.  For example, to set up for tracing lock
** primatives only:
**
**    PRAGMA vfstrace('-all, +Lock,Unlock,ShmLock');
**
** The argument to the vfstrace pragma ignores capitalization and any
** characters other than alphabetics, '+', and '-'.
*/
#include <stdlib.h>







|







16296
16297
16298
16299
16300
16301
16302
16303
16304
16305
16306
16307
16308
16309
16310
**
** To enable all output (which is the default setting):
**
**    PRAGMA vfstrace('+all');
**
** Individual APIs can be enabled or disabled by name, with or without
** the initial "x" character.  For example, to set up for tracing lock
** primitives only:
**
**    PRAGMA vfstrace('-all, +Lock,Unlock,ShmLock');
**
** The argument to the vfstrace pragma ignores capitalization and any
** characters other than alphabetics, '+', and '-'.
*/
#include <stdlib.h>
18698
18699
18700
18701
18702
18703
18704










18705
18706
18707
18708
18709
18710
18711
#endif
int sqlite3_dbdata_init(sqlite3*, char**, const sqlite3_api_routines*);

/* typedef unsigned int u32; */
/* typedef unsigned char u8; */
/* typedef sqlite3_int64 i64; */











typedef struct RecoverTable RecoverTable;
typedef struct RecoverColumn RecoverColumn;

/*
** When recovering rows of data that can be associated with table
** definitions recovered from the sqlite_schema table, each table is
** represented by an instance of the following object.







>
>
>
>
>
>
>
>
>
>







18706
18707
18708
18709
18710
18711
18712
18713
18714
18715
18716
18717
18718
18719
18720
18721
18722
18723
18724
18725
18726
18727
18728
18729
#endif
int sqlite3_dbdata_init(sqlite3*, char**, const sqlite3_api_routines*);

/* typedef unsigned int u32; */
/* typedef unsigned char u8; */
/* typedef sqlite3_int64 i64; */

/*
** Work around C99 "flex-array" syntax for pre-C99 compilers, so as
** to avoid complaints from -fsanitize=strict-bounds.
*/
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
# define FLEXARRAY
#else
# define FLEXARRAY 1
#endif

typedef struct RecoverTable RecoverTable;
typedef struct RecoverColumn RecoverColumn;

/*
** When recovering rows of data that can be associated with table
** definitions recovered from the sqlite_schema table, each table is
** represented by an instance of the following object.
18805
18806
18807
18808
18809
18810
18811
18812
18813



18814
18815
18816
18817
18818
18819
18820
**   false if it is clear:
**
**       (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0
*/
typedef struct RecoverBitmap RecoverBitmap;
struct RecoverBitmap {
  i64 nPg;                        /* Size of bitmap */
  u32 aElem[1];                   /* Array of 32-bit bitmasks */
};




/*
** State variables (part of the sqlite3_recover structure) used while
** recovering data for tables identified in the recovered schema (state
** RECOVER_STATE_WRITING).
*/
typedef struct RecoverStateW1 RecoverStateW1;







|

>
>
>







18823
18824
18825
18826
18827
18828
18829
18830
18831
18832
18833
18834
18835
18836
18837
18838
18839
18840
18841
**   false if it is clear:
**
**       (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0
*/
typedef struct RecoverBitmap RecoverBitmap;
struct RecoverBitmap {
  i64 nPg;                        /* Size of bitmap */
  u32 aElem[FLEXARRAY];           /* Array of 32-bit bitmasks */
};

/* Size in bytes of a RecoverBitmap object sufficient to cover 32 pages */
#define SZ_RECOVERBITMAP_32  (16)

/*
** State variables (part of the sqlite3_recover structure) used while
** recovering data for tables identified in the recovered schema (state
** RECOVER_STATE_WRITING).
*/
typedef struct RecoverStateW1 RecoverStateW1;
19047
19048
19049
19050
19051
19052
19053
19054
19055
19056
19057
19058
19059
19060
19061
**
** Otherwise, an attempt is made to allocate and return a bitmap object
** large enough to store a bit for all page numbers between 1 and nPg,
** inclusive. The bitmap is initially zeroed.
*/
static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){
  int nElem = (nPg+1+31) / 32;
  int nByte = sizeof(RecoverBitmap) + nElem*sizeof(u32);
  RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte);

  if( pRet ){
    pRet->nPg = nPg;
  }
  return pRet;
}







|







19068
19069
19070
19071
19072
19073
19074
19075
19076
19077
19078
19079
19080
19081
19082
**
** Otherwise, an attempt is made to allocate and return a bitmap object
** large enough to store a bit for all page numbers between 1 and nPg,
** inclusive. The bitmap is initially zeroed.
*/
static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){
  int nElem = (nPg+1+31) / 32;
  int nByte = SZ_RECOVERBITMAP_32 + nElem*sizeof(u32);
  RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte);

  if( pRet ){
    pRet->nPg = nPg;
  }
  return pRet;
}
21239
21240
21241
21242
21243
21244
21245
21246

21247
21248
21249
21250

21251
21252
21253
21254
21255
21256
21257
21258









21259

21260
21261
21262
21263
21264
21265
21266
21267
21268
21269



21270
21271
21272
21273


21274
21275
21276

21277
21278
21279
21280
21281
21282
21283
** This function does the work of a single sqlite3_recover_step() call. It
** is guaranteed that the handle is not in an error state when this
** function is called.
*/
static void recoverStep(sqlite3_recover *p){
  assert( p && p->errCode==SQLITE_OK );
  switch( p->eState ){
    case RECOVER_STATE_INIT:

      /* This is the very first call to sqlite3_recover_step() on this object.
      */
      recoverSqlCallback(p, "BEGIN");
      recoverSqlCallback(p, "PRAGMA writable_schema = on");


      recoverEnterMutex();
      recoverInstallWrapper(p);

      /* Open the output database. And register required virtual tables and 
      ** user functions with the new handle. */
      recoverOpenOutput(p);










      /* Open transactions on both the input and output databases. */

      sqlite3_file_control(p->dbIn, p->zDb, SQLITE_FCNTL_RESET_CACHE, 0);
      recoverExec(p, p->dbIn, "PRAGMA writable_schema = on");
      recoverExec(p, p->dbIn, "BEGIN");
      if( p->errCode==SQLITE_OK ) p->bCloseTransaction = 1;
      recoverExec(p, p->dbIn, "SELECT 1 FROM sqlite_schema");
      recoverTransferSettings(p);
      recoverOpenRecovery(p);
      recoverCacheSchema(p);

      recoverUninstallWrapper(p);



      recoverLeaveMutex();

      recoverExec(p, p->dbOut, "BEGIN");



      recoverWriteSchema1(p);
      p->eState = RECOVER_STATE_WRITING;
      break;

      
    case RECOVER_STATE_WRITING: {
      if( p->w1.pTbls==0 ){
        recoverWriteDataInit(p);
      }
      if( SQLITE_DONE==recoverWriteDataStep(p) ){
        recoverWriteDataCleanup(p);







|
>




>


<





>
>
>
>
>
>
>
>
>
|
>
|
|
|
|
|
|
|
|

|
>
>
>
|
|
<

>
>



>







21260
21261
21262
21263
21264
21265
21266
21267
21268
21269
21270
21271
21272
21273
21274
21275

21276
21277
21278
21279
21280
21281
21282
21283
21284
21285
21286
21287
21288
21289
21290
21291
21292
21293
21294
21295
21296
21297
21298
21299
21300
21301
21302
21303
21304
21305
21306

21307
21308
21309
21310
21311
21312
21313
21314
21315
21316
21317
21318
21319
21320
** This function does the work of a single sqlite3_recover_step() call. It
** is guaranteed that the handle is not in an error state when this
** function is called.
*/
static void recoverStep(sqlite3_recover *p){
  assert( p && p->errCode==SQLITE_OK );
  switch( p->eState ){
    case RECOVER_STATE_INIT: {
      int bUseWrapper = 1;
      /* This is the very first call to sqlite3_recover_step() on this object.
      */
      recoverSqlCallback(p, "BEGIN");
      recoverSqlCallback(p, "PRAGMA writable_schema = on");
      recoverSqlCallback(p, "PRAGMA foreign_keys = off");

      recoverEnterMutex();


      /* Open the output database. And register required virtual tables and 
      ** user functions with the new handle. */
      recoverOpenOutput(p);

      /* Attempt to open a transaction and read page 1 of the input database.
      ** Two attempts may be made - one with a wrapper installed to ensure
      ** that the database header is sane, and then if that attempt returns
      ** SQLITE_NOTADB, then again with no wrapper. The second attempt is
      ** required for encrypted databases.  */
      if( p->errCode==SQLITE_OK ){
        do{
          p->errCode = SQLITE_OK;
          if( bUseWrapper ) recoverInstallWrapper(p);

          /* Open a transaction on the input database. */
          sqlite3_file_control(p->dbIn, p->zDb, SQLITE_FCNTL_RESET_CACHE, 0);
          recoverExec(p, p->dbIn, "PRAGMA writable_schema = on");
          recoverExec(p, p->dbIn, "BEGIN");
          if( p->errCode==SQLITE_OK ) p->bCloseTransaction = 1;
          recoverExec(p, p->dbIn, "SELECT 1 FROM sqlite_schema");
          recoverTransferSettings(p);
          recoverOpenRecovery(p);
          recoverCacheSchema(p);

          if( bUseWrapper ) recoverUninstallWrapper(p);
        }while( p->errCode==SQLITE_NOTADB 
             && (bUseWrapper--) 
             && SQLITE_OK==sqlite3_exec(p->dbIn, "ROLLBACK", 0, 0, 0)
        );
      }


      recoverLeaveMutex();
      recoverExec(p, p->dbOut, "BEGIN");
      recoverWriteSchema1(p);
      p->eState = RECOVER_STATE_WRITING;
      break;
    }
      
    case RECOVER_STATE_WRITING: {
      if( p->w1.pTbls==0 ){
        recoverWriteDataInit(p);
      }
      if( SQLITE_DONE==recoverWriteDataStep(p) ){
        recoverWriteDataCleanup(p);
21608
21609
21610
21611
21612
21613
21614

21615
21616
21617
21618
21619
21620
21621
  u8 doXdgOpen;          /* Invoke start/open/xdg-open in output_reset() */
  u8 nEqpLevel;          /* Depth of the EQP output graph */
  u8 eTraceType;         /* SHELL_TRACE_* value for type of trace */
  u8 bSafeMode;          /* True to prohibit unsafe operations */
  u8 bSafeModePersist;   /* The long-term value of bSafeMode */
  u8 eRestoreState;      /* See comments above doAutoDetectRestore() */
  u8 crlfMode;           /* Do NL-to-CRLF translations when enabled (maybe) */

  ColModeOpts cmOpts;    /* Option values affecting columnar mode output */
  unsigned statsOn;      /* True to display memory stats before each finalize */
  unsigned mEqpLines;    /* Mask of vertical lines in the EQP output graph */
  int inputNesting;      /* Track nesting level of .read and other redirects */
  int outCount;          /* Revert to stdout when reaching zero */
  int cnt;               /* Number of records displayed so far */
  int lineno;            /* Line number of last line read from in */







>







21645
21646
21647
21648
21649
21650
21651
21652
21653
21654
21655
21656
21657
21658
21659
  u8 doXdgOpen;          /* Invoke start/open/xdg-open in output_reset() */
  u8 nEqpLevel;          /* Depth of the EQP output graph */
  u8 eTraceType;         /* SHELL_TRACE_* value for type of trace */
  u8 bSafeMode;          /* True to prohibit unsafe operations */
  u8 bSafeModePersist;   /* The long-term value of bSafeMode */
  u8 eRestoreState;      /* See comments above doAutoDetectRestore() */
  u8 crlfMode;           /* Do NL-to-CRLF translations when enabled (maybe) */
  u8 eEscMode;           /* Escape mode for text output */
  ColModeOpts cmOpts;    /* Option values affecting columnar mode output */
  unsigned statsOn;      /* True to display memory stats before each finalize */
  unsigned mEqpLines;    /* Mask of vertical lines in the EQP output graph */
  int inputNesting;      /* Track nesting level of .read and other redirects */
  int outCount;          /* Revert to stdout when reaching zero */
  int cnt;               /* Number of records displayed so far */
  int lineno;            /* Line number of last line read from in */
21708
21709
21710
21711
21712
21713
21714









21715
21716
21717
21718
21719
21720
21721
/* Bits in the ShellState.flgProgress variable */
#define SHELL_PROGRESS_QUIET 0x01  /* Omit announcing every progress callback */
#define SHELL_PROGRESS_RESET 0x02  /* Reset the count when the progress
                                   ** callback limit is reached, and for each
                                   ** top-level SQL statement */
#define SHELL_PROGRESS_ONCE  0x04  /* Cancel the --limit after firing once */










/*
** These are the allowed shellFlgs values
*/
#define SHFLG_Pagecache      0x00000001 /* The --pagecache option is used */
#define SHFLG_Lookaside      0x00000002 /* Lookaside memory is used */
#define SHFLG_Backslash      0x00000004 /* The --backslash option is used */
#define SHFLG_PreserveRowid  0x00000008 /* .dump preserves rowid values */







>
>
>
>
>
>
>
>
>







21746
21747
21748
21749
21750
21751
21752
21753
21754
21755
21756
21757
21758
21759
21760
21761
21762
21763
21764
21765
21766
21767
21768
/* Bits in the ShellState.flgProgress variable */
#define SHELL_PROGRESS_QUIET 0x01  /* Omit announcing every progress callback */
#define SHELL_PROGRESS_RESET 0x02  /* Reset the count when the progress
                                   ** callback limit is reached, and for each
                                   ** top-level SQL statement */
#define SHELL_PROGRESS_ONCE  0x04  /* Cancel the --limit after firing once */

/* Allowed values for ShellState.eEscMode.  The default value should
** be 0, so to change the default, reorder the names.
*/
#define SHELL_ESC_ASCII        0      /* Substitute ^Y for X where Y=X+0x40 */
#define SHELL_ESC_SYMBOL       1      /* Substitute U+2400 graphics */
#define SHELL_ESC_OFF          2      /* Send characters verbatim */

static const char *shell_EscModeNames[] = { "ascii", "symbol", "off" };

/*
** These are the allowed shellFlgs values
*/
#define SHFLG_Pagecache      0x00000001 /* The --pagecache option is used */
#define SHFLG_Lookaside      0x00000002 /* Lookaside memory is used */
#define SHFLG_Backslash      0x00000004 /* The --backslash option is used */
#define SHFLG_PreserveRowid  0x00000008 /* .dump preserves rowid values */
22045
22046
22047
22048
22049
22050
22051
22052
22053
22054
22055
22056
22057
22058

22059
22060
22061
22062
22063
22064
22065
22066
22067
22068
22069
22070
22071
22072
22073

22074
22075

22076
22077
22078



22079
22080
22081
22082
22083
22084









22085




22086



22087

22088
22089
22090





22091
22092
22093
22094

22095
22096
22097
22098
22099
22100
22101
22102
22103



22104

22105
22106
22107
22108
22109
22110
22111
22112
22113
22114
22115
22116
22117
22118
22119
22120
22121
22122
22123
22124
22125
22126
22127
22128
22129
22130
22131
22132
22133
22134
22135
22136
22137
22138
22139
22140
22141
22142
22143
22144
22145
22146
22147
22148
22149
22150
22151
22152
22153
22154
22155
22156
22157
22158
22159
22160
22161
22162
22163
22164
22165
22166
22167
22168
22169
22170
22171
22172
22173
22174
22175
22176
22177
22178
22179
22180
  zStr[i*2] = '\0';

  sqlite3_fprintf(out, "X'%s'", zStr);
  sqlite3_free(zStr);
}

/*
** Find a string that is not found anywhere in z[].  Return a pointer
** to that string.
**
** Try to use zA and zB first.  If both of those are already found in z[]
** then make up some string and store it in the buffer zBuf.
*/
static const char *unused_string(

  const char *z,                    /* Result must not appear anywhere in z */
  const char *zA, const char *zB,   /* Try these first */
  char *zBuf                        /* Space to store a generated string */
){
  unsigned i = 0;
  if( strstr(z, zA)==0 ) return zA;
  if( strstr(z, zB)==0 ) return zB;
  do{
    sqlite3_snprintf(20,zBuf,"(%s%u)", zA, i++);
  }while( strstr(z,zBuf)!=0 );
  return zBuf;
}

/*
** Output the given string as a quoted string using SQL quoting conventions.

**
** See also: output_quoted_escaped_string()

*/
static void output_quoted_string(ShellState *p, const char *z){
  int i;



  char c;
  FILE *out = p->out;
  sqlite3_fsetmode(out, _O_BINARY);
  if( z==0 ) return;
  for(i=0; (c = z[i])!=0 && c!='\''; i++){}
  if( c==0 ){









    sqlite3_fprintf(out, "'%s'",z);




  }else{



    sqlite3_fputs("'", out);

    while( *z ){
      for(i=0; (c = z[i])!=0 && c!='\''; i++){}
      if( c=='\'' ) i++;





      if( i ){
        sqlite3_fprintf(out, "%.*s", i, z);
        z += i;
      }

      if( c=='\'' ){
        sqlite3_fputs("'", out);
        continue;
      }
      if( c==0 ){
        break;
      }
      z++;
    }



    sqlite3_fputs("'", out);

  }
  setCrlfMode(p);
}

/*
** Output the given string as a quoted string using SQL quoting conventions.
** Additionallly , escape the "\n" and "\r" characters so that they do not
** get corrupted by end-of-line translation facilities in some operating
** systems.
**
** This is like output_quoted_string() but with the addition of the \r\n
** escape mechanism.
*/
static void output_quoted_escaped_string(ShellState *p, const char *z){
  int i;
  char c;
  FILE *out = p->out;
  sqlite3_fsetmode(out, _O_BINARY);
  for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){}
  if( c==0 ){
    sqlite3_fprintf(out, "'%s'",z);
  }else{
    const char *zNL = 0;
    const char *zCR = 0;
    int nNL = 0;
    int nCR = 0;
    char zBuf1[20], zBuf2[20];
    for(i=0; z[i]; i++){
      if( z[i]=='\n' ) nNL++;
      if( z[i]=='\r' ) nCR++;
    }
    if( nNL ){
      sqlite3_fputs("replace(", out);
      zNL = unused_string(z, "\\n", "\\012", zBuf1);
    }
    if( nCR ){
      sqlite3_fputs("replace(", out);
      zCR = unused_string(z, "\\r", "\\015", zBuf2);
    }
    sqlite3_fputs("'", out);
    while( *z ){
      for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){}
      if( c=='\'' ) i++;
      if( i ){
        sqlite3_fprintf(out, "%.*s", i, z);
        z += i;
      }
      if( c=='\'' ){
        sqlite3_fputs("'", out);
        continue;
      }
      if( c==0 ){
        break;
      }
      z++;
      if( c=='\n' ){
        sqlite3_fputs(zNL, out);
        continue;
      }
      sqlite3_fputs(zCR, out);
    }
    sqlite3_fputs("'", out);
    if( nCR ){
      sqlite3_fprintf(out, ",'%s',char(13))", zCR);
    }
    if( nNL ){
      sqlite3_fprintf(out, ",'%s',char(10))", zNL);
    }
  }
  setCrlfMode(p);
}

/*
** Find earliest of chars within s specified in zAny.
** With ns == ~0, is like strpbrk(s,zAny) and s must be 0-terminated.
*/







<
|

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

|
>

|

>
>
>
|



|
|
>
>
>
>
>
>
>
>
>

>
>
>
>

>
>
>
|
>

|
|
>
>
>
>
>




>

|
<
|
|
<



>
>
>
|
>














<
|
<
|
<
|
|

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
|
<
<
|
<
<
<
<
<
<
<
<
<







22092
22093
22094
22095
22096
22097
22098

22099
22100

22101

22102
22103
22104











22105
22106

22107
22108
22109
22110
22111
22112
22113
22114
22115
22116
22117
22118
22119
22120
22121
22122
22123
22124
22125
22126
22127
22128
22129
22130
22131
22132
22133
22134
22135
22136
22137
22138
22139
22140
22141
22142
22143
22144
22145
22146
22147
22148
22149
22150
22151
22152
22153
22154
22155
22156
22157

22158
22159

22160
22161
22162
22163
22164
22165
22166
22167
22168
22169
22170
22171
22172
22173
22174
22175
22176
22177
22178
22179
22180
22181

22182

22183

22184
22185
22186

















22187






22188









22189


22190









22191
22192
22193
22194
22195
22196
22197
  zStr[i*2] = '\0';

  sqlite3_fprintf(out, "X'%s'", zStr);
  sqlite3_free(zStr);
}

/*

** Output the given string as a quoted string using SQL quoting conventions:
**

**   (1)   Single quotes (') within the string are doubled

**   (2)   The whle string is enclosed in '...'
**   (3)   Control characters other than \n, \t, and \r\n are escaped
**         using \u00XX notation and if such substitutions occur,











**         the whole string is enclosed in unistr('...') instead of '...'.
**         

** Step (3) is omitted if the control-character escape mode is OFF.
**
** See also: output_quoted_escaped_string() which does the same except
** that it does not make exceptions for \n, \t, and \r\n in step (3).
*/
static void output_quoted_string(ShellState *p, const char *zInX){
  int i;
  int needUnistr = 0;
  int needDblQuote = 0;
  const unsigned char *z = (const unsigned char*)zInX;
  unsigned char c;
  FILE *out = p->out;
  sqlite3_fsetmode(out, _O_BINARY);
  if( z==0 ) return;
  for(i=0; (c = z[i])!=0; i++){
    if( c=='\'' ){ needDblQuote = 1; }
    if( c>0x1f ) continue;
    if( c=='\t' || c=='\n' ) continue;
    if( c=='\r' && z[i+1]=='\n' ) continue;
    needUnistr = 1;
    break;
  }
  if( (needDblQuote==0 && needUnistr==0)
   || (needDblQuote==0 && p->eEscMode==SHELL_ESC_OFF)
  ){
    sqlite3_fprintf(out, "'%s'",z);
  }else if( p->eEscMode==SHELL_ESC_OFF ){
    char *zEncoded = sqlite3_mprintf("%Q", z);
    sqlite3_fputs(zEncoded, out);
    sqlite3_free(zEncoded);
  }else{
    if( needUnistr ){
      sqlite3_fputs("unistr('", out);
    }else{
      sqlite3_fputs("'", out);
    }
    while( *z ){
      for(i=0; (c = z[i])!=0; i++){
        if( c=='\'' ) break;
        if( c>0x1f ) continue;
        if( c=='\t' || c=='\n' ) continue;
        if( c=='\r' && z[i+1]=='\n' ) continue;
        break;
      }
      if( i ){
        sqlite3_fprintf(out, "%.*s", i, z);
        z += i;
      }
      if( c==0 ) break;
      if( c=='\'' ){
        sqlite3_fputs("''", out);

      }else{
        sqlite3_fprintf(out, "\\u%04x", c);

      }
      z++;
    }
    if( needUnistr ){
      sqlite3_fputs("')", out);
    }else{
      sqlite3_fputs("'", out);
    }
  }
  setCrlfMode(p);
}

/*
** Output the given string as a quoted string using SQL quoting conventions.
** Additionallly , escape the "\n" and "\r" characters so that they do not
** get corrupted by end-of-line translation facilities in some operating
** systems.
**
** This is like output_quoted_string() but with the addition of the \r\n
** escape mechanism.
*/
static void output_quoted_escaped_string(ShellState *p, const char *z){

  char *zEscaped;

  sqlite3_fsetmode(p->out, _O_BINARY);

  if( p->eEscMode==SHELL_ESC_OFF ){
    zEscaped = sqlite3_mprintf("%Q", z);
  }else{

















    zEscaped = sqlite3_mprintf("%#Q", z);






  }









  sqlite3_fputs(zEscaped, p->out);


  sqlite3_free(zEscaped);









  setCrlfMode(p);
}

/*
** Find earliest of chars within s specified in zAny.
** With ns == ~0, is like strpbrk(s,zAny) and s must be 0-terminated.
*/
22315
22316
22317
22318
22319
22320
22321























































































22322
22323
22324
22325
22326
22327
22328
    }else{
      ace[1] = (char)c;
      sqlite3_fputs(ace+1, out);
    }
  }
  sqlite3_fputs(zq, out);
}
























































































/*
** Output the given string with characters that are special to
** HTML escaped.
*/
static void output_html_string(FILE *out, const char *z){
  int i;







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







22332
22333
22334
22335
22336
22337
22338
22339
22340
22341
22342
22343
22344
22345
22346
22347
22348
22349
22350
22351
22352
22353
22354
22355
22356
22357
22358
22359
22360
22361
22362
22363
22364
22365
22366
22367
22368
22369
22370
22371
22372
22373
22374
22375
22376
22377
22378
22379
22380
22381
22382
22383
22384
22385
22386
22387
22388
22389
22390
22391
22392
22393
22394
22395
22396
22397
22398
22399
22400
22401
22402
22403
22404
22405
22406
22407
22408
22409
22410
22411
22412
22413
22414
22415
22416
22417
22418
22419
22420
22421
22422
22423
22424
22425
22426
22427
22428
22429
22430
22431
22432
    }else{
      ace[1] = (char)c;
      sqlite3_fputs(ace+1, out);
    }
  }
  sqlite3_fputs(zq, out);
}

/*
** Escape the input string if it is needed and in accordance with
** eEscMode.
**
** Escaping is needed if the string contains any control characters
** other than \t, \n, and \r\n
**
** If no escaping is needed (the common case) then set *ppFree to NULL
** and return the original string.  If escapingn is needed, write the
** escaped string into memory obtained from sqlite3_malloc64() or the
** equivalent, and return the new string and set *ppFree to the new string
** as well.
**
** The caller is responsible for freeing *ppFree if it is non-NULL in order
** to reclaim memory.
*/
static const char *escapeOutput(
  ShellState *p,
  const char *zInX,
  char **ppFree
){
  i64 i, j;
  i64 nCtrl = 0;
  unsigned char *zIn;
  unsigned char c;
  unsigned char *zOut;


  /* No escaping if disabled */
  if( p->eEscMode==SHELL_ESC_OFF ){
    *ppFree = 0;
     return zInX;
  }

  /* Count the number of control characters in the string. */
  zIn = (unsigned char*)zInX;
  for(i=0; (c = zIn[i])!=0; i++){
    if( c<=0x1f
     && c!='\t'
     && c!='\n'
     && (c!='\r' || zIn[i+1]!='\n')
    ){
      nCtrl++;
    }
  }
  if( nCtrl==0 ){
    *ppFree = 0;
    return zInX;
  }
  if( p->eEscMode==SHELL_ESC_SYMBOL ) nCtrl *= 2;
  zOut = sqlite3_malloc64( i + nCtrl + 1 );
  shell_check_oom(zOut);
  for(i=j=0; (c = zIn[i])!=0; i++){
    if( c>0x1f
     || c=='\t'
     || c=='\n'
     || (c=='\r' && zIn[i+1]=='\n')
    ){
      continue;
    }
    if( i>0 ){
      memcpy(&zOut[j], zIn, i);
      j += i;
    }
    zIn += i+1;
    i = -1;
    switch( p->eEscMode ){
      case SHELL_ESC_SYMBOL: 
        zOut[j++] = 0xe2;
        zOut[j++] = 0x90;
        zOut[j++] = 0x80+c;
        break;
      case SHELL_ESC_ASCII:
        zOut[j++] = '^';
        zOut[j++] = 0x40+c;
        break;
    }
  }
  if( i>0 ){
    memcpy(&zOut[j], zIn, i);
    j += i;
  }
  zOut[j] = 0;
  *ppFree = (char*)zOut;
  return (char*)zOut;
}

/*
** Output the given string with characters that are special to
** HTML escaped.
*/
static void output_html_string(FILE *out, const char *z){
  int i;
22759
22760
22761
22762
22763
22764
22765



22766
22767

22768
22769
22770
22771
22772
22773
22774
      if( azArg==0 ) break;
      for(i=0; i<nArg; i++){
        int len = strlen30(azCol[i] ? azCol[i] : "");
        if( len>w ) w = len;
      }
      if( p->cnt++>0 ) sqlite3_fputs(p->rowSeparator, p->out);
      for(i=0; i<nArg; i++){



        sqlite3_fprintf(p->out, "%*s = %s%s", w, azCol[i],
              azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator);

      }
      break;
    }
    case MODE_ScanExp:
    case MODE_Explain: {
      static const int aExplainWidth[] = {4,       13, 4, 4, 4, 13, 2, 13};
      static const int aExplainMap[] =   {0,       1,  2, 3, 4, 5,  6, 7 };







>
>
>

|
>







22863
22864
22865
22866
22867
22868
22869
22870
22871
22872
22873
22874
22875
22876
22877
22878
22879
22880
22881
22882
      if( azArg==0 ) break;
      for(i=0; i<nArg; i++){
        int len = strlen30(azCol[i] ? azCol[i] : "");
        if( len>w ) w = len;
      }
      if( p->cnt++>0 ) sqlite3_fputs(p->rowSeparator, p->out);
      for(i=0; i<nArg; i++){
        char *pFree = 0;
        const char *pDisplay;
        pDisplay = escapeOutput(p, azArg[i] ? azArg[i] : p->nullValue, &pFree);
        sqlite3_fprintf(p->out, "%*s = %s%s", w, azCol[i],
                        pDisplay, p->rowSeparator);
        if( pFree ) sqlite3_free(pFree);
      }
      break;
    }
    case MODE_ScanExp:
    case MODE_Explain: {
      static const int aExplainWidth[] = {4,       13, 4, 4, 4, 13, 2, 13};
      static const int aExplainMap[] =   {0,       1,  2, 3, 4, 5,  6, 7 };
22830
22831
22832
22833
22834
22835
22836


22837
22838
22839
22840
22841
22842
22843
22844


22845
22846
22847
22848
22849
22850
22851
    case MODE_Pretty: {  /* .schema and .fullschema with --indent */
      char *z;
      int j;
      int nParen = 0;
      char cEnd = 0;
      char c;
      int nLine = 0;


      assert( nArg==1 );
      if( azArg[0]==0 ) break;
      if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0
       || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0
      ){
        sqlite3_fprintf(p->out, "%s;\n", azArg[0]);
        break;
      }


      z = sqlite3_mprintf("%s", azArg[0]);
      shell_check_oom(z);
      j = 0;
      for(i=0; IsSpace(z[i]); i++){}
      for(; (c = z[i])!=0; i++){
        if( IsSpace(c) ){
          if( z[j-1]=='\r' ) z[j-1] = '\n';







>
>








>
>







22938
22939
22940
22941
22942
22943
22944
22945
22946
22947
22948
22949
22950
22951
22952
22953
22954
22955
22956
22957
22958
22959
22960
22961
22962
22963
    case MODE_Pretty: {  /* .schema and .fullschema with --indent */
      char *z;
      int j;
      int nParen = 0;
      char cEnd = 0;
      char c;
      int nLine = 0;
      int isIndex;
      int isWhere = 0;
      assert( nArg==1 );
      if( azArg[0]==0 ) break;
      if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0
       || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0
      ){
        sqlite3_fprintf(p->out, "%s;\n", azArg[0]);
        break;
      }
      isIndex = sqlite3_strlike("CREATE INDEX%", azArg[0], 0)==0
             || sqlite3_strlike("CREATE UNIQUE INDEX%", azArg[0], 0)==0;
      z = sqlite3_mprintf("%s", azArg[0]);
      shell_check_oom(z);
      j = 0;
      for(i=0; IsSpace(z[i]); i++){}
      for(; (c = z[i])!=0; i++){
        if( IsSpace(c) ){
          if( z[j-1]=='\r' ) z[j-1] = '\n';
22867
22868
22869
22870
22871
22872
22873
22874
22875
22876
22877











22878
22879
22880
22881

22882
22883
22884
22885
22886
22887
22888
22889
22890
22891
22892
22893
22894
22895
22896
22897
22898



22899
22900

22901
22902
22903
22904
22905


22906

22907

22908
22909
22910
22911
22912
22913
22914
            cEnd = ']';
          }else if( c=='-' && z[i+1]=='-' ){
            cEnd = '\n';
          }else if( c=='(' ){
            nParen++;
          }else if( c==')' ){
            nParen--;
            if( nLine>0 && nParen==0 && j>0 ){
              printSchemaLineN(p->out, z, j, "\n");
              j = 0;
            }











          }
          z[j++] = c;
          if( nParen==1 && cEnd==0
           && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1)))

          ){
            if( c=='\n' ) j--;
            printSchemaLineN(p->out, z, j, "\n  ");
            j = 0;
            nLine++;
            while( IsSpace(z[i+1]) ){ i++; }
          }
        }
        z[j] = 0;
      }
      printSchemaLine(p->out, z, ";\n");
      sqlite3_free(z);
      break;
    }
    case MODE_List: {
      if( p->cnt++==0 && p->showHeader ){
        for(i=0; i<nArg; i++){



          sqlite3_fprintf(p->out, "%s%s", azCol[i],
                          i==nArg-1 ? p->rowSeparator : p->colSeparator);

        }
      }
      if( azArg==0 ) break;
      for(i=0; i<nArg; i++){
        char *z = azArg[i];


        if( z==0 ) z = p->nullValue;

        sqlite3_fputs(z, p->out);

        sqlite3_fputs((i<nArg-1)? p->colSeparator : p->rowSeparator, p->out);
      }
      break;
    }
    case MODE_Www:
    case MODE_Html: {
      if( p->cnt==0 && p->cMode==MODE_Www ){







|



>
>
>
>
>
>
>
>
>
>
>




>

















>
>
>
|

>





>
>

>
|
>







22979
22980
22981
22982
22983
22984
22985
22986
22987
22988
22989
22990
22991
22992
22993
22994
22995
22996
22997
22998
22999
23000
23001
23002
23003
23004
23005
23006
23007
23008
23009
23010
23011
23012
23013
23014
23015
23016
23017
23018
23019
23020
23021
23022
23023
23024
23025
23026
23027
23028
23029
23030
23031
23032
23033
23034
23035
23036
23037
23038
23039
23040
23041
23042
23043
23044
23045
23046
            cEnd = ']';
          }else if( c=='-' && z[i+1]=='-' ){
            cEnd = '\n';
          }else if( c=='(' ){
            nParen++;
          }else if( c==')' ){
            nParen--;
            if( nLine>0 && nParen==0 && j>0 && !isWhere ){
              printSchemaLineN(p->out, z, j, "\n");
              j = 0;
            }
          }else if( (c=='w' || c=='W')
                 && nParen==0 && isIndex
                 && sqlite3_strnicmp("WHERE",&z[i],5)==0
                 && !IsAlnum(z[i+5]) && z[i+5]!='_' ){
            isWhere = 1;
          }else if( isWhere && (c=='A' || c=='a')
                 && nParen==0
                 && sqlite3_strnicmp("AND",&z[i],3)==0
                 && !IsAlnum(z[i+3]) && z[i+3]!='_' ){
            printSchemaLineN(p->out, z, j, "\n    ");
            j = 0;
          }
          z[j++] = c;
          if( nParen==1 && cEnd==0
           && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1)))
           && !isWhere
          ){
            if( c=='\n' ) j--;
            printSchemaLineN(p->out, z, j, "\n  ");
            j = 0;
            nLine++;
            while( IsSpace(z[i+1]) ){ i++; }
          }
        }
        z[j] = 0;
      }
      printSchemaLine(p->out, z, ";\n");
      sqlite3_free(z);
      break;
    }
    case MODE_List: {
      if( p->cnt++==0 && p->showHeader ){
        for(i=0; i<nArg; i++){
          char *z = azCol[i];
          char *pFree;
          const char *zOut = escapeOutput(p, z, &pFree);
          sqlite3_fprintf(p->out, "%s%s", zOut,
                          i==nArg-1 ? p->rowSeparator : p->colSeparator);
          if( pFree ) sqlite3_free(pFree);
        }
      }
      if( azArg==0 ) break;
      for(i=0; i<nArg; i++){
        char *z = azArg[i];
        char *pFree;
        const char *zOut;
        if( z==0 ) z = p->nullValue;
        zOut = escapeOutput(p, z, &pFree);
        sqlite3_fputs(zOut, p->out);
        if( pFree ) sqlite3_free(pFree);
        sqlite3_fputs((i<nArg-1)? p->colSeparator : p->rowSeparator, p->out);
      }
      break;
    }
    case MODE_Www:
    case MODE_Html: {
      if( p->cnt==0 && p->cMode==MODE_Www ){
24019
24020
24021
24022
24023
24024
24025

24026
24027
24028
24029
24030
24031
24032
** Compute characters to display on the first line of z[].  Stop at the
** first \r, \n, or \f.  Expand \t into spaces.  Return a copy (obtained
** from malloc()) of that first line, which caller should free sometime.
** Write anything to display on the next line into *pzTail.  If this is
** the last line, write a NULL into *pzTail. (*pzTail is not allocated.)
*/
static char *translateForDisplayAndDup(

  const unsigned char *z,            /* Input text to be transformed */
  const unsigned char **pzTail,      /* OUT: Tail of the input for next line */
  int mxWidth,                       /* Max width.  0 means no limit */
  u8 bWordWrap                       /* If true, avoid breaking mid-word */
){
  int i;                 /* Input bytes consumed */
  int j;                 /* Output bytes generated */







>







24151
24152
24153
24154
24155
24156
24157
24158
24159
24160
24161
24162
24163
24164
24165
** Compute characters to display on the first line of z[].  Stop at the
** first \r, \n, or \f.  Expand \t into spaces.  Return a copy (obtained
** from malloc()) of that first line, which caller should free sometime.
** Write anything to display on the next line into *pzTail.  If this is
** the last line, write a NULL into *pzTail. (*pzTail is not allocated.)
*/
static char *translateForDisplayAndDup(
  ShellState *p,                     /* To access current settings */
  const unsigned char *z,            /* Input text to be transformed */
  const unsigned char **pzTail,      /* OUT: Tail of the input for next line */
  int mxWidth,                       /* Max width.  0 means no limit */
  u8 bWordWrap                       /* If true, avoid breaking mid-word */
){
  int i;                 /* Input bytes consumed */
  int j;                 /* Output bytes generated */
24053
24054
24055
24056
24057
24058
24059

24060
24061
24062
24063
24064
24065
24066
24067
24068



24069
24070
24071
24072
24073
24074
24075
24076
24077
24078
24079
24080
24081
24082
24083
24084
    }
    if( c>=' ' ){
      n++;
      i++;
      j++;
      continue;
    }

    if( c=='\t' ){
      do{
        n++;
        j++;
      }while( (n&7)!=0 && n<mxWidth );
      i++;
      continue;
    }
    break;



  }
  if( n>=mxWidth && bWordWrap  ){
    /* Perhaps try to back up to a better place to break the line */
    for(k=i; k>i/2; k--){
      if( isspace(z[k-1]) ) break;
    }
    if( k<=i/2 ){
      for(k=i; k>i/2; k--){
        if( isalnum(z[k-1])!=isalnum(z[k]) && (z[k]&0xc0)!=0x80 ) break;
      }
    }
    if( k<=i/2 ){
      k = i;
    }else{
      i = k;
      while( z[i]==' ' ) i++;







>








<
>
>
>




|



|







24186
24187
24188
24189
24190
24191
24192
24193
24194
24195
24196
24197
24198
24199
24200
24201

24202
24203
24204
24205
24206
24207
24208
24209
24210
24211
24212
24213
24214
24215
24216
24217
24218
24219
24220
    }
    if( c>=' ' ){
      n++;
      i++;
      j++;
      continue;
    }
    if( c==0 || c=='\n' || (c=='\r' && z[i+1]=='\n') ) break;
    if( c=='\t' ){
      do{
        n++;
        j++;
      }while( (n&7)!=0 && n<mxWidth );
      i++;
      continue;
    }

    n++;
    j += 3;
    i++;
  }
  if( n>=mxWidth && bWordWrap  ){
    /* Perhaps try to back up to a better place to break the line */
    for(k=i; k>i/2; k--){
      if( IsSpace(z[k-1]) ) break;
    }
    if( k<=i/2 ){
      for(k=i; k>i/2; k--){
        if( IsAlnum(z[k-1])!=IsAlnum(z[k]) && (z[k]&0xc0)!=0x80 ) break;
      }
    }
    if( k<=i/2 ){
      k = i;
    }else{
      i = k;
      while( z[i]==' ' ) i++;
24108
24109
24110
24111
24112
24113
24114

24115
24116
24117
24118
24119
24120
24121
24122





24123









24124
24125
24126
24127










24128
24129
24130
24131
24132
24133
24134
24135
24136
24137
24138
24139
24140
24141
24142
24143

24144
24145
24146
24147
24148
24149
24150
      continue;
    }
    if( c>=' ' ){
      n++;
      zOut[j++] = z[i++];
      continue;
    }

    if( z[i]=='\t' ){
      do{
        n++;
        zOut[j++] = ' ';
      }while( (n&7)!=0 && n<mxWidth );
      i++;
      continue;
    }





    break;









  }
  zOut[j] = 0;
  return (char*)zOut;
}











/* Extract the value of the i-th current column for pStmt as an SQL literal
** value.  Memory is obtained from sqlite3_malloc64() and must be freed by
** the caller.
*/
static char *quoted_column(sqlite3_stmt *pStmt, int i){
  switch( sqlite3_column_type(pStmt, i) ){
    case SQLITE_NULL: {
      return sqlite3_mprintf("NULL");
    }
    case SQLITE_INTEGER:
    case SQLITE_FLOAT: {
      return sqlite3_mprintf("%s",sqlite3_column_text(pStmt,i));
    }
    case SQLITE_TEXT: {
      return sqlite3_mprintf("%Q",sqlite3_column_text(pStmt,i));

    }
    case SQLITE_BLOB: {
      int j;
      sqlite3_str *pStr = sqlite3_str_new(0);
      const unsigned char *a = sqlite3_column_blob(pStmt,i);
      int n = sqlite3_column_bytes(pStmt,i);
      sqlite3_str_append(pStr, "x'", 2);







>








>
>
>
>
>
|
>
>
>
>
>
>
>
>
>




>
>
>
>
>
>
>
>
>
>















|
>







24244
24245
24246
24247
24248
24249
24250
24251
24252
24253
24254
24255
24256
24257
24258
24259
24260
24261
24262
24263
24264
24265
24266
24267
24268
24269
24270
24271
24272
24273
24274
24275
24276
24277
24278
24279
24280
24281
24282
24283
24284
24285
24286
24287
24288
24289
24290
24291
24292
24293
24294
24295
24296
24297
24298
24299
24300
24301
24302
24303
24304
24305
24306
24307
24308
24309
24310
24311
24312
      continue;
    }
    if( c>=' ' ){
      n++;
      zOut[j++] = z[i++];
      continue;
    }
    if( c==0 ) break;
    if( z[i]=='\t' ){
      do{
        n++;
        zOut[j++] = ' ';
      }while( (n&7)!=0 && n<mxWidth );
      i++;
      continue;
    }
    switch( p->eEscMode ){
      case SHELL_ESC_SYMBOL:
        zOut[j++] = 0xe2;
        zOut[j++] = 0x90;
        zOut[j++] = 0x80 + c;
        break;
      case SHELL_ESC_ASCII:
        zOut[j++] = '^';
        zOut[j++] = 0x40 + c;
        break;
      case SHELL_ESC_OFF:
        zOut[j++] = c;
        break;
    }
    i++;
  }
  zOut[j] = 0;
  return (char*)zOut;
}

/* Return true if the text string z[] contains characters that need
** unistr() escaping.
*/
static int needUnistr(const unsigned char *z){
  unsigned char c;
  if( z==0 ) return 0;
  while( (c = *z)>0x1f || c=='\t' || c=='\n' || (c=='\r' && z[1]=='\n') ){ z++; }
  return c!=0;
}

/* Extract the value of the i-th current column for pStmt as an SQL literal
** value.  Memory is obtained from sqlite3_malloc64() and must be freed by
** the caller.
*/
static char *quoted_column(sqlite3_stmt *pStmt, int i){
  switch( sqlite3_column_type(pStmt, i) ){
    case SQLITE_NULL: {
      return sqlite3_mprintf("NULL");
    }
    case SQLITE_INTEGER:
    case SQLITE_FLOAT: {
      return sqlite3_mprintf("%s",sqlite3_column_text(pStmt,i));
    }
    case SQLITE_TEXT: {
      const unsigned char *zText = sqlite3_column_text(pStmt,i);
      return sqlite3_mprintf(needUnistr(zText)?"%#Q":"%Q",zText);
    }
    case SQLITE_BLOB: {
      int j;
      sqlite3_str *pStr = sqlite3_str_new(0);
      const unsigned char *a = sqlite3_column_blob(pStmt,i);
      int n = sqlite3_column_bytes(pStmt,i);
      sqlite3_str_append(pStr, "x'", 2);
24228
24229
24230
24231
24232
24233
24234
24235
24236
24237
24238
24239
24240
24241
24242
    int wx = p->colWidth[i];
    if( wx==0 ){
      wx = p->cmOpts.iWrap;
    }
    if( wx<0 ) wx = -wx;
    uz = (const unsigned char*)sqlite3_column_name(pStmt,i);
    if( uz==0 ) uz = (u8*)"";
    azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw);
  }
  do{
    int useNextLine = bNextLine;
    bNextLine = 0;
    if( (nRow+2)*nColumn >= nAlloc ){
      nAlloc *= 2;
      azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*));







|







24390
24391
24392
24393
24394
24395
24396
24397
24398
24399
24400
24401
24402
24403
24404
    int wx = p->colWidth[i];
    if( wx==0 ){
      wx = p->cmOpts.iWrap;
    }
    if( wx<0 ) wx = -wx;
    uz = (const unsigned char*)sqlite3_column_name(pStmt,i);
    if( uz==0 ) uz = (u8*)"";
    azData[i] = translateForDisplayAndDup(p, uz, &zNotUsed, wx, bw);
  }
  do{
    int useNextLine = bNextLine;
    bNextLine = 0;
    if( (nRow+2)*nColumn >= nAlloc ){
      nAlloc *= 2;
      azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*));
24252
24253
24254
24255
24256
24257
24258

24259
24260
24261
24262
24263
24264
24265
24266
24267
24268
24269
24270
24271
24272
24273
24274
        wx = p->cmOpts.iWrap;
      }
      if( wx<0 ) wx = -wx;
      if( useNextLine ){
        uz = azNextLine[i];
        if( uz==0 ) uz = (u8*)zEmpty;
      }else if( p->cmOpts.bQuote ){

        sqlite3_free(azQuoted[i]);
        azQuoted[i] = quoted_column(pStmt,i);
        uz = (const unsigned char*)azQuoted[i];
      }else{
        uz = (const unsigned char*)sqlite3_column_text(pStmt,i);
        if( uz==0 ) uz = (u8*)zShowNull;
      }
      azData[nRow*nColumn + i]
        = translateForDisplayAndDup(uz, &azNextLine[i], wx, bw);
      if( azNextLine[i] ){
        bNextLine = 1;
        abRowDiv[nRow-1] = 0;
        bMultiLineRowExists = 1;
      }
    }
  }while( bNextLine || sqlite3_step(pStmt)==SQLITE_ROW );







>








|







24414
24415
24416
24417
24418
24419
24420
24421
24422
24423
24424
24425
24426
24427
24428
24429
24430
24431
24432
24433
24434
24435
24436
24437
        wx = p->cmOpts.iWrap;
      }
      if( wx<0 ) wx = -wx;
      if( useNextLine ){
        uz = azNextLine[i];
        if( uz==0 ) uz = (u8*)zEmpty;
      }else if( p->cmOpts.bQuote ){
        assert( azQuoted!=0 );
        sqlite3_free(azQuoted[i]);
        azQuoted[i] = quoted_column(pStmt,i);
        uz = (const unsigned char*)azQuoted[i];
      }else{
        uz = (const unsigned char*)sqlite3_column_text(pStmt,i);
        if( uz==0 ) uz = (u8*)zShowNull;
      }
      azData[nRow*nColumn + i]
        = translateForDisplayAndDup(p, uz, &azNextLine[i], wx, bw);
      if( azNextLine[i] ){
        bNextLine = 1;
        abRowDiv[nRow-1] = 0;
        bMultiLineRowExists = 1;
      }
    }
  }while( bNextLine || sqlite3_step(pStmt)==SQLITE_ROW );
25183
25184
25185
25186
25187
25188
25189
25190
25191
25192
25193
25194
25195
25196
25197
25198
25199
25200
25201
25202
25203
25204
25205
25206
25207

25208
25209
25210
25211
25212
25213
25214
  ".load FILE ?ENTRY?       Load an extension library",
#endif
#if !defined(SQLITE_SHELL_FIDDLE)
  ".log FILE|on|off         Turn logging on or off.  FILE can be stderr/stdout",
#else
  ".log on|off              Turn logging on or off.",
#endif
  ".mode MODE ?OPTIONS?     Set output mode",
  "   MODE is one of:",
  "     ascii       Columns/rows delimited by 0x1F and 0x1E",
  "     box         Tables using unicode box-drawing characters",
  "     csv         Comma-separated values",
  "     column      Output in columns.  (See .width)",
  "     html        HTML <table> code",
  "     insert      SQL insert statements for TABLE",
  "     json        Results in a JSON array",
  "     line        One value per line",
  "     list        Values delimited by \"|\"",
  "     markdown    Markdown table format",
  "     qbox        Shorthand for \"box --wrap 60 --quote\"",
  "     quote       Escape answers as for SQL",
  "     table       ASCII-art table",
  "     tabs        Tab-separated values",
  "     tcl         TCL list elements",
  "   OPTIONS: (for columnar modes or insert mode):",

  "     --wrap N       Wrap output lines to no longer than N characters",
  "     --wordwrap B   Wrap or not at word boundaries per B (on/off)",
  "     --ww           Shorthand for \"--wordwrap 1\"",
  "     --quote        Quote output text as SQL literals",
  "     --noquote      Do not quote output text",
  "     TABLE          The name of SQL table used for \"insert\" mode",
#ifndef SQLITE_SHELL_FIDDLE







|

















>







25346
25347
25348
25349
25350
25351
25352
25353
25354
25355
25356
25357
25358
25359
25360
25361
25362
25363
25364
25365
25366
25367
25368
25369
25370
25371
25372
25373
25374
25375
25376
25377
25378
  ".load FILE ?ENTRY?       Load an extension library",
#endif
#if !defined(SQLITE_SHELL_FIDDLE)
  ".log FILE|on|off         Turn logging on or off.  FILE can be stderr/stdout",
#else
  ".log on|off              Turn logging on or off.",
#endif
  ".mode ?MODE? ?OPTIONS?   Set output mode",
  "   MODE is one of:",
  "     ascii       Columns/rows delimited by 0x1F and 0x1E",
  "     box         Tables using unicode box-drawing characters",
  "     csv         Comma-separated values",
  "     column      Output in columns.  (See .width)",
  "     html        HTML <table> code",
  "     insert      SQL insert statements for TABLE",
  "     json        Results in a JSON array",
  "     line        One value per line",
  "     list        Values delimited by \"|\"",
  "     markdown    Markdown table format",
  "     qbox        Shorthand for \"box --wrap 60 --quote\"",
  "     quote       Escape answers as for SQL",
  "     table       ASCII-art table",
  "     tabs        Tab-separated values",
  "     tcl         TCL list elements",
  "   OPTIONS: (for columnar modes or insert mode):",
  "     --escape T     ctrl-char escape; T is one of: symbol, ascii, off",
  "     --wrap N       Wrap output lines to no longer than N characters",
  "     --wordwrap B   Wrap or not at word boundaries per B (on/off)",
  "     --ww           Shorthand for \"--wordwrap 1\"",
  "     --quote        Quote output text as SQL literals",
  "     --noquote      Do not quote output text",
  "     TABLE          The name of SQL table used for \"insert\" mode",
#ifndef SQLITE_SHELL_FIDDLE
25976
25977
25978
25979
25980
25981
25982
25983
25984
25985
25986
25987
25988
25989
25990
  char zBuf[1000];

#if HAVE_LINENOISE==2
  UNUSED_PARAMETER(pUserData);
#endif
  if( nLine>(i64)sizeof(zBuf)-30 ) return;
  if( zLine[0]=='.' || zLine[0]=='#') return;
  for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){}
  if( i==nLine-1 ) return;
  iStart = i+1;
  memcpy(zBuf, zLine, iStart);
  zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase"
                         "  FROM completion(%Q,%Q) ORDER BY 1",
                         &zLine[iStart], zLine);
  shell_check_oom(zSql);







|







26140
26141
26142
26143
26144
26145
26146
26147
26148
26149
26150
26151
26152
26153
26154
  char zBuf[1000];

#if HAVE_LINENOISE==2
  UNUSED_PARAMETER(pUserData);
#endif
  if( nLine>(i64)sizeof(zBuf)-30 ) return;
  if( zLine[0]=='.' || zLine[0]=='#') return;
  for(i=nLine-1; i>=0 && (IsAlnum(zLine[i]) || zLine[i]=='_'); i--){}
  if( i==nLine-1 ) return;
  iStart = i+1;
  memcpy(zBuf, zLine, iStart);
  zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase"
                         "  FROM completion(%Q,%Q) ORDER BY 1",
                         &zLine[iStart], zLine);
  shell_check_oom(zSql);
28216
28217
28218
28219
28220
28221
28222

28223
28224
28225
28226
28227
28228
28229
  );

  sqlite3_recover_config(p, 789, (void*)zRecoveryDb);  /* Debug use only */
  sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF);
  sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids);
  sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist);


  sqlite3_recover_run(p);
  if( sqlite3_recover_errcode(p)!=SQLITE_OK ){
    const char *zErr = sqlite3_recover_errmsg(p);
    int errCode = sqlite3_recover_errcode(p);
    sqlite3_fprintf(stderr,"sql error: %s (%d)\n", zErr, errCode);
  }
  rc = sqlite3_recover_finish(p);







>







28380
28381
28382
28383
28384
28385
28386
28387
28388
28389
28390
28391
28392
28393
28394
  );

  sqlite3_recover_config(p, 789, (void*)zRecoveryDb);  /* Debug use only */
  sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF);
  sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids);
  sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist);

  sqlite3_fprintf(pState->out, ".dbconfig defensive off\n");
  sqlite3_recover_run(p);
  if( sqlite3_recover_errcode(p)!=SQLITE_OK ){
    const char *zErr = sqlite3_recover_errmsg(p);
    int errCode = sqlite3_recover_errcode(p);
    sqlite3_fprintf(stderr,"sql error: %s (%d)\n", zErr, errCode);
  }
  rc = sqlite3_recover_finish(p);
29941
29942
29943
29944
29945
29946
29947

29948
29949
29950
29951
29952

29953
29954

29955
29956

29957
29958

29959
29960






















29961
29962
29963
29964
29965

29966
29967
29968
29969
29970
29971
29972
29973
29974
29975

29976
29977
29978
29979
29980
29981
29982
29983
29984
29985
29986
29987
29988
29989
29990
29991
29992
29993
29994

29995
29996
29997


29998
29999
30000



30001


30002

30003
30004
30005
30006
30007
30008
30009
    }
  }else

  if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){
    const char *zMode = 0;
    const char *zTabname = 0;
    int i, n2;

    ColModeOpts cmOpts = ColModeOpts_default;
    for(i=1; i<nArg; i++){
      const char *z = azArg[i];
      if( optionMatch(z,"wrap") && i+1<nArg ){
        cmOpts.iWrap = integerValue(azArg[++i]);

      }else if( optionMatch(z,"ww") ){
        cmOpts.bWordWrap = 1;

      }else if( optionMatch(z,"wordwrap") && i+1<nArg ){
        cmOpts.bWordWrap = (u8)booleanValue(azArg[++i]);

      }else if( optionMatch(z,"quote") ){
        cmOpts.bQuote = 1;

      }else if( optionMatch(z,"noquote") ){
        cmOpts.bQuote = 0;






















      }else if( zMode==0 ){
        zMode = z;
        /* Apply defaults for qbox pseudo-mode.  If that
         * overwrites already-set values, user was informed of this.
         */

        if( cli_strcmp(z, "qbox")==0 ){
          ColModeOpts cmo = ColModeOpts_default_qbox;
          zMode = "box";
          cmOpts = cmo;
        }
      }else if( zTabname==0 ){
        zTabname = z;
      }else if( z[0]=='-' ){
        sqlite3_fprintf(stderr,"unknown option: %s\n", z);
        eputz("options:\n"

              "  --noquote\n"
              "  --quote\n"
              "  --wordwrap on/off\n"
              "  --wrap N\n"
              "  --ww\n");
        rc = 1;
        goto meta_command_exit;
      }else{
        sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z);
        rc = 1;
        goto meta_command_exit;
      }
    }
    if( zMode==0 ){
      if( p->mode==MODE_Column
       || (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
      ){
        sqlite3_fprintf(p->out,
              "current output mode: %s --wrap %d --wordwrap %s --%squote\n",

              modeDescr[p->mode], p->cmOpts.iWrap,
              p->cmOpts.bWordWrap ? "on" : "off",
              p->cmOpts.bQuote ? "" : "no");


      }else{
        sqlite3_fprintf(p->out,
              "current output mode: %s\n", modeDescr[p->mode]);



      }


      zMode = modeDescr[p->mode];

    }
    n2 = strlen30(zMode);
    if( cli_strncmp(zMode,"lines",n2)==0 ){
      p->mode = MODE_Line;
      sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
    }else if( cli_strncmp(zMode,"columns",n2)==0 ){
      p->mode = MODE_Column;







>





>


>


>


>


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





>










>













|




|
>


|
>
>


|
>
>
>

>
>

>







30106
30107
30108
30109
30110
30111
30112
30113
30114
30115
30116
30117
30118
30119
30120
30121
30122
30123
30124
30125
30126
30127
30128
30129
30130
30131
30132
30133
30134
30135
30136
30137
30138
30139
30140
30141
30142
30143
30144
30145
30146
30147
30148
30149
30150
30151
30152
30153
30154
30155
30156
30157
30158
30159
30160
30161
30162
30163
30164
30165
30166
30167
30168
30169
30170
30171
30172
30173
30174
30175
30176
30177
30178
30179
30180
30181
30182
30183
30184
30185
30186
30187
30188
30189
30190
30191
30192
30193
30194
30195
30196
30197
30198
30199
30200
30201
30202
30203
30204
30205
30206
30207
30208
30209
30210
30211
30212
    }
  }else

  if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){
    const char *zMode = 0;
    const char *zTabname = 0;
    int i, n2;
    int chng = 0;       /* 0x01:  change to cmopts.  0x02:  Any other change */
    ColModeOpts cmOpts = ColModeOpts_default;
    for(i=1; i<nArg; i++){
      const char *z = azArg[i];
      if( optionMatch(z,"wrap") && i+1<nArg ){
        cmOpts.iWrap = integerValue(azArg[++i]);
        chng |= 1;
      }else if( optionMatch(z,"ww") ){
        cmOpts.bWordWrap = 1;
        chng |= 1;
      }else if( optionMatch(z,"wordwrap") && i+1<nArg ){
        cmOpts.bWordWrap = (u8)booleanValue(azArg[++i]);
        chng |= 1;
      }else if( optionMatch(z,"quote") ){
        cmOpts.bQuote = 1;
        chng |= 1;
      }else if( optionMatch(z,"noquote") ){
        cmOpts.bQuote = 0;
        chng |= 1;
      }else if( optionMatch(z,"escape") && i+1<nArg ){
        /* See similar code at tag-20250224-1 */
        const char *zEsc = azArg[++i];
        int k;
        for(k=0; k<ArraySize(shell_EscModeNames); k++){
          if( sqlite3_stricmp(zEsc,shell_EscModeNames[k])==0 ){
            p->eEscMode = k;
            chng |= 2;
            break;
          }
        }
        if( k>=ArraySize(shell_EscModeNames) ){
          sqlite3_fprintf(stderr, "unknown control character escape mode \"%s\""
                                  " - choices:", zEsc);
          for(k=0; k<ArraySize(shell_EscModeNames); k++){
            sqlite3_fprintf(stderr, " %s", shell_EscModeNames[k]);
          }
          sqlite3_fprintf(stderr, "\n");
          rc = 1;
          goto meta_command_exit;
        }
      }else if( zMode==0 ){
        zMode = z;
        /* Apply defaults for qbox pseudo-mode.  If that
         * overwrites already-set values, user was informed of this.
         */
        chng |= 1;
        if( cli_strcmp(z, "qbox")==0 ){
          ColModeOpts cmo = ColModeOpts_default_qbox;
          zMode = "box";
          cmOpts = cmo;
        }
      }else if( zTabname==0 ){
        zTabname = z;
      }else if( z[0]=='-' ){
        sqlite3_fprintf(stderr,"unknown option: %s\n", z);
        eputz("options:\n"
              "  --escape MODE\n"
              "  --noquote\n"
              "  --quote\n"
              "  --wordwrap on/off\n"
              "  --wrap N\n"
              "  --ww\n");
        rc = 1;
        goto meta_command_exit;
      }else{
        sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z);
        rc = 1;
        goto meta_command_exit;
      }
    }
    if( !chng ){
      if( p->mode==MODE_Column
       || (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
      ){
        sqlite3_fprintf(p->out,
              "current output mode: %s --wrap %d --wordwrap %s "
              "--%squote --escape %s\n",
              modeDescr[p->mode], p->cmOpts.iWrap,
              p->cmOpts.bWordWrap ? "on" : "off",
              p->cmOpts.bQuote ? "" : "no",
              shell_EscModeNames[p->eEscMode]
        );
      }else{
        sqlite3_fprintf(p->out,
              "current output mode: %s --escape %s\n",
              modeDescr[p->mode],
              shell_EscModeNames[p->eEscMode]
        );
      }
    }
    if( zMode==0 ){
      zMode = modeDescr[p->mode];
      if( (chng&1)==0 ) cmOpts = p->cmOpts;
    }
    n2 = strlen30(zMode);
    if( cli_strncmp(zMode,"lines",n2)==0 ){
      p->mode = MODE_Line;
      sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
    }else if( cli_strncmp(zMode,"columns",n2)==0 ){
      p->mode = MODE_Column;
30028
30029
30030
30031
30032
30033
30034





30035
30036
30037
30038
30039
30040
30041
      sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf);
    }else if( cli_strncmp(zMode,"tabs",n2)==0 ){
      p->mode = MODE_List;
      sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab);
    }else if( cli_strncmp(zMode,"insert",n2)==0 ){
      p->mode = MODE_Insert;
      set_table_name(p, zTabname ? zTabname : "table");





    }else if( cli_strncmp(zMode,"quote",n2)==0 ){
      p->mode = MODE_Quote;
      sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
      sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
    }else if( cli_strncmp(zMode,"ascii",n2)==0 ){
      p->mode = MODE_Ascii;
      sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit);







>
>
>
>
>







30231
30232
30233
30234
30235
30236
30237
30238
30239
30240
30241
30242
30243
30244
30245
30246
30247
30248
30249
      sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf);
    }else if( cli_strncmp(zMode,"tabs",n2)==0 ){
      p->mode = MODE_List;
      sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab);
    }else if( cli_strncmp(zMode,"insert",n2)==0 ){
      p->mode = MODE_Insert;
      set_table_name(p, zTabname ? zTabname : "table");
      if( p->eEscMode==SHELL_ESC_OFF ){
        ShellSetFlag(p, SHFLG_Newlines);
      }else{
        ShellClearFlag(p, SHFLG_Newlines);
      }
    }else if( cli_strncmp(zMode,"quote",n2)==0 ){
      p->mode = MODE_Quote;
      sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
      sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
    }else if( cli_strncmp(zMode,"ascii",n2)==0 ){
      p->mode = MODE_Ascii;
      sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit);
32264
32265
32266
32267
32268
32269
32270
32271
32272
32273
32274
32275
32276
32277
32278
}

/*
** The CLI needs a working sqlite3_complete() to work properly.  So error
** out of the build if compiling with SQLITE_OMIT_COMPLETE.
*/
#ifdef SQLITE_OMIT_COMPLETE
# error the CLI application is imcompatable with SQLITE_OMIT_COMPLETE.
#endif

/*
** Return true if zSql is a complete SQL statement.  Return false if it
** ends in the middle of a string literal or C-style comment.
*/
static int line_is_complete(char *zSql, int nSql){







|







32472
32473
32474
32475
32476
32477
32478
32479
32480
32481
32482
32483
32484
32485
32486
}

/*
** The CLI needs a working sqlite3_complete() to work properly.  So error
** out of the build if compiling with SQLITE_OMIT_COMPLETE.
*/
#ifdef SQLITE_OMIT_COMPLETE
# error the CLI application is incompatible with SQLITE_OMIT_COMPLETE.
#endif

/*
** Return true if zSql is a complete SQL statement.  Return false if it
** ends in the middle of a string literal or C-style comment.
*/
static int line_is_complete(char *zSql, int nSql){
32443
32444
32445
32446
32447
32448
32449
32450
32451
32452
32453
32454
32455
32456
32457
  i64 nZ = 0;

  UNUSED_PARAMETER(in);
  UNUSED_PARAMETER(isContinuation);
  if(!z || !*z){
    return 0;
  }
  while(*z && isspace(*z)) ++z;
  zBegin = z;
  for(; *z && '\n'!=*z; ++nZ, ++z){}
  if(nZ>0 && '\r'==zBegin[nZ-1]){
    --nZ;
  }
  shellState.wasm.zPos = z;
  zLine = realloc(zPrior, nZ+1);







|







32651
32652
32653
32654
32655
32656
32657
32658
32659
32660
32661
32662
32663
32664
32665
  i64 nZ = 0;

  UNUSED_PARAMETER(in);
  UNUSED_PARAMETER(isContinuation);
  if(!z || !*z){
    return 0;
  }
  while(*z && IsSpace(*z)) ++z;
  zBegin = z;
  for(; *z && '\n'!=*z; ++nZ, ++z){}
  if(nZ>0 && '\r'==zBegin[nZ-1]){
    --nZ;
  }
  shellState.wasm.zPos = z;
  zLine = realloc(zPrior, nZ+1);
32748
32749
32750
32751
32752
32753
32754

32755
32756
32757
32758
32759
32760
32761
  "   -column              set output mode to 'column'\n"
  "   -cmd COMMAND         run \"COMMAND\" before reading stdin\n"
  "   -csv                 set output mode to 'csv'\n"
#if !defined(SQLITE_OMIT_DESERIALIZE)
  "   -deserialize         open the database using sqlite3_deserialize()\n"
#endif
  "   -echo                print inputs before execution\n"

  "   -init FILENAME       read/process named file\n"
  "   -[no]header          turn headers on or off\n"
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
  "   -heap SIZE           Size of heap for memsys3 or memsys5\n"
#endif
  "   -help                show this message\n"
  "   -html                set output mode to HTML\n"







>







32956
32957
32958
32959
32960
32961
32962
32963
32964
32965
32966
32967
32968
32969
32970
  "   -column              set output mode to 'column'\n"
  "   -cmd COMMAND         run \"COMMAND\" before reading stdin\n"
  "   -csv                 set output mode to 'csv'\n"
#if !defined(SQLITE_OMIT_DESERIALIZE)
  "   -deserialize         open the database using sqlite3_deserialize()\n"
#endif
  "   -echo                print inputs before execution\n"
  "   -escape T            ctrl-char escape; T is one of: symbol, ascii, off\n"
  "   -init FILENAME       read/process named file\n"
  "   -[no]header          turn headers on or off\n"
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
  "   -heap SIZE           Size of heap for memsys3 or memsys5\n"
#endif
  "   -help                show this message\n"
  "   -html                set output mode to HTML\n"
32795
32796
32797
32798
32799
32800
32801
32802
32803
32804
32805
32806
32807
32808
32809
  "   -vfs NAME            use NAME as the default VFS\n"
  "   -vfstrace            enable tracing of all VFS calls\n"
#ifdef SQLITE_HAVE_ZLIB
  "   -zip                 open the file as a ZIP Archive\n"
#endif
;
static void usage(int showDetail){
  sqlite3_fprintf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL]]\n"
       "FILENAME is the name of an SQLite database. A new database is created\n"
       "if the file does not previously exist. Defaults to :memory:.\n", Argv0);
  if( showDetail ){
    sqlite3_fprintf(stderr,"OPTIONS include:\n%s", zOptions);
  }else{
    eputz("Use the -help option for additional information\n");
  }







|







33004
33005
33006
33007
33008
33009
33010
33011
33012
33013
33014
33015
33016
33017
33018
  "   -vfs NAME            use NAME as the default VFS\n"
  "   -vfstrace            enable tracing of all VFS calls\n"
#ifdef SQLITE_HAVE_ZLIB
  "   -zip                 open the file as a ZIP Archive\n"
#endif
;
static void usage(int showDetail){
  sqlite3_fprintf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL...]]\n"
       "FILENAME is the name of an SQLite database. A new database is created\n"
       "if the file does not previously exist. Defaults to :memory:.\n", Argv0);
  if( showDetail ){
    sqlite3_fprintf(stderr,"OPTIONS include:\n%s", zOptions);
  }else{
    eputz("Use the -help option for additional information\n");
  }
33181
33182
33183
33184
33185
33186
33187



33188
33189
33190
33191
33192
33193
33194
    }else if( cli_strcmp(z,"-nonce")==0 ){
      free(data.zNonce);
      data.zNonce = strdup(cmdline_option_value(argc, argv, ++i));
    }else if( cli_strcmp(z,"-unsafe-testing")==0 ){
      ShellSetFlag(&data,SHFLG_TestingMode);
    }else if( cli_strcmp(z,"-safe")==0 ){
      /* no-op - catch this on the second pass */



    }
  }
#ifndef SQLITE_SHELL_FIDDLE
  if( !bEnableVfstrace ) verify_uninitialized();
#endif









>
>
>







33390
33391
33392
33393
33394
33395
33396
33397
33398
33399
33400
33401
33402
33403
33404
33405
33406
    }else if( cli_strcmp(z,"-nonce")==0 ){
      free(data.zNonce);
      data.zNonce = strdup(cmdline_option_value(argc, argv, ++i));
    }else if( cli_strcmp(z,"-unsafe-testing")==0 ){
      ShellSetFlag(&data,SHFLG_TestingMode);
    }else if( cli_strcmp(z,"-safe")==0 ){
      /* no-op - catch this on the second pass */
    }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){
      /* skip over the argument */
      i++;
    }
  }
#ifndef SQLITE_SHELL_FIDDLE
  if( !bEnableVfstrace ) verify_uninitialized();
#endif


33280
33281
33282
33283
33284
33285
33286



















33287
33288
33289
33290
33291
33292
33293
    }else if( cli_strcmp(z,"-table")==0 ){
      data.mode = MODE_Table;
    }else if( cli_strcmp(z,"-box")==0 ){
      data.mode = MODE_Box;
    }else if( cli_strcmp(z,"-csv")==0 ){
      data.mode = MODE_Csv;
      memcpy(data.colSeparator,",",2);



















#ifdef SQLITE_HAVE_ZLIB
    }else if( cli_strcmp(z,"-zip")==0 ){
      data.openMode = SHELL_OPEN_ZIPFILE;
#endif
    }else if( cli_strcmp(z,"-append")==0 ){
      data.openMode = SHELL_OPEN_APPENDVFS;
#ifndef SQLITE_OMIT_DESERIALIZE







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







33492
33493
33494
33495
33496
33497
33498
33499
33500
33501
33502
33503
33504
33505
33506
33507
33508
33509
33510
33511
33512
33513
33514
33515
33516
33517
33518
33519
33520
33521
33522
33523
33524
    }else if( cli_strcmp(z,"-table")==0 ){
      data.mode = MODE_Table;
    }else if( cli_strcmp(z,"-box")==0 ){
      data.mode = MODE_Box;
    }else if( cli_strcmp(z,"-csv")==0 ){
      data.mode = MODE_Csv;
      memcpy(data.colSeparator,",",2);
    }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){
      /* See similar code at tag-20250224-1 */
      const char *zEsc = argv[++i];
      int k;
      for(k=0; k<ArraySize(shell_EscModeNames); k++){
        if( sqlite3_stricmp(zEsc,shell_EscModeNames[k])==0 ){
          data.eEscMode = k;
          break;
        }
      }
      if( k>=ArraySize(shell_EscModeNames) ){
        sqlite3_fprintf(stderr, "unknown control character escape mode \"%s\""
                                " - choices:", zEsc);
        for(k=0; k<ArraySize(shell_EscModeNames); k++){
          sqlite3_fprintf(stderr, " %s", shell_EscModeNames[k]);
        }
        sqlite3_fprintf(stderr, "\n");
        exit(1);
      }
#ifdef SQLITE_HAVE_ZLIB
    }else if( cli_strcmp(z,"-zip")==0 ){
      data.openMode = SHELL_OPEN_ZIPFILE;
#endif
    }else if( cli_strcmp(z,"-append")==0 ){
      data.openMode = SHELL_OPEN_APPENDVFS;
#ifndef SQLITE_OMIT_DESERIALIZE
33441
33442
33443
33444
33445
33446
33447

33448
33449
33450
33451
33452
33453
33454
33455
33456
33457
33458
33459
33460
33461
33462
33463

  if( !readStdin ){
    /* Run all arguments that do not begin with '-' as if they were separate
    ** command-line inputs, except for the argToSkip argument which contains
    ** the database filename.
    */
    for(i=0; i<nCmd; i++){

      if( azCmd[i][0]=='.' ){
        rc = do_meta_command(azCmd[i], &data);
        if( rc ){
          if( rc==2 ) rc = 0;
          goto shell_main_exit;
        }
      }else{
        open_db(&data, 0);
        echo_group_input(&data, azCmd[i]);
        rc = shell_exec(&data, azCmd[i], &zErrMsg);
        if( zErrMsg || rc ){
          if( zErrMsg!=0 ){
            shellEmitError(zErrMsg);
          }else{
            sqlite3_fprintf(stderr,
                            "Error: unable to process SQL: %s\n", azCmd[i]);







>








<







33672
33673
33674
33675
33676
33677
33678
33679
33680
33681
33682
33683
33684
33685
33686
33687

33688
33689
33690
33691
33692
33693
33694

  if( !readStdin ){
    /* Run all arguments that do not begin with '-' as if they were separate
    ** command-line inputs, except for the argToSkip argument which contains
    ** the database filename.
    */
    for(i=0; i<nCmd; i++){
      echo_group_input(&data, azCmd[i]);
      if( azCmd[i][0]=='.' ){
        rc = do_meta_command(azCmd[i], &data);
        if( rc ){
          if( rc==2 ) rc = 0;
          goto shell_main_exit;
        }
      }else{
        open_db(&data, 0);

        rc = shell_exec(&data, azCmd[i], &zErrMsg);
        if( zErrMsg || rc ){
          if( zErrMsg!=0 ){
            shellEmitError(zErrMsg);
          }else{
            sqlite3_fprintf(stderr,
                            "Error: unable to process SQL: %s\n", azCmd[i]);
Changes to extsrc/sqlite3.c.
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
** the text of this file.  Search for "Begin file sqlite3.h" to find the start
** of the embedded sqlite3.h header file.) Additional code files may be needed
** if you want a wrapper to interface SQLite with your choice of programming
** language. The code for the "sqlite3" command-line shell is also in a
** separate file. This file contains only code for the core SQLite library.
**
** The content in this amalgamation comes from Fossil check-in
** 57caa3136d1bfca06e4f2285734a4977b8d3 with changes in files:
**
**    
*/
#ifndef SQLITE_AMALGAMATION
#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
#ifndef SQLITE_PRIVATE







|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
** the text of this file.  Search for "Begin file sqlite3.h" to find the start
** of the embedded sqlite3.h header file.) Additional code files may be needed
** if you want a wrapper to interface SQLite with your choice of programming
** language. The code for the "sqlite3" command-line shell is also in a
** separate file. This file contains only code for the core SQLite library.
**
** The content in this amalgamation comes from Fossil check-in
** 18bda13e197e4b4ec7464b3e70012f71edc0 with changes in files:
**
**    
*/
#ifndef SQLITE_AMALGAMATION
#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
#ifndef SQLITE_PRIVATE
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.50.0"
#define SQLITE_VERSION_NUMBER 3050000
#define SQLITE_SOURCE_ID      "2025-02-18 01:16:26 57caa3136d1bfca06e4f2285734a4977b8d3fa1f75bf87453b975867e9de38fc"

/*
** CAPI3REF: Run-Time Library Version Numbers
** KEYWORDS: sqlite3_version sqlite3_sourceid
**
** These interfaces provide the same information as the [SQLITE_VERSION],
** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros







|







463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.50.0"
#define SQLITE_VERSION_NUMBER 3050000
#define SQLITE_SOURCE_ID      "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185"

/*
** CAPI3REF: Run-Time Library Version Numbers
** KEYWORDS: sqlite3_version sqlite3_sourceid
**
** These interfaces provide the same information as the [SQLITE_VERSION],
** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
1477
1478
1479
1480
1481
1482
1483






1484
1485
1486
1487
1488
1489
1490
** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS
** to block for up to M milliseconds before failing when attempting to
** obtain a file lock using the xLock or xShmLock methods of the VFS.
** The parameter is a pointer to a 32-bit signed integer that contains
** the value that M is to be set to. Before returning, the 32-bit signed
** integer is overwritten with the previous value of M.






**
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
** a database file.  The argument is a pointer to a 32-bit unsigned integer.
** The "data version" for the pager is written into the pointer.  The
** "data version" changes whenever any change occurs to the corresponding
** database file, either through SQL statements on the same database







>
>
>
>
>
>







1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS
** to block for up to M milliseconds before failing when attempting to
** obtain a file lock using the xLock or xShmLock methods of the VFS.
** The parameter is a pointer to a 32-bit signed integer that contains
** the value that M is to be set to. Before returning, the 32-bit signed
** integer is overwritten with the previous value of M.
**
** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]]
** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the
** VFS to block when taking a SHARED lock to connect to a wal mode database.
** This is used to implement the functionality associated with
** SQLITE_SETLK_BLOCK_ON_CONNECT.
**
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
** a database file.  The argument is a pointer to a 32-bit unsigned integer.
** The "data version" for the pager is written into the pointer.  The
** "data version" changes whenever any change occurs to the corresponding
** database file, either through SQL statements on the same database
1574
1575
1576
1577
1578
1579
1580

1581
1582
1583
1584
1585
1586
1587
#define SQLITE_FCNTL_CKPT_DONE              37
#define SQLITE_FCNTL_RESERVE_BYTES          38
#define SQLITE_FCNTL_CKPT_START             39
#define SQLITE_FCNTL_EXTERNAL_READER        40
#define SQLITE_FCNTL_CKSM_FILE              41
#define SQLITE_FCNTL_RESET_CACHE            42
#define SQLITE_FCNTL_NULL_IO                43


/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE      SQLITE_FCNTL_GET_LOCKPROXYFILE
#define SQLITE_SET_LOCKPROXYFILE      SQLITE_FCNTL_SET_LOCKPROXYFILE
#define SQLITE_LAST_ERRNO             SQLITE_FCNTL_LAST_ERRNO









>







1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
#define SQLITE_FCNTL_CKPT_DONE              37
#define SQLITE_FCNTL_RESERVE_BYTES          38
#define SQLITE_FCNTL_CKPT_START             39
#define SQLITE_FCNTL_EXTERNAL_READER        40
#define SQLITE_FCNTL_CKSM_FILE              41
#define SQLITE_FCNTL_RESET_CACHE            42
#define SQLITE_FCNTL_NULL_IO                43
#define SQLITE_FCNTL_BLOCK_ON_CONNECT       44

/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE      SQLITE_FCNTL_GET_LOCKPROXYFILE
#define SQLITE_SET_LOCKPROXYFILE      SQLITE_FCNTL_SET_LOCKPROXYFILE
#define SQLITE_LAST_ERRNO             SQLITE_FCNTL_LAST_ERRNO


3330
3331
3332
3333
3334
3335
3336






































3337
3338
3339
3340
3341
3342
3343
** was defined  (using [sqlite3_busy_handler()]) prior to calling
** this routine, that other busy handler is cleared.)^
**
** See also:  [PRAGMA busy_timeout]
*/
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);







































/*
** CAPI3REF: Convenience Routines For Running Queries
** METHOD: sqlite3
**
** This is a legacy interface that is preserved for backwards compatibility.
** Use of this interface is not recommended.
**







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







3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
** was defined  (using [sqlite3_busy_handler()]) prior to calling
** this routine, that other busy handler is cleared.)^
**
** See also:  [PRAGMA busy_timeout]
*/
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);

/*
** CAPI3REF: Set the Setlk Timeout
** METHOD: sqlite3
**
** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If
** the VFS supports blocking locks, it sets the timeout in ms used by
** eligible locks taken on wal mode databases by the specified database
** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does
** not support blocking locks, this function is a no-op.
**
** Passing 0 to this function disables blocking locks altogether. Passing
** -1 to this function requests that the VFS blocks for a long time -
** indefinitely if possible. The results of passing any other negative value
** are undefined.
**
** Internally, each SQLite database handle store two timeout values - the
** busy-timeout (used for rollback mode databases, or if the VFS does not
** support blocking locks) and the setlk-timeout (used for blocking locks
** on wal-mode databases). The sqlite3_busy_timeout() method sets both
** values, this function sets only the setlk-timeout value. Therefore,
** to configure separate busy-timeout and setlk-timeout values for a single
** database handle, call sqlite3_busy_timeout() followed by this function.
**
** Whenever the number of connections to a wal mode database falls from
** 1 to 0, the last connection takes an exclusive lock on the database,
** then checkpoints and deletes the wal file. While it is doing this, any
** new connection that tries to read from the database fails with an
** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is
** passed to this API, the new connection blocks until the exclusive lock
** has been released.
*/
SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags);

/*
** CAPI3REF: Flags for sqlite3_setlk_timeout()
*/
#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01

/*
** CAPI3REF: Convenience Routines For Running Queries
** METHOD: sqlite3
**
** This is a legacy interface that is preserved for backwards compatibility.
** Use of this interface is not recommended.
**
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
** more threads at the same moment in time.
**
** For all versions of SQLite up to and including 3.6.23.1, a call to
** [sqlite3_reset()] was required after sqlite3_step() returned anything
** other than [SQLITE_ROW] before any subsequent invocation of
** sqlite3_step().  Failure to reset the prepared statement using
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
** sqlite3_step().  But after [version 3.6.23.1] ([dateof:3.6.23.1],
** sqlite3_step() began
** calling [sqlite3_reset()] automatically in this circumstance rather
** than returning [SQLITE_MISUSE].  This is not considered a compatibility
** break because any application that ever receives an SQLITE_MISUSE error
** is broken by definition.  The [SQLITE_OMIT_AUTORESET] compile-time option
** can be used to restore the legacy behavior.
**







|







5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
** more threads at the same moment in time.
**
** For all versions of SQLite up to and including 3.6.23.1, a call to
** [sqlite3_reset()] was required after sqlite3_step() returned anything
** other than [SQLITE_ROW] before any subsequent invocation of
** sqlite3_step().  Failure to reset the prepared statement using
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
** sqlite3_step().  But after [version 3.6.23.1] ([dateof:3.6.23.1]),
** sqlite3_step() began
** calling [sqlite3_reset()] automatically in this circumstance rather
** than returning [SQLITE_MISUSE].  This is not considered a compatibility
** break because any application that ever receives an SQLITE_MISUSE error
** is broken by definition.  The [SQLITE_OMIT_AUTORESET] compile-time option
** can be used to restore the legacy behavior.
**
7341
7342
7343
7344
7345
7346
7347


7348
7349
7350
7351
7352
7353
7354
** to be invoked whenever a row is updated, inserted or deleted in
** a [rowid table].
** ^Any callback set by a previous call to this function
** for the same database connection is overridden.
**
** ^The second argument is a pointer to the function to invoke when a
** row is updated, inserted or deleted in a rowid table.


** ^The first argument to the callback is a copy of the third argument
** to sqlite3_update_hook().
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
** or [SQLITE_UPDATE], depending on the operation that caused the callback
** to be invoked.
** ^The third and fourth arguments to the callback contain pointers to the
** database and table name containing the affected row.







>
>







7386
7387
7388
7389
7390
7391
7392
7393
7394
7395
7396
7397
7398
7399
7400
7401
** to be invoked whenever a row is updated, inserted or deleted in
** a [rowid table].
** ^Any callback set by a previous call to this function
** for the same database connection is overridden.
**
** ^The second argument is a pointer to the function to invoke when a
** row is updated, inserted or deleted in a rowid table.
** ^The update hook is disabled by invoking sqlite3_update_hook()
** with a NULL pointer as the second parameter.
** ^The first argument to the callback is a copy of the third argument
** to sqlite3_update_hook().
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
** or [SQLITE_UPDATE], depending on the operation that caused the callback
** to be invoked.
** ^The third and fourth arguments to the callback contain pointers to the
** database and table name containing the affected row.
14095
14096
14097
14098
14099
14100
14101
14102
14103
14104
14105
14106






14107
14108
14109


14110
14111
14112
14113
14114
14115
14116
**    * Columns in an index
**    * Columns in a view
**    * Terms in the SET clause of an UPDATE statement
**    * Terms in the result set of a SELECT statement
**    * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement.
**    * Terms in the VALUES clause of an INSERT statement
**
** The hard upper limit here is 32676.  Most database people will
** tell you that in a well-normalized database, you usually should
** not have more than a dozen or so columns in any table.  And if
** that is the case, there is no point in having more than a few
** dozen values in any of the other situations described above.






*/
#ifndef SQLITE_MAX_COLUMN
# define SQLITE_MAX_COLUMN 2000


#endif

/*
** The maximum length of a single SQL statement in bytes.
**
** It used to be the case that setting this value to zero would
** turn the limit off.  That is no longer true.  It is not possible







|




>
>
>
>
>
>

|

>
>







14142
14143
14144
14145
14146
14147
14148
14149
14150
14151
14152
14153
14154
14155
14156
14157
14158
14159
14160
14161
14162
14163
14164
14165
14166
14167
14168
14169
14170
14171
**    * Columns in an index
**    * Columns in a view
**    * Terms in the SET clause of an UPDATE statement
**    * Terms in the result set of a SELECT statement
**    * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement.
**    * Terms in the VALUES clause of an INSERT statement
**
** The hard upper limit here is 32767.  Most database people will
** tell you that in a well-normalized database, you usually should
** not have more than a dozen or so columns in any table.  And if
** that is the case, there is no point in having more than a few
** dozen values in any of the other situations described above.
**
** An index can only have SQLITE_MAX_COLUMN columns from the user
** point of view, but the underlying b-tree that implements the index
** might have up to twice as many columns in a WITHOUT ROWID table,
** since must also store the primary key at the end.  Hence the
** column count for Index is u16 instead of i16.
*/
#if !defined(SQLITE_MAX_COLUMN)
# define SQLITE_MAX_COLUMN 2000
#elif SQLITE_MAX_COLUMN>32767
# error SQLITE_MAX_COLUMN may not exceed 32767
#endif

/*
** The maximum length of a single SQL statement in bytes.
**
** It used to be the case that setting this value to zero would
** turn the limit off.  That is no longer true.  It is not possible
15115
15116
15117
15118
15119
15120
15121
15122










15123
15124
15125
15126
15127
15128
15129
#endif

/*
** GCC does not define the offsetof() macro so we'll have to do it
** ourselves.
*/
#ifndef offsetof
#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD))










#endif

/*
** Macros to compute minimum and maximum of two numbers.
*/
#ifndef MIN
# define MIN(A,B) ((A)<(B)?(A):(B))







|
>
>
>
>
>
>
>
>
>
>







15170
15171
15172
15173
15174
15175
15176
15177
15178
15179
15180
15181
15182
15183
15184
15185
15186
15187
15188
15189
15190
15191
15192
15193
15194
#endif

/*
** GCC does not define the offsetof() macro so we'll have to do it
** ourselves.
*/
#ifndef offsetof
#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
#endif

/*
** Work around C99 "flex-array" syntax for pre-C99 compilers, so as
** to avoid complaints from -fsanitize=strict-bounds.
*/
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
# define FLEXARRAY
#else
# define FLEXARRAY 1
#endif

/*
** Macros to compute minimum and maximum of two numbers.
*/
#ifndef MIN
# define MIN(A,B) ((A)<(B)?(A):(B))
17350
17351
17352
17353
17354
17355
17356
17357
17358
17359
17360
17361
17362
17363
17364
17365
SQLITE_PRIVATE void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val);

SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*);
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*);
#endif

/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on
** each VDBE opcode.
**
** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op
** comments in VDBE programs that show key decision points in the code
** generator.
*/
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
SQLITE_PRIVATE   void sqlite3VdbeComment(Vdbe*, const char*, ...);







|
|







17415
17416
17417
17418
17419
17420
17421
17422
17423
17424
17425
17426
17427
17428
17429
17430
SQLITE_PRIVATE void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val);

SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*);
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*);
#endif

/* Use SQLITE_ENABLE_EXPLAIN_COMMENTS to enable generation of extra
** comments on each VDBE opcode.
**
** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op
** comments in VDBE programs that show key decision points in the code
** generator.
*/
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
SQLITE_PRIVATE   void sqlite3VdbeComment(Vdbe*, const char*, ...);
18074
18075
18076
18077
18078
18079
18080




18081
18082
18083
18084
18085
18086
18087
  Hash aFunc;                   /* Hash table of connection functions */
  Hash aCollSeq;                /* All collating sequences */
  BusyHandler busyHandler;      /* Busy callback */
  Db aDbStatic[2];              /* Static space for the 2 default backends */
  Savepoint *pSavepoint;        /* List of active savepoints */
  int nAnalysisLimit;           /* Number of index rows to ANALYZE */
  int busyTimeout;              /* Busy handler timeout, in msec */




  int nSavepoint;               /* Number of non-transaction savepoints */
  int nStatement;               /* Number of nested statement-transactions  */
  i64 nDeferredCons;            /* Net deferred constraints this transaction. */
  i64 nDeferredImmCons;         /* Net deferred immediate constraints */
  int *pnBytesFreed;            /* If not NULL, increment this in DbFree() */
  DbClientData *pDbData;        /* sqlite3_set_clientdata() content */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY







>
>
>
>







18139
18140
18141
18142
18143
18144
18145
18146
18147
18148
18149
18150
18151
18152
18153
18154
18155
18156
  Hash aFunc;                   /* Hash table of connection functions */
  Hash aCollSeq;                /* All collating sequences */
  BusyHandler busyHandler;      /* Busy callback */
  Db aDbStatic[2];              /* Static space for the 2 default backends */
  Savepoint *pSavepoint;        /* List of active savepoints */
  int nAnalysisLimit;           /* Number of index rows to ANALYZE */
  int busyTimeout;              /* Busy handler timeout, in msec */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  int setlkTimeout;             /* Blocking lock timeout, in msec. -1 -> inf. */
  int setlkFlags;               /* Flags passed to setlk_timeout() */
#endif
  int nSavepoint;               /* Number of non-transaction savepoints */
  int nStatement;               /* Number of nested statement-transactions  */
  i64 nDeferredCons;            /* Net deferred constraints this transaction. */
  i64 nDeferredImmCons;         /* Net deferred immediate constraints */
  int *pnBytesFreed;            /* If not NULL, increment this in DbFree() */
  DbClientData *pDbData;        /* sqlite3_set_clientdata() content */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
18886
18887
18888
18889
18890
18891
18892
18893
18894




18895
18896
18897
18898
18899
18900
18901
  /* EV: R-30323-21917 */
  u8 isDeferred;       /* True if constraint checking is deferred till COMMIT */
  u8 aAction[2];        /* ON DELETE and ON UPDATE actions, respectively */
  Trigger *apTrigger[2];/* Triggers for aAction[] actions */
  struct sColMap {      /* Mapping of columns in pFrom to columns in zTo */
    int iFrom;            /* Index of column in pFrom */
    char *zCol;           /* Name of column in zTo.  If NULL use PRIMARY KEY */
  } aCol[1];            /* One entry for each of nCol columns */
};





/*
** SQLite supports many different ways to resolve a constraint
** error.  ROLLBACK processing means that a constraint violation
** causes the operation in process to fail and for the current transaction
** to be rolled back.  ABORT processing means the operation in process
** fails and any prior changes from that one operation are backed out,







|

>
>
>
>







18955
18956
18957
18958
18959
18960
18961
18962
18963
18964
18965
18966
18967
18968
18969
18970
18971
18972
18973
18974
  /* EV: R-30323-21917 */
  u8 isDeferred;       /* True if constraint checking is deferred till COMMIT */
  u8 aAction[2];        /* ON DELETE and ON UPDATE actions, respectively */
  Trigger *apTrigger[2];/* Triggers for aAction[] actions */
  struct sColMap {      /* Mapping of columns in pFrom to columns in zTo */
    int iFrom;            /* Index of column in pFrom */
    char *zCol;           /* Name of column in zTo.  If NULL use PRIMARY KEY */
  } aCol[FLEXARRAY];      /* One entry for each of nCol columns */
};

/* The size (in bytes) of an FKey object holding N columns.  The answer
** does NOT include space to hold the zTo name. */
#define SZ_FKEY(N)  (offsetof(FKey,aCol)+(N)*sizeof(struct sColMap))

/*
** SQLite supports many different ways to resolve a constraint
** error.  ROLLBACK processing means that a constraint violation
** causes the operation in process to fail and for the current transaction
** to be rolled back.  ABORT processing means the operation in process
** fails and any prior changes from that one operation are backed out,
18950
18951
18952
18953
18954
18955
18956
18957
18958
18959



18960
18961
18962
18963
18964
18965
18966
struct KeyInfo {
  u32 nRef;           /* Number of references to this KeyInfo object */
  u8 enc;             /* Text encoding - one of the SQLITE_UTF* values */
  u16 nKeyField;      /* Number of key columns in the index */
  u16 nAllField;      /* Total columns, including key plus others */
  sqlite3 *db;        /* The database connection */
  u8 *aSortFlags;     /* Sort order for each column. */
  CollSeq *aColl[1];  /* Collating sequence for each term of the key */
};




/*
** Allowed bit values for entries in the KeyInfo.aSortFlags[] array.
*/
#define KEYINFO_ORDER_DESC    0x01    /* DESC sort order */
#define KEYINFO_ORDER_BIGNULL 0x02    /* NULL is larger than any other value */

/*







|


>
>
>







19023
19024
19025
19026
19027
19028
19029
19030
19031
19032
19033
19034
19035
19036
19037
19038
19039
19040
19041
19042
struct KeyInfo {
  u32 nRef;           /* Number of references to this KeyInfo object */
  u8 enc;             /* Text encoding - one of the SQLITE_UTF* values */
  u16 nKeyField;      /* Number of key columns in the index */
  u16 nAllField;      /* Total columns, including key plus others */
  sqlite3 *db;        /* The database connection */
  u8 *aSortFlags;     /* Sort order for each column. */
  CollSeq *aColl[FLEXARRAY]; /* Collating sequence for each term of the key */
};

/* The size (in bytes) of a KeyInfo object with up to N fields */
#define SZ_KEYINFO(N)  (offsetof(KeyInfo,aColl) + (N)*sizeof(CollSeq*))

/*
** Allowed bit values for entries in the KeyInfo.aSortFlags[] array.
*/
#define KEYINFO_ORDER_DESC    0x01    /* DESC sort order */
#define KEYINFO_ORDER_BIGNULL 0x02    /* NULL is larger than any other value */

/*
19072
19073
19074
19075
19076
19077
19078
19079
19080
19081
19082
19083
19084
19085
19086
  u8 *aSortOrder;          /* for each column: True==DESC, False==ASC */
  const char **azColl;     /* Array of collation sequence names for index */
  Expr *pPartIdxWhere;     /* WHERE clause for partial indices */
  ExprList *aColExpr;      /* Column expressions */
  Pgno tnum;               /* DB Page containing root of this index */
  LogEst szIdxRow;         /* Estimated average row size in bytes */
  u16 nKeyCol;             /* Number of columns forming the key */
  u16 nColumn;             /* Number of columns stored in the index */
  u8 onError;              /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
  unsigned idxType:2;      /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */
  unsigned bUnordered:1;   /* Use this index for == or IN queries only */
  unsigned uniqNotNull:1;  /* True if UNIQUE and NOT NULL for all columns */
  unsigned isResized:1;    /* True if resizeIndexObject() has been called */
  unsigned isCovering:1;   /* True if this is a covering index */
  unsigned noSkipScan:1;   /* Do not try to use skip-scan if true */







|







19148
19149
19150
19151
19152
19153
19154
19155
19156
19157
19158
19159
19160
19161
19162
  u8 *aSortOrder;          /* for each column: True==DESC, False==ASC */
  const char **azColl;     /* Array of collation sequence names for index */
  Expr *pPartIdxWhere;     /* WHERE clause for partial indices */
  ExprList *aColExpr;      /* Column expressions */
  Pgno tnum;               /* DB Page containing root of this index */
  LogEst szIdxRow;         /* Estimated average row size in bytes */
  u16 nKeyCol;             /* Number of columns forming the key */
  u16 nColumn;             /* Nr columns in btree. Can be 2*Table.nCol */
  u8 onError;              /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
  unsigned idxType:2;      /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */
  unsigned bUnordered:1;   /* Use this index for == or IN queries only */
  unsigned uniqNotNull:1;  /* True if UNIQUE and NOT NULL for all columns */
  unsigned isResized:1;    /* True if resizeIndexObject() has been called */
  unsigned isCovering:1;   /* True if this is a covering index */
  unsigned noSkipScan:1;   /* Do not try to use skip-scan if true */
19410
19411
19412
19413
19414
19415
19416
19417
19418
19419
19420
19421
19422
19423
19424
19425
19426
19427
** upwards into parent nodes.
*/
#define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc)

/* Macros can be used to test, set, or clear bits in the
** Expr.flags field.
*/
#define ExprHasProperty(E,P)     (((E)->flags&(P))!=0)
#define ExprHasAllProperty(E,P)  (((E)->flags&(P))==(P))
#define ExprSetProperty(E,P)     (E)->flags|=(P)
#define ExprClearProperty(E,P)   (E)->flags&=~(P)
#define ExprAlwaysTrue(E)   (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue)
#define ExprAlwaysFalse(E)  (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse)
#define ExprIsFullSize(E)   (((E)->flags&(EP_Reduced|EP_TokenOnly))==0)

/* Macros used to ensure that the correct members of unions are accessed
** in Expr.
*/







|
|
|
|







19486
19487
19488
19489
19490
19491
19492
19493
19494
19495
19496
19497
19498
19499
19500
19501
19502
19503
** upwards into parent nodes.
*/
#define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc)

/* Macros can be used to test, set, or clear bits in the
** Expr.flags field.
*/
#define ExprHasProperty(E,P)     (((E)->flags&(u32)(P))!=0)
#define ExprHasAllProperty(E,P)  (((E)->flags&(u32)(P))==(u32)(P))
#define ExprSetProperty(E,P)     (E)->flags|=(u32)(P)
#define ExprClearProperty(E,P)   (E)->flags&=~(u32)(P)
#define ExprAlwaysTrue(E)   (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue)
#define ExprAlwaysFalse(E)  (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse)
#define ExprIsFullSize(E)   (((E)->flags&(EP_Reduced|EP_TokenOnly))==0)

/* Macros used to ensure that the correct members of unions are accessed
** in Expr.
*/
19525
19526
19527
19528
19529
19530
19531
19532
19533





19534
19535
19536
19537
19538
19539
19540
      struct {             /* Used by any ExprList other than Parse.pConsExpr */
        u16 iOrderByCol;      /* For ORDER BY, column number in result set */
        u16 iAlias;           /* Index into Parse.aAlias[] for zName */
      } x;
      int iConstExprReg;   /* Register in which Expr value is cached. Used only
                           ** by Parse.pConstExpr */
    } u;
  } a[1];                  /* One slot for each expression in the list */
};






/*
** Allowed values for Expr.a.eEName
*/
#define ENAME_NAME  0       /* The AS clause of a result set */
#define ENAME_SPAN  1       /* Complete text of the result set expression */
#define ENAME_TAB   2       /* "DB.TABLE.NAME" for the result set */







|

>
>
>
>
>







19601
19602
19603
19604
19605
19606
19607
19608
19609
19610
19611
19612
19613
19614
19615
19616
19617
19618
19619
19620
19621
      struct {             /* Used by any ExprList other than Parse.pConsExpr */
        u16 iOrderByCol;      /* For ORDER BY, column number in result set */
        u16 iAlias;           /* Index into Parse.aAlias[] for zName */
      } x;
      int iConstExprReg;   /* Register in which Expr value is cached. Used only
                           ** by Parse.pConstExpr */
    } u;
  } a[FLEXARRAY];          /* One slot for each expression in the list */
};

/* The size (in bytes) of an ExprList object that is big enough to hold
** as many as N expressions. */
#define SZ_EXPRLIST(N)  \
             (offsetof(ExprList,a) + (N)*sizeof(struct ExprList_item))

/*
** Allowed values for Expr.a.eEName
*/
#define ENAME_NAME  0       /* The AS clause of a result set */
#define ENAME_SPAN  1       /* Complete text of the result set expression */
#define ENAME_TAB   2       /* "DB.TABLE.NAME" for the result set */
19555
19556
19557
19558
19559
19560
19561
19562
19563
19564



19565
19566
19567
19568
19569
19570
19571
**
** If "a" is the k-th column of table "t", then IdList.a[0].idx==k.
*/
struct IdList {
  int nId;         /* Number of identifiers on the list */
  struct IdList_item {
    char *zName;      /* Name of the identifier */
  } a[1];
};




/*
** Allowed values for IdList.eType, which determines which value of the a.u4
** is valid.
*/
#define EU4_NONE   0   /* Does not use IdList.a.u4 */
#define EU4_IDX    1   /* Uses IdList.a.u4.idx */
#define EU4_EXPR   2   /* Uses IdList.a.u4.pExpr -- NOT CURRENTLY USED */







|


>
>
>







19636
19637
19638
19639
19640
19641
19642
19643
19644
19645
19646
19647
19648
19649
19650
19651
19652
19653
19654
19655
**
** If "a" is the k-th column of table "t", then IdList.a[0].idx==k.
*/
struct IdList {
  int nId;         /* Number of identifiers on the list */
  struct IdList_item {
    char *zName;      /* Name of the identifier */
  } a[FLEXARRAY];
};

/* The size (in bytes) of an IdList object that can hold up to N IDs. */
#define SZ_IDLIST(N)  (offsetof(IdList,a)+(N)*sizeof(struct IdList_item))

/*
** Allowed values for IdList.eType, which determines which value of the a.u4
** is valid.
*/
#define EU4_NONE   0   /* Does not use IdList.a.u4 */
#define EU4_IDX    1   /* Uses IdList.a.u4.idx */
#define EU4_EXPR   2   /* Uses IdList.a.u4.pExpr -- NOT CURRENTLY USED */
19677
19678
19679
19680
19681
19682
19683
19684
19685
19686
19687








19688
19689
19690
19691
19692
19693
19694
** This object represents one or more tables that are the source of
** content for an SQL statement.  For example, a single SrcList object
** is used to hold the FROM clause of a SELECT statement.  SrcList also
** represents the target tables for DELETE, INSERT, and UPDATE statements.
**
*/
struct SrcList {
  int nSrc;        /* Number of tables or subqueries in the FROM clause */
  u32 nAlloc;      /* Number of entries allocated in a[] below */
  SrcItem a[1];    /* One entry for each identifier on the list */
};









/*
** Permitted values of the SrcList.a.jointype field
*/
#define JT_INNER     0x01    /* Any kind of inner or cross join */
#define JT_CROSS     0x02    /* Explicit use of the CROSS keyword */
#define JT_NATURAL   0x04    /* True for a "natural" join */







|
|
|

>
>
>
>
>
>
>
>







19761
19762
19763
19764
19765
19766
19767
19768
19769
19770
19771
19772
19773
19774
19775
19776
19777
19778
19779
19780
19781
19782
19783
19784
19785
19786
** This object represents one or more tables that are the source of
** content for an SQL statement.  For example, a single SrcList object
** is used to hold the FROM clause of a SELECT statement.  SrcList also
** represents the target tables for DELETE, INSERT, and UPDATE statements.
**
*/
struct SrcList {
  int nSrc;             /* Number of tables or subqueries in the FROM clause */
  u32 nAlloc;           /* Number of entries allocated in a[] below */
  SrcItem a[FLEXARRAY]; /* One entry for each identifier on the list */
};

/* Size (in bytes) of a SrcList object that can hold as many as N
** SrcItem objects. */
#define SZ_SRCLIST(N) (offsetof(SrcList,a)+(N)*sizeof(SrcItem))

/* Size (in bytes( of a SrcList object that holds 1 SrcItem.  This is a
** special case of SZ_SRCITEM(1) that comes up often. */
#define SZ_SRCLIST_1  (offsetof(SrcList,a)+sizeof(SrcItem))

/*
** Permitted values of the SrcList.a.jointype field
*/
#define JT_INNER     0x01    /* Any kind of inner or cross join */
#define JT_CROSS     0x02    /* Explicit use of the CROSS keyword */
#define JT_NATURAL   0x04    /* True for a "natural" join */
20745
20746
20747
20748
20749
20750
20751
20752
20753




20754
20755
20756
20757
20758
20759
20760
** An instance of the With object represents a WITH clause containing
** one or more CTEs (common table expressions).
*/
struct With {
  int nCte;               /* Number of CTEs in the WITH clause */
  int bView;              /* Belongs to the outermost Select of a view */
  With *pOuter;           /* Containing WITH clause, or NULL */
  Cte a[1];               /* For each CTE in the WITH clause.... */
};





/*
** The Cte object is not guaranteed to persist for the entire duration
** of code generation.  (The query flattener or other parser tree
** edits might delete it.)  The following object records information
** about each Common Table Expression that must be preserved for the
** duration of the parse.







|

>
>
>
>







20837
20838
20839
20840
20841
20842
20843
20844
20845
20846
20847
20848
20849
20850
20851
20852
20853
20854
20855
20856
** An instance of the With object represents a WITH clause containing
** one or more CTEs (common table expressions).
*/
struct With {
  int nCte;               /* Number of CTEs in the WITH clause */
  int bView;              /* Belongs to the outermost Select of a view */
  With *pOuter;           /* Containing WITH clause, or NULL */
  Cte a[FLEXARRAY];       /* For each CTE in the WITH clause.... */
};

/* The size (in bytes) of a With object that can hold as many
** as N different CTEs. */
#define SZ_WITH(N)  (offsetof(With,a) + (N)*sizeof(Cte))

/*
** The Cte object is not guaranteed to persist for the entire duration
** of code generation.  (The query flattener or other parser tree
** edits might delete it.)  The following object records information
** about each Common Table Expression that must be preserved for the
** duration of the parse.
20776
20777
20778
20779
20780
20781
20782
20783
20784




20785
20786
20787
20788
20789
20790
20791
/* Client data associated with sqlite3_set_clientdata() and
** sqlite3_get_clientdata().
*/
struct DbClientData {
  DbClientData *pNext;        /* Next in a linked list */
  void *pData;                /* The data */
  void (*xDestructor)(void*); /* Destructor.  Might be NULL */
  char zName[1];              /* Name of this client data. MUST BE LAST */
};





#ifdef SQLITE_DEBUG
/*
** An instance of the TreeView object is used for printing the content of
** data structures on sqlite3DebugPrintf() using a tree-like view.
*/
struct TreeView {







|

>
>
>
>







20872
20873
20874
20875
20876
20877
20878
20879
20880
20881
20882
20883
20884
20885
20886
20887
20888
20889
20890
20891
/* Client data associated with sqlite3_set_clientdata() and
** sqlite3_get_clientdata().
*/
struct DbClientData {
  DbClientData *pNext;        /* Next in a linked list */
  void *pData;                /* The data */
  void (*xDestructor)(void*); /* Destructor.  Might be NULL */
  char zName[FLEXARRAY];      /* Name of this client data. MUST BE LAST */
};

/* The size (in bytes) of a DbClientData object that can has a name
** that is N bytes long, including the zero-terminator. */
#define SZ_DBCLIENTDATA(N) (offsetof(DbClientData,zName)+(N))

#ifdef SQLITE_DEBUG
/*
** An instance of the TreeView object is used for printing the content of
** data structures on sqlite3DebugPrintf() using a tree-like view.
*/
struct TreeView {
21221
21222
21223
21224
21225
21226
21227
21228
21229
21230
21231
21232
21233
21234
21235
SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3*,Table*);
SQLITE_PRIVATE void sqlite3GenerateColumnNames(Parse *pParse, Select *pSelect);
SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**);
SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(Parse*,Table*,Select*,char);
SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*,char);
SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *, int);
SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*);
SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index*, i16);
#ifdef SQLITE_OMIT_GENERATED_COLUMNS
# define sqlite3TableColumnToStorage(T,X) (X)  /* No-op pass-through */
# define sqlite3StorageColumnToTable(T,X) (X)  /* No-op pass-through */
#else
SQLITE_PRIVATE   i16 sqlite3TableColumnToStorage(Table*, i16);
SQLITE_PRIVATE   i16 sqlite3StorageColumnToTable(Table*, i16);
#endif







|







21321
21322
21323
21324
21325
21326
21327
21328
21329
21330
21331
21332
21333
21334
21335
SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3*,Table*);
SQLITE_PRIVATE void sqlite3GenerateColumnNames(Parse *pParse, Select *pSelect);
SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**);
SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(Parse*,Table*,Select*,char);
SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*,char);
SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *, int);
SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*);
SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index*, int);
#ifdef SQLITE_OMIT_GENERATED_COLUMNS
# define sqlite3TableColumnToStorage(T,X) (X)  /* No-op pass-through */
# define sqlite3StorageColumnToTable(T,X) (X)  /* No-op pass-through */
#else
SQLITE_PRIVATE   i16 sqlite3TableColumnToStorage(Table*, i16);
SQLITE_PRIVATE   i16 sqlite3StorageColumnToTable(Table*, i16);
#endif
21319
21320
21321
21322
21323
21324
21325
21326
21327
21328
21329
21330
21331
21332
21333
SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*);
SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, SrcItem *);
SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(Parse*,SrcList*);
SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*);
SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*);
SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3*, OnOrUsing*);
SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*);
SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**);
SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
                          Expr*, int, int, u8);
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
                         Expr*,ExprList*,u32,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);







|







21419
21420
21421
21422
21423
21424
21425
21426
21427
21428
21429
21430
21431
21432
21433
SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*);
SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, SrcItem *);
SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(Parse*,SrcList*);
SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*);
SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*);
SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3*, OnOrUsing*);
SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*);
SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,int,int,char**);
SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
                          Expr*, int, int, u8);
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
                         Expr*,ExprList*,u32,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
21455
21456
21457
21458
21459
21460
21461
21462

21463
21464
21465
21466
21467
21468
21469
SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,const ExprList*,int);
SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,const SrcList*,int);
SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,const IdList*);
SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,const Select*,int);
SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch(int,const char*);
SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int);
SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8);
SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum*,sqlite3_value*);

SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void);
SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void);
SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void);
SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*);
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
SQLITE_PRIVATE   int sqlite3JsonTableFunctions(sqlite3*);
#endif







|
>







21555
21556
21557
21558
21559
21560
21561
21562
21563
21564
21565
21566
21567
21568
21569
21570
SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,const ExprList*,int);
SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,const SrcList*,int);
SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,const IdList*);
SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,const Select*,int);
SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch(int,const char*);
SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int);
SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8);
SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum*,sqlite3_value*,int);
SQLITE_PRIVATE int sqlite3AppendOneUtf8Character(char*, u32);
SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void);
SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void);
SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void);
SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*);
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
SQLITE_PRIVATE   int sqlite3JsonTableFunctions(sqlite3*);
#endif
22555
22556
22557
22558
22559
22560
22561



22562
22563
22564
22565
22566
22567
22568
  "ENABLE_RBU",
#endif
#ifdef SQLITE_ENABLE_RTREE
  "ENABLE_RTREE",
#endif
#ifdef SQLITE_ENABLE_SESSION
  "ENABLE_SESSION",



#endif
#ifdef SQLITE_ENABLE_SNAPSHOT
  "ENABLE_SNAPSHOT",
#endif
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
  "ENABLE_SORTER_REFERENCES",
#endif







>
>
>







22656
22657
22658
22659
22660
22661
22662
22663
22664
22665
22666
22667
22668
22669
22670
22671
22672
  "ENABLE_RBU",
#endif
#ifdef SQLITE_ENABLE_RTREE
  "ENABLE_RTREE",
#endif
#ifdef SQLITE_ENABLE_SESSION
  "ENABLE_SESSION",
#endif
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  "ENABLE_SETLK_TIMEOUT",
#endif
#ifdef SQLITE_ENABLE_SNAPSHOT
  "ENABLE_SNAPSHOT",
#endif
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
  "ENABLE_SORTER_REFERENCES",
#endif
22610
22611
22612
22613
22614
22615
22616



22617
22618
22619
22620
22621
22622
22623
#endif
#ifdef SQLITE_EXTRA_IFNULLROW
  "EXTRA_IFNULLROW",
#endif
#ifdef SQLITE_EXTRA_INIT
  "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT),
#endif



#ifdef SQLITE_EXTRA_SHUTDOWN
  "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN),
#endif
#ifdef SQLITE_FTS3_MAX_EXPR_DEPTH
  "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH),
#endif
#ifdef SQLITE_FTS5_ENABLE_TEST_MI







>
>
>







22714
22715
22716
22717
22718
22719
22720
22721
22722
22723
22724
22725
22726
22727
22728
22729
22730
#endif
#ifdef SQLITE_EXTRA_IFNULLROW
  "EXTRA_IFNULLROW",
#endif
#ifdef SQLITE_EXTRA_INIT
  "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT),
#endif
#ifdef SQLITE_EXTRA_INIT_MUTEXED
  "EXTRA_INIT_MUTEXED=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT_MUTEXED),
#endif
#ifdef SQLITE_EXTRA_SHUTDOWN
  "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN),
#endif
#ifdef SQLITE_FTS3_MAX_EXPR_DEPTH
  "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH),
#endif
#ifdef SQLITE_FTS5_ENABLE_TEST_MI
23594
23595
23596
23597
23598
23599
23600
23601
23602
23603
23604
23605
23606








23607
23608
23609
23610
23611
23612
23613
  u32 payloadSize;        /* Total number of bytes in the record */
  u32 szRow;              /* Byte available in aRow */
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
  u64 maskUsed;           /* Mask of columns used by this cursor */
#endif
  VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */

  /* 2*nField extra array elements allocated for aType[], beyond the one
  ** static element declared in the structure.  nField total array slots for
  ** aType[] and nField+1 array slots for aOffset[] */
  u32 aType[1];           /* Type values record decode.  MUST BE LAST */
};









/* Return true if P is a null-only cursor
*/
#define IsNullCursor(P) \
  ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0)

/*
** A value for VdbeCursor.cacheStatus that means the cache is always invalid.







|
<
|
|


>
>
>
>
>
>
>
>







23701
23702
23703
23704
23705
23706
23707
23708

23709
23710
23711
23712
23713
23714
23715
23716
23717
23718
23719
23720
23721
23722
23723
23724
23725
23726
23727
  u32 payloadSize;        /* Total number of bytes in the record */
  u32 szRow;              /* Byte available in aRow */
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
  u64 maskUsed;           /* Mask of columns used by this cursor */
#endif
  VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */

  /* Space is allocated for aType to hold at least 2*nField+1 entries:

  ** nField slots for aType[] and nField+1 array slots for aOffset[] */
  u32 aType[FLEXARRAY];    /* Type values record decode.  MUST BE LAST */
};

/*
** The size (in bytes) of a VdbeCursor object that has an nField value of N
** or less.  The value of SZ_VDBECURSOR(n) is guaranteed to be a multiple
** of 8.
*/
#define SZ_VDBECURSOR(N) \
    (ROUND8(offsetof(VdbeCursor,aType)) + ((N)+1)*sizeof(u64))

/* Return true if P is a null-only cursor
*/
#define IsNullCursor(P) \
  ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0)

/*
** A value for VdbeCursor.cacheStatus that means the cache is always invalid.
23856
23857
23858
23859
23860
23861
23862
23863
23864
23865







23866
23867
23868
23869
23870
23871
23872
  Mem *pMem;              /* Memory cell used to store aggregate context */
  Vdbe *pVdbe;            /* The VM that owns this context */
  int iOp;                /* Instruction number of OP_Function */
  int isError;            /* Error code returned by the function. */
  u8 enc;                 /* Encoding to use for results */
  u8 skipFlag;            /* Skip accumulator loading if true */
  u16 argc;               /* Number of arguments */
  sqlite3_value *argv[1]; /* Argument set */
};









/* The ScanStatus object holds a single value for the
** sqlite3_stmt_scanstatus() interface.
**
** aAddrRange[]:
**   This array is used by ScanStatus elements associated with EQP
**   notes that make an SQLITE_SCANSTAT_NCYCLE value available. It is







|


>
>
>
>
>
>
>







23970
23971
23972
23973
23974
23975
23976
23977
23978
23979
23980
23981
23982
23983
23984
23985
23986
23987
23988
23989
23990
23991
23992
23993
  Mem *pMem;              /* Memory cell used to store aggregate context */
  Vdbe *pVdbe;            /* The VM that owns this context */
  int iOp;                /* Instruction number of OP_Function */
  int isError;            /* Error code returned by the function. */
  u8 enc;                 /* Encoding to use for results */
  u8 skipFlag;            /* Skip accumulator loading if true */
  u16 argc;               /* Number of arguments */
  sqlite3_value *argv[FLEXARRAY]; /* Argument set */
};

/*
** The size (in bytes) of an sqlite3_context object that holds N
** argv[] arguments.
*/
#define SZ_CONTEXT(N)  \
   (offsetof(sqlite3_context,argv)+(N)*sizeof(sqlite3_value*))


/* The ScanStatus object holds a single value for the
** sqlite3_stmt_scanstatus() interface.
**
** aAddrRange[]:
**   This array is used by ScanStatus elements associated with EQP
**   notes that make an SQLITE_SCANSTAT_NCYCLE value available. It is
23992
23993
23994
23995
23996
23997
23998
23999
24000
24001
24002
24003
24004
24005
24006
24007
24008
24009
24010

24011
24012
24013
24014
24015
24016
24017
** sqlite3_preupdate_*() API functions.
*/
struct PreUpdate {
  Vdbe *v;
  VdbeCursor *pCsr;               /* Cursor to read old values from */
  int op;                         /* One of SQLITE_INSERT, UPDATE, DELETE */
  u8 *aRecord;                    /* old.* database record */
  KeyInfo keyinfo;
  UnpackedRecord *pUnpacked;      /* Unpacked version of aRecord[] */
  UnpackedRecord *pNewUnpacked;   /* Unpacked version of new.* record */
  int iNewReg;                    /* Register for new.* values */
  int iBlobWrite;                 /* Value returned by preupdate_blobwrite() */
  i64 iKey1;                      /* First key value passed to hook */
  i64 iKey2;                      /* Second key value passed to hook */
  Mem oldipk;                     /* Memory cell holding "old" IPK value */
  Mem *aNew;                      /* Array of new.* values */
  Table *pTab;                    /* Schema object being updated */
  Index *pPk;                     /* PK index if pTab is WITHOUT ROWID */
  sqlite3_value **apDflt;         /* Array of default values, if required */

};

/*
** An instance of this object is used to pass an vector of values into
** OP_VFilter, the xFilter method of a virtual table.  The vector is the
** set of values on the right-hand side of an IN constraint.
**







|











>







24113
24114
24115
24116
24117
24118
24119
24120
24121
24122
24123
24124
24125
24126
24127
24128
24129
24130
24131
24132
24133
24134
24135
24136
24137
24138
24139
** sqlite3_preupdate_*() API functions.
*/
struct PreUpdate {
  Vdbe *v;
  VdbeCursor *pCsr;               /* Cursor to read old values from */
  int op;                         /* One of SQLITE_INSERT, UPDATE, DELETE */
  u8 *aRecord;                    /* old.* database record */
  KeyInfo *pKeyinfo;              /* Key information */
  UnpackedRecord *pUnpacked;      /* Unpacked version of aRecord[] */
  UnpackedRecord *pNewUnpacked;   /* Unpacked version of new.* record */
  int iNewReg;                    /* Register for new.* values */
  int iBlobWrite;                 /* Value returned by preupdate_blobwrite() */
  i64 iKey1;                      /* First key value passed to hook */
  i64 iKey2;                      /* Second key value passed to hook */
  Mem oldipk;                     /* Memory cell holding "old" IPK value */
  Mem *aNew;                      /* Array of new.* values */
  Table *pTab;                    /* Schema object being updated */
  Index *pPk;                     /* PK index if pTab is WITHOUT ROWID */
  sqlite3_value **apDflt;         /* Array of default values, if required */
  u8 keyinfoSpace[SZ_KEYINFO(0)]; /* Space to hold pKeyinfo[0] content */
};

/*
** An instance of this object is used to pass an vector of values into
** OP_VFilter, the xFilter method of a virtual table.  The vector is the
** set of values on the right-hand side of an IN constraint.
**
24370
24371
24372
24373
24374
24375
24376

24377
24378
24379
24380
24381
24382
24383
24384
24385
SQLITE_PRIVATE int sqlite3LookasideUsed(sqlite3 *db, int *pHighwater){
  u32 nInit = countLookasideSlots(db->lookaside.pInit);
  u32 nFree = countLookasideSlots(db->lookaside.pFree);
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
  nInit += countLookasideSlots(db->lookaside.pSmallInit);
  nFree += countLookasideSlots(db->lookaside.pSmallFree);
#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */

  if( pHighwater ) *pHighwater = db->lookaside.nSlot - nInit;
  return db->lookaside.nSlot - (nInit+nFree);
}

/*
** Query status information for a single database connection
*/
SQLITE_API int sqlite3_db_status(
  sqlite3 *db,          /* The database connection whose status is desired */







>
|
|







24492
24493
24494
24495
24496
24497
24498
24499
24500
24501
24502
24503
24504
24505
24506
24507
24508
SQLITE_PRIVATE int sqlite3LookasideUsed(sqlite3 *db, int *pHighwater){
  u32 nInit = countLookasideSlots(db->lookaside.pInit);
  u32 nFree = countLookasideSlots(db->lookaside.pFree);
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
  nInit += countLookasideSlots(db->lookaside.pSmallInit);
  nFree += countLookasideSlots(db->lookaside.pSmallFree);
#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
  assert( db->lookaside.nSlot >= nInit+nFree );
  if( pHighwater ) *pHighwater = (int)(db->lookaside.nSlot - nInit);
  return (int)(db->lookaside.nSlot - (nInit+nFree));
}

/*
** Query status information for a single database connection
*/
SQLITE_API int sqlite3_db_status(
  sqlite3 *db,          /* The database connection whose status is desired */
24424
24425
24426
24427
24428
24429
24430
24431
24432
24433
24434
24435
24436
24437
24438
    case SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL: {
      testcase( op==SQLITE_DBSTATUS_LOOKASIDE_HIT );
      testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE );
      testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL );
      assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 );
      assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 );
      *pCurrent = 0;
      *pHighwater = db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT];
      if( resetFlag ){
        db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0;
      }
      break;
    }

    /*







|







24547
24548
24549
24550
24551
24552
24553
24554
24555
24556
24557
24558
24559
24560
24561
    case SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL: {
      testcase( op==SQLITE_DBSTATUS_LOOKASIDE_HIT );
      testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE );
      testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL );
      assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 );
      assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 );
      *pCurrent = 0;
      *pHighwater = (int)db->lookaside.anStat[op-SQLITE_DBSTATUS_LOOKASIDE_HIT];
      if( resetFlag ){
        db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0;
      }
      break;
    }

    /*
25936
25937
25938
25939
25940
25941
25942
25943
25944
25945
25946
25947
25948
25949
25950

/*
** Return the number of days after the most recent Sunday.
**
** In other words, return the day of the week according
** to this code:
**
**   0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday
*/
static int daysAfterSunday(DateTime *pDate){
  assert( pDate->validJD );
  return (int)((pDate->iJD+129600000)/86400000) % 7;
}

/*







|







26059
26060
26061
26062
26063
26064
26065
26066
26067
26068
26069
26070
26071
26072
26073

/*
** Return the number of days after the most recent Sunday.
**
** In other words, return the day of the week according
** to this code:
**
**   0=Sunday, 1=Monday, 2=Tuesday, ..., 6=Saturday
*/
static int daysAfterSunday(DateTime *pDate){
  assert( pDate->validJD );
  return (int)((pDate->iJD+129600000)/86400000) % 7;
}

/*
31539
31540
31541
31542
31543
31544
31545
31546
31547
31548
31549
31550
31551
31552
31553
31554
31555
31556
31557
31558
31559
31560
31561
31562
31563
#define etGENERIC     3 /* Floating or exponential, depending on exponent. %g */
#define etSIZE        4 /* Return number of characters processed so far. %n */
#define etSTRING      5 /* Strings. %s */
#define etDYNSTRING   6 /* Dynamically allocated strings. %z */
#define etPERCENT     7 /* Percent symbol. %% */
#define etCHARX       8 /* Characters. %c */
/* The rest are extensions, not normally found in printf() */
#define etSQLESCAPE   9 /* Strings with '\'' doubled.  %q */
#define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '',
                          NULL pointers replaced by SQL NULL.  %Q */
#define etTOKEN      11 /* a pointer to a Token structure */
#define etSRCITEM    12 /* a pointer to a SrcItem */
#define etPOINTER    13 /* The %p conversion */
#define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */
#define etORDINAL    15 /* %r -> 1st, 2nd, 3rd, 4th, etc.  English only */
#define etDECIMAL    16 /* %d or %u, but not %x, %o */

#define etINVALID    17 /* Any unrecognized conversion type */


/*
** An "etByte" is an 8-bit unsigned value.
*/
typedef unsigned char etByte;








|
|
|
|
|
|
|
|
|

|







31662
31663
31664
31665
31666
31667
31668
31669
31670
31671
31672
31673
31674
31675
31676
31677
31678
31679
31680
31681
31682
31683
31684
31685
31686
#define etGENERIC     3 /* Floating or exponential, depending on exponent. %g */
#define etSIZE        4 /* Return number of characters processed so far. %n */
#define etSTRING      5 /* Strings. %s */
#define etDYNSTRING   6 /* Dynamically allocated strings. %z */
#define etPERCENT     7 /* Percent symbol. %% */
#define etCHARX       8 /* Characters. %c */
/* The rest are extensions, not normally found in printf() */
#define etESCAPE_q    9  /* Strings with '\'' doubled.  %q */
#define etESCAPE_Q    10 /* Strings with '\'' doubled and enclosed in '',
                            NULL pointers replaced by SQL NULL.  %Q */
#define etTOKEN       11 /* a pointer to a Token structure */
#define etSRCITEM     12 /* a pointer to a SrcItem */
#define etPOINTER     13 /* The %p conversion */
#define etESCAPE_w    14 /* %w -> Strings with '\"' doubled */
#define etORDINAL     15 /* %r -> 1st, 2nd, 3rd, 4th, etc.  English only */
#define etDECIMAL     16 /* %d or %u, but not %x, %o */

#define etINVALID     17 /* Any unrecognized conversion type */


/*
** An "etByte" is an 8-bit unsigned value.
*/
typedef unsigned char etByte;

31588
31589
31590
31591
31592
31593
31594
31595
31596
31597
31598
31599
31600
31601
31602
31603
31604
static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
static const char aPrefix[] = "-x0\000X0";
static const et_info fmtinfo[] = {
  {  'd', 10, 1, etDECIMAL,    0,  0 },
  {  's',  0, 4, etSTRING,     0,  0 },
  {  'g',  0, 1, etGENERIC,    30, 0 },
  {  'z',  0, 4, etDYNSTRING,  0,  0 },
  {  'q',  0, 4, etSQLESCAPE,  0,  0 },
  {  'Q',  0, 4, etSQLESCAPE2, 0,  0 },
  {  'w',  0, 4, etSQLESCAPE3, 0,  0 },
  {  'c',  0, 0, etCHARX,      0,  0 },
  {  'o',  8, 0, etRADIX,      0,  2 },
  {  'u', 10, 0, etDECIMAL,    0,  0 },
  {  'x', 16, 0, etRADIX,      16, 1 },
  {  'X', 16, 0, etRADIX,      0,  4 },
#ifndef SQLITE_OMIT_FLOATING_POINT
  {  'f',  0, 1, etFLOAT,      0,  0 },







|
|
|







31711
31712
31713
31714
31715
31716
31717
31718
31719
31720
31721
31722
31723
31724
31725
31726
31727
static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
static const char aPrefix[] = "-x0\000X0";
static const et_info fmtinfo[] = {
  {  'd', 10, 1, etDECIMAL,    0,  0 },
  {  's',  0, 4, etSTRING,     0,  0 },
  {  'g',  0, 1, etGENERIC,    30, 0 },
  {  'z',  0, 4, etDYNSTRING,  0,  0 },
  {  'q',  0, 4, etESCAPE_q,   0,  0 },
  {  'Q',  0, 4, etESCAPE_Q,   0,  0 },
  {  'w',  0, 4, etESCAPE_w,   0,  0 },
  {  'c',  0, 0, etCHARX,      0,  0 },
  {  'o',  8, 0, etRADIX,      0,  2 },
  {  'u', 10, 0, etDECIMAL,    0,  0 },
  {  'x', 16, 0, etRADIX,      16, 1 },
  {  'X', 16, 0, etRADIX,      0,  4 },
#ifndef SQLITE_OMIT_FLOATING_POINT
  {  'f',  0, 1, etFLOAT,      0,  0 },
32187
32188
32189
32190
32191
32192
32193
32194
32195
32196
32197
32198
32199
32200
32201
32202
32203
32204
32205
32206
32207
32208
32209
32210
32211
32212
32213
32214
32215
32216
32217
32218
32219
              }
            }
          }else{
            buf[0] = 0;
          }
        }else{
          unsigned int ch = va_arg(ap,unsigned int);
          if( ch<0x00080 ){
            buf[0] = ch & 0xff;
            length = 1;
          }else if( ch<0x00800 ){
            buf[0] = 0xc0 + (u8)((ch>>6)&0x1f);
            buf[1] = 0x80 + (u8)(ch & 0x3f);
            length = 2;
          }else if( ch<0x10000 ){
            buf[0] = 0xe0 + (u8)((ch>>12)&0x0f);
            buf[1] = 0x80 + (u8)((ch>>6) & 0x3f);
            buf[2] = 0x80 + (u8)(ch & 0x3f);
            length = 3;
          }else{
            buf[0] = 0xf0 + (u8)((ch>>18) & 0x07);
            buf[1] = 0x80 + (u8)((ch>>12) & 0x3f);
            buf[2] = 0x80 + (u8)((ch>>6) & 0x3f);
            buf[3] = 0x80 + (u8)(ch & 0x3f);
            length = 4;
          }
        }
        if( precision>1 ){
          i64 nPrior = 1;
          width -= precision-1;
          if( width>1 && !flag_leftjustify ){
            sqlite3_str_appendchar(pAccum, width-1, ' ');
            width = 0;







<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







32310
32311
32312
32313
32314
32315
32316


32317
















32318
32319
32320
32321
32322
32323
32324
              }
            }
          }else{
            buf[0] = 0;
          }
        }else{
          unsigned int ch = va_arg(ap,unsigned int);


          length = sqlite3AppendOneUtf8Character(buf, ch);
















        }
        if( precision>1 ){
          i64 nPrior = 1;
          width -= precision-1;
          if( width>1 && !flag_leftjustify ){
            sqlite3_str_appendchar(pAccum, width-1, ' ');
            width = 0;
32285
32286
32287
32288
32289
32290
32291
32292
32293
32294
32295
32296
32297
32298
32299

32300
32301
32302
32303
32304
32305
32306
32307









32308
32309
32310
32311
32312
32313
32314
32315
32316
32317
32318
32319


















32320





32321
32322
32323
32324
32325
32326
32327
32328
32329







32330

32331
32332
















32333
32334

32335



32336
32337
32338
32339
32340
32341
32342
      adjust_width_for_utf8:
        if( flag_altform2 && width>0 ){
          /* Adjust width to account for extra bytes in UTF-8 characters */
          int ii = length - 1;
          while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++;
        }
        break;
      case etSQLESCAPE:           /* %q: Escape ' characters */
      case etSQLESCAPE2:          /* %Q: Escape ' and enclose in '...' */
      case etSQLESCAPE3: {        /* %w: Escape " characters */
        i64 i, j, k, n;
        int needQuote, isnull;
        char ch;
        char q = ((xtype==etSQLESCAPE3)?'"':'\'');   /* Quote character */
        char *escarg;


        if( bArgList ){
          escarg = getTextArg(pArgList);
        }else{
          escarg = va_arg(ap,char*);
        }
        isnull = escarg==0;
        if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");









        /* For %q, %Q, and %w, the precision is the number of bytes (or
        ** characters if the ! flags is present) to use from the input.
        ** Because of the extra quoting characters inserted, the number
        ** of output characters may be larger than the precision.
        */
        k = precision;
        for(i=n=0; k!=0 && (ch=escarg[i])!=0; i++, k--){
          if( ch==q )  n++;
          if( flag_altform2 && (ch&0xc0)==0xc0 ){
            while( (escarg[i+1]&0xc0)==0x80 ){ i++; }
          }
        }


















        needQuote = !isnull && xtype==etSQLESCAPE2;





        n += i + 3;
        if( n>etBUFSIZE ){
          bufpt = zExtra = printfTempBuf(pAccum, n);
          if( bufpt==0 ) return;
        }else{
          bufpt = buf;
        }
        j = 0;
        if( needQuote ) bufpt[j++] = q;







        k = i;

        for(i=0; i<k; i++){
          bufpt[j++] = ch = escarg[i];
















          if( ch==q ) bufpt[j++] = ch;
        }

        if( needQuote ) bufpt[j++] = q;



        bufpt[j] = 0;
        length = j;
        goto adjust_width_for_utf8;
      }
      case etTOKEN: {
        if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return;
        if( flag_alternateform ){







|
|
|

|

<

>






|
|
>
>
>
>
>
>
>
>
>












>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>








|
>
>
>
>
>
>
>

>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
>
|
>
>
>







32390
32391
32392
32393
32394
32395
32396
32397
32398
32399
32400
32401
32402

32403
32404
32405
32406
32407
32408
32409
32410
32411
32412
32413
32414
32415
32416
32417
32418
32419
32420
32421
32422
32423
32424
32425
32426
32427
32428
32429
32430
32431
32432
32433
32434
32435
32436
32437
32438
32439
32440
32441
32442
32443
32444
32445
32446
32447
32448
32449
32450
32451
32452
32453
32454
32455
32456
32457
32458
32459
32460
32461
32462
32463
32464
32465
32466
32467
32468
32469
32470
32471
32472
32473
32474
32475
32476
32477
32478
32479
32480
32481
32482
32483
32484
32485
32486
32487
32488
32489
32490
32491
32492
32493
32494
32495
32496
32497
32498
32499
32500
32501
32502
32503
32504
32505
32506
32507
      adjust_width_for_utf8:
        if( flag_altform2 && width>0 ){
          /* Adjust width to account for extra bytes in UTF-8 characters */
          int ii = length - 1;
          while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++;
        }
        break;
      case etESCAPE_q:          /* %q: Escape ' characters */
      case etESCAPE_Q:          /* %Q: Escape ' and enclose in '...' */
      case etESCAPE_w: {        /* %w: Escape " characters */
        i64 i, j, k, n;
        int needQuote = 0;
        char ch;

        char *escarg;
        char q;

        if( bArgList ){
          escarg = getTextArg(pArgList);
        }else{
          escarg = va_arg(ap,char*);
        }
        if( escarg==0 ){
          escarg = (xtype==etESCAPE_Q ? "NULL" : "(NULL)");
        }else if( xtype==etESCAPE_Q ){
          needQuote = 1;
        }
        if( xtype==etESCAPE_w ){
          q = '"';
          flag_alternateform = 0;
        }else{
          q = '\'';
        }
        /* For %q, %Q, and %w, the precision is the number of bytes (or
        ** characters if the ! flags is present) to use from the input.
        ** Because of the extra quoting characters inserted, the number
        ** of output characters may be larger than the precision.
        */
        k = precision;
        for(i=n=0; k!=0 && (ch=escarg[i])!=0; i++, k--){
          if( ch==q )  n++;
          if( flag_altform2 && (ch&0xc0)==0xc0 ){
            while( (escarg[i+1]&0xc0)==0x80 ){ i++; }
          }
        }
        if( flag_alternateform ){
          /* For %#q, do unistr()-style backslash escapes for
          ** all control characters, and for backslash itself.
          ** For %#Q, do the same but only if there is at least
          ** one control character. */
          u32 nBack = 0;
          u32 nCtrl = 0;
          for(k=0; k<i; k++){
            if( escarg[k]=='\\' ){
              nBack++;
            }else if( ((u8*)escarg)[k]<=0x1f ){
              nCtrl++;
            }
          }
          if( nCtrl || xtype==etESCAPE_q ){
            n += nBack + 5*nCtrl;
            if( xtype==etESCAPE_Q ){
              n += 10;
              needQuote = 2;
            }
          }else{
            flag_alternateform = 0;
          }
        }
        n += i + 3;
        if( n>etBUFSIZE ){
          bufpt = zExtra = printfTempBuf(pAccum, n);
          if( bufpt==0 ) return;
        }else{
          bufpt = buf;
        }
        j = 0;
        if( needQuote ){
          if( needQuote==2 ){
            memcpy(&bufpt[j], "unistr('", 8);
            j += 8;
          }else{
            bufpt[j++] = '\'';
          }
        }
        k = i;
        if( flag_alternateform ){
          for(i=0; i<k; i++){
            bufpt[j++] = ch = escarg[i];
            if( ch==q ){
              bufpt[j++] = ch;
            }else if( ch=='\\' ){
              bufpt[j++] = '\\';
            }else if( ((unsigned char)ch)<=0x1f ){
              bufpt[j-1] = '\\';
              bufpt[j++] = 'u';
              bufpt[j++] = '0';
              bufpt[j++] = '0';
              bufpt[j++] = ch>=0x10 ? '1' : '0';
              bufpt[j++] = "0123456789abcdef"[ch&0xf];
            }
          }
        }else{
          for(i=0; i<k; i++){
            bufpt[j++] = ch = escarg[i];
            if( ch==q ) bufpt[j++] = ch;
          }
        }
        if( needQuote ){
          bufpt[j++] = '\'';
          if( needQuote==2 ) bufpt[j++] = ')';
        }
        bufpt[j] = 0;
        length = j;
        goto adjust_width_for_utf8;
      }
      case etTOKEN: {
        if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return;
        if( flag_alternateform ){
34826
34827
34828
34829
34830
34831
34832





























34833
34834
34835
34836
34837
34838
34839
  }else{                                                            \
    *zOut++ = (u8)(0x00D8 + (((c-0x10000)>>18)&0x03));              \
    *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0));  \
    *zOut++ = (u8)(0x00DC + ((c>>8)&0x03));                         \
    *zOut++ = (u8)(c&0x00FF);                                       \
  }                                                                 \
}






























/*
** Translate a single UTF-8 character.  Return the unicode value.
**
** During translation, assume that the byte that zTerm points
** is a 0x00.
**







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







34991
34992
34993
34994
34995
34996
34997
34998
34999
35000
35001
35002
35003
35004
35005
35006
35007
35008
35009
35010
35011
35012
35013
35014
35015
35016
35017
35018
35019
35020
35021
35022
35023
35024
35025
35026
35027
35028
35029
35030
35031
35032
35033
  }else{                                                            \
    *zOut++ = (u8)(0x00D8 + (((c-0x10000)>>18)&0x03));              \
    *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0));  \
    *zOut++ = (u8)(0x00DC + ((c>>8)&0x03));                         \
    *zOut++ = (u8)(c&0x00FF);                                       \
  }                                                                 \
}

/*
** Write a single UTF8 character whose value is v into the
** buffer starting at zOut.  zOut must be sized to hold at
** least for bytes.  Return the number of bytes needed
** to encode the new character.
*/
SQLITE_PRIVATE int sqlite3AppendOneUtf8Character(char *zOut, u32 v){
  if( v<0x00080 ){
    zOut[0] = (u8)(v & 0xff);
    return 1;
  }
  if( v<0x00800 ){
    zOut[0] = 0xc0 + (u8)((v>>6) & 0x1f);
    zOut[1] = 0x80 + (u8)(v & 0x3f);
    return 2;
  }
  if( v<0x10000 ){
    zOut[0] = 0xe0 + (u8)((v>>12) & 0x0f);
    zOut[1] = 0x80 + (u8)((v>>6) & 0x3f);
    zOut[2] = 0x80 + (u8)(v & 0x3f);
    return 3;
  }
  zOut[0] = 0xf0 + (u8)((v>>18) & 0x07);
  zOut[1] = 0x80 + (u8)((v>>12) & 0x3f);
  zOut[2] = 0x80 + (u8)((v>>6) & 0x3f);
  zOut[3] = 0x80 + (u8)(v & 0x3f);
  return 4;
}

/*
** Translate a single UTF-8 character.  Return the unicode value.
**
** During translation, assume that the byte that zTerm points
** is a 0x00.
**
36932
36933
36934
36935
36936
36937
36938
36939
36940
36941
36942
36943
36944
36945
36946
  }
  *pA = iA*iB;
  return 0;
#endif
}

/*
** Compute the absolute value of a 32-bit signed integer, of possible.  Or
** if the integer has a value of -2147483648, return +2147483647
*/
SQLITE_PRIVATE int sqlite3AbsInt32(int x){
  if( x>=0 ) return x;
  if( x==(int)0x80000000 ) return 0x7fffffff;
  return -x;
}







|







37126
37127
37128
37129
37130
37131
37132
37133
37134
37135
37136
37137
37138
37139
37140
  }
  *pA = iA*iB;
  return 0;
#endif
}

/*
** Compute the absolute value of a 32-bit signed integer, if possible.  Or
** if the integer has a value of -2147483648, return +2147483647
*/
SQLITE_PRIVATE int sqlite3AbsInt32(int x){
  if( x>=0 ) return x;
  if( x==(int)0x80000000 ) return 0x7fffffff;
  return -x;
}
38909
38910
38911
38912
38913
38914
38915

38916
38917
38918
38919
38920
38921
38922
  int openFlags;                      /* The flags specified at open() */
#endif
#if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__)
  unsigned fsFlags;                   /* cached details from statfs() */
#endif
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  unsigned iBusyTimeout;              /* Wait this many millisec on locks */

#endif
#if OS_VXWORKS
  struct vxworksFileId *pId;          /* Unique file ID */
#endif
#ifdef SQLITE_DEBUG
  /* The next group of variables are used to track whether or not the
  ** transaction counter in bytes 24-27 of database files are updated







>







39103
39104
39105
39106
39107
39108
39109
39110
39111
39112
39113
39114
39115
39116
39117
  int openFlags;                      /* The flags specified at open() */
#endif
#if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__)
  unsigned fsFlags;                   /* cached details from statfs() */
#endif
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  unsigned iBusyTimeout;              /* Wait this many millisec on locks */
  int bBlockOnConnect;                /* True to block for SHARED locks */
#endif
#if OS_VXWORKS
  struct vxworksFileId *pId;          /* Unique file ID */
#endif
#ifdef SQLITE_DEBUG
  /* The next group of variables are used to track whether or not the
  ** transaction counter in bytes 24-27 of database files are updated
40302
40303
40304
40305
40306
40307
40308







40309
40310
40311
40312
40313
40314
40315
      if( rc<0 ) return rc;
      pInode->bProcessLock = 1;
      pInode->nLock++;
    }else{
      rc = 0;
    }
  }else{







    rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile);
  }
  return rc;
}

/*
** Lock the file with the lock specified by parameter eFileLock - one







>
>
>
>
>
>
>







40497
40498
40499
40500
40501
40502
40503
40504
40505
40506
40507
40508
40509
40510
40511
40512
40513
40514
40515
40516
40517
      if( rc<0 ) return rc;
      pInode->bProcessLock = 1;
      pInode->nLock++;
    }else{
      rc = 0;
    }
  }else{
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    if( pFile->bBlockOnConnect && pLock->l_type==F_RDLCK
     && pLock->l_start==SHARED_FIRST && pLock->l_len==SHARED_SIZE
    ){
      rc = osFcntl(pFile->h, F_SETLKW, pLock);
    }else
#endif
    rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile);
  }
  return rc;
}

/*
** Lock the file with the lock specified by parameter eFileLock - one
42663
42664
42665
42666
42667
42668
42669

42670
42671
42672
42673
42674
42675
42676
42677
42678
42679





42680
42681
42682
42683
42684
42685
42686
42687
    case SQLITE_FCNTL_HAS_MOVED: {
      *(int*)pArg = fileHasMoved(pFile);
      return SQLITE_OK;
    }
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    case SQLITE_FCNTL_LOCK_TIMEOUT: {
      int iOld = pFile->iBusyTimeout;

#if SQLITE_ENABLE_SETLK_TIMEOUT==1
      pFile->iBusyTimeout = *(int*)pArg;
#elif SQLITE_ENABLE_SETLK_TIMEOUT==2
      pFile->iBusyTimeout = !!(*(int*)pArg);
#else
# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2"
#endif
      *(int*)pArg = iOld;
      return SQLITE_OK;
    }





#endif
#if SQLITE_MAX_MMAP_SIZE>0
    case SQLITE_FCNTL_MMAP_SIZE: {
      i64 newLimit = *(i64*)pArg;
      int rc = SQLITE_OK;
      if( newLimit>sqlite3GlobalConfig.mxMmap ){
        newLimit = sqlite3GlobalConfig.mxMmap;
      }







>

|








>
>
>
>
>
|







42865
42866
42867
42868
42869
42870
42871
42872
42873
42874
42875
42876
42877
42878
42879
42880
42881
42882
42883
42884
42885
42886
42887
42888
42889
42890
42891
42892
42893
42894
42895
    case SQLITE_FCNTL_HAS_MOVED: {
      *(int*)pArg = fileHasMoved(pFile);
      return SQLITE_OK;
    }
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    case SQLITE_FCNTL_LOCK_TIMEOUT: {
      int iOld = pFile->iBusyTimeout;
      int iNew = *(int*)pArg;
#if SQLITE_ENABLE_SETLK_TIMEOUT==1
      pFile->iBusyTimeout = iNew<0 ? 0x7FFFFFFF : (unsigned)iNew;
#elif SQLITE_ENABLE_SETLK_TIMEOUT==2
      pFile->iBusyTimeout = !!(*(int*)pArg);
#else
# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2"
#endif
      *(int*)pArg = iOld;
      return SQLITE_OK;
    }
    case SQLITE_FCNTL_BLOCK_ON_CONNECT: {
      int iNew = *(int*)pArg;
      pFile->bBlockOnConnect = iNew;
      return SQLITE_OK;
    }
#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */
#if SQLITE_MAX_MMAP_SIZE>0
    case SQLITE_FCNTL_MMAP_SIZE: {
      i64 newLimit = *(i64*)pArg;
      int rc = SQLITE_OK;
      if( newLimit>sqlite3GlobalConfig.mxMmap ){
        newLimit = sqlite3GlobalConfig.mxMmap;
      }
43656
43657
43658
43659
43660
43661
43662
43663
43664
43665
43666
43667
43668
43669
43670
  **
  ** In other words, if this is a blocking lock, none of the locks that
  ** occur later in the above list than the lock being obtained may be
  ** held.
  **
  ** It is not permitted to block on the RECOVER lock.
  */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  {
    u16 lockMask = (p->exclMask|p->sharedMask);
    assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
          (ofst!=2)                                   /* not RECOVER */
       && (ofst!=1 || lockMask==0 || lockMask==2)
       && (ofst!=0 || lockMask<3)
       && (ofst<3  || lockMask<(1<<ofst))







|







43864
43865
43866
43867
43868
43869
43870
43871
43872
43873
43874
43875
43876
43877
43878
  **
  ** In other words, if this is a blocking lock, none of the locks that
  ** occur later in the above list than the lock being obtained may be
  ** held.
  **
  ** It is not permitted to block on the RECOVER lock.
  */
#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG)
  {
    u16 lockMask = (p->exclMask|p->sharedMask);
    assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
          (ofst!=2)                                   /* not RECOVER */
       && (ofst!=1 || lockMask==0 || lockMask==2)
       && (ofst!=0 || lockMask<3)
       && (ofst<3  || lockMask<(1<<ofst))
45465
45466
45467
45468
45469
45470
45471
45472
45473
45474
45475
45476
45477
45478
45479
#if !defined(HAVE_NANOSLEEP) || HAVE_NANOSLEEP+0
  struct timespec sp;
  sp.tv_sec = microseconds / 1000000;
  sp.tv_nsec = (microseconds % 1000000) * 1000;

  /* Almost all modern unix systems support nanosleep().  But if you are
  ** compiling for one of the rare exceptions, you can use
  ** -DHAVE_NANOSLEEP=0 (perhaps in conjuction with -DHAVE_USLEEP if
  ** usleep() is available) in order to bypass the use of nanosleep() */
  nanosleep(&sp, NULL);

  UNUSED_PARAMETER(NotUsed);
  return microseconds;
#elif defined(HAVE_USLEEP) && HAVE_USLEEP
  if( microseconds>=1000000 ) sleep(microseconds/1000000);







|







45673
45674
45675
45676
45677
45678
45679
45680
45681
45682
45683
45684
45685
45686
45687
#if !defined(HAVE_NANOSLEEP) || HAVE_NANOSLEEP+0
  struct timespec sp;
  sp.tv_sec = microseconds / 1000000;
  sp.tv_nsec = (microseconds % 1000000) * 1000;

  /* Almost all modern unix systems support nanosleep().  But if you are
  ** compiling for one of the rare exceptions, you can use
  ** -DHAVE_NANOSLEEP=0 (perhaps in conjunction with -DHAVE_USLEEP if
  ** usleep() is available) in order to bypass the use of nanosleep() */
  nanosleep(&sp, NULL);

  UNUSED_PARAMETER(NotUsed);
  return microseconds;
#elif defined(HAVE_USLEEP) && HAVE_USLEEP
  if( microseconds>=1000000 ) sleep(microseconds/1000000);
47186
47187
47188
47189
47190
47191
47192




47193






47194
47195
47196
47197
47198
47199
47200
#if SQLITE_MAX_MMAP_SIZE>0
  int nFetchOut;                /* Number of outstanding xFetch references */
  HANDLE hMap;                  /* Handle for accessing memory mapping */
  void *pMapRegion;             /* Area memory mapped */
  sqlite3_int64 mmapSize;       /* Size of mapped region */
  sqlite3_int64 mmapSizeMax;    /* Configured FCNTL_MMAP_SIZE value */
#endif




};







/*
** The winVfsAppData structure is used for the pAppData member for all of the
** Win32 VFS variants.
*/
typedef struct winVfsAppData winVfsAppData;
struct winVfsAppData {







>
>
>
>

>
>
>
>
>
>







47394
47395
47396
47397
47398
47399
47400
47401
47402
47403
47404
47405
47406
47407
47408
47409
47410
47411
47412
47413
47414
47415
47416
47417
47418
#if SQLITE_MAX_MMAP_SIZE>0
  int nFetchOut;                /* Number of outstanding xFetch references */
  HANDLE hMap;                  /* Handle for accessing memory mapping */
  void *pMapRegion;             /* Area memory mapped */
  sqlite3_int64 mmapSize;       /* Size of mapped region */
  sqlite3_int64 mmapSizeMax;    /* Configured FCNTL_MMAP_SIZE value */
#endif
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  DWORD iBusyTimeout;        /* Wait this many millisec on locks */
  int bBlockOnConnect;
#endif
};

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
# define winFileBusyTimeout(pDbFd) pDbFd->iBusyTimeout
#else
# define winFileBusyTimeout(pDbFd) 0
#endif

/*
** The winVfsAppData structure is used for the pAppData member for all of the
** Win32 VFS variants.
*/
typedef struct winVfsAppData winVfsAppData;
struct winVfsAppData {
47621
47622
47623
47624
47625
47626
47627






47628
47629
47630
47631
47632
47633
47634
#else
  { "GetFullPathNameW",        (SYSCALL)0,                       0 },
#endif

#define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \
        LPWSTR*))aSyscall[25].pCurrent)







  { "GetLastError",            (SYSCALL)GetLastError,            0 },

#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent)

#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
#if SQLITE_OS_WINCE
  /* The GetProcAddressA() routine is only available on Windows CE. */







>
>
>
>
>
>







47839
47840
47841
47842
47843
47844
47845
47846
47847
47848
47849
47850
47851
47852
47853
47854
47855
47856
47857
47858
#else
  { "GetFullPathNameW",        (SYSCALL)0,                       0 },
#endif

#define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \
        LPWSTR*))aSyscall[25].pCurrent)

/*
** For GetLastError(), MSDN says:
**
** Minimum supported client: Windows XP [desktop apps | UWP apps]
** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps]
*/
  { "GetLastError",            (SYSCALL)GetLastError,            0 },

#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent)

#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
#if SQLITE_OS_WINCE
  /* The GetProcAddressA() routine is only available on Windows CE. */
47903
47904
47905
47906
47907
47908
47909
47910

47911
47912




47913
47914
47915
47916
47917
47918
47919
47920
47921
#else
  { "CreateEventExW",          (SYSCALL)0,                       0 },
#endif

#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \
        DWORD,DWORD))aSyscall[62].pCurrent)

#if !SQLITE_OS_WINRT

  { "WaitForSingleObject",     (SYSCALL)WaitForSingleObject,     0 },
#else




  { "WaitForSingleObject",     (SYSCALL)0,                       0 },
#endif

#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
        DWORD))aSyscall[63].pCurrent)

#if !SQLITE_OS_WINCE
  { "WaitForSingleObjectEx",   (SYSCALL)WaitForSingleObjectEx,   0 },
#else







<
>
|
<
>
>
>
>
|
<







48127
48128
48129
48130
48131
48132
48133

48134
48135

48136
48137
48138
48139
48140

48141
48142
48143
48144
48145
48146
48147
#else
  { "CreateEventExW",          (SYSCALL)0,                       0 },
#endif

#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \
        DWORD,DWORD))aSyscall[62].pCurrent)


/*
** For WaitForSingleObject(), MSDN says:

**
** Minimum supported client: Windows XP [desktop apps | UWP apps]
** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps]
*/
  { "WaitForSingleObject",     (SYSCALL)WaitForSingleObject,     0 },


#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
        DWORD))aSyscall[63].pCurrent)

#if !SQLITE_OS_WINCE
  { "WaitForSingleObjectEx",   (SYSCALL)WaitForSingleObjectEx,   0 },
#else
48054
48055
48056
48057
48058
48059
48060


































48061
48062
48063
48064
48065
48066
48067
#else
  { "FlushViewOfFile",          (SYSCALL)0,                      0 },
#endif

#define osFlushViewOfFile \
        ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent)



































}; /* End of the overrideable system calls */

/*
** This is the xSetSystemCall() method of sqlite3_vfs for all of the
** "win32" VFSes.  Return SQLITE_OK upon successfully updating the
** system call pointer, or SQLITE_NOTFOUND if there is no configurable
** system call named zName.







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







48280
48281
48282
48283
48284
48285
48286
48287
48288
48289
48290
48291
48292
48293
48294
48295
48296
48297
48298
48299
48300
48301
48302
48303
48304
48305
48306
48307
48308
48309
48310
48311
48312
48313
48314
48315
48316
48317
48318
48319
48320
48321
48322
48323
48324
48325
48326
48327
#else
  { "FlushViewOfFile",          (SYSCALL)0,                      0 },
#endif

#define osFlushViewOfFile \
        ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent)

/*
** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CreateEvent()
** to implement blocking locks with timeouts. MSDN says:
**
** Minimum supported client: Windows XP [desktop apps | UWP apps]
** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps]
*/
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  { "CreateEvent",              (SYSCALL)CreateEvent,            0 },
#else
  { "CreateEvent",              (SYSCALL)0,                      0 },
#endif

#define osCreateEvent ( \
    (HANDLE(WINAPI*) (LPSECURITY_ATTRIBUTES,BOOL,BOOL,LPCSTR)) \
    aSyscall[80].pCurrent \
)

/*
** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CancelIo()
** for the case where a timeout expires and a lock request must be
** cancelled.
**
** Minimum supported client: Windows XP [desktop apps | UWP apps]
** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps]
*/
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  { "CancelIo",                 (SYSCALL)CancelIo,               0 },
#else
  { "CancelIo",                 (SYSCALL)0,                      0 },
#endif

#define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[81].pCurrent)

}; /* End of the overrideable system calls */

/*
** This is the xSetSystemCall() method of sqlite3_vfs for all of the
** "win32" VFSes.  Return SQLITE_OK upon successfully updating the
** system call pointer, or SQLITE_NOTFOUND if there is no configurable
** system call named zName.
48352
48353
48354
48355
48356
48357
48358
48359


48360
48361
48362
48363
48364
48365
48366
    osGetVersionExW(&sInfo);
    osInterlockedCompareExchange(&sqlite3_os_type,
        (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0);
#endif
  }
  return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
#elif SQLITE_TEST
  return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;


#else
  /*
  ** NOTE: All sub-platforms where the GetVersionEx[AW] functions are
  **       deprecated are always assumed to be based on the NT kernel.
  */
  return 1;
#endif







|
>
>







48612
48613
48614
48615
48616
48617
48618
48619
48620
48621
48622
48623
48624
48625
48626
48627
48628
    osGetVersionExW(&sInfo);
    osInterlockedCompareExchange(&sqlite3_os_type,
        (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0);
#endif
  }
  return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
#elif SQLITE_TEST
  return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2
      || osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0
  ;
#else
  /*
  ** NOTE: All sub-platforms where the GetVersionEx[AW] functions are
  **       deprecated are always assumed to be based on the NT kernel.
  */
  return 1;
#endif
49438
49439
49440
49441
49442
49443
49444















































































49445
49446
49447
49448
49449
49450
49451
    return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp);
  }else{
    return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
                      numBytesHigh);
  }
#endif
}
















































































/*
** Unlock a file region.
 */
static BOOL winUnlockFile(
  LPHANDLE phFile,
  DWORD offsetLow,







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







49700
49701
49702
49703
49704
49705
49706
49707
49708
49709
49710
49711
49712
49713
49714
49715
49716
49717
49718
49719
49720
49721
49722
49723
49724
49725
49726
49727
49728
49729
49730
49731
49732
49733
49734
49735
49736
49737
49738
49739
49740
49741
49742
49743
49744
49745
49746
49747
49748
49749
49750
49751
49752
49753
49754
49755
49756
49757
49758
49759
49760
49761
49762
49763
49764
49765
49766
49767
49768
49769
49770
49771
49772
49773
49774
49775
49776
49777
49778
49779
49780
49781
49782
49783
49784
49785
49786
49787
49788
49789
49790
49791
49792
    return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp);
  }else{
    return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
                      numBytesHigh);
  }
#endif
}

/*
** Lock a region of nByte bytes starting at offset offset of file hFile.
** Take an EXCLUSIVE lock if parameter bExclusive is true, or a SHARED lock
** otherwise. If nMs is greater than zero and the lock cannot be obtained
** immediately, block for that many ms before giving up.
**
** This function returns SQLITE_OK if the lock is obtained successfully. If
** some other process holds the lock, SQLITE_BUSY is returned if nMs==0, or
** SQLITE_BUSY_TIMEOUT otherwise. Or, if an error occurs, SQLITE_IOERR.
*/
static int winHandleLockTimeout(
  HANDLE hFile,
  DWORD offset,
  DWORD nByte,
  int bExcl,
  DWORD nMs
){
  DWORD flags = LOCKFILE_FAIL_IMMEDIATELY | (bExcl?LOCKFILE_EXCLUSIVE_LOCK:0);
  int rc = SQLITE_OK;
  BOOL ret;

  if( !osIsNT() ){
    ret = winLockFile(&hFile, flags, offset, 0, nByte, 0);
  }else{
    OVERLAPPED ovlp;
    memset(&ovlp, 0, sizeof(OVERLAPPED));
    ovlp.Offset = offset;

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    if( nMs!=0 ){
      flags &= ~LOCKFILE_FAIL_IMMEDIATELY;
    }
    ovlp.hEvent = osCreateEvent(NULL, TRUE, FALSE, NULL);
    if( ovlp.hEvent==NULL ){
      return SQLITE_IOERR_LOCK;
    }
#endif

    ret = osLockFileEx(hFile, flags, 0, nByte, 0, &ovlp);

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    /* If SQLITE_ENABLE_SETLK_TIMEOUT is defined, then the file-handle was
    ** opened with FILE_FLAG_OVERHEAD specified. In this case, the call to
    ** LockFileEx() may fail because the request is still pending. This can
    ** happen even if LOCKFILE_FAIL_IMMEDIATELY was specified.
    **
    ** If nMs is 0, then LOCKFILE_FAIL_IMMEDIATELY was set in the flags
    ** passed to LockFileEx(). In this case, if the operation is pending,
    ** block indefinitely until it is finished.
    **
    ** Otherwise, wait for up to nMs ms for the operation to finish. nMs
    ** may be set to INFINITE.
    */
    if( !ret && GetLastError()==ERROR_IO_PENDING ){
      DWORD nDelay = (nMs==0 ? INFINITE : nMs);
      DWORD res = osWaitForSingleObject(ovlp.hEvent, nDelay);
      if( res==WAIT_OBJECT_0 ){
        ret = TRUE;
      }else if( res==WAIT_TIMEOUT ){
        rc = SQLITE_BUSY_TIMEOUT;
      }else{
        /* Some other error has occurred */
        rc = SQLITE_IOERR_LOCK;
      }

      /* If it is still pending, cancel the LockFileEx() call. */
      osCancelIo(hFile);
    }

    osCloseHandle(ovlp.hEvent);
#endif
  }

  if( rc==SQLITE_OK && !ret ){
    rc = SQLITE_BUSY;
  }
  return rc;
}

/*
** Unlock a file region.
 */
static BOOL winUnlockFile(
  LPHANDLE phFile,
  DWORD offsetLow,
49469
49470
49471
49472
49473
49474
49475








49476
49477
49478
49479
49480
49481
49482
49483
49484
49485
49486
49487
49488
49489
49490

49491
49492

49493
49494


49495
49496
49497
49498
49499
49500
49501
49502
49503
49504


49505
49506
49507
49508
49509
49510
49511
49512
49513
49514
49515
49516
49517
49518
49519
49520
49521
49522
49523
49524
49525
49526
49527
49528
49529
49530
49531
49532
49533
49534
49535
49536
49537
49538
49539


49540
49541
49542
49543
49544







49545





49546
49547
49548

49549
49550
49551
49552
49553
49554
49555
    return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp);
  }else{
    return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
                        numBytesHigh);
  }
#endif
}









/*****************************************************************************
** The next group of routines implement the I/O methods specified
** by the sqlite3_io_methods object.
******************************************************************************/

/*
** Some Microsoft compilers lack this definition.
*/
#ifndef INVALID_SET_FILE_POINTER
# define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif

/*
** Move the current position of the file handle passed as the first

** argument to offset iOffset within the file. If successful, return 0.
** Otherwise, set pFile->lastErrno and return non-zero.

*/
static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){


#if !SQLITE_OS_WINRT
  LONG upperBits;                 /* Most sig. 32 bits of new offset */
  LONG lowerBits;                 /* Least sig. 32 bits of new offset */
  DWORD dwRet;                    /* Value returned by SetFilePointer() */
  DWORD lastErrno;                /* Value returned by GetLastError() */

  OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset));

  upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
  lowerBits = (LONG)(iOffset & 0xffffffff);



  /* API oddity: If successful, SetFilePointer() returns a dword
  ** containing the lower 32-bits of the new file-offset. Or, if it fails,
  ** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
  ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
  ** whether an error has actually occurred, it is also necessary to call
  ** GetLastError().
  */
  dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);

  if( (dwRet==INVALID_SET_FILE_POINTER
      && ((lastErrno = osGetLastError())!=NO_ERROR)) ){
    pFile->lastErrno = lastErrno;
    winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
                "winSeekFile", pFile->zPath);
    OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
    return 1;
  }

  OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));
  return 0;
#else
  /*
  ** Same as above, except that this implementation works for WinRT.
  */

  LARGE_INTEGER x;                /* The new offset */
  BOOL bRet;                      /* Value returned by SetFilePointerEx() */

  x.QuadPart = iOffset;
  bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN);

  if(!bRet){
    pFile->lastErrno = osGetLastError();
    winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,


                "winSeekFile", pFile->zPath);
    OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
    return 1;
  }








  OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));





  return 0;
#endif
}


#if SQLITE_MAX_MMAP_SIZE>0
/* Forward references to VFS helper methods used for memory mapped files */
static int winMapfile(winFile*, sqlite3_int64);
static int winUnmapfile(winFile*);
#endif








>
>
>
>
>
>
>
>














|
>
|
<
>

|
>
>




<
<
<



>
>






|
<
<
<
|
|
|
|
<
<
<
|
|
<
<

<
|
<
<




|


<
|
>
>
|
|
|
|

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

>







49810
49811
49812
49813
49814
49815
49816
49817
49818
49819
49820
49821
49822
49823
49824
49825
49826
49827
49828
49829
49830
49831
49832
49833
49834
49835
49836
49837
49838
49839
49840
49841

49842
49843
49844
49845
49846
49847
49848
49849
49850



49851
49852
49853
49854
49855
49856
49857
49858
49859
49860
49861
49862



49863
49864
49865
49866



49867
49868


49869

49870


49871
49872
49873
49874
49875
49876
49877

49878
49879
49880
49881
49882
49883
49884
49885
49886
49887
49888
49889
49890
49891
49892
49893
49894
49895
49896
49897
49898
49899

49900
49901
49902
49903
49904
49905
49906
49907
49908
    return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp);
  }else{
    return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
                        numBytesHigh);
  }
#endif
}

/*
** Remove an nByte lock starting at offset iOff from HANDLE h.
*/
static int winHandleUnlock(HANDLE h, int iOff, int nByte){
  BOOL ret = winUnlockFile(&h, iOff, 0, nByte, 0);
  return (ret ? SQLITE_OK : SQLITE_IOERR_UNLOCK);
}

/*****************************************************************************
** The next group of routines implement the I/O methods specified
** by the sqlite3_io_methods object.
******************************************************************************/

/*
** Some Microsoft compilers lack this definition.
*/
#ifndef INVALID_SET_FILE_POINTER
# define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif

/*
** Seek the file handle h to offset nByte of the file.
**
** If successful, return SQLITE_OK. Or, if an error occurs, return an SQLite

** error code.
*/
static int winHandleSeek(HANDLE h, sqlite3_int64 iOffset){
  int rc = SQLITE_OK;             /* Return value */

#if !SQLITE_OS_WINRT
  LONG upperBits;                 /* Most sig. 32 bits of new offset */
  LONG lowerBits;                 /* Least sig. 32 bits of new offset */
  DWORD dwRet;                    /* Value returned by SetFilePointer() */




  upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
  lowerBits = (LONG)(iOffset & 0xffffffff);

  dwRet = osSetFilePointer(h, lowerBits, &upperBits, FILE_BEGIN);

  /* API oddity: If successful, SetFilePointer() returns a dword
  ** containing the lower 32-bits of the new file-offset. Or, if it fails,
  ** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
  ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
  ** whether an error has actually occurred, it is also necessary to call
  ** GetLastError().  */



  if( dwRet==INVALID_SET_FILE_POINTER ){
    DWORD lastErrno = osGetLastError();
    if( lastErrno!=NO_ERROR ){
      rc = SQLITE_IOERR_SEEK;



    }
  }


#else

  /* This implementation works for WinRT. */


  LARGE_INTEGER x;                /* The new offset */
  BOOL bRet;                      /* Value returned by SetFilePointerEx() */

  x.QuadPart = iOffset;
  bRet = osSetFilePointerEx(h, x, 0, FILE_BEGIN);

  if(!bRet){

    rc = SQLITE_IOERR_SEEK;
  }
#endif

  OSTRACE(("SEEK file=%p, offset=%lld rc=%s\n", h, iOffset, sqlite3ErrName(rc)));
  return rc;
}

/*
** Move the current position of the file handle passed as the first
** argument to offset iOffset within the file. If successful, return 0.
** Otherwise, set pFile->lastErrno and return non-zero.
*/
static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){
  int rc;

  rc = winHandleSeek(pFile->h, iOffset);
  if( rc!=SQLITE_OK ){
    pFile->lastErrno = osGetLastError();
    winLogError(rc, pFile->lastErrno, "winSeekFile", pFile->zPath);
  }
  return rc;

}


#if SQLITE_MAX_MMAP_SIZE>0
/* Forward references to VFS helper methods used for memory mapped files */
static int winMapfile(winFile*, sqlite3_int64);
static int winUnmapfile(winFile*);
#endif

49801
49802
49803
49804
49805
49806
49807






















































49808
49809
49810
49811
49812
49813
49814
  }else{
    winLogIoerr(nRetry, __LINE__);
  }
  OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
           osGetCurrentProcessId(), pFile, pFile->h));
  return SQLITE_OK;
}























































/*
** Truncate an open file to a specified size
*/
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
  winFile *pFile = (winFile*)id;  /* File handle object */
  int rc = SQLITE_OK;             /* Return code for this function */







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







50154
50155
50156
50157
50158
50159
50160
50161
50162
50163
50164
50165
50166
50167
50168
50169
50170
50171
50172
50173
50174
50175
50176
50177
50178
50179
50180
50181
50182
50183
50184
50185
50186
50187
50188
50189
50190
50191
50192
50193
50194
50195
50196
50197
50198
50199
50200
50201
50202
50203
50204
50205
50206
50207
50208
50209
50210
50211
50212
50213
50214
50215
50216
50217
50218
50219
50220
50221
  }else{
    winLogIoerr(nRetry, __LINE__);
  }
  OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
           osGetCurrentProcessId(), pFile, pFile->h));
  return SQLITE_OK;
}

/*
** Truncate the file opened by handle h to nByte bytes in size.
*/
static int winHandleTruncate(HANDLE h, sqlite3_int64 nByte){
  int rc = SQLITE_OK;             /* Return code */
  rc = winHandleSeek(h, nByte);
  if( rc==SQLITE_OK ){
    if( 0==osSetEndOfFile(h) ){
      rc = SQLITE_IOERR_TRUNCATE;
    }
  }
  return rc;
}

/*
** Determine the size in bytes of the file opened by the handle passed as
** the first argument.
*/
static int winHandleSize(HANDLE h, sqlite3_int64 *pnByte){
  int rc = SQLITE_OK;

#if SQLITE_OS_WINRT
  FILE_STANDARD_INFO info;
  BOOL b;
  b = osGetFileInformationByHandleEx(h, FileStandardInfo, &info, sizeof(info));
  if( b ){
    *pnByte = info.EndOfFile.QuadPart;
  }else{
    rc = SQLITE_IOERR_FSTAT;
  }
#else
  DWORD upperBits = 0;
  DWORD lowerBits = 0;

  assert( pnByte );
  lowerBits = osGetFileSize(h, &upperBits);
  *pnByte = (((sqlite3_int64)upperBits)<<32) + lowerBits;
  if( lowerBits==INVALID_FILE_SIZE && osGetLastError()!=NO_ERROR ){
    rc = SQLITE_IOERR_FSTAT;
  }
#endif

  return rc;
}

/*
** Close the handle passed as the only argument.
*/
static void winHandleClose(HANDLE h){
  if( h!=INVALID_HANDLE_VALUE ){
    osCloseHandle(h);
  }
}

/*
** Truncate an open file to a specified size
*/
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
  winFile *pFile = (winFile*)id;  /* File handle object */
  int rc = SQLITE_OK;             /* Return code for this function */
50057
50058
50059
50060
50061
50062
50063
50064
50065

50066
50067
50068
50069
50070
50071
50072
50073
50074
50075
50076
50077
50078
50079
50080
50081
50082
50083
50084
50085
50086
50087
50088
50089
50090
50091
#endif

/*
** Acquire a reader lock.
** Different API routines are called depending on whether or not this
** is Win9x or WinNT.
*/
static int winGetReadLock(winFile *pFile){
  int res;

  OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
  if( osIsNT() ){
#if SQLITE_OS_WINCE
    /*
    ** NOTE: Windows CE is handled differently here due its lack of the Win32
    **       API LockFileEx.
    */
    res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0);
#else
    res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0,
                      SHARED_SIZE, 0);
#endif
  }
#ifdef SQLITE_WIN32_HAS_ANSI
  else{
    int lk;
    sqlite3_randomness(sizeof(lk), &lk);
    pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1));
    res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS,
                      SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
  }
#endif
  if( res == 0 ){
    pFile->lastErrno = osGetLastError();
    /* No need to log a failure to lock */
  }







|

>









|








|







50464
50465
50466
50467
50468
50469
50470
50471
50472
50473
50474
50475
50476
50477
50478
50479
50480
50481
50482
50483
50484
50485
50486
50487
50488
50489
50490
50491
50492
50493
50494
50495
50496
50497
50498
50499
#endif

/*
** Acquire a reader lock.
** Different API routines are called depending on whether or not this
** is Win9x or WinNT.
*/
static int winGetReadLock(winFile *pFile, int bBlock){
  int res;
  DWORD mask = ~(bBlock ? LOCKFILE_FAIL_IMMEDIATELY : 0);
  OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
  if( osIsNT() ){
#if SQLITE_OS_WINCE
    /*
    ** NOTE: Windows CE is handled differently here due its lack of the Win32
    **       API LockFileEx.
    */
    res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0);
#else
    res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS&mask, SHARED_FIRST, 0,
                      SHARED_SIZE, 0);
#endif
  }
#ifdef SQLITE_WIN32_HAS_ANSI
  else{
    int lk;
    sqlite3_randomness(sizeof(lk), &lk);
    pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1));
    res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS&mask,
                      SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
  }
#endif
  if( res == 0 ){
    pFile->lastErrno = osGetLastError();
    /* No need to log a failure to lock */
  }
50172
50173
50174
50175
50176
50177
50178
50179
50180
50181
50182
50183
50184
50185
50186
50187
50188







50189
50190
50191
50192

50193
50194


50195
50196
50197
50198


50199
50200
50201
50202
50203

50204
50205


50206
50207
50208
50209
50210
50211
50212
50213
50214
50215
50216
50217

50218



50219
50220
50221
50222
50223
50224
50225

  /* Make sure the locking sequence is correct
  */
  assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
  assert( locktype!=PENDING_LOCK );
  assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );

  /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
  ** a SHARED lock.  If we are acquiring a SHARED lock, the acquisition of
  ** the PENDING_LOCK byte is temporary.
  */
  newLocktype = pFile->locktype;
  if( pFile->locktype==NO_LOCK
   || (locktype==EXCLUSIVE_LOCK && pFile->locktype<=RESERVED_LOCK)
  ){
    int cnt = 3;
    while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS,







                                         PENDING_BYTE, 0, 1, 0))==0 ){
      /* Try 3 times to get the pending lock.  This is needed to work
      ** around problems caused by indexing and/or anti-virus software on
      ** Windows systems.

      ** If you are using this code as a model for alternative VFSes, do not
      ** copy this retry logic.  It is a hack intended for Windows only.


      */
      lastErrno = osGetLastError();
      OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n",
               pFile->h, cnt, res));


      if( lastErrno==ERROR_INVALID_HANDLE ){
        pFile->lastErrno = lastErrno;
        rc = SQLITE_IOERR_LOCK;
        OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n",
                 pFile->h, cnt, sqlite3ErrName(rc)));

        return rc;
      }


      if( cnt ) sqlite3_win32_sleep(1);
    }
    gotPendingLock = res;
    if( !res ){
      lastErrno = osGetLastError();
    }
  }

  /* Acquire a shared lock
  */
  if( locktype==SHARED_LOCK && res ){
    assert( pFile->locktype==NO_LOCK );

    res = winGetReadLock(pFile);



    if( res ){
      newLocktype = SHARED_LOCK;
    }else{
      lastErrno = osGetLastError();
    }
  }








|




|
|


|
>
>
>
>
>
>
>
|



>

|
>
>
|


|
>
>




|
>


>
>
|


<
<
<






>
|
>
>
>







50580
50581
50582
50583
50584
50585
50586
50587
50588
50589
50590
50591
50592
50593
50594
50595
50596
50597
50598
50599
50600
50601
50602
50603
50604
50605
50606
50607
50608
50609
50610
50611
50612
50613
50614
50615
50616
50617
50618
50619
50620
50621
50622
50623
50624
50625
50626
50627
50628
50629
50630
50631



50632
50633
50634
50635
50636
50637
50638
50639
50640
50641
50642
50643
50644
50645
50646
50647
50648
50649

  /* Make sure the locking sequence is correct
  */
  assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
  assert( locktype!=PENDING_LOCK );
  assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );

  /* Lock the PENDING_LOCK byte if we need to acquire an EXCLUSIVE lock or
  ** a SHARED lock.  If we are acquiring a SHARED lock, the acquisition of
  ** the PENDING_LOCK byte is temporary.
  */
  newLocktype = pFile->locktype;
  if( locktype==SHARED_LOCK
   || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK)
  ){
    int cnt = 3;

    /* Flags for the LockFileEx() call. This should be an exclusive lock if
    ** this call is to obtain EXCLUSIVE, or a shared lock if this call is to
    ** obtain SHARED.  */
    int flags = LOCKFILE_FAIL_IMMEDIATELY;
    if( locktype==EXCLUSIVE_LOCK ){
      flags |= LOCKFILE_EXCLUSIVE_LOCK;
    }
    while( cnt>0 ){
      /* Try 3 times to get the pending lock.  This is needed to work
      ** around problems caused by indexing and/or anti-virus software on
      ** Windows systems.
      **
      ** If you are using this code as a model for alternative VFSes, do not
      ** copy this retry logic.  It is a hack intended for Windows only.  */
      res = winLockFile(&pFile->h, flags, PENDING_BYTE, 0, 1, 0);
      if( res ) break;

      lastErrno = osGetLastError();
      OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n",
            pFile->h, cnt, res
      ));

      if( lastErrno==ERROR_INVALID_HANDLE ){
        pFile->lastErrno = lastErrno;
        rc = SQLITE_IOERR_LOCK;
        OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n",
              pFile->h, cnt, sqlite3ErrName(rc)
        ));
        return rc;
      }

      cnt--;
      if( cnt>0 ) sqlite3_win32_sleep(1);
    }
    gotPendingLock = res;



  }

  /* Acquire a shared lock
  */
  if( locktype==SHARED_LOCK && res ){
    assert( pFile->locktype==NO_LOCK );
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    res = winGetReadLock(pFile, pFile->bBlockOnConnect);
#else
    res = winGetReadLock(pFile, 0);
#endif
    if( res ){
      newLocktype = SHARED_LOCK;
    }else{
      lastErrno = osGetLastError();
    }
  }

50249
50250
50251
50252
50253
50254
50255
50256
50257
50258
50259
50260
50261
50262
50263
    (void)winUnlockReadLock(pFile);
    res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0,
                      SHARED_SIZE, 0);
    if( res ){
      newLocktype = EXCLUSIVE_LOCK;
    }else{
      lastErrno = osGetLastError();
      winGetReadLock(pFile);
    }
  }

  /* If we are holding a PENDING lock that ought to be released, then
  ** release it now.
  */
  if( gotPendingLock && locktype==SHARED_LOCK ){







|







50673
50674
50675
50676
50677
50678
50679
50680
50681
50682
50683
50684
50685
50686
50687
    (void)winUnlockReadLock(pFile);
    res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0,
                      SHARED_SIZE, 0);
    if( res ){
      newLocktype = EXCLUSIVE_LOCK;
    }else{
      lastErrno = osGetLastError();
      winGetReadLock(pFile, 0);
    }
  }

  /* If we are holding a PENDING lock that ought to be released, then
  ** release it now.
  */
  if( gotPendingLock && locktype==SHARED_LOCK ){
50329
50330
50331
50332
50333
50334
50335
50336
50337
50338
50339
50340
50341
50342
50343
  assert( pFile!=0 );
  assert( locktype<=SHARED_LOCK );
  OSTRACE(("UNLOCK file=%p, oldLock=%d(%d), newLock=%d\n",
           pFile->h, pFile->locktype, pFile->sharedLockByte, locktype));
  type = pFile->locktype;
  if( type>=EXCLUSIVE_LOCK ){
    winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
    if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){
      /* This should never happen.  We should always be able to
      ** reacquire the read lock */
      rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(),
                       "winUnlock", pFile->zPath);
    }
  }
  if( type>=RESERVED_LOCK ){







|







50753
50754
50755
50756
50757
50758
50759
50760
50761
50762
50763
50764
50765
50766
50767
  assert( pFile!=0 );
  assert( locktype<=SHARED_LOCK );
  OSTRACE(("UNLOCK file=%p, oldLock=%d(%d), newLock=%d\n",
           pFile->h, pFile->locktype, pFile->sharedLockByte, locktype));
  type = pFile->locktype;
  if( type>=EXCLUSIVE_LOCK ){
    winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
    if( locktype==SHARED_LOCK && !winGetReadLock(pFile, 0) ){
      /* This should never happen.  We should always be able to
      ** reacquire the read lock */
      rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(),
                       "winUnlock", pFile->zPath);
    }
  }
  if( type>=RESERVED_LOCK ){
50539
50540
50541
50542
50543
50544
50545






















50546
50547
50548
50549
50550
50551
50552
          rc = winMapfile(pFile, -1);
        }
      }
      OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
      return rc;
    }
#endif






















  }
  OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h));
  return SQLITE_NOTFOUND;
}

/*
** Return the sector size in bytes of the underlying block device for







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







50963
50964
50965
50966
50967
50968
50969
50970
50971
50972
50973
50974
50975
50976
50977
50978
50979
50980
50981
50982
50983
50984
50985
50986
50987
50988
50989
50990
50991
50992
50993
50994
50995
50996
50997
50998
          rc = winMapfile(pFile, -1);
        }
      }
      OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
      return rc;
    }
#endif

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    case SQLITE_FCNTL_LOCK_TIMEOUT: {
      int iOld = pFile->iBusyTimeout;
      int iNew = *(int*)pArg;
#if SQLITE_ENABLE_SETLK_TIMEOUT==1
      pFile->iBusyTimeout = (iNew < 0) ? INFINITE : (DWORD)iNew;
#elif SQLITE_ENABLE_SETLK_TIMEOUT==2
      pFile->iBusyTimeout = (DWORD)(!!iNew);
#else
# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2"
#endif
      *(int*)pArg = iOld;
      return SQLITE_OK;
    }
    case SQLITE_FCNTL_BLOCK_ON_CONNECT: {
      int iNew = *(int*)pArg;
      pFile->bBlockOnConnect = iNew;
      return SQLITE_OK;
    }
#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */

  }
  OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h));
  return SQLITE_NOTFOUND;
}

/*
** Return the sector size in bytes of the underlying block device for
50619
50620
50621
50622
50623
50624
50625
50626
50627
50628
50629
50630
50631
50632





50633
50634
50635
50636
50637
50638


50639
50640
50641
50642
50643
50644
50645
50646
50647
50648
50649
50650
50651
50652
50653
50654
50655
50656
50657
50658
50659
50660
50661
50662
50663
50664
50665
50666
50667
50668
50669
50670
50671
50672
50673
50674
50675
50676
50677
50678
50679
50680
50681
50682
50683


50684
50685
50686
50687
50688
50689
50690
50691
50692
50693
50694
50695
50696
50697
50698
50699
50700
50701
50702
50703
50704
50705
50706
50707
50708
50709
50710
50711
50712
50713
50714
50715
50716
50717
50718
50719
50720
50721
50722
50723
50724
50725
50726
50727
50728
50729
50730
50731
50732
50733
50734
50735
50736
50737
50738
50739
50740
50741
50742
50743
50744
50745
** this object or while reading or writing the following fields:
**
**      nRef
**      pNext
**
** The following fields are read-only after the object is created:
**
**      fid
**      zFilename
**
** Either winShmNode.mutex must be held or winShmNode.nRef==0 and
** winShmMutexHeld() is true when reading or writing any other field
** in this structure.
**





*/
struct winShmNode {
  sqlite3_mutex *mutex;      /* Mutex to access this object */
  char *zFilename;           /* Name of the file */
  winFile hFile;             /* File handle from winOpen */



  int szRegion;              /* Size of shared-memory regions */
  int nRegion;               /* Size of array apRegion */
  u8 isReadonly;             /* True if read-only */
  u8 isUnlocked;             /* True if no DMS lock held */

  struct ShmRegion {
    HANDLE hMap;             /* File handle from CreateFileMapping */
    void *pMap;
  } *aRegion;
  DWORD lastErrno;           /* The Windows errno from the last I/O error */

  int nRef;                  /* Number of winShm objects pointing to this */
  winShm *pFirst;            /* All winShm objects pointing to this */
  winShmNode *pNext;         /* Next in list of all winShmNode objects */
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
  u8 nextShmId;              /* Next available winShm.id value */
#endif
};

/*
** A global array of all winShmNode objects.
**
** The winShmMutexHeld() must be true while reading or writing this list.
*/
static winShmNode *winShmNodeList = 0;

/*
** Structure used internally by this VFS to record the state of an
** open shared memory connection.
**
** The following fields are initialized when this object is created and
** are read-only thereafter:
**
**    winShm.pShmNode
**    winShm.id
**
** All other fields are read/write.  The winShm.pShmNode->mutex must be held
** while accessing any read/write fields.
*/
struct winShm {
  winShmNode *pShmNode;      /* The underlying winShmNode object */
  winShm *pNext;             /* Next winShm with the same winShmNode */
  u8 hasMutex;               /* True if holding the winShmNode mutex */
  u16 sharedMask;            /* Mask of shared locks held */
  u16 exclMask;              /* Mask of exclusive locks held */


#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
  u8 id;                     /* Id of this connection with its winShmNode */
#endif
};

/*
** Constants used for locking
*/
#define WIN_SHM_BASE   ((22+SQLITE_SHM_NLOCK)*4)        /* first lock byte */
#define WIN_SHM_DMS    (WIN_SHM_BASE+SQLITE_SHM_NLOCK)  /* deadman switch */

/*
** Apply advisory locks for all n bytes beginning at ofst.
*/
#define WINSHM_UNLCK  1
#define WINSHM_RDLCK  2
#define WINSHM_WRLCK  3
static int winShmSystemLock(
  winShmNode *pFile,    /* Apply locks to this open shared-memory segment */
  int lockType,         /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */
  int ofst,             /* Offset to first byte to be locked/unlocked */
  int nByte             /* Number of bytes to lock or unlock */
){
  int rc = 0;           /* Result code form Lock/UnlockFileEx() */

  /* Access to the winShmNode object is serialized by the caller */
  assert( pFile->nRef==0 || sqlite3_mutex_held(pFile->mutex) );

  OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n",
           pFile->hFile.h, lockType, ofst, nByte));

  /* Release/Acquire the system-level lock */
  if( lockType==WINSHM_UNLCK ){
    rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0);
  }else{
    /* Initialize the locking parameters */
    DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
    if( lockType == WINSHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
    rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0);
  }

  if( rc!= 0 ){
    rc = SQLITE_OK;
  }else{
    pFile->lastErrno =  osGetLastError();
    rc = SQLITE_BUSY;
  }

  OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n",
           pFile->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" :
           "winLockFile", pFile->lastErrno, sqlite3ErrName(rc)));

  return rc;
}

/* Forward references to VFS methods */
static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*);
static int winDelete(sqlite3_vfs *,const char*,int);

/*
** Purge the winShmNodeList list of all entries with winShmNode.nRef==0.
**







<






>
>
>
>
>




|

>
>


<
<








<















|
<
<
<
<
<
|
<
<
<



<
<


>
>











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







51065
51066
51067
51068
51069
51070
51071

51072
51073
51074
51075
51076
51077
51078
51079
51080
51081
51082
51083
51084
51085
51086
51087
51088
51089
51090
51091
51092


51093
51094
51095
51096
51097
51098
51099
51100

51101
51102
51103
51104
51105
51106
51107
51108
51109
51110
51111
51112
51113
51114
51115
51116





51117



51118
51119
51120


51121
51122
51123
51124
51125
51126
51127
51128
51129
51130
51131
51132
51133
51134
51135












































51136
51137
51138
51139
51140
51141
51142
** this object or while reading or writing the following fields:
**
**      nRef
**      pNext
**
** The following fields are read-only after the object is created:
**

**      zFilename
**
** Either winShmNode.mutex must be held or winShmNode.nRef==0 and
** winShmMutexHeld() is true when reading or writing any other field
** in this structure.
**
** File-handle hSharedShm is used to (a) take the DMS lock, (b) truncate
** the *-shm file if the DMS-locking protocol demands it, and (c) map
** regions of the *-shm file into memory using MapViewOfFile() or
** similar. Other locks are taken by individual clients using the
** winShm.hShm handles.
*/
struct winShmNode {
  sqlite3_mutex *mutex;      /* Mutex to access this object */
  char *zFilename;           /* Name of the file */
  HANDLE hSharedShm;         /* File handle open on zFilename */

  int isUnlocked;            /* DMS lock has not yet been obtained */
  int isReadonly;            /* True if read-only */
  int szRegion;              /* Size of shared-memory regions */
  int nRegion;               /* Size of array apRegion */



  struct ShmRegion {
    HANDLE hMap;             /* File handle from CreateFileMapping */
    void *pMap;
  } *aRegion;
  DWORD lastErrno;           /* The Windows errno from the last I/O error */

  int nRef;                  /* Number of winShm objects pointing to this */

  winShmNode *pNext;         /* Next in list of all winShmNode objects */
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
  u8 nextShmId;              /* Next available winShm.id value */
#endif
};

/*
** A global array of all winShmNode objects.
**
** The winShmMutexHeld() must be true while reading or writing this list.
*/
static winShmNode *winShmNodeList = 0;

/*
** Structure used internally by this VFS to record the state of an
** open shared memory connection. There is one such structure for each





** winFile open on a wal mode database.



*/
struct winShm {
  winShmNode *pShmNode;      /* The underlying winShmNode object */


  u16 sharedMask;            /* Mask of shared locks held */
  u16 exclMask;              /* Mask of exclusive locks held */
  HANDLE hShm;               /* File-handle on *-shm file. For locking. */
  int bReadonly;             /* True if hShm is opened read-only */
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
  u8 id;                     /* Id of this connection with its winShmNode */
#endif
};

/*
** Constants used for locking
*/
#define WIN_SHM_BASE   ((22+SQLITE_SHM_NLOCK)*4)        /* first lock byte */
#define WIN_SHM_DMS    (WIN_SHM_BASE+SQLITE_SHM_NLOCK)  /* deadman switch */













































/* Forward references to VFS methods */
static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*);
static int winDelete(sqlite3_vfs *,const char*,int);

/*
** Purge the winShmNodeList list of all entries with winShmNode.nRef==0.
**
50763
50764
50765
50766
50767
50768
50769
50770
50771
50772
50773
50774
50775
50776
50777
50778
50779
50780
50781
50782
50783
50784
50785
50786
50787
50788
50789
50790
50791
50792

50793
50794
50795
50796
50797
50798
50799
50800

50801
50802


50803



50804















50805


50806
50807

50808
50809

















50810

50811















50812





50813






50814












50815




































50816






50817
50818
50819





50820
50821

50822
50823
50824
50825
50826
50827
50828
50829
50830
50831
50832
50833
50834
50835
50836
50837
50838
50839
50840
50841
50842
50843
50844
50845
50846
50847
50848
50849


50850
50851
50852






50853
50854
50855
50856
50857
50858
50859
50860
50861
50862
50863
50864
50865
50866
50867
50868
50869
50870
50871
50872
50873
50874

50875
50876
50877
50878
50879
50880
50881






50882
50883


50884



50885
50886
50887
50888
50889
50890
50891
50892
50893
50894
50895
50896
50897
50898
50899
50900
50901

50902

50903

50904
50905
50906
50907
50908

50909

50910
50911
50912
50913
50914
50915
50916
50917
50918
50919
50920
50921
50922
50923
50924
50925
50926
50927
50928
50929
50930
50931
50932
50933
50934
50935
50936
50937
50938
50939
50940
50941
50942
50943
50944
50945
50946
50947
50948
50949
50950

50951
50952
50953
50954
50955
50956
50957
50958
50959
50960
50961
50962
50963
50964
50965
50966
50967
50968
50969
50970
50971
50972



50973
50974
50975
50976
50977
50978
50979
50980
50981
50982
50983
50984
50985
50986
50987
50988
50989
50990
50991
50992
50993
50994
50995
50996
50997
50998
50999
51000
51001
51002
51003



51004
51005
51006
51007



51008




51009
51010
51011
51012
51013
51014
51015
51016

51017
51018
51019
51020
51021
51022


51023
51024
51025
51026
51027
51028
51029
51030
51031


51032
51033
51034
51035


51036
51037

51038
51039
51040
51041
51042


51043
51044
51045
51046
51047
51048
51049
51050



51051

51052
51053
51054

51055
51056
51057
51058
51059
51060

51061
51062
51063
51064

51065

51066
51067
51068
51069
51070
51071
51072
51073
51074
51075
51076
51077
51078
51079

51080


51081
51082

51083
51084
51085
51086
51087
51088
51089
                 osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
        UNUSED_VARIABLE_VALUE(bRc);
        bRc = osCloseHandle(p->aRegion[i].hMap);
        OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n",
                 osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
        UNUSED_VARIABLE_VALUE(bRc);
      }
      if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){
        SimulateIOErrorBenign(1);
        winClose((sqlite3_file *)&p->hFile);
        SimulateIOErrorBenign(0);
      }
      if( deleteFlag ){
        SimulateIOErrorBenign(1);
        sqlite3BeginBenignMalloc();
        winDelete(pVfs, p->zFilename, 0);
        sqlite3EndBenignMalloc();
        SimulateIOErrorBenign(0);
      }
      *pp = p->pNext;
      sqlite3_free(p->aRegion);
      sqlite3_free(p);
    }else{
      pp = &p->pNext;
    }
  }
}

/*
** The DMS lock has not yet been taken on shm file pShmNode. Attempt to

** take it now. Return SQLITE_OK if successful, or an SQLite error
** code otherwise.
**
** If the DMS cannot be locked because this is a readonly_shm=1
** connection and no other process already holds a lock, return
** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1.
*/
static int winLockSharedMemory(winShmNode *pShmNode){

  int rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1);



  if( rc==SQLITE_OK ){



    if( pShmNode->isReadonly ){















      pShmNode->isUnlocked = 1;


      winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
      return SQLITE_READONLY_CANTINIT;

    }else if( winTruncate((sqlite3_file*)&pShmNode->hFile, 0) ){
      winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);

















      return winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),

                         "winLockSharedMemory", pShmNode->zFilename);















    }





  }



















  if( rc==SQLITE_OK ){




































    winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);






  }

  return winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1);





}


/*
** Open the shared-memory area associated with database file pDbFd.
**
** When opening a new shared-memory file, if no other instances of that
** file are currently open, in this process or in other processes, then
** the file must be truncated to zero length or have its header cleared.
*/
static int winOpenSharedMemory(winFile *pDbFd){
  struct winShm *p;                  /* The connection to be opened */
  winShmNode *pShmNode = 0;          /* The underlying mmapped file */
  int rc = SQLITE_OK;                /* Result code */
  winShmNode *pNew;                  /* Newly allocated winShmNode */
  int nName;                         /* Size of zName in bytes */

  assert( pDbFd->pShm==0 );    /* Not previously opened */

  /* Allocate space for the new sqlite3_shm object.  Also speculatively
  ** allocate space for a new winShmNode and filename.
  */
  p = sqlite3MallocZero( sizeof(*p) );
  if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT;
  nName = sqlite3Strlen30(pDbFd->zPath);
  pNew = sqlite3MallocZero( sizeof(*pShmNode) + (i64)nName + 17 );
  if( pNew==0 ){
    sqlite3_free(p);
    return SQLITE_IOERR_NOMEM_BKPT;
  }
  pNew->zFilename = (char*)&pNew[1];


  sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
  sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);







  /* Look to see if there is an existing winShmNode that can be used.
  ** If no matching winShmNode currently exists, create a new one.
  */
  winShmEnterMutex();
  for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){
    /* TBD need to come up with better match here.  Perhaps
    ** use FILE_ID_BOTH_DIR_INFO Structure.
    */
    if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break;
  }
  if( pShmNode ){
    sqlite3_free(pNew);
  }else{
    int inFlags = SQLITE_OPEN_WAL;
    int outFlags = 0;

    pShmNode = pNew;
    pNew = 0;
    ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE;
    pShmNode->pNext = winShmNodeList;
    winShmNodeList = pShmNode;


    if( sqlite3GlobalConfig.bCoreMutex ){
      pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
      if( pShmNode->mutex==0 ){
        rc = SQLITE_IOERR_NOMEM_BKPT;
        goto shm_open_err;
      }
    }







    if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){


      inFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;



    }else{
      inFlags |= SQLITE_OPEN_READONLY;
    }
    rc = winOpen(pDbFd->pVfs, pShmNode->zFilename,
                 (sqlite3_file*)&pShmNode->hFile,
                 inFlags, &outFlags);
    if( rc!=SQLITE_OK ){
      rc = winLogError(rc, osGetLastError(), "winOpenShm",
                       pShmNode->zFilename);
      goto shm_open_err;
    }
    if( outFlags==SQLITE_OPEN_READONLY ) pShmNode->isReadonly = 1;

    rc = winLockSharedMemory(pShmNode);
    if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err;
  }


  /* Make the new connection a child of the winShmNode */

  p->pShmNode = pShmNode;

#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
  p->id = pShmNode->nextShmId++;
#endif
  pShmNode->nRef++;
  pDbFd->pShm = p;

  winShmLeaveMutex();


  /* The reference count on pShmNode has already been incremented under
  ** the cover of the winShmEnterMutex() mutex and the pointer from the
  ** new (struct winShm) object to the pShmNode has been set. All that is
  ** left to do is to link the new object into the linked list starting
  ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
  ** mutex.
  */
  sqlite3_mutex_enter(pShmNode->mutex);
  p->pNext = pShmNode->pFirst;
  pShmNode->pFirst = p;
  sqlite3_mutex_leave(pShmNode->mutex);
  return rc;

  /* Jump here on any error */
shm_open_err:
  winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
  winShmPurge(pDbFd->pVfs, 0);      /* This call frees pShmNode if required */
  sqlite3_free(p);
  sqlite3_free(pNew);
  winShmLeaveMutex();
  return rc;
}

/*
** Close a connection to shared-memory.  Delete the underlying
** storage if deleteFlag is true.
*/
static int winShmUnmap(
  sqlite3_file *fd,          /* Database holding shared memory */
  int deleteFlag             /* Delete after closing if true */
){
  winFile *pDbFd;       /* Database holding shared-memory */
  winShm *p;            /* The connection to be closed */
  winShmNode *pShmNode; /* The underlying shared-memory file */
  winShm **pp;          /* For looping over sibling connections */

  pDbFd = (winFile*)fd;
  p = pDbFd->pShm;
  if( p==0 ) return SQLITE_OK;
  pShmNode = p->pShmNode;


  /* Remove connection p from the set of connections associated
  ** with pShmNode */
  sqlite3_mutex_enter(pShmNode->mutex);
  for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){}
  *pp = p->pNext;

  /* Free the connection p */
  sqlite3_free(p);
  pDbFd->pShm = 0;
  sqlite3_mutex_leave(pShmNode->mutex);

  /* If pShmNode->nRef has reached 0, then close the underlying
  ** shared-memory file, too */
  winShmEnterMutex();
  assert( pShmNode->nRef>0 );
  pShmNode->nRef--;
  if( pShmNode->nRef==0 ){
    winShmPurge(pDbFd->pVfs, deleteFlag);
  }
  winShmLeaveMutex();




  return SQLITE_OK;
}

/*
** Change the lock state for a shared-memory segment.
*/
static int winShmLock(
  sqlite3_file *fd,          /* Database file holding the shared memory */
  int ofst,                  /* First lock to acquire or release */
  int n,                     /* Number of locks to acquire or release */
  int flags                  /* What to do with the lock */
){
  winFile *pDbFd = (winFile*)fd;        /* Connection holding shared memory */
  winShm *p = pDbFd->pShm;              /* The shared memory being locked */
  winShm *pX;                           /* For looping over all siblings */
  winShmNode *pShmNode;
  int rc = SQLITE_OK;                   /* Result code */
  u16 mask;                             /* Mask of locks to take or release */

  if( p==0 ) return SQLITE_IOERR_SHMLOCK;
  pShmNode = p->pShmNode;
  if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK;

  assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK );
  assert( n>=1 );
  assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
       || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
  assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );




  mask = (u16)((1U<<(ofst+n)) - (1U<<ofst));
  assert( n>1 || mask==(1<<ofst) );
  sqlite3_mutex_enter(pShmNode->mutex);
  if( flags & SQLITE_SHM_UNLOCK ){



    u16 allMask = 0; /* Mask of locks held by siblings */





    /* See if any siblings hold this same lock */
    for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
      if( pX==p ) continue;
      assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
      allMask |= pX->sharedMask;
    }


    /* Unlock the system-level locks */
    if( (mask & allMask)==0 ){
      rc = winShmSystemLock(pShmNode, WINSHM_UNLCK, ofst+WIN_SHM_BASE, n);
    }else{
      rc = SQLITE_OK;
    }



    /* Undo the local locks */
    if( rc==SQLITE_OK ){
      p->exclMask &= ~mask;
      p->sharedMask &= ~mask;
    }
  }else if( flags & SQLITE_SHM_SHARED ){
    u16 allShared = 0;  /* Union of locks held by connections other than "p" */



    /* Find out which shared locks are already held by sibling connections.
    ** If any sibling already holds an exclusive lock, go ahead and return
    ** SQLITE_BUSY.
    */


    for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
      if( (pX->exclMask & mask)!=0 ){

        rc = SQLITE_BUSY;
        break;
      }
      allShared |= pX->sharedMask;
    }



    /* Get shared locks at the system level, if necessary */
    if( rc==SQLITE_OK ){
      if( (allShared & mask)==0 ){
        rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n);
      }else{
        rc = SQLITE_OK;
      }



    }


    /* Get the local shared locks */
    if( rc==SQLITE_OK ){

      p->sharedMask |= mask;
    }
  }else{
    /* Make sure no sibling connections hold locks that will block this
    ** lock.  If any do, return SQLITE_BUSY right away.
    */

    for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
      if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){
        rc = SQLITE_BUSY;
        break;

      }

    }

    /* Get the exclusive locks at the system level.  Then if successful
    ** also mark the local connection as being locked.
    */
    if( rc==SQLITE_OK ){
      rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n);
      if( rc==SQLITE_OK ){
        assert( (p->sharedMask & mask)==0 );
        p->exclMask |= mask;
      }
    }
  }
  sqlite3_mutex_leave(pShmNode->mutex);

  OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n",


           osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask,
           sqlite3ErrName(rc)));

  return rc;
}

/*
** Implement a memory barrier or memory fence on shared memory.
**
** All loads and stores begun before the barrier must complete before







<
<
|
<
<

















|
>
|
<
<
<
<
<

|
>
|

>
>

>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
|
|
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
|
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
|
|
|
>
>
>
>
>


>


<
<
<
<











|
<









>
>



>
>
>
>
>
>

|
<



|
<


|
<
<
<
<
<

<
<
<
<

>


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

<
<
|
|
<
<
<
|
<
|
<
|
<
<


>
|
>
|
>

|

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

<
<
|
|
<

<














<




|
>
|
<
<
<
<
<

<
<
|
|


|
<







>
>
>














<


|













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

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

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







51160
51161
51162
51163
51164
51165
51166


51167


51168
51169
51170
51171
51172
51173
51174
51175
51176
51177
51178
51179
51180
51181
51182
51183
51184
51185
51186
51187





51188
51189
51190
51191
51192
51193
51194
51195
51196
51197
51198
51199
51200
51201
51202
51203
51204
51205
51206
51207
51208
51209
51210
51211
51212
51213
51214
51215
51216
51217
51218
51219
51220
51221
51222
51223
51224
51225
51226
51227
51228
51229
51230
51231
51232
51233
51234
51235
51236
51237
51238
51239
51240
51241
51242
51243
51244
51245
51246
51247
51248
51249
51250
51251
51252
51253
51254
51255
51256
51257
51258
51259
51260
51261
51262
51263
51264
51265
51266
51267
51268
51269
51270
51271
51272
51273
51274
51275
51276
51277
51278
51279
51280
51281
51282
51283
51284
51285
51286
51287
51288
51289
51290
51291
51292
51293
51294
51295
51296
51297
51298
51299
51300
51301
51302
51303
51304
51305
51306
51307
51308
51309
51310
51311
51312
51313
51314
51315
51316
51317
51318
51319
51320
51321
51322
51323
51324
51325
51326
51327
51328
51329
51330
51331
51332
51333
51334
51335
51336
51337
51338
51339
51340




51341
51342
51343
51344
51345
51346
51347
51348
51349
51350
51351
51352

51353
51354
51355
51356
51357
51358
51359
51360
51361
51362
51363
51364
51365
51366
51367
51368
51369
51370
51371
51372
51373
51374

51375
51376
51377
51378

51379
51380
51381





51382




51383
51384
51385
51386
51387


51388
51389
51390
51391
51392
51393
51394
51395
51396
51397
51398
51399
51400
51401
51402
51403
51404


51405
51406



51407

51408

51409


51410
51411
51412
51413
51414
51415
51416
51417
51418
51419

51420
51421
51422
51423
51424












51425


51426
51427

51428

51429
51430
51431
51432
51433
51434
51435
51436
51437
51438
51439
51440
51441
51442

51443
51444
51445
51446
51447
51448
51449





51450


51451
51452
51453
51454
51455

51456
51457
51458
51459
51460
51461
51462
51463
51464
51465
51466
51467
51468
51469
51470
51471
51472
51473
51474
51475
51476
51477
51478
51479

51480
51481
51482
51483
51484
51485
51486
51487
51488
51489
51490
51491
51492
51493
51494
51495
51496
51497
51498
51499
51500

51501
51502
51503
51504
51505
51506
51507
51508
51509
51510



51511


51512
51513
51514
51515




51516
51517
51518

51519


51520


51521
51522
51523
51524
51525

51526
51527
51528
51529
51530
51531
51532


51533

51534
51535
51536

51537


51538

51539
51540
51541
51542
51543
51544
51545
51546
51547
51548
51549
51550
51551

51552

51553
51554

51555
51556
51557
51558
51559
51560
51561








51562
51563
51564

51565
51566
51567
51568
51569
51570
51571
51572
51573
51574
51575
51576
51577
51578
                 osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
        UNUSED_VARIABLE_VALUE(bRc);
        bRc = osCloseHandle(p->aRegion[i].hMap);
        OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n",
                 osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
        UNUSED_VARIABLE_VALUE(bRc);
      }


      winHandleClose(p->hSharedShm);


      if( deleteFlag ){
        SimulateIOErrorBenign(1);
        sqlite3BeginBenignMalloc();
        winDelete(pVfs, p->zFilename, 0);
        sqlite3EndBenignMalloc();
        SimulateIOErrorBenign(0);
      }
      *pp = p->pNext;
      sqlite3_free(p->aRegion);
      sqlite3_free(p);
    }else{
      pp = &p->pNext;
    }
  }
}

/*
** The DMS lock has not yet been taken on the shm file associated with
** pShmNode. Take the lock. Truncate the *-shm file if required.
** Return SQLITE_OK if successful, or an SQLite error code otherwise.





*/
static int winLockSharedMemory(winShmNode *pShmNode, DWORD nMs){
  HANDLE h = pShmNode->hSharedShm;
  int rc = SQLITE_OK;

  assert( sqlite3_mutex_held(pShmNode->mutex) );
  rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 1, 0);
  if( rc==SQLITE_OK ){
    /* We have an EXCLUSIVE lock on the DMS byte. This means that this
    ** is the first process to open the file. Truncate it to zero bytes
    ** in this case.  */
    if( pShmNode->isReadonly ){
      rc = SQLITE_READONLY_CANTINIT;
    }else{
      rc = winHandleTruncate(h, 0);
    }

    /* Release the EXCLUSIVE lock acquired above. */
    winUnlockFile(&h, WIN_SHM_DMS, 0, 1, 0);
  }else if( (rc & 0xFF)==SQLITE_BUSY ){
    rc = SQLITE_OK;
  }

  if( rc==SQLITE_OK ){
    /* Take a SHARED lock on the DMS byte. */
    rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 0, nMs);
    if( rc==SQLITE_OK ){
      pShmNode->isUnlocked = 0;
    }
  }

  return rc;
}


/*
** Convert a UTF-8 filename into whatever form the underlying
** operating system wants filenames in.  Space to hold the result
** is obtained from malloc and must be freed by the calling
** function.
*/
static void *winConvertFromUtf8Filename(const char *zFilename){
  void *zConverted = 0;
  if( osIsNT() ){
    zConverted = winUtf8ToUnicode(zFilename);
  }
#ifdef SQLITE_WIN32_HAS_ANSI
  else{
    zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
  }
#endif
  /* caller will handle out of memory */
  return zConverted;
}

/*
** This function is used to open a handle on a *-shm file.
**
** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file
** is opened with FILE_FLAG_OVERLAPPED specified. If not, it is not.
*/
static int winHandleOpen(
  const char *zUtf8,              /* File to open */
  int *pbReadonly,                /* IN/OUT: True for readonly handle */
  HANDLE *ph                      /* OUT: New HANDLE for file */
){
  int rc = SQLITE_OK;
  void *zConverted = 0;
  int bReadonly = *pbReadonly;
  HANDLE h = INVALID_HANDLE_VALUE;

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  const DWORD flag_overlapped = FILE_FLAG_OVERLAPPED;
#else
  const DWORD flag_overlapped = 0;
#endif

  /* Convert the filename to the system encoding. */
  zConverted = winConvertFromUtf8Filename(zUtf8);
  if( zConverted==0 ){
    OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8));
    rc = SQLITE_IOERR_NOMEM_BKPT;
    goto winopenfile_out;
  }

  /* Ensure the file we are trying to open is not actually a directory. */
  if( winIsDir(zConverted) ){
    OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8));
    rc = SQLITE_CANTOPEN_ISDIR;
    goto winopenfile_out;
  }

  /* TODO: platforms.
  ** TODO: retry-on-ioerr.
  */
  if( osIsNT() ){
#if SQLITE_OS_WINRT
    CREATEFILE2_EXTENDED_PARAMETERS extendedParameters;
    memset(&extendedParameters, 0, sizeof(extendedParameters));
    extendedParameters.dwSize = sizeof(extendedParameters);
    extendedParameters.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
    extendedParameters.dwFileFlags = flag_overlapped;
    extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS;
    h = osCreateFile2((LPCWSTR)zConverted,
        (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)),/* dwDesiredAccess */
        FILE_SHARE_READ | FILE_SHARE_WRITE,      /* dwShareMode */
        OPEN_ALWAYS,                             /* dwCreationDisposition */
        &extendedParameters
    );
#else
    h = osCreateFileW((LPCWSTR)zConverted,         /* lpFileName */
        (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)),  /* dwDesiredAccess */
        FILE_SHARE_READ | FILE_SHARE_WRITE,        /* dwShareMode */
        NULL,                                      /* lpSecurityAttributes */
        OPEN_ALWAYS,                               /* dwCreationDisposition */
        FILE_ATTRIBUTE_NORMAL|flag_overlapped,
        NULL
    );
#endif
  }else{
    /* Due to pre-processor directives earlier in this file,
    ** SQLITE_WIN32_HAS_ANSI is always defined if osIsNT() is false. */
#ifdef SQLITE_WIN32_HAS_ANSI
    h = osCreateFileA((LPCSTR)zConverted,
        (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)),  /* dwDesiredAccess */
        FILE_SHARE_READ | FILE_SHARE_WRITE,        /* dwShareMode */
        NULL,                                      /* lpSecurityAttributes */
        OPEN_ALWAYS,                               /* dwCreationDisposition */
        FILE_ATTRIBUTE_NORMAL|flag_overlapped,
        NULL
    );
#endif
  }

  if( h==INVALID_HANDLE_VALUE ){
    if( bReadonly==0 ){
      bReadonly = 1;
      rc = winHandleOpen(zUtf8, &bReadonly, &h);
    }else{
      rc = SQLITE_CANTOPEN_BKPT;
    }
  }

 winopenfile_out:
  sqlite3_free(zConverted);
  *pbReadonly = bReadonly;
  *ph = h;
  return rc;
}


/*
** Open the shared-memory area associated with database file pDbFd.




*/
static int winOpenSharedMemory(winFile *pDbFd){
  struct winShm *p;                  /* The connection to be opened */
  winShmNode *pShmNode = 0;          /* The underlying mmapped file */
  int rc = SQLITE_OK;                /* Result code */
  winShmNode *pNew;                  /* Newly allocated winShmNode */
  int nName;                         /* Size of zName in bytes */

  assert( pDbFd->pShm==0 );    /* Not previously opened */

  /* Allocate space for the new sqlite3_shm object.  Also speculatively
  ** allocate space for a new winShmNode and filename.  */

  p = sqlite3MallocZero( sizeof(*p) );
  if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT;
  nName = sqlite3Strlen30(pDbFd->zPath);
  pNew = sqlite3MallocZero( sizeof(*pShmNode) + (i64)nName + 17 );
  if( pNew==0 ){
    sqlite3_free(p);
    return SQLITE_IOERR_NOMEM_BKPT;
  }
  pNew->zFilename = (char*)&pNew[1];
  pNew->hSharedShm = INVALID_HANDLE_VALUE;
  pNew->isUnlocked = 1;
  sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
  sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);

  /* Open a file-handle on the *-shm file for this connection. This file-handle
  ** is only used for locking. The mapping of the *-shm file is created using
  ** the shared file handle in winShmNode.hSharedShm.  */
  p->bReadonly = sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0);
  rc = winHandleOpen(pNew->zFilename, &p->bReadonly, &p->hShm);

  /* Look to see if there is an existing winShmNode that can be used.
  ** If no matching winShmNode currently exists, then create a new one.  */

  winShmEnterMutex();
  for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){
    /* TBD need to come up with better match here.  Perhaps
    ** use FILE_ID_BOTH_DIR_INFO Structure.  */

    if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break;
  }
  if( pShmNode==0 ){





    pShmNode = pNew;





    /* Allocate a mutex for this winShmNode object, if one is required. */
    if( sqlite3GlobalConfig.bCoreMutex ){
      pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
      if( pShmNode->mutex==0 ) rc = SQLITE_IOERR_NOMEM_BKPT;


    }

    /* Open a file-handle to use for mappings, and for the DMS lock. */
    if( rc==SQLITE_OK ){
      HANDLE h = INVALID_HANDLE_VALUE;
      pShmNode->isReadonly = p->bReadonly;
      rc = winHandleOpen(pNew->zFilename, &pShmNode->isReadonly, &h);
      pShmNode->hSharedShm = h;
    }

    /* If successful, link the new winShmNode into the global list. If an
    ** error occurred, free the object. */
    if( rc==SQLITE_OK ){
      pShmNode->pNext = winShmNodeList;
      winShmNodeList = pShmNode;
      pNew = 0;
    }else{


      sqlite3_mutex_free(pShmNode->mutex);
      if( pShmNode->hSharedShm!=INVALID_HANDLE_VALUE ){



        osCloseHandle(pShmNode->hSharedShm);

      }

    }


  }

  /* If no error has occurred, link the winShm object to the winShmNode and
  ** the winShm to pDbFd.  */
  if( rc==SQLITE_OK ){
    p->pShmNode = pShmNode;
    pShmNode->nRef++;
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
    p->id = pShmNode->nextShmId++;
#endif

    pDbFd->pShm = p;
  }else if( p ){
    winHandleClose(p->hShm);
    sqlite3_free(p);
  }















  assert( rc!=SQLITE_OK || pShmNode->isUnlocked==0 || pShmNode->nRegion==0 );
  winShmLeaveMutex();

  sqlite3_free(pNew);

  return rc;
}

/*
** Close a connection to shared-memory.  Delete the underlying
** storage if deleteFlag is true.
*/
static int winShmUnmap(
  sqlite3_file *fd,          /* Database holding shared memory */
  int deleteFlag             /* Delete after closing if true */
){
  winFile *pDbFd;       /* Database holding shared-memory */
  winShm *p;            /* The connection to be closed */
  winShmNode *pShmNode; /* The underlying shared-memory file */


  pDbFd = (winFile*)fd;
  p = pDbFd->pShm;
  if( p==0 ) return SQLITE_OK;
  if( p->hShm!=INVALID_HANDLE_VALUE ){
    osCloseHandle(p->hShm);
  }








  pShmNode = p->pShmNode;
  winShmEnterMutex();

  /* If pShmNode->nRef has reached 0, then close the underlying
  ** shared-memory file, too. */

  assert( pShmNode->nRef>0 );
  pShmNode->nRef--;
  if( pShmNode->nRef==0 ){
    winShmPurge(pDbFd->pVfs, deleteFlag);
  }
  winShmLeaveMutex();

  /* Free the connection p */
  sqlite3_free(p);
  pDbFd->pShm = 0;
  return SQLITE_OK;
}

/*
** Change the lock state for a shared-memory segment.
*/
static int winShmLock(
  sqlite3_file *fd,          /* Database file holding the shared memory */
  int ofst,                  /* First lock to acquire or release */
  int n,                     /* Number of locks to acquire or release */
  int flags                  /* What to do with the lock */
){
  winFile *pDbFd = (winFile*)fd;        /* Connection holding shared memory */
  winShm *p = pDbFd->pShm;              /* The shared memory being locked */

  winShmNode *pShmNode;
  int rc = SQLITE_OK;                   /* Result code */
  u16 mask = (u16)((1U<<(ofst+n)) - (1U<<ofst)); /* Mask of locks to [un]take */

  if( p==0 ) return SQLITE_IOERR_SHMLOCK;
  pShmNode = p->pShmNode;
  if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK;

  assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK );
  assert( n>=1 );
  assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
       || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
  assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );

  /* Check that, if this to be a blocking lock, no locks that occur later
  ** in the following list than the lock being obtained are already held:
  **
  **   1. Checkpointer lock (ofst==1).
  **   2. Write lock (ofst==0).

  **   3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
  **
  ** In other words, if this is a blocking lock, none of the locks that
  ** occur later in the above list than the lock being obtained may be
  ** held.
  **
  ** It is not permitted to block on the RECOVER lock.
  */
#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG)
  {



    u16 lockMask = (p->exclMask|p->sharedMask);


    assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
          (ofst!=2)                                   /* not RECOVER */
       && (ofst!=1 || lockMask==0 || lockMask==2)
       && (ofst!=0 || lockMask<3)




       && (ofst<3  || lockMask<(1<<ofst))
    ));
  }

#endif





  /* Check if there is any work to do. There are three cases:
  **
  **    a) An unlock operation where there are locks to unlock,
  **    b) An shared lock where the requested lock is not already held
  **    c) An exclusive lock where the requested lock is not already held

  **
  ** The SQLite core never requests an exclusive lock that it already holds.
  ** This is assert()ed immediately below.  */
  assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)
       || 0==(p->exclMask & mask)
  );
  if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))


   || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))

   || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
  ){


    if( flags & SQLITE_SHM_UNLOCK ){


      /* Case (a) - unlock.  */


      assert( (p->exclMask & p->sharedMask)==0 );
      assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
      assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );

      rc = winHandleUnlock(p->hShm, ofst+WIN_SHM_BASE, n);

      /* If successful, also clear the bits in sharedMask/exclMask */
      if( rc==SQLITE_OK ){
        p->exclMask = (p->exclMask & ~mask);
        p->sharedMask = (p->sharedMask & ~mask);
      }
    }else{

      int bExcl = ((flags & SQLITE_SHM_EXCLUSIVE) ? 1 : 0);

      DWORD nMs = winFileBusyTimeout(pDbFd);
      rc = winHandleLockTimeout(p->hShm, ofst+WIN_SHM_BASE, n, bExcl, nMs);

      if( rc==SQLITE_OK ){
        if( bExcl ){
          p->exclMask = (p->exclMask | mask);
        }else{
          p->sharedMask = (p->sharedMask | mask);
        }
      }








    }
  }


  OSTRACE((
      "SHM-LOCK(%d,%d,%d) pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x,"
      " rc=%s\n",
      ofst, n, flags,
      osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask,
      sqlite3ErrName(rc))
  );
  return rc;
}

/*
** Implement a memory barrier or memory fence on shared memory.
**
** All loads and stores begun before the barrier must complete before
51137
51138
51139
51140
51141
51142
51143


51144
51145
51146
51147
51148
51149

51150

51151
51152
51153
51154
51155
51156
51157
51158
51159
51160
51161
51162
51163
51164
51165
51166
51167
51168
51169
51170
51171
51172
51173
51174
51175
51176
51177
51178
51179
51180
51181
51182
51183
51184
51185
51186
51187
51188
51189
51190
51191
51192
51193
51194
51195
51196
51197
51198
51199
51200
51201
51202
51203
51204
51205
51206
51207
51208
51209
51210
51211
51212
51213
51214
51215

51216
51217
51218
51219
51220
51221
51222
    pShm = pDbFd->pShm;
    assert( pShm!=0 );
  }
  pShmNode = pShm->pShmNode;

  sqlite3_mutex_enter(pShmNode->mutex);
  if( pShmNode->isUnlocked ){


    rc = winLockSharedMemory(pShmNode);
    if( rc!=SQLITE_OK ) goto shmpage_out;
    pShmNode->isUnlocked = 0;
  }
  assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );


  if( pShmNode->nRegion<=iRegion ){

    struct ShmRegion *apNew;           /* New aRegion[] array */
    int nByte = (iRegion+1)*szRegion;  /* Minimum required file size */
    sqlite3_int64 sz;                  /* Current size of wal-index file */

    pShmNode->szRegion = szRegion;

    /* The requested region is not mapped into this processes address space.
    ** Check to see if it has been allocated (i.e. if the wal-index file is
    ** large enough to contain the requested region).
    */
    rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz);
    if( rc!=SQLITE_OK ){
      rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
                       "winShmMap1", pDbFd->zPath);
      goto shmpage_out;
    }

    if( sz<nByte ){
      /* The requested memory region does not exist. If isWrite is set to
      ** zero, exit early. *pp will be set to NULL and SQLITE_OK returned.
      **
      ** Alternatively, if isWrite is non-zero, use ftruncate() to allocate
      ** the requested memory region.
      */
      if( !isWrite ) goto shmpage_out;
      rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte);
      if( rc!=SQLITE_OK ){
        rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
                         "winShmMap2", pDbFd->zPath);
        goto shmpage_out;
      }
    }

    /* Map the requested memory region into this processes address space. */
    apNew = (struct ShmRegion *)sqlite3_realloc64(
        pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0])
    );
    if( !apNew ){
      rc = SQLITE_IOERR_NOMEM_BKPT;
      goto shmpage_out;
    }
    pShmNode->aRegion = apNew;

    if( pShmNode->isReadonly ){
      protect = PAGE_READONLY;
      flags = FILE_MAP_READ;
    }

    while( pShmNode->nRegion<=iRegion ){
      HANDLE hMap = NULL;         /* file-mapping handle */
      void *pMap = 0;             /* Mapped memory region */

#if SQLITE_OS_WINRT
      hMap = osCreateFileMappingFromApp(pShmNode->hFile.h,
          NULL, protect, nByte, NULL
      );
#elif defined(SQLITE_WIN32_HAS_WIDE)
      hMap = osCreateFileMappingW(pShmNode->hFile.h,
          NULL, protect, 0, nByte, NULL
      );
#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
      hMap = osCreateFileMappingA(pShmNode->hFile.h,
          NULL, protect, 0, nByte, NULL
      );
#endif

      OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n",
               osGetCurrentProcessId(), pShmNode->nRegion, nByte,
               hMap ? "ok" : "failed"));
      if( hMap ){
        int iOffset = pShmNode->nRegion*szRegion;
        int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
#if SQLITE_OS_WINRT







>
>
|

<

<

>

>










|

<
|








|
<

|

<
|





|


















|
<
<

|
<
<

|
<
<

>







51626
51627
51628
51629
51630
51631
51632
51633
51634
51635
51636

51637

51638
51639
51640
51641
51642
51643
51644
51645
51646
51647
51648
51649
51650
51651
51652
51653

51654
51655
51656
51657
51658
51659
51660
51661
51662
51663

51664
51665
51666

51667
51668
51669
51670
51671
51672
51673
51674
51675
51676
51677
51678
51679
51680
51681
51682
51683
51684
51685
51686
51687
51688
51689
51690
51691
51692


51693
51694


51695
51696


51697
51698
51699
51700
51701
51702
51703
51704
51705
    pShm = pDbFd->pShm;
    assert( pShm!=0 );
  }
  pShmNode = pShm->pShmNode;

  sqlite3_mutex_enter(pShmNode->mutex);
  if( pShmNode->isUnlocked ){
    /* Take the DMS lock. */
    assert( pShmNode->nRegion==0 );
    rc = winLockSharedMemory(pShmNode, winFileBusyTimeout(pDbFd));
    if( rc!=SQLITE_OK ) goto shmpage_out;

  }


  assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
  if( pShmNode->nRegion<=iRegion ){
    HANDLE hShared = pShmNode->hSharedShm;
    struct ShmRegion *apNew;           /* New aRegion[] array */
    int nByte = (iRegion+1)*szRegion;  /* Minimum required file size */
    sqlite3_int64 sz;                  /* Current size of wal-index file */

    pShmNode->szRegion = szRegion;

    /* The requested region is not mapped into this processes address space.
    ** Check to see if it has been allocated (i.e. if the wal-index file is
    ** large enough to contain the requested region).
    */
    rc = winHandleSize(hShared, &sz);
    if( rc!=SQLITE_OK ){

      rc = winLogError(rc, osGetLastError(), "winShmMap1", pDbFd->zPath);
      goto shmpage_out;
    }

    if( sz<nByte ){
      /* The requested memory region does not exist. If isWrite is set to
      ** zero, exit early. *pp will be set to NULL and SQLITE_OK returned.
      **
      ** Alternatively, if isWrite is non-zero, use ftruncate() to allocate
      ** the requested memory region.  */

      if( !isWrite ) goto shmpage_out;
      rc = winHandleTruncate(hShared, nByte);
      if( rc!=SQLITE_OK ){

        rc = winLogError(rc, osGetLastError(), "winShmMap2", pDbFd->zPath);
        goto shmpage_out;
      }
    }

    /* Map the requested memory region into this processes address space. */
    apNew = (struct ShmRegion*)sqlite3_realloc64(
        pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0])
    );
    if( !apNew ){
      rc = SQLITE_IOERR_NOMEM_BKPT;
      goto shmpage_out;
    }
    pShmNode->aRegion = apNew;

    if( pShmNode->isReadonly ){
      protect = PAGE_READONLY;
      flags = FILE_MAP_READ;
    }

    while( pShmNode->nRegion<=iRegion ){
      HANDLE hMap = NULL;         /* file-mapping handle */
      void *pMap = 0;             /* Mapped memory region */

#if SQLITE_OS_WINRT
      hMap = osCreateFileMappingFromApp(hShared, NULL, protect, nByte, NULL);


#elif defined(SQLITE_WIN32_HAS_WIDE)
      hMap = osCreateFileMappingW(hShared, NULL, protect, 0, nByte, NULL);


#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
      hMap = osCreateFileMappingA(hShared, NULL, protect, 0, nByte, NULL);


#endif

      OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n",
               osGetCurrentProcessId(), pShmNode->nRegion, nByte,
               hMap ? "ok" : "failed"));
      if( hMap ){
        int iOffset = pShmNode->nRegion*szRegion;
        int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
#if SQLITE_OS_WINRT
51251
51252
51253
51254
51255
51256
51257
51258


51259
51260
51261
51262
51263
51264
51265
    int iOffset = iRegion*szRegion;
    int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
    char *p = (char *)pShmNode->aRegion[iRegion].pMap;
    *pp = (void *)&p[iOffsetShift];
  }else{
    *pp = 0;
  }
  if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;


  sqlite3_mutex_leave(pShmNode->mutex);
  return rc;
}

#else
# define winShmMap     0
# define winShmLock    0







|
>
>







51734
51735
51736
51737
51738
51739
51740
51741
51742
51743
51744
51745
51746
51747
51748
51749
51750
    int iOffset = iRegion*szRegion;
    int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
    char *p = (char *)pShmNode->aRegion[iRegion].pMap;
    *pp = (void *)&p[iOffsetShift];
  }else{
    *pp = 0;
  }
  if( pShmNode->isReadonly && rc==SQLITE_OK ){
    rc = SQLITE_READONLY;
  }
  sqlite3_mutex_leave(pShmNode->mutex);
  return rc;
}

#else
# define winShmMap     0
# define winShmLock    0
51592
51593
51594
51595
51596
51597
51598
51599
51600
51601
51602
51603
51604
51605
51606
51607
51608
51609
51610
51611
51612
51613
51614
51615
51616
51617
51618
51619
51620
51621
51622
51623
51624
51625
  }
#endif
  /* caller will handle out of memory */
  return zConverted;
}
#endif

/*
** Convert a UTF-8 filename into whatever form the underlying
** operating system wants filenames in.  Space to hold the result
** is obtained from malloc and must be freed by the calling
** function.
*/
static void *winConvertFromUtf8Filename(const char *zFilename){
  void *zConverted = 0;
  if( osIsNT() ){
    zConverted = winUtf8ToUnicode(zFilename);
  }
#ifdef SQLITE_WIN32_HAS_ANSI
  else{
    zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
  }
#endif
  /* caller will handle out of memory */
  return zConverted;
}

/*
** This function returns non-zero if the specified UTF-8 string buffer
** ends with a directory separator character or one was successfully
** added to it.
*/
static int winMakeEndInDirSep(int nBuf, char *zBuf){
  if( zBuf ){







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







52077
52078
52079
52080
52081
52082
52083




















52084
52085
52086
52087
52088
52089
52090
  }
#endif
  /* caller will handle out of memory */
  return zConverted;
}
#endif





















/*
** This function returns non-zero if the specified UTF-8 string buffer
** ends with a directory separator character or one was successfully
** added to it.
*/
static int winMakeEndInDirSep(int nBuf, char *zBuf){
  if( zBuf ){
53065
53066
53067
53068
53069
53070
53071
53072
53073
53074
53075
53076
53077
53078
53079
    winGetSystemCall,      /* xGetSystemCall */
    winNextSystemCall,     /* xNextSystemCall */
  };
#endif

  /* Double-check that the aSyscall[] array has been constructed
  ** correctly.  See ticket [bb3a86e890c8e96ab] */
  assert( ArraySize(aSyscall)==80 );

  /* get memory map allocation granularity */
  memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
#if SQLITE_OS_WINRT
  osGetNativeSystemInfo(&winSysInfo);
#else
  osGetSystemInfo(&winSysInfo);







|







53530
53531
53532
53533
53534
53535
53536
53537
53538
53539
53540
53541
53542
53543
53544
    winGetSystemCall,      /* xGetSystemCall */
    winNextSystemCall,     /* xNextSystemCall */
  };
#endif

  /* Double-check that the aSyscall[] array has been constructed
  ** correctly.  See ticket [bb3a86e890c8e96ab] */
  assert( ArraySize(aSyscall)==82 );

  /* get memory map allocation granularity */
  memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
#if SQLITE_OS_WINRT
  osGetNativeSystemInfo(&winSysInfo);
#else
  osGetSystemInfo(&winSysInfo);
54123
54124
54125
54126
54127
54128
54129
54130
54131
54132
54133
54134
54135
54136
54137
#define BITVEC_MXHASH    (BITVEC_NINT/2)
/* Hashing function for the aHash representation.
** Empirical testing showed that the *37 multiplier
** (an arbitrary prime)in the hash function provided
** no fewer collisions than the no-op *1. */
#define BITVEC_HASH(X)   (((X)*1)%BITVEC_NINT)

#define BITVEC_NPTR      (BITVEC_USIZE/sizeof(Bitvec *))


/*
** A bitmap is an instance of the following structure.
**
** This bitmap records the existence of zero or more bits
** with values between 1 and iSize, inclusive.







|







54588
54589
54590
54591
54592
54593
54594
54595
54596
54597
54598
54599
54600
54601
54602
#define BITVEC_MXHASH    (BITVEC_NINT/2)
/* Hashing function for the aHash representation.
** Empirical testing showed that the *37 multiplier
** (an arbitrary prime)in the hash function provided
** no fewer collisions than the no-op *1. */
#define BITVEC_HASH(X)   (((X)*1)%BITVEC_NINT)

#define BITVEC_NPTR      ((u32)(BITVEC_USIZE/sizeof(Bitvec *)))


/*
** A bitmap is an instance of the following structure.
**
** This bitmap records the existence of zero or more bits
** with values between 1 and iSize, inclusive.
54306
54307
54308
54309
54310
54311
54312
54313
54314
54315
54316
54317
54318
54319
54320
    i = i%p->iDivisor;
    p = p->u.apSub[bin];
    if (!p) {
      return;
    }
  }
  if( p->iSize<=BITVEC_NBIT ){
    p->u.aBitmap[i/BITVEC_SZELEM] &= ~(1 << (i&(BITVEC_SZELEM-1)));
  }else{
    unsigned int j;
    u32 *aiValues = pBuf;
    memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash));
    memset(p->u.aHash, 0, sizeof(p->u.aHash));
    p->nSet = 0;
    for(j=0; j<BITVEC_NINT; j++){







|







54771
54772
54773
54774
54775
54776
54777
54778
54779
54780
54781
54782
54783
54784
54785
    i = i%p->iDivisor;
    p = p->u.apSub[bin];
    if (!p) {
      return;
    }
  }
  if( p->iSize<=BITVEC_NBIT ){
    p->u.aBitmap[i/BITVEC_SZELEM] &= ~(BITVEC_TELEM)(1<<(i&(BITVEC_SZELEM-1)));
  }else{
    unsigned int j;
    u32 *aiValues = pBuf;
    memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash));
    memset(p->u.aHash, 0, sizeof(p->u.aHash));
    p->nSet = 0;
    for(j=0; j<BITVEC_NINT; j++){
54357
54358
54359
54360
54361
54362
54363
54364
54365
54366
54367
54368
54369
54370
54371
/*
** Let V[] be an array of unsigned characters sufficient to hold
** up to N bits.  Let I be an integer between 0 and N.  0<=I<N.
** Then the following macros can be used to set, clear, or test
** individual bits within V.
*/
#define SETBIT(V,I)      V[I>>3] |= (1<<(I&7))
#define CLEARBIT(V,I)    V[I>>3] &= ~(1<<(I&7))
#define TESTBIT(V,I)     (V[I>>3]&(1<<(I&7)))!=0

/*
** This routine runs an extensive test of the Bitvec code.
**
** The input is an array of integers that acts as a program
** to test the Bitvec.  The integers are opcodes followed







|







54822
54823
54824
54825
54826
54827
54828
54829
54830
54831
54832
54833
54834
54835
54836
/*
** Let V[] be an array of unsigned characters sufficient to hold
** up to N bits.  Let I be an integer between 0 and N.  0<=I<N.
** Then the following macros can be used to set, clear, or test
** individual bits within V.
*/
#define SETBIT(V,I)      V[I>>3] |= (1<<(I&7))
#define CLEARBIT(V,I)    V[I>>3] &= ~(BITVEC_TELEM)(1<<(I&7))
#define TESTBIT(V,I)     (V[I>>3]&(1<<(I&7)))!=0

/*
** This routine runs an extensive test of the Bitvec code.
**
** The input is an array of integers that acts as a program
** to test the Bitvec.  The integers are opcodes followed
55641
55642
55643
55644
55645
55646
55647
55648
55649
55650
55651
55652
55653
55654
55655
55656
55657
55658
  int nSlot;                     /* The number of pcache slots */
  int nReserve;                  /* Try to keep nFreeSlot above this */
  void *pStart, *pEnd;           /* Bounds of global page cache memory */
  /* Above requires no mutex.  Use mutex below for variable that follow. */
  sqlite3_mutex *mutex;          /* Mutex for accessing the following: */
  PgFreeslot *pFree;             /* Free page blocks */
  int nFreeSlot;                 /* Number of unused pcache slots */
  /* The following value requires a mutex to change.  We skip the mutex on
  ** reading because (1) most platforms read a 32-bit integer atomically and
  ** (2) even if an incorrect value is read, no great harm is done since this
  ** is really just an optimization. */
  int bUnderPressure;            /* True if low on PAGECACHE memory */
} pcache1_g;

/*
** All code in this file should access the global structure above via the
** alias "pcache1". This ensures that the WSD emulation is used when
** compiling for systems that do not support real WSD.







<
<
<
<







56106
56107
56108
56109
56110
56111
56112




56113
56114
56115
56116
56117
56118
56119
  int nSlot;                     /* The number of pcache slots */
  int nReserve;                  /* Try to keep nFreeSlot above this */
  void *pStart, *pEnd;           /* Bounds of global page cache memory */
  /* Above requires no mutex.  Use mutex below for variable that follow. */
  sqlite3_mutex *mutex;          /* Mutex for accessing the following: */
  PgFreeslot *pFree;             /* Free page blocks */
  int nFreeSlot;                 /* Number of unused pcache slots */




  int bUnderPressure;            /* True if low on PAGECACHE memory */
} pcache1_g;

/*
** All code in this file should access the global structure above via the
** alias "pcache1". This ensures that the WSD emulation is used when
** compiling for systems that do not support real WSD.
55692
55693
55694
55695
55696
55697
55698
55699
55700
55701
55702
55703
55704
55705
55706
    if( n==0 ) sz = 0;
    sz = ROUNDDOWN8(sz);
    pcache1.szSlot = sz;
    pcache1.nSlot = pcache1.nFreeSlot = n;
    pcache1.nReserve = n>90 ? 10 : (n/10 + 1);
    pcache1.pStart = pBuf;
    pcache1.pFree = 0;
    pcache1.bUnderPressure = 0;
    while( n-- ){
      p = (PgFreeslot*)pBuf;
      p->pNext = pcache1.pFree;
      pcache1.pFree = p;
      pBuf = (void*)&((char*)pBuf)[sz];
    }
    pcache1.pEnd = pBuf;







|







56153
56154
56155
56156
56157
56158
56159
56160
56161
56162
56163
56164
56165
56166
56167
    if( n==0 ) sz = 0;
    sz = ROUNDDOWN8(sz);
    pcache1.szSlot = sz;
    pcache1.nSlot = pcache1.nFreeSlot = n;
    pcache1.nReserve = n>90 ? 10 : (n/10 + 1);
    pcache1.pStart = pBuf;
    pcache1.pFree = 0;
    AtomicStore(&pcache1.bUnderPressure,0);
    while( n-- ){
      p = (PgFreeslot*)pBuf;
      p->pNext = pcache1.pFree;
      pcache1.pFree = p;
      pBuf = (void*)&((char*)pBuf)[sz];
    }
    pcache1.pEnd = pBuf;
55760
55761
55762
55763
55764
55765
55766
55767
55768
55769
55770
55771
55772
55773
55774
  assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
  if( nByte<=pcache1.szSlot ){
    sqlite3_mutex_enter(pcache1.mutex);
    p = (PgHdr1 *)pcache1.pFree;
    if( p ){
      pcache1.pFree = pcache1.pFree->pNext;
      pcache1.nFreeSlot--;
      pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
      assert( pcache1.nFreeSlot>=0 );
      sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
      sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1);
    }
    sqlite3_mutex_leave(pcache1.mutex);
  }
  if( p==0 ){







|







56221
56222
56223
56224
56225
56226
56227
56228
56229
56230
56231
56232
56233
56234
56235
  assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
  if( nByte<=pcache1.szSlot ){
    sqlite3_mutex_enter(pcache1.mutex);
    p = (PgHdr1 *)pcache1.pFree;
    if( p ){
      pcache1.pFree = pcache1.pFree->pNext;
      pcache1.nFreeSlot--;
      AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve);
      assert( pcache1.nFreeSlot>=0 );
      sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
      sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1);
    }
    sqlite3_mutex_leave(pcache1.mutex);
  }
  if( p==0 ){
55799
55800
55801
55802
55803
55804
55805
55806
55807
55808
55809
55810
55811
55812
55813
    PgFreeslot *pSlot;
    sqlite3_mutex_enter(pcache1.mutex);
    sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1);
    pSlot = (PgFreeslot*)p;
    pSlot->pNext = pcache1.pFree;
    pcache1.pFree = pSlot;
    pcache1.nFreeSlot++;
    pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
    assert( pcache1.nFreeSlot<=pcache1.nSlot );
    sqlite3_mutex_leave(pcache1.mutex);
  }else{
    assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
    sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
    {







|







56260
56261
56262
56263
56264
56265
56266
56267
56268
56269
56270
56271
56272
56273
56274
    PgFreeslot *pSlot;
    sqlite3_mutex_enter(pcache1.mutex);
    sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1);
    pSlot = (PgFreeslot*)p;
    pSlot->pNext = pcache1.pFree;
    pcache1.pFree = pSlot;
    pcache1.nFreeSlot++;
    AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve);
    assert( pcache1.nFreeSlot<=pcache1.nSlot );
    sqlite3_mutex_leave(pcache1.mutex);
  }else{
    assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
    sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
    {
55930
55931
55932
55933
55934
55935
55936
55937
55938
55939
55940
55941
55942
55943
55944
** Or, the heap is used for all page cache memory but the heap is
** under memory pressure, then again it is desirable to avoid
** allocating a new page cache entry in order to avoid stressing
** the heap even further.
*/
static int pcache1UnderMemoryPressure(PCache1 *pCache){
  if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){
    return pcache1.bUnderPressure;
  }else{
    return sqlite3HeapNearlyFull();
  }
}

/******************************************************************************/
/******** General Implementation Functions ************************************/







|







56391
56392
56393
56394
56395
56396
56397
56398
56399
56400
56401
56402
56403
56404
56405
** Or, the heap is used for all page cache memory but the heap is
** under memory pressure, then again it is desirable to avoid
** allocating a new page cache entry in order to avoid stressing
** the heap even further.
*/
static int pcache1UnderMemoryPressure(PCache1 *pCache){
  if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){
    return AtomicLoad(&pcache1.bUnderPressure);
  }else{
    return sqlite3HeapNearlyFull();
  }
}

/******************************************************************************/
/******** General Implementation Functions ************************************/
59209
59210
59211
59212
59213
59214
59215









59216
59217
59218
59219
59220
59221
59222

  sqlite3BitvecDestroy(pPager->pInJournal);
  pPager->pInJournal = 0;
  releaseAllSavepoints(pPager);

  if( pagerUseWal(pPager) ){
    assert( !isOpen(pPager->jfd) );









    sqlite3WalEndReadTransaction(pPager->pWal);
    pPager->eState = PAGER_OPEN;
  }else if( !pPager->exclusiveMode ){
    int rc;                       /* Error code returned by pagerUnlockDb() */
    int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0;

    /* If the operating system support deletion of open files, then







>
>
>
>
>
>
>
>
>







59670
59671
59672
59673
59674
59675
59676
59677
59678
59679
59680
59681
59682
59683
59684
59685
59686
59687
59688
59689
59690
59691
59692

  sqlite3BitvecDestroy(pPager->pInJournal);
  pPager->pInJournal = 0;
  releaseAllSavepoints(pPager);

  if( pagerUseWal(pPager) ){
    assert( !isOpen(pPager->jfd) );
    if( pPager->eState==PAGER_ERROR ){
      /* If an IO error occurs in wal.c while attempting to wrap the wal file,
      ** then the Wal object may be holding a write-lock but no read-lock.
      ** This call ensures that the write-lock is dropped as well. We cannot
      ** have sqlite3WalEndReadTransaction() drop the write-lock, as it once
      ** did, because this would break "BEGIN EXCLUSIVE" handling for
      ** SQLITE_ENABLE_SETLK_TIMEOUT builds.  */
      sqlite3WalEndWriteTransaction(pPager->pWal);
    }
    sqlite3WalEndReadTransaction(pPager->pWal);
    pPager->eState = PAGER_OPEN;
  }else if( !pPager->exclusiveMode ){
    int rc;                       /* Error code returned by pagerUnlockDb() */
    int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0;

    /* If the operating system support deletion of open files, then
65667
65668
65669
65670
65671
65672
65673





65674
65675
65676
65677
65678
65679
65680
#define walFrameOffset(iFrame, szPage) (                               \
  WAL_HDRSIZE + ((iFrame)-1)*(i64)((szPage)+WAL_FRAME_HDRSIZE)         \
)

/*
** An open write-ahead log file is represented by an instance of the
** following object.





*/
struct Wal {
  sqlite3_vfs *pVfs;         /* The VFS used to create pDbFd */
  sqlite3_file *pDbFd;       /* File handle for the database file */
  sqlite3_file *pWalFd;      /* File handle for WAL file */
  u32 iCallback;             /* Value to pass to log callback (or 0) */
  i64 mxWalSize;             /* Truncate WAL to this size upon reset */







>
>
>
>
>







66137
66138
66139
66140
66141
66142
66143
66144
66145
66146
66147
66148
66149
66150
66151
66152
66153
66154
66155
#define walFrameOffset(iFrame, szPage) (                               \
  WAL_HDRSIZE + ((iFrame)-1)*(i64)((szPage)+WAL_FRAME_HDRSIZE)         \
)

/*
** An open write-ahead log file is represented by an instance of the
** following object.
**
** writeLock:
**   This is usually set to 1 whenever the WRITER lock is held. However,
**   if it is set to 2, then the WRITER lock is held but must be released
**   by walHandleException() if a SEH exception is thrown.
*/
struct Wal {
  sqlite3_vfs *pVfs;         /* The VFS used to create pDbFd */
  sqlite3_file *pDbFd;       /* File handle for the database file */
  sqlite3_file *pWalFd;      /* File handle for WAL file */
  u32 iCallback;             /* Value to pass to log callback (or 0) */
  i64 mxWalSize;             /* Truncate WAL to this size upon reset */
65757
65758
65759
65760
65761
65762
65763
65764
65765




65766
65767
65768
65769
65770
65771
65772
  int nSegment;                   /* Number of entries in aSegment[] */
  struct WalSegment {
    int iNext;                    /* Next slot in aIndex[] not yet returned */
    ht_slot *aIndex;              /* i0, i1, i2... such that aPgno[iN] ascend */
    u32 *aPgno;                   /* Array of page numbers. */
    int nEntry;                   /* Nr. of entries in aPgno[] and aIndex[] */
    int iZero;                    /* Frame number associated with aPgno[0] */
  } aSegment[1];                  /* One for every 32KB page in the wal-index */
};





/*
** Define the parameters of the hash tables in the wal-index file. There
** is a hash-table following every HASHTABLE_NPAGE page numbers in the
** wal-index.
**
** Changing any of these constants will alter the wal-index format and







|

>
>
>
>







66232
66233
66234
66235
66236
66237
66238
66239
66240
66241
66242
66243
66244
66245
66246
66247
66248
66249
66250
66251
  int nSegment;                   /* Number of entries in aSegment[] */
  struct WalSegment {
    int iNext;                    /* Next slot in aIndex[] not yet returned */
    ht_slot *aIndex;              /* i0, i1, i2... such that aPgno[iN] ascend */
    u32 *aPgno;                   /* Array of page numbers. */
    int nEntry;                   /* Nr. of entries in aPgno[] and aIndex[] */
    int iZero;                    /* Frame number associated with aPgno[0] */
  } aSegment[FLEXARRAY];          /* One for every 32KB page in the wal-index */
};

/* Size (in bytes) of a WalIterator object suitable for N or fewer segments */
#define SZ_WALITERATOR(N)  \
     (offsetof(WalIterator,aSegment)*(N)*sizeof(struct WalSegment))

/*
** Define the parameters of the hash tables in the wal-index file. There
** is a hash-table following every HASHTABLE_NPAGE page numbers in the
** wal-index.
**
** Changing any of these constants will alter the wal-index format and
67120
67121
67122
67123
67124
67125
67126
67127
67128
67129
67130
67131
67132
67133
67134
67135
  ** it only runs if there is actually content in the log (mxFrame>0).
  */
  assert( pWal->ckptLock && pWal->hdr.mxFrame>0 );
  iLast = pWal->hdr.mxFrame;

  /* Allocate space for the WalIterator object. */
  nSegment = walFramePage(iLast) + 1;
  nByte = sizeof(WalIterator)
        + (nSegment-1)*sizeof(struct WalSegment)
        + iLast*sizeof(ht_slot);
  p = (WalIterator *)sqlite3_malloc64(nByte
      + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
  );
  if( !p ){
    return SQLITE_NOMEM_BKPT;
  }







|
<







67599
67600
67601
67602
67603
67604
67605
67606

67607
67608
67609
67610
67611
67612
67613
  ** it only runs if there is actually content in the log (mxFrame>0).
  */
  assert( pWal->ckptLock && pWal->hdr.mxFrame>0 );
  iLast = pWal->hdr.mxFrame;

  /* Allocate space for the WalIterator object. */
  nSegment = walFramePage(iLast) + 1;
  nByte = SZ_WALITERATOR(nSegment)

        + iLast*sizeof(ht_slot);
  p = (WalIterator *)sqlite3_malloc64(nByte
      + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
  );
  if( !p ){
    return SQLITE_NOMEM_BKPT;
  }
67192
67193
67194
67195
67196
67197
67198
67199
67200
67201
67202
67203
67204
67205
67206
** they are supported by the VFS, and (b) the database handle is configured
** with a busy-timeout. Return 1 if blocking locks are successfully enabled,
** or 0 otherwise.
*/
static int walEnableBlocking(Wal *pWal){
  int res = 0;
  if( pWal->db ){
    int tmout = pWal->db->busyTimeout;
    if( tmout ){
      res = walEnableBlockingMs(pWal, tmout);
    }
  }
  return res;
}








|







67670
67671
67672
67673
67674
67675
67676
67677
67678
67679
67680
67681
67682
67683
67684
** they are supported by the VFS, and (b) the database handle is configured
** with a busy-timeout. Return 1 if blocking locks are successfully enabled,
** or 0 otherwise.
*/
static int walEnableBlocking(Wal *pWal){
  int res = 0;
  if( pWal->db ){
    int tmout = pWal->db->setlkTimeout;
    if( tmout ){
      res = walEnableBlockingMs(pWal, tmout);
    }
  }
  return res;
}

67578
67579
67580
67581
67582
67583
67584


67585
67586
67587
67588
67589
67590
67591
67592
**   4) Returns SQLITE_IOERR.
*/
static int walHandleException(Wal *pWal){
  if( pWal->exclusiveMode==0 ){
    static const int S = 1;
    static const int E = (1<<SQLITE_SHM_NLOCK);
    int ii;


    u32 mUnlock = pWal->lockMask & ~(
        (pWal->readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock)))
        | (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0)
        | (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0)
        );
    for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
      if( (S<<ii) & mUnlock ) walUnlockShared(pWal, ii);
      if( (E<<ii) & mUnlock ) walUnlockExclusive(pWal, ii, 1);







>
>
|







68056
68057
68058
68059
68060
68061
68062
68063
68064
68065
68066
68067
68068
68069
68070
68071
68072
**   4) Returns SQLITE_IOERR.
*/
static int walHandleException(Wal *pWal){
  if( pWal->exclusiveMode==0 ){
    static const int S = 1;
    static const int E = (1<<SQLITE_SHM_NLOCK);
    int ii;
    u32 mUnlock;
    if( pWal->writeLock==2 ) pWal->writeLock = 0;
    mUnlock = pWal->lockMask & ~(
        (pWal->readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock)))
        | (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0)
        | (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0)
        );
    for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
      if( (S<<ii) & mUnlock ) walUnlockShared(pWal, ii);
      if( (E<<ii) & mUnlock ) walUnlockExclusive(pWal, ii, 1);
67850
67851
67852
67853
67854
67855
67856





67857
67858
67859
67860
67861
67862
67863
67864
        rc = SQLITE_READONLY_RECOVERY;
      }
    }else{
      int bWriteLock = pWal->writeLock;
      if( bWriteLock
       || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1))
      ){





        pWal->writeLock = 1;
        if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
          badHdr = walIndexTryHdr(pWal, pChanged);
          if( badHdr ){
            /* If the wal-index header is still malformed even while holding
            ** a WRITE lock, it can only mean that the header is corrupted and
            ** needs to be reconstructed.  So run recovery to do exactly that.
            ** Disable blocking locks first.  */







>
>
>
>
>
|







68330
68331
68332
68333
68334
68335
68336
68337
68338
68339
68340
68341
68342
68343
68344
68345
68346
68347
68348
68349
        rc = SQLITE_READONLY_RECOVERY;
      }
    }else{
      int bWriteLock = pWal->writeLock;
      if( bWriteLock
       || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1))
      ){
        /* If the write-lock was just obtained, set writeLock to 2 instead of
        ** the usual 1. This causes walIndexPage() to behave as if the
        ** write-lock were held (so that it allocates new pages as required),
        ** and walHandleException() to unlock the write-lock if a SEH exception
        ** is thrown.  */
        if( !bWriteLock ) pWal->writeLock = 2;
        if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
          badHdr = walIndexTryHdr(pWal, pChanged);
          if( badHdr ){
            /* If the wal-index header is still malformed even while holding
            ** a WRITE lock, it can only mean that the header is corrupted and
            ** needs to be reconstructed.  So run recovery to do exactly that.
            ** Disable blocking locks first.  */
68635
68636
68637
68638
68639
68640
68641

68642

68643

68644
68645
68646
68647
68648
68649
68650
}

/*
** Finish with a read transaction.  All this does is release the
** read-lock.
*/
SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){

  sqlite3WalEndWriteTransaction(pWal);

  if( pWal->readLock>=0 ){

    walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
    pWal->readLock = -1;
  }
}

/*
** Search the wal file for page pgno. If found, set *piRead to the frame that







>
|
>

>







69120
69121
69122
69123
69124
69125
69126
69127
69128
69129
69130
69131
69132
69133
69134
69135
69136
69137
69138
}

/*
** Finish with a read transaction.  All this does is release the
** read-lock.
*/
SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){
#ifndef SQLITE_ENABLE_SETLK_TIMEOUT
  assert( pWal->writeLock==0 || pWal->readLock<0 );
#endif
  if( pWal->readLock>=0 ){
    sqlite3WalEndWriteTransaction(pWal);
    walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
    pWal->readLock = -1;
  }
}

/*
** Search the wal file for page pgno. If found, set *piRead to the frame that
68829
68830
68831
68832
68833
68834
68835
68836
68837
68838
68839
68840
68841
68842
68843
  int rc;

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  /* If the write-lock is already held, then it was obtained before the
  ** read-transaction was even opened, making this call a no-op.
  ** Return early. */
  if( pWal->writeLock ){
    assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) );
    return SQLITE_OK;
  }
#endif

  /* Cannot start a write transaction without first holding a read
  ** transaction. */
  assert( pWal->readLock>=0 );







|







69317
69318
69319
69320
69321
69322
69323
69324
69325
69326
69327
69328
69329
69330
69331
  int rc;

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  /* If the write-lock is already held, then it was obtained before the
  ** read-transaction was even opened, making this call a no-op.
  ** Return early. */
  if( pWal->writeLock ){
    assert( !memcmp(&pWal->hdr,(void*)pWal->apWiData[0],sizeof(WalIndexHdr)) );
    return SQLITE_OK;
  }
#endif

  /* Cannot start a write transaction without first holding a read
  ** transaction. */
  assert( pWal->readLock>=0 );
70278
70279
70280
70281
70282
70283
70284






70285
70286
70287
70288
70289
70290
70291
** root-node and 3 for all other internal nodes.
**
** If a tree that appears to be taller than this is encountered, it is
** assumed that the database is corrupt.
*/
#define BTCURSOR_MAX_DEPTH 20







/*
** A cursor is a pointer to a particular entry within a particular
** b-tree within a database file.
**
** The entry is identified by its MemPage and the index in
** MemPage.aCell[] of the entry.
**







>
>
>
>
>
>







70766
70767
70768
70769
70770
70771
70772
70773
70774
70775
70776
70777
70778
70779
70780
70781
70782
70783
70784
70785
** root-node and 3 for all other internal nodes.
**
** If a tree that appears to be taller than this is encountered, it is
** assumed that the database is corrupt.
*/
#define BTCURSOR_MAX_DEPTH 20

/*
** Maximum amount of storage local to a database page, regardless of
** page size.
*/
#define BT_MAX_LOCAL  65501  /* 65536 - 35 */

/*
** A cursor is a pointer to a particular entry within a particular
** b-tree within a database file.
**
** The entry is identified by its MemPage and the index in
** MemPage.aCell[] of the entry.
**
70686
70687
70688
70689
70690
70691
70692
70693
70694
70695
70696
70697
70698
70699
70700
** Enter the mutexes in ascending order by BtShared pointer address
** to avoid the possibility of deadlock when two threads with
** two or more btrees in common both try to lock all their btrees
** at the same instant.
*/
static void SQLITE_NOINLINE btreeEnterAll(sqlite3 *db){
  int i;
  int skipOk = 1;
  Btree *p;
  assert( sqlite3_mutex_held(db->mutex) );
  for(i=0; i<db->nDb; i++){
    p = db->aDb[i].pBt;
    if( p && p->sharable ){
      sqlite3BtreeEnter(p);
      skipOk = 0;







|







71180
71181
71182
71183
71184
71185
71186
71187
71188
71189
71190
71191
71192
71193
71194
** Enter the mutexes in ascending order by BtShared pointer address
** to avoid the possibility of deadlock when two threads with
** two or more btrees in common both try to lock all their btrees
** at the same instant.
*/
static void SQLITE_NOINLINE btreeEnterAll(sqlite3 *db){
  int i;
  u8 skipOk = 1;
  Btree *p;
  assert( sqlite3_mutex_held(db->mutex) );
  for(i=0; i<db->nDb; i++){
    p = db->aDb[i].pBt;
    if( p && p->sharable ){
      sqlite3BtreeEnter(p);
      skipOk = 0;
71832
71833
71834
71835
71836
71837
71838
71839
71840
71841
71842
71843
71844
71845
71846


/*
** Provide flag hints to the cursor.
*/
SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){
  assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 );
  pCur->hints = x;
}


#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** Given a page number of a regular database page, return the page
** number for the pointer-map page that contains the entry for the







|







72326
72327
72328
72329
72330
72331
72332
72333
72334
72335
72336
72337
72338
72339
72340


/*
** Provide flag hints to the cursor.
*/
SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){
  assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 );
  pCur->hints = (u8)x;
}


#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** Given a page number of a regular database page, return the page
** number for the pointer-map page that contains the entry for the
72026
72027
72028
72029
72030
72031
72032

72033
72034
72035
72036
72037
72038
72039
72040
72041
72042
72043
72044
72045
72046
72047
/*
** Given a record with nPayload bytes of payload stored within btree
** page pPage, return the number of bytes of payload stored locally.
*/
static int btreePayloadToLocal(MemPage *pPage, i64 nPayload){
  int maxLocal;  /* Maximum amount of payload held locally */
  maxLocal = pPage->maxLocal;

  if( nPayload<=maxLocal ){
    return nPayload;
  }else{
    int minLocal;  /* Minimum amount of payload held locally */
    int surplus;   /* Overflow payload available for local storage */
    minLocal = pPage->minLocal;
    surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize-4);
    return ( surplus <= maxLocal ) ? surplus : minLocal;
  }
}

/*
** The following routines are implementations of the MemPage.xParseCell()
** method.
**







>

|




|
|







72520
72521
72522
72523
72524
72525
72526
72527
72528
72529
72530
72531
72532
72533
72534
72535
72536
72537
72538
72539
72540
72541
72542
/*
** Given a record with nPayload bytes of payload stored within btree
** page pPage, return the number of bytes of payload stored locally.
*/
static int btreePayloadToLocal(MemPage *pPage, i64 nPayload){
  int maxLocal;  /* Maximum amount of payload held locally */
  maxLocal = pPage->maxLocal;
  assert( nPayload>=0 );
  if( nPayload<=maxLocal ){
    return (int)nPayload;
  }else{
    int minLocal;  /* Minimum amount of payload held locally */
    int surplus;   /* Overflow payload available for local storage */
    minLocal = pPage->minLocal;
    surplus = (int)(minLocal +(nPayload - minLocal)%(pPage->pBt->usableSize-4));
    return (surplus <= maxLocal) ? surplus : minLocal;
  }
}

/*
** The following routines are implementations of the MemPage.xParseCell()
** method.
**
72143
72144
72145
72146
72147
72148
72149


72150
72151
72152
72153
72154
72155
72156
72157
72158
72159
72160
72161
  pIter++;

  pInfo->nKey = *(i64*)&iKey;
  pInfo->nPayload = nPayload;
  pInfo->pPayload = pIter;
  testcase( nPayload==pPage->maxLocal );
  testcase( nPayload==(u32)pPage->maxLocal+1 );


  if( nPayload<=pPage->maxLocal ){
    /* This is the (easy) common case where the entire payload fits
    ** on the local page.  No overflow is required.
    */
    pInfo->nSize = nPayload + (u16)(pIter - pCell);
    if( pInfo->nSize<4 ) pInfo->nSize = 4;
    pInfo->nLocal = (u16)nPayload;
  }else{
    btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo);
  }
}
static void btreeParseCellPtrIndex(







>
>




|







72638
72639
72640
72641
72642
72643
72644
72645
72646
72647
72648
72649
72650
72651
72652
72653
72654
72655
72656
72657
72658
  pIter++;

  pInfo->nKey = *(i64*)&iKey;
  pInfo->nPayload = nPayload;
  pInfo->pPayload = pIter;
  testcase( nPayload==pPage->maxLocal );
  testcase( nPayload==(u32)pPage->maxLocal+1 );
  assert( nPayload>=0 );
  assert( pPage->maxLocal <= BT_MAX_LOCAL );
  if( nPayload<=pPage->maxLocal ){
    /* This is the (easy) common case where the entire payload fits
    ** on the local page.  No overflow is required.
    */
    pInfo->nSize = (u16)nPayload + (u16)(pIter - pCell);
    if( pInfo->nSize<4 ) pInfo->nSize = 4;
    pInfo->nLocal = (u16)nPayload;
  }else{
    btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo);
  }
}
static void btreeParseCellPtrIndex(
72180
72181
72182
72183
72184
72185
72186


72187
72188
72189
72190
72191
72192
72193
72194
72195
72196
72197
72198
  }
  pIter++;
  pInfo->nKey = nPayload;
  pInfo->nPayload = nPayload;
  pInfo->pPayload = pIter;
  testcase( nPayload==pPage->maxLocal );
  testcase( nPayload==(u32)pPage->maxLocal+1 );


  if( nPayload<=pPage->maxLocal ){
    /* This is the (easy) common case where the entire payload fits
    ** on the local page.  No overflow is required.
    */
    pInfo->nSize = nPayload + (u16)(pIter - pCell);
    if( pInfo->nSize<4 ) pInfo->nSize = 4;
    pInfo->nLocal = (u16)nPayload;
  }else{
    btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo);
  }
}
static void btreeParseCell(







>
>




|







72677
72678
72679
72680
72681
72682
72683
72684
72685
72686
72687
72688
72689
72690
72691
72692
72693
72694
72695
72696
72697
  }
  pIter++;
  pInfo->nKey = nPayload;
  pInfo->nPayload = nPayload;
  pInfo->pPayload = pIter;
  testcase( nPayload==pPage->maxLocal );
  testcase( nPayload==(u32)pPage->maxLocal+1 );
  assert( nPayload>=0 );
  assert( pPage->maxLocal <= BT_MAX_LOCAL );
  if( nPayload<=pPage->maxLocal ){
    /* This is the (easy) common case where the entire payload fits
    ** on the local page.  No overflow is required.
    */
    pInfo->nSize = (u16)nPayload + (u16)(pIter - pCell);
    if( pInfo->nSize<4 ) pInfo->nSize = 4;
    pInfo->nLocal = (u16)nPayload;
  }else{
    btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo);
  }
}
static void btreeParseCell(
72723
72724
72725
72726
72727
72728
72729
72730
72731
72732
72733
72734
72735
72736
72737
72738
72739
72740
72741
72742
72743
72744
**
** Even though the freeblock list was checked by btreeComputeFreeSpace(),
** that routine will not detect overlap between cells or freeblocks.  Nor
** does it detect cells or freeblocks that encroach into the reserved bytes
** at the end of the page.  So do additional corruption checks inside this
** routine and return SQLITE_CORRUPT if any problems are found.
*/
static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
  u16 iPtr;                             /* Address of ptr to next freeblock */
  u16 iFreeBlk;                         /* Address of the next freeblock */
  u8 hdr;                               /* Page header size.  0 or 100 */
  u8 nFrag = 0;                         /* Reduction in fragmentation */
  u16 iOrigSize = iSize;                /* Original value of iSize */
  u16 x;                                /* Offset to cell content area */
  u32 iEnd = iStart + iSize;            /* First byte past the iStart buffer */
  unsigned char *data = pPage->aData;   /* Page content */
  u8 *pTmp;                             /* Temporary ptr into data[] */

  assert( pPage->pBt!=0 );
  assert( sqlite3PagerIswriteable(pPage->pDbPage) );
  assert( CORRUPT_DB || iStart>=pPage->hdrOffset+6+pPage->childPtrSize );
  assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize );







|
|
|

|
|
|
|







73222
73223
73224
73225
73226
73227
73228
73229
73230
73231
73232
73233
73234
73235
73236
73237
73238
73239
73240
73241
73242
73243
**
** Even though the freeblock list was checked by btreeComputeFreeSpace(),
** that routine will not detect overlap between cells or freeblocks.  Nor
** does it detect cells or freeblocks that encroach into the reserved bytes
** at the end of the page.  So do additional corruption checks inside this
** routine and return SQLITE_CORRUPT if any problems are found.
*/
static int freeSpace(MemPage *pPage, int iStart, int iSize){
  int iPtr;                             /* Address of ptr to next freeblock */
  int iFreeBlk;                         /* Address of the next freeblock */
  u8 hdr;                               /* Page header size.  0 or 100 */
  int nFrag = 0;                        /* Reduction in fragmentation */
  int iOrigSize = iSize;                /* Original value of iSize */
  int x;                                /* Offset to cell content area */
  int iEnd = iStart + iSize;            /* First byte past the iStart buffer */
  unsigned char *data = pPage->aData;   /* Page content */
  u8 *pTmp;                             /* Temporary ptr into data[] */

  assert( pPage->pBt!=0 );
  assert( sqlite3PagerIswriteable(pPage->pDbPage) );
  assert( CORRUPT_DB || iStart>=pPage->hdrOffset+6+pPage->childPtrSize );
  assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize );
72757
72758
72759
72760
72761
72762
72763
72764
72765
72766
72767
72768
72769
72770
72771
72772
72773
72774
72775
72776
72777
72778
72779
72780
72781
72782
72783
72784
72785
72786
72787
72788
72789
72790
72791
72792
72793
72794
72795
72796
72797
72798
72799
72800
72801
72802
72803
72804
72805
72806
72807
72808
72809
72810
72811
72812
72813
72814
72815
72816
72817
72818
72819
72820

72821
72822
72823
72824
72825
72826
72827
72828
    while( (iFreeBlk = get2byte(&data[iPtr]))<iStart ){
      if( iFreeBlk<=iPtr ){
        if( iFreeBlk==0 ) break; /* TH3: corrupt082.100 */
        return SQLITE_CORRUPT_PAGE(pPage);
      }
      iPtr = iFreeBlk;
    }
    if( iFreeBlk>pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */
      return SQLITE_CORRUPT_PAGE(pPage);
    }
    assert( iFreeBlk>iPtr || iFreeBlk==0 || CORRUPT_DB );

    /* At this point:
    **    iFreeBlk:   First freeblock after iStart, or zero if none
    **    iPtr:       The address of a pointer to iFreeBlk
    **
    ** Check to see if iFreeBlk should be coalesced onto the end of iStart.
    */
    if( iFreeBlk && iEnd+3>=iFreeBlk ){
      nFrag = iFreeBlk - iEnd;
      if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage);
      iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]);
      if( iEnd > pPage->pBt->usableSize ){
        return SQLITE_CORRUPT_PAGE(pPage);
      }
      iSize = iEnd - iStart;
      iFreeBlk = get2byte(&data[iFreeBlk]);
    }

    /* If iPtr is another freeblock (that is, if iPtr is not the freelist
    ** pointer in the page header) then check to see if iStart should be
    ** coalesced onto the end of iPtr.
    */
    if( iPtr>hdr+1 ){
      int iPtrEnd = iPtr + get2byte(&data[iPtr+2]);
      if( iPtrEnd+3>=iStart ){
        if( iPtrEnd>iStart ) return SQLITE_CORRUPT_PAGE(pPage);
        nFrag += iStart - iPtrEnd;
        iSize = iEnd - iPtr;
        iStart = iPtr;
      }
    }
    if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage);
    data[hdr+7] -= nFrag;
  }
  pTmp = &data[hdr+5];
  x = get2byte(pTmp);
  if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){
    /* Overwrite deleted information with zeros when the secure_delete
    ** option is enabled */
    memset(&data[iStart], 0, iSize);
  }
  if( iStart<=x ){
    /* The new freeblock is at the beginning of the cell content area,
    ** so just extend the cell content area rather than create another
    ** freelist entry */
    if( iStart<x ) return SQLITE_CORRUPT_PAGE(pPage);
    if( iPtr!=hdr+1 ) return SQLITE_CORRUPT_PAGE(pPage);
    put2byte(&data[hdr+1], iFreeBlk);
    put2byte(&data[hdr+5], iEnd);
  }else{
    /* Insert the new freeblock into the freelist */
    put2byte(&data[iPtr], iStart);
    put2byte(&data[iStart], iFreeBlk);

    put2byte(&data[iStart+2], iSize);
  }
  pPage->nFree += iOrigSize;
  return SQLITE_OK;
}

/*
** Decode the flags byte (the first byte of the header) for a page







|














|




















|




















>
|







73256
73257
73258
73259
73260
73261
73262
73263
73264
73265
73266
73267
73268
73269
73270
73271
73272
73273
73274
73275
73276
73277
73278
73279
73280
73281
73282
73283
73284
73285
73286
73287
73288
73289
73290
73291
73292
73293
73294
73295
73296
73297
73298
73299
73300
73301
73302
73303
73304
73305
73306
73307
73308
73309
73310
73311
73312
73313
73314
73315
73316
73317
73318
73319
73320
73321
73322
73323
73324
73325
73326
73327
73328
    while( (iFreeBlk = get2byte(&data[iPtr]))<iStart ){
      if( iFreeBlk<=iPtr ){
        if( iFreeBlk==0 ) break; /* TH3: corrupt082.100 */
        return SQLITE_CORRUPT_PAGE(pPage);
      }
      iPtr = iFreeBlk;
    }
    if( iFreeBlk>(int)pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */
      return SQLITE_CORRUPT_PAGE(pPage);
    }
    assert( iFreeBlk>iPtr || iFreeBlk==0 || CORRUPT_DB );

    /* At this point:
    **    iFreeBlk:   First freeblock after iStart, or zero if none
    **    iPtr:       The address of a pointer to iFreeBlk
    **
    ** Check to see if iFreeBlk should be coalesced onto the end of iStart.
    */
    if( iFreeBlk && iEnd+3>=iFreeBlk ){
      nFrag = iFreeBlk - iEnd;
      if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage);
      iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]);
      if( iEnd > (int)pPage->pBt->usableSize ){
        return SQLITE_CORRUPT_PAGE(pPage);
      }
      iSize = iEnd - iStart;
      iFreeBlk = get2byte(&data[iFreeBlk]);
    }

    /* If iPtr is another freeblock (that is, if iPtr is not the freelist
    ** pointer in the page header) then check to see if iStart should be
    ** coalesced onto the end of iPtr.
    */
    if( iPtr>hdr+1 ){
      int iPtrEnd = iPtr + get2byte(&data[iPtr+2]);
      if( iPtrEnd+3>=iStart ){
        if( iPtrEnd>iStart ) return SQLITE_CORRUPT_PAGE(pPage);
        nFrag += iStart - iPtrEnd;
        iSize = iEnd - iPtr;
        iStart = iPtr;
      }
    }
    if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage);
    data[hdr+7] -= (u8)nFrag;
  }
  pTmp = &data[hdr+5];
  x = get2byte(pTmp);
  if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){
    /* Overwrite deleted information with zeros when the secure_delete
    ** option is enabled */
    memset(&data[iStart], 0, iSize);
  }
  if( iStart<=x ){
    /* The new freeblock is at the beginning of the cell content area,
    ** so just extend the cell content area rather than create another
    ** freelist entry */
    if( iStart<x ) return SQLITE_CORRUPT_PAGE(pPage);
    if( iPtr!=hdr+1 ) return SQLITE_CORRUPT_PAGE(pPage);
    put2byte(&data[hdr+1], iFreeBlk);
    put2byte(&data[hdr+5], iEnd);
  }else{
    /* Insert the new freeblock into the freelist */
    put2byte(&data[iPtr], iStart);
    put2byte(&data[iStart], iFreeBlk);
    assert( iSize>=0 && iSize<=0xffff );
    put2byte(&data[iStart+2], (u16)iSize);
  }
  pPage->nFree += iOrigSize;
  return SQLITE_OK;
}

/*
** Decode the flags byte (the first byte of the header) for a page
73040
73041
73042
73043
73044
73045
73046
73047
73048
73049
73050
73051
73052
73053
73054
  ** the b-tree page type. */
  if( decodeFlags(pPage, data[0]) ){
    return SQLITE_CORRUPT_PAGE(pPage);
  }
  assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
  pPage->maskPage = (u16)(pBt->pageSize - 1);
  pPage->nOverflow = 0;
  pPage->cellOffset = pPage->hdrOffset + 8 + pPage->childPtrSize;
  pPage->aCellIdx = data + pPage->childPtrSize + 8;
  pPage->aDataEnd = pPage->aData + pBt->pageSize;
  pPage->aDataOfst = pPage->aData + pPage->childPtrSize;
  /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
  ** number of cells on the page. */
  pPage->nCell = get2byte(&data[3]);
  if( pPage->nCell>MX_CELL(pBt) ){







|







73540
73541
73542
73543
73544
73545
73546
73547
73548
73549
73550
73551
73552
73553
73554
  ** the b-tree page type. */
  if( decodeFlags(pPage, data[0]) ){
    return SQLITE_CORRUPT_PAGE(pPage);
  }
  assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
  pPage->maskPage = (u16)(pBt->pageSize - 1);
  pPage->nOverflow = 0;
  pPage->cellOffset = (u16)(pPage->hdrOffset + 8 + pPage->childPtrSize);
  pPage->aCellIdx = data + pPage->childPtrSize + 8;
  pPage->aDataEnd = pPage->aData + pBt->pageSize;
  pPage->aDataOfst = pPage->aData + pPage->childPtrSize;
  /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
  ** number of cells on the page. */
  pPage->nCell = get2byte(&data[3]);
  if( pPage->nCell>MX_CELL(pBt) ){
73074
73075
73076
73077
73078
73079
73080
73081
73082
73083
73084
73085
73086
73087
73088
73089
73090
73091
73092
73093
73094
73095
73096
73097
73098
73099
73100
73101
73102
73103
73104
73105
73106
/*
** Set up a raw page so that it looks like a database page holding
** no entries.
*/
static void zeroPage(MemPage *pPage, int flags){
  unsigned char *data = pPage->aData;
  BtShared *pBt = pPage->pBt;
  u8 hdr = pPage->hdrOffset;
  u16 first;

  assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno || CORRUPT_DB );
  assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
  assert( sqlite3PagerGetData(pPage->pDbPage) == data );
  assert( sqlite3PagerIswriteable(pPage->pDbPage) );
  assert( sqlite3_mutex_held(pBt->mutex) );
  if( pBt->btsFlags & BTS_FAST_SECURE ){
    memset(&data[hdr], 0, pBt->usableSize - hdr);
  }
  data[hdr] = (char)flags;
  first = hdr + ((flags&PTF_LEAF)==0 ? 12 : 8);
  memset(&data[hdr+1], 0, 4);
  data[hdr+7] = 0;
  put2byte(&data[hdr+5], pBt->usableSize);
  pPage->nFree = (u16)(pBt->usableSize - first);
  decodeFlags(pPage, flags);
  pPage->cellOffset = first;
  pPage->aDataEnd = &data[pBt->pageSize];
  pPage->aCellIdx = &data[first];
  pPage->aDataOfst = &data[pPage->childPtrSize];
  pPage->nOverflow = 0;
  assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
  pPage->maskPage = (u16)(pBt->pageSize - 1);
  pPage->nCell = 0;







|
|
















|







73574
73575
73576
73577
73578
73579
73580
73581
73582
73583
73584
73585
73586
73587
73588
73589
73590
73591
73592
73593
73594
73595
73596
73597
73598
73599
73600
73601
73602
73603
73604
73605
73606
/*
** Set up a raw page so that it looks like a database page holding
** no entries.
*/
static void zeroPage(MemPage *pPage, int flags){
  unsigned char *data = pPage->aData;
  BtShared *pBt = pPage->pBt;
  int hdr = pPage->hdrOffset;
  int first;

  assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno || CORRUPT_DB );
  assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
  assert( sqlite3PagerGetData(pPage->pDbPage) == data );
  assert( sqlite3PagerIswriteable(pPage->pDbPage) );
  assert( sqlite3_mutex_held(pBt->mutex) );
  if( pBt->btsFlags & BTS_FAST_SECURE ){
    memset(&data[hdr], 0, pBt->usableSize - hdr);
  }
  data[hdr] = (char)flags;
  first = hdr + ((flags&PTF_LEAF)==0 ? 12 : 8);
  memset(&data[hdr+1], 0, 4);
  data[hdr+7] = 0;
  put2byte(&data[hdr+5], pBt->usableSize);
  pPage->nFree = (u16)(pBt->usableSize - first);
  decodeFlags(pPage, flags);
  pPage->cellOffset = (u16)first;
  pPage->aDataEnd = &data[pBt->pageSize];
  pPage->aCellIdx = &data[first];
  pPage->aDataOfst = &data[pPage->childPtrSize];
  pPage->nOverflow = 0;
  assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
  pPage->maskPage = (u16)(pBt->pageSize - 1);
  pPage->nCell = 0;
73878
73879
73880
73881
73882
73883
73884
73885
73886
73887
73888
73889
73890
73891
73892
*/
SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
  int rc = SQLITE_OK;
  int x;
  BtShared *pBt = p->pBt;
  assert( nReserve>=0 && nReserve<=255 );
  sqlite3BtreeEnter(p);
  pBt->nReserveWanted = nReserve;
  x = pBt->pageSize - pBt->usableSize;
  if( nReserve<x ) nReserve = x;
  if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
    sqlite3BtreeLeave(p);
    return SQLITE_READONLY;
  }
  assert( nReserve>=0 && nReserve<=255 );







|







74378
74379
74380
74381
74382
74383
74384
74385
74386
74387
74388
74389
74390
74391
74392
*/
SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
  int rc = SQLITE_OK;
  int x;
  BtShared *pBt = p->pBt;
  assert( nReserve>=0 && nReserve<=255 );
  sqlite3BtreeEnter(p);
  pBt->nReserveWanted = (u8)nReserve;
  x = pBt->pageSize - pBt->usableSize;
  if( nReserve<x ) nReserve = x;
  if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
    sqlite3BtreeLeave(p);
    return SQLITE_READONLY;
  }
  assert( nReserve>=0 && nReserve<=255 );
73984
73985
73986
73987
73988
73989
73990
73991
73992
73993
73994
73995
73996
73997
73998
  int b;
  if( p==0 ) return 0;
  sqlite3BtreeEnter(p);
  assert( BTS_OVERWRITE==BTS_SECURE_DELETE*2 );
  assert( BTS_FAST_SECURE==(BTS_OVERWRITE|BTS_SECURE_DELETE) );
  if( newFlag>=0 ){
    p->pBt->btsFlags &= ~BTS_FAST_SECURE;
    p->pBt->btsFlags |= BTS_SECURE_DELETE*newFlag;
  }
  b = (p->pBt->btsFlags & BTS_FAST_SECURE)/BTS_SECURE_DELETE;
  sqlite3BtreeLeave(p);
  return b;
}

/*







|







74484
74485
74486
74487
74488
74489
74490
74491
74492
74493
74494
74495
74496
74497
74498
  int b;
  if( p==0 ) return 0;
  sqlite3BtreeEnter(p);
  assert( BTS_OVERWRITE==BTS_SECURE_DELETE*2 );
  assert( BTS_FAST_SECURE==(BTS_OVERWRITE|BTS_SECURE_DELETE) );
  if( newFlag>=0 ){
    p->pBt->btsFlags &= ~BTS_FAST_SECURE;
    p->pBt->btsFlags |= (u16)(BTS_SECURE_DELETE*newFlag);
  }
  b = (p->pBt->btsFlags & BTS_FAST_SECURE)/BTS_SECURE_DELETE;
  sqlite3BtreeLeave(p);
  return b;
}

/*
78432
78433
78434
78435
78436
78437
78438

78439
78440
78441
78442
78443
78444
78445
78446
    if( pCArray->ixNx[k]<=i ){
      k++;
      pSrcEnd = pCArray->apEnd[k];
    }
  }

  /* The pPg->nFree field is now set incorrectly. The caller will fix it. */

  pPg->nCell = nCell;
  pPg->nOverflow = 0;

  put2byte(&aData[hdr+1], 0);
  put2byte(&aData[hdr+3], pPg->nCell);
  put2byte(&aData[hdr+5], pData - aData);
  aData[hdr+7] = 0x00;
  return SQLITE_OK;







>
|







78932
78933
78934
78935
78936
78937
78938
78939
78940
78941
78942
78943
78944
78945
78946
78947
    if( pCArray->ixNx[k]<=i ){
      k++;
      pSrcEnd = pCArray->apEnd[k];
    }
  }

  /* The pPg->nFree field is now set incorrectly. The caller will fix it. */
  assert( nCell < 10922 );
  pPg->nCell = (u16)nCell;
  pPg->nOverflow = 0;

  put2byte(&aData[hdr+1], 0);
  put2byte(&aData[hdr+3], pPg->nCell);
  put2byte(&aData[hdr+5], pData - aData);
  aData[hdr+7] = 0x00;
  return SQLITE_OK;
78679
78680
78681
78682
78683
78684
78685


78686
78687


78688
78689
78690
78691
78692
78693
78694
78695

  /* Append cells to the end of the page */
  assert( nCell>=0 );
  pCellptr = &pPg->aCellIdx[nCell*2];
  if( pageInsertArray(
        pPg, pBegin, &pData, pCellptr,
        iNew+nCell, nNew-nCell, pCArray


  ) ) goto editpage_fail;



  pPg->nCell = nNew;
  pPg->nOverflow = 0;

  put2byte(&aData[hdr+3], pPg->nCell);
  put2byte(&aData[hdr+5], pData - aData);

#ifdef SQLITE_DEBUG
  for(i=0; i<nNew && !CORRUPT_DB; i++){







>
>
|
|
>
>
|







79180
79181
79182
79183
79184
79185
79186
79187
79188
79189
79190
79191
79192
79193
79194
79195
79196
79197
79198
79199
79200

  /* Append cells to the end of the page */
  assert( nCell>=0 );
  pCellptr = &pPg->aCellIdx[nCell*2];
  if( pageInsertArray(
        pPg, pBegin, &pData, pCellptr,
        iNew+nCell, nNew-nCell, pCArray
      )
  ){
    goto editpage_fail;
  }

  assert( nNew < 10922 );
  pPg->nCell = (u16)nNew;
  pPg->nOverflow = 0;

  put2byte(&aData[hdr+3], pPg->nCell);
  put2byte(&aData[hdr+5], pData - aData);

#ifdef SQLITE_DEBUG
  for(i=0; i<nNew && !CORRUPT_DB; i++){
78990
78991
78992
78993
78994
78995
78996
78997
78998
78999
79000
79001
79002
79003
79004
  int rc = SQLITE_OK;          /* The return code */
  u16 leafCorrection;          /* 4 if pPage is a leaf.  0 if not */
  int leafData;                /* True if pPage is a leaf of a LEAFDATA tree */
  int usableSpace;             /* Bytes in pPage beyond the header */
  int pageFlags;               /* Value of pPage->aData[0] */
  int iSpace1 = 0;             /* First unused byte of aSpace1[] */
  int iOvflSpace = 0;          /* First unused byte of aOvflSpace[] */
  int szScratch;               /* Size of scratch memory requested */
  MemPage *apOld[NB];          /* pPage and up to two siblings */
  MemPage *apNew[NB+2];        /* pPage and up to NB siblings after balancing */
  u8 *pRight;                  /* Location in parent of right-sibling pointer */
  u8 *apDiv[NB-1];             /* Divider cells in pParent */
  int cntNew[NB+2];            /* Index in b.paCell[] of cell after i-th page */
  int cntOld[NB+2];            /* Old index in b.apCell[] */
  int szNew[NB+2];             /* Combined size of cells placed on i-th page */







|







79495
79496
79497
79498
79499
79500
79501
79502
79503
79504
79505
79506
79507
79508
79509
  int rc = SQLITE_OK;          /* The return code */
  u16 leafCorrection;          /* 4 if pPage is a leaf.  0 if not */
  int leafData;                /* True if pPage is a leaf of a LEAFDATA tree */
  int usableSpace;             /* Bytes in pPage beyond the header */
  int pageFlags;               /* Value of pPage->aData[0] */
  int iSpace1 = 0;             /* First unused byte of aSpace1[] */
  int iOvflSpace = 0;          /* First unused byte of aOvflSpace[] */
  u64 szScratch;               /* Size of scratch memory requested */
  MemPage *apOld[NB];          /* pPage and up to two siblings */
  MemPage *apNew[NB+2];        /* pPage and up to NB siblings after balancing */
  u8 *pRight;                  /* Location in parent of right-sibling pointer */
  u8 *apDiv[NB-1];             /* Divider cells in pParent */
  int cntNew[NB+2];            /* Index in b.paCell[] of cell after i-th page */
  int cntOld[NB+2];            /* Old index in b.apCell[] */
  int szNew[NB+2];             /* Combined size of cells placed on i-th page */
80275
80276
80277
80278
80279
80280
80281
80282
80283
80284
80285
80286
80287
80288
80289
    ** overwrite optimization.
    */
    if( loc==0 ){
      getCellInfo(pCur);
      if( pCur->info.nKey==pX->nKey ){
        BtreePayload x2;
        x2.pData = pX->pKey;
        x2.nData = pX->nKey;
        x2.nZero = 0;
        return btreeOverwriteCell(pCur, &x2);
      }
    }
  }
  assert( pCur->eState==CURSOR_VALID
       || (pCur->eState==CURSOR_INVALID && loc) || CORRUPT_DB );







|







80780
80781
80782
80783
80784
80785
80786
80787
80788
80789
80790
80791
80792
80793
80794
    ** overwrite optimization.
    */
    if( loc==0 ){
      getCellInfo(pCur);
      if( pCur->info.nKey==pX->nKey ){
        BtreePayload x2;
        x2.pData = pX->pKey;
        x2.nData = (int)pX->nKey;  assert( pX->nKey<=0x7fffffff );
        x2.nZero = 0;
        return btreeOverwriteCell(pCur, &x2);
      }
    }
  }
  assert( pCur->eState==CURSOR_VALID
       || (pCur->eState==CURSOR_INVALID && loc) || CORRUPT_DB );
80456
80457
80458
80459
80460
80461
80462
80463
80464
80465
80466
80467
80468
80469
80470
80471
80472
80473
80474
80475
80476
80477
80478
80479
80480
80481
80482
80483
80484
80485
80486
80487
80488
80489
80490
80491
80492
80493
80494
80495
  u8 *aOut = pBt->pTmpSpace;    /* Pointer to next output buffer */
  const u8 *aIn;                /* Pointer to next input buffer */
  u32 nIn;                      /* Size of input buffer aIn[] */
  u32 nRem;                     /* Bytes of data still to copy */

  getCellInfo(pSrc);
  if( pSrc->info.nPayload<0x80 ){
    *(aOut++) = pSrc->info.nPayload;
  }else{
    aOut += sqlite3PutVarint(aOut, pSrc->info.nPayload);
  }
  if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey);
  nIn = pSrc->info.nLocal;
  aIn = pSrc->info.pPayload;
  if( aIn+nIn>pSrc->pPage->aDataEnd ){
    return SQLITE_CORRUPT_PAGE(pSrc->pPage);
  }
  nRem = pSrc->info.nPayload;
  if( nIn==nRem && nIn<pDest->pPage->maxLocal ){
    memcpy(aOut, aIn, nIn);
    pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace);
    return SQLITE_OK;
  }else{
    int rc = SQLITE_OK;
    Pager *pSrcPager = pSrc->pBt->pPager;
    u8 *pPgnoOut = 0;
    Pgno ovflIn = 0;
    DbPage *pPageIn = 0;
    MemPage *pPageOut = 0;
    u32 nOut;                     /* Size of output buffer aOut[] */

    nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload);
    pBt->nPreformatSize = nOut + (aOut - pBt->pTmpSpace);
    if( nOut<pSrc->info.nPayload ){
      pPgnoOut = &aOut[nOut];
      pBt->nPreformatSize += 4;
    }

    if( nRem>nIn ){
      if( aIn+nIn+4>pSrc->pPage->aDataEnd ){







|












|











|







80961
80962
80963
80964
80965
80966
80967
80968
80969
80970
80971
80972
80973
80974
80975
80976
80977
80978
80979
80980
80981
80982
80983
80984
80985
80986
80987
80988
80989
80990
80991
80992
80993
80994
80995
80996
80997
80998
80999
81000
  u8 *aOut = pBt->pTmpSpace;    /* Pointer to next output buffer */
  const u8 *aIn;                /* Pointer to next input buffer */
  u32 nIn;                      /* Size of input buffer aIn[] */
  u32 nRem;                     /* Bytes of data still to copy */

  getCellInfo(pSrc);
  if( pSrc->info.nPayload<0x80 ){
    *(aOut++) = (u8)pSrc->info.nPayload;
  }else{
    aOut += sqlite3PutVarint(aOut, pSrc->info.nPayload);
  }
  if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey);
  nIn = pSrc->info.nLocal;
  aIn = pSrc->info.pPayload;
  if( aIn+nIn>pSrc->pPage->aDataEnd ){
    return SQLITE_CORRUPT_PAGE(pSrc->pPage);
  }
  nRem = pSrc->info.nPayload;
  if( nIn==nRem && nIn<pDest->pPage->maxLocal ){
    memcpy(aOut, aIn, nIn);
    pBt->nPreformatSize = nIn + (int)(aOut - pBt->pTmpSpace);
    return SQLITE_OK;
  }else{
    int rc = SQLITE_OK;
    Pager *pSrcPager = pSrc->pBt->pPager;
    u8 *pPgnoOut = 0;
    Pgno ovflIn = 0;
    DbPage *pPageIn = 0;
    MemPage *pPageOut = 0;
    u32 nOut;                     /* Size of output buffer aOut[] */

    nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload);
    pBt->nPreformatSize = (int)nOut + (int)(aOut - pBt->pTmpSpace);
    if( nOut<pSrc->info.nPayload ){
      pPgnoOut = &aOut[nOut];
      pBt->nPreformatSize += 4;
    }

    if( nRem>nIn ){
      if( aIn+nIn+4>pSrc->pPage->aDataEnd ){
85582
85583
85584
85585
85586
85587
85588
85589
85590
85591
85592
85593
85594
85595
85596
85597
85598
85599
85600
85601
  int p2,               /* First argument register */
  int p3,               /* Register into which results are written */
  int nArg,             /* Number of argument */
  const FuncDef *pFunc, /* The function to be invoked */
  int eCallCtx          /* Calling context */
){
  Vdbe *v = pParse->pVdbe;
  int nByte;
  int addr;
  sqlite3_context *pCtx;
  assert( v );
  nByte = sizeof(*pCtx) + (nArg-1)*sizeof(sqlite3_value*);
  pCtx = sqlite3DbMallocRawNN(pParse->db, nByte);
  if( pCtx==0 ){
    assert( pParse->db->mallocFailed );
    freeEphemeralFunction(pParse->db, (FuncDef*)pFunc);
    return 0;
  }
  pCtx->pOut = 0;
  pCtx->pFunc = (FuncDef*)pFunc;







<



<
|







86087
86088
86089
86090
86091
86092
86093

86094
86095
86096

86097
86098
86099
86100
86101
86102
86103
86104
  int p2,               /* First argument register */
  int p3,               /* Register into which results are written */
  int nArg,             /* Number of argument */
  const FuncDef *pFunc, /* The function to be invoked */
  int eCallCtx          /* Calling context */
){
  Vdbe *v = pParse->pVdbe;

  int addr;
  sqlite3_context *pCtx;
  assert( v );

  pCtx = sqlite3DbMallocRawNN(pParse->db, SZ_CONTEXT(nArg));
  if( pCtx==0 ){
    assert( pParse->db->mallocFailed );
    freeEphemeralFunction(pParse->db, (FuncDef*)pFunc);
    return 0;
  }
  pCtx->pOut = 0;
  pCtx->pFunc = (FuncDef*)pFunc;
90663
90664
90665
90666
90667
90668
90669

90670
90671
90672
90673
90674
90675
90676
90677
90678
90679
90680
90681
90682
90683
90684
90685
90686
90687
90688
90689
90690
90691
       || (pCsr->nField==nRealCol+1 && op==SQLITE_DELETE && iReg==-1)
  );

  preupdate.v = v;
  preupdate.pCsr = pCsr;
  preupdate.op = op;
  preupdate.iNewReg = iReg;

  preupdate.keyinfo.db = db;
  preupdate.keyinfo.enc = ENC(db);
  preupdate.keyinfo.nKeyField = pTab->nCol;
  preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder;
  preupdate.iKey1 = iKey1;
  preupdate.iKey2 = iKey2;
  preupdate.pTab = pTab;
  preupdate.iBlobWrite = iBlobWrite;

  db->pPreUpdate = &preupdate;
  db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
  db->pPreUpdate = 0;
  sqlite3DbFree(db, preupdate.aRecord);
  vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked);
  vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked);
  sqlite3VdbeMemRelease(&preupdate.oldipk);
  if( preupdate.aNew ){
    int i;
    for(i=0; i<pCsr->nField; i++){
      sqlite3VdbeMemRelease(&preupdate.aNew[i]);
    }
    sqlite3DbNNFreeNN(db, preupdate.aNew);







>
|
|
|
|









|
|







91166
91167
91168
91169
91170
91171
91172
91173
91174
91175
91176
91177
91178
91179
91180
91181
91182
91183
91184
91185
91186
91187
91188
91189
91190
91191
91192
91193
91194
91195
       || (pCsr->nField==nRealCol+1 && op==SQLITE_DELETE && iReg==-1)
  );

  preupdate.v = v;
  preupdate.pCsr = pCsr;
  preupdate.op = op;
  preupdate.iNewReg = iReg;
  preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace;
  preupdate.pKeyinfo->db = db;
  preupdate.pKeyinfo->enc = ENC(db);
  preupdate.pKeyinfo->nKeyField = pTab->nCol;
  preupdate.pKeyinfo->aSortFlags = (u8*)&fakeSortOrder;
  preupdate.iKey1 = iKey1;
  preupdate.iKey2 = iKey2;
  preupdate.pTab = pTab;
  preupdate.iBlobWrite = iBlobWrite;

  db->pPreUpdate = &preupdate;
  db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
  db->pPreUpdate = 0;
  sqlite3DbFree(db, preupdate.aRecord);
  vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pUnpacked);
  vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pNewUnpacked);
  sqlite3VdbeMemRelease(&preupdate.oldipk);
  if( preupdate.aNew ){
    int i;
    for(i=0; i<pCsr->nField; i++){
      sqlite3VdbeMemRelease(&preupdate.aNew[i]);
    }
    sqlite3DbNNFreeNN(db, preupdate.aNew);
92916
92917
92918
92919
92920
92921
92922
92923
92924
92925
92926
92927
92928
92929
92930

      assert( p->pCsr->eCurType==CURTYPE_BTREE );
      nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor);
      aRec = sqlite3DbMallocRaw(db, nRec);
      if( !aRec ) goto preupdate_old_out;
      rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec);
      if( rc==SQLITE_OK ){
        p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec);
        if( !p->pUnpacked ) rc = SQLITE_NOMEM;
      }
      if( rc!=SQLITE_OK ){
        sqlite3DbFree(db, aRec);
        goto preupdate_old_out;
      }
      p->aRecord = aRec;







|







93420
93421
93422
93423
93424
93425
93426
93427
93428
93429
93430
93431
93432
93433
93434

      assert( p->pCsr->eCurType==CURTYPE_BTREE );
      nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor);
      aRec = sqlite3DbMallocRaw(db, nRec);
      if( !aRec ) goto preupdate_old_out;
      rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec);
      if( rc==SQLITE_OK ){
        p->pUnpacked = vdbeUnpackRecord(p->pKeyinfo, nRec, aRec);
        if( !p->pUnpacked ) rc = SQLITE_NOMEM;
      }
      if( rc!=SQLITE_OK ){
        sqlite3DbFree(db, aRec);
        goto preupdate_old_out;
      }
      p->aRecord = aRec;
92981
92982
92983
92984
92985
92986
92987
92988
92989
92990
92991
92992
92993
92994
92995
SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){
  PreUpdate *p;
#ifdef SQLITE_ENABLE_API_ARMOR
  p = db!=0 ? db->pPreUpdate : 0;
#else
  p = db->pPreUpdate;
#endif
  return (p ? p->keyinfo.nKeyField : 0);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */

#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/*
** This function is designed to be called from within a pre-update callback
** only. It returns zero if the change that caused the callback was made







|







93485
93486
93487
93488
93489
93490
93491
93492
93493
93494
93495
93496
93497
93498
93499
SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){
  PreUpdate *p;
#ifdef SQLITE_ENABLE_API_ARMOR
  p = db!=0 ? db->pPreUpdate : 0;
#else
  p = db->pPreUpdate;
#endif
  return (p ? p->pKeyinfo->nKeyField : 0);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */

#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/*
** This function is designed to be called from within a pre-update callback
** only. It returns zero if the change that caused the callback was made
93064
93065
93066
93067
93068
93069
93070
93071
93072
93073
93074
93075
93076
93077
93078
    /* For an INSERT, memory cell p->iNewReg contains the serialized record
    ** that is being inserted. Deserialize it. */
    UnpackedRecord *pUnpack = p->pNewUnpacked;
    if( !pUnpack ){
      Mem *pData = &p->v->aMem[p->iNewReg];
      rc = ExpandBlob(pData);
      if( rc!=SQLITE_OK ) goto preupdate_new_out;
      pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z);
      if( !pUnpack ){
        rc = SQLITE_NOMEM;
        goto preupdate_new_out;
      }
      p->pNewUnpacked = pUnpack;
    }
    pMem = &pUnpack->aMem[iStore];







|







93568
93569
93570
93571
93572
93573
93574
93575
93576
93577
93578
93579
93580
93581
93582
    /* For an INSERT, memory cell p->iNewReg contains the serialized record
    ** that is being inserted. Deserialize it. */
    UnpackedRecord *pUnpack = p->pNewUnpacked;
    if( !pUnpack ){
      Mem *pData = &p->v->aMem[p->iNewReg];
      rc = ExpandBlob(pData);
      if( rc!=SQLITE_OK ) goto preupdate_new_out;
      pUnpack = vdbeUnpackRecord(p->pKeyinfo, pData->n, pData->z);
      if( !pUnpack ){
        rc = SQLITE_NOMEM;
        goto preupdate_new_out;
      }
      p->pNewUnpacked = pUnpack;
    }
    pMem = &pUnpack->aMem[iStore];
93858
93859
93860
93861
93862
93863
93864
93865
93866
93867
93868
93869
93870
93871
93872
93873
93874
  ** the top of the register space.  Cursor 1 is at Mem[p->nMem-1].
  ** Cursor 2 is at Mem[p->nMem-2]. And so forth.
  */
  Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem;

  i64 nByte;
  VdbeCursor *pCx = 0;
  nByte =
      ROUND8P(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField +
      (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0);

  assert( iCur>=0 && iCur<p->nCursor );
  if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/
    sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]);
    p->apCsr[iCur] = 0;
  }








|
|
|







94362
94363
94364
94365
94366
94367
94368
94369
94370
94371
94372
94373
94374
94375
94376
94377
94378
  ** the top of the register space.  Cursor 1 is at Mem[p->nMem-1].
  ** Cursor 2 is at Mem[p->nMem-2]. And so forth.
  */
  Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem;

  i64 nByte;
  VdbeCursor *pCx = 0;
  nByte = SZ_VDBECURSOR(nField);
  assert( ROUND8(nByte)==nByte );
  if( eCurType==CURTYPE_BTREE ) nByte += sqlite3BtreeCursorSize();

  assert( iCur>=0 && iCur<p->nCursor );
  if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/
    sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]);
    p->apCsr[iCur] = 0;
  }

93893
93894
93895
93896
93897
93898
93899

93900
93901
93902
93903
93904
93905
93906
93907
93908

  p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->zMalloc;
  memset(pCx, 0, offsetof(VdbeCursor,pAltCursor));
  pCx->eCurType = eCurType;
  pCx->nField = nField;
  pCx->aOffset = &pCx->aType[nField];
  if( eCurType==CURTYPE_BTREE ){

    pCx->uc.pCursor = (BtCursor*)
        &pMem->z[ROUND8P(sizeof(VdbeCursor))+2*sizeof(u32)*nField];
    sqlite3BtreeCursorZero(pCx->uc.pCursor);
  }
  return pCx;
}

/*
** The string in pRec is known to look like an integer and to have a







>
|
<







94397
94398
94399
94400
94401
94402
94403
94404
94405

94406
94407
94408
94409
94410
94411
94412

  p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->zMalloc;
  memset(pCx, 0, offsetof(VdbeCursor,pAltCursor));
  pCx->eCurType = eCurType;
  pCx->nField = nField;
  pCx->aOffset = &pCx->aType[nField];
  if( eCurType==CURTYPE_BTREE ){
    assert( ROUND8(SZ_VDBECURSOR(nField))==SZ_VDBECURSOR(nField) );
    pCx->uc.pCursor = (BtCursor*)&pMem->z[SZ_VDBECURSOR(nField)];

    sqlite3BtreeCursorZero(pCx->uc.pCursor);
  }
  return pCx;
}

/*
** The string in pRec is known to look like an integer and to have a
99636
99637
99638
99639
99640
99641
99642
99643
99644
99645
99646
99647
99648
99649
99650
  assert( pC->nullRow==0 );
  assert( pC->uc.pCursor!=0 );
  pCrsr = pC->uc.pCursor;

  /* The OP_RowData opcodes always follow OP_NotExists or
  ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions
  ** that might invalidate the cursor.
  ** If this where not the case, on of the following assert()s
  ** would fail.  Should this ever change (because of changes in the code
  ** generator) then the fix would be to insert a call to
  ** sqlite3VdbeCursorMoveto().
  */
  assert( pC->deferredMoveto==0 );
  assert( sqlite3BtreeCursorIsValid(pCrsr) );








|







100140
100141
100142
100143
100144
100145
100146
100147
100148
100149
100150
100151
100152
100153
100154
  assert( pC->nullRow==0 );
  assert( pC->uc.pCursor!=0 );
  pCrsr = pC->uc.pCursor;

  /* The OP_RowData opcodes always follow OP_NotExists or
  ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions
  ** that might invalidate the cursor.
  ** If this were not the case, one of the following assert()s
  ** would fail.  Should this ever change (because of changes in the code
  ** generator) then the fix would be to insert a call to
  ** sqlite3VdbeCursorMoveto().
  */
  assert( pC->deferredMoveto==0 );
  assert( sqlite3BtreeCursorIsValid(pCrsr) );

101285
101286
101287
101288
101289
101290
101291
101292
101293
101294
101295
101296
101297
101298
101299
  /* Allocate space for (a) the context object and (n-1) extra pointers
  ** to append to the sqlite3_context.argv[1] array, and (b) a memory
  ** cell in which to store the accumulation. Be careful that the memory
  ** cell is 8-byte aligned, even on platforms where a pointer is 32-bits.
  **
  ** Note: We could avoid this by using a regular memory cell from aMem[] for
  ** the accumulator, instead of allocating one here. */
  nAlloc = ROUND8P( sizeof(pCtx[0]) + (n-1)*sizeof(sqlite3_value*) );
  pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem));
  if( pCtx==0 ) goto no_mem;
  pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc);
  assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) );

  sqlite3VdbeMemInit(pCtx->pOut, db, MEM_Null);
  pCtx->pMem = 0;







|







101789
101790
101791
101792
101793
101794
101795
101796
101797
101798
101799
101800
101801
101802
101803
  /* Allocate space for (a) the context object and (n-1) extra pointers
  ** to append to the sqlite3_context.argv[1] array, and (b) a memory
  ** cell in which to store the accumulation. Be careful that the memory
  ** cell is 8-byte aligned, even on platforms where a pointer is 32-bits.
  **
  ** Note: We could avoid this by using a regular memory cell from aMem[] for
  ** the accumulator, instead of allocating one here. */
  nAlloc = ROUND8P( SZ_CONTEXT(n) );
  pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem));
  if( pCtx==0 ) goto no_mem;
  pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc);
  assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) );

  sqlite3VdbeMemInit(pCtx->pOut, db, MEM_Null);
  pCtx->pMem = 0;
102943
102944
102945
102946
102947
102948
102949

102950
102951
102952
102953
102954
102955
102956
){
  int nAttempt = 0;
  int iCol;               /* Index of zColumn in row-record */
  int rc = SQLITE_OK;
  char *zErr = 0;
  Table *pTab;
  Incrblob *pBlob = 0;

  Parse sParse;

#ifdef SQLITE_ENABLE_API_ARMOR
  if( ppBlob==0 ){
    return SQLITE_MISUSE_BKPT;
  }
#endif







>







103447
103448
103449
103450
103451
103452
103453
103454
103455
103456
103457
103458
103459
103460
103461
){
  int nAttempt = 0;
  int iCol;               /* Index of zColumn in row-record */
  int rc = SQLITE_OK;
  char *zErr = 0;
  Table *pTab;
  Incrblob *pBlob = 0;
  int iDb;
  Parse sParse;

#ifdef SQLITE_ENABLE_API_ARMOR
  if( ppBlob==0 ){
    return SQLITE_MISUSE_BKPT;
  }
#endif
102988
102989
102990
102991
102992
102993
102994
102995



102996
102997
102998
102999
103000
103001
103002
103003
103004
103005
103006
103007
103008
103009
103010
103011
103012
103013
    }
#ifndef SQLITE_OMIT_VIEW
    if( pTab && IsView(pTab) ){
      pTab = 0;
      sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
    }
#endif
    if( !pTab ){



      if( sParse.zErrMsg ){
        sqlite3DbFree(db, zErr);
        zErr = sParse.zErrMsg;
        sParse.zErrMsg = 0;
      }
      rc = SQLITE_ERROR;
      sqlite3BtreeLeaveAll(db);
      goto blob_open_out;
    }
    pBlob->pTab = pTab;
    pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;

    /* Now search pTab for the exact column. */
    iCol = sqlite3ColumnIndex(pTab, zColumn);
    if( iCol<0 ){
      sqlite3DbFree(db, zErr);
      zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn);
      rc = SQLITE_ERROR;







|
>
>
>










|







103493
103494
103495
103496
103497
103498
103499
103500
103501
103502
103503
103504
103505
103506
103507
103508
103509
103510
103511
103512
103513
103514
103515
103516
103517
103518
103519
103520
103521
    }
#ifndef SQLITE_OMIT_VIEW
    if( pTab && IsView(pTab) ){
      pTab = 0;
      sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
    }
#endif
    if( pTab==0
     || ((iDb = sqlite3SchemaToIndex(db, pTab->pSchema))==1 &&
         sqlite3OpenTempDatabase(&sParse))
    ){
      if( sParse.zErrMsg ){
        sqlite3DbFree(db, zErr);
        zErr = sParse.zErrMsg;
        sParse.zErrMsg = 0;
      }
      rc = SQLITE_ERROR;
      sqlite3BtreeLeaveAll(db);
      goto blob_open_out;
    }
    pBlob->pTab = pTab;
    pBlob->zDb = db->aDb[iDb].zDbSName;

    /* Now search pTab for the exact column. */
    iCol = sqlite3ColumnIndex(pTab, zColumn);
    if( iCol<0 ){
      sqlite3DbFree(db, zErr);
      zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn);
      rc = SQLITE_ERROR;
103083
103084
103085
103086
103087
103088
103089
103090
103091
103092
103093
103094
103095
103096
103097
        /* blobSeekToRow() will initialize r[1] to the desired rowid */
        {OP_NotExists,      0, 5, 1},  /* 2: Seek the cursor to rowid=r[1] */
        {OP_Column,         0, 0, 1},  /* 3  */
        {OP_ResultRow,      1, 0, 0},  /* 4  */
        {OP_Halt,           0, 0, 0},  /* 5  */
      };
      Vdbe *v = (Vdbe *)pBlob->pStmt;
      int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
      VdbeOp *aOp;

      sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag,
                           pTab->pSchema->schema_cookie,
                           pTab->pSchema->iGeneration);
      sqlite3VdbeChangeP5(v, 1);
      assert( sqlite3VdbeCurrentAddr(v)==2 || db->mallocFailed );







<







103591
103592
103593
103594
103595
103596
103597

103598
103599
103600
103601
103602
103603
103604
        /* blobSeekToRow() will initialize r[1] to the desired rowid */
        {OP_NotExists,      0, 5, 1},  /* 2: Seek the cursor to rowid=r[1] */
        {OP_Column,         0, 0, 1},  /* 3  */
        {OP_ResultRow,      1, 0, 0},  /* 4  */
        {OP_Halt,           0, 0, 0},  /* 5  */
      };
      Vdbe *v = (Vdbe *)pBlob->pStmt;

      VdbeOp *aOp;

      sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag,
                           pTab->pSchema->schema_cookie,
                           pTab->pSchema->iGeneration);
      sqlite3VdbeChangeP5(v, 1);
      assert( sqlite3VdbeCurrentAddr(v)==2 || db->mallocFailed );
103661
103662
103663
103664
103665
103666
103667
103668
103669



103670
103671
103672
103673
103674
103675
103676
  int iMemory;                    /* Offset of free space in list.aMemory */
  int nMemory;                    /* Size of list.aMemory allocation in bytes */
  u8 bUsePMA;                     /* True if one or more PMAs created */
  u8 bUseThreads;                 /* True to use background threads */
  u8 iPrev;                       /* Previous thread used to flush PMA */
  u8 nTask;                       /* Size of aTask[] array */
  u8 typeMask;
  SortSubtask aTask[1];           /* One or more subtasks */
};




#define SORTER_TYPE_INTEGER 0x01
#define SORTER_TYPE_TEXT    0x02

/*
** An instance of the following object is used to read records out of a
** PMA, in sorted order.  The next key to be read is cached in nKey/aKey.







|

>
>
>







104168
104169
104170
104171
104172
104173
104174
104175
104176
104177
104178
104179
104180
104181
104182
104183
104184
104185
104186
  int iMemory;                    /* Offset of free space in list.aMemory */
  int nMemory;                    /* Size of list.aMemory allocation in bytes */
  u8 bUsePMA;                     /* True if one or more PMAs created */
  u8 bUseThreads;                 /* True to use background threads */
  u8 iPrev;                       /* Previous thread used to flush PMA */
  u8 nTask;                       /* Size of aTask[] array */
  u8 typeMask;
  SortSubtask aTask[FLEXARRAY];   /* One or more subtasks */
};

/* Size (in bytes) of a VdbeSorter object that works with N or fewer subtasks */
#define SZ_VDBESORTER(N)  (offsetof(VdbeSorter,aTask)+(N)*sizeof(SortSubtask))

#define SORTER_TYPE_INTEGER 0x01
#define SORTER_TYPE_TEXT    0x02

/*
** An instance of the following object is used to read records out of a
** PMA, in sorted order.  The next key to be read is cached in nKey/aKey.
104295
104296
104297
104298
104299
104300
104301
104302
104303
104304
104305
104306
104307
104308
104309
104310
#endif

  assert( pCsr->pKeyInfo );
  assert( !pCsr->isEphemeral );
  assert( pCsr->eCurType==CURTYPE_SORTER );
  assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*)
               < 0x7fffffff );
  szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*);
  sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask);

  pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
  pCsr->uc.pSorter = pSorter;
  if( pSorter==0 ){
    rc = SQLITE_NOMEM_BKPT;
  }else{
    Btree *pBt = db->aDb[0].pBt;







|
|







104805
104806
104807
104808
104809
104810
104811
104812
104813
104814
104815
104816
104817
104818
104819
104820
#endif

  assert( pCsr->pKeyInfo );
  assert( !pCsr->isEphemeral );
  assert( pCsr->eCurType==CURTYPE_SORTER );
  assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*)
               < 0x7fffffff );
  szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField+1);
  sz = SZ_VDBESORTER(nWorker+1);

  pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
  pCsr->uc.pSorter = pSorter;
  if( pSorter==0 ){
    rc = SQLITE_NOMEM_BKPT;
  }else{
    Btree *pBt = db->aDb[0].pBt;
104760
104761
104762
104763
104764
104765
104766




104767
104768
104769
104770
104771
104772
104773
    }else{
      pNext = p->u.pNext;
    }

    p->u.pNext = 0;
    for(i=0; aSlot[i]; i++){
      p = vdbeSorterMerge(pTask, p, aSlot[i]);




      aSlot[i] = 0;
    }
    aSlot[i] = p;
    p = pNext;
  }

  p = 0;







>
>
>
>







105270
105271
105272
105273
105274
105275
105276
105277
105278
105279
105280
105281
105282
105283
105284
105285
105286
105287
    }else{
      pNext = p->u.pNext;
    }

    p->u.pNext = 0;
    for(i=0; aSlot[i]; i++){
      p = vdbeSorterMerge(pTask, p, aSlot[i]);
      /* ,--Each aSlot[] holds twice as much as the previous. So we cannot use
      ** |  up all 64 aSlots[] with only a 64-bit address space.
      ** v                                                                */
      assert( i<ArraySize(aSlot) );
      aSlot[i] = 0;
    }
    aSlot[i] = p;
    p = pNext;
  }

  p = 0;
109534
109535
109536
109537
109538
109539
109540
109541
109542
109543

109544
109545
109546
109547
109548

109549
109550
109551
109552
109553
109554
109555
109556
109557
109558
109559
109560
109561
109562
109563
109564
109565
109566
109567
109568
109569
SQLITE_PRIVATE int sqlite3ResolveSelfReference(
  Parse *pParse,   /* Parsing context */
  Table *pTab,     /* The table being referenced, or NULL */
  int type,        /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */
  Expr *pExpr,     /* Expression to resolve.  May be NULL. */
  ExprList *pList  /* Expression list to resolve.  May be NULL. */
){
  SrcList sSrc;                   /* Fake SrcList for pParse->pNewTable */
  NameContext sNC;                /* Name context for pParse->pNewTable */
  int rc;


  assert( type==0 || pTab!=0 );
  assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr
          || type==NC_GenCol || pTab==0 );
  memset(&sNC, 0, sizeof(sNC));

  memset(&sSrc, 0, sizeof(sSrc));
  if( pTab ){
    sSrc.nSrc = 1;
    sSrc.a[0].zName = pTab->zName;
    sSrc.a[0].pSTab = pTab;
    sSrc.a[0].iCursor = -1;
    if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){
      /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP
      ** schema elements */
      type |= NC_FromDDL;
    }
  }
  sNC.pParse = pParse;
  sNC.pSrcList = &sSrc;
  sNC.ncFlags = type | NC_IsDDL;
  if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc;
  if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList);
  return rc;
}

/************** End of resolve.c *********************************************/







|


>





>
|

|
|
|
|







|







110048
110049
110050
110051
110052
110053
110054
110055
110056
110057
110058
110059
110060
110061
110062
110063
110064
110065
110066
110067
110068
110069
110070
110071
110072
110073
110074
110075
110076
110077
110078
110079
110080
110081
110082
110083
110084
110085
SQLITE_PRIVATE int sqlite3ResolveSelfReference(
  Parse *pParse,   /* Parsing context */
  Table *pTab,     /* The table being referenced, or NULL */
  int type,        /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */
  Expr *pExpr,     /* Expression to resolve.  May be NULL. */
  ExprList *pList  /* Expression list to resolve.  May be NULL. */
){
  SrcList *pSrc;                  /* Fake SrcList for pParse->pNewTable */
  NameContext sNC;                /* Name context for pParse->pNewTable */
  int rc;
  u8 srcSpace[SZ_SRCLIST_1];     /* Memory space for the fake SrcList */

  assert( type==0 || pTab!=0 );
  assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr
          || type==NC_GenCol || pTab==0 );
  memset(&sNC, 0, sizeof(sNC));
  pSrc = (SrcList*)srcSpace;
  memset(pSrc, 0, SZ_SRCLIST_1);
  if( pTab ){
    pSrc->nSrc = 1;
    pSrc->a[0].zName = pTab->zName;
    pSrc->a[0].pSTab = pTab;
    pSrc->a[0].iCursor = -1;
    if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){
      /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP
      ** schema elements */
      type |= NC_FromDDL;
    }
  }
  sNC.pParse = pParse;
  sNC.pSrcList = pSrc;
  sNC.ncFlags = type | NC_IsDDL;
  if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc;
  if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList);
  return rc;
}

/************** End of resolve.c *********************************************/
111304
111305
111306
111307
111308
111309
111310
111311
111312
111313
111314
111315
111316
111317
111318
** argument. If an OOM condition is encountered, NULL is returned
** and the db->mallocFailed flag set.
*/
#ifndef SQLITE_OMIT_CTE
SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){
  With *pRet = 0;
  if( p ){
    sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1);
    pRet = sqlite3DbMallocZero(db, nByte);
    if( pRet ){
      int i;
      pRet->nCte = p->nCte;
      for(i=0; i<p->nCte; i++){
        pRet->a[i].pSelect = sqlite3SelectDup(db, p->a[i].pSelect, 0);
        pRet->a[i].pCols = sqlite3ExprListDup(db, p->a[i].pCols, 0);







|







111820
111821
111822
111823
111824
111825
111826
111827
111828
111829
111830
111831
111832
111833
111834
** argument. If an OOM condition is encountered, NULL is returned
** and the db->mallocFailed flag set.
*/
#ifndef SQLITE_OMIT_CTE
SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){
  With *pRet = 0;
  if( p ){
    sqlite3_int64 nByte = SZ_WITH(p->nCte);
    pRet = sqlite3DbMallocZero(db, nByte);
    if( pRet ){
      int i;
      pRet->nCte = p->nCte;
      for(i=0; i<p->nCte; i++){
        pRet->a[i].pSelect = sqlite3SelectDup(db, p->a[i].pSelect, 0);
        pRet->a[i].pCols = sqlite3ExprListDup(db, p->a[i].pCols, 0);
111431
111432
111433
111434
111435
111436
111437
111438
111439
111440
111441
111442
111443
111444
111445
111446
111447
111448
111449
** called with a NULL argument.
*/
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \
 || !defined(SQLITE_OMIT_SUBQUERY)
SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){
  SrcList *pNew;
  int i;
  int nByte;
  assert( db!=0 );
  if( p==0 ) return 0;
  nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0);
  pNew = sqlite3DbMallocRawNN(db, nByte );
  if( pNew==0 ) return 0;
  pNew->nSrc = pNew->nAlloc = p->nSrc;
  for(i=0; i<p->nSrc; i++){
    SrcItem *pNewItem = &pNew->a[i];
    const SrcItem *pOldItem = &p->a[i];
    Table *pTab;
    pNewItem->fg = pOldItem->fg;







<


<
|







111947
111948
111949
111950
111951
111952
111953

111954
111955

111956
111957
111958
111959
111960
111961
111962
111963
** called with a NULL argument.
*/
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \
 || !defined(SQLITE_OMIT_SUBQUERY)
SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){
  SrcList *pNew;
  int i;

  assert( db!=0 );
  if( p==0 ) return 0;

  pNew = sqlite3DbMallocRawNN(db, SZ_SRCLIST(p->nSrc) );
  if( pNew==0 ) return 0;
  pNew->nSrc = pNew->nAlloc = p->nSrc;
  for(i=0; i<p->nSrc; i++){
    SrcItem *pNewItem = &pNew->a[i];
    const SrcItem *pOldItem = &p->a[i];
    Table *pTab;
    pNewItem->fg = pOldItem->fg;
111497
111498
111499
111500
111501
111502
111503
111504
111505
111506
111507
111508
111509
111510
111511
  return pNew;
}
SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){
  IdList *pNew;
  int i;
  assert( db!=0 );
  if( p==0 ) return 0;
  pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew)+(p->nId-1)*sizeof(p->a[0]) );
  if( pNew==0 ) return 0;
  pNew->nId = p->nId;
  for(i=0; i<p->nId; i++){
    struct IdList_item *pNewItem = &pNew->a[i];
    const struct IdList_item *pOldItem = &p->a[i];
    pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
  }







|







112011
112012
112013
112014
112015
112016
112017
112018
112019
112020
112021
112022
112023
112024
112025
  return pNew;
}
SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){
  IdList *pNew;
  int i;
  assert( db!=0 );
  if( p==0 ) return 0;
  pNew = sqlite3DbMallocRawNN(db, SZ_IDLIST(p->nId));
  if( pNew==0 ) return 0;
  pNew->nId = p->nId;
  for(i=0; i<p->nId; i++){
    struct IdList_item *pNewItem = &pNew->a[i];
    const struct IdList_item *pOldItem = &p->a[i];
    pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
  }
111529
111530
111531
111532
111533
111534
111535
111536
111537
111538
111539
111540
111541
111542
111543
    pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, flags);
    pNew->op = p->op;
    pNew->pNext = pNext;
    pNew->pPrior = 0;
    pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags);
    pNew->iLimit = 0;
    pNew->iOffset = 0;
    pNew->selFlags = p->selFlags & ~SF_UsesEphemeral;
    pNew->addrOpenEphm[0] = -1;
    pNew->addrOpenEphm[1] = -1;
    pNew->nSelectRow = p->nSelectRow;
    pNew->pWith = sqlite3WithDup(db, p->pWith);
#ifndef SQLITE_OMIT_WINDOWFUNC
    pNew->pWin = 0;
    pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn);







|







112043
112044
112045
112046
112047
112048
112049
112050
112051
112052
112053
112054
112055
112056
112057
    pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, flags);
    pNew->op = p->op;
    pNew->pNext = pNext;
    pNew->pPrior = 0;
    pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags);
    pNew->iLimit = 0;
    pNew->iOffset = 0;
    pNew->selFlags = p->selFlags & ~(u32)SF_UsesEphemeral;
    pNew->addrOpenEphm[0] = -1;
    pNew->addrOpenEphm[1] = -1;
    pNew->nSelectRow = p->nSelectRow;
    pNew->pWith = sqlite3WithDup(db, p->pWith);
#ifndef SQLITE_OMIT_WINDOWFUNC
    pNew->pWin = 0;
    pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn);
111581
111582
111583
111584
111585
111586
111587
111588
111589
111590
111591
111592
111593
111594
111595
111596
111597
111598
111599
111600
111601
111602
111603
111604
111605
111606
111607
111608
111609
111610
111611
111612
111613
111614
111615
111616
SQLITE_PRIVATE SQLITE_NOINLINE ExprList *sqlite3ExprListAppendNew(
  sqlite3 *db,            /* Database handle.  Used for memory allocation */
  Expr *pExpr             /* Expression to be appended. Might be NULL */
){
  struct ExprList_item *pItem;
  ExprList *pList;

  pList = sqlite3DbMallocRawNN(db, sizeof(ExprList)+sizeof(pList->a[0])*4 );
  if( pList==0 ){
    sqlite3ExprDelete(db, pExpr);
    return 0;
  }
  pList->nAlloc = 4;
  pList->nExpr = 1;
  pItem = &pList->a[0];
  *pItem = zeroItem;
  pItem->pExpr = pExpr;
  return pList;
}
SQLITE_PRIVATE SQLITE_NOINLINE ExprList *sqlite3ExprListAppendGrow(
  sqlite3 *db,            /* Database handle.  Used for memory allocation */
  ExprList *pList,        /* List to which to append. Might be NULL */
  Expr *pExpr             /* Expression to be appended. Might be NULL */
){
  struct ExprList_item *pItem;
  ExprList *pNew;
  pList->nAlloc *= 2;
  pNew = sqlite3DbRealloc(db, pList,
       sizeof(*pList)+(pList->nAlloc-1)*sizeof(pList->a[0]));
  if( pNew==0 ){
    sqlite3ExprListDelete(db, pList);
    sqlite3ExprDelete(db, pExpr);
    return 0;
  }else{
    pList = pNew;
  }







|



















|
<







112095
112096
112097
112098
112099
112100
112101
112102
112103
112104
112105
112106
112107
112108
112109
112110
112111
112112
112113
112114
112115
112116
112117
112118
112119
112120
112121
112122

112123
112124
112125
112126
112127
112128
112129
SQLITE_PRIVATE SQLITE_NOINLINE ExprList *sqlite3ExprListAppendNew(
  sqlite3 *db,            /* Database handle.  Used for memory allocation */
  Expr *pExpr             /* Expression to be appended. Might be NULL */
){
  struct ExprList_item *pItem;
  ExprList *pList;

  pList = sqlite3DbMallocRawNN(db, SZ_EXPRLIST(4));
  if( pList==0 ){
    sqlite3ExprDelete(db, pExpr);
    return 0;
  }
  pList->nAlloc = 4;
  pList->nExpr = 1;
  pItem = &pList->a[0];
  *pItem = zeroItem;
  pItem->pExpr = pExpr;
  return pList;
}
SQLITE_PRIVATE SQLITE_NOINLINE ExprList *sqlite3ExprListAppendGrow(
  sqlite3 *db,            /* Database handle.  Used for memory allocation */
  ExprList *pList,        /* List to which to append. Might be NULL */
  Expr *pExpr             /* Expression to be appended. Might be NULL */
){
  struct ExprList_item *pItem;
  ExprList *pNew;
  pList->nAlloc *= 2;
  pNew = sqlite3DbRealloc(db, pList, SZ_EXPRLIST(pList->nAlloc));

  if( pNew==0 ){
    sqlite3ExprListDelete(db, pList);
    sqlite3ExprDelete(db, pExpr);
    return 0;
  }else{
    pList = pNew;
  }
114238
114239
114240
114241
114242
114243
114244
114245
114246
114247
114248
114249
114250
114251
114252
    return target;
  }
  return -1;  /* Not found */
}


/*
** Expresion pExpr is guaranteed to be a TK_COLUMN or equivalent. This
** function checks the Parse.pIdxPartExpr list to see if this column
** can be replaced with a constant value. If so, it generates code to
** put the constant value in a register (ideally, but not necessarily,
** register iTarget) and returns the register number.
**
** Or, if the TK_COLUMN cannot be replaced by a constant, zero is
** returned.







|







114751
114752
114753
114754
114755
114756
114757
114758
114759
114760
114761
114762
114763
114764
114765
    return target;
  }
  return -1;  /* Not found */
}


/*
** Expression pExpr is guaranteed to be a TK_COLUMN or equivalent. This
** function checks the Parse.pIdxPartExpr list to see if this column
** can be replaced with a constant value. If so, it generates code to
** put the constant value in a register (ideally, but not necessarily,
** register iTarget) and returns the register number.
**
** Or, if the TK_COLUMN cannot be replaced by a constant, zero is
** returned.
117479
117480
117481
117482
117483
117484
117485
117486
117487
117488
117489
117490
117491
117492
117493
117494
117495
117496
117497
117498
117499
  if( !pNew ) goto exit_begin_add_column;
  pParse->pNewTable = pNew;
  pNew->nTabRef = 1;
  pNew->nCol = pTab->nCol;
  assert( pNew->nCol>0 );
  nAlloc = (((pNew->nCol-1)/8)*8)+8;
  assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 );
  pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc);
  pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName);
  if( !pNew->aCol || !pNew->zName ){
    assert( db->mallocFailed );
    goto exit_begin_add_column;
  }
  memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
  for(i=0; i<pNew->nCol; i++){
    Column *pCol = &pNew->aCol[i];
    pCol->zCnName = sqlite3DbStrDup(db, pCol->zCnName);
    pCol->hName = sqlite3StrIHash(pCol->zCnName);
  }
  assert( IsOrdinaryTable(pNew) );
  pNew->u.tab.pDfltList = sqlite3ExprListDup(db, pTab->u.tab.pDfltList, 0);







|





|







117992
117993
117994
117995
117996
117997
117998
117999
118000
118001
118002
118003
118004
118005
118006
118007
118008
118009
118010
118011
118012
  if( !pNew ) goto exit_begin_add_column;
  pParse->pNewTable = pNew;
  pNew->nTabRef = 1;
  pNew->nCol = pTab->nCol;
  assert( pNew->nCol>0 );
  nAlloc = (((pNew->nCol-1)/8)*8)+8;
  assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 );
  pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*(u32)nAlloc);
  pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName);
  if( !pNew->aCol || !pNew->zName ){
    assert( db->mallocFailed );
    goto exit_begin_add_column;
  }
  memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*(size_t)pNew->nCol);
  for(i=0; i<pNew->nCol; i++){
    Column *pCol = &pNew->aCol[i];
    pCol->zCnName = sqlite3DbStrDup(db, pCol->zCnName);
    pCol->hName = sqlite3StrIHash(pCol->zCnName);
  }
  assert( IsOrdinaryTable(pNew) );
  pNew->u.tab.pDfltList = sqlite3ExprListDup(db, pTab->u.tab.pDfltList, 0);
118084
118085
118086
118087
118088
118089
118090

118091
118092
118093
118094
118095
118096
118097
118098

118099





118100
118101
118102



118103

118104
118105
118106
118107
118108
118109
118110
  Parse *p,                       /* Memory to use for Parse object */
  const char *zDb,                /* Name of schema SQL belongs to */
  sqlite3 *db,                    /* Database handle */
  const char *zSql,               /* SQL to parse */
  int bTemp                       /* True if SQL is from temp schema */
){
  int rc;


  sqlite3ParseObjectInit(p, db);
  if( zSql==0 ){
    return SQLITE_NOMEM;
  }
  if( sqlite3StrNICmp(zSql,"CREATE ",7)!=0 ){
    return SQLITE_CORRUPT_BKPT;
  }

  db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb);





  p->eParseMode = PARSE_MODE_RENAME;
  p->db = db;
  p->nQueryLoop = 1;



  rc = sqlite3RunParser(p, zSql);

  if( db->mallocFailed ) rc = SQLITE_NOMEM;
  if( rc==SQLITE_OK
   && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0)
  ){
    rc = SQLITE_CORRUPT_BKPT;
  }








>








>
|
>
>
>
>
>



>
>
>

>







118597
118598
118599
118600
118601
118602
118603
118604
118605
118606
118607
118608
118609
118610
118611
118612
118613
118614
118615
118616
118617
118618
118619
118620
118621
118622
118623
118624
118625
118626
118627
118628
118629
118630
118631
118632
118633
118634
  Parse *p,                       /* Memory to use for Parse object */
  const char *zDb,                /* Name of schema SQL belongs to */
  sqlite3 *db,                    /* Database handle */
  const char *zSql,               /* SQL to parse */
  int bTemp                       /* True if SQL is from temp schema */
){
  int rc;
  u64 flags;

  sqlite3ParseObjectInit(p, db);
  if( zSql==0 ){
    return SQLITE_NOMEM;
  }
  if( sqlite3StrNICmp(zSql,"CREATE ",7)!=0 ){
    return SQLITE_CORRUPT_BKPT;
  }
  if( bTemp ){
    db->init.iDb = 1;
  }else{
    int iDb = sqlite3FindDbName(db, zDb);
    assert( iDb>=0 && iDb<=0xff );
    db->init.iDb = (u8)iDb;
  }
  p->eParseMode = PARSE_MODE_RENAME;
  p->db = db;
  p->nQueryLoop = 1;
  flags = db->flags;
  testcase( (db->flags & SQLITE_Comments)==0 && strstr(zSql," /* ")!=0 );
  db->flags |= SQLITE_Comments;
  rc = sqlite3RunParser(p, zSql);
  db->flags = flags;
  if( db->mallocFailed ) rc = SQLITE_NOMEM;
  if( rc==SQLITE_OK
   && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0)
  ){
    rc = SQLITE_CORRUPT_BKPT;
  }

118159
118160
118161
118162
118163
118164
118165
118166
118167
118168

118169
118170
118171
118172
118173
118174
118175
118176
118177
118178
118179
118180
118181

118182
118183
118184
118185
118186
118187
118188
118189
118190
118191
118192
118193
118194
118195
118196
118197
118198
118199
118200
118201
118202
118203
118204
118205
118206
118207

118208
118209
118210
118211
118212
118213
118214
118215
118216
118217
118218
118219
118220
118221
118222
    zQuot = sqlite3MPrintf(db, "\"%w\" ", zNew);
    if( zQuot==0 ){
      return SQLITE_NOMEM;
    }else{
      nQuot = sqlite3Strlen30(zQuot)-1;
    }

    assert( nQuot>=nNew );
    zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1);
  }else{

    zOut = (char*)sqlite3DbMallocZero(db, (nSql*2+1) * 3);
    if( zOut ){
      zBuf1 = &zOut[nSql*2+1];
      zBuf2 = &zOut[nSql*4+2];
    }
  }

  /* At this point pRename->pList contains a list of RenameToken objects
  ** corresponding to all tokens in the input SQL that must be replaced
  ** with the new column name, or with single-quoted versions of themselves.
  ** All that remains is to construct and return the edited SQL string. */
  if( zOut ){
    int nOut = nSql;

    memcpy(zOut, zSql, nSql);
    while( pRename->pList ){
      int iOff;                   /* Offset of token to replace in zOut */
      u32 nReplace;
      const char *zReplace;
      RenameToken *pBest = renameColumnTokenNext(pRename);

      if( zNew ){
        if( bQuote==0 && sqlite3IsIdChar(*pBest->t.z) ){
          nReplace = nNew;
          zReplace = zNew;
        }else{
          nReplace = nQuot;
          zReplace = zQuot;
          if( pBest->t.z[pBest->t.n]=='"' ) nReplace++;
        }
      }else{
        /* Dequote the double-quoted token. Then requote it again, this time
        ** using single quotes. If the character immediately following the
        ** original token within the input SQL was a single quote ('), then
        ** add another space after the new, single-quoted version of the
        ** token. This is so that (SELECT "string"'alias') maps to
        ** (SELECT 'string' 'alias'), and not (SELECT 'string''alias').  */
        memcpy(zBuf1, pBest->t.z, pBest->t.n);
        zBuf1[pBest->t.n] = 0;
        sqlite3Dequote(zBuf1);

        sqlite3_snprintf(nSql*2, zBuf2, "%Q%s", zBuf1,
            pBest->t.z[pBest->t.n]=='\'' ? " " : ""
        );
        zReplace = zBuf2;
        nReplace = sqlite3Strlen30(zReplace);
      }

      iOff = pBest->t.z - zSql;
      if( pBest->t.n!=nReplace ){
        memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n],
            nOut - (iOff + pBest->t.n)
        );
        nOut += nReplace - pBest->t.n;
        zOut[nOut] = '\0';
      }







|
|

>
|











|
>
|


|




|

















>
|






|







118683
118684
118685
118686
118687
118688
118689
118690
118691
118692
118693
118694
118695
118696
118697
118698
118699
118700
118701
118702
118703
118704
118705
118706
118707
118708
118709
118710
118711
118712
118713
118714
118715
118716
118717
118718
118719
118720
118721
118722
118723
118724
118725
118726
118727
118728
118729
118730
118731
118732
118733
118734
118735
118736
118737
118738
118739
118740
118741
118742
118743
118744
118745
118746
118747
118748
118749
    zQuot = sqlite3MPrintf(db, "\"%w\" ", zNew);
    if( zQuot==0 ){
      return SQLITE_NOMEM;
    }else{
      nQuot = sqlite3Strlen30(zQuot)-1;
    }

    assert( nQuot>=nNew && nSql>=0 && nNew>=0 );
    zOut = sqlite3DbMallocZero(db, (u64)(nSql + pRename->nList*nQuot + 1));
  }else{
    assert( nSql>0 );
    zOut = (char*)sqlite3DbMallocZero(db, (u64)(nSql*2+1) * 3);
    if( zOut ){
      zBuf1 = &zOut[nSql*2+1];
      zBuf2 = &zOut[nSql*4+2];
    }
  }

  /* At this point pRename->pList contains a list of RenameToken objects
  ** corresponding to all tokens in the input SQL that must be replaced
  ** with the new column name, or with single-quoted versions of themselves.
  ** All that remains is to construct and return the edited SQL string. */
  if( zOut ){
    i64 nOut = nSql;
    assert( nSql>0 );
    memcpy(zOut, zSql, (size_t)nSql);
    while( pRename->pList ){
      int iOff;                   /* Offset of token to replace in zOut */
      i64 nReplace;
      const char *zReplace;
      RenameToken *pBest = renameColumnTokenNext(pRename);

      if( zNew ){
        if( bQuote==0 && sqlite3IsIdChar(*(u8*)pBest->t.z) ){
          nReplace = nNew;
          zReplace = zNew;
        }else{
          nReplace = nQuot;
          zReplace = zQuot;
          if( pBest->t.z[pBest->t.n]=='"' ) nReplace++;
        }
      }else{
        /* Dequote the double-quoted token. Then requote it again, this time
        ** using single quotes. If the character immediately following the
        ** original token within the input SQL was a single quote ('), then
        ** add another space after the new, single-quoted version of the
        ** token. This is so that (SELECT "string"'alias') maps to
        ** (SELECT 'string' 'alias'), and not (SELECT 'string''alias').  */
        memcpy(zBuf1, pBest->t.z, pBest->t.n);
        zBuf1[pBest->t.n] = 0;
        sqlite3Dequote(zBuf1);
        assert( nSql < 0x15555554 /* otherwise malloc would have failed */ );
        sqlite3_snprintf((int)(nSql*2), zBuf2, "%Q%s", zBuf1,
            pBest->t.z[pBest->t.n]=='\'' ? " " : ""
        );
        zReplace = zBuf2;
        nReplace = sqlite3Strlen30(zReplace);
      }

      iOff = (int)(pBest->t.z - zSql);
      if( pBest->t.n!=nReplace ){
        memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n],
            nOut - (iOff + pBest->t.n)
        );
        nOut += nReplace - pBest->t.n;
        zOut[nOut] = '\0';
      }
118234
118235
118236
118237
118238
118239
118240

118241
118242
118243
118244
118245
118246
118247
118248
118249
118250
118251
118252
  return rc;
}

/*
** Set all pEList->a[].fg.eEName fields in the expression-list to val.
*/
static void renameSetENames(ExprList *pEList, int val){

  if( pEList ){
    int i;
    for(i=0; i<pEList->nExpr; i++){
      assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME );
      pEList->a[i].fg.eEName = val;
    }
  }
}

/*
** Resolve all symbols in the trigger at pParse->pNewTrigger, assuming
** it was read from the schema of database zDb. Return SQLITE_OK if







>




|







118761
118762
118763
118764
118765
118766
118767
118768
118769
118770
118771
118772
118773
118774
118775
118776
118777
118778
118779
118780
  return rc;
}

/*
** Set all pEList->a[].fg.eEName fields in the expression-list to val.
*/
static void renameSetENames(ExprList *pEList, int val){
  assert( val==ENAME_NAME || val==ENAME_TAB || val==ENAME_SPAN );
  if( pEList ){
    int i;
    for(i=0; i<pEList->nExpr; i++){
      assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME );
      pEList->a[i].fg.eEName = val&0x3;
    }
  }
}

/*
** Resolve all symbols in the trigger at pParse->pNewTrigger, assuming
** it was read from the schema of database zDb. Return SQLITE_OK if
118495
118496
118497
118498
118499
118500
118501
118502
118503
118504
118505
118506
118507
118508
118509
  sWalker.u.pRename = &sCtx;

  sCtx.pTab = pTab;
  if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
  if( sParse.pNewTable ){
    if( IsView(sParse.pNewTable) ){
      Select *pSelect = sParse.pNewTable->u.view.pSelect;
      pSelect->selFlags &= ~SF_View;
      sParse.rc = SQLITE_OK;
      sqlite3SelectPrep(&sParse, pSelect, 0);
      rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
      if( rc==SQLITE_OK ){
        sqlite3WalkSelect(&sWalker, pSelect);
      }
      if( rc!=SQLITE_OK ) goto renameColumnFunc_done;







|







119023
119024
119025
119026
119027
119028
119029
119030
119031
119032
119033
119034
119035
119036
119037
  sWalker.u.pRename = &sCtx;

  sCtx.pTab = pTab;
  if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
  if( sParse.pNewTable ){
    if( IsView(sParse.pNewTable) ){
      Select *pSelect = sParse.pNewTable->u.view.pSelect;
      pSelect->selFlags &= ~(u32)SF_View;
      sParse.rc = SQLITE_OK;
      sqlite3SelectPrep(&sParse, pSelect, 0);
      rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
      if( rc==SQLITE_OK ){
        sqlite3WalkSelect(&sWalker, pSelect);
      }
      if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
118713
118714
118715
118716
118717
118718
118719
118720
118721
118722
118723
118724
118725
118726
118727
          if( isLegacy==0 ){
            Select *pSelect = pTab->u.view.pSelect;
            NameContext sNC;
            memset(&sNC, 0, sizeof(sNC));
            sNC.pParse = &sParse;

            assert( pSelect->selFlags & SF_View );
            pSelect->selFlags &= ~SF_View;
            sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC);
            if( sParse.nErr ){
              rc = sParse.rc;
            }else{
              sqlite3WalkSelect(&sWalker, pTab->u.view.pSelect);
            }
          }







|







119241
119242
119243
119244
119245
119246
119247
119248
119249
119250
119251
119252
119253
119254
119255
          if( isLegacy==0 ){
            Select *pSelect = pTab->u.view.pSelect;
            NameContext sNC;
            memset(&sNC, 0, sizeof(sNC));
            sNC.pParse = &sParse;

            assert( pSelect->selFlags & SF_View );
            pSelect->selFlags &= ~(u32)SF_View;
            sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC);
            if( sParse.nErr ){
              rc = sParse.rc;
            }else{
              sqlite3WalkSelect(&sWalker, pTab->u.view.pSelect);
            }
          }
118886
118887
118888
118889
118890
118891
118892
118893
118894
118895
118896
118897
118898
118899
118900
      sWalker.xExprCallback = renameQuotefixExprCb;
      sWalker.xSelectCallback = renameColumnSelectCb;
      sWalker.u.pRename = &sCtx;

      if( sParse.pNewTable ){
        if( IsView(sParse.pNewTable) ){
          Select *pSelect = sParse.pNewTable->u.view.pSelect;
          pSelect->selFlags &= ~SF_View;
          sParse.rc = SQLITE_OK;
          sqlite3SelectPrep(&sParse, pSelect, 0);
          rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
          if( rc==SQLITE_OK ){
            sqlite3WalkSelect(&sWalker, pSelect);
          }
        }else{







|







119414
119415
119416
119417
119418
119419
119420
119421
119422
119423
119424
119425
119426
119427
119428
      sWalker.xExprCallback = renameQuotefixExprCb;
      sWalker.xSelectCallback = renameColumnSelectCb;
      sWalker.u.pRename = &sCtx;

      if( sParse.pNewTable ){
        if( IsView(sParse.pNewTable) ){
          Select *pSelect = sParse.pNewTable->u.view.pSelect;
          pSelect->selFlags &= ~(u32)SF_View;
          sParse.rc = SQLITE_OK;
          sqlite3SelectPrep(&sParse, pSelect, 0);
          rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
          if( rc==SQLITE_OK ){
            sqlite3WalkSelect(&sWalker, pSelect);
          }
        }else{
118985
118986
118987
118988
118989
118990
118991
118992
118993
118994
118995
118996
118997
118998
118999
119000
119001
119002
#endif

  UNUSED_PARAMETER(NotUsed);

  if( zDb && zInput ){
    int rc;
    Parse sParse;
    int flags = db->flags;
    if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL);
    rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
    db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL));
    if( rc==SQLITE_OK ){
      if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){
        NameContext sNC;
        memset(&sNC, 0, sizeof(sNC));
        sNC.pParse = &sParse;
        sqlite3SelectPrep(&sParse, sParse.pNewTable->u.view.pSelect, &sNC);
        if( sParse.nErr ) rc = sParse.rc;







|


|







119513
119514
119515
119516
119517
119518
119519
119520
119521
119522
119523
119524
119525
119526
119527
119528
119529
119530
#endif

  UNUSED_PARAMETER(NotUsed);

  if( zDb && zInput ){
    int rc;
    Parse sParse;
    u64 flags = db->flags;
    if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL);
    rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
    db->flags = flags;
    if( rc==SQLITE_OK ){
      if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){
        NameContext sNC;
        memset(&sNC, 0, sizeof(sNC));
        sNC.pParse = &sParse;
        sqlite3SelectPrep(&sParse, sParse.pNewTable->u.view.pSelect, &sNC);
        if( sParse.nErr ) rc = sParse.rc;
119708
119709
119710
119711
119712
119713
119714
119715
119716
119717
119718
119719
119720
119721
119722
    sqlite3_result_error_nomem(context);
    return;
  }

  p->db = db;
  p->nEst = sqlite3_value_int64(argv[2]);
  p->nRow = 0;
  p->nLimit = sqlite3_value_int64(argv[3]);
  p->nCol = nCol;
  p->nKeyCol = nKeyCol;
  p->nSkipAhead = 0;
  p->current.anDLt = (tRowcnt*)&p[1];

#ifdef SQLITE_ENABLE_STAT4
  p->current.anEq = &p->current.anDLt[nColUp];







|







120236
120237
120238
120239
120240
120241
120242
120243
120244
120245
120246
120247
120248
120249
120250
    sqlite3_result_error_nomem(context);
    return;
  }

  p->db = db;
  p->nEst = sqlite3_value_int64(argv[2]);
  p->nRow = 0;
  p->nLimit = sqlite3_value_int(argv[3]);
  p->nCol = nCol;
  p->nKeyCol = nKeyCol;
  p->nSkipAhead = 0;
  p->current.anDLt = (tRowcnt*)&p[1];

#ifdef SQLITE_ENABLE_STAT4
  p->current.anEq = &p->current.anDLt[nColUp];
121517
121518
121519
121520
121521
121522
121523







121524
121525
121526
121527
121528
121529
121530
  ** remove the entry from the db->aDb[] array. i.e. put everything back the
  ** way we found it.
  */
  if( rc==SQLITE_OK ){
    sqlite3BtreeEnterAll(db);
    db->init.iDb = 0;
    db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);







    if( !REOPEN_AS_MEMDB(db) ){
      rc = sqlite3Init(db, &zErrDyn);
    }
    sqlite3BtreeLeaveAll(db);
    assert( zErrDyn==0 || rc!=SQLITE_OK );
  }
  if( rc ){







>
>
>
>
>
>
>







122045
122046
122047
122048
122049
122050
122051
122052
122053
122054
122055
122056
122057
122058
122059
122060
122061
122062
122063
122064
122065
  ** remove the entry from the db->aDb[] array. i.e. put everything back the
  ** way we found it.
  */
  if( rc==SQLITE_OK ){
    sqlite3BtreeEnterAll(db);
    db->init.iDb = 0;
    db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    if( db->setlkFlags & SQLITE_SETLK_BLOCK_ON_CONNECT ){
      int val = 1;
      sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pNew->pBt));
      sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, &val);
    }
#endif
    if( !REOPEN_AS_MEMDB(db) ){
      rc = sqlite3Init(db, &zErrDyn);
    }
    sqlite3BtreeLeaveAll(db);
    assert( zErrDyn==0 || rc!=SQLITE_OK );
  }
  if( rc ){
123238
123239
123240
123241
123242
123243
123244
123245
123246




123247
123248


123249
123250
123251
123252
123253
123254
123255

/*
** Convert an table column number into a index column number.  That is,
** for the column iCol in the table (as defined by the CREATE TABLE statement)
** find the (first) offset of that column in index pIdx.  Or return -1
** if column iCol is not used in index pIdx.
*/
SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index *pIdx, i16 iCol){
  int i;




  for(i=0; i<pIdx->nColumn; i++){
    if( iCol==pIdx->aiColumn[i] ) return i;


  }
  return -1;
}

#ifndef SQLITE_OMIT_GENERATED_COLUMNS
/* Convert a storage column number into a table column number.
**







|

>
>
>
>

|
>
>







123773
123774
123775
123776
123777
123778
123779
123780
123781
123782
123783
123784
123785
123786
123787
123788
123789
123790
123791
123792
123793
123794
123795
123796

/*
** Convert an table column number into a index column number.  That is,
** for the column iCol in the table (as defined by the CREATE TABLE statement)
** find the (first) offset of that column in index pIdx.  Or return -1
** if column iCol is not used in index pIdx.
*/
SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){
  int i;
  i16 iCol16;
  assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN );
  assert( pIdx->nColumn<=SQLITE_MAX_COLUMN );
  iCol16 = iCol;
  for(i=0; i<pIdx->nColumn; i++){
    if( iCol16==pIdx->aiColumn[i] ){
      return i;
    }
  }
  return -1;
}

#ifndef SQLITE_OMIT_GENERATED_COLUMNS
/* Convert a storage column number into a table column number.
**
124338
124339
124340
124341
124342
124343
124344
124345
124346
124347

124348




124349
124350
124351
124352
124353
124354
124355
124356
124357
124358
124359
124360
124361
124362
124363
124364
124365
124366
124367
124368
124369
124370
124371
  return zStmt;
}

/*
** Resize an Index object to hold N columns total.  Return SQLITE_OK
** on success and SQLITE_NOMEM on an OOM error.
*/
static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){
  char *zExtra;
  int nByte;

  if( pIdx->nColumn>=N ) return SQLITE_OK;




  assert( pIdx->isResized==0 );
  nByte = (sizeof(char*) + sizeof(LogEst) + sizeof(i16) + 1)*N;
  zExtra = sqlite3DbMallocZero(db, nByte);
  if( zExtra==0 ) return SQLITE_NOMEM_BKPT;
  memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn);
  pIdx->azColl = (const char**)zExtra;
  zExtra += sizeof(char*)*N;
  memcpy(zExtra, pIdx->aiRowLogEst, sizeof(LogEst)*(pIdx->nKeyCol+1));
  pIdx->aiRowLogEst = (LogEst*)zExtra;
  zExtra += sizeof(LogEst)*N;
  memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn);
  pIdx->aiColumn = (i16*)zExtra;
  zExtra += sizeof(i16)*N;
  memcpy(zExtra, pIdx->aSortOrder, pIdx->nColumn);
  pIdx->aSortOrder = (u8*)zExtra;
  pIdx->nColumn = N;
  pIdx->isResized = 1;
  return SQLITE_OK;
}

/*
** Estimate the total row width for a table.
*/







|

|
>

>
>
>
>

|













|







124879
124880
124881
124882
124883
124884
124885
124886
124887
124888
124889
124890
124891
124892
124893
124894
124895
124896
124897
124898
124899
124900
124901
124902
124903
124904
124905
124906
124907
124908
124909
124910
124911
124912
124913
124914
124915
124916
124917
  return zStmt;
}

/*
** Resize an Index object to hold N columns total.  Return SQLITE_OK
** on success and SQLITE_NOMEM on an OOM error.
*/
static int resizeIndexObject(Parse *pParse, Index *pIdx, int N){
  char *zExtra;
  u64 nByte;
  sqlite3 *db;
  if( pIdx->nColumn>=N ) return SQLITE_OK;
  db = pParse->db;
  assert( N>0 );
  assert( N <= SQLITE_MAX_COLUMN*2 /* tag-20250221-1 */ );
  testcase( N==2*pParse->db->aLimit[SQLITE_LIMIT_COLUMN] );
  assert( pIdx->isResized==0 );
  nByte = (sizeof(char*) + sizeof(LogEst) + sizeof(i16) + 1)*(u64)N;
  zExtra = sqlite3DbMallocZero(db, nByte);
  if( zExtra==0 ) return SQLITE_NOMEM_BKPT;
  memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn);
  pIdx->azColl = (const char**)zExtra;
  zExtra += sizeof(char*)*N;
  memcpy(zExtra, pIdx->aiRowLogEst, sizeof(LogEst)*(pIdx->nKeyCol+1));
  pIdx->aiRowLogEst = (LogEst*)zExtra;
  zExtra += sizeof(LogEst)*N;
  memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn);
  pIdx->aiColumn = (i16*)zExtra;
  zExtra += sizeof(i16)*N;
  memcpy(zExtra, pIdx->aSortOrder, pIdx->nColumn);
  pIdx->aSortOrder = (u8*)zExtra;
  pIdx->nColumn = (u16)N;  /* See tag-20250221-1 above for proof of safety */
  pIdx->isResized = 1;
  return SQLITE_OK;
}

/*
** Estimate the total row width for a table.
*/
124611
124612
124613
124614
124615
124616
124617
124618
124619
124620
124621
124622
124623
124624
124625
      }
    }
    if( n==0 ){
      /* This index is a superset of the primary key */
      pIdx->nColumn = pIdx->nKeyCol;
      continue;
    }
    if( resizeIndexObject(db, pIdx, pIdx->nKeyCol+n) ) return;
    for(i=0, j=pIdx->nKeyCol; i<nPk; i++){
      if( !isDupColumn(pIdx, pIdx->nKeyCol, pPk, i) ){
        testcase( hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) );
        pIdx->aiColumn[j] = pPk->aiColumn[i];
        pIdx->azColl[j] = pPk->azColl[i];
        if( pPk->aSortOrder[i] ){
          /* See ticket https://www.sqlite.org/src/info/bba7b69f9849b5bf */







|







125157
125158
125159
125160
125161
125162
125163
125164
125165
125166
125167
125168
125169
125170
125171
      }
    }
    if( n==0 ){
      /* This index is a superset of the primary key */
      pIdx->nColumn = pIdx->nKeyCol;
      continue;
    }
    if( resizeIndexObject(pParse, pIdx, pIdx->nKeyCol+n) ) return;
    for(i=0, j=pIdx->nKeyCol; i<nPk; i++){
      if( !isDupColumn(pIdx, pIdx->nKeyCol, pPk, i) ){
        testcase( hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) );
        pIdx->aiColumn[j] = pPk->aiColumn[i];
        pIdx->azColl[j] = pPk->azColl[i];
        if( pPk->aSortOrder[i] ){
          /* See ticket https://www.sqlite.org/src/info/bba7b69f9849b5bf */
124635
124636
124637
124638
124639
124640
124641
124642
124643
124644
124645
124646
124647
124648
124649
  /* Add all table columns to the PRIMARY KEY index
  */
  nExtra = 0;
  for(i=0; i<pTab->nCol; i++){
    if( !hasColumn(pPk->aiColumn, nPk, i)
     && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ) nExtra++;
  }
  if( resizeIndexObject(db, pPk, nPk+nExtra) ) return;
  for(i=0, j=nPk; i<pTab->nCol; i++){
    if( !hasColumn(pPk->aiColumn, j, i)
     && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0
    ){
      assert( j<pPk->nColumn );
      pPk->aiColumn[j] = i;
      pPk->azColl[j] = sqlite3StrBINARY;







|







125181
125182
125183
125184
125185
125186
125187
125188
125189
125190
125191
125192
125193
125194
125195
  /* Add all table columns to the PRIMARY KEY index
  */
  nExtra = 0;
  for(i=0; i<pTab->nCol; i++){
    if( !hasColumn(pPk->aiColumn, nPk, i)
     && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ) nExtra++;
  }
  if( resizeIndexObject(pParse, pPk, nPk+nExtra) ) return;
  for(i=0, j=nPk; i<pTab->nCol; i++){
    if( !hasColumn(pPk->aiColumn, j, i)
     && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0
    ){
      assert( j<pPk->nColumn );
      pPk->aiColumn[j] = i;
      pPk->azColl[j] = sqlite3StrBINARY;
125793
125794
125795
125796
125797
125798
125799
125800
125801
125802
125803
125804
125805
125806
125807
    sqlite3ErrorMsg(pParse,
        "number of columns in foreign key does not match the number of "
        "columns in the referenced table");
    goto fk_end;
  }else{
    nCol = pFromCol->nExpr;
  }
  nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1;
  if( pToCol ){
    for(i=0; i<pToCol->nExpr; i++){
      nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1;
    }
  }
  pFKey = sqlite3DbMallocZero(db, nByte );
  if( pFKey==0 ){







|







126339
126340
126341
126342
126343
126344
126345
126346
126347
126348
126349
126350
126351
126352
126353
    sqlite3ErrorMsg(pParse,
        "number of columns in foreign key does not match the number of "
        "columns in the referenced table");
    goto fk_end;
  }else{
    nCol = pFromCol->nExpr;
  }
  nByte = SZ_FKEY(nCol) + pTo->n + 1;
  if( pToCol ){
    for(i=0; i<pToCol->nExpr; i++){
      nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1;
    }
  }
  pFKey = sqlite3DbMallocZero(db, nByte );
  if( pFKey==0 ){
126019
126020
126021
126022
126023
126024
126025
126026
126027
126028
126029
126030
126031
126032

126033
126034
126035
126036
126037
126038
126039
126040
126041
126042
126043
126044

126045
126046
126047
126048
126049
126050
126051
126052
126053
**
** Increase the allocation size to provide an extra nExtra bytes
** of 8-byte aligned space after the Index object and return a
** pointer to this extra space in *ppExtra.
*/
SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(
  sqlite3 *db,         /* Database connection */
  i16 nCol,            /* Total number of columns in the index */
  int nExtra,          /* Number of bytes of extra space to alloc */
  char **ppExtra       /* Pointer to the "extra" space */
){
  Index *p;            /* Allocated index object */
  i64 nByte;           /* Bytes of space for Index object + arrays */


  nByte = ROUND8(sizeof(Index)) +              /* Index structure  */
          ROUND8(sizeof(char*)*nCol) +         /* Index.azColl     */
          ROUND8(sizeof(LogEst)*(nCol+1) +     /* Index.aiRowLogEst   */
                 sizeof(i16)*nCol +            /* Index.aiColumn   */
                 sizeof(u8)*nCol);             /* Index.aSortOrder */
  p = sqlite3DbMallocZero(db, nByte + nExtra);
  if( p ){
    char *pExtra = ((char*)p)+ROUND8(sizeof(Index));
    p->azColl = (const char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol);
    p->aiRowLogEst = (LogEst*)pExtra; pExtra += sizeof(LogEst)*(nCol+1);
    p->aiColumn = (i16*)pExtra;       pExtra += sizeof(i16)*nCol;
    p->aSortOrder = (u8*)pExtra;

    p->nColumn = nCol;
    p->nKeyCol = nCol - 1;
    *ppExtra = ((char*)p) + nByte;
  }
  return p;
}

/*
** If expression list pList contains an expression that was parsed with







|






>












>
|
|







126565
126566
126567
126568
126569
126570
126571
126572
126573
126574
126575
126576
126577
126578
126579
126580
126581
126582
126583
126584
126585
126586
126587
126588
126589
126590
126591
126592
126593
126594
126595
126596
126597
126598
126599
126600
126601
**
** Increase the allocation size to provide an extra nExtra bytes
** of 8-byte aligned space after the Index object and return a
** pointer to this extra space in *ppExtra.
*/
SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(
  sqlite3 *db,         /* Database connection */
  int nCol,            /* Total number of columns in the index */
  int nExtra,          /* Number of bytes of extra space to alloc */
  char **ppExtra       /* Pointer to the "extra" space */
){
  Index *p;            /* Allocated index object */
  i64 nByte;           /* Bytes of space for Index object + arrays */

  assert( nCol <= 2*db->aLimit[SQLITE_LIMIT_COLUMN] );
  nByte = ROUND8(sizeof(Index)) +              /* Index structure  */
          ROUND8(sizeof(char*)*nCol) +         /* Index.azColl     */
          ROUND8(sizeof(LogEst)*(nCol+1) +     /* Index.aiRowLogEst   */
                 sizeof(i16)*nCol +            /* Index.aiColumn   */
                 sizeof(u8)*nCol);             /* Index.aSortOrder */
  p = sqlite3DbMallocZero(db, nByte + nExtra);
  if( p ){
    char *pExtra = ((char*)p)+ROUND8(sizeof(Index));
    p->azColl = (const char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol);
    p->aiRowLogEst = (LogEst*)pExtra; pExtra += sizeof(LogEst)*(nCol+1);
    p->aiColumn = (i16*)pExtra;       pExtra += sizeof(i16)*nCol;
    p->aSortOrder = (u8*)pExtra;
    assert( nCol>0 );
    p->nColumn = (u16)nCol;
    p->nKeyCol = (u16)(nCol - 1);
    *ppExtra = ((char*)p) + nByte;
  }
  return p;
}

/*
** If expression list pList contains an expression that was parsed with
126850
126851
126852
126853
126854
126855
126856
126857
126858
126859
126860
126861
126862
126863
126864
126865
126866
126867
126868
126869
**
** A new IdList is returned, or NULL if malloc() fails.
*/
SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){
  sqlite3 *db = pParse->db;
  int i;
  if( pList==0 ){
    pList = sqlite3DbMallocZero(db, sizeof(IdList) );
    if( pList==0 ) return 0;
  }else{
    IdList *pNew;
    pNew = sqlite3DbRealloc(db, pList,
                 sizeof(IdList) + pList->nId*sizeof(pList->a));
    if( pNew==0 ){
      sqlite3IdListDelete(db, pList);
      return 0;
    }
    pList = pNew;
  }
  i = pList->nId++;







|



|
<







127398
127399
127400
127401
127402
127403
127404
127405
127406
127407
127408
127409

127410
127411
127412
127413
127414
127415
127416
**
** A new IdList is returned, or NULL if malloc() fails.
*/
SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){
  sqlite3 *db = pParse->db;
  int i;
  if( pList==0 ){
    pList = sqlite3DbMallocZero(db, SZ_IDLIST(1));
    if( pList==0 ) return 0;
  }else{
    IdList *pNew;
    pNew = sqlite3DbRealloc(db, pList, SZ_IDLIST(pList->nId+1));

    if( pNew==0 ){
      sqlite3IdListDelete(db, pList);
      return 0;
    }
    pList = pNew;
  }
  i = pList->nId++;
126954
126955
126956
126957
126958
126959
126960
126961
126962
126963
126964
126965
126966
126967
126968
126969

    if( pSrc->nSrc+nExtra>=SQLITE_MAX_SRCLIST ){
      sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d",
                      SQLITE_MAX_SRCLIST);
      return 0;
    }
    if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST;
    pNew = sqlite3DbRealloc(db, pSrc,
               sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) );
    if( pNew==0 ){
      assert( db->mallocFailed );
      return 0;
    }
    pSrc = pNew;
    pSrc->nAlloc = nAlloc;
  }







|
<







127501
127502
127503
127504
127505
127506
127507
127508

127509
127510
127511
127512
127513
127514
127515

    if( pSrc->nSrc+nExtra>=SQLITE_MAX_SRCLIST ){
      sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d",
                      SQLITE_MAX_SRCLIST);
      return 0;
    }
    if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST;
    pNew = sqlite3DbRealloc(db, pSrc, SZ_SRCLIST(nAlloc));

    if( pNew==0 ){
      assert( db->mallocFailed );
      return 0;
    }
    pSrc = pNew;
    pSrc->nAlloc = nAlloc;
  }
127030
127031
127032
127033
127034
127035
127036
127037
127038
127039
127040
127041
127042
127043
127044
  SrcItem *pItem;
  sqlite3 *db;
  assert( pDatabase==0 || pTable!=0 );  /* Cannot have C without B */
  assert( pParse!=0 );
  assert( pParse->db!=0 );
  db = pParse->db;
  if( pList==0 ){
    pList = sqlite3DbMallocRawNN(pParse->db, sizeof(SrcList) );
    if( pList==0 ) return 0;
    pList->nAlloc = 1;
    pList->nSrc = 1;
    memset(&pList->a[0], 0, sizeof(pList->a[0]));
    pList->a[0].iCursor = -1;
  }else{
    SrcList *pNew = sqlite3SrcListEnlarge(pParse, pList, 1, pList->nSrc);







|







127576
127577
127578
127579
127580
127581
127582
127583
127584
127585
127586
127587
127588
127589
127590
  SrcItem *pItem;
  sqlite3 *db;
  assert( pDatabase==0 || pTable!=0 );  /* Cannot have C without B */
  assert( pParse!=0 );
  assert( pParse->db!=0 );
  db = pParse->db;
  if( pList==0 ){
    pList = sqlite3DbMallocRawNN(pParse->db, SZ_SRCLIST(1));
    if( pList==0 ) return 0;
    pList->nAlloc = 1;
    pList->nSrc = 1;
    memset(&pList->a[0], 0, sizeof(pList->a[0]));
    pList->a[0].iCursor = -1;
  }else{
    SrcList *pNew = sqlite3SrcListEnlarge(pParse, pList, 1, pList->nSrc);
127916
127917
127918
127919
127920
127921
127922
127923
127924
127925
127926
127927
127928
127929
127930
127931
127932
127933
      if( sqlite3StrICmp(zName, pWith->a[i].zName)==0 ){
        sqlite3ErrorMsg(pParse, "duplicate WITH table name: %s", zName);
      }
    }
  }

  if( pWith ){
    sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte);
    pNew = sqlite3DbRealloc(db, pWith, nByte);
  }else{
    pNew = sqlite3DbMallocZero(db, sizeof(*pWith));
  }
  assert( (pNew!=0 && zName!=0) || db->mallocFailed );

  if( db->mallocFailed ){
    sqlite3CteDelete(db, pCte);
    pNew = pWith;
  }else{







<
|

|







128462
128463
128464
128465
128466
128467
128468

128469
128470
128471
128472
128473
128474
128475
128476
128477
128478
      if( sqlite3StrICmp(zName, pWith->a[i].zName)==0 ){
        sqlite3ErrorMsg(pParse, "duplicate WITH table name: %s", zName);
      }
    }
  }

  if( pWith ){

    pNew = sqlite3DbRealloc(db, pWith, SZ_WITH(pWith->nCte+1));
  }else{
    pNew = sqlite3DbMallocZero(db, SZ_WITH(1));
  }
  assert( (pNew!=0 && zName!=0) || db->mallocFailed );

  if( db->mallocFailed ){
    sqlite3CteDelete(db, pCte);
    pNew = pWith;
  }else{
130627
130628
130629
130630
130631
130632
130633
130634
130635
130636
130637
130638
130639
130640
130641
  '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};

/*
** Append to pStr text that is the SQL literal representation of the
** value contained in pValue.
*/
SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){
  /* As currently implemented, the string must be initially empty.
  ** we might relax this requirement in the future, but that will
  ** require enhancements to the implementation. */
  assert( pStr!=0 && pStr->nChar==0 );

  switch( sqlite3_value_type(pValue) ){
    case SQLITE_FLOAT: {







|







131172
131173
131174
131175
131176
131177
131178
131179
131180
131181
131182
131183
131184
131185
131186
  '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};

/*
** Append to pStr text that is the SQL literal representation of the
** value contained in pValue.
*/
SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue, int bEscape){
  /* As currently implemented, the string must be initially empty.
  ** we might relax this requirement in the future, but that will
  ** require enhancements to the implementation. */
  assert( pStr!=0 && pStr->nChar==0 );

  switch( sqlite3_value_type(pValue) ){
    case SQLITE_FLOAT: {
130675
130676
130677
130678
130679
130680
130681
130682
130683
130684
130685
130686
130687
130688
130689
130690
130691



































































































130692
130693
130694
130695
130696
130697
130698
130699
130700
130701




130702
130703
130704
130705
130706
130707
130708
130709
130710
130711
130712
130713
130714
130715
130716
        zText[1] = '\'';
        pStr->nChar = nBlob*2 + 3;
      }
      break;
    }
    case SQLITE_TEXT: {
      const unsigned char *zArg = sqlite3_value_text(pValue);
      sqlite3_str_appendf(pStr, "%Q", zArg);
      break;
    }
    default: {
      assert( sqlite3_value_type(pValue)==SQLITE_NULL );
      sqlite3_str_append(pStr, "NULL", 4);
      break;
    }
  }
}




































































































/*
** Implementation of the QUOTE() function.
**
** The quote(X) function returns the text of an SQL literal which is the
** value of its argument suitable for inclusion into an SQL statement.
** Strings are surrounded by single-quotes with escapes on interior quotes
** as needed. BLOBs are encoded as hexadecimal literals. Strings with
** embedded NUL characters cannot be represented as string literals in SQL
** and hence the returned string literal is truncated prior to the first NUL.




*/
static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
  sqlite3_str str;
  sqlite3 *db = sqlite3_context_db_handle(context);
  assert( argc==1 );
  UNUSED_PARAMETER(argc);
  sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
  sqlite3QuoteValue(&str,argv[0]);
  sqlite3_result_text(context, sqlite3StrAccumFinish(&str), str.nChar,
                      SQLITE_DYNAMIC);
  if( str.accError!=SQLITE_OK ){
    sqlite3_result_null(context);
    sqlite3_result_error_code(context, str.accError);
  }
}







|









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










>
>
>
>







|







131220
131221
131222
131223
131224
131225
131226
131227
131228
131229
131230
131231
131232
131233
131234
131235
131236
131237
131238
131239
131240
131241
131242
131243
131244
131245
131246
131247
131248
131249
131250
131251
131252
131253
131254
131255
131256
131257
131258
131259
131260
131261
131262
131263
131264
131265
131266
131267
131268
131269
131270
131271
131272
131273
131274
131275
131276
131277
131278
131279
131280
131281
131282
131283
131284
131285
131286
131287
131288
131289
131290
131291
131292
131293
131294
131295
131296
131297
131298
131299
131300
131301
131302
131303
131304
131305
131306
131307
131308
131309
131310
131311
131312
131313
131314
131315
131316
131317
131318
131319
131320
131321
131322
131323
131324
131325
131326
131327
131328
131329
131330
131331
131332
131333
131334
131335
131336
131337
131338
131339
131340
131341
131342
131343
131344
131345
131346
131347
131348
131349
131350
131351
131352
131353
131354
131355
131356
131357
131358
131359
131360
131361
131362
131363
131364
        zText[1] = '\'';
        pStr->nChar = nBlob*2 + 3;
      }
      break;
    }
    case SQLITE_TEXT: {
      const unsigned char *zArg = sqlite3_value_text(pValue);
      sqlite3_str_appendf(pStr, bEscape ? "%#Q" : "%Q", zArg);
      break;
    }
    default: {
      assert( sqlite3_value_type(pValue)==SQLITE_NULL );
      sqlite3_str_append(pStr, "NULL", 4);
      break;
    }
  }
}

/*
** Return true if z[] begins with N hexadecimal digits, and write
** a decoding of those digits into *pVal.  Or return false if any
** one of the first N characters in z[] is not a hexadecimal digit.
*/
static int isNHex(const char *z, int N, u32 *pVal){
  int i;
  int v = 0;
  for(i=0; i<N; i++){
    if( !sqlite3Isxdigit(z[i]) ) return 0;
    v = (v<<4) + sqlite3HexToInt(z[i]);
  }
  *pVal = v;
  return 1;
}

/*
** Implementation of the UNISTR() function.
**
** This is intended to be a work-alike of the UNISTR() function in
** PostgreSQL.  Quoting from the PG documentation (PostgreSQL 17 -
** scraped on 2025-02-22):
**
**    Evaluate escaped Unicode characters in the argument. Unicode
**    characters can be specified as \XXXX (4 hexadecimal digits),
**    \+XXXXXX (6 hexadecimal digits), \uXXXX (4 hexadecimal digits),
**    or \UXXXXXXXX (8 hexadecimal digits). To specify a backslash,
**    write two backslashes. All other characters are taken literally.
*/
static void unistrFunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  char *zOut;
  const char *zIn;
  int nIn;
  int i, j, n;
  u32 v;

  assert( argc==1 );
  UNUSED_PARAMETER( argc );
  zIn = (const char*)sqlite3_value_text(argv[0]);
  if( zIn==0 ) return;
  nIn = sqlite3_value_bytes(argv[0]);
  zOut = sqlite3_malloc64(nIn+1);
  if( zOut==0 ){
    sqlite3_result_error_nomem(context);
    return;
  }
  i = j = 0;
  while( i<nIn ){
    char *z = strchr(&zIn[i],'\\');
    if( z==0 ){
      n = nIn - i;
      memmove(&zOut[j], &zIn[i], n);
      j += n;
      break;
    }
    n = z - &zIn[i];
    if( n>0 ){
      memmove(&zOut[j], &zIn[i], n);
      j += n;
      i += n;
    }
    if( zIn[i+1]=='\\' ){
      i += 2;
      zOut[j++] = '\\';
    }else if( sqlite3Isxdigit(zIn[i+1]) ){
      if( !isNHex(&zIn[i+1], 4, &v) ) goto unistr_error;
      i += 5;
      j += sqlite3AppendOneUtf8Character(&zOut[j], v);
    }else if( zIn[i+1]=='+' ){
      if( !isNHex(&zIn[i+2], 6, &v) ) goto unistr_error;
      i += 8;
      j += sqlite3AppendOneUtf8Character(&zOut[j], v);
    }else if( zIn[i+1]=='u' ){
      if( !isNHex(&zIn[i+2], 4, &v) ) goto unistr_error;
      i += 6;
      j += sqlite3AppendOneUtf8Character(&zOut[j], v);
    }else if( zIn[i+1]=='U' ){
      if( !isNHex(&zIn[i+2], 8, &v) ) goto unistr_error;
      i += 10;
      j += sqlite3AppendOneUtf8Character(&zOut[j], v);
    }else{
      goto unistr_error;
    }
  }
  zOut[j] = 0;
  sqlite3_result_text64(context, zOut, j, sqlite3_free, SQLITE_UTF8);
  return;

unistr_error:
  sqlite3_free(zOut);
  sqlite3_result_error(context, "invalid Unicode escape", -1);
  return;
}


/*
** Implementation of the QUOTE() function.
**
** The quote(X) function returns the text of an SQL literal which is the
** value of its argument suitable for inclusion into an SQL statement.
** Strings are surrounded by single-quotes with escapes on interior quotes
** as needed. BLOBs are encoded as hexadecimal literals. Strings with
** embedded NUL characters cannot be represented as string literals in SQL
** and hence the returned string literal is truncated prior to the first NUL.
**
** If sqlite3_user_data() is non-zero, then the UNISTR_QUOTE() function is
** implemented instead.  The difference is that UNISTR_QUOTE() uses the
** UNISTR() function to escape control characters.
*/
static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
  sqlite3_str str;
  sqlite3 *db = sqlite3_context_db_handle(context);
  assert( argc==1 );
  UNUSED_PARAMETER(argc);
  sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
  sqlite3QuoteValue(&str,argv[0],SQLITE_PTR_TO_INT(sqlite3_user_data(context)));
  sqlite3_result_text(context, sqlite3StrAccumFinish(&str), str.nChar,
                      SQLITE_DYNAMIC);
  if( str.accError!=SQLITE_OK ){
    sqlite3_result_null(context);
    sqlite3_result_error_code(context, str.accError);
  }
}
131353
131354
131355
131356
131357
131358
131359
131360
131361
131362
131363
131364
131365
131366
131367
/*
** Routines used to compute the sum, average, and total.
**
** The SUM() function follows the (broken) SQL standard which means
** that it returns NULL if it sums over no inputs.  TOTAL returns
** 0.0 in that case.  In addition, TOTAL always returns a float where
** SUM might return an integer if it never encounters a floating point
** value.  TOTAL never fails, but SUM might through an exception if
** it overflows an integer.
*/
static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
  SumCtx *p;
  int type;
  assert( argc==1 );
  UNUSED_PARAMETER(argc);







|







132001
132002
132003
132004
132005
132006
132007
132008
132009
132010
132011
132012
132013
132014
132015
/*
** Routines used to compute the sum, average, and total.
**
** The SUM() function follows the (broken) SQL standard which means
** that it returns NULL if it sums over no inputs.  TOTAL returns
** 0.0 in that case.  In addition, TOTAL always returns a float where
** SUM might return an integer if it never encounters a floating point
** value.  TOTAL never fails, but SUM might throw an exception if
** it overflows an integer.
*/
static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
  SumCtx *p;
  int type;
  assert( argc==1 );
  UNUSED_PARAMETER(argc);
132273
132274
132275
132276
132277
132278
132279

132280

132281
132282
132283
132284
132285
132286
132287
    INLINE_FUNC(ifnull,          2, INLINEFUNC_coalesce, 0 ),
    VFUNCTION(random,            0, 0, 0, randomFunc       ),
    VFUNCTION(randomblob,        1, 0, 0, randomBlob       ),
    FUNCTION(nullif,             2, 0, 1, nullifFunc       ),
    DFUNCTION(sqlite_version,    0, 0, 0, versionFunc      ),
    DFUNCTION(sqlite_source_id,  0, 0, 0, sourceidFunc     ),
    FUNCTION(sqlite_log,         2, 0, 0, errlogFunc       ),

    FUNCTION(quote,              1, 0, 0, quoteFunc        ),

    VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
    VFUNCTION(changes,           0, 0, 0, changes          ),
    VFUNCTION(total_changes,     0, 0, 0, total_changes    ),
    FUNCTION(replace,            3, 0, 0, replaceFunc      ),
    FUNCTION(zeroblob,           1, 0, 0, zeroblobFunc     ),
    FUNCTION(substr,             2, 0, 0, substrFunc       ),
    FUNCTION(substr,             3, 0, 0, substrFunc       ),







>

>







132921
132922
132923
132924
132925
132926
132927
132928
132929
132930
132931
132932
132933
132934
132935
132936
132937
    INLINE_FUNC(ifnull,          2, INLINEFUNC_coalesce, 0 ),
    VFUNCTION(random,            0, 0, 0, randomFunc       ),
    VFUNCTION(randomblob,        1, 0, 0, randomBlob       ),
    FUNCTION(nullif,             2, 0, 1, nullifFunc       ),
    DFUNCTION(sqlite_version,    0, 0, 0, versionFunc      ),
    DFUNCTION(sqlite_source_id,  0, 0, 0, sourceidFunc     ),
    FUNCTION(sqlite_log,         2, 0, 0, errlogFunc       ),
    FUNCTION(unistr,             1, 0, 0, unistrFunc       ),
    FUNCTION(quote,              1, 0, 0, quoteFunc        ),
    FUNCTION(unistr_quote,       1, 1, 0, quoteFunc        ),
    VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
    VFUNCTION(changes,           0, 0, 0, changes          ),
    VFUNCTION(total_changes,     0, 0, 0, total_changes    ),
    FUNCTION(replace,            3, 0, 0, replaceFunc      ),
    FUNCTION(zeroblob,           1, 0, 0, zeroblobFunc     ),
    FUNCTION(substr,             2, 0, 0, substrFunc       ),
    FUNCTION(substr,             3, 0, 0, substrFunc       ),
134560
134561
134562
134563
134564
134565
134566
134567
134568
134569
134570
134571
134572
134573
134574
      sqlite3MultiValuesEnd(pParse, pLeft);
      f = SF_Values;
    }else if( pLeft->pPrior ){
      /* In this case set the SF_MultiValue flag only if it was set on pLeft */
      f = (f & pLeft->selFlags);
    }
    pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, f, 0);
    pLeft->selFlags &= ~SF_MultiValue;
    if( pSelect ){
      pSelect->op = TK_ALL;
      pSelect->pPrior = pLeft;
      pLeft = pSelect;
    }
  }else{
    SrcItem *p = 0;               /* SrcItem that reads from co-routine */







|







135210
135211
135212
135213
135214
135215
135216
135217
135218
135219
135220
135221
135222
135223
135224
      sqlite3MultiValuesEnd(pParse, pLeft);
      f = SF_Values;
    }else if( pLeft->pPrior ){
      /* In this case set the SF_MultiValue flag only if it was set on pLeft */
      f = (f & pLeft->selFlags);
    }
    pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, f, 0);
    pLeft->selFlags &= ~(u32)SF_MultiValue;
    if( pSelect ){
      pSelect->op = TK_ALL;
      pSelect->pPrior = pLeft;
      pLeft = pSelect;
    }
  }else{
    SrcItem *p = 0;               /* SrcItem that reads from co-routine */
139166
139167
139168
139169
139170
139171
139172
139173
139174
139175
139176
139177
139178
139179
139180
139181
139182
139183
139184
139185
139186
139187
139188
139189
139190
139191
139192
139193
139194
139195
139196
139197
139198
139199
139200
139201
139202
139203
139204
139205
139206
139207
139208
139209
139210
139211
139212
139213
139214

139215
139216
139217
139218
139219
139220
139221
  /*   9 */ "name",
  /*  10 */ "type",
  /*  11 */ "notnull",
  /*  12 */ "dflt_value",
  /*  13 */ "pk",
  /*  14 */ "hidden",
                           /* table_info reuses 8 */
  /*  15 */ "schema",      /* Used by: table_list */
  /*  16 */ "name",
  /*  17 */ "type",
  /*  18 */ "ncol",
  /*  19 */ "wr",
  /*  20 */ "strict",
  /*  21 */ "seqno",       /* Used by: index_xinfo */
  /*  22 */ "cid",
  /*  23 */ "name",
  /*  24 */ "desc",
  /*  25 */ "coll",
  /*  26 */ "key",
  /*  27 */ "name",        /* Used by: function_list */
  /*  28 */ "builtin",
  /*  29 */ "type",
  /*  30 */ "enc",
  /*  31 */ "narg",
  /*  32 */ "flags",
  /*  33 */ "tbl",         /* Used by: stats */
  /*  34 */ "idx",
  /*  35 */ "wdth",
  /*  36 */ "hght",
  /*  37 */ "flgs",
  /*  38 */ "seq",         /* Used by: index_list */
  /*  39 */ "name",
  /*  40 */ "unique",
  /*  41 */ "origin",
  /*  42 */ "partial",
  /*  43 */ "table",       /* Used by: foreign_key_check */
  /*  44 */ "rowid",
  /*  45 */ "parent",
  /*  46 */ "fkid",
                           /* index_info reuses 21 */
  /*  47 */ "seq",         /* Used by: database_list */
  /*  48 */ "name",
  /*  49 */ "file",
  /*  50 */ "busy",        /* Used by: wal_checkpoint */
  /*  51 */ "log",
  /*  52 */ "checkpointed",
                           /* collation_list reuses 38 */
  /*  53 */ "database",    /* Used by: lock_status */
  /*  54 */ "status",

  /*  55 */ "cache_size",  /* Used by: default_cache_size */
                           /* module_list pragma_list reuses 9 */
  /*  56 */ "timeout",     /* Used by: busy_timeout */
};

/* Definitions of all built-in pragmas */
typedef struct PragmaName {







|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|




<
|
|
|
|
|
|
|


>







139816
139817
139818
139819
139820
139821
139822
139823
139824
139825
139826
139827
139828
139829
139830
139831
139832
139833
139834
139835
139836
139837
139838
139839
139840
139841
139842
139843
139844
139845
139846
139847
139848
139849
139850
139851
139852
139853
139854

139855
139856
139857
139858
139859
139860
139861
139862
139863
139864
139865
139866
139867
139868
139869
139870
139871
  /*   9 */ "name",
  /*  10 */ "type",
  /*  11 */ "notnull",
  /*  12 */ "dflt_value",
  /*  13 */ "pk",
  /*  14 */ "hidden",
                           /* table_info reuses 8 */
  /*  15 */ "name",        /* Used by: function_list */
  /*  16 */ "builtin",
  /*  17 */ "type",
  /*  18 */ "enc",
  /*  19 */ "narg",
  /*  20 */ "flags",
  /*  21 */ "schema",      /* Used by: table_list */
  /*  22 */ "name",
  /*  23 */ "type",
  /*  24 */ "ncol",
  /*  25 */ "wr",
  /*  26 */ "strict",
  /*  27 */ "seqno",       /* Used by: index_xinfo */
  /*  28 */ "cid",
  /*  29 */ "name",
  /*  30 */ "desc",
  /*  31 */ "coll",
  /*  32 */ "key",
  /*  33 */ "seq",         /* Used by: index_list */
  /*  34 */ "name",
  /*  35 */ "unique",
  /*  36 */ "origin",
  /*  37 */ "partial",
  /*  38 */ "tbl",         /* Used by: stats */
  /*  39 */ "idx",
  /*  40 */ "wdth",
  /*  41 */ "hght",
  /*  42 */ "flgs",
  /*  43 */ "table",       /* Used by: foreign_key_check */
  /*  44 */ "rowid",
  /*  45 */ "parent",
  /*  46 */ "fkid",

  /*  47 */ "busy",        /* Used by: wal_checkpoint */
  /*  48 */ "log",
  /*  49 */ "checkpointed",
  /*  50 */ "seq",         /* Used by: database_list */
  /*  51 */ "name",
  /*  52 */ "file",
                           /* index_info reuses 27 */
  /*  53 */ "database",    /* Used by: lock_status */
  /*  54 */ "status",
                           /* collation_list reuses 33 */
  /*  55 */ "cache_size",  /* Used by: default_cache_size */
                           /* module_list pragma_list reuses 9 */
  /*  56 */ "timeout",     /* Used by: busy_timeout */
};

/* Definitions of all built-in pragmas */
typedef struct PragmaName {
139300
139301
139302
139303
139304
139305
139306
139307
139308
139309
139310
139311
139312
139313
139314
  /* ColNames:  */ 0, 0,
  /* iArg:      */ SQLITE_CkptFullFSync },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
 {/* zName:     */ "collation_list",
  /* ePragTyp:  */ PragTyp_COLLATION_LIST,
  /* ePragFlg:  */ PragFlg_Result0,
  /* ColNames:  */ 38, 2,
  /* iArg:      */ 0 },
#endif
#if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
 {/* zName:     */ "compile_options",
  /* ePragTyp:  */ PragTyp_COMPILE_OPTIONS,
  /* ePragFlg:  */ PragFlg_Result0,
  /* ColNames:  */ 0, 0,







|







139950
139951
139952
139953
139954
139955
139956
139957
139958
139959
139960
139961
139962
139963
139964
  /* ColNames:  */ 0, 0,
  /* iArg:      */ SQLITE_CkptFullFSync },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
 {/* zName:     */ "collation_list",
  /* ePragTyp:  */ PragTyp_COLLATION_LIST,
  /* ePragFlg:  */ PragFlg_Result0,
  /* ColNames:  */ 33, 2,
  /* iArg:      */ 0 },
#endif
#if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
 {/* zName:     */ "compile_options",
  /* ePragTyp:  */ PragTyp_COMPILE_OPTIONS,
  /* ePragFlg:  */ PragFlg_Result0,
  /* ColNames:  */ 0, 0,
139335
139336
139337
139338
139339
139340
139341
139342
139343
139344
139345
139346
139347
139348
139349
  /* ColNames:  */ 0, 0,
  /* iArg:      */ BTREE_DATA_VERSION },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
 {/* zName:     */ "database_list",
  /* ePragTyp:  */ PragTyp_DATABASE_LIST,
  /* ePragFlg:  */ PragFlg_Result0,
  /* ColNames:  */ 47, 3,
  /* iArg:      */ 0 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
 {/* zName:     */ "default_cache_size",
  /* ePragTyp:  */ PragTyp_DEFAULT_CACHE_SIZE,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1,
  /* ColNames:  */ 55, 1,







|







139985
139986
139987
139988
139989
139990
139991
139992
139993
139994
139995
139996
139997
139998
139999
  /* ColNames:  */ 0, 0,
  /* iArg:      */ BTREE_DATA_VERSION },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
 {/* zName:     */ "database_list",
  /* ePragTyp:  */ PragTyp_DATABASE_LIST,
  /* ePragFlg:  */ PragFlg_Result0,
  /* ColNames:  */ 50, 3,
  /* iArg:      */ 0 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
 {/* zName:     */ "default_cache_size",
  /* ePragTyp:  */ PragTyp_DEFAULT_CACHE_SIZE,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1,
  /* ColNames:  */ 55, 1,
139415
139416
139417
139418
139419
139420
139421
139422
139423
139424
139425
139426
139427
139428
139429
  /* iArg:      */ SQLITE_FullFSync },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
 {/* zName:     */ "function_list",
  /* ePragTyp:  */ PragTyp_FUNCTION_LIST,
  /* ePragFlg:  */ PragFlg_Result0,
  /* ColNames:  */ 27, 6,
  /* iArg:      */ 0 },
#endif
#endif
 {/* zName:     */ "hard_heap_limit",
  /* ePragTyp:  */ PragTyp_HARD_HEAP_LIMIT,
  /* ePragFlg:  */ PragFlg_Result0,
  /* ColNames:  */ 0, 0,







|







140065
140066
140067
140068
140069
140070
140071
140072
140073
140074
140075
140076
140077
140078
140079
  /* iArg:      */ SQLITE_FullFSync },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
 {/* zName:     */ "function_list",
  /* ePragTyp:  */ PragTyp_FUNCTION_LIST,
  /* ePragFlg:  */ PragFlg_Result0,
  /* ColNames:  */ 15, 6,
  /* iArg:      */ 0 },
#endif
#endif
 {/* zName:     */ "hard_heap_limit",
  /* ePragTyp:  */ PragTyp_HARD_HEAP_LIMIT,
  /* ePragFlg:  */ PragFlg_Result0,
  /* ColNames:  */ 0, 0,
139444
139445
139446
139447
139448
139449
139450
139451
139452
139453
139454
139455
139456
139457
139458
139459
139460
139461
139462
139463
139464
139465
139466
139467
139468
  /* ColNames:  */ 0, 0,
  /* iArg:      */ 0 },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
 {/* zName:     */ "index_info",
  /* ePragTyp:  */ PragTyp_INDEX_INFO,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
  /* ColNames:  */ 21, 3,
  /* iArg:      */ 0 },
 {/* zName:     */ "index_list",
  /* ePragTyp:  */ PragTyp_INDEX_LIST,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
  /* ColNames:  */ 38, 5,
  /* iArg:      */ 0 },
 {/* zName:     */ "index_xinfo",
  /* ePragTyp:  */ PragTyp_INDEX_INFO,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
  /* ColNames:  */ 21, 6,
  /* iArg:      */ 1 },
#endif
#if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
 {/* zName:     */ "integrity_check",
  /* ePragTyp:  */ PragTyp_INTEGRITY_CHECK,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt,
  /* ColNames:  */ 0, 0,







|




|




|







140094
140095
140096
140097
140098
140099
140100
140101
140102
140103
140104
140105
140106
140107
140108
140109
140110
140111
140112
140113
140114
140115
140116
140117
140118
  /* ColNames:  */ 0, 0,
  /* iArg:      */ 0 },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
 {/* zName:     */ "index_info",
  /* ePragTyp:  */ PragTyp_INDEX_INFO,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
  /* ColNames:  */ 27, 3,
  /* iArg:      */ 0 },
 {/* zName:     */ "index_list",
  /* ePragTyp:  */ PragTyp_INDEX_LIST,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
  /* ColNames:  */ 33, 5,
  /* iArg:      */ 0 },
 {/* zName:     */ "index_xinfo",
  /* ePragTyp:  */ PragTyp_INDEX_INFO,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
  /* ColNames:  */ 27, 6,
  /* iArg:      */ 1 },
#endif
#if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
 {/* zName:     */ "integrity_check",
  /* ePragTyp:  */ PragTyp_INTEGRITY_CHECK,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt,
  /* ColNames:  */ 0, 0,
139633
139634
139635
139636
139637
139638
139639
139640
139641
139642
139643
139644
139645
139646
139647
139648
139649
139650
139651
139652
139653
139654
139655
139656
139657
139658
139659
139660
139661
139662
139663
139664
139665
139666
  /* iArg:      */ SQLITE_SqlTrace },
#endif
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG)
 {/* zName:     */ "stats",
  /* ePragTyp:  */ PragTyp_STATS,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
  /* ColNames:  */ 33, 5,
  /* iArg:      */ 0 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
 {/* zName:     */ "synchronous",
  /* ePragTyp:  */ PragTyp_SYNCHRONOUS,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1,
  /* ColNames:  */ 0, 0,
  /* iArg:      */ 0 },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
 {/* zName:     */ "table_info",
  /* ePragTyp:  */ PragTyp_TABLE_INFO,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
  /* ColNames:  */ 8, 6,
  /* iArg:      */ 0 },
 {/* zName:     */ "table_list",
  /* ePragTyp:  */ PragTyp_TABLE_LIST,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result1,
  /* ColNames:  */ 15, 6,
  /* iArg:      */ 0 },
 {/* zName:     */ "table_xinfo",
  /* ePragTyp:  */ PragTyp_TABLE_INFO,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
  /* ColNames:  */ 8, 7,
  /* iArg:      */ 1 },
#endif







|


















|







140283
140284
140285
140286
140287
140288
140289
140290
140291
140292
140293
140294
140295
140296
140297
140298
140299
140300
140301
140302
140303
140304
140305
140306
140307
140308
140309
140310
140311
140312
140313
140314
140315
140316
  /* iArg:      */ SQLITE_SqlTrace },
#endif
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG)
 {/* zName:     */ "stats",
  /* ePragTyp:  */ PragTyp_STATS,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
  /* ColNames:  */ 38, 5,
  /* iArg:      */ 0 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
 {/* zName:     */ "synchronous",
  /* ePragTyp:  */ PragTyp_SYNCHRONOUS,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1,
  /* ColNames:  */ 0, 0,
  /* iArg:      */ 0 },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
 {/* zName:     */ "table_info",
  /* ePragTyp:  */ PragTyp_TABLE_INFO,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
  /* ColNames:  */ 8, 6,
  /* iArg:      */ 0 },
 {/* zName:     */ "table_list",
  /* ePragTyp:  */ PragTyp_TABLE_LIST,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result1,
  /* ColNames:  */ 21, 6,
  /* iArg:      */ 0 },
 {/* zName:     */ "table_xinfo",
  /* ePragTyp:  */ PragTyp_TABLE_INFO,
  /* ePragFlg:  */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
  /* ColNames:  */ 8, 7,
  /* iArg:      */ 1 },
#endif
139729
139730
139731
139732
139733
139734
139735
139736
139737
139738
139739
139740
139741
139742
139743
  /* ePragTyp:  */ PragTyp_WAL_AUTOCHECKPOINT,
  /* ePragFlg:  */ 0,
  /* ColNames:  */ 0, 0,
  /* iArg:      */ 0 },
 {/* zName:     */ "wal_checkpoint",
  /* ePragTyp:  */ PragTyp_WAL_CHECKPOINT,
  /* ePragFlg:  */ PragFlg_NeedSchema,
  /* ColNames:  */ 50, 3,
  /* iArg:      */ 0 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
 {/* zName:     */ "writable_schema",
  /* ePragTyp:  */ PragTyp_FLAG,
  /* ePragFlg:  */ PragFlg_Result0|PragFlg_NoColumns1,
  /* ColNames:  */ 0, 0,







|







140379
140380
140381
140382
140383
140384
140385
140386
140387
140388
140389
140390
140391
140392
140393
  /* ePragTyp:  */ PragTyp_WAL_AUTOCHECKPOINT,
  /* ePragFlg:  */ 0,
  /* ColNames:  */ 0, 0,
  /* iArg:      */ 0 },
 {/* zName:     */ "wal_checkpoint",
  /* ePragTyp:  */ PragTyp_WAL_CHECKPOINT,
  /* ePragFlg:  */ PragFlg_NeedSchema,
  /* ColNames:  */ 47, 3,
  /* iArg:      */ 0 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
 {/* zName:     */ "writable_schema",
  /* ePragTyp:  */ PragTyp_FLAG,
  /* ePragFlg:  */ PragFlg_Result0|PragFlg_NoColumns1,
  /* ColNames:  */ 0, 0,
139751
139752
139753
139754
139755
139756
139757
139758
139759
139760
139761
139762
139763
139764
139765

/*
** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands
** will be run with an analysis_limit set to the lessor of the value of
** the following macro or to the actual analysis_limit if it is non-zero,
** in order to prevent PRAGMA optimize from running for too long.
**
** The value of 2000 is chosen emperically so that the worst-case run-time
** for PRAGMA optimize does not exceed 100 milliseconds against a variety
** of test databases on a RaspberryPI-4 compiled using -Os and without
** -DSQLITE_DEBUG.  Of course, your mileage may vary.  For the purpose of
** this paragraph, "worst-case" means that ANALYZE ends up being
** run on every table in the database.  The worst case typically only
** happens if PRAGMA optimize is run on a database file for which ANALYZE
** has not been previously run and the 0x10000 flag is included so that







|







140401
140402
140403
140404
140405
140406
140407
140408
140409
140410
140411
140412
140413
140414
140415

/*
** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands
** will be run with an analysis_limit set to the lessor of the value of
** the following macro or to the actual analysis_limit if it is non-zero,
** in order to prevent PRAGMA optimize from running for too long.
**
** The value of 2000 is chosen empirically so that the worst-case run-time
** for PRAGMA optimize does not exceed 100 milliseconds against a variety
** of test databases on a RaspberryPI-4 compiled using -Os and without
** -DSQLITE_DEBUG.  Of course, your mileage may vary.  For the purpose of
** this paragraph, "worst-case" means that ANALYZE ends up being
** run on every table in the database.  The worst case typically only
** happens if PRAGMA optimize is run on a database file for which ANALYZE
** has not been previously run and the 0x10000 flag is included so that
144040
144041
144042
144043
144044
144045
144046
144047
144048
144049
144050
144051
144052
144053
144054
  pNew->selFlags = selFlags;
  pNew->iLimit = 0;
  pNew->iOffset = 0;
  pNew->selId = ++pParse->nSelect;
  pNew->addrOpenEphm[0] = -1;
  pNew->addrOpenEphm[1] = -1;
  pNew->nSelectRow = 0;
  if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*pSrc));
  pNew->pSrc = pSrc;
  pNew->pWhere = pWhere;
  pNew->pGroupBy = pGroupBy;
  pNew->pHaving = pHaving;
  pNew->pOrderBy = pOrderBy;
  pNew->pPrior = 0;
  pNew->pNext = 0;







|







144690
144691
144692
144693
144694
144695
144696
144697
144698
144699
144700
144701
144702
144703
144704
  pNew->selFlags = selFlags;
  pNew->iLimit = 0;
  pNew->iOffset = 0;
  pNew->selId = ++pParse->nSelect;
  pNew->addrOpenEphm[0] = -1;
  pNew->addrOpenEphm[1] = -1;
  pNew->nSelectRow = 0;
  if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1);
  pNew->pSrc = pSrc;
  pNew->pWhere = pWhere;
  pNew->pGroupBy = pGroupBy;
  pNew->pHaving = pHaving;
  pNew->pOrderBy = pOrderBy;
  pNew->pPrior = 0;
  pNew->pNext = 0;
145423
145424
145425
145426
145427
145428
145429
145430
145431
145432
145433
145434
145435
145436
145437
145438
145439
145440
145441
145442
145443
145444
145445
145446
}

/*
** Allocate a KeyInfo object sufficient for an index of N key columns and
** X extra columns.
*/
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
  int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*);
  KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
  if( p ){
    p->aSortFlags = (u8*)&p->aColl[N+X];
    p->nKeyField = (u16)N;
    p->nAllField = (u16)(N+X);
    p->enc = ENC(db);
    p->db = db;
    p->nRef = 1;
    memset(&p[1], 0, nExtra);
  }else{
    return (KeyInfo*)sqlite3OomFault(db);
  }
  return p;
}

/*







|
|







|







146073
146074
146075
146076
146077
146078
146079
146080
146081
146082
146083
146084
146085
146086
146087
146088
146089
146090
146091
146092
146093
146094
146095
146096
}

/*
** Allocate a KeyInfo object sufficient for an index of N key columns and
** X extra columns.
*/
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
  int nExtra = (N+X)*(sizeof(CollSeq*)+1);
  KeyInfo *p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra);
  if( p ){
    p->aSortFlags = (u8*)&p->aColl[N+X];
    p->nKeyField = (u16)N;
    p->nAllField = (u16)(N+X);
    p->enc = ENC(db);
    p->db = db;
    p->nRef = 1;
    memset(p->aColl, 0, nExtra);
  }else{
    return (KeyInfo*)sqlite3OomFault(db);
  }
  return p;
}

/*
149532
149533
149534
149535
149536
149537
149538
149539
149540
149541
149542
149543
149544
149545
149546
  pNew->pOrderBy = 0;
  p->pPrior = 0;
  p->pNext = 0;
  p->pWith = 0;
#ifndef SQLITE_OMIT_WINDOWFUNC
  p->pWinDefn = 0;
#endif
  p->selFlags &= ~SF_Compound;
  assert( (p->selFlags & SF_Converted)==0 );
  p->selFlags |= SF_Converted;
  assert( pNew->pPrior!=0 );
  pNew->pPrior->pNext = pNew;
  pNew->pLimit = 0;
  return WRC_Continue;
}







|







150182
150183
150184
150185
150186
150187
150188
150189
150190
150191
150192
150193
150194
150195
150196
  pNew->pOrderBy = 0;
  p->pPrior = 0;
  p->pNext = 0;
  p->pWith = 0;
#ifndef SQLITE_OMIT_WINDOWFUNC
  p->pWinDefn = 0;
#endif
  p->selFlags &= ~(u32)SF_Compound;
  assert( (p->selFlags & SF_Converted)==0 );
  p->selFlags |= SF_Converted;
  assert( pNew->pPrior!=0 );
  pNew->pPrior->pNext = pNew;
  pNew->pLimit = 0;
  return WRC_Continue;
}
149948
149949
149950
149951
149952
149953
149954
149955
149956
149957
149958
149959
149960
149961
149962
    /* Renumber selId because it has been copied from a view */
    p->selId = ++pParse->nSelect;
  }
  pTabList = p->pSrc;
  pEList = p->pEList;
  if( pParse->pWith && (p->selFlags & SF_View) ){
    if( p->pWith==0 ){
      p->pWith = (With*)sqlite3DbMallocZero(db, sizeof(With));
      if( p->pWith==0 ){
        return WRC_Abort;
      }
    }
    p->pWith->bView = 1;
  }
  sqlite3WithPush(pParse, p->pWith, 0);







|







150598
150599
150600
150601
150602
150603
150604
150605
150606
150607
150608
150609
150610
150611
150612
    /* Renumber selId because it has been copied from a view */
    p->selId = ++pParse->nSelect;
  }
  pTabList = p->pSrc;
  pEList = p->pEList;
  if( pParse->pWith && (p->selFlags & SF_View) ){
    if( p->pWith==0 ){
      p->pWith = (With*)sqlite3DbMallocZero(db, SZ_WITH(1) );
      if( p->pWith==0 ){
        return WRC_Abort;
      }
    }
    p->pWith->bView = 1;
  }
  sqlite3WithPush(pParse, p->pWith, 0);
151087
151088
151089
151090
151091
151092
151093

151094
151095
151096
151097
151098
151099
151100
** The transformation only works if all of the following are true:
**
**   *  The subquery is a UNION ALL of two or more terms
**   *  The subquery does not have a LIMIT clause
**   *  There is no WHERE or GROUP BY or HAVING clauses on the subqueries
**   *  The outer query is a simple count(*) with no WHERE clause or other
**      extraneous syntax.

**
** Return TRUE if the optimization is undertaken.
*/
static int countOfViewOptimization(Parse *pParse, Select *p){
  Select *pSub, *pPrior;
  Expr *pExpr;
  Expr *pCount;







>







151737
151738
151739
151740
151741
151742
151743
151744
151745
151746
151747
151748
151749
151750
151751
** The transformation only works if all of the following are true:
**
**   *  The subquery is a UNION ALL of two or more terms
**   *  The subquery does not have a LIMIT clause
**   *  There is no WHERE or GROUP BY or HAVING clauses on the subqueries
**   *  The outer query is a simple count(*) with no WHERE clause or other
**      extraneous syntax.
**   *  None of the subqueries are DISTINCT (forumpost/a860f5fb2e 2025-03-10)
**
** Return TRUE if the optimization is undertaken.
*/
static int countOfViewOptimization(Parse *pParse, Select *p){
  Select *pSub, *pPrior;
  Expr *pExpr;
  Expr *pCount;
151119
151120
151121
151122
151123
151124
151125

151126



151127
151128
151129
151130
151131
151132
151133
151134
151135
151136
151137
151138
151139
151140
151141
151142
151143
151144
151145
151146
151147
151148
151149
151150
151151
151152
151153
151154
151155
151156
151157
151158
151159
151160
151161
151162
151163
151164
151165
151166
151167
  pSub = pFrom->u4.pSubq->pSelect;
  if( pSub->pPrior==0 ) return 0;                   /* Must be a compound */
  if( pSub->selFlags & SF_CopyCte ) return 0;       /* Not a CTE */
  do{
    if( pSub->op!=TK_ALL && pSub->pPrior ) return 0;  /* Must be UNION ALL */
    if( pSub->pWhere ) return 0;                      /* No WHERE clause */
    if( pSub->pLimit ) return 0;                      /* No LIMIT clause */

    if( pSub->selFlags & SF_Aggregate ) return 0;     /* Not an aggregate */



    assert( pSub->pHaving==0 );  /* Due to the previous */
    pSub = pSub->pPrior;                              /* Repeat over compound */
  }while( pSub );

  /* If we reach this point then it is OK to perform the transformation */

  db = pParse->db;
  pCount = pExpr;
  pExpr = 0;
  pSub = sqlite3SubqueryDetach(db, pFrom);
  sqlite3SrcListDelete(db, p->pSrc);
  p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc));
  while( pSub ){
    Expr *pTerm;
    pPrior = pSub->pPrior;
    pSub->pPrior = 0;
    pSub->pNext = 0;
    pSub->selFlags |= SF_Aggregate;
    pSub->selFlags &= ~SF_Compound;
    pSub->nSelectRow = 0;
    sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList);
    pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount;
    pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm);
    pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
    sqlite3PExprAddSelect(pParse, pTerm, pSub);
    if( pExpr==0 ){
      pExpr = pTerm;
    }else{
      pExpr = sqlite3PExpr(pParse, TK_PLUS, pTerm, pExpr);
    }
    pSub = pPrior;
  }
  p->pEList->a[0].pExpr = pExpr;
  p->selFlags &= ~SF_Aggregate;

#if TREETRACE_ENABLED
  if( sqlite3TreeTrace & 0x200 ){
    TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif







>
|
>
>
>











|






|














|







151770
151771
151772
151773
151774
151775
151776
151777
151778
151779
151780
151781
151782
151783
151784
151785
151786
151787
151788
151789
151790
151791
151792
151793
151794
151795
151796
151797
151798
151799
151800
151801
151802
151803
151804
151805
151806
151807
151808
151809
151810
151811
151812
151813
151814
151815
151816
151817
151818
151819
151820
151821
151822
  pSub = pFrom->u4.pSubq->pSelect;
  if( pSub->pPrior==0 ) return 0;                   /* Must be a compound */
  if( pSub->selFlags & SF_CopyCte ) return 0;       /* Not a CTE */
  do{
    if( pSub->op!=TK_ALL && pSub->pPrior ) return 0;  /* Must be UNION ALL */
    if( pSub->pWhere ) return 0;                      /* No WHERE clause */
    if( pSub->pLimit ) return 0;                      /* No LIMIT clause */
    if( pSub->selFlags & (SF_Aggregate|SF_Distinct) ){
       testcase( pSub->selFlags & SF_Aggregate );
       testcase( pSub->selFlags & SF_Distinct );
       return 0;     /* Not an aggregate nor DISTINCT */
    }
    assert( pSub->pHaving==0 );  /* Due to the previous */
    pSub = pSub->pPrior;                              /* Repeat over compound */
  }while( pSub );

  /* If we reach this point then it is OK to perform the transformation */

  db = pParse->db;
  pCount = pExpr;
  pExpr = 0;
  pSub = sqlite3SubqueryDetach(db, pFrom);
  sqlite3SrcListDelete(db, p->pSrc);
  p->pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1);
  while( pSub ){
    Expr *pTerm;
    pPrior = pSub->pPrior;
    pSub->pPrior = 0;
    pSub->pNext = 0;
    pSub->selFlags |= SF_Aggregate;
    pSub->selFlags &= ~(u32)SF_Compound;
    pSub->nSelectRow = 0;
    sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList);
    pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount;
    pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm);
    pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
    sqlite3PExprAddSelect(pParse, pTerm, pSub);
    if( pExpr==0 ){
      pExpr = pTerm;
    }else{
      pExpr = sqlite3PExpr(pParse, TK_PLUS, pTerm, pExpr);
    }
    pSub = pPrior;
  }
  p->pEList->a[0].pExpr = pExpr;
  p->selFlags &= ~(u32)SF_Aggregate;

#if TREETRACE_ENABLED
  if( sqlite3TreeTrace & 0x200 ){
    TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif
151360
151361
151362
151363
151364
151365
151366
151367
151368
151369
151370
151371
151372
151373
151374
      }
#endif
      sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,
                              p->pOrderBy);
      testcase( pParse->earlyCleanup );
      p->pOrderBy = 0;
    }
    p->selFlags &= ~SF_Distinct;
    p->selFlags |= SF_NoopOrderBy;
  }
  sqlite3SelectPrep(pParse, p, 0);
  if( pParse->nErr ){
    goto select_end;
  }
  assert( db->mallocFailed==0 );







|







152015
152016
152017
152018
152019
152020
152021
152022
152023
152024
152025
152026
152027
152028
152029
      }
#endif
      sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,
                              p->pOrderBy);
      testcase( pParse->earlyCleanup );
      p->pOrderBy = 0;
    }
    p->selFlags &= ~(u32)SF_Distinct;
    p->selFlags |= SF_NoopOrderBy;
  }
  sqlite3SelectPrep(pParse, p, 0);
  if( pParse->nErr ){
    goto select_end;
  }
  assert( db->mallocFailed==0 );
151399
151400
151401
151402
151403
151404
151405
151406
151407
151408
151409
151410
151411
151412
151413
      goto select_end;
    }

    /* Clear the SF_UFSrcCheck flag. The check has already been performed,
    ** and leaving this flag set can cause errors if a compound sub-query
    ** in p->pSrc is flattened into this query and this function called
    ** again as part of compound SELECT processing.  */
    p->selFlags &= ~SF_UFSrcCheck;
  }

  if( pDest->eDest==SRT_Output ){
    sqlite3GenerateColumnNames(pParse, p);
  }

#ifndef SQLITE_OMIT_WINDOWFUNC







|







152054
152055
152056
152057
152058
152059
152060
152061
152062
152063
152064
152065
152066
152067
152068
      goto select_end;
    }

    /* Clear the SF_UFSrcCheck flag. The check has already been performed,
    ** and leaving this flag set can cause errors if a compound sub-query
    ** in p->pSrc is flattened into this query and this function called
    ** again as part of compound SELECT processing.  */
    p->selFlags &= ~(u32)SF_UFSrcCheck;
  }

  if( pDest->eDest==SRT_Output ){
    sqlite3GenerateColumnNames(pParse, p);
  }

#ifndef SQLITE_OMIT_WINDOWFUNC
151888
151889
151890
151891
151892
151893
151894
151895
151896
151897
151898
151899
151900
151901
151902
  if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
   && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0
   && OptimizationEnabled(db, SQLITE_GroupByOrder)
#ifndef SQLITE_OMIT_WINDOWFUNC
   && p->pWin==0
#endif
  ){
    p->selFlags &= ~SF_Distinct;
    pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0);
    if( pGroupBy ){
      for(i=0; i<pGroupBy->nExpr; i++){
        pGroupBy->a[i].u.x.iOrderByCol = i+1;
      }
    }
    p->selFlags |= SF_Aggregate;







|







152543
152544
152545
152546
152547
152548
152549
152550
152551
152552
152553
152554
152555
152556
152557
  if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
   && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0
   && OptimizationEnabled(db, SQLITE_GroupByOrder)
#ifndef SQLITE_OMIT_WINDOWFUNC
   && p->pWin==0
#endif
  ){
    p->selFlags &= ~(u32)SF_Distinct;
    pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0);
    if( pGroupBy ){
      for(i=0; i<pGroupBy->nExpr; i++){
        pGroupBy->a[i].u.x.iOrderByCol = i+1;
      }
    }
    p->selFlags |= SF_Aggregate;
153922
153923
153924
153925
153926
153927
153928
153929

153930
153931
153932
153933
153934
153935
153936
153937
153938
153939
153940
153941
153942
153943
153944

153945
153946
153947
153948
153949
153950
153951
153952
153953
153954
153955
153956
153957
153958
  int regIn            /* The first in an array of registers */
){
  Vdbe *v = pParse->pVdbe;
  sqlite3 *db = pParse->db;
  ExprList *pNew;
  Returning *pReturning;
  Select sSelect;
  SrcList sFrom;


  assert( v!=0 );
  if( !pParse->bReturning ){
    /* This RETURNING trigger must be for a different statement as
    ** this statement lacks a RETURNING clause. */
    return;
  }
  assert( db->pParse==pParse );
  assert( !pParse->isCreate );
  pReturning = pParse->u1.d.pReturning;
  if( pTrigger != &(pReturning->retTrig) ){
    /* This RETURNING trigger is for a different statement */
    return;
  }
  memset(&sSelect, 0, sizeof(sSelect));

  memset(&sFrom, 0, sizeof(sFrom));
  sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
  sSelect.pSrc = &sFrom;
  sFrom.nSrc = 1;
  sFrom.a[0].pSTab = pTab;
  sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */
  sFrom.a[0].iCursor = -1;
  sqlite3SelectPrep(pParse, &sSelect, 0);
  if( pParse->nErr==0 ){
    assert( db->mallocFailed==0 );
    sqlite3GenerateColumnNames(pParse, &sSelect);
  }
  sqlite3ExprListDelete(db, sSelect.pEList);
  pNew = sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab);







|
>















>
|

|
|
|
|
|







154577
154578
154579
154580
154581
154582
154583
154584
154585
154586
154587
154588
154589
154590
154591
154592
154593
154594
154595
154596
154597
154598
154599
154600
154601
154602
154603
154604
154605
154606
154607
154608
154609
154610
154611
154612
154613
154614
154615
  int regIn            /* The first in an array of registers */
){
  Vdbe *v = pParse->pVdbe;
  sqlite3 *db = pParse->db;
  ExprList *pNew;
  Returning *pReturning;
  Select sSelect;
  SrcList *pFrom;
  u8 fromSpace[SZ_SRCLIST_1];

  assert( v!=0 );
  if( !pParse->bReturning ){
    /* This RETURNING trigger must be for a different statement as
    ** this statement lacks a RETURNING clause. */
    return;
  }
  assert( db->pParse==pParse );
  assert( !pParse->isCreate );
  pReturning = pParse->u1.d.pReturning;
  if( pTrigger != &(pReturning->retTrig) ){
    /* This RETURNING trigger is for a different statement */
    return;
  }
  memset(&sSelect, 0, sizeof(sSelect));
  pFrom = (SrcList*)fromSpace;
  memset(pFrom, 0, SZ_SRCLIST_1);
  sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
  sSelect.pSrc = pFrom;
  pFrom->nSrc = 1;
  pFrom->a[0].pSTab = pTab;
  pFrom->a[0].zName = pTab->zName; /* tag-20240424-1 */
  pFrom->a[0].iCursor = -1;
  sqlite3SelectPrep(pParse, &sSelect, 0);
  if( pParse->nErr==0 ){
    assert( db->mallocFailed==0 );
    sqlite3GenerateColumnNames(pParse, &sSelect);
  }
  sqlite3ExprListDelete(db, sSelect.pEList);
  pNew = sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab);
156345
156346
156347
156348
156349
156350
156351
156352
156353
156354
156355
156356
156357
156358
156359
  ** restored before returning. Then set the writable-schema flag, and
  ** disable CHECK and foreign key constraints.  */
  saved_flags = db->flags;
  saved_mDbFlags = db->mDbFlags;
  saved_nChange = db->nChange;
  saved_nTotalChange = db->nTotalChange;
  saved_mTrace = db->mTrace;
  db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
  db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
  db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder
                   | SQLITE_Defensive | SQLITE_CountRows);
  db->mTrace = 0;

  zDbMain = db->aDb[iDb].zDbSName;
  pMain = db->aDb[iDb].pBt;







|







157002
157003
157004
157005
157006
157007
157008
157009
157010
157011
157012
157013
157014
157015
157016
  ** restored before returning. Then set the writable-schema flag, and
  ** disable CHECK and foreign key constraints.  */
  saved_flags = db->flags;
  saved_mDbFlags = db->mDbFlags;
  saved_nChange = db->nChange;
  saved_nTotalChange = db->nTotalChange;
  saved_mTrace = db->mTrace;
  db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments;
  db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
  db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder
                   | SQLITE_Defensive | SQLITE_CountRows);
  db->mTrace = 0;

  zDbMain = db->aDb[iDb].zDbSName;
  pMain = db->aDb[iDb].pBt;
158474
158475
158476
158477
158478
158479
158480
158481
158482
158483





158484
158485
158486
158487
158488
158489
158490
  int iTop;                 /* The very beginning of the WHERE loop */
  int iEndWhere;            /* End of the WHERE clause itself */
  WhereLoop *pLoops;        /* List of all WhereLoop objects */
  WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */
  Bitmask revMask;          /* Mask of ORDER BY terms that need reversing */
  WhereClause sWC;          /* Decomposition of the WHERE clause */
  WhereMaskSet sMaskSet;    /* Map cursor numbers to bitmasks */
  WhereLevel a[1];          /* Information about each nest loop in WHERE */
};






/*
** Private interfaces - callable only by other where.c routines.
**
** where.c:
*/
SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet*,int);
#ifdef WHERETRACE_ENABLED







|


>
>
>
>
>







159131
159132
159133
159134
159135
159136
159137
159138
159139
159140
159141
159142
159143
159144
159145
159146
159147
159148
159149
159150
159151
159152
  int iTop;                 /* The very beginning of the WHERE loop */
  int iEndWhere;            /* End of the WHERE clause itself */
  WhereLoop *pLoops;        /* List of all WhereLoop objects */
  WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */
  Bitmask revMask;          /* Mask of ORDER BY terms that need reversing */
  WhereClause sWC;          /* Decomposition of the WHERE clause */
  WhereMaskSet sMaskSet;    /* Map cursor numbers to bitmasks */
  WhereLevel a[FLEXARRAY];  /* Information about each nest loop in WHERE */
};

/*
** The size (in bytes) of a WhereInfo object that holds N WhereLevels.
*/
#define SZ_WHEREINFO(N) ROUND8(offsetof(WhereInfo,a)+(N)*sizeof(WhereLevel))

/*
** Private interfaces - callable only by other where.c routines.
**
** where.c:
*/
SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet*,int);
#ifdef WHERETRACE_ENABLED
160927
160928
160929
160930
160931
160932
160933
160934
160935
160936
160937
160938
160939
160940
160941
160942
    ** by this loop in the a[0] slot and all notReady tables in a[1..] slots.
    ** This becomes the SrcList in the recursive call to sqlite3WhereBegin().
    */
    if( pWInfo->nLevel>1 ){
      int nNotReady;                 /* The number of notReady tables */
      SrcItem *origSrc;              /* Original list of tables */
      nNotReady = pWInfo->nLevel - iLevel - 1;
      pOrTab = sqlite3DbMallocRawNN(db,
                            sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
      if( pOrTab==0 ) return notReady;
      pOrTab->nAlloc = (u8)(nNotReady + 1);
      pOrTab->nSrc = pOrTab->nAlloc;
      memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem));
      origSrc = pWInfo->pTabList->a;
      for(k=1; k<=nNotReady; k++){
        memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], sizeof(pOrTab->a[k]));







|
<







161589
161590
161591
161592
161593
161594
161595
161596

161597
161598
161599
161600
161601
161602
161603
    ** by this loop in the a[0] slot and all notReady tables in a[1..] slots.
    ** This becomes the SrcList in the recursive call to sqlite3WhereBegin().
    */
    if( pWInfo->nLevel>1 ){
      int nNotReady;                 /* The number of notReady tables */
      SrcItem *origSrc;              /* Original list of tables */
      nNotReady = pWInfo->nLevel - iLevel - 1;
      pOrTab = sqlite3DbMallocRawNN(db, SZ_SRCLIST(nNotReady+1));

      if( pOrTab==0 ) return notReady;
      pOrTab->nAlloc = (u8)(nNotReady + 1);
      pOrTab->nSrc = pOrTab->nAlloc;
      memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem));
      origSrc = pWInfo->pTabList->a;
      for(k=1; k<=nNotReady; k++){
        memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], sizeof(pOrTab->a[k]));
161471
161472
161473
161474
161475
161476
161477
161478

161479
161480
161481
161482
161483
161484
161485
  Vdbe *v = pParse->pVdbe;
  WhereRightJoin *pRJ = pLevel->pRJ;
  Expr *pSubWhere = 0;
  WhereClause *pWC = &pWInfo->sWC;
  WhereInfo *pSubWInfo;
  WhereLoop *pLoop = pLevel->pWLoop;
  SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
  SrcList sFrom;

  Bitmask mAll = 0;
  int k;

  ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName));
  sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn,
                                  pRJ->regReturn);
  for(k=0; k<iLevel; k++){







|
>







162132
162133
162134
162135
162136
162137
162138
162139
162140
162141
162142
162143
162144
162145
162146
162147
  Vdbe *v = pParse->pVdbe;
  WhereRightJoin *pRJ = pLevel->pRJ;
  Expr *pSubWhere = 0;
  WhereClause *pWC = &pWInfo->sWC;
  WhereInfo *pSubWInfo;
  WhereLoop *pLoop = pLevel->pWLoop;
  SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
  SrcList *pFrom;
  u8 fromSpace[SZ_SRCLIST_1];
  Bitmask mAll = 0;
  int k;

  ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName));
  sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn,
                                  pRJ->regReturn);
  for(k=0; k<iLevel; k++){
161515
161516
161517
161518
161519
161520
161521

161522
161523
161524
161525
161526
161527
161528
161529
161530
161531
161532
161533
161534
161535
      }
      if( pTerm->prereqAll & ~mAll ) continue;
      if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
      pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
                                 sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
    }
  }

  sFrom.nSrc = 1;
  sFrom.nAlloc = 1;
  memcpy(&sFrom.a[0], pTabItem, sizeof(SrcItem));
  sFrom.a[0].fg.jointype = 0;
  assert( pParse->withinRJSubrtn < 100 );
  pParse->withinRJSubrtn++;
  pSubWInfo = sqlite3WhereBegin(pParse, &sFrom, pSubWhere, 0, 0, 0,
                                WHERE_RIGHT_JOIN, 0);
  if( pSubWInfo ){
    int iCur = pLevel->iTabCur;
    int r = ++pParse->nMem;
    int nPk;
    int jmp;
    int addrCont = sqlite3WhereContinueLabel(pSubWInfo);







>
|
|
|
|


|







162177
162178
162179
162180
162181
162182
162183
162184
162185
162186
162187
162188
162189
162190
162191
162192
162193
162194
162195
162196
162197
162198
      }
      if( pTerm->prereqAll & ~mAll ) continue;
      if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
      pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
                                 sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
    }
  }
  pFrom = (SrcList*)fromSpace;
  pFrom->nSrc = 1;
  pFrom->nAlloc = 1;
  memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem));
  pFrom->a[0].fg.jointype = 0;
  assert( pParse->withinRJSubrtn < 100 );
  pParse->withinRJSubrtn++;
  pSubWInfo = sqlite3WhereBegin(pParse, pFrom, pSubWhere, 0, 0, 0,
                                WHERE_RIGHT_JOIN, 0);
  if( pSubWInfo ){
    int iCur = pLevel->iTabCur;
    int r = ++pParse->nMem;
    int nPk;
    int jmp;
    int addrCont = sqlite3WhereContinueLabel(pSubWInfo);
163509
163510
163511
163512
163513
163514
163515
163516
163517
163518
163519
163520





163521
163522
163523
163524
163525
163526
163527
typedef struct HiddenIndexInfo HiddenIndexInfo;
struct HiddenIndexInfo {
  WhereClause *pWC;        /* The Where clause being analyzed */
  Parse *pParse;           /* The parsing context */
  int eDistinct;           /* Value to return from sqlite3_vtab_distinct() */
  u32 mIn;                 /* Mask of terms that are <col> IN (...) */
  u32 mHandleIn;           /* Terms that vtab will handle as <col> IN (...) */
  sqlite3_value *aRhs[1];  /* RHS values for constraints. MUST BE LAST
                           ** because extra space is allocated to hold up
                           ** to nTerm such values */
};






/* Forward declaration of methods */
static int whereLoopResize(sqlite3*, WhereLoop*, int);

/*
** Return the estimated number of output rows from a WHERE clause
*/
SQLITE_PRIVATE LogEst sqlite3WhereOutputRowCount(WhereInfo *pWInfo){







|
|
|


>
>
>
>
>







164172
164173
164174
164175
164176
164177
164178
164179
164180
164181
164182
164183
164184
164185
164186
164187
164188
164189
164190
164191
164192
164193
164194
164195
typedef struct HiddenIndexInfo HiddenIndexInfo;
struct HiddenIndexInfo {
  WhereClause *pWC;        /* The Where clause being analyzed */
  Parse *pParse;           /* The parsing context */
  int eDistinct;           /* Value to return from sqlite3_vtab_distinct() */
  u32 mIn;                 /* Mask of terms that are <col> IN (...) */
  u32 mHandleIn;           /* Terms that vtab will handle as <col> IN (...) */
  sqlite3_value *aRhs[FLEXARRAY];  /* RHS values for constraints. MUST BE LAST
                                   ** Extra space is allocated to hold up
                                   ** to nTerm such values */
};

/* Size (in bytes) of a HiddenIndeInfo object sufficient to hold as
** many as N constraints */
#define SZ_HIDDENINDEXINFO(N) \
                  (offsetof(HiddenIndexInfo,aRhs) + (N)*sizeof(sqlite3_value*))

/* Forward declaration of methods */
static int whereLoopResize(sqlite3*, WhereLoop*, int);

/*
** Return the estimated number of output rows from a WHERE clause
*/
SQLITE_PRIVATE LogEst sqlite3WhereOutputRowCount(WhereInfo *pWInfo){
164578
164579
164580
164581
164582
164583
164584


164585
164586
164587
164588
164589
164590
164591
    if( extraCols & MASKBIT(i) ) nKeyCol++;
  }
  if( pSrc->colUsed & MASKBIT(BMS-1) ){
    nKeyCol += pTable->nCol - BMS + 1;
  }

  /* Construct the Index object to describe this index */


  pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+HasRowid(pTable),
                                    0, &zNotUsed);
  if( pIdx==0 ) goto end_auto_index_create;
  pLoop->u.btree.pIndex = pIdx;
  pIdx->zName = "auto-index";
  pIdx->pTable = pTable;
  n = 0;







>
>







165246
165247
165248
165249
165250
165251
165252
165253
165254
165255
165256
165257
165258
165259
165260
165261
    if( extraCols & MASKBIT(i) ) nKeyCol++;
  }
  if( pSrc->colUsed & MASKBIT(BMS-1) ){
    nKeyCol += pTable->nCol - BMS + 1;
  }

  /* Construct the Index object to describe this index */
  assert( nKeyCol <= pTable->nCol + MAX(0, pTable->nCol - BMS + 1) );
  /* ^-- This guarantees that the number of index columns will fit in the u16 */
  pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+HasRowid(pTable),
                                    0, &zNotUsed);
  if( pIdx==0 ) goto end_auto_index_create;
  pLoop->u.btree.pIndex = pIdx;
  pIdx->zName = "auto-index";
  pIdx->pTable = pTable;
  n = 0;
164989
164990
164991
164992
164993
164994
164995
164996
164997
164998
164999
165000
165001
165002
165003
165004
    }
  }

  /* Allocate the sqlite3_index_info structure
  */
  pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo)
                           + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm
                           + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden)
                           + sizeof(sqlite3_value*)*nTerm );
  if( pIdxInfo==0 ){
    sqlite3ErrorMsg(pParse, "out of memory");
    return 0;
  }
  pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1];
  pIdxCons = (struct sqlite3_index_constraint*)&pHidden->aRhs[nTerm];
  pIdxOrderBy = (struct sqlite3_index_orderby*)&pIdxCons[nTerm];







|
|







165659
165660
165661
165662
165663
165664
165665
165666
165667
165668
165669
165670
165671
165672
165673
165674
    }
  }

  /* Allocate the sqlite3_index_info structure
  */
  pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo)
                           + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm
                           + sizeof(*pIdxOrderBy)*nOrderBy
                           + SZ_HIDDENINDEXINFO(nTerm) );
  if( pIdxInfo==0 ){
    sqlite3ErrorMsg(pParse, "out of memory");
    return 0;
  }
  pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1];
  pIdxCons = (struct sqlite3_index_constraint*)&pHidden->aRhs[nTerm];
  pIdxOrderBy = (struct sqlite3_index_orderby*)&pIdxCons[nTerm];
170184
170185
170186
170187
170188
170189
170190
170191
170192
170193
170194
170195
170196
170197
170198
170199
170200
170201
  /* Allocate and initialize the WhereInfo structure that will become the
  ** return value. A single allocation is used to store the WhereInfo
  ** struct, the contents of WhereInfo.a[], the WhereClause structure
  ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte
  ** field (type Bitmask) it must be aligned on an 8-byte boundary on
  ** some architectures. Hence the ROUND8() below.
  */
  nByteWInfo = ROUND8P(sizeof(WhereInfo));
  if( nTabList>1 ){
    nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel));
  }
  pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
  if( db->mallocFailed ){
    sqlite3DbFree(db, pWInfo);
    pWInfo = 0;
    goto whereBeginError;
  }
  pWInfo->pParse = pParse;







|
<
<
<







170854
170855
170856
170857
170858
170859
170860
170861



170862
170863
170864
170865
170866
170867
170868
  /* Allocate and initialize the WhereInfo structure that will become the
  ** return value. A single allocation is used to store the WhereInfo
  ** struct, the contents of WhereInfo.a[], the WhereClause structure
  ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte
  ** field (type Bitmask) it must be aligned on an 8-byte boundary on
  ** some architectures. Hence the ROUND8() below.
  */
  nByteWInfo = SZ_WHEREINFO(nTabList);



  pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
  if( db->mallocFailed ){
    sqlite3DbFree(db, pWInfo);
    pWInfo = 0;
    goto whereBeginError;
  }
  pWInfo->pParse = pParse;
172139
172140
172141
172142
172143
172144
172145
172146
172147
172148
172149
172150
172151
172152
172153
      sqlite3WalkExprList(&w, p->pOrderBy);
    }

    p->pSrc = 0;
    p->pWhere = 0;
    p->pGroupBy = 0;
    p->pHaving = 0;
    p->selFlags &= ~SF_Aggregate;
    p->selFlags |= SF_WinRewrite;

    /* Create the ORDER BY clause for the sub-select. This is the concatenation
    ** of the window PARTITION and ORDER BY clauses. Then, if this makes it
    ** redundant, remove the ORDER BY from the parent SELECT.  */
    pSort = exprListAppendList(pParse, 0, pMWin->pPartition, 1);
    pSort = exprListAppendList(pParse, pSort, pMWin->pOrderBy, 1);







|







172806
172807
172808
172809
172810
172811
172812
172813
172814
172815
172816
172817
172818
172819
172820
      sqlite3WalkExprList(&w, p->pOrderBy);
    }

    p->pSrc = 0;
    p->pWhere = 0;
    p->pGroupBy = 0;
    p->pHaving = 0;
    p->selFlags &= ~(u32)SF_Aggregate;
    p->selFlags |= SF_WinRewrite;

    /* Create the ORDER BY clause for the sub-select. This is the concatenation
    ** of the window PARTITION and ORDER BY clauses. Then, if this makes it
    ** redundant, remove the ORDER BY from the parent SELECT.  */
    pSort = exprListAppendList(pParse, 0, pMWin->pPartition, 1);
    pSort = exprListAppendList(pParse, pSort, pMWin->pOrderBy, 1);
178278
178279
178280
178281
178282
178283
178284
178285
178286
178287
178288
178289
178290
178291
178292
178293
    parserDoubleLinkSelect(pParse, pRhs);
    pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0);
    pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0);
  }
  if( pRhs ){
    pRhs->op = (u8)yymsp[-1].minor.yy502;
    pRhs->pPrior = pLhs;
    if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue;
    pRhs->selFlags &= ~SF_MultiValue;
    if( yymsp[-1].minor.yy502!=TK_ALL ) pParse->hasCompound = 1;
  }else{
    sqlite3SelectDelete(pParse->db, pLhs);
  }
  yymsp[-2].minor.yy637 = pRhs;
}
        break;







|
|







178945
178946
178947
178948
178949
178950
178951
178952
178953
178954
178955
178956
178957
178958
178959
178960
    parserDoubleLinkSelect(pParse, pRhs);
    pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0);
    pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0);
  }
  if( pRhs ){
    pRhs->op = (u8)yymsp[-1].minor.yy502;
    pRhs->pPrior = pLhs;
    if( ALWAYS(pLhs) ) pLhs->selFlags &= ~(u32)SF_MultiValue;
    pRhs->selFlags &= ~(u32)SF_MultiValue;
    if( yymsp[-1].minor.yy502!=TK_ALL ) pParse->hasCompound = 1;
  }else{
    sqlite3SelectDelete(pParse->db, pLhs);
  }
  yymsp[-2].minor.yy637 = pRhs;
}
        break;
181023
181024
181025
181026
181027
181028
181029
181030




181031
181032
181033
181034
181035
181036
181037
      }else if( tokenType==TK_OVER ){
        assert( n==4 );
        tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed);
      }else if( tokenType==TK_FILTER ){
        assert( n==6 );
        tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed);
#endif /* SQLITE_OMIT_WINDOWFUNC */
      }else if( tokenType==TK_COMMENT && (db->flags & SQLITE_Comments)!=0 ){




        zSql += n;
        continue;
      }else if( tokenType!=TK_QNUMBER ){
        Token x;
        x.z = zSql;
        x.n = n;
        sqlite3ErrorMsg(pParse, "unrecognized token: \"%T\"", &x);







|
>
>
>
>







181690
181691
181692
181693
181694
181695
181696
181697
181698
181699
181700
181701
181702
181703
181704
181705
181706
181707
181708
      }else if( tokenType==TK_OVER ){
        assert( n==4 );
        tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed);
      }else if( tokenType==TK_FILTER ){
        assert( n==6 );
        tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed);
#endif /* SQLITE_OMIT_WINDOWFUNC */
      }else if( tokenType==TK_COMMENT
             && (db->init.busy || (db->flags & SQLITE_Comments)!=0)
      ){
        /* Ignore SQL comments if either (1) we are reparsing the schema or
        ** (2) SQLITE_DBCONFIG_ENABLE_COMMENTS is turned on (the default). */
        zSql += n;
        continue;
      }else if( tokenType!=TK_QNUMBER ){
        Token x;
        x.z = zSql;
        x.n = n;
        sqlite3ErrorMsg(pParse, "unrecognized token: \"%T\"", &x);
181918
181919
181920
181921
181922
181923
181924








181925
181926
181927
181928
181929
181930
181931
    if( rc==SQLITE_OK ){
      rc = sqlite3MemdbInit();
    }
#endif
    if( rc==SQLITE_OK ){
      sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
          sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);








      sqlite3MemoryBarrier();
      sqlite3GlobalConfig.isInit = 1;
#ifdef SQLITE_EXTRA_INIT
      bRunExtraInit = 1;
#endif
    }
    sqlite3GlobalConfig.inProgress = 0;







>
>
>
>
>
>
>
>







182589
182590
182591
182592
182593
182594
182595
182596
182597
182598
182599
182600
182601
182602
182603
182604
182605
182606
182607
182608
182609
182610
    if( rc==SQLITE_OK ){
      rc = sqlite3MemdbInit();
    }
#endif
    if( rc==SQLITE_OK ){
      sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
          sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);
#ifdef SQLITE_EXTRA_INIT_MUTEXED
      {
        int SQLITE_EXTRA_INIT_MUTEXED(const char*);
        rc = SQLITE_EXTRA_INIT_MUTEXED(0);
      }
#endif
    }
    if( rc==SQLITE_OK ){
      sqlite3MemoryBarrier();
      sqlite3GlobalConfig.isInit = 1;
#ifdef SQLITE_EXTRA_INIT
      bRunExtraInit = 1;
#endif
    }
    sqlite3GlobalConfig.inProgress = 0;
183394
183395
183396
183397
183398
183399
183400



183401
183402
183403
183404
183405
183406
183407
  if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
  sqlite3_mutex_enter(db->mutex);
  db->busyHandler.xBusyHandler = xBusy;
  db->busyHandler.pBusyArg = pArg;
  db->busyHandler.nBusy = 0;
  db->busyTimeout = 0;



  sqlite3_mutex_leave(db->mutex);
  return SQLITE_OK;
}

#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
/*
** This routine sets the progress callback for an Sqlite database to the







>
>
>







184073
184074
184075
184076
184077
184078
184079
184080
184081
184082
184083
184084
184085
184086
184087
184088
184089
  if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
  sqlite3_mutex_enter(db->mutex);
  db->busyHandler.xBusyHandler = xBusy;
  db->busyHandler.pBusyArg = pArg;
  db->busyHandler.nBusy = 0;
  db->busyTimeout = 0;
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  db->setlkTimeout = 0;
#endif
  sqlite3_mutex_leave(db->mutex);
  return SQLITE_OK;
}

#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
/*
** This routine sets the progress callback for an Sqlite database to the
183443
183444
183445
183446
183447
183448
183449



183450
183451
183452
183453
183454
































183455
183456
183457
183458
183459
183460
183461
#ifdef SQLITE_ENABLE_API_ARMOR
  if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
  if( ms>0 ){
    sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback,
                             (void*)db);
    db->busyTimeout = ms;



  }else{
    sqlite3_busy_handler(db, 0, 0);
  }
  return SQLITE_OK;
}

































/*
** Cause any pending operation to stop at its earliest opportunity.
*/
SQLITE_API void sqlite3_interrupt(sqlite3 *db){
#ifdef SQLITE_ENABLE_API_ARMOR
  if( !sqlite3SafetyCheckOk(db)







>
>
>





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







184125
184126
184127
184128
184129
184130
184131
184132
184133
184134
184135
184136
184137
184138
184139
184140
184141
184142
184143
184144
184145
184146
184147
184148
184149
184150
184151
184152
184153
184154
184155
184156
184157
184158
184159
184160
184161
184162
184163
184164
184165
184166
184167
184168
184169
184170
184171
184172
184173
184174
184175
184176
184177
184178
#ifdef SQLITE_ENABLE_API_ARMOR
  if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
  if( ms>0 ){
    sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback,
                             (void*)db);
    db->busyTimeout = ms;
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    db->setlkTimeout = ms;
#endif
  }else{
    sqlite3_busy_handler(db, 0, 0);
  }
  return SQLITE_OK;
}

/*
** Set the setlk timeout value.
*/
SQLITE_API int sqlite3_setlk_timeout(sqlite3 *db, int ms, int flags){
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  int iDb;
  int bBOC = ((flags & SQLITE_SETLK_BLOCK_ON_CONNECT) ? 1 : 0);
#endif
#ifdef SQLITE_ENABLE_API_ARMOR
  if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
  if( ms<-1 ) return SQLITE_RANGE;
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  db->setlkTimeout = ms;
  db->setlkFlags = flags;
  sqlite3BtreeEnterAll(db);
  for(iDb=0; iDb<db->nDb; iDb++){
    Btree *pBt = db->aDb[iDb].pBt;
    if( pBt ){
      sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pBt));
      sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, (void*)&bBOC);
    }
  }
  sqlite3BtreeLeaveAll(db);
#endif
#if !defined(SQLITE_ENABLE_API_ARMOR) && !defined(SQLITE_ENABLE_SETLK_TIMEOUT)
  UNUSED_PARAMETER(db);
  UNUSED_PARAMETER(flags);
#endif
  return SQLITE_OK;
}

/*
** Cause any pending operation to stop at its earliest opportunity.
*/
SQLITE_API void sqlite3_interrupt(sqlite3 *db){
#ifdef SQLITE_ENABLE_API_ARMOR
  if( !sqlite3SafetyCheckOk(db)
185414
185415
185416
185417
185418
185419
185420
185421
185422
185423
185424
185425
185426
185427
185428
      return SQLITE_OK;
    }
  }else if( pData==0 ){
    sqlite3_mutex_leave(db->mutex);
    return SQLITE_OK;
  }else{
    size_t n = strlen(zName);
    p = sqlite3_malloc64( sizeof(DbClientData)+n+1 );
    if( p==0 ){
      if( xDestructor ) xDestructor(pData);
      sqlite3_mutex_leave(db->mutex);
      return SQLITE_NOMEM;
    }
    memcpy(p->zName, zName, n+1);
    p->pNext = db->pDbData;







|







186131
186132
186133
186134
186135
186136
186137
186138
186139
186140
186141
186142
186143
186144
186145
      return SQLITE_OK;
    }
  }else if( pData==0 ){
    sqlite3_mutex_leave(db->mutex);
    return SQLITE_OK;
  }else{
    size_t n = strlen(zName);
    p = sqlite3_malloc64( SZ_DBCLIENTDATA(n+1) );
    if( p==0 ){
      if( xDestructor ) xDestructor(pData);
      sqlite3_mutex_leave(db->mutex);
      return SQLITE_NOMEM;
    }
    memcpy(p->zName, zName, n+1);
    p->pNext = db->pDbData;
185780
185781
185782
185783
185784
185785
185786
185787
185788
185789
185790
185791
185792
185793
185794
185795
      break;
    }
#endif

    /*  sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b);
    **
    ** If b is true, then activate the SQLITE_FkNoAction setting.  If b is
    ** false then clearn that setting.  If the SQLITE_FkNoAction setting is
    ** abled, all foreign key ON DELETE and ON UPDATE actions behave as if
    ** they were NO ACTION, regardless of how they are defined.
    **
    ** NB:  One must usually run "PRAGMA writable_schema=RESET" after
    ** using this test-control, before it will take full effect.  failing
    ** to reset the schema can result in some unexpected behavior.
    */
    case SQLITE_TESTCTRL_FK_NO_ACTION: {







|
|







186497
186498
186499
186500
186501
186502
186503
186504
186505
186506
186507
186508
186509
186510
186511
186512
      break;
    }
#endif

    /*  sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b);
    **
    ** If b is true, then activate the SQLITE_FkNoAction setting.  If b is
    ** false then clear that setting.  If the SQLITE_FkNoAction setting is
    ** enabled, all foreign key ON DELETE and ON UPDATE actions behave as if
    ** they were NO ACTION, regardless of how they are defined.
    **
    ** NB:  One must usually run "PRAGMA writable_schema=RESET" after
    ** using this test-control, before it will take full effect.  failing
    ** to reset the schema can result in some unexpected behavior.
    */
    case SQLITE_TESTCTRL_FK_NO_ACTION: {
187128
187129
187130
187131
187132
187133
187134
187135
187136
187137
187138
187139
187140
187141
187142
**   }
**   varint POS_END;        (marks end of positions for this document.
** }
**
** Here, array { X } means zero or more occurrences of X, adjacent in
** memory.  A "position" is an index of a token in the token stream
** generated by the tokenizer. Note that POS_END and POS_COLUMN occur
** in the same logical place as the position element, and act as sentinals
** ending a position list array.  POS_END is 0.  POS_COLUMN is 1.
** The positions numbers are not stored literally but rather as two more
** than the difference from the prior position, or the just the position plus
** 2 for the first position.  Example:
**
**   label:       A B C D E  F  G H   I  J K
**   value:     123 5 9 1 1 14 35 0 234 72 0







|







187845
187846
187847
187848
187849
187850
187851
187852
187853
187854
187855
187856
187857
187858
187859
**   }
**   varint POS_END;        (marks end of positions for this document.
** }
**
** Here, array { X } means zero or more occurrences of X, adjacent in
** memory.  A "position" is an index of a token in the token stream
** generated by the tokenizer. Note that POS_END and POS_COLUMN occur
** in the same logical place as the position element, and act as sentinels
** ending a position list array.  POS_END is 0.  POS_COLUMN is 1.
** The positions numbers are not stored literally but rather as two more
** than the difference from the prior position, or the just the position plus
** 2 for the first position.  Example:
**
**   label:       A B C D E  F  G H   I  J K
**   value:     123 5 9 1 1 14 35 0 234 72 0
187815
187816
187817
187818
187819
187820
187821













187822
187823
187824
187825
187826
187827
187828
# define TESTONLY(X)
#endif

#define LARGEST_INT64  (0xffffffff|(((i64)0x7fffffff)<<32))
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)

#define deliberate_fall_through














#endif /* SQLITE_AMALGAMATION */

#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3Fts3Corrupt(void);
# define FTS_CORRUPT_VTAB sqlite3Fts3Corrupt()
#else







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







188532
188533
188534
188535
188536
188537
188538
188539
188540
188541
188542
188543
188544
188545
188546
188547
188548
188549
188550
188551
188552
188553
188554
188555
188556
188557
188558
# define TESTONLY(X)
#endif

#define LARGEST_INT64  (0xffffffff|(((i64)0x7fffffff)<<32))
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)

#define deliberate_fall_through

/*
** Macros needed to provide flexible arrays in a portable way
*/
#ifndef offsetof
# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
# define FLEXARRAY
#else
# define FLEXARRAY 1
#endif


#endif /* SQLITE_AMALGAMATION */

#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3Fts3Corrupt(void);
# define FTS_CORRUPT_VTAB sqlite3Fts3Corrupt()
#else
187920
187921
187922
187923
187924
187925
187926
187927
187928
187929
187930
187931
187932
187933
187934
  ** verifying the operation of the SQLite core.
  */
  int inTransaction;     /* True after xBegin but before xCommit/xRollback */
  int mxSavepoint;       /* Largest valid xSavepoint integer */
#endif

#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
  /* True to disable the incremental doclist optimization. This is controled
  ** by special insert command 'test-no-incr-doclist'.  */
  int bNoIncrDoclist;

  /* Number of segments in a level */
  int nMergeCount;
#endif
};







|







188650
188651
188652
188653
188654
188655
188656
188657
188658
188659
188660
188661
188662
188663
188664
  ** verifying the operation of the SQLite core.
  */
  int inTransaction;     /* True after xBegin but before xCommit/xRollback */
  int mxSavepoint;       /* Largest valid xSavepoint integer */
#endif

#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
  /* True to disable the incremental doclist optimization. This is controlled
  ** by special insert command 'test-no-incr-doclist'.  */
  int bNoIncrDoclist;

  /* Number of segments in a level */
  int nMergeCount;
#endif
};
187972
187973
187974
187975
187976
187977
187978
187979
187980
187981
187982
187983
187984
187985
187986

#define FTS3_EVAL_FILTER    0
#define FTS3_EVAL_NEXT      1
#define FTS3_EVAL_MATCHINFO 2

/*
** The Fts3Cursor.eSearch member is always set to one of the following.
** Actualy, Fts3Cursor.eSearch can be greater than or equal to
** FTS3_FULLTEXT_SEARCH.  If so, then Fts3Cursor.eSearch - 2 is the index
** of the column to be searched.  For example, in
**
**     CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d);
**     SELECT docid FROM ex1 WHERE b MATCH 'one two three';
**
** Because the LHS of the MATCH operator is 2nd column "b",







|







188702
188703
188704
188705
188706
188707
188708
188709
188710
188711
188712
188713
188714
188715
188716

#define FTS3_EVAL_FILTER    0
#define FTS3_EVAL_NEXT      1
#define FTS3_EVAL_MATCHINFO 2

/*
** The Fts3Cursor.eSearch member is always set to one of the following.
** Actually, Fts3Cursor.eSearch can be greater than or equal to
** FTS3_FULLTEXT_SEARCH.  If so, then Fts3Cursor.eSearch - 2 is the index
** of the column to be searched.  For example, in
**
**     CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d);
**     SELECT docid FROM ex1 WHERE b MATCH 'one two three';
**
** Because the LHS of the MATCH operator is 2nd column "b",
188045
188046
188047
188048
188049
188050
188051
188052
188053




188054
188055
188056
188057
188058
188059
188060
  i64 iOrDocid;

  /* Variables below this point are populated by fts3_expr.c when parsing
  ** a MATCH expression. Everything above is part of the evaluation phase.
  */
  int nToken;                /* Number of tokens in the phrase */
  int iColumn;               /* Index of column this phrase must match */
  Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */
};





/*
** A tree of these objects forms the RHS of a MATCH operator.
**
** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist
** points to a malloced buffer, size nDoclist bytes, containing the results
** of this phrase query in FTS3 doclist format. As usual, the initial







|

>
>
>
>







188775
188776
188777
188778
188779
188780
188781
188782
188783
188784
188785
188786
188787
188788
188789
188790
188791
188792
188793
188794
  i64 iOrDocid;

  /* Variables below this point are populated by fts3_expr.c when parsing
  ** a MATCH expression. Everything above is part of the evaluation phase.
  */
  int nToken;                /* Number of tokens in the phrase */
  int iColumn;               /* Index of column this phrase must match */
  Fts3PhraseToken aToken[FLEXARRAY]; /* One for each token in the phrase */
};

/* Size (in bytes) of an Fts3Phrase object large enough to hold N tokens */
#define SZ_FTS3PHRASE(N) \
  (offsetof(Fts3Phrase,aToken)+(N)*sizeof(Fts3PhraseToken))

/*
** A tree of these objects forms the RHS of a MATCH operator.
**
** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist
** points to a malloced buffer, size nDoclist bytes, containing the results
** of this phrase query in FTS3 doclist format. As usual, the initial
190625
190626
190627
190628
190629
190630
190631
190632
190633
190634
190635
190636
190637
190638
190639
  ** be larger in the output than it was in the input (since the delta value
  ** may be a larger positive integer than the actual docid).
  **
  ** The space required to store the output is therefore the sum of the
  ** sizes of the two inputs, plus enough space for exactly one of the input
  ** docids to grow.
  **
  ** A symetric argument may be made if the doclists are in descending
  ** order.
  */
  aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING);
  if( !aOut ) return SQLITE_NOMEM;

  p = aOut;
  fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1);







|







191359
191360
191361
191362
191363
191364
191365
191366
191367
191368
191369
191370
191371
191372
191373
  ** be larger in the output than it was in the input (since the delta value
  ** may be a larger positive integer than the actual docid).
  **
  ** The space required to store the output is therefore the sum of the
  ** sizes of the two inputs, plus enough space for exactly one of the input
  ** docids to grow.
  **
  ** A symmetric argument may be made if the doclists are in descending
  ** order.
  */
  aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING);
  if( !aOut ) return SQLITE_NOMEM;

  p = aOut;
  fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1);
192723
192724
192725
192726
192727
192728
192729
192730
192731
192732
192733
192734
192735
192736
192737
/*
** The phrase iterator passed as the second argument:
**
**   * features at least one token that uses an incremental doclist, and
**
**   * does not contain any deferred tokens.
**
** Advance it to the next matching documnent in the database and populate
** the Fts3Doclist.pList and nList fields.
**
** If there is no "next" entry and no error occurs, then *pbEof is set to
** 1 before returning. Otherwise, if no error occurs and the iterator is
** successfully advanced, *pbEof is set to 0.
**
** If an error occurs, return an SQLite error code. Otherwise, return







|







193457
193458
193459
193460
193461
193462
193463
193464
193465
193466
193467
193468
193469
193470
193471
/*
** The phrase iterator passed as the second argument:
**
**   * features at least one token that uses an incremental doclist, and
**
**   * does not contain any deferred tokens.
**
** Advance it to the next matching document in the database and populate
** the Fts3Doclist.pList and nList fields.
**
** If there is no "next" entry and no error occurs, then *pbEof is set to
** 1 before returning. Otherwise, if no error occurs and the iterator is
** successfully advanced, *pbEof is set to 0.
**
** If an error occurs, return an SQLite error code. Otherwise, return
193730
193731
193732
193733
193734
193735
193736
193737
193738
193739
193740
193741
193742
193743
193744
    pCsr->isEof = 1;
  }

  return rc;
}

/*
** Restart interation for expression pExpr so that the next call to
** fts3EvalNext() visits the first row. Do not allow incremental
** loading or merging of phrase doclists for this iteration.
**
** If *pRc is other than SQLITE_OK when this function is called, it is
** a no-op. If an error occurs within this function, *pRc is set to an
** SQLite error code before returning.
*/







|







194464
194465
194466
194467
194468
194469
194470
194471
194472
194473
194474
194475
194476
194477
194478
    pCsr->isEof = 1;
  }

  return rc;
}

/*
** Restart iteration for expression pExpr so that the next call to
** fts3EvalNext() visits the first row. Do not allow incremental
** loading or merging of phrase doclists for this iteration.
**
** If *pRc is other than SQLITE_OK when this function is called, it is
** a no-op. If an error occurs within this function, *pRc is set to an
** SQLite error code before returning.
*/
194921
194922
194923
194924
194925
194926
194927

















194928
194929
194930
194931
194932
194933
194934
}

/*
** Function getNextNode(), which is called by fts3ExprParse(), may itself
** call fts3ExprParse(). So this forward declaration is required.
*/
static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *);


















/*
** Extract the next token from buffer z (length n) using the tokenizer
** and other information (column names etc.) in pParse. Create an Fts3Expr
** structure of type FTSQUERY_PHRASE containing a phrase consisting of this
** single token and set *ppExpr to point to it. If the end of the buffer is
** reached before a token is found, set *ppExpr to zero. It is the







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







195655
195656
195657
195658
195659
195660
195661
195662
195663
195664
195665
195666
195667
195668
195669
195670
195671
195672
195673
195674
195675
195676
195677
195678
195679
195680
195681
195682
195683
195684
195685
}

/*
** Function getNextNode(), which is called by fts3ExprParse(), may itself
** call fts3ExprParse(). So this forward declaration is required.
*/
static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *);

/*
** Search buffer z[], size n, for a '"' character. Or, if enable_parenthesis
** is defined, search for '(' and ')' as well. Return the index of the first
** such character in the buffer. If there is no such character, return -1.
*/
static int findBarredChar(const char *z, int n){
  int ii;
  for(ii=0; ii<n; ii++){
    if( (z[ii]=='"')
     || (sqlite3_fts3_enable_parentheses && (z[ii]=='(' || z[ii]==')'))
    ){
      return ii;
    }
  }
  return -1;
}

/*
** Extract the next token from buffer z (length n) using the tokenizer
** and other information (column names etc.) in pParse. Create an Fts3Expr
** structure of type FTSQUERY_PHRASE containing a phrase consisting of this
** single token and set *ppExpr to point to it. If the end of the buffer is
** reached before a token is found, set *ppExpr to zero. It is the
194946
194947
194948
194949
194950
194951
194952
194953
194954
194955
194956
194957
194958
194959
194960
194961
194962
194963
194964
194965
194966
194967
194968
194969











194970
194971
194972
194973
194974
194975
194976
194977
194978
194979
194980
194981
194982
194983
194984
194985
194986
194987
  int *pnConsumed                         /* OUT: Number of bytes consumed */
){
  sqlite3_tokenizer *pTokenizer = pParse->pTokenizer;
  sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
  int rc;
  sqlite3_tokenizer_cursor *pCursor;
  Fts3Expr *pRet = 0;
  int i = 0;

  /* Set variable i to the maximum number of bytes of input to tokenize. */
  for(i=0; i<n; i++){
    if( sqlite3_fts3_enable_parentheses && (z[i]=='(' || z[i]==')') ) break;
    if( z[i]=='"' ) break;
  }

  *pnConsumed = i;
  rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, i, &pCursor);
  if( rc==SQLITE_OK ){
    const char *zToken;
    int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0;
    sqlite3_int64 nByte;                    /* total space to allocate */

    rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition);
    if( rc==SQLITE_OK ){











      nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken;
      pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte);
      if( !pRet ){
        rc = SQLITE_NOMEM;
      }else{
        pRet->eType = FTSQUERY_PHRASE;
        pRet->pPhrase = (Fts3Phrase *)&pRet[1];
        pRet->pPhrase->nToken = 1;
        pRet->pPhrase->iColumn = iCol;
        pRet->pPhrase->aToken[0].n = nToken;
        pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1];
        memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken);

        if( iEnd<n && z[iEnd]=='*' ){
          pRet->pPhrase->aToken[0].isPrefix = 1;
          iEnd++;
        }








<

<
<
<
<
<
<
|
|







>
>
>
>
>
>
>
>
>
>
>
|









|







195697
195698
195699
195700
195701
195702
195703

195704






195705
195706
195707
195708
195709
195710
195711
195712
195713
195714
195715
195716
195717
195718
195719
195720
195721
195722
195723
195724
195725
195726
195727
195728
195729
195730
195731
195732
195733
195734
195735
195736
195737
195738
195739
195740
195741
195742
  int *pnConsumed                         /* OUT: Number of bytes consumed */
){
  sqlite3_tokenizer *pTokenizer = pParse->pTokenizer;
  sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
  int rc;
  sqlite3_tokenizer_cursor *pCursor;
  Fts3Expr *pRet = 0;








  *pnConsumed = n;
  rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor);
  if( rc==SQLITE_OK ){
    const char *zToken;
    int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0;
    sqlite3_int64 nByte;                    /* total space to allocate */

    rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition);
    if( rc==SQLITE_OK ){
      /* Check that this tokenization did not gobble up any " characters. Or,
      ** if enable_parenthesis is true, that it did not gobble up any
      ** open or close parenthesis characters either. If it did, call
      ** getNextToken() again, but pass only that part of the input buffer
      ** up to the first such character.  */
      int iBarred = findBarredChar(z, iEnd);
      if( iBarred>=0 ){
        pModule->xClose(pCursor);
        return getNextToken(pParse, iCol, z, iBarred, ppExpr, pnConsumed);
      }

      nByte = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1) + nToken;
      pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte);
      if( !pRet ){
        rc = SQLITE_NOMEM;
      }else{
        pRet->eType = FTSQUERY_PHRASE;
        pRet->pPhrase = (Fts3Phrase *)&pRet[1];
        pRet->pPhrase->nToken = 1;
        pRet->pPhrase->iColumn = iCol;
        pRet->pPhrase->aToken[0].n = nToken;
        pRet->pPhrase->aToken[0].z = (char*)&pRet->pPhrase->aToken[1];
        memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken);

        if( iEnd<n && z[iEnd]=='*' ){
          pRet->pPhrase->aToken[0].isPrefix = 1;
          iEnd++;
        }

194997
194998
194999
195000
195001
195002
195003
195004




195005
195006
195007
195008
195009
195010
195011
          }else{
            break;
          }
        }

      }
      *pnConsumed = iEnd;
    }else if( i && rc==SQLITE_DONE ){




      rc = SQLITE_OK;
    }

    pModule->xClose(pCursor);
  }

  *ppExpr = pRet;







|
>
>
>
>







195752
195753
195754
195755
195756
195757
195758
195759
195760
195761
195762
195763
195764
195765
195766
195767
195768
195769
195770
          }else{
            break;
          }
        }

      }
      *pnConsumed = iEnd;
    }else if( n && rc==SQLITE_DONE ){
      int iBarred = findBarredChar(z, n);
      if( iBarred>=0 ){
        *pnConsumed = iBarred;
      }
      rc = SQLITE_OK;
    }

    pModule->xClose(pCursor);
  }

  *ppExpr = pRet;
195046
195047
195048
195049
195050
195051
195052
195053
195054
195055
195056
195057
195058
195059
195060
  sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
  int rc;
  Fts3Expr *p = 0;
  sqlite3_tokenizer_cursor *pCursor = 0;
  char *zTemp = 0;
  i64 nTemp = 0;

  const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
  int nToken = 0;

  /* The final Fts3Expr data structure, including the Fts3Phrase,
  ** Fts3PhraseToken structures token buffers are all stored as a single
  ** allocation so that the expression can be freed with a single call to
  ** sqlite3_free(). Setting this up requires a two pass approach.
  **







|







195805
195806
195807
195808
195809
195810
195811
195812
195813
195814
195815
195816
195817
195818
195819
  sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
  int rc;
  Fts3Expr *p = 0;
  sqlite3_tokenizer_cursor *pCursor = 0;
  char *zTemp = 0;
  i64 nTemp = 0;

  const int nSpace = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1);
  int nToken = 0;

  /* The final Fts3Expr data structure, including the Fts3Phrase,
  ** Fts3PhraseToken structures token buffers are all stored as a single
  ** allocation so that the expression can be freed with a single call to
  ** sqlite3_free(). Setting this up requires a two pass approach.
  **
195418
195419
195420
195421
195422
195423
195424
195425
195426
195427
195428
195429
195430
195431
195432
          p = pPrev;
        }else{
          int eType = p->eType;
          isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);

          /* The isRequirePhrase variable is set to true if a phrase or
          ** an expression contained in parenthesis is required. If a
          ** binary operator (AND, OR, NOT or NEAR) is encounted when
          ** isRequirePhrase is set, this is a syntax error.
          */
          if( !isPhrase && isRequirePhrase ){
            sqlite3Fts3ExprFree(p);
            rc = SQLITE_ERROR;
            goto exprparse_out;
          }







|







196177
196178
196179
196180
196181
196182
196183
196184
196185
196186
196187
196188
196189
196190
196191
          p = pPrev;
        }else{
          int eType = p->eType;
          isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);

          /* The isRequirePhrase variable is set to true if a phrase or
          ** an expression contained in parenthesis is required. If a
          ** binary operator (AND, OR, NOT or NEAR) is encountered when
          ** isRequirePhrase is set, this is a syntax error.
          */
          if( !isPhrase && isRequirePhrase ){
            sqlite3Fts3ExprFree(p);
            rc = SQLITE_ERROR;
            goto exprparse_out;
          }
196000
196001
196002
196003
196004
196005
196006
196007
196008
196009
196010
196011
196012
196013
196014
  }else{
    rc = fts3ExprParseUnbalanced(
        pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr
    );
  }

  if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
    sqlite3Fts3ExprFree(pExpr);
    sqlite3_result_error(context, "Error parsing expression", -1);
  }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){
    sqlite3_result_error_nomem(context);
  }else{
    sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
    sqlite3_free(zBuf);
  }







<







196759
196760
196761
196762
196763
196764
196765

196766
196767
196768
196769
196770
196771
196772
  }else{
    rc = fts3ExprParseUnbalanced(
        pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr
    );
  }

  if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){

    sqlite3_result_error(context, "Error parsing expression", -1);
  }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){
    sqlite3_result_error_nomem(context);
  }else{
    sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
    sqlite3_free(zBuf);
  }
196243
196244
196245
196246
196247
196248
196249
196250
196251
196252
196253
196254
196255
196256
196257
    pH->first = pNew;
  }
  pEntry->count++;
  pEntry->chain = pNew;
}


/* Resize the hash table so that it cantains "new_size" buckets.
** "new_size" must be a power of 2.  The hash table might fail
** to resize if sqliteMalloc() fails.
**
** Return non-zero if a memory allocation error occurs.
*/
static int fts3Rehash(Fts3Hash *pH, int new_size){
  struct _fts3ht *new_ht;          /* The new hash table */







|







197001
197002
197003
197004
197005
197006
197007
197008
197009
197010
197011
197012
197013
197014
197015
    pH->first = pNew;
  }
  pEntry->count++;
  pEntry->chain = pNew;
}


/* Resize the hash table so that it contains "new_size" buckets.
** "new_size" must be a power of 2.  The hash table might fail
** to resize if sqliteMalloc() fails.
**
** Return non-zero if a memory allocation error occurs.
*/
static int fts3Rehash(Fts3Hash *pH, int new_size){
  struct _fts3ht *new_ht;          /* The new hash table */
196698
196699
196700
196701
196702
196703
196704
196705
196706
196707
196708
196709
196710
196711
196712
    z[0]!='w' && z[0]!='x' && z[0]!='y' &&
    isVowel(z+1) &&
    isConsonant(z+2);
}

/*
** If the word ends with zFrom and xCond() is true for the stem
** of the word that preceeds the zFrom ending, then change the
** ending to zTo.
**
** The input word *pz and zFrom are both in reverse order.  zTo
** is in normal order.
**
** Return TRUE if zFrom matches.  Return FALSE if zFrom does not
** match.  Not that TRUE is returned even if xCond() fails and







|







197456
197457
197458
197459
197460
197461
197462
197463
197464
197465
197466
197467
197468
197469
197470
    z[0]!='w' && z[0]!='x' && z[0]!='y' &&
    isVowel(z+1) &&
    isConsonant(z+2);
}

/*
** If the word ends with zFrom and xCond() is true for the stem
** of the word that precedes the zFrom ending, then change the
** ending to zTo.
**
** The input word *pz and zFrom are both in reverse order.  zTo
** is in normal order.
**
** Return TRUE if zFrom matches.  Return FALSE if zFrom does not
** match.  Not that TRUE is returned even if xCond() fails and
202281
202282
202283
202284
202285
202286
202287
202288
202289
202290
202291
202292
202293
202294
202295
** If the size of the value in blob pPrev is zero, then this is the first
** term written to the node. Otherwise, pPrev contains a copy of the
** previous term. Before this function returns, it is updated to contain a
** copy of zTerm/nTerm.
**
** It is assumed that the buffer associated with pNode is already large
** enough to accommodate the new entry. The buffer associated with pPrev
** is extended by this function if requrired.
**
** If an error (i.e. OOM condition) occurs, an SQLite error code is
** returned. Otherwise, SQLITE_OK.
*/
static int fts3AppendToNode(
  Blob *pNode,                    /* Current node image to append to */
  Blob *pPrev,                    /* Buffer containing previous term written */







|







203039
203040
203041
203042
203043
203044
203045
203046
203047
203048
203049
203050
203051
203052
203053
** If the size of the value in blob pPrev is zero, then this is the first
** term written to the node. Otherwise, pPrev contains a copy of the
** previous term. Before this function returns, it is updated to contain a
** copy of zTerm/nTerm.
**
** It is assumed that the buffer associated with pNode is already large
** enough to accommodate the new entry. The buffer associated with pPrev
** is extended by this function if required.
**
** If an error (i.e. OOM condition) occurs, an SQLite error code is
** returned. Otherwise, SQLITE_OK.
*/
static int fts3AppendToNode(
  Blob *pNode,                    /* Current node image to append to */
  Blob *pPrev,                    /* Buffer containing previous term written */
203944
203945
203946
203947
203948
203949
203950
203951
203952
203953
203954
203955
203956
203957
203958
  return SQLITE_OK;
}
#endif

/*
** SQLite value pRowid contains the rowid of a row that may or may not be
** present in the FTS3 table. If it is, delete it and adjust the contents
** of subsiduary data structures accordingly.
*/
static int fts3DeleteByRowid(
  Fts3Table *p,
  sqlite3_value *pRowid,
  int *pnChng,                    /* IN/OUT: Decrement if row is deleted */
  u32 *aSzDel
){







|







204702
204703
204704
204705
204706
204707
204708
204709
204710
204711
204712
204713
204714
204715
204716
  return SQLITE_OK;
}
#endif

/*
** SQLite value pRowid contains the rowid of a row that may or may not be
** present in the FTS3 table. If it is, delete it and adjust the contents
** of subsidiary data structures accordingly.
*/
static int fts3DeleteByRowid(
  Fts3Table *p,
  sqlite3_value *pRowid,
  int *pnChng,                    /* IN/OUT: Decrement if row is deleted */
  u32 *aSzDel
){
204270
204271
204272
204273
204274
204275
204276
204277
204278




204279
204280
204281
204282
204283
204284
204285
** for details.
*/
struct MatchinfoBuffer {
  u8 aRef[3];
  int nElem;
  int bGlobal;                    /* Set if global data is loaded */
  char *zMatchinfo;
  u32 aMatchinfo[1];
};






/*
** The snippet() and offsets() functions both return text values. An instance
** of the following structure is used to accumulate those values while the
** functions are running. See fts3StringAppend() for details.
*/







|

>
>
>
>







205028
205029
205030
205031
205032
205033
205034
205035
205036
205037
205038
205039
205040
205041
205042
205043
205044
205045
205046
205047
** for details.
*/
struct MatchinfoBuffer {
  u8 aRef[3];
  int nElem;
  int bGlobal;                    /* Set if global data is loaded */
  char *zMatchinfo;
  u32 aMI[FLEXARRAY];
};

/* Size (in bytes) of a MatchinfoBuffer sufficient for N elements */
#define SZ_MATCHINFOBUFFER(N) \
            (offsetof(MatchinfoBuffer,aMI)+(((N)+1)/2)*sizeof(u64))


/*
** The snippet() and offsets() functions both return text values. An instance
** of the following structure is used to accumulate those values while the
** functions are running. See fts3StringAppend() for details.
*/
204297
204298
204299
204300
204301
204302
204303
204304
204305
204306
204307
204308
204309
204310
204311
204312
204313
204314
204315
204316
204317
204318
204319
204320
204321
204322
204323
204324
204325
204326
204327
204328
204329
204330
204331
204332
204333
204334
204335
204336
204337
204338
204339
204340
204341
204342
204343
204344
204345
204346
204347
204348
204349
204350
204351
204352
204353
204354
204355
204356
204357
204358
204359
204360
204361
204362
204363
204364
204365
204366
204367
204368
204369
204370
204371
204372

/*
** Allocate a two-slot MatchinfoBuffer object.
*/
static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){
  MatchinfoBuffer *pRet;
  sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1)
                           + sizeof(MatchinfoBuffer);
  sqlite3_int64 nStr = strlen(zMatchinfo);

  pRet = sqlite3Fts3MallocZero(nByte + nStr+1);
  if( pRet ){
    pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet;
    pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0]
                                      + sizeof(u32)*((int)nElem+1);
    pRet->nElem = (int)nElem;
    pRet->zMatchinfo = ((char*)pRet) + nByte;
    memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1);
    pRet->aRef[0] = 1;
  }

  return pRet;
}

static void fts3MIBufferFree(void *p){
  MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]);

  assert( (u32*)p==&pBuf->aMatchinfo[1]
       || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2]
  );
  if( (u32*)p==&pBuf->aMatchinfo[1] ){
    pBuf->aRef[1] = 0;
  }else{
    pBuf->aRef[2] = 0;
  }

  if( pBuf->aRef[0]==0 && pBuf->aRef[1]==0 && pBuf->aRef[2]==0 ){
    sqlite3_free(pBuf);
  }
}

static void (*fts3MIBufferAlloc(MatchinfoBuffer *p, u32 **paOut))(void*){
  void (*xRet)(void*) = 0;
  u32 *aOut = 0;

  if( p->aRef[1]==0 ){
    p->aRef[1] = 1;
    aOut = &p->aMatchinfo[1];
    xRet = fts3MIBufferFree;
  }
  else if( p->aRef[2]==0 ){
    p->aRef[2] = 1;
    aOut = &p->aMatchinfo[p->nElem+2];
    xRet = fts3MIBufferFree;
  }else{
    aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32));
    if( aOut ){
      xRet = sqlite3_free;
      if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32));
    }
  }

  *paOut = aOut;
  return xRet;
}

static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){
  p->bGlobal = 1;
  memcpy(&p->aMatchinfo[2+p->nElem], &p->aMatchinfo[1], p->nElem*sizeof(u32));
}

/*
** Free a MatchinfoBuffer object allocated using fts3MIBufferNew()
*/
SQLITE_PRIVATE void sqlite3Fts3MIBufferFree(MatchinfoBuffer *p){
  if( p ){







|




|
|













|
|

|
















|




|





|









|







205059
205060
205061
205062
205063
205064
205065
205066
205067
205068
205069
205070
205071
205072
205073
205074
205075
205076
205077
205078
205079
205080
205081
205082
205083
205084
205085
205086
205087
205088
205089
205090
205091
205092
205093
205094
205095
205096
205097
205098
205099
205100
205101
205102
205103
205104
205105
205106
205107
205108
205109
205110
205111
205112
205113
205114
205115
205116
205117
205118
205119
205120
205121
205122
205123
205124
205125
205126
205127
205128
205129
205130
205131
205132
205133
205134

/*
** Allocate a two-slot MatchinfoBuffer object.
*/
static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){
  MatchinfoBuffer *pRet;
  sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1)
                           + SZ_MATCHINFOBUFFER(1);
  sqlite3_int64 nStr = strlen(zMatchinfo);

  pRet = sqlite3Fts3MallocZero(nByte + nStr+1);
  if( pRet ){
    pRet->aMI[0] = (u8*)(&pRet->aMI[1]) - (u8*)pRet;
    pRet->aMI[1+nElem] = pRet->aMI[0]
                                      + sizeof(u32)*((int)nElem+1);
    pRet->nElem = (int)nElem;
    pRet->zMatchinfo = ((char*)pRet) + nByte;
    memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1);
    pRet->aRef[0] = 1;
  }

  return pRet;
}

static void fts3MIBufferFree(void *p){
  MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]);

  assert( (u32*)p==&pBuf->aMI[1]
       || (u32*)p==&pBuf->aMI[pBuf->nElem+2]
  );
  if( (u32*)p==&pBuf->aMI[1] ){
    pBuf->aRef[1] = 0;
  }else{
    pBuf->aRef[2] = 0;
  }

  if( pBuf->aRef[0]==0 && pBuf->aRef[1]==0 && pBuf->aRef[2]==0 ){
    sqlite3_free(pBuf);
  }
}

static void (*fts3MIBufferAlloc(MatchinfoBuffer *p, u32 **paOut))(void*){
  void (*xRet)(void*) = 0;
  u32 *aOut = 0;

  if( p->aRef[1]==0 ){
    p->aRef[1] = 1;
    aOut = &p->aMI[1];
    xRet = fts3MIBufferFree;
  }
  else if( p->aRef[2]==0 ){
    p->aRef[2] = 1;
    aOut = &p->aMI[p->nElem+2];
    xRet = fts3MIBufferFree;
  }else{
    aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32));
    if( aOut ){
      xRet = sqlite3_free;
      if( p->bGlobal ) memcpy(aOut, &p->aMI[1], p->nElem*sizeof(u32));
    }
  }

  *paOut = aOut;
  return xRet;
}

static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){
  p->bGlobal = 1;
  memcpy(&p->aMI[2+p->nElem], &p->aMI[1], p->nElem*sizeof(u32));
}

/*
** Free a MatchinfoBuffer object allocated using fts3MIBufferNew()
*/
SQLITE_PRIVATE void sqlite3Fts3MIBufferFree(MatchinfoBuffer *p){
  if( p ){
204773
204774
204775
204776
204777
204778
204779
204780
204781
204782
204783
204784
204785
204786
204787
  int nAppend                     /* Size of zAppend in bytes (or -1) */
){
  if( nAppend<0 ){
    nAppend = (int)strlen(zAppend);
  }

  /* If there is insufficient space allocated at StrBuffer.z, use realloc()
  ** to grow the buffer until so that it is big enough to accomadate the
  ** appended data.
  */
  if( pStr->n+nAppend+1>=pStr->nAlloc ){
    sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100;
    char *zNew = sqlite3_realloc64(pStr->z, nAlloc);
    if( !zNew ){
      return SQLITE_NOMEM;







|







205535
205536
205537
205538
205539
205540
205541
205542
205543
205544
205545
205546
205547
205548
205549
  int nAppend                     /* Size of zAppend in bytes (or -1) */
){
  if( nAppend<0 ){
    nAppend = (int)strlen(zAppend);
  }

  /* If there is insufficient space allocated at StrBuffer.z, use realloc()
  ** to grow the buffer until so that it is big enough to accommodate the
  ** appended data.
  */
  if( pStr->n+nAppend+1>=pStr->nAlloc ){
    sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100;
    char *zNew = sqlite3_realloc64(pStr->z, nAlloc);
    if( !zNew ){
      return SQLITE_NOMEM;
207148
207149
207150
207151
207152
207153
207154
207155
207156
207157
207158
207159
207160
207161
207162
** Search for a cached translation the json text supplied by pArg.  Return
** the JsonParse object if found.  Return NULL if not found.
**
** When a match if found, the matching entry is moved to become the
** most-recently used entry if it isn't so already.
**
** The JsonParse object returned still belongs to the Cache and might
** be deleted at any moment.  If the caller whants the JsonParse to
** linger, it needs to increment the nPJRef reference counter.
*/
static JsonParse *jsonCacheSearch(
  sqlite3_context *ctx,    /* The SQL statement context holding the cache */
  sqlite3_value *pArg      /* Function argument containing SQL text */
){
  JsonCache *p;







|







207910
207911
207912
207913
207914
207915
207916
207917
207918
207919
207920
207921
207922
207923
207924
** Search for a cached translation the json text supplied by pArg.  Return
** the JsonParse object if found.  Return NULL if not found.
**
** When a match if found, the matching entry is moved to become the
** most-recently used entry if it isn't so already.
**
** The JsonParse object returned still belongs to the Cache and might
** be deleted at any moment.  If the caller wants the JsonParse to
** linger, it needs to increment the nPJRef reference counter.
*/
static JsonParse *jsonCacheSearch(
  sqlite3_context *ctx,    /* The SQL statement context holding the cache */
  sqlite3_value *pArg      /* Function argument containing SQL text */
){
  JsonCache *p;
210193
210194
210195
210196
210197
210198
210199
210200
210201
210202
210203
210204
210205
210206
210207
    /* If the blob is not valid JSONB, fall through into trying to cast
    ** the blob into text which is then interpreted as JSON.  (tag-20240123-a)
    **
    ** This goes against all historical documentation about how the SQLite
    ** JSON functions were suppose to work.  From the beginning, blob was
    ** reserved for expansion and a blob value should have raised an error.
    ** But it did not, due to a bug.  And many applications came to depend
    ** upon this buggy behavior, espeically when using the CLI and reading
    ** JSON text using readfile(), which returns a blob.  For this reason
    ** we will continue to support the bug moving forward.
    ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
    */
  }
  p->zJson = (char*)sqlite3_value_text(pArg);
  p->nJson = sqlite3_value_bytes(pArg);







|







210955
210956
210957
210958
210959
210960
210961
210962
210963
210964
210965
210966
210967
210968
210969
    /* If the blob is not valid JSONB, fall through into trying to cast
    ** the blob into text which is then interpreted as JSON.  (tag-20240123-a)
    **
    ** This goes against all historical documentation about how the SQLite
    ** JSON functions were suppose to work.  From the beginning, blob was
    ** reserved for expansion and a blob value should have raised an error.
    ** But it did not, due to a bug.  And many applications came to depend
    ** upon this buggy behavior, especially when using the CLI and reading
    ** JSON text using readfile(), which returns a blob.  For this reason
    ** we will continue to support the bug moving forward.
    ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
    */
  }
  p->zJson = (char*)sqlite3_value_text(pArg);
  p->nJson = sqlite3_value_bytes(pArg);
212293
212294
212295
212296
212297
212298
212299








212300
212301
212302
212303
212304
212305
212306
#elif !defined(NDEBUG)
# define ALWAYS(X)      ((X)?1:(assert(0),0))
# define NEVER(X)       ((X)?(assert(0),1):0)
#else
# define ALWAYS(X)      (X)
# define NEVER(X)       (X)
#endif








#endif /* !defined(SQLITE_AMALGAMATION) */

/* Macro to check for 4-byte alignment.  Only used inside of assert() */
#ifdef SQLITE_DEBUG
# define FOUR_BYTE_ALIGNED(X)  ((((char*)(X) - (char*)0) & 3)==0)
#endif








>
>
>
>
>
>
>
>







213055
213056
213057
213058
213059
213060
213061
213062
213063
213064
213065
213066
213067
213068
213069
213070
213071
213072
213073
213074
213075
213076
#elif !defined(NDEBUG)
# define ALWAYS(X)      ((X)?1:(assert(0),0))
# define NEVER(X)       ((X)?(assert(0),1):0)
#else
# define ALWAYS(X)      (X)
# define NEVER(X)       (X)
#endif
#ifndef offsetof
#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
# define FLEXARRAY
#else
# define FLEXARRAY 1
#endif
#endif /* !defined(SQLITE_AMALGAMATION) */

/* Macro to check for 4-byte alignment.  Only used inside of assert() */
#ifdef SQLITE_DEBUG
# define FOUR_BYTE_ALIGNED(X)  ((((char*)(X) - (char*)0) & 3)==0)
#endif

212613
212614
212615
212616
212617
212618
212619
212620
212621
212622




212623
212624
212625
212626
212627
212628
212629
** operand to the MATCH operator of an R-Tree.
*/
struct RtreeMatchArg {
  u32 iSize;                  /* Size of this object */
  RtreeGeomCallback cb;       /* Info about the callback functions */
  int nParam;                 /* Number of parameters to the SQL function */
  sqlite3_value **apSqlParam; /* Original SQL parameter values */
  RtreeDValue aParam[1];      /* Values for parameters to the SQL function */
};





#ifndef MAX
# define MAX(x,y) ((x) < (y) ? (y) : (x))
#endif
#ifndef MIN
# define MIN(x,y) ((x) > (y) ? (y) : (x))
#endif








|


>
>
>
>







213383
213384
213385
213386
213387
213388
213389
213390
213391
213392
213393
213394
213395
213396
213397
213398
213399
213400
213401
213402
213403
** operand to the MATCH operator of an R-Tree.
*/
struct RtreeMatchArg {
  u32 iSize;                  /* Size of this object */
  RtreeGeomCallback cb;       /* Info about the callback functions */
  int nParam;                 /* Number of parameters to the SQL function */
  sqlite3_value **apSqlParam; /* Original SQL parameter values */
  RtreeDValue aParam[FLEXARRAY]; /* Values for parameters to the SQL function */
};

/* Size of an RtreeMatchArg object with N parameters */
#define SZ_RTREEMATCHARG(N)  \
        (offsetof(RtreeMatchArg,aParam)+(N)*sizeof(RtreeDValue))

#ifndef MAX
# define MAX(x,y) ((x) < (y) ? (y) : (x))
#endif
#ifndef MIN
# define MIN(x,y) ((x) > (y) ? (y) : (x))
#endif

214304
214305
214306
214307
214308
214309
214310
214311
214312
214313
214314
214315
214316
214317
214318
  pIdxInfo->estimatedCost = (double)6.0 * (double)nRow;
  pIdxInfo->estimatedRows = nRow;

  return rc;
}

/*
** Return the N-dimensional volumn of the cell stored in *p.
*/
static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
  RtreeDValue area = (RtreeDValue)1;
  assert( pRtree->nDim>=1 && pRtree->nDim<=5 );
#ifndef SQLITE_RTREE_INT_ONLY
  if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
    switch( pRtree->nDim ){







|







215078
215079
215080
215081
215082
215083
215084
215085
215086
215087
215088
215089
215090
215091
215092
  pIdxInfo->estimatedCost = (double)6.0 * (double)nRow;
  pIdxInfo->estimatedRows = nRow;

  return rc;
}

/*
** Return the N-dimensional volume of the cell stored in *p.
*/
static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
  RtreeDValue area = (RtreeDValue)1;
  assert( pRtree->nDim>=1 && pRtree->nDim<=5 );
#ifndef SQLITE_RTREE_INT_ONLY
  if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
    switch( pRtree->nDim ){
216070
216071
216072
216073
216074
216075
216076
216077
216078
216079
216080
216081
216082
216083
216084
  va_end(ap);
  return pRet;
}

/*
** The second and subsequent arguments to this function are a printf()
** style format string and arguments. This function formats the string and
** appends it to the report being accumuated in pCheck.
*/
static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){
  va_list ap;
  va_start(ap, zFmt);
  if( pCheck->rc==SQLITE_OK && pCheck->nErr<RTREE_CHECK_MAX_ERROR ){
    char *z = sqlite3_vmprintf(zFmt, ap);
    if( z==0 ){







|







216844
216845
216846
216847
216848
216849
216850
216851
216852
216853
216854
216855
216856
216857
216858
  va_end(ap);
  return pRet;
}

/*
** The second and subsequent arguments to this function are a printf()
** style format string and arguments. This function formats the string and
** appends it to the report being accumulated in pCheck.
*/
static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){
  va_list ap;
  va_start(ap, zFmt);
  if( pCheck->rc==SQLITE_OK && pCheck->nErr<RTREE_CHECK_MAX_ERROR ){
    char *z = sqlite3_vmprintf(zFmt, ap);
    if( z==0 ){
217258
217259
217260
217261
217262
217263
217264
217265
217266
217267
217268
217269
217270
217271
217272
}


/*
** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2).
** Returns:
**
**    +2  x0,y0 is on the line segement
**
**    +1  x0,y0 is beneath line segment
**
**    0   x0,y0 is not on or beneath the line segment or the line segment
**        is vertical and x0,y0 is not on the line segment
**
** The left-most coordinate min(x1,x2) is not considered to be part of







|







218032
218033
218034
218035
218036
218037
218038
218039
218040
218041
218042
218043
218044
218045
218046
}


/*
** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2).
** Returns:
**
**    +2  x0,y0 is on the line segment
**
**    +1  x0,y0 is beneath line segment
**
**    0   x0,y0 is not on or beneath the line segment or the line segment
**        is vertical and x0,y0 is not on the line segment
**
** The left-most coordinate min(x1,x2) is not considered to be part of
217364
217365
217366
217367
217368
217369
217370
217371
217372
217373
217374
217375
217376
217377
217378
      sqlite3_result_int(context, x==2 ? 1 : x==4 ? 2 : 0);
    }
  }
  sqlite3_free(p1);
  sqlite3_free(p2);
}

/* Objects used by the overlap algorihm. */
typedef struct GeoEvent GeoEvent;
typedef struct GeoSegment GeoSegment;
typedef struct GeoOverlap GeoOverlap;
struct GeoEvent {
  double x;              /* X coordinate at which event occurs */
  int eType;             /* 0 for ADD, 1 for REMOVE */
  GeoSegment *pSeg;      /* The segment to be added or removed */







|







218138
218139
218140
218141
218142
218143
218144
218145
218146
218147
218148
218149
218150
218151
218152
      sqlite3_result_int(context, x==2 ? 1 : x==4 ? 2 : 0);
    }
  }
  sqlite3_free(p1);
  sqlite3_free(p2);
}

/* Objects used by the overlap algorithm. */
typedef struct GeoEvent GeoEvent;
typedef struct GeoSegment GeoSegment;
typedef struct GeoOverlap GeoOverlap;
struct GeoEvent {
  double x;              /* X coordinate at which event occurs */
  int eType;             /* 0 for ADD, 1 for REMOVE */
  GeoSegment *pSeg;      /* The segment to be added or removed */
218411
218412
218413
218414
218415
218416
218417
218418
218419
218420
218421
218422
218423
218424
218425
218426
*/
static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
  RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx);
  RtreeMatchArg *pBlob;
  sqlite3_int64 nBlob;
  int memErr = 0;

  nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue)
           + nArg*sizeof(sqlite3_value*);
  pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob);
  if( !pBlob ){
    sqlite3_result_error_nomem(ctx);
  }else{
    int i;
    pBlob->iSize = nBlob;
    pBlob->cb = pGeomCtx[0];







<
|







219185
219186
219187
219188
219189
219190
219191

219192
219193
219194
219195
219196
219197
219198
219199
*/
static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
  RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx);
  RtreeMatchArg *pBlob;
  sqlite3_int64 nBlob;
  int memErr = 0;


  nBlob = SZ_RTREEMATCHARG(nArg) + nArg*sizeof(sqlite3_value*);
  pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob);
  if( !pBlob ){
    sqlite3_result_error_nomem(ctx);
  }else{
    int i;
    pBlob->iSize = nBlob;
    pBlob->cb = pGeomCtx[0];
219507
219508
219509
219510
219511
219512
219513
219514
219515
219516
219517
219518
219519
219520
219521
** mobile device that is frequently rebooted. Even after the writer process
** has committed one or more sub-transactions, other database clients continue
** to read from the original database snapshot. In other words, partially
** applied transactions are not visible to other clients.
**
** "RBU" stands for "Resumable Bulk Update". As in a large database update
** transmitted via a wireless network to a mobile device. A transaction
** applied using this extension is hence refered to as an "RBU update".
**
**
** LIMITATIONS
**
** An "RBU update" transaction is subject to the following limitations:
**
**   * The transaction must consist of INSERT, UPDATE and DELETE operations







|







220280
220281
220282
220283
220284
220285
220286
220287
220288
220289
220290
220291
220292
220293
220294
** mobile device that is frequently rebooted. Even after the writer process
** has committed one or more sub-transactions, other database clients continue
** to read from the original database snapshot. In other words, partially
** applied transactions are not visible to other clients.
**
** "RBU" stands for "Resumable Bulk Update". As in a large database update
** transmitted via a wireless network to a mobile device. A transaction
** applied using this extension is hence referred to as an "RBU update".
**
**
** LIMITATIONS
**
** An "RBU update" transaction is subject to the following limitations:
**
**   * The transaction must consist of INSERT, UPDATE and DELETE operations
219804
219805
219806
219807
219808
219809
219810
219811
219812
219813
219814
219815
219816
219817
219818
** This function does not delete the state database after an RBU vacuum
** is completed, even if it created it. However, if the call to
** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
** of the state tables within the state database are zeroed. This way,
** the next call to sqlite3rbu_vacuum() opens a handle that starts a
** new RBU vacuum operation.
**
** As with sqlite3rbu_open(), Zipvfs users should rever to the comment
** describing the sqlite3rbu_create_vfs() API function below for
** a description of the complications associated with using RBU with
** zipvfs databases.
*/
SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
  const char *zTarget,
  const char *zState







|







220577
220578
220579
220580
220581
220582
220583
220584
220585
220586
220587
220588
220589
220590
220591
** This function does not delete the state database after an RBU vacuum
** is completed, even if it created it. However, if the call to
** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
** of the state tables within the state database are zeroed. This way,
** the next call to sqlite3rbu_vacuum() opens a handle that starts a
** new RBU vacuum operation.
**
** As with sqlite3rbu_open(), Zipvfs users should refer to the comment
** describing the sqlite3rbu_create_vfs() API function below for
** a description of the complications associated with using RBU with
** zipvfs databases.
*/
SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
  const char *zTarget,
  const char *zState
219900
219901
219902
219903
219904
219905
219906
219907
219908
219909
219910
219911
219912
219913
219914
SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *pRbu);

/*
** Close an RBU handle.
**
** If the RBU update has been completely applied, mark the RBU database
** as fully applied. Otherwise, assuming no error has occurred, save the
** current state of the RBU update appliation to the RBU database.
**
** If an error has already occurred as part of an sqlite3rbu_step()
** or sqlite3rbu_open() call, or if one occurs within this function, an
** SQLite error code is returned. Additionally, if pzErrmsg is not NULL,
** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted
** English language error message. It is the responsibility of the caller to
** eventually free any such buffer using sqlite3_free().







|







220673
220674
220675
220676
220677
220678
220679
220680
220681
220682
220683
220684
220685
220686
220687
SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *pRbu);

/*
** Close an RBU handle.
**
** If the RBU update has been completely applied, mark the RBU database
** as fully applied. Otherwise, assuming no error has occurred, save the
** current state of the RBU update application to the RBU database.
**
** If an error has already occurred as part of an sqlite3rbu_step()
** or sqlite3rbu_open() call, or if one occurs within this function, an
** SQLite error code is returned. Additionally, if pzErrmsg is not NULL,
** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted
** English language error message. It is the responsibility of the caller to
** eventually free any such buffer using sqlite3_free().
224826
224827
224828
224829
224830
224831
224832
224833
224834
224835
224836
224837
224838
224839
224840
static int rbuVfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
  rbu_file *p = (rbu_file *)pFile;
  int rc;
  rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);

  /* If this is an RBU vacuum operation and this is the target database,
  ** pretend that it has at least one page. Otherwise, SQLite will not
  ** check for the existance of a *-wal file. rbuVfsRead() contains
  ** similar logic.  */
  if( rc==SQLITE_OK && *pSize==0
   && p->pRbu && rbuIsVacuum(p->pRbu)
   && (p->openFlags & SQLITE_OPEN_MAIN_DB)
  ){
    *pSize = 1024;
  }







|







225599
225600
225601
225602
225603
225604
225605
225606
225607
225608
225609
225610
225611
225612
225613
static int rbuVfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
  rbu_file *p = (rbu_file *)pFile;
  int rc;
  rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);

  /* If this is an RBU vacuum operation and this is the target database,
  ** pretend that it has at least one page. Otherwise, SQLite will not
  ** check for the existence of a *-wal file. rbuVfsRead() contains
  ** similar logic.  */
  if( rc==SQLITE_OK && *pSize==0
   && p->pRbu && rbuIsVacuum(p->pRbu)
   && (p->openFlags & SQLITE_OPEN_MAIN_DB)
  ){
    *pSize = 1024;
  }
228056
228057
228058
228059
228060
228061
228062
228063
228064
228065
228066
228067
228068
228069
228070
  sqlite3_finalize(pStmt);
  return rc;
}

/*
** This function is called to initialize the SessionTable.nCol, azCol[]
** abPK[] and azDflt[] members of SessionTable object pTab. If these
** fields are already initilialized, this function is a no-op.
**
** If an error occurs, an error code is stored in sqlite3_session.rc and
** non-zero returned. Or, if no error occurs but the table has no primary
** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to
** indicate that updates on this table should be ignored. SessionTable.abPK
** is set to NULL in this case.
*/







|







228829
228830
228831
228832
228833
228834
228835
228836
228837
228838
228839
228840
228841
228842
228843
  sqlite3_finalize(pStmt);
  return rc;
}

/*
** This function is called to initialize the SessionTable.nCol, azCol[]
** abPK[] and azDflt[] members of SessionTable object pTab. If these
** fields are already initialized, this function is a no-op.
**
** If an error occurs, an error code is stored in sqlite3_session.rc and
** non-zero returned. Or, if no error occurs but the table has no primary
** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to
** indicate that updates on this table should be ignored. SessionTable.abPK
** is set to NULL in this case.
*/
229879
229880
229881
229882
229883
229884
229885
229886
229887
229888
229889
229890
229891
229892
229893
  int (*xOutput)(void *pOut, const void *pData, int nData),
  void *pOut,                     /* First argument for xOutput */
  int *pnChangeset,               /* OUT: Size of buffer at *ppChangeset */
  void **ppChangeset              /* OUT: Buffer containing changeset */
){
  sqlite3 *db = pSession->db;     /* Source database handle */
  SessionTable *pTab;             /* Used to iterate through attached tables */
  SessionBuffer buf = {0,0,0};    /* Buffer in which to accumlate changeset */
  int rc;                         /* Return code */

  assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) );
  assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) );

  /* Zero the output variables in case an error occurs. If this session
  ** object is already in the error state (sqlite3_session.rc != SQLITE_OK),







|







230652
230653
230654
230655
230656
230657
230658
230659
230660
230661
230662
230663
230664
230665
230666
  int (*xOutput)(void *pOut, const void *pData, int nData),
  void *pOut,                     /* First argument for xOutput */
  int *pnChangeset,               /* OUT: Size of buffer at *ppChangeset */
  void **ppChangeset              /* OUT: Buffer containing changeset */
){
  sqlite3 *db = pSession->db;     /* Source database handle */
  SessionTable *pTab;             /* Used to iterate through attached tables */
  SessionBuffer buf = {0,0,0};    /* Buffer in which to accumulate changeset */
  int rc;                         /* Return code */

  assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) );
  assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) );

  /* Zero the output variables in case an error occurs. If this session
  ** object is already in the error state (sqlite3_session.rc != SQLITE_OK),
234313
234314
234315
234316
234317
234318
234319












234320
234321
234322
234323
234324
234325
234326

#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC
# define EIGHT_BYTE_ALIGNMENT(X)   ((((uptr)(X) - (uptr)0)&3)==0)
#else
# define EIGHT_BYTE_ALIGNMENT(X)   ((((uptr)(X) - (uptr)0)&7)==0)
#endif













#endif

/* Truncate very long tokens to this many bytes. Hard limit is
** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
** field that occurs at the start of each leaf page (see fts5_index.c). */
#define FTS5_MAX_TOKEN_SIZE 32768








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







235086
235087
235088
235089
235090
235091
235092
235093
235094
235095
235096
235097
235098
235099
235100
235101
235102
235103
235104
235105
235106
235107
235108
235109
235110
235111

#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC
# define EIGHT_BYTE_ALIGNMENT(X)   ((((uptr)(X) - (uptr)0)&3)==0)
#else
# define EIGHT_BYTE_ALIGNMENT(X)   ((((uptr)(X) - (uptr)0)&7)==0)
#endif

/*
** Macros needed to provide flexible arrays in a portable way
*/
#ifndef offsetof
# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
# define FLEXARRAY
#else
# define FLEXARRAY 1
#endif

#endif

/* Truncate very long tokens to this many bytes. Hard limit is
** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
** field that occurs at the start of each leaf page (see fts5_index.c). */
#define FTS5_MAX_TOKEN_SIZE 32768

234385
234386
234387
234388
234389
234390
234391
234392
234393
234394
234395

234396
234397
234398
234399
234400
234401
234402
** then an object of the following type is used to record the set of columns.
** Each entry in the aiCol[] array is a column that may be matched.
**
** This object is used by fts5_expr.c and fts5_index.c.
*/
struct Fts5Colset {
  int nCol;
  int aiCol[1];
};




/**************************************************************************
** Interface to code in fts5_config.c. fts5_config.c contains contains code
** to parse the arguments passed to the CREATE VIRTUAL TABLE statement.
*/

typedef struct Fts5Config Fts5Config;







|


|
>







235170
235171
235172
235173
235174
235175
235176
235177
235178
235179
235180
235181
235182
235183
235184
235185
235186
235187
235188
** then an object of the following type is used to record the set of columns.
** Each entry in the aiCol[] array is a column that may be matched.
**
** This object is used by fts5_expr.c and fts5_index.c.
*/
struct Fts5Colset {
  int nCol;
  int aiCol[FLEXARRAY];
};

/* Size (int bytes) of a complete Fts5Colset object with N columns. */
#define SZ_FTS5COLSET(N) (sizeof(i64)*((N+2)/2))

/**************************************************************************
** Interface to code in fts5_config.c. fts5_config.c contains contains code
** to parse the arguments passed to the CREATE VIRTUAL TABLE statement.
*/

typedef struct Fts5Config Fts5Config;
235217
235218
235219
235220
235221
235222
235223
235224
235225
235226
235227
235228
235229
235230
235231
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Driver template for the LEMON parser generator.
**
** The "lemon" program processes an LALR(1) input grammar file, then uses
** this template to construct a parser.  The "lemon" program inserts text
** at each "%%" line.  Also, any "P-a-r-s-e" identifer prefix (without the
** interstitial "-" characters) contained in this template is changed into
** the value of the %name directive from the grammar.  Otherwise, the content
** of this template is copied straight through into the generate parser
** source file.
**
** The following is the concatenation of all %include directives from the
** input grammar file:







|







236003
236004
236005
236006
236007
236008
236009
236010
236011
236012
236013
236014
236015
236016
236017
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Driver template for the LEMON parser generator.
**
** The "lemon" program processes an LALR(1) input grammar file, then uses
** this template to construct a parser.  The "lemon" program inserts text
** at each "%%" line.  Also, any "P-a-r-s-e" identifier prefix (without the
** interstitial "-" characters) contained in this template is changed into
** the value of the %name directive from the grammar.  Otherwise, the content
** of this template is copied straight through into the generate parser
** source file.
**
** The following is the concatenation of all %include directives from the
** input grammar file:
237371
237372
237373
237374
237375
237376
237377
237378
237379
237380
237381
237382
237383
237384
237385
        **   IDF = log( (N - nHit + 0.5) / (nHit + 0.5) )
        **
        ** where "N" is the total number of documents in the set and nHit
        ** is the number that contain at least one instance of the phrase
        ** under consideration.
        **
        ** The problem with this is that if (N < 2*nHit), the IDF is
        ** negative. Which is undesirable. So the mimimum allowable IDF is
        ** (1e-6) - roughly the same as a term that appears in just over
        ** half of set of 5,000,000 documents.  */
        double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) );
        if( idf<=0.0 ) idf = 1e-6;
        p->aIDF[i] = idf;
      }
    }







|







238157
238158
238159
238160
238161
238162
238163
238164
238165
238166
238167
238168
238169
238170
238171
        **   IDF = log( (N - nHit + 0.5) / (nHit + 0.5) )
        **
        ** where "N" is the total number of documents in the set and nHit
        ** is the number that contain at least one instance of the phrase
        ** under consideration.
        **
        ** The problem with this is that if (N < 2*nHit), the IDF is
        ** negative. Which is undesirable. So the minimum allowable IDF is
        ** (1e-6) - roughly the same as a term that appears in just over
        ** half of set of 5,000,000 documents.  */
        double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) );
        if( idf<=0.0 ) idf = 1e-6;
        p->aIDF[i] = idf;
      }
    }
237834
237835
237836
237837
237838
237839
237840
237841
237842
237843
237844
237845
237846
237847
237848
** Return true if character 't' may be part of an FTS5 bareword, or false
** otherwise. Characters that may be part of barewords:
**
**   * All non-ASCII characters,
**   * The 52 upper and lower case ASCII characters, and
**   * The 10 integer ASCII characters.
**   * The underscore character "_" (0x5F).
**   * The unicode "subsitute" character (0x1A).
*/
static int sqlite3Fts5IsBareword(char t){
  u8 aBareword[128] = {
    0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0,   /* 0x00 .. 0x0F */
    0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 1, 0, 0, 0, 0, 0,   /* 0x10 .. 0x1F */
    0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0,   /* 0x20 .. 0x2F */
    1, 1, 1, 1, 1, 1, 1, 1,    1, 1, 0, 0, 0, 0, 0, 0,   /* 0x30 .. 0x3F */







|







238620
238621
238622
238623
238624
238625
238626
238627
238628
238629
238630
238631
238632
238633
238634
** Return true if character 't' may be part of an FTS5 bareword, or false
** otherwise. Characters that may be part of barewords:
**
**   * All non-ASCII characters,
**   * The 52 upper and lower case ASCII characters, and
**   * The 10 integer ASCII characters.
**   * The underscore character "_" (0x5F).
**   * The unicode "substitute" character (0x1A).
*/
static int sqlite3Fts5IsBareword(char t){
  u8 aBareword[128] = {
    0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0,   /* 0x00 .. 0x0F */
    0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 1, 0, 0, 0, 0, 0,   /* 0x10 .. 0x1F */
    0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0,   /* 0x20 .. 0x2F */
    1, 1, 1, 1, 1, 1, 1, 1,    1, 1, 0, 0, 0, 0, 0, 0,   /* 0x30 .. 0x3F */
239152
239153
239154
239155
239156
239157
239158
239159
239160




239161
239162
239163
239164
239165
239166
239167

  i64 iRowid;                     /* Current rowid */
  Fts5ExprNearset *pNear;         /* For FTS5_STRING - cluster of phrases */

  /* Child nodes. For a NOT node, this array always contains 2 entries. For
  ** AND or OR nodes, it contains 2 or more entries.  */
  int nChild;                     /* Number of child nodes */
  Fts5ExprNode *apChild[1];       /* Array of child nodes */
};





#define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING)

/*
** Invoke the xNext method of an Fts5ExprNode object. This macro should be
** used as if it has the same signature as the xNext() methods themselves.
*/







|

>
>
>
>







239938
239939
239940
239941
239942
239943
239944
239945
239946
239947
239948
239949
239950
239951
239952
239953
239954
239955
239956
239957

  i64 iRowid;                     /* Current rowid */
  Fts5ExprNearset *pNear;         /* For FTS5_STRING - cluster of phrases */

  /* Child nodes. For a NOT node, this array always contains 2 entries. For
  ** AND or OR nodes, it contains 2 or more entries.  */
  int nChild;                     /* Number of child nodes */
  Fts5ExprNode *apChild[FLEXARRAY]; /* Array of child nodes */
};

/* Size (in bytes) of an Fts5ExprNode object that holds up to N children */
#define SZ_FTS5EXPRNODE(N) \
  (offsetof(Fts5ExprNode,apChild) + (N)*sizeof(Fts5ExprNode*))

#define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING)

/*
** Invoke the xNext method of an Fts5ExprNode object. This macro should be
** used as if it has the same signature as the xNext() methods themselves.
*/
239185
239186
239187
239188
239189
239190
239191
239192
239193




239194
239195
239196
239197
239198
239199
239200
239201
239202
239203
239204
239205



239206
239207
239208
239209
239210
239211
239212
** A phrase. One or more terms that must appear in a contiguous sequence
** within a document for it to match.
*/
struct Fts5ExprPhrase {
  Fts5ExprNode *pNode;            /* FTS5_STRING node this phrase is part of */
  Fts5Buffer poslist;             /* Current position list */
  int nTerm;                      /* Number of entries in aTerm[] */
  Fts5ExprTerm aTerm[1];          /* Terms that make up this phrase */
};





/*
** One or more phrases that must appear within a certain token distance of
** each other within each matching document.
*/
struct Fts5ExprNearset {
  int nNear;                      /* NEAR parameter */
  Fts5Colset *pColset;            /* Columns to search (NULL -> all columns) */
  int nPhrase;                    /* Number of entries in aPhrase[] array */
  Fts5ExprPhrase *apPhrase[1];    /* Array of phrase pointers */
};





/*
** Parse context.
*/
struct Fts5Parse {
  Fts5Config *pConfig;
  char *zErr;







|

>
>
>
>









|


>
>
>







239975
239976
239977
239978
239979
239980
239981
239982
239983
239984
239985
239986
239987
239988
239989
239990
239991
239992
239993
239994
239995
239996
239997
239998
239999
240000
240001
240002
240003
240004
240005
240006
240007
240008
240009
** A phrase. One or more terms that must appear in a contiguous sequence
** within a document for it to match.
*/
struct Fts5ExprPhrase {
  Fts5ExprNode *pNode;            /* FTS5_STRING node this phrase is part of */
  Fts5Buffer poslist;             /* Current position list */
  int nTerm;                      /* Number of entries in aTerm[] */
  Fts5ExprTerm aTerm[FLEXARRAY];  /* Terms that make up this phrase */
};

/* Size (in bytes) of an Fts5ExprPhrase object that holds up to N terms */
#define SZ_FTS5EXPRPHRASE(N) \
    (offsetof(Fts5ExprPhrase,aTerm) + (N)*sizeof(Fts5ExprTerm))

/*
** One or more phrases that must appear within a certain token distance of
** each other within each matching document.
*/
struct Fts5ExprNearset {
  int nNear;                      /* NEAR parameter */
  Fts5Colset *pColset;            /* Columns to search (NULL -> all columns) */
  int nPhrase;                    /* Number of entries in aPhrase[] array */
  Fts5ExprPhrase *apPhrase[FLEXARRAY]; /* Array of phrase pointers */
};

/* Size (in bytes) of an Fts5ExprNearset object covering up to N phrases */
#define SZ_FTS5EXPRNEARSET(N) \
  (offsetof(Fts5ExprNearset,apPhrase)+(N)*sizeof(Fts5ExprPhrase*))

/*
** Parse context.
*/
struct Fts5Parse {
  Fts5Config *pConfig;
  char *zErr;
239358
239359
239360
239361
239362
239363
239364
239365
239366
239367
239368
239369
239370
239371
239372

  assert( sParse.pExpr || sParse.rc!=SQLITE_OK );
  assert_expr_depth_ok(sParse.rc, sParse.pExpr);

  /* If the LHS of the MATCH expression was a user column, apply the
  ** implicit column-filter.  */
  if( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){
    int n = sizeof(Fts5Colset);
    Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n);
    if( pColset ){
      pColset->nCol = 1;
      pColset->aiCol[0] = iCol;
      sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset);
    }
  }







|







240155
240156
240157
240158
240159
240160
240161
240162
240163
240164
240165
240166
240167
240168
240169

  assert( sParse.pExpr || sParse.rc!=SQLITE_OK );
  assert_expr_depth_ok(sParse.rc, sParse.pExpr);

  /* If the LHS of the MATCH expression was a user column, apply the
  ** implicit column-filter.  */
  if( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){
    int n = SZ_FTS5COLSET(1);
    Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n);
    if( pColset ){
      pColset->nCol = 1;
      pColset->aiCol[0] = iCol;
      sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset);
    }
  }
240716
240717
240718
240719
240720
240721
240722
240723
240724
240725
240726
240727
240728
240729
240730
240731
240732
240733
240734
240735
240736
240737
240738
240739
240740
240741
){
  const int SZALLOC = 8;
  Fts5ExprNearset *pRet = 0;

  if( pParse->rc==SQLITE_OK ){
    if( pNear==0 ){
      sqlite3_int64 nByte;
      nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*);
      pRet = sqlite3_malloc64(nByte);
      if( pRet==0 ){
        pParse->rc = SQLITE_NOMEM;
      }else{
        memset(pRet, 0, (size_t)nByte);
      }
    }else if( (pNear->nPhrase % SZALLOC)==0 ){
      int nNew = pNear->nPhrase + SZALLOC;
      sqlite3_int64 nByte;

      nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*);
      pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte);
      if( pRet==0 ){
        pParse->rc = SQLITE_NOMEM;
      }
    }else{
      pRet = pNear;
    }







|










|







241513
241514
241515
241516
241517
241518
241519
241520
241521
241522
241523
241524
241525
241526
241527
241528
241529
241530
241531
241532
241533
241534
241535
241536
241537
241538
){
  const int SZALLOC = 8;
  Fts5ExprNearset *pRet = 0;

  if( pParse->rc==SQLITE_OK ){
    if( pNear==0 ){
      sqlite3_int64 nByte;
      nByte = SZ_FTS5EXPRNEARSET(SZALLOC+1);
      pRet = sqlite3_malloc64(nByte);
      if( pRet==0 ){
        pParse->rc = SQLITE_NOMEM;
      }else{
        memset(pRet, 0, (size_t)nByte);
      }
    }else if( (pNear->nPhrase % SZALLOC)==0 ){
      int nNew = pNear->nPhrase + SZALLOC;
      sqlite3_int64 nByte;

      nByte = SZ_FTS5EXPRNEARSET(nNew+1);
      pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte);
      if( pRet==0 ){
        pParse->rc = SQLITE_NOMEM;
      }
    }else{
      pRet = pNear;
    }
240818
240819
240820
240821
240822
240823
240824
240825
240826
240827
240828
240829
240830
240831
240832
240833
240834
240835
240836
240837
  }else{
    Fts5ExprTerm *pTerm;
    if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){
      Fts5ExprPhrase *pNew;
      int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0);

      pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase,
          sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew
      );
      if( pNew==0 ){
        rc = SQLITE_NOMEM;
      }else{
        if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase));
        pCtx->pPhrase = pPhrase = pNew;
        pNew->nTerm = nNew - SZALLOC;
      }
    }

    if( rc==SQLITE_OK ){
      pTerm = &pPhrase->aTerm[pPhrase->nTerm++];







|




|







241615
241616
241617
241618
241619
241620
241621
241622
241623
241624
241625
241626
241627
241628
241629
241630
241631
241632
241633
241634
  }else{
    Fts5ExprTerm *pTerm;
    if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){
      Fts5ExprPhrase *pNew;
      int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0);

      pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase,
          SZ_FTS5EXPRPHRASE(nNew+1)
      );
      if( pNew==0 ){
        rc = SQLITE_NOMEM;
      }else{
        if( pPhrase==0 ) memset(pNew, 0, SZ_FTS5EXPRPHRASE(1));
        pCtx->pPhrase = pPhrase = pNew;
        pNew->nTerm = nNew - SZALLOC;
      }
    }

    if( rc==SQLITE_OK ){
      pTerm = &pPhrase->aTerm[pPhrase->nTerm++];
240931
240932
240933
240934
240935
240936
240937
240938
240939
240940
240941
240942
240943
240944
240945
      }
      pParse->nPhrase++;
    }

    if( sCtx.pPhrase==0 ){
      /* This happens when parsing a token or quoted phrase that contains
      ** no token characters at all. (e.g ... MATCH '""'). */
      sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase));
    }else if( sCtx.pPhrase->nTerm ){
      sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix;
    }
    assert( pParse->apPhrase!=0 );
    pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase;
  }








|







241728
241729
241730
241731
241732
241733
241734
241735
241736
241737
241738
241739
241740
241741
241742
      }
      pParse->nPhrase++;
    }

    if( sCtx.pPhrase==0 ){
      /* This happens when parsing a token or quoted phrase that contains
      ** no token characters at all. (e.g ... MATCH '""'). */
      sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, SZ_FTS5EXPRPHRASE(1));
    }else if( sCtx.pPhrase->nTerm ){
      sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix;
    }
    assert( pParse->apPhrase!=0 );
    pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase;
  }

240966
240967
240968
240969
240970
240971
240972
240973
240974
240975
240976
240977
240978
240979
240980
240981
240982
240983
240984
240985
240986
240987
240988
240989
240990
240991
240992
    pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));
  }
  if( rc==SQLITE_OK ){
    pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
        sizeof(Fts5ExprPhrase*));
  }
  if( rc==SQLITE_OK ){
    pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc,
        sizeof(Fts5ExprNode));
  }
  if( rc==SQLITE_OK ){
    pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
        sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
  }
  if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){
    Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
    if( pColsetOrig ){
      sqlite3_int64 nByte;
      Fts5Colset *pColset;
      nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
      pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
      if( pColset ){
        memcpy(pColset, pColsetOrig, (size_t)nByte);
      }
      pNew->pRoot->pNear->pColset = pColset;
    }
  }







|
<



|






|







241763
241764
241765
241766
241767
241768
241769
241770

241771
241772
241773
241774
241775
241776
241777
241778
241779
241780
241781
241782
241783
241784
241785
241786
241787
241788
    pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));
  }
  if( rc==SQLITE_OK ){
    pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
        sizeof(Fts5ExprPhrase*));
  }
  if( rc==SQLITE_OK ){
    pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRNODE(1));

  }
  if( rc==SQLITE_OK ){
    pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
                                                    SZ_FTS5EXPRNEARSET(2));
  }
  if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){
    Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
    if( pColsetOrig ){
      sqlite3_int64 nByte;
      Fts5Colset *pColset;
      nByte = SZ_FTS5COLSET(pColsetOrig->nCol);
      pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
      if( pColset ){
        memcpy(pColset, pColsetOrig, (size_t)nByte);
      }
      pNew->pRoot->pNear->pColset = pColset;
    }
  }
241006
241007
241008
241009
241010
241011
241012
241013
241014
241015
241016
241017
241018
241019
241020
          sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
          sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst;
        }
      }
    }else{
      /* This happens when parsing a token or quoted phrase that contains
      ** no token characters at all. (e.g ... MATCH '""'). */
      sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
    }
  }

  if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){
    /* All the allocations succeeded. Put the expression object together. */
    pNew->pIndex = pExpr->pIndex;
    pNew->pConfig = pExpr->pConfig;







|







241802
241803
241804
241805
241806
241807
241808
241809
241810
241811
241812
241813
241814
241815
241816
          sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
          sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst;
        }
      }
    }else{
      /* This happens when parsing a token or quoted phrase that contains
      ** no token characters at all. (e.g ... MATCH '""'). */
      sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRPHRASE(1));
    }
  }

  if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){
    /* All the allocations succeeded. Put the expression object together. */
    pNew->pIndex = pExpr->pIndex;
    pNew->pConfig = pExpr->pConfig;
241100
241101
241102
241103
241104
241105
241106
241107
241108
241109
241110
241111
241112
241113
241114
){
  int nCol = p ? p->nCol : 0;     /* Num. columns already in colset object */
  Fts5Colset *pNew;               /* New colset object to return */

  assert( pParse->rc==SQLITE_OK );
  assert( iCol>=0 && iCol<pParse->pConfig->nCol );

  pNew = sqlite3_realloc64(p, sizeof(Fts5Colset) + sizeof(int)*nCol);
  if( pNew==0 ){
    pParse->rc = SQLITE_NOMEM;
  }else{
    int *aiCol = pNew->aiCol;
    int i, j;
    for(i=0; i<nCol; i++){
      if( aiCol[i]==iCol ) return pNew;







|







241896
241897
241898
241899
241900
241901
241902
241903
241904
241905
241906
241907
241908
241909
241910
){
  int nCol = p ? p->nCol : 0;     /* Num. columns already in colset object */
  Fts5Colset *pNew;               /* New colset object to return */

  assert( pParse->rc==SQLITE_OK );
  assert( iCol>=0 && iCol<pParse->pConfig->nCol );

  pNew = sqlite3_realloc64(p, SZ_FTS5COLSET(nCol+1));
  if( pNew==0 ){
    pParse->rc = SQLITE_NOMEM;
  }else{
    int *aiCol = pNew->aiCol;
    int i, j;
    for(i=0; i<nCol; i++){
      if( aiCol[i]==iCol ) return pNew;
241135
241136
241137
241138
241139
241140
241141
241142
241143
241144
241145
241146
241147
241148
241149
** as the second argument before returning.
*/
static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){
  Fts5Colset *pRet;
  int nCol = pParse->pConfig->nCol;

  pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc,
      sizeof(Fts5Colset) + sizeof(int)*nCol
  );
  if( pRet ){
    int i;
    int iOld = 0;
    for(i=0; i<nCol; i++){
      if( iOld>=p->nCol || p->aiCol[iOld]!=i ){
        pRet->aiCol[pRet->nCol++] = i;







|







241931
241932
241933
241934
241935
241936
241937
241938
241939
241940
241941
241942
241943
241944
241945
** as the second argument before returning.
*/
static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){
  Fts5Colset *pRet;
  int nCol = pParse->pConfig->nCol;

  pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc,
      SZ_FTS5COLSET(nCol+1)
  );
  if( pRet ){
    int i;
    int iOld = 0;
    for(i=0; i<nCol; i++){
      if( iOld>=p->nCol || p->aiCol[iOld]!=i ){
        pRet->aiCol[pRet->nCol++] = i;
241196
241197
241198
241199
241200
241201
241202
241203
241204
241205
241206
241207
241208
241209
241210
** Otherwise, a copy of (*pOrig) is made into memory obtained from
** sqlite3Fts5MallocZero() and a pointer to it returned. If the allocation
** fails, (*pRc) is set to SQLITE_NOMEM and NULL is returned.
*/
static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){
  Fts5Colset *pRet;
  if( pOrig ){
    sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int);
    pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte);
    if( pRet ){
      memcpy(pRet, pOrig, (size_t)nByte);
    }
  }else{
    pRet = 0;
  }







|







241992
241993
241994
241995
241996
241997
241998
241999
242000
242001
242002
242003
242004
242005
242006
** Otherwise, a copy of (*pOrig) is made into memory obtained from
** sqlite3Fts5MallocZero() and a pointer to it returned. If the allocation
** fails, (*pRc) is set to SQLITE_NOMEM and NULL is returned.
*/
static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){
  Fts5Colset *pRet;
  if( pOrig ){
    sqlite3_int64 nByte = SZ_FTS5COLSET(pOrig->nCol);
    pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte);
    if( pRet ){
      memcpy(pRet, pOrig, (size_t)nByte);
    }
  }else{
    pRet = 0;
  }
241364
241365
241366
241367
241368
241369
241370
241371
241372
241373
241374
241375
241376
241377
241378
241379
241380
241381
241382
241383
241384
241385
241386
241387
241388
  int ii;
  int nByte;
  Fts5ExprNode *pRet;

  assert( pNear->nPhrase==1 );
  assert( pParse->bPhraseToAnd );

  nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*);
  pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
  if( pRet ){
    pRet->eType = FTS5_AND;
    pRet->nChild = nTerm;
    pRet->iHeight = 1;
    fts5ExprAssignXNext(pRet);
    pParse->nPhrase--;
    for(ii=0; ii<nTerm; ii++){
      Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
          &pParse->rc, sizeof(Fts5ExprPhrase)
      );
      if( pPhrase ){
        if( parseGrowPhraseArray(pParse) ){
          fts5ExprPhraseFree(pPhrase);
        }else{
          Fts5ExprTerm *p = &pNear->apPhrase[0]->aTerm[ii];
          Fts5ExprTerm *pTo = &pPhrase->aTerm[0];







|









|







242160
242161
242162
242163
242164
242165
242166
242167
242168
242169
242170
242171
242172
242173
242174
242175
242176
242177
242178
242179
242180
242181
242182
242183
242184
  int ii;
  int nByte;
  Fts5ExprNode *pRet;

  assert( pNear->nPhrase==1 );
  assert( pParse->bPhraseToAnd );

  nByte = SZ_FTS5EXPRNODE(nTerm+1);
  pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
  if( pRet ){
    pRet->eType = FTS5_AND;
    pRet->nChild = nTerm;
    pRet->iHeight = 1;
    fts5ExprAssignXNext(pRet);
    pParse->nPhrase--;
    for(ii=0; ii<nTerm; ii++){
      Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
          &pParse->rc, SZ_FTS5EXPRPHRASE(1)
      );
      if( pPhrase ){
        if( parseGrowPhraseArray(pParse) ){
          fts5ExprPhraseFree(pPhrase);
        }else{
          Fts5ExprTerm *p = &pNear->apPhrase[0]->aTerm[ii];
          Fts5ExprTerm *pTo = &pPhrase->aTerm[0];
241443
241444
241445
241446
241447
241448
241449
241450
241451
241452
241453
241454
241455
241456
241457
        nChild = 2;
      }else if( eType==FTS5_AND || eType==FTS5_OR ){
        nChild = 2;
        if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
        if( pRight->eType==eType ) nChild += pRight->nChild-1;
      }

      nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1);
      pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);

      if( pRet ){
        pRet->eType = eType;
        pRet->pNear = pNear;
        fts5ExprAssignXNext(pRet);
        if( eType==FTS5_STRING ){







|







242239
242240
242241
242242
242243
242244
242245
242246
242247
242248
242249
242250
242251
242252
242253
        nChild = 2;
      }else if( eType==FTS5_AND || eType==FTS5_OR ){
        nChild = 2;
        if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
        if( pRight->eType==eType ) nChild += pRight->nChild-1;
      }

      nByte = SZ_FTS5EXPRNODE(nChild);
      pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);

      if( pRet ){
        pRet->eType = eType;
        pRet->pNear = pNear;
        fts5ExprAssignXNext(pRet);
        if( eType==FTS5_STRING ){
242318
242319
242320
242321
242322
242323
242324
242325
242326
242327
242328
242329
242330
242331
242332
    *ppOut = pTerm->pTerm;
    *pnOut = pTerm->nFullTerm;
  }
  return rc;
}

/*
** Clear the token mappings for all Fts5IndexIter objects mannaged by
** the expression passed as the only argument.
*/
static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){
  int ii;
  for(ii=0; ii<pExpr->nPhrase; ii++){
    Fts5ExprTerm *pT;
    for(pT=&pExpr->apExprPhrase[ii]->aTerm[0]; pT; pT=pT->pSynonym){







|







243114
243115
243116
243117
243118
243119
243120
243121
243122
243123
243124
243125
243126
243127
243128
    *ppOut = pTerm->pTerm;
    *pnOut = pTerm->nFullTerm;
  }
  return rc;
}

/*
** Clear the token mappings for all Fts5IndexIter objects managed by
** the expression passed as the only argument.
*/
static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){
  int ii;
  for(ii=0; ii<pExpr->nPhrase; ii++){
    Fts5ExprTerm *pT;
    for(pT=&pExpr->apExprPhrase[ii]->aTerm[0]; pT; pT=pT->pSynonym){
242353
242354
242355
242356
242357
242358
242359
242360
242361
242362
242363
242364
242365
242366
242367

/* #include "fts5Int.h" */

typedef struct Fts5HashEntry Fts5HashEntry;

/*
** This file contains the implementation of an in-memory hash table used
** to accumuluate "term -> doclist" content before it is flused to a level-0
** segment.
*/


struct Fts5Hash {
  int eDetail;                    /* Copy of Fts5Config.eDetail */
  int *pnByte;                    /* Pointer to bytes counter */







|







243149
243150
243151
243152
243153
243154
243155
243156
243157
243158
243159
243160
243161
243162
243163

/* #include "fts5Int.h" */

typedef struct Fts5HashEntry Fts5HashEntry;

/*
** This file contains the implementation of an in-memory hash table used
** to accumulate "term -> doclist" content before it is flushed to a level-0
** segment.
*/


struct Fts5Hash {
  int eDetail;                    /* Copy of Fts5Config.eDetail */
  int *pnByte;                    /* Pointer to bytes counter */
242410
242411
242412
242413
242414
242415
242416
242417
242418
242419
242420
242421
242422
242423
242424
  u8 bContent;                    /* Set content-flag (detail=none mode) */
  i16 iCol;                       /* Column of last value written */
  int iPos;                       /* Position of last value written */
  i64 iRowid;                     /* Rowid of last value written */
};

/*
** Eqivalent to:
**
**   char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; }
*/
#define fts5EntryKey(p) ( ((char *)(&(p)[1])) )


/*







|







243206
243207
243208
243209
243210
243211
243212
243213
243214
243215
243216
243217
243218
243219
243220
  u8 bContent;                    /* Set content-flag (detail=none mode) */
  i16 iCol;                       /* Column of last value written */
  int iPos;                       /* Position of last value written */
  i64 iRowid;                     /* Rowid of last value written */
};

/*
** Equivalent to:
**
**   char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; }
*/
#define fts5EntryKey(p) ( ((char *)(&(p)[1])) )


/*
243346
243347
243348
243349
243350
243351
243352
243353
243354
243355




243356
243357
243358
243359
243360
243361
243362
};
struct Fts5Structure {
  int nRef;                       /* Object reference count */
  u64 nWriteCounter;              /* Total leaves written to level 0 */
  u64 nOriginCntr;                /* Origin value for next top-level segment */
  int nSegment;                   /* Total segments in this structure */
  int nLevel;                     /* Number of levels in this index */
  Fts5StructureLevel aLevel[1];   /* Array of nLevel level objects */
};





/*
** An object of type Fts5SegWriter is used to write to segments.
*/
struct Fts5PageWriter {
  int pgno;                       /* Page number for this page */
  int iPrevPgidx;                 /* Previous value written into pgidx */
  Fts5Buffer buf;                 /* Buffer containing leaf data */







|


>
>
>
>







244142
244143
244144
244145
244146
244147
244148
244149
244150
244151
244152
244153
244154
244155
244156
244157
244158
244159
244160
244161
244162
};
struct Fts5Structure {
  int nRef;                       /* Object reference count */
  u64 nWriteCounter;              /* Total leaves written to level 0 */
  u64 nOriginCntr;                /* Origin value for next top-level segment */
  int nSegment;                   /* Total segments in this structure */
  int nLevel;                     /* Number of levels in this index */
  Fts5StructureLevel aLevel[FLEXARRAY]; /* Array of nLevel level objects */
};

/* Size (in bytes) of an Fts5Structure object holding up to N levels */
#define SZ_FTS5STRUCTURE(N) \
         (offsetof(Fts5Structure,aLevel) + (N)*sizeof(Fts5StructureLevel))

/*
** An object of type Fts5SegWriter is used to write to segments.
*/
struct Fts5PageWriter {
  int pgno;                       /* Page number for this page */
  int iPrevPgidx;                 /* Previous value written into pgidx */
  Fts5Buffer buf;                 /* Buffer containing leaf data */
243478
243479
243480
243481
243482
243483
243484
243485
243486
243487
243488




243489
243490
243491
243492
243493
243494
243495
  u8 bDel;                        /* True if the delete flag is set */
};

/*
** Array of tombstone pages. Reference counted.
*/
struct Fts5TombstoneArray {
  int nRef;                       /* Number of pointers to this object */
  int nTombstone;
  Fts5Data *apTombstone[1];       /* Array of tombstone pages */
};





/*
** Argument is a pointer to an Fts5Data structure that contains a
** leaf page.
*/
#define ASSERT_SZLEAF_OK(x) assert( \
    (x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \







|

|

>
>
>
>







244278
244279
244280
244281
244282
244283
244284
244285
244286
244287
244288
244289
244290
244291
244292
244293
244294
244295
244296
244297
244298
244299
  u8 bDel;                        /* True if the delete flag is set */
};

/*
** Array of tombstone pages. Reference counted.
*/
struct Fts5TombstoneArray {
  int nRef;                         /* Number of pointers to this object */
  int nTombstone;
  Fts5Data *apTombstone[FLEXARRAY]; /* Array of tombstone pages */
};

/* Size (in bytes) of an Fts5TombstoneArray holding up to N tombstones */
#define SZ_FTS5TOMBSTONEARRAY(N) \
  (offsetof(Fts5TombstoneArray,apTombstone)+(N)*sizeof(Fts5Data*))

/*
** Argument is a pointer to an Fts5Data structure that contains a
** leaf page.
*/
#define ASSERT_SZLEAF_OK(x) assert( \
    (x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \
243551
243552
243553
243554
243555
243556
243557
243558
243559



243560
243561
243562
243563
243564
243565
243566

  int nSeg;                       /* Size of aSeg[] array */
  int bRev;                       /* True to iterate in reverse order */
  u8 bSkipEmpty;                  /* True to skip deleted entries */

  i64 iSwitchRowid;               /* Firstest rowid of other than aFirst[1] */
  Fts5CResult *aFirst;            /* Current merge state (see above) */
  Fts5SegIter aSeg[1];            /* Array of segment iterators */
};




/*
** An instance of the following type is used to iterate through the contents
** of a doclist-index record.
**
** pData:
**   Record containing the doclist-index data.







|

>
>
>







244355
244356
244357
244358
244359
244360
244361
244362
244363
244364
244365
244366
244367
244368
244369
244370
244371
244372
244373

  int nSeg;                       /* Size of aSeg[] array */
  int bRev;                       /* True to iterate in reverse order */
  u8 bSkipEmpty;                  /* True to skip deleted entries */

  i64 iSwitchRowid;               /* Firstest rowid of other than aFirst[1] */
  Fts5CResult *aFirst;            /* Current merge state (see above) */
  Fts5SegIter aSeg[FLEXARRAY];    /* Array of segment iterators */
};

/* Size (in bytes) of an Fts5Iter object holding up to N segment iterators */
#define SZ_FTS5ITER(N)  (offsetof(Fts5Iter,aSeg)+(N)*sizeof(Fts5SegIter))

/*
** An instance of the following type is used to iterate through the contents
** of a doclist-index record.
**
** pData:
**   Record containing the doclist-index data.
243580
243581
243582
243583
243584
243585
243586
243587
243588




243589
243590
243591
243592
243593
243594
243595
  /* Output variables */
  int iLeafPgno;                /* Page number of current leaf page */
  i64 iRowid;                   /* First rowid on leaf iLeafPgno */
};
struct Fts5DlidxIter {
  int nLvl;
  int iSegid;
  Fts5DlidxLvl aLvl[1];
};





static void fts5PutU16(u8 *aOut, u16 iVal){
  aOut[0] = (iVal>>8);
  aOut[1] = (iVal&0xFF);
}

static u16 fts5GetU16(const u8 *aIn){







|

>
>
>
>







244387
244388
244389
244390
244391
244392
244393
244394
244395
244396
244397
244398
244399
244400
244401
244402
244403
244404
244405
244406
  /* Output variables */
  int iLeafPgno;                /* Page number of current leaf page */
  i64 iRowid;                   /* First rowid on leaf iLeafPgno */
};
struct Fts5DlidxIter {
  int nLvl;
  int iSegid;
  Fts5DlidxLvl aLvl[FLEXARRAY];
};

/* Size (in bytes) of an Fts5DlidxIter object with up to N levels */
#define SZ_FTS5DLIDXITER(N) \
          (offsetof(Fts5DlidxIter,aLvl)+(N)*sizeof(Fts5DlidxLvl))

static void fts5PutU16(u8 *aOut, u16 iVal){
  aOut[0] = (iVal>>8);
  aOut[1] = (iVal&0xFF);
}

static u16 fts5GetU16(const u8 *aIn){
243950
243951
243952
243953
243954
243955
243956
243957
243958
243959
243960
243961
243962
243963
243964
**
** This function is a no-op if (*pRc) is not SQLITE_OK when it is called. If
** an error occurs, (*pRc) is set to an SQLite error code before returning.
*/
static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){
  Fts5Structure *p = *pp;
  if( *pRc==SQLITE_OK && p->nRef>1 ){
    i64 nByte = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel);
    Fts5Structure *pNew;
    pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte);
    if( pNew ){
      int i;
      memcpy(pNew, p, nByte);
      for(i=0; i<p->nLevel; i++) pNew->aLevel[i].aSeg = 0;
      for(i=0; i<p->nLevel; i++){







|







244761
244762
244763
244764
244765
244766
244767
244768
244769
244770
244771
244772
244773
244774
244775
**
** This function is a no-op if (*pRc) is not SQLITE_OK when it is called. If
** an error occurs, (*pRc) is set to an SQLite error code before returning.
*/
static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){
  Fts5Structure *p = *pp;
  if( *pRc==SQLITE_OK && p->nRef>1 ){
    i64 nByte = SZ_FTS5STRUCTURE(p->nLevel);
    Fts5Structure *pNew;
    pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte);
    if( pNew ){
      int i;
      memcpy(pNew, p, nByte);
      for(i=0; i<p->nLevel; i++) pNew->aLevel[i].aSeg = 0;
      for(i=0; i<p->nLevel; i++){
244024
244025
244026
244027
244028
244029
244030
244031
244032
244033
244034
244035
244036
244037
244038
244039
244040
244041
  i += fts5GetVarint32(&pData[i], nLevel);
  i += fts5GetVarint32(&pData[i], nSegment);
  if( nLevel>FTS5_MAX_SEGMENT   || nLevel<0
   || nSegment>FTS5_MAX_SEGMENT || nSegment<0
  ){
    return FTS5_CORRUPT;
  }
  nByte = (
      sizeof(Fts5Structure) +                    /* Main structure */
      sizeof(Fts5StructureLevel) * (nLevel-1)    /* aLevel[] array */
  );
  pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte);

  if( pRet ){
    pRet->nRef = 1;
    pRet->nLevel = nLevel;
    pRet->nSegment = nSegment;
    i += sqlite3Fts5GetVarint(&pData[i], &pRet->nWriteCounter);







|
<
<
<







244835
244836
244837
244838
244839
244840
244841
244842



244843
244844
244845
244846
244847
244848
244849
  i += fts5GetVarint32(&pData[i], nLevel);
  i += fts5GetVarint32(&pData[i], nSegment);
  if( nLevel>FTS5_MAX_SEGMENT   || nLevel<0
   || nSegment>FTS5_MAX_SEGMENT || nSegment<0
  ){
    return FTS5_CORRUPT;
  }
  nByte = SZ_FTS5STRUCTURE(nLevel);



  pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte);

  if( pRet ){
    pRet->nRef = 1;
    pRet->nLevel = nLevel;
    pRet->nSegment = nSegment;
    i += sqlite3Fts5GetVarint(&pData[i], &pRet->nWriteCounter);
244107
244108
244109
244110
244111
244112
244113
244114
244115
244116
244117
244118
244119
244120
244121
244122
244123
244124
*/
static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){
  fts5StructureMakeWritable(pRc, ppStruct);
  assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK );
  if( *pRc==SQLITE_OK ){
    Fts5Structure *pStruct = *ppStruct;
    int nLevel = pStruct->nLevel;
    sqlite3_int64 nByte = (
        sizeof(Fts5Structure) +                  /* Main structure */
        sizeof(Fts5StructureLevel) * (nLevel+1)  /* aLevel[] array */
    );

    pStruct = sqlite3_realloc64(pStruct, nByte);
    if( pStruct ){
      memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel));
      pStruct->nLevel++;
      *ppStruct = pStruct;
    }else{







|
<
<
<







244915
244916
244917
244918
244919
244920
244921
244922



244923
244924
244925
244926
244927
244928
244929
*/
static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){
  fts5StructureMakeWritable(pRc, ppStruct);
  assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK );
  if( *pRc==SQLITE_OK ){
    Fts5Structure *pStruct = *ppStruct;
    int nLevel = pStruct->nLevel;
    sqlite3_int64 nByte = SZ_FTS5STRUCTURE(nLevel+2);




    pStruct = sqlite3_realloc64(pStruct, nByte);
    if( pStruct ){
      memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel));
      pStruct->nLevel++;
      *ppStruct = pStruct;
    }else{
244649
244650
244651
244652
244653
244654
244655
244656
244657
244658
244659
244660
244661
244662
244663
  int iLeafPg                     /* Leaf page number to load dlidx for */
){
  Fts5DlidxIter *pIter = 0;
  int i;
  int bDone = 0;

  for(i=0; p->rc==SQLITE_OK && bDone==0; i++){
    sqlite3_int64 nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl);
    Fts5DlidxIter *pNew;

    pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte);
    if( pNew==0 ){
      p->rc = SQLITE_NOMEM;
    }else{
      i64 iRowid = FTS5_DLIDX_ROWID(iSegid, i, iLeafPg);







|







245454
245455
245456
245457
245458
245459
245460
245461
245462
245463
245464
245465
245466
245467
245468
  int iLeafPg                     /* Leaf page number to load dlidx for */
){
  Fts5DlidxIter *pIter = 0;
  int i;
  int bDone = 0;

  for(i=0; p->rc==SQLITE_OK && bDone==0; i++){
    sqlite3_int64 nByte = SZ_FTS5DLIDXITER(i+1);
    Fts5DlidxIter *pNew;

    pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte);
    if( pNew==0 ){
      p->rc = SQLITE_NOMEM;
    }else{
      i64 iRowid = FTS5_DLIDX_ROWID(iSegid, i, iLeafPg);
244867
244868
244869
244870
244871
244872
244873
244874
244875
244876
244877
244878
244879
244880
244881
** Allocate a tombstone hash page array object (pIter->pTombArray) for
** the iterator passed as the second argument. If an OOM error occurs,
** leave an error in the Fts5Index object.
*/
static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
  const int nTomb = pIter->pSeg->nPgTombstone;
  if( nTomb>0 ){
    int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray);
    Fts5TombstoneArray *pNew;
    pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
    if( pNew ){
      pNew->nTombstone = nTomb;
      pNew->nRef = 1;
      pIter->pTombArray = pNew;
    }







|







245672
245673
245674
245675
245676
245677
245678
245679
245680
245681
245682
245683
245684
245685
245686
** Allocate a tombstone hash page array object (pIter->pTombArray) for
** the iterator passed as the second argument. If an OOM error occurs,
** leave an error in the Fts5Index object.
*/
static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
  const int nTomb = pIter->pSeg->nPgTombstone;
  if( nTomb>0 ){
    int nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1);
    Fts5TombstoneArray *pNew;
    pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
    if( pNew ){
      pNew->nTombstone = nTomb;
      pNew->nRef = 1;
      pIter->pTombArray = pNew;
    }
246328
246329
246330
246331
246332
246333
246334
246335
246336
246337
246338
246339
246340
246341
246342
246343
  int nSeg
){
  Fts5Iter *pNew;
  i64 nSlot;                      /* Power of two >= nSeg */

  for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2);
  pNew = fts5IdxMalloc(p,
      sizeof(Fts5Iter) +                  /* pNew */
      sizeof(Fts5SegIter) * (nSlot-1) +   /* pNew->aSeg[] */
      sizeof(Fts5CResult) * nSlot         /* pNew->aFirst[] */
  );
  if( pNew ){
    pNew->nSeg = nSlot;
    pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot];
    pNew->pIndex = p;
    pNew->xSetOutputs = fts5IterSetOutputs_Noop;







|
<







247133
247134
247135
247136
247137
247138
247139
247140

247141
247142
247143
247144
247145
247146
247147
  int nSeg
){
  Fts5Iter *pNew;
  i64 nSlot;                      /* Power of two >= nSeg */

  for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2);
  pNew = fts5IdxMalloc(p,
      SZ_FTS5ITER(nSlot) +                /* pNew + pNew->aSeg[] */

      sizeof(Fts5CResult) * nSlot         /* pNew->aFirst[] */
  );
  if( pNew ){
    pNew->nSeg = nSlot;
    pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot];
    pNew->pIndex = p;
    pNew->xSetOutputs = fts5IterSetOutputs_Noop;
248695
248696
248697
248698
248699
248700
248701
248702
248703
248704
248705
248706
248707
248708
248709
}

static Fts5Structure *fts5IndexOptimizeStruct(
  Fts5Index *p,
  Fts5Structure *pStruct
){
  Fts5Structure *pNew = 0;
  sqlite3_int64 nByte = sizeof(Fts5Structure);
  int nSeg = pStruct->nSegment;
  int i;

  /* Figure out if this structure requires optimization. A structure does
  ** not require optimization if either:
  **
  **  1. it consists of fewer than two segments, or







|







249499
249500
249501
249502
249503
249504
249505
249506
249507
249508
249509
249510
249511
249512
249513
}

static Fts5Structure *fts5IndexOptimizeStruct(
  Fts5Index *p,
  Fts5Structure *pStruct
){
  Fts5Structure *pNew = 0;
  sqlite3_int64 nByte = SZ_FTS5STRUCTURE(1);
  int nSeg = pStruct->nSegment;
  int i;

  /* Figure out if this structure requires optimization. A structure does
  ** not require optimization if either:
  **
  **  1. it consists of fewer than two segments, or
248725
248726
248727
248728
248729
248730
248731

248732
248733
248734
248735
248736
248737
248738
      fts5StructureRef(pStruct);
      return pStruct;
    }
    assert( pStruct->aLevel[i].nMerge<=nThis );
  }

  nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel);

  pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte);

  if( pNew ){
    Fts5StructureLevel *pLvl;
    nByte = nSeg * sizeof(Fts5StructureSegment);
    pNew->nLevel = MIN(pStruct->nLevel+1, FTS5_MAX_LEVEL);
    pNew->nRef = 1;







>







249529
249530
249531
249532
249533
249534
249535
249536
249537
249538
249539
249540
249541
249542
249543
      fts5StructureRef(pStruct);
      return pStruct;
    }
    assert( pStruct->aLevel[i].nMerge<=nThis );
  }

  nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel);
  assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) );
  pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte);

  if( pNew ){
    Fts5StructureLevel *pLvl;
    nByte = nSeg * sizeof(Fts5StructureSegment);
    pNew->nLevel = MIN(pStruct->nLevel+1, FTS5_MAX_LEVEL);
    pNew->nRef = 1;
249301
249302
249303
249304
249305
249306
249307
249308
249309




249310
249311
249312
249313
249314
249315
249316
  Fts5Buffer terms;

  /* The following are used for other full-token tokendata queries only. */
  int nIter;
  int nIterAlloc;
  Fts5PoslistReader *aPoslistReader;
  int *aPoslistToIter;
  Fts5Iter *apIter[1];
};





/*
** The two input arrays - a1[] and a2[] - are in sorted order. This function
** merges the two arrays together and writes the result to output array
** aOut[]. aOut[] is guaranteed to be large enough to hold the result.
**
** Duplicate entries are copied into the output. So the size of the output







|

>
>
>
>







250106
250107
250108
250109
250110
250111
250112
250113
250114
250115
250116
250117
250118
250119
250120
250121
250122
250123
250124
250125
  Fts5Buffer terms;

  /* The following are used for other full-token tokendata queries only. */
  int nIter;
  int nIterAlloc;
  Fts5PoslistReader *aPoslistReader;
  int *aPoslistToIter;
  Fts5Iter *apIter[FLEXARRAY];
};

/* Size in bytes of an Fts5TokenDataIter object holding up to N iterators */
#define SZ_FTS5TOKENDATAITER(N) \
    (offsetof(Fts5TokenDataIter,apIter) + (N)*sizeof(Fts5Iter))

/*
** The two input arrays - a1[] and a2[] - are in sorted order. This function
** merges the two arrays together and writes the result to output array
** aOut[]. aOut[] is guaranteed to be large enough to hold the result.
**
** Duplicate entries are copied into the output. So the size of the output
249375
249376
249377
249378
249379
249380
249381
249382
249383
249384
249385
249386
249387
249388
249389
    pT->nMap++;
  }
}

/*
** Sort the contents of the pT->aMap[] array.
**
** The sorting algorithm requries a malloc(). If this fails, an error code
** is left in Fts5Index.rc before returning.
*/
static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
  Fts5TokenDataMap *aTmp = 0;
  int nByte = pT->nMap * sizeof(Fts5TokenDataMap);

  aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte);







|







250184
250185
250186
250187
250188
250189
250190
250191
250192
250193
250194
250195
250196
250197
250198
    pT->nMap++;
  }
}

/*
** Sort the contents of the pT->aMap[] array.
**
** The sorting algorithm requires a malloc(). If this fails, an error code
** is left in Fts5Index.rc before returning.
*/
static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
  Fts5TokenDataMap *aTmp = 0;
  int nByte = pT->nMap * sizeof(Fts5TokenDataMap);

  aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte);
249566
249567
249568
249569
249570
249571
249572
249573
249574
249575
249576
249577
249578
249579
249580
  s.iLastRowid = 0;
  s.nBuf = 32;
  if( iIdx==0
   && p->pConfig->eDetail==FTS5_DETAIL_FULL
   && p->pConfig->bPrefixInsttoken
  ){
    s.pTokendata = &s2;
    s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT));
  }

  if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
    s.xMerge = fts5MergeRowidLists;
    s.xAppend = fts5AppendRowid;
  }else{
    s.nMerge = FTS5_MERGE_NLIST-1;







|







250375
250376
250377
250378
250379
250380
250381
250382
250383
250384
250385
250386
250387
250388
250389
  s.iLastRowid = 0;
  s.nBuf = 32;
  if( iIdx==0
   && p->pConfig->eDetail==FTS5_DETAIL_FULL
   && p->pConfig->bPrefixInsttoken
  ){
    s.pTokendata = &s2;
    s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, SZ_FTS5TOKENDATAITER(1));
  }

  if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
    s.xMerge = fts5MergeRowidLists;
    s.xAppend = fts5AppendRowid;
  }else{
    s.nMerge = FTS5_MERGE_NLIST-1;
249694
249695
249696
249697
249698
249699
249700
249701

249702
249703
249704

249705
249706
249707
249708
249709
249710
249711
249712
249713
249714
249715
249716

/*
** The %_data table is completely empty when this function is called. This
** function populates it with the initial structure objects for each index,
** and the initial version of the "averages" record (a zero-byte blob).
*/
static int sqlite3Fts5IndexReinit(Fts5Index *p){
  Fts5Structure s;

  fts5StructureInvalidate(p);
  fts5IndexDiscardData(p);
  memset(&s, 0, sizeof(Fts5Structure));

  if( p->pConfig->bContentlessDelete ){
    s.nOriginCntr = 1;
  }
  fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
  fts5StructureWrite(p, &s);
  return fts5IndexReturn(p);
}

/*
** Open a new Fts5Index handle. If the bCreate argument is true, create
** and initialize the underlying %_data table.
**







|
>


|
>

|


|







250503
250504
250505
250506
250507
250508
250509
250510
250511
250512
250513
250514
250515
250516
250517
250518
250519
250520
250521
250522
250523
250524
250525
250526
250527

/*
** The %_data table is completely empty when this function is called. This
** function populates it with the initial structure objects for each index,
** and the initial version of the "averages" record (a zero-byte blob).
*/
static int sqlite3Fts5IndexReinit(Fts5Index *p){
  Fts5Structure *pTmp;
  u8 tmpSpace[SZ_FTS5STRUCTURE(1)];
  fts5StructureInvalidate(p);
  fts5IndexDiscardData(p);
  pTmp = (Fts5Structure*)tmpSpace;
  memset(pTmp, 0, SZ_FTS5STRUCTURE(1));
  if( p->pConfig->bContentlessDelete ){
    pTmp->nOriginCntr = 1;
  }
  fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
  fts5StructureWrite(p, pTmp);
  return fts5IndexReturn(p);
}

/*
** Open a new Fts5Index handle. If the bCreate argument is true, create
** and initialize the underlying %_data table.
**
249910
249911
249912
249913
249914
249915
249916
249917
249918
249919
249920
249921
249922
249923
249924
  Fts5Iter *pAppend               /* Append this iterator */
){
  Fts5TokenDataIter *pRet = pIn;

  if( p->rc==SQLITE_OK ){
    if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
      int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
      int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter);
      Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);

      if( pNew==0 ){
        p->rc = SQLITE_NOMEM;
      }else{
        if( pIn==0 ) memset(pNew, 0, nByte);
        pRet = pNew;







|







250721
250722
250723
250724
250725
250726
250727
250728
250729
250730
250731
250732
250733
250734
250735
  Fts5Iter *pAppend               /* Append this iterator */
){
  Fts5TokenDataIter *pRet = pIn;

  if( p->rc==SQLITE_OK ){
    if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
      int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
      int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1);
      Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);

      if( pNew==0 ){
        p->rc = SQLITE_NOMEM;
      }else{
        if( pIn==0 ) memset(pNew, 0, nByte);
        pRet = pNew;
250426
250427
250428
250429
250430
250431
250432
250433

250434
250435
250436
250437
250438
250439
250440
  Fts5Buffer token = {0, 0, 0};
  TokendataSetupCtx ctx;

  memset(&ctx, 0, sizeof(ctx));

  fts5BufferGrow(&p->rc, &token, nToken+1);
  assert( token.p!=0 || p->rc!=SQLITE_OK );
  ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT));


  if( p->rc==SQLITE_OK ){

    /* Fill in the token prefix to search for */
    token.p[0] = FTS5_MAIN_PREFIX;
    memcpy(&token.p[1], pToken, nToken);
    token.n = nToken+1;







|
>







251237
251238
251239
251240
251241
251242
251243
251244
251245
251246
251247
251248
251249
251250
251251
251252
  Fts5Buffer token = {0, 0, 0};
  TokendataSetupCtx ctx;

  memset(&ctx, 0, sizeof(ctx));

  fts5BufferGrow(&p->rc, &token, nToken+1);
  assert( token.p!=0 || p->rc!=SQLITE_OK );
  ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc,
                                                   SZ_FTS5TOKENDATAITER(1));

  if( p->rc==SQLITE_OK ){

    /* Fill in the token prefix to search for */
    token.p[0] = FTS5_MAIN_PREFIX;
    memcpy(&token.p[1], pToken, nToken);
    token.n = nToken+1;
250557
250558
250559
250560
250561
250562
250563
250564

250565
250566
250567
250568
250569
250570
250571
  i64 iPos = (((i64)iCol)<<32) + iOff;

  assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
  assert( pIter->pTokenDataIter || pIter->nSeg>0 );
  if( pIter->nSeg>0 ){
    /* This is a prefix term iterator. */
    if( pT==0 ){
      pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT));

      pIter->pTokenDataIter = pT;
    }
    if( pT ){
      fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos);
      fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken);
    }
  }else{







|
>







251369
251370
251371
251372
251373
251374
251375
251376
251377
251378
251379
251380
251381
251382
251383
251384
  i64 iPos = (((i64)iCol)<<32) + iOff;

  assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
  assert( pIter->pTokenDataIter || pIter->nSeg>0 );
  if( pIter->nSeg>0 ){
    /* This is a prefix term iterator. */
    if( pT==0 ){
      pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc,
                                           SZ_FTS5TOKENDATAITER(1));
      pIter->pTokenDataIter = pT;
    }
    if( pT ){
      fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos);
      fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken);
    }
  }else{
251591
251592
251593
251594
251595
251596
251597
251598
251599
251600
251601
251602
251603
251604
251605

  *pbTombstone = (int)(iRowid & 0x0001);
}
#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */

#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){
  int iSegid, iHeight, iPgno, bDlidx, bTomb;     /* Rowid compenents */
  fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno);

  if( iSegid==0 ){
    if( iKey==FTS5_AVERAGES_ROWID ){
      sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} ");
    }else{
      sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{structure}");







|







252404
252405
252406
252407
252408
252409
252410
252411
252412
252413
252414
252415
252416
252417
252418

  *pbTombstone = (int)(iRowid & 0x0001);
}
#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */

#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){
  int iSegid, iHeight, iPgno, bDlidx, bTomb;     /* Rowid components */
  fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno);

  if( iSegid==0 ){
    if( iKey==FTS5_AVERAGES_ROWID ){
      sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} ");
    }else{
      sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{structure}");
252552
252553
252554
252555
252556
252557
252558
252559
252560
252561


252562
252563
252564
252565
252566
252567
252568
**   byte of the position list for the corresponding phrase.
*/
struct Fts5Sorter {
  sqlite3_stmt *pStmt;
  i64 iRowid;                     /* Current rowid */
  const u8 *aPoslist;             /* Position lists for current row */
  int nIdx;                       /* Number of entries in aIdx[] */
  int aIdx[1];                    /* Offsets into aPoslist for current row */
};




/*
** Virtual-table cursor object.
**
** iSpecial:
**   If this is a 'special' query (refer to function fts5SpecialMatch()),
**   then this variable contains the result of the query.







|


>
>







253365
253366
253367
253368
253369
253370
253371
253372
253373
253374
253375
253376
253377
253378
253379
253380
253381
253382
253383
**   byte of the position list for the corresponding phrase.
*/
struct Fts5Sorter {
  sqlite3_stmt *pStmt;
  i64 iRowid;                     /* Current rowid */
  const u8 *aPoslist;             /* Position lists for current row */
  int nIdx;                       /* Number of entries in aIdx[] */
  int aIdx[FLEXARRAY];            /* Offsets into aPoslist for current row */
};

/* Size (int bytes) of an Fts5Sorter object with N indexes */
#define SZ_FTS5SORTER(N) (offsetof(Fts5Sorter,nIdx)+((N+2)/2)*sizeof(i64))

/*
** Virtual-table cursor object.
**
** iSpecial:
**   If this is a 'special' query (refer to function fts5SpecialMatch()),
**   then this variable contains the result of the query.
253432
253433
253434
253435
253436
253437
253438
253439
253440
253441
253442
253443
253444
253445
253446
  int nPhrase;
  sqlite3_int64 nByte;
  int rc;
  const char *zRank = pCsr->zRank;
  const char *zRankArgs = pCsr->zRankArgs;

  nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
  nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1);
  pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte);
  if( pSorter==0 ) return SQLITE_NOMEM;
  memset(pSorter, 0, (size_t)nByte);
  pSorter->nIdx = nPhrase;

  /* TODO: It would be better to have some system for reusing statement
  ** handles here, rather than preparing a new one for each query. But that







|







254247
254248
254249
254250
254251
254252
254253
254254
254255
254256
254257
254258
254259
254260
254261
  int nPhrase;
  sqlite3_int64 nByte;
  int rc;
  const char *zRank = pCsr->zRank;
  const char *zRankArgs = pCsr->zRankArgs;

  nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
  nByte = SZ_FTS5SORTER(nPhrase);
  pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte);
  if( pSorter==0 ) return SQLITE_NOMEM;
  memset(pSorter, 0, (size_t)nByte);
  pSorter->nIdx = nPhrase;

  /* TODO: It would be better to have some system for reusing statement
  ** handles here, rather than preparing a new one for each query. But that
255958
255959
255960
255961
255962
255963
255964
255965
255966
255967
255968
255969
255970
255971
255972
static void fts5SourceIdFunc(
  sqlite3_context *pCtx,          /* Function call context */
  int nArg,                       /* Number of args */
  sqlite3_value **apUnused        /* Function arguments */
){
  assert( nArg==0 );
  UNUSED_PARAM2(nArg, apUnused);
  sqlite3_result_text(pCtx, "fts5: 2025-02-18 01:16:26 57caa3136d1bfca06e4f2285734a4977b8d3fa1f75bf87453b975867e9de38fc", -1, SQLITE_TRANSIENT);
}

/*
** Implementation of fts5_locale(LOCALE, TEXT) function.
**
** If parameter LOCALE is NULL, or a zero-length string, then a copy of
** TEXT is returned. Otherwise, both LOCALE and TEXT are interpreted as







|







256773
256774
256775
256776
256777
256778
256779
256780
256781
256782
256783
256784
256785
256786
256787
static void fts5SourceIdFunc(
  sqlite3_context *pCtx,          /* Function call context */
  int nArg,                       /* Number of args */
  sqlite3_value **apUnused        /* Function arguments */
){
  assert( nArg==0 );
  UNUSED_PARAM2(nArg, apUnused);
  sqlite3_result_text(pCtx, "fts5: 2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185", -1, SQLITE_TRANSIENT);
}

/*
** Implementation of fts5_locale(LOCALE, TEXT) function.
**
** If parameter LOCALE is NULL, or a zero-length string, then a copy of
** TEXT is returned. Otherwise, both LOCALE and TEXT are interpreted as
256183
256184
256185
256186
256187
256188
256189
256190
256191
256192
256193
256194
256195
256196
256197
256198
  }

  /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file
  ** fts5_test_mi.c is compiled and linked into the executable. And call
  ** its entry point to enable the matchinfo() demo.  */
#ifdef SQLITE_FTS5_ENABLE_TEST_MI
  if( rc==SQLITE_OK ){
    extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3*);
    rc = sqlite3Fts5TestRegisterMatchinfo(db);
  }
#endif

  return rc;
}

/*







|
|







256998
256999
257000
257001
257002
257003
257004
257005
257006
257007
257008
257009
257010
257011
257012
257013
  }

  /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file
  ** fts5_test_mi.c is compiled and linked into the executable. And call
  ** its entry point to enable the matchinfo() demo.  */
#ifdef SQLITE_FTS5_ENABLE_TEST_MI
  if( rc==SQLITE_OK ){
    extern int sqlite3Fts5TestRegisterMatchinfoAPI(fts5_api*);
    rc = sqlite3Fts5TestRegisterMatchinfoAPI(&pGlobal->api);
  }
#endif

  return rc;
}

/*
Changes to extsrc/sqlite3.h.
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.50.0"
#define SQLITE_VERSION_NUMBER 3050000
#define SQLITE_SOURCE_ID      "2025-02-18 01:16:26 57caa3136d1bfca06e4f2285734a4977b8d3fa1f75bf87453b975867e9de38fc"

/*
** CAPI3REF: Run-Time Library Version Numbers
** KEYWORDS: sqlite3_version sqlite3_sourceid
**
** These interfaces provide the same information as the [SQLITE_VERSION],
** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros







|







144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.50.0"
#define SQLITE_VERSION_NUMBER 3050000
#define SQLITE_SOURCE_ID      "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185"

/*
** CAPI3REF: Run-Time Library Version Numbers
** KEYWORDS: sqlite3_version sqlite3_sourceid
**
** These interfaces provide the same information as the [SQLITE_VERSION],
** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
1158
1159
1160
1161
1162
1163
1164






1165
1166
1167
1168
1169
1170
1171
** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS
** to block for up to M milliseconds before failing when attempting to
** obtain a file lock using the xLock or xShmLock methods of the VFS.
** The parameter is a pointer to a 32-bit signed integer that contains
** the value that M is to be set to. Before returning, the 32-bit signed
** integer is overwritten with the previous value of M.






**
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
** a database file.  The argument is a pointer to a 32-bit unsigned integer.
** The "data version" for the pager is written into the pointer.  The
** "data version" changes whenever any change occurs to the corresponding
** database file, either through SQL statements on the same database







>
>
>
>
>
>







1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS
** to block for up to M milliseconds before failing when attempting to
** obtain a file lock using the xLock or xShmLock methods of the VFS.
** The parameter is a pointer to a 32-bit signed integer that contains
** the value that M is to be set to. Before returning, the 32-bit signed
** integer is overwritten with the previous value of M.
**
** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]]
** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the
** VFS to block when taking a SHARED lock to connect to a wal mode database.
** This is used to implement the functionality associated with
** SQLITE_SETLK_BLOCK_ON_CONNECT.
**
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
** a database file.  The argument is a pointer to a 32-bit unsigned integer.
** The "data version" for the pager is written into the pointer.  The
** "data version" changes whenever any change occurs to the corresponding
** database file, either through SQL statements on the same database
1255
1256
1257
1258
1259
1260
1261

1262
1263
1264
1265
1266
1267
1268
#define SQLITE_FCNTL_CKPT_DONE              37
#define SQLITE_FCNTL_RESERVE_BYTES          38
#define SQLITE_FCNTL_CKPT_START             39
#define SQLITE_FCNTL_EXTERNAL_READER        40
#define SQLITE_FCNTL_CKSM_FILE              41
#define SQLITE_FCNTL_RESET_CACHE            42
#define SQLITE_FCNTL_NULL_IO                43


/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE      SQLITE_FCNTL_GET_LOCKPROXYFILE
#define SQLITE_SET_LOCKPROXYFILE      SQLITE_FCNTL_SET_LOCKPROXYFILE
#define SQLITE_LAST_ERRNO             SQLITE_FCNTL_LAST_ERRNO









>







1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
#define SQLITE_FCNTL_CKPT_DONE              37
#define SQLITE_FCNTL_RESERVE_BYTES          38
#define SQLITE_FCNTL_CKPT_START             39
#define SQLITE_FCNTL_EXTERNAL_READER        40
#define SQLITE_FCNTL_CKSM_FILE              41
#define SQLITE_FCNTL_RESET_CACHE            42
#define SQLITE_FCNTL_NULL_IO                43
#define SQLITE_FCNTL_BLOCK_ON_CONNECT       44

/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE      SQLITE_FCNTL_GET_LOCKPROXYFILE
#define SQLITE_SET_LOCKPROXYFILE      SQLITE_FCNTL_SET_LOCKPROXYFILE
#define SQLITE_LAST_ERRNO             SQLITE_FCNTL_LAST_ERRNO


3011
3012
3013
3014
3015
3016
3017






































3018
3019
3020
3021
3022
3023
3024
** was defined  (using [sqlite3_busy_handler()]) prior to calling
** this routine, that other busy handler is cleared.)^
**
** See also:  [PRAGMA busy_timeout]
*/
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);







































/*
** CAPI3REF: Convenience Routines For Running Queries
** METHOD: sqlite3
**
** This is a legacy interface that is preserved for backwards compatibility.
** Use of this interface is not recommended.
**







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







3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
** was defined  (using [sqlite3_busy_handler()]) prior to calling
** this routine, that other busy handler is cleared.)^
**
** See also:  [PRAGMA busy_timeout]
*/
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);

/*
** CAPI3REF: Set the Setlk Timeout
** METHOD: sqlite3
**
** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If
** the VFS supports blocking locks, it sets the timeout in ms used by
** eligible locks taken on wal mode databases by the specified database
** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does
** not support blocking locks, this function is a no-op.
**
** Passing 0 to this function disables blocking locks altogether. Passing
** -1 to this function requests that the VFS blocks for a long time -
** indefinitely if possible. The results of passing any other negative value
** are undefined.
**
** Internally, each SQLite database handle store two timeout values - the
** busy-timeout (used for rollback mode databases, or if the VFS does not
** support blocking locks) and the setlk-timeout (used for blocking locks
** on wal-mode databases). The sqlite3_busy_timeout() method sets both
** values, this function sets only the setlk-timeout value. Therefore,
** to configure separate busy-timeout and setlk-timeout values for a single
** database handle, call sqlite3_busy_timeout() followed by this function.
**
** Whenever the number of connections to a wal mode database falls from
** 1 to 0, the last connection takes an exclusive lock on the database,
** then checkpoints and deletes the wal file. While it is doing this, any
** new connection that tries to read from the database fails with an
** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is
** passed to this API, the new connection blocks until the exclusive lock
** has been released.
*/
SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags);

/*
** CAPI3REF: Flags for sqlite3_setlk_timeout()
*/
#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01

/*
** CAPI3REF: Convenience Routines For Running Queries
** METHOD: sqlite3
**
** This is a legacy interface that is preserved for backwards compatibility.
** Use of this interface is not recommended.
**
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
** more threads at the same moment in time.
**
** For all versions of SQLite up to and including 3.6.23.1, a call to
** [sqlite3_reset()] was required after sqlite3_step() returned anything
** other than [SQLITE_ROW] before any subsequent invocation of
** sqlite3_step().  Failure to reset the prepared statement using
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
** sqlite3_step().  But after [version 3.6.23.1] ([dateof:3.6.23.1],
** sqlite3_step() began
** calling [sqlite3_reset()] automatically in this circumstance rather
** than returning [SQLITE_MISUSE].  This is not considered a compatibility
** break because any application that ever receives an SQLITE_MISUSE error
** is broken by definition.  The [SQLITE_OMIT_AUTORESET] compile-time option
** can be used to restore the legacy behavior.
**







|







5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
** more threads at the same moment in time.
**
** For all versions of SQLite up to and including 3.6.23.1, a call to
** [sqlite3_reset()] was required after sqlite3_step() returned anything
** other than [SQLITE_ROW] before any subsequent invocation of
** sqlite3_step().  Failure to reset the prepared statement using
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
** sqlite3_step().  But after [version 3.6.23.1] ([dateof:3.6.23.1]),
** sqlite3_step() began
** calling [sqlite3_reset()] automatically in this circumstance rather
** than returning [SQLITE_MISUSE].  This is not considered a compatibility
** break because any application that ever receives an SQLITE_MISUSE error
** is broken by definition.  The [SQLITE_OMIT_AUTORESET] compile-time option
** can be used to restore the legacy behavior.
**
7022
7023
7024
7025
7026
7027
7028


7029
7030
7031
7032
7033
7034
7035
** to be invoked whenever a row is updated, inserted or deleted in
** a [rowid table].
** ^Any callback set by a previous call to this function
** for the same database connection is overridden.
**
** ^The second argument is a pointer to the function to invoke when a
** row is updated, inserted or deleted in a rowid table.


** ^The first argument to the callback is a copy of the third argument
** to sqlite3_update_hook().
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
** or [SQLITE_UPDATE], depending on the operation that caused the callback
** to be invoked.
** ^The third and fourth arguments to the callback contain pointers to the
** database and table name containing the affected row.







>
>







7067
7068
7069
7070
7071
7072
7073
7074
7075
7076
7077
7078
7079
7080
7081
7082
** to be invoked whenever a row is updated, inserted or deleted in
** a [rowid table].
** ^Any callback set by a previous call to this function
** for the same database connection is overridden.
**
** ^The second argument is a pointer to the function to invoke when a
** row is updated, inserted or deleted in a rowid table.
** ^The update hook is disabled by invoking sqlite3_update_hook()
** with a NULL pointer as the second parameter.
** ^The first argument to the callback is a copy of the third argument
** to sqlite3_update_hook().
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
** or [SQLITE_UPDATE], depending on the operation that caused the callback
** to be invoked.
** ^The third and fourth arguments to the callback contain pointers to the
** database and table name containing the affected row.
Changes to skins/default/css.txt.
354
355
356
357
358
359
360
361
362
363

364
365
366
367
368
369
370
  padding: 0 4px;
}
.content pre, table.numbered-lines > tbody > tr {
  hyphens: none;
  line-height: 1.25;
}

.content ul:not(.browser) li {
  list-style-type: disc;
}

div.filetree ul li.dir,
div.filetree ul li.subdir,
div.filetree ul li.file{
  list-style-type: none;
}

.artifact > .content table,







|


>







354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
  padding: 0 4px;
}
.content pre, table.numbered-lines > tbody > tr {
  hyphens: none;
  line-height: 1.25;
}

.content ul:not(.browser) > li {
  list-style-type: disc;
}

div.filetree ul li.dir,
div.filetree ul li.subdir,
div.filetree ul li.file{
  list-style-type: none;
}

.artifact > .content table,
Changes to src/add.c.
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  /* Cached setting "manifest" */
  static int cachedManifest = -1;
  static int numManifests;

  if( cachedManifest == -1 ){
    int i;
    Blob repo;
    cachedManifest = db_get_manifest_setting();
    numManifests = 0;
    for(i=0; i<count(aManifestflags); i++){
      if( cachedManifest&aManifestflags[i].flg ) {
        azManifests[numManifests++] = aManifestflags[i].fname;
      }
    }
    blob_zero(&repo);







|







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  /* Cached setting "manifest" */
  static int cachedManifest = -1;
  static int numManifests;

  if( cachedManifest == -1 ){
    int i;
    Blob repo;
    cachedManifest = db_get_manifest_setting(0);
    numManifests = 0;
    for(i=0; i<count(aManifestflags); i++){
      if( cachedManifest&aManifestflags[i].flg ) {
        azManifests[numManifests++] = aManifestflags[i].fname;
      }
    }
    blob_zero(&repo);
Changes to src/alerts.c.
49
50
51
52
53
54
55

56
57
58
59
60
61
62
@ --     a - Announcements
@ --     c - Check-ins
@ --     f - Forum posts
@ --     k - ** Special: Unsubscribed using /oneclickunsub
@ --     n - New forum threads
@ --     r - Replies to my own forum posts
@ --     t - Ticket changes

@ --     w - Wiki changes
@ --     x - Edits to forum posts
@ -- Probably different codes will be added in the future.  In the future
@ -- we might also add a separate table that allows subscribing to email
@ -- notifications for specific branches or tags or tickets.
@ --
@ CREATE TABLE repository.subscriber(







>







49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@ --     a - Announcements
@ --     c - Check-ins
@ --     f - Forum posts
@ --     k - ** Special: Unsubscribed using /oneclickunsub
@ --     n - New forum threads
@ --     r - Replies to my own forum posts
@ --     t - Ticket changes
@ --     u - Elevation of users' permissions (admins only)
@ --     w - Wiki changes
@ --     x - Edits to forum posts
@ -- Probably different codes will be added in the future.  In the future
@ -- we might also add a separate table that allows subscribing to email
@ -- notifications for specific branches or tags or tickets.
@ --
@ CREATE TABLE repository.subscriber(
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
** are sent when email-send-method is "relay".  There should be an
** SMTP server configured as a Mail Submission Agent listening on the
** designated host and port and all times.
*/


/*
** COMMAND: alerts*
**
** Usage: %fossil alerts SUBCOMMAND ARGS...
**
** Subcommands:
**
**    pending                 Show all pending alerts.  Useful for debugging.
**







|







1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
** are sent when email-send-method is "relay".  There should be an
** SMTP server configured as a Mail Submission Agent listening on the
** designated host and port and all times.
*/


/*
** COMMAND: alerts*                     abbrv-subcom
**
** Usage: %fossil alerts SUBCOMMAND ARGS...
**
** Subcommands:
**
**    pending                 Show all pending alerts.  Useful for debugging.
**
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
      }
      db_set(pSetting->name/*works-like:""*/, g.argv[4], isGlobal);
      g.argc = 3;
    }
    pSetting = setting_info(&nSetting);
    for(; nSetting>0; nSetting--, pSetting++ ){
      if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
      print_setting(pSetting, 0);
    }
  }else
  if( strncmp(zCmd, "status", nCmd)==0 ){
    Stmt q;
    int iCutoff;
    int nSetting, n;
    static const char *zFmt = "%-29s %d\n";
    const Setting *pSetting = setting_info(&nSetting);
    db_open_config(1, 0);
    verify_all_options();
    if( g.argc!=3 ) usage("status");
    pSetting = setting_info(&nSetting);
    for(; nSetting>0; nSetting--, pSetting++ ){
      if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
      print_setting(pSetting, 0);
    }
    n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep");
    fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n);
    n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest");
    fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n);
    db_prepare(&q,
       "SELECT"







|














|







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
      }
      db_set(pSetting->name/*works-like:""*/, g.argv[4], isGlobal);
      g.argc = 3;
    }
    pSetting = setting_info(&nSetting);
    for(; nSetting>0; nSetting--, pSetting++ ){
      if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
      print_setting(pSetting, 0, 0);
    }
  }else
  if( strncmp(zCmd, "status", nCmd)==0 ){
    Stmt q;
    int iCutoff;
    int nSetting, n;
    static const char *zFmt = "%-29s %d\n";
    const Setting *pSetting = setting_info(&nSetting);
    db_open_config(1, 0);
    verify_all_options();
    if( g.argc!=3 ) usage("status");
    pSetting = setting_info(&nSetting);
    for(; nSetting>0; nSetting--, pSetting++ ){
      if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
      print_setting(pSetting, 0, 0);
    }
    n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep");
    fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n);
    n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest");
    fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n);
    db_prepare(&q,
       "SELECT"
1561
1562
1563
1564
1565
1566
1567

1568
1569
1570
1571
1572
1573
1574
    if( suname && suname[0]==0 ) suname = 0;
    if( PB("sa") ) ssub[nsub++] = 'a';
    if( g.perm.Read && PB("sc") )    ssub[nsub++] = 'c';
    if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
    if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n';
    if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r';
    if( g.perm.RdTkt && PB("st") )   ssub[nsub++] = 't';

    if( g.perm.RdWiki && PB("sw") )  ssub[nsub++] = 'w';
    if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
    ssub[nsub] = 0;
    zCode = db_text(0,
      "INSERT INTO subscriber(semail,suname,"
      "  sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip,lastContact)"
      "VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q,now()/86400)"







>







1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
    if( suname && suname[0]==0 ) suname = 0;
    if( PB("sa") ) ssub[nsub++] = 'a';
    if( g.perm.Read && PB("sc") )    ssub[nsub++] = 'c';
    if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
    if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n';
    if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r';
    if( g.perm.RdTkt && PB("st") )   ssub[nsub++] = 't';
    if( g.perm.Admin && PB("su") )   ssub[nsub++] = 'u';
    if( g.perm.RdWiki && PB("sw") )  ssub[nsub++] = 'w';
    if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
    ssub[nsub] = 0;
    zCode = db_text(0,
      "INSERT INTO subscriber(semail,suname,"
      "  sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip,lastContact)"
      "VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q,now()/86400)"
1625
1626
1627
1628
1629
1630
1631

1632
1633
1634
1635
1636
1637
1638
    ** subscription options to "on" */
    cgi_set_parameter_nocopy("sa","1",1);
    if( g.perm.Read )    cgi_set_parameter_nocopy("sc","1",1);
    if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1);
    if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1);
    if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1);
    if( g.perm.RdTkt )   cgi_set_parameter_nocopy("st","1",1);

    if( g.perm.RdWiki )  cgi_set_parameter_nocopy("sw","1",1);
  }
  @ <p>To receive email notifications for changes to this
  @ repository, fill out the form below and press the "Submit" button.</p>
  form_begin(0, "%R/subscribe");
  @ <table class="subscribe">
  @ <tr>







>







1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
    ** subscription options to "on" */
    cgi_set_parameter_nocopy("sa","1",1);
    if( g.perm.Read )    cgi_set_parameter_nocopy("sc","1",1);
    if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1);
    if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1);
    if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1);
    if( g.perm.RdTkt )   cgi_set_parameter_nocopy("st","1",1);
    if( g.perm.Admin )   cgi_set_parameter_nocopy("su","1",1);
    if( g.perm.RdWiki )  cgi_set_parameter_nocopy("sw","1",1);
  }
  @ <p>To receive email notifications for changes to this
  @ repository, fill out the form below and press the "Submit" button.</p>
  form_begin(0, "%R/subscribe");
  @ <table class="subscribe">
  @ <tr>
1697
1698
1699
1700
1701
1702
1703




1704
1705
1706
1707
1708
1709
1710
    @  <label><input type="checkbox" name="st" %s(PCK("st"))> \
    @  Ticket changes</label><br>
  }
  if( g.perm.RdWiki ){
    @  <label><input type="checkbox" name="sw" %s(PCK("sw"))> \
    @  Wiki</label><br>
  }




  di = PB("di");
  @ </td></tr>
  @ <tr>
  @  <td class="form_label">Delivery:</td>
  @  <td><select size="1" name="di">
  @     <option value="0" %s(di?"":"selected")>Individual Emails</option>
  @     <option value="1" %s(di?"selected":"")>Daily Digest</option>







>
>
>
>







1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
    @  <label><input type="checkbox" name="st" %s(PCK("st"))> \
    @  Ticket changes</label><br>
  }
  if( g.perm.RdWiki ){
    @  <label><input type="checkbox" name="sw" %s(PCK("sw"))> \
    @  Wiki</label><br>
  }
  if( g.perm.Admin ){
    @  <label><input type="checkbox" name="su" %s(PCK("su"))> \
    @  User permission elevation</label>
  }
  di = PB("di");
  @ </td></tr>
  @ <tr>
  @  <td class="form_label">Delivery:</td>
  @  <td><select size="1" name="di">
  @     <option value="0" %s(di?"":"selected")>Individual Emails</option>
  @     <option value="1" %s(di?"selected":"")>Daily Digest</option>
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
**         in length.) This uniquely identifies the subscriber without
**         revealing the complete subscriber code, and hence without
**         verifying the email address.
*/
void alert_page(void){
  const char *zName = 0;        /* Value of the name= query parameter */
  Stmt q;                       /* For querying the database */
  int sa, sc, sf, st, sw, sx;   /* Types of notifications requested */
  int sn, sr;
  int sdigest = 0, sdonotcall = 0, sverified = 0;  /* Other fields */
  int isLogin;                  /* True if logged in as an individual */
  const char *ssub = 0;         /* Subscription flags */
  const char *semail = 0;       /* Email address */
  const char *smip;             /* */
  const char *suname = 0;       /* Corresponding user.login value */







|







1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
**         in length.) This uniquely identifies the subscriber without
**         revealing the complete subscriber code, and hence without
**         verifying the email address.
*/
void alert_page(void){
  const char *zName = 0;        /* Value of the name= query parameter */
  Stmt q;                       /* For querying the database */
  int sa, sc, sf, st, su, sw, sx; /* Types of notifications requested */
  int sn, sr;
  int sdigest = 0, sdonotcall = 0, sverified = 0;  /* Other fields */
  int isLogin;                  /* True if logged in as an individual */
  const char *ssub = 0;         /* Subscription flags */
  const char *semail = 0;       /* Email address */
  const char *smip;             /* */
  const char *suname = 0;       /* Corresponding user.login value */
1879
1880
1881
1882
1883
1884
1885

1886
1887
1888
1889
1890
1891
1892
    semail = P("semail");
    if( PB("sa") )                   newSsub[nsub++] = 'a';
    if( g.perm.Read && PB("sc") )    newSsub[nsub++] = 'c';
    if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f';
    if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n';
    if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r';
    if( g.perm.RdTkt && PB("st") )   newSsub[nsub++] = 't';

    if( g.perm.RdWiki && PB("sw") )  newSsub[nsub++] = 'w';
    if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x';
    newSsub[nsub] = 0;
    ssub = newSsub;
    blob_init(&update, "UPDATE subscriber SET", -1);
    blob_append_sql(&update,
        " sdonotcall=%d,"







>







1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
    semail = P("semail");
    if( PB("sa") )                   newSsub[nsub++] = 'a';
    if( g.perm.Read && PB("sc") )    newSsub[nsub++] = 'c';
    if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f';
    if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n';
    if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r';
    if( g.perm.RdTkt && PB("st") )   newSsub[nsub++] = 't';
    if( g.perm.Admin && PB("su") )   newSsub[nsub++] = 'u';
    if( g.perm.RdWiki && PB("sw") )  newSsub[nsub++] = 'w';
    if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x';
    newSsub[nsub] = 0;
    ssub = newSsub;
    blob_init(&update, "UPDATE subscriber SET", -1);
    blob_append_sql(&update,
        " sdonotcall=%d,"
1977
1978
1979
1980
1981
1982
1983

1984
1985
1986
1987
1988
1989
1990
  }
  sa = strchr(ssub,'a')!=0;
  sc = strchr(ssub,'c')!=0;
  sf = strchr(ssub,'f')!=0;
  sn = strchr(ssub,'n')!=0;
  sr = strchr(ssub,'r')!=0;
  st = strchr(ssub,'t')!=0;

  sw = strchr(ssub,'w')!=0;
  sx = strchr(ssub,'x')!=0;
  smip = db_column_text(&q, 5);
  mtime = db_column_text(&q, 7);
  sctime = db_column_text(&q, 8);
  if( !g.perm.Admin && !sverified ){
    if( nName==64 ){







>







1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
  }
  sa = strchr(ssub,'a')!=0;
  sc = strchr(ssub,'c')!=0;
  sf = strchr(ssub,'f')!=0;
  sn = strchr(ssub,'n')!=0;
  sr = strchr(ssub,'r')!=0;
  st = strchr(ssub,'t')!=0;
  su = strchr(ssub,'u')!=0;
  sw = strchr(ssub,'w')!=0;
  sx = strchr(ssub,'x')!=0;
  smip = db_column_text(&q, 5);
  mtime = db_column_text(&q, 7);
  sctime = db_column_text(&q, 8);
  if( !g.perm.Admin && !sverified ){
    if( nName==64 ){
2095
2096
2097
2098
2099
2100
2101
2102








2103
2104
2105
2106
2107
2108
2109
  }
  if( g.perm.RdTkt ){
    @  <label><input type="checkbox" name="st" %s(st?"checked":"")>\
    @  Ticket changes</label><br>
  }
  if( g.perm.RdWiki ){
    @  <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
    @  Wiki</label>








  }
  @ </td></tr>
  if( strchr(ssub,'k')!=0 ){
    @ <tr><td></td><td>&nbsp;&uarr;&nbsp;
    @ Note: User did a one-click unsubscribe</td></tr>
  }
  @ <tr>







|
>
>
>
>
>
>
>
>







2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
  }
  if( g.perm.RdTkt ){
    @  <label><input type="checkbox" name="st" %s(st?"checked":"")>\
    @  Ticket changes</label><br>
  }
  if( g.perm.RdWiki ){
    @  <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
    @  Wiki</label><br>
  }
  if( g.perm.Admin ){
    /* Corner-case bug: if an admin assigns 'u' to a non-admin, that
    ** subscription will get removed if the user later edits their
    ** subscriptions, as non-admins are not permitted to add that
    ** subscription. */
    @  <label><input type="checkbox" name="su" %s(su?"checked":"")>\
    @  User permission elevation</label>
  }
  @ </td></tr>
  if( strchr(ssub,'k')!=0 ){
    @ <tr><td></td><td>&nbsp;&uarr;&nbsp;
    @ Note: User did a one-click unsubscribe</td></tr>
  }
  @ <tr>
2527
2528
2529
2530
2531
2532
2533

2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
**
**      c       A new check-in
**      f       An original forum post
**      n       New forum threads
**      r       Replies to my forum posts
**      x       An edit to a prior forum post
**      t       A new ticket or a change to an existing ticket

**      w       A change to a wiki page
**      x       Edits to forum posts
*/
struct EmailEvent {
  int type;          /* 'c', 'f', 'n', 'r', 't', 'w', 'x' */
  int needMod;       /* Pending moderator approval */
  Blob hdr;          /* Header content, for forum entries */
  Blob txt;          /* Text description to appear in an alert */
  char *zFromName;   /* Human name of the sender */
  char *zPriors;     /* Upthread sender IDs for forum posts */
  EmailEvent *pNext; /* Next in chronological order */
};







>




|







2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
**
**      c       A new check-in
**      f       An original forum post
**      n       New forum threads
**      r       Replies to my forum posts
**      x       An edit to a prior forum post
**      t       A new ticket or a change to an existing ticket
**      u       A user was added or received new permissions
**      w       A change to a wiki page
**      x       Edits to forum posts
*/
struct EmailEvent {
  int type;          /* 'c', 'f', 'n', 'r', 't', 'u', 'w', 'x' */
  int needMod;       /* Pending moderator approval */
  Blob hdr;          /* Header content, for forum entries */
  Blob txt;          /* Text description to appear in an alert */
  char *zFromName;   /* Human name of the sender */
  char *zPriors;     /* Upthread sender IDs for forum posts */
  EmailEvent *pNext; /* Next in chronological order */
};
2930
2931
2932
2933
2934
2935
2936

2937
2938
2939
2940
2941
2942
2943
    "on the %s Fossil repository at %s:\n\n",
    zRepoName, zUrl
  );
  if( strchr(zSub, 'a') )  blob_appendf(pBody, "  *  Announcements\n");
  if( strchr(zSub, 'c') )  blob_appendf(pBody, "  *  Check-ins\n");
  if( strchr(zSub, 'f') )  blob_appendf(pBody, "  *  Forum posts\n");
  if( strchr(zSub, 't') )  blob_appendf(pBody, "  *  Ticket changes\n");

  if( strchr(zSub, 'w') )  blob_appendf(pBody, "  *  Wiki changes\n");
  blob_appendf(pBody, "\n"
    "If you take no action, your subscription will expire and you will be\n"
    "unsubscribed in about %d days.  To make other changes or to unsubscribe\n"
    "immediately, visit the following webpage:\n\n"
    "  %s/alerts/%s\n\n",
    ALERT_RENEWAL_MSG_FREQUENCY, zUrl, zCode







>







2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
    "on the %s Fossil repository at %s:\n\n",
    zRepoName, zUrl
  );
  if( strchr(zSub, 'a') )  blob_appendf(pBody, "  *  Announcements\n");
  if( strchr(zSub, 'c') )  blob_appendf(pBody, "  *  Check-ins\n");
  if( strchr(zSub, 'f') )  blob_appendf(pBody, "  *  Forum posts\n");
  if( strchr(zSub, 't') )  blob_appendf(pBody, "  *  Ticket changes\n");
  if( strchr(zSub, 'u') )  blob_appendf(pBody, "  *  User permission elevation\n");
  if( strchr(zSub, 'w') )  blob_appendf(pBody, "  *  Wiki changes\n");
  blob_appendf(pBody, "\n"
    "If you take no action, your subscription will expire and you will be\n"
    "unsubscribed in about %d days.  To make other changes or to unsubscribe\n"
    "immediately, visit the following webpage:\n\n"
    "  %s/alerts/%s\n\n",
    ALERT_RENEWAL_MSG_FREQUENCY, zUrl, zCode
3142
3143
3144
3145
3146
3147
3148

3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164

3165
3166
3167
3168
3169
3170
3171
        char xType = '*';
        if( strpbrk(zCap,"as")==0 ){
          switch( p->type ){
            case 'x': case 'f':
            case 'n': case 'r':  xType = '5';  break;
            case 't':            xType = 'q';  break;
            case 'w':            xType = 'l';  break;

          }
          if( strchr(zCap,xType)==0 ) continue;
        }
      }else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){
        /* Setup and admin users can get any notification that does not
        ** require moderation */
      }else{
        /* Other users only see the alert if they have sufficient
        ** privilege to view the event itself */
        char xType = '*';
        switch( p->type ){
          case 'c':            xType = 'o';  break;
          case 'x': case 'f':
          case 'n': case 'r':  xType = '2';  break;
          case 't':            xType = 'r';  break;
          case 'w':            xType = 'j';  break;

        }
        if( strchr(zCap,xType)==0 ) continue;
      }
      if( blob_size(&p->hdr)>0 ){
        /* This alert should be sent as a separate email */
        Blob fhdr, fbody;
        blob_init(&fhdr, 0, 0);







>
















>







3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
        char xType = '*';
        if( strpbrk(zCap,"as")==0 ){
          switch( p->type ){
            case 'x': case 'f':
            case 'n': case 'r':  xType = '5';  break;
            case 't':            xType = 'q';  break;
            case 'w':            xType = 'l';  break;
            /* Note: case 'u' is not handled here */
          }
          if( strchr(zCap,xType)==0 ) continue;
        }
      }else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){
        /* Setup and admin users can get any notification that does not
        ** require moderation */
      }else{
        /* Other users only see the alert if they have sufficient
        ** privilege to view the event itself */
        char xType = '*';
        switch( p->type ){
          case 'c':            xType = 'o';  break;
          case 'x': case 'f':
          case 'n': case 'r':  xType = '2';  break;
          case 't':            xType = 'r';  break;
          case 'w':            xType = 'j';  break;
          /* Note: case 'u' is not handled here */
        }
        if( strchr(zCap,xType)==0 ) continue;
      }
      if( blob_size(&p->hdr)>0 ){
        /* This alert should be sent as a separate email */
        Blob fhdr, fbody;
        blob_init(&fhdr, 0, 0);
Changes to src/allrepo.c.
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  int i;
  for(i=iStart; i<g.argc; i++){
    blob_appendf(pExtra, " %s", g.argv[i]);
  }
}

/*
** COMMAND: all
**
** Usage: %fossil all SUBCOMMAND ...
**
** The ~/.fossil file records the location of all repositories for a
** user.  This command performs certain operations on all repositories
** that can be useful before or after a period of disconnected operation.
**







|







49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  int i;
  for(i=iStart; i<g.argc; i++){
    blob_appendf(pExtra, " %s", g.argv[i]);
  }
}

/*
** COMMAND: all               abbrv-subcom
**
** Usage: %fossil all SUBCOMMAND ...
**
** The ~/.fossil file records the location of all repositories for a
** user.  This command performs certain operations on all repositories
** that can be useful before or after a period of disconnected operation.
**
314
315
316
317
318
319
320

321
322
323
324
325
326
327
    }
  }else if( fossil_strcmp(zCmd, "repack")==0 ){
    zCmd = "repack";
  }else if( fossil_strcmp(zCmd, "set")==0
            || fossil_strcmp(zCmd, "setting")==0
            || fossil_strcmp(zCmd, "settings")==0 ){
    zCmd = "settings -R";

    collect_argv(&extra, 3);
  }else if( fossil_strcmp(zCmd, "unset")==0 ){
    zCmd = "unset -R";
    collect_argv(&extra, 3);
  }else if( fossil_strcmp(zCmd, "fts-config")==0 ){
    zCmd = "fts-config -R";
    collect_argv(&extra, 3);







>







314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
    }
  }else if( fossil_strcmp(zCmd, "repack")==0 ){
    zCmd = "repack";
  }else if( fossil_strcmp(zCmd, "set")==0
            || fossil_strcmp(zCmd, "setting")==0
            || fossil_strcmp(zCmd, "settings")==0 ){
    zCmd = "settings -R";
    collect_argument(&extra, "changed", 0);
    collect_argv(&extra, 3);
  }else if( fossil_strcmp(zCmd, "unset")==0 ){
    zCmd = "unset -R";
    collect_argv(&extra, 3);
  }else if( fossil_strcmp(zCmd, "fts-config")==0 ){
    zCmd = "fts-config -R";
    collect_argv(&extra, 3);
Changes to src/backlink.c.
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
  );
  backlink_extract(blob_str(&in),mimetype,srcid,srctype,mtime,0);
  blob_reset(&in);
}


/*
** COMMAND: test-wiki-relink
**
** Usage: %fossil test-wiki-relink  WIKI-PAGE-NAME
**
** Run the backlink_wiki_refresh() procedure on the wiki page
** named.  WIKI-PAGE-NAME can be a glob pattern or a prefix
** of the wiki page.
*/
void test_wiki_relink_cmd(void){
  Stmt q;







|

|







429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
  );
  backlink_extract(blob_str(&in),mimetype,srcid,srctype,mtime,0);
  blob_reset(&in);
}


/*
** COMMAND: test-relink-wiki
**
** Usage: %fossil test-relink-wiki  WIKI-PAGE-NAME
**
** Run the backlink_wiki_refresh() procedure on the wiki page
** named.  WIKI-PAGE-NAME can be a glob pattern or a prefix
** of the wiki page.
*/
void test_wiki_relink_cmd(void){
  Stmt q;
Changes to src/bisect.c.
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
** Find the shortest path between bad and good.
*/
void bisect_path(void){
  PathNode *p;
  bisect.bad = db_lget_int("bisect-bad", 0);
  bisect.good = db_lget_int("bisect-good", 0);
  if( bisect.good>0 && bisect.bad==0 ){
    path_shortest(bisect.good, bisect.good, 0, 0, 0);
  }else if( bisect.bad>0 && bisect.good==0 ){
    path_shortest(bisect.bad, bisect.bad, 0, 0, 0);
  }else if( bisect.bad==0 && bisect.good==0 ){
    fossil_fatal("neither \"good\" nor \"bad\" versions have been identified");
  }else{
    Bag skip;
    int bDirect = bisect_option("direct-only");
    char *zLog = db_lget("bisect-log","");
    Blob log, id;
    bag_init(&skip);
    blob_init(&log, zLog, -1);
    while( blob_token(&log, &id) ){
      if( blob_str(&id)[0]=='s' ){
        bag_insert(&skip, atoi(blob_str(&id)+1));
      }
    }
    blob_reset(&log);
    p = path_shortest(bisect.good, bisect.bad, bDirect, 0, &skip);
    bag_clear(&skip);
    if( p==0 ){
      char *zBad = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.bad);
      char *zGood = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.good);
      fossil_fatal("no path from good ([%S]) to bad ([%S]) or back",
                   zGood, zBad);
    }







|

|















|







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
** Find the shortest path between bad and good.
*/
void bisect_path(void){
  PathNode *p;
  bisect.bad = db_lget_int("bisect-bad", 0);
  bisect.good = db_lget_int("bisect-good", 0);
  if( bisect.good>0 && bisect.bad==0 ){
    path_shortest(bisect.good, bisect.good, 0, 0, 0, 0);
  }else if( bisect.bad>0 && bisect.good==0 ){
    path_shortest(bisect.bad, bisect.bad, 0, 0, 0, 0);
  }else if( bisect.bad==0 && bisect.good==0 ){
    fossil_fatal("neither \"good\" nor \"bad\" versions have been identified");
  }else{
    Bag skip;
    int bDirect = bisect_option("direct-only");
    char *zLog = db_lget("bisect-log","");
    Blob log, id;
    bag_init(&skip);
    blob_init(&log, zLog, -1);
    while( blob_token(&log, &id) ){
      if( blob_str(&id)[0]=='s' ){
        bag_insert(&skip, atoi(blob_str(&id)+1));
      }
    }
    blob_reset(&log);
    p = path_shortest(bisect.good, bisect.bad, bDirect, 0, &skip, 0);
    bag_clear(&skip);
    if( p==0 ){
      char *zBad = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.bad);
      char *zGood = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.good);
      fossil_fatal("no path from good ([%S]) to bad ([%S]) or back",
                   zGood, zBad);
    }
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
    }
  }
  if( iCurrent>0 ){
    bisect_log_append(&ins, ++cnt, "CURRENT", iCurrent);
  }
  if( bDetail && lastGood>0 && lastBad>0 ){
    PathNode *p;
    p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0);
    while( p ){
      bisect_log_append(&ins, ++cnt, 0, p->rid);
      p = p->u.pTo;
    }
    path_reset();
  }
  db_exec_sql(blob_sql_text(&ins));







|







290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
    }
  }
  if( iCurrent>0 ){
    bisect_log_append(&ins, ++cnt, "CURRENT", iCurrent);
  }
  if( bDetail && lastGood>0 && lastBad>0 ){
    PathNode *p;
    p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0, 0);
    while( p ){
      bisect_log_append(&ins, ++cnt, 0, p->rid);
      p = p->u.pTo;
    }
    path_reset();
  }
  db_exec_sql(blob_sql_text(&ins));
Changes to src/blob.c.
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
    /* Make sure the blob contains two terminating 0-bytes */
    blob_append(pBlob, "\000\000", 3);
    zUtf8 = blob_str(pBlob) + bomSize;
    zUtf8 = fossil_unicode_to_utf8(zUtf8);
    blob_reset(pBlob);
    blob_set_dynamic(pBlob, zUtf8);
  }else if( useMbcs && invalid_utf8(pBlob) ){
#if defined(_WIN32) || defined(__CYGWIN__)
    zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob));
    blob_reset(pBlob);
    blob_append(pBlob, zUtf8, -1);
    fossil_mbcs_free(zUtf8);
#else
    blob_cp1252_to_utf8(pBlob);
#endif /* _WIN32 */







|







1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
    /* Make sure the blob contains two terminating 0-bytes */
    blob_append(pBlob, "\000\000", 3);
    zUtf8 = blob_str(pBlob) + bomSize;
    zUtf8 = fossil_unicode_to_utf8(zUtf8);
    blob_reset(pBlob);
    blob_set_dynamic(pBlob, zUtf8);
  }else if( useMbcs && invalid_utf8(pBlob) ){
#if defined(_WIN32)
    zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob));
    blob_reset(pBlob);
    blob_append(pBlob, zUtf8, -1);
    fossil_mbcs_free(zUtf8);
#else
    blob_cp1252_to_utf8(pBlob);
#endif /* _WIN32 */
Changes to src/branch.c.
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
** COMMAND: branch
**
** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS?
**
** Run various subcommands to manage branches of the open repository or
** of the repository identified by the -R or --repository option.
**
** >  fossil branch close|reopen ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
**
**       Adds or cancels the "closed" tag to one or more branches.
**       It accepts arbitrary unambiguous symbolic names but
**       will only resolve check-in names and skips any which resolve
**       to non-leaf check-ins.
**
**       Options:
**         -n|--dry-run          Do not commit changes, but dump artifact
**                               to stdout
**         -v|--verbose          Output more information
**         --date-override DATE  DATE to use instead of 'now'
**         --user-override USER  USER to use instead of the current default
**
** >  fossil branch current
**
**        Print the name of the branch for the current check-out
**
** >  fossil branch hide|unhide ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
**
**       Adds or cancels the "hidden" tag for the specified branches or
**       or check-in IDs. Accepts the same options as the close
**       subcommand.
**
** >  fossil branch info BRANCH-NAME
**
**        Print information about a branch
**
** >  fossil branch list|ls ?OPTIONS? ?GLOB?
** >  fossil branch lsh ?OPTIONS? ?LIMIT?
**
**        List all branches.
**
**        Options:
**          -a|--all         List all branches.  Default show only open branches
**          -c|--closed      List closed branches
**          -m|--merged      List branches merged into the current branch







|













|



|





|



|
|







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
** COMMAND: branch
**
** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS?
**
** Run various subcommands to manage branches of the open repository or
** of the repository identified by the -R or --repository option.
**
** > fossil branch close|reopen ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
**
**       Adds or cancels the "closed" tag to one or more branches.
**       It accepts arbitrary unambiguous symbolic names but
**       will only resolve check-in names and skips any which resolve
**       to non-leaf check-ins.
**
**       Options:
**         -n|--dry-run          Do not commit changes, but dump artifact
**                               to stdout
**         -v|--verbose          Output more information
**         --date-override DATE  DATE to use instead of 'now'
**         --user-override USER  USER to use instead of the current default
**
** > fossil branch current
**
**        Print the name of the branch for the current check-out
**
** > fossil branch hide|unhide ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
**
**       Adds or cancels the "hidden" tag for the specified branches or
**       or check-in IDs. Accepts the same options as the close
**       subcommand.
**
** > fossil branch info BRANCH-NAME
**
**        Print information about a branch
**
** > fossil branch list|ls ?OPTIONS? ?GLOB?
** > fossil branch lsh ?OPTIONS? ?LIMIT?
**
**        List all branches.
**
**        Options:
**          -a|--all         List all branches.  Default show only open branches
**          -c|--closed      List closed branches
**          -m|--merged      List branches merged into the current branch
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
**        If GLOB is given, show only branches matching the pattern.
**
**        The "lsh" variant of this subcommand shows recently changed branches,
**        and accepts an optional LIMIT argument (defaults to 5) to cap output,
**        but no GLOB argument.  All other options are supported, with -t being
**        an implied no-op.
**
** >  fossil branch new BRANCH-NAME BASIS ?OPTIONS?
**
**        Create a new branch BRANCH-NAME off of check-in BASIS.
**
**        Options:
**          --private             Branch is private (i.e., remains local)
**          --bgcolor COLOR       Use COLOR instead of automatic background
**          --nosign              Do not sign the manifest for the check-in







|







651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
**        If GLOB is given, show only branches matching the pattern.
**
**        The "lsh" variant of this subcommand shows recently changed branches,
**        and accepts an optional LIMIT argument (defaults to 5) to cap output,
**        but no GLOB argument.  All other options are supported, with -t being
**        an implied no-op.
**
** > fossil branch new BRANCH-NAME BASIS ?OPTIONS?
**
**        Create a new branch BRANCH-NAME off of check-in BASIS.
**
**        Options:
**          --private             Branch is private (i.e., remains local)
**          --bgcolor COLOR       Use COLOR instead of automatic background
**          --nosign              Do not sign the manifest for the check-in
868
869
870
871
872
873
874

875
876
877
878
879
880
881
882
    const char *zMergeTo = db_column_text(&q, 3);
    int nCkin = db_column_int(&q, 4);
    const char *zLastCkin = db_column_text(&q, 5);
    const char *zBgClr = db_column_text(&q, 6);
    char *zAge = human_readable_age(rNow - rMtime);
    sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0);
    if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0;

    if( zBgClr == 0 ){
      if( zBranch==0 || strcmp(zBranch,"trunk")==0 ){
        zBgClr = 0;
      }else{
        zBgClr = hash_color(zBranch);
      }
    }
    if( zBgClr && zBgClr[0] && show_colors ){







>
|







868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
    const char *zMergeTo = db_column_text(&q, 3);
    int nCkin = db_column_int(&q, 4);
    const char *zLastCkin = db_column_text(&q, 5);
    const char *zBgClr = db_column_text(&q, 6);
    char *zAge = human_readable_age(rNow - rMtime);
    sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0);
    if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0;
    if( zBgClr ) zBgClr = reasonable_bg_color(zBgClr, 0);
    if( zBgClr==0 ){
      if( zBranch==0 || strcmp(zBranch,"trunk")==0 ){
        zBgClr = 0;
      }else{
        zBgClr = hash_color(zBranch);
      }
    }
    if( zBgClr && zBgClr[0] && show_colors ){
Changes to src/builtin.c.
50
51
52
53
54
55
56






57
58
59
60
61
62
63










64
65
66
67
68
69
70
    }
  }
  return -1;
}

/*
** Return a pointer to built-in content






*/
const unsigned char *builtin_file(const char *zFilename, int *piSize){
  int i = builtin_file_index(zFilename);
  if( i>=0 ){
    if( piSize ) *piSize = aBuiltinFiles[i].nByte;
    return aBuiltinFiles[i].pData;
  }else{










    if( piSize ) *piSize = 0;
    return 0;
  }
}
const char *builtin_text(const char *zFilename){
  return (char*)builtin_file(zFilename, 0);
}







>
>
>
>
>
>







>
>
>
>
>
>
>
>
>
>







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
    }
  }
  return -1;
}

/*
** Return a pointer to built-in content
**
** If the filename contains "-vNNNNNNNN" just before the final file
** suffix, where each N is a random digit, then omit that part of the
** filename before doing the lookup.  The extra -vNNNNNNNN was added
** to defeat overly aggressive caching by web browsers.  There must be
** at least 8 digits in NNNNNNNN but more than 8 are allowed.
*/
const unsigned char *builtin_file(const char *zFilename, int *piSize){
  int i = builtin_file_index(zFilename);
  if( i>=0 ){
    if( piSize ) *piSize = aBuiltinFiles[i].nByte;
    return aBuiltinFiles[i].pData;
  }else{
    char *zV = strstr(zFilename, "-v");
    if( zV!=0 ){
      for(i=0; fossil_isdigit(zV[i+2]); i++){}
      if( i>=8 && zV[i+2]=='.' ){
        char *zNew = mprintf("%.*s%s", (int)(zV-zFilename), zFilename, zV+i+2);
        const unsigned char *pRes = builtin_file(zNew, piSize);
        fossil_free(zNew);
        return pRes;
      }
    }
    if( piSize ) *piSize = 0;
    return 0;
  }
}
const char *builtin_text(const char *zFilename){
  return (char*)builtin_file(zFilename, 0);
}
Changes to src/cache.c.
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
** database already exists.
*/
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.







|







253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
** database already exists.
*/
void cache_initialize(void){
  sqlite3_close(cacheOpen(1));
}

/*
** COMMAND: cache*                    abbrv-subcom
**
** 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.
Changes to src/cgi.c.
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
  }
  return zRef;
}


/*
** Return true if the current request is coming from the same origin.




*/
int cgi_same_origin(void){
  const char *zRef;

  int nBase;

  if( g.zBaseURL==0 ) return 0;
  zRef = P("HTTP_REFERER");
  if( zRef==0 ) return 0;





  nBase = (int)strlen(g.zBaseURL);
  if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0;

  if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0;








  return 1;
}

/*
** Return true if the current CGI request is a POST request
*/
static int cgi_is_post_request(void){
  const char *zMethod = P("REQUEST_METHOD");







>
>
>
>

|

>

>



>
>
>
>
>

|
>
|
>
>
>
>
>
>
>
>
|







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
  }
  return zRef;
}


/*
** Return true if the current request is coming from the same origin.
**
** If the request comes from a different origin and bErrorLog is true, then
** put a warning message on the error log as this was a possible hack
** attempt.
*/
int cgi_same_origin(int bErrorLog){
  const char *zRef;
  char *zToFree = 0;
  int nBase;
  int rc;
  if( g.zBaseURL==0 ) return 0;
  zRef = P("HTTP_REFERER");
  if( zRef==0 ) return 0;
  if( strchr(zRef,'%')!=0 ){
    zToFree = strdup(zRef);
    dehttpize(zToFree);
    zRef = zToFree;
  }
  nBase = (int)strlen(g.zBaseURL);
  if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ){
    rc = 0;
  }else if( zRef[nBase]!=0 && zRef[nBase]!='/' ){
    rc = 0;
  }else{
    rc = 1;
  }
  if( rc==0 && bErrorLog && fossil_strcmp(P("REQUST_METHOD"),"POST")==0 ){
    fossil_errorlog("warning: POST from different origin");
  }
  fossil_free(zToFree);
  return rc;
}

/*
** Return true if the current CGI request is a POST request
*/
static int cgi_is_post_request(void){
  const char *zMethod = P("REQUEST_METHOD");
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
**     1:   Request comes from the same origin
**     2:   (1) plus it is a POST request
**     3:   (2) plus there is a valid "csrf" token in the request
*/
int cgi_csrf_safe(int securityLevel){
  if( g.okCsrf<0 ) return 0;
  if( g.okCsrf==0 ){
    if( !cgi_same_origin() ){
      g.okCsrf = -1;
    }else{
      g.okCsrf = 1;
      if( cgi_is_post_request() ){
        g.okCsrf = 2;
        if( fossil_strcmp(P("csrf"), g.zCsrfToken)==0 ){
          g.okCsrf = 3;







|







751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
**     1:   Request comes from the same origin
**     2:   (1) plus it is a POST request
**     3:   (2) plus there is a valid "csrf" token in the request
*/
int cgi_csrf_safe(int securityLevel){
  if( g.okCsrf<0 ) return 0;
  if( g.okCsrf==0 ){
    if( !cgi_same_origin(1) ){
      g.okCsrf = -1;
    }else{
      g.okCsrf = 1;
      if( cgi_is_post_request() ){
        g.okCsrf = 2;
        if( fossil_strcmp(P("csrf"), g.zCsrfToken)==0 ){
          g.okCsrf = 3;
Changes to src/checkin.c.
1465
1466
1467
1468
1469
1470
1471

1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487























1488
1489
1490
1491
1492
1493
1494
  Blob *pComment,
  char *zInit,
  CheckinInfo *p,
  int parent_rid,
  int dryRunFlag
){
  Blob prompt;

#if defined(_WIN32) || defined(__CYGWIN__)
  int bomSize;
  const unsigned char *bom = get_utf8_bom(&bomSize);
  blob_init(&prompt, (const char *) bom, bomSize);
  if( zInit && zInit[0]){
    blob_append(&prompt, zInit, -1);
  }
#else
  blob_init(&prompt, zInit, -1);
#endif
  blob_append(&prompt,
    "\n"
    "# Enter a commit message for this check-in."
        " Lines beginning with # are ignored.\n"
    "#\n", -1
  );























  if( dryRunFlag ){
    blob_appendf(&prompt, "# DRY-RUN:  This is a test commit.  No changes "
                          "will be made to the repository\n#\n");
  }
  blob_appendf(&prompt, "# user: %s\n",
               p->zUserOvrd ? p->zUserOvrd : login_name());
  if( p->zBranch && p->zBranch[0] ){







>












|
|
|

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







1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
  Blob *pComment,
  char *zInit,
  CheckinInfo *p,
  int parent_rid,
  int dryRunFlag
){
  Blob prompt;
  int wikiFlags;
#if defined(_WIN32) || defined(__CYGWIN__)
  int bomSize;
  const unsigned char *bom = get_utf8_bom(&bomSize);
  blob_init(&prompt, (const char *) bom, bomSize);
  if( zInit && zInit[0]){
    blob_append(&prompt, zInit, -1);
  }
#else
  blob_init(&prompt, zInit, -1);
#endif
  blob_append(&prompt,
    "\n"
    "# Enter the commit message.  Formatting rules:\n"
    "#   *  Lines beginning with # are ignored.\n",
    -1
  );
  wikiFlags = wiki_convert_flags(1);
  if( wikiFlags & WIKI_LINKSONLY ){
    blob_append(&prompt,"#   *  Hyperlinks inside of [...]\n", -1);
    if( wikiFlags & WIKI_NEWLINE ){
      blob_append(&prompt,
        "#   *  Newlines are significant and are displayed as written\n", -1);
    }else{
      blob_append(&prompt,
        "#   *  Newlines are interpreted as ordinary spaces\n",
        -1
      );
    }
    blob_append(&prompt,
        "#   *  All other text will be displayed as written\n", -1);
  }else{
    blob_append(&prompt,
       "#   *  Hyperlinks:   [target]   or   [target|display-text]\n"
       "#   *  Blank lines cause a paragraph break\n"
       "#   *  Other text rendered as if it where HTML\n", -1
    );
  }
  blob_append(&prompt, "#\n", 2);

  if( dryRunFlag ){
    blob_appendf(&prompt, "# DRY-RUN:  This is a test commit.  No changes "
                          "will be made to the repository\n#\n");
  }
  blob_appendf(&prompt, "# user: %s\n",
               p->zUserOvrd ? p->zUserOvrd : login_name());
  if( p->zBranch && p->zBranch[0] ){
2280
2281
2282
2283
2284
2285
2286
















































































2287
2288
2289
2290
2291
2292
2293
2294




2295
2296
2297
2298
2299
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
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
2354
2355


2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370

2371
2372
2373
2374
2375
2376

2377
2378
2379
2380


2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
*/
static int tagCmp(const void *a, const void *b){
  char **pA = (char**)a;
  char **pB = (char**)b;
  return fossil_strcmp(pA[0], pB[0]);
}

















































































/*
** COMMAND: ci#
** COMMAND: commit
**
** Usage: %fossil commit ?OPTIONS? ?FILE...?
**    or: %fossil ci ?OPTIONS? ?FILE...?
**
** Create a new version containing all of the changes in the current




** check-out.  You will be prompted to enter a check-in comment unless
** the comment has been specified on the command-line using "-m" or a
** file containing the comment using -M.  The editor defined in the
** "editor" fossil option (see %fossil help set) will be used, or from
** the "VISUAL" or "EDITOR" environment variables (in that order) if
** no editor is set.
**

** All files that have changed will be committed unless some subset of
** files is specified on the command line.
**
** The --branch option followed by a branch name causes the new
** check-in to be placed in a newly-created branch with the name
** passed to the --branch option.
**
** Use the --branchcolor option followed by a color name (ex:
** '#ffc0c0') to specify the background color of entries in the new
** branch when shown in the web timeline interface.  The use of
** the --branchcolor option is not recommended.  Instead, let Fossil
** choose the branch color automatically.
**
** The --bgcolor option works like --branchcolor but only sets the
** background color for a single check-in.  Subsequent check-ins revert
** to the default color.
**
** A check-in is not permitted to fork unless the --allow-fork option
** appears.  An empty check-in (i.e. with nothing changed) is not
** allowed unless the --allow-empty option appears.  A check-in may not
** be older than its ancestor unless the --allow-older option appears.
** If any files in the check-in appear to contain unresolved merge
** conflicts, the check-in will not be allowed unless the
** --allow-conflict option is present.  In addition, the entire
** check-in process may be aborted if a file contains content that
** appears to be binary, Unicode text, or text with CR/LF line endings
** unless the interactive user chooses to proceed.  If there is no
** interactive user or these warnings should be skipped for some other
** reason, the --no-warnings option may be used.  A check-in is not
** allowed against a closed leaf.
**
** If a commit message is blank, you will be prompted:
** ("continue (y/N)?") to confirm you really want to commit with a
** blank commit message.  The default value is "N", do not commit.
**
** The --private option creates a private check-in that is never synced.
** Children of private check-ins are automatically private.
**
** The --tag option applies the symbolic tag name to the check-in.
**
** The --hash option detects edited files by computing each file's
** artifact hash rather than just checking for changes to its size or mtime.

**
** Options:
**    --allow-conflict           Allow unresolved merge conflicts
**    --allow-empty              Allow a commit with no changes
**    --allow-fork               Allow the commit to fork
**    --allow-older              Allow a commit older than its ancestor
**    --baseline                 Use a baseline manifest in the commit process
**    --bgcolor COLOR            Apply COLOR to this one check-in only
**    --branch NEW-BRANCH-NAME   Check in to this new branch
**    --branchcolor COLOR        Apply given COLOR to the branch
**    --close                    Close the branch being committed
**    --date-override DATETIME   DATE to use instead of 'now'


**    --delta                    Use a delta manifest in the commit process
**    --hash                     Verify file status using hashing rather
**                               than relying on file mtimes
**    --if-changes               Make this command a silent no-op if there
**                               are no changes
**    --ignore-clock-skew        If a clock skew is detected, ignore it and
**                               behave as if the user had entered 'yes' to
**                               the question of whether to proceed despite
**                               the skew.
**    --ignore-oversize          Do not warn the user about oversized files
**    --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

**    -v|--verbose               Show a diff in the commit message prompt
**    --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
**    --no-verify                Do not run before-commit hooks

**    --nosign                   Do not attempt to sign this commit with gpg
**    --nosync                   Do not auto-sync prior to committing
**    --override-lock            Allow a check-in even though parent is locked
**    --private                  Do not sync changes and their descendants


**    --tag TAG-NAME             Assign given tag TAG-NAME to the check-in
**    --trace                    Debug tracing
**    --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]], [[update]], [[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 */







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







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


|
<
<
<
<
<
<
<
<
<
<
<















<
<
<
<




<
|
<
>











|
>
>


|








|
|
<
|
>






>



|
>
>
|

|
<
<
|
<
<
<







2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
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
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404

2405
2406


2407
2408
2409
2410
2411
2412











2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427




2428
2429
2430
2431

2432

2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460

2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478


2479



2480
2481
2482
2483
2484
2485
2486
*/
static int tagCmp(const void *a, const void *b){
  char **pA = (char**)a;
  char **pB = (char**)b;
  return fossil_strcmp(pA[0], pB[0]);
}

/*
** SETTING: verify-comments                              width=8 default=on
**
** This setting determines how much sanity checking, if any, the 
** "fossil commit" and "fossil amend" commands do against check-in
** comments. Recognized values:
**
**     on         (Default) Check for bad syntax and/or broken hyperlinks
**                in check-in comments and offer the user a chance to
**                continue editing for interactive sessions, or simply
**                abort the commit if the comment was entered using -m or -M
**
**     off        Do not do syntax checking of any kind
**
**     preview    Do all the same checks as "on" but also always preview the
**                check-in comment to the user during interactive sessions
**                even if no obvious errors are found, and provide an
**                opportunity to accept or re-edit
*/

#if INTERFACE
#define COMCK_MARKUP    0x01  /* Check for mistakes */
#define COMCK_PREVIEW   0x02  /* Always preview, even if no issues found */
#endif /* INTERFACE */

/*
** Check for possible formatting errors in the comment string pComment.
**
** If issues are found, write an appropriate error notice, probably also
** including the complete text of the comment formatted to highlight the
** problem, to stdout and return non-zero.  The return value is some
** combination of the COMCK_* flags, depending on what went wrong.
**
** If no issues are seen, do not output anything and return zero.
*/
int verify_comment(Blob *pComment, int mFlags){
  Blob in, html;
  int mResult;
  int rc = mFlags & COMCK_PREVIEW;
  int wFlags;

  if( mFlags==0 ) return 0;
  blob_init(&in, blob_str(pComment), -1);
  blob_init(&html, 0, 0);
  wFlags = wiki_convert_flags(0);
  wFlags &= ~WIKI_NOBADLINKS;
  wFlags |= WIKI_MARK;
  mResult = wiki_convert(&in, &html, wFlags);
  if( mResult & RENDER_ANYERROR ) rc |= COMCK_MARKUP;
  if( rc ){
    int htot = ((wFlags & WIKI_NEWLINE)!=0 ? 0 : HTOT_FLOW)|HTOT_TRIM;
    Blob txt;
    if( terminal_is_vt100() ) htot |= HTOT_VT100;
    blob_init(&txt, 0, 0);
    html_to_plaintext(blob_str(&html), &txt, htot);
    if( rc & COMCK_MARKUP ){
      fossil_print("Possible format errors in the check-in comment:\n\n   ");
    }else{
      fossil_print("Preview of the check-in comment:\n\n   ");
    }
    if( wFlags & WIKI_NEWLINE ){
      Blob line;
      char *zIndent = "";
      while( blob_line(&txt, &line) ){
        fossil_print("%s%b", zIndent, &line);
        zIndent = "   ";
      }
      fossil_print("\n");
    }else{
      comment_print(blob_str(&txt), 0, 3, -1, get_comment_format());
    }
    fossil_print("\n");
    fflush(stdout);
    blob_reset(&txt);
  }
  blob_reset(&html);
  blob_reset(&in);
  return rc;
} 

/*
** COMMAND: ci#
** COMMAND: commit
**
** Usage: %fossil commit ?OPTIONS? ?FILE...?
**    or: %fossil ci ?OPTIONS? ?FILE...?
**
** Create a new check-in containing all of the changes in the current
** check-out.  All changes are committed unless some subset of files
** is specified on the command line, in which case only the named files
** become part of the new check-in.
**
** You will be prompted to enter a check-in comment unless the comment
** has been specified on the command-line using "-m" or "-M".  The

** text editor used is determined by the "editor" setting, or by the
** "VISUAL" or "EDITOR" environment variables.  Commit message text is


** interpreted as fossil-wiki format.  Potentially misformatted check-in
** comment text is detected and reported unless the --no-verify-comment
** option is used.
**
** The --branch option followed by a branch name causes the new
** check-in to be placed in a newly-created branch with name specified.











**
** A check-in is not permitted to fork unless the --allow-fork option
** appears.  An empty check-in (i.e. with nothing changed) is not
** allowed unless the --allow-empty option appears.  A check-in may not
** be older than its ancestor unless the --allow-older option appears.
** If any files in the check-in appear to contain unresolved merge
** conflicts, the check-in will not be allowed unless the
** --allow-conflict option is present.  In addition, the entire
** check-in process may be aborted if a file contains content that
** appears to be binary, Unicode text, or text with CR/LF line endings
** unless the interactive user chooses to proceed.  If there is no
** interactive user or these warnings should be skipped for some other
** reason, the --no-warnings option may be used.  A check-in is not
** allowed against a closed leaf.
**




** The --private option creates a private check-in that is never synced.
** Children of private check-ins are automatically private.
**
** The --tag option applies the symbolic tag name to the check-in.

** The --tag option can be repeated to assign multiple tags to a check-in.

** For example: "... --tag release --tag version-1.2.3 ..."
**
** Options:
**    --allow-conflict           Allow unresolved merge conflicts
**    --allow-empty              Allow a commit with no changes
**    --allow-fork               Allow the commit to fork
**    --allow-older              Allow a commit older than its ancestor
**    --baseline                 Use a baseline manifest in the commit process
**    --bgcolor COLOR            Apply COLOR to this one check-in only
**    --branch NEW-BRANCH-NAME   Check in to this new branch
**    --branchcolor COLOR        Apply given COLOR to the branch
**    --close                    Close the branch being committed
**    --date-override DATETIME   Make DATETIME the time of the check-in.
**                               Useful when importing historical check-ins
**                               from another version control system.
**    --delta                    Use a delta manifest in the commit process
**    --hash                     Verify file status using hashing rather
**                               than relying on filesystem mtimes
**    --if-changes               Make this command a silent no-op if there
**                               are no changes
**    --ignore-clock-skew        If a clock skew is detected, ignore it and
**                               behave as if the user had entered 'yes' to
**                               the question of whether to proceed despite
**                               the skew.
**    --ignore-oversize          Do not warn the user about oversized files
**    --integrate                Close all merged-in branches
**    -m|--comment COMMENT-TEXT  Use COMMENT-TEXT as the check-in comment
**    -M|--message-file FILE     Read the check-in comment from FILE

**    -n|--dry-run               Do not actually create a new check-in. Just
**                               show what would have happened. For debugging.
**    -v|--verbose               Show a diff in the commit message prompt
**    --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
**    --no-verify                Do not run before-commit hooks
**    --no-verify-comment        Do not validate the check-in comment
**    --nosign                   Do not attempt to sign this commit with gpg
**    --nosync                   Do not auto-sync prior to committing
**    --override-lock            Allow a check-in even though parent is locked
**    --private                  Never sync the resulting check-in and make
**                               all descendants private too.
**    --proxy PROXY              Use PROXY as http proxy during sync operation
**    --tag TAG-NAME             Add TAG-NAME to the check-in. May be repeated.
**    --trace                    Debug tracing
**    --user-override USER       Record USER as the login that created the


**                               new check-in, rather that the current user.



**
** See also: [[branch]], [[changes]], [[update]], [[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 */
2412
2413
2414
2415
2416
2417
2418

2419
2420
2421
2422
2423
2424
2425
  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 onlyIfChanges = 0; /* No-op if there are no changes */
  int allowFork = 0;     /* Allow the commit to fork */
  int allowOlder = 0;    /* Allow a commit older than its ancestor */

  char *zManifestFile;   /* Name of the manifest file */
  int useCksum;          /* True if checksums should be computed and verified */
  int outputManifest;    /* True to output "manifest" and "manifest.uuid" */
  int dryRunFlag;        /* True for a test run.  Debugging only */
  CheckinInfo sCiInfo;   /* Information about this check-in */
  const char *zComFile;  /* Read commit message from this file */
  int nTag = 0;          /* Number of --tag arguments */







>







2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
  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 onlyIfChanges = 0; /* No-op if there are no changes */
  int allowFork = 0;     /* Allow the commit to fork */
  int allowOlder = 0;    /* Allow a commit older than its ancestor */
  int noVerifyCom = 0;   /* Allow suspicious check-in comments */
  char *zManifestFile;   /* Name of the manifest file */
  int useCksum;          /* True if checksums should be computed and verified */
  int outputManifest;    /* True to output "manifest" and "manifest.uuid" */
  int dryRunFlag;        /* True for a test run.  Debugging only */
  CheckinInfo sCiInfo;   /* Information about this check-in */
  const char *zComFile;  /* Read commit message from this file */
  int nTag = 0;          /* Number of --tag arguments */
2437
2438
2439
2440
2441
2442
2443

2444
2445
2446
2447
2448
2449
2450
  Blob ans;              /* Answer to continuation prompts */
  char cReply;           /* First character of ans */
  int bRecheck = 0;      /* Repeat fork and closed-branch checks*/
  int bIgnoreSkew = 0;   /* --ignore-clock-skew flag */
  int mxSize;
  char *zCurBranch = 0;  /* The current branch name of checkout */
  char *zNewBranch = 0;  /* The branch name after update */


  memset(&sCiInfo, 0, sizeof(sCiInfo));
  url_proxy_options();
  /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
  useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
  noSign = find_option("nosign",0,0)!=0;
  if( find_option("nosync",0,0) ) g.fNoSync = 1;







>







2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
  Blob ans;              /* Answer to continuation prompts */
  char cReply;           /* First character of ans */
  int bRecheck = 0;      /* Repeat fork and closed-branch checks*/
  int bIgnoreSkew = 0;   /* --ignore-clock-skew flag */
  int mxSize;
  char *zCurBranch = 0;  /* The current branch name of checkout */
  char *zNewBranch = 0;  /* The branch name after update */
  int ckComFlgs;         /* Flags passed to verify_comment() */

  memset(&sCiInfo, 0, sizeof(sCiInfo));
  url_proxy_options();
  /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
  useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
  noSign = find_option("nosign",0,0)!=0;
  if( find_option("nosync",0,0) ) g.fNoSync = 1;
2467
2468
2469
2470
2471
2472
2473

2474
2475
2476
2477
2478
2479
2480
2481
2482




2483
2484


2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506






2507
2508
2509
2510
2511
2512
2513
  if( !dryRunFlag ){
    dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
  }
  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;

  onlyIfChanges = find_option("if-changes",0,0)!=0;
  allowFork = find_option("allow-fork",0,0)!=0;
  if( find_option("override-lock",0,0)!=0 ) allowFork = 1;
  allowOlder = find_option("allow-older",0,0)!=0;
  noPrompt = find_option("no-prompt", 0, 0)!=0;
  noWarningFlag = find_option("no-warnings", 0, 0)!=0;
  noVerify = find_option("no-verify",0,0)!=0;
  bTrace = find_option("trace",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);
  sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0;
  while( (zTag = find_option("tag",0,1))!=0 ){
    if( zTag[0]==0 ) continue;
    sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag,
                                    sizeof(char*)*(nTag+2));
    sCiInfo.azTag[nTag++] = zTag;
    sCiInfo.azTag[nTag] = 0;
  }
  zComFile = find_option("message-file", "M", 1);
  sCiInfo.zDateOvrd = find_option("date-override",0,1);
  sCiInfo.zUserOvrd = find_option("user-override",0,1);
  noSign = db_get_boolean("omitsign", 0)|noSign;
  if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
  useCksum = db_get_boolean("repo-cksum", 1);
  bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0;
  outputManifest = db_get_manifest_setting();
  mxSize = db_large_file_size();
  if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0;
  verify_all_options();







  /* Get the ID of the parent manifest artifact */
  vid = db_lget_int("checkout", 0);
  if( vid==0 ){
    useCksum = 1;
    if( privateFlag==0 && sCiInfo.zBranch==0 ) {
      sCiInfo.zBranch=db_get("main-branch", 0);







>









>
>
>
>
|
|
>
>


<















|



>
>
>
>
>
>







2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585

2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
  if( !dryRunFlag ){
    dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
  }
  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;
  noVerifyCom = find_option("no-verify-comment",0,0)!=0;
  onlyIfChanges = find_option("if-changes",0,0)!=0;
  allowFork = find_option("allow-fork",0,0)!=0;
  if( find_option("override-lock",0,0)!=0 ) allowFork = 1;
  allowOlder = find_option("allow-older",0,0)!=0;
  noPrompt = find_option("no-prompt", 0, 0)!=0;
  noWarningFlag = find_option("no-warnings", 0, 0)!=0;
  noVerify = find_option("no-verify",0,0)!=0;
  bTrace = find_option("trace",0,0)!=0;
  sCiInfo.zBranch = find_option("branch","b",1);

  /* NB: the --bgcolor and --branchcolor flags still work, but are
  ** now undocumented, to discourage their use. --mimetype has never
  ** been used for anything, so also leave it undocumented */
  sCiInfo.zColor = find_option("bgcolor",0,1);     /* Deprecated, undocumented*/
  sCiInfo.zBrClr = find_option("branchcolor",0,1); /* Deprecated, undocumented*/
  sCiInfo.zMimetype = find_option("mimetype",0,1); /* Deprecated, undocumented*/

  sCiInfo.closeFlag = find_option("close",0,0)!=0;
  sCiInfo.integrateFlag = find_option("integrate",0,0)!=0;

  sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0;
  while( (zTag = find_option("tag",0,1))!=0 ){
    if( zTag[0]==0 ) continue;
    sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag,
                                    sizeof(char*)*(nTag+2));
    sCiInfo.azTag[nTag++] = zTag;
    sCiInfo.azTag[nTag] = 0;
  }
  zComFile = find_option("message-file", "M", 1);
  sCiInfo.zDateOvrd = find_option("date-override",0,1);
  sCiInfo.zUserOvrd = find_option("user-override",0,1);
  noSign = db_get_boolean("omitsign", 0)|noSign;
  if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
  useCksum = db_get_boolean("repo-cksum", 1);
  bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0;
  outputManifest = db_get_manifest_setting(0);
  mxSize = db_large_file_size();
  if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0;
  verify_all_options();

  /* The --no-warnings flag and the --force flag each imply
  ** the --no-verify-comment flag */
  if( noWarningFlag || forceFlag ){
    noVerifyCom = 1;
  }

  /* Get the ID of the parent manifest artifact */
  vid = db_lget_int("checkout", 0);
  if( vid==0 ){
    useCksum = 1;
    if( privateFlag==0 && sCiInfo.zBranch==0 ) {
      sCiInfo.zBranch=db_get("main-branch", 0);
2604
2605
2606
2607
2608
2609
2610
2611


2612
2613
2614
2615
2616

2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
    }
    if( autosync_loop(syncFlags, 1, "commit") ){
      fossil_exit(1);
    }
  }

  /* So that older versions of Fossil (that do not understand delta-
  ** manifest) can continue to use this repository, do not create a new


  ** delta-manifest unless this repository already contains one or more
  ** delta-manifests, or unless the delta-manifest is explicitly requested
  ** by the --delta option.
  **
  ** The forbid-delta-manifests setting prevents new delta manifests.

  **
  ** If the remote repository sent an avoid-delta-manifests pragma on
  ** the autosync above, then also try to avoid deltas, unless the
  ** --delta option is specified.  The remote repo will send the
  ** avoid-delta-manifests pragma if it has its "forbid-delta-manifests"
  ** setting enabled.
  */
  if( !db_get_boolean("seen-delta-manifest",0)
   || db_get_boolean("forbid-delta-manifests",0)
   || g.bAvoidDeltaManifests
  ){
    if( !forceDelta ) forceBaseline = 1;
  }


  /* Require confirmation to continue with the check-in if there is
  ** clock skew
  */
  if( g.clockSkewSeen ){
    if( bIgnoreSkew!=0 ){
      cReply = 'y';
      fossil_warning("Clock skew ignored due to --ignore-clock-skew.");
    }else if( !noPrompt ){
      prompt_user("continue in spite of time skew (y/N)? ", &ans);







|
>
>




|
>


|

|
|

|



|


<

|







2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737

2738
2739
2740
2741
2742
2743
2744
2745
2746
    }
    if( autosync_loop(syncFlags, 1, "commit") ){
      fossil_exit(1);
    }
  }

  /* So that older versions of Fossil (that do not understand delta-
  ** manifest) can continue to use this repository, and because
  ** delta manifests are usually a bad idea unless the repository
  ** has a really large number of files, do not create a new
  ** delta-manifest unless this repository already contains one or more
  ** delta-manifests, or unless the delta-manifest is explicitly requested
  ** by the --delta option.
  **
  ** The forbid-delta-manifests setting prevents new delta manifests,
  ** even if the --delta option is used.
  **
  ** If the remote repository sent an avoid-delta-manifests pragma on
  ** the autosync above, then also forbid delta manifests, even if the
  ** --delta option is specified.  The remote repo will send the
  ** avoid-delta-manifests pragma if its "forbid-delta-manifests"
  ** setting is enabled.
  */
  if( !(forceDelta || db_get_boolean("seen-delta-manifest",0))
   || db_get_boolean("forbid-delta-manifests",0)
   || g.bAvoidDeltaManifests
  ){
    forceBaseline = 1;
  }


  /* Require confirmation to continue with the check-in if there is
  ** clock skew.  This helps to prevent timewarps.
  */
  if( g.clockSkewSeen ){
    if( bIgnoreSkew!=0 ){
      cReply = 'y';
      fossil_warning("Clock skew ignored due to --ignore-clock-skew.");
    }else if( !noPrompt ){
      prompt_user("continue in spite of time skew (y/N)? ", &ans);
2785
2786
2787
2788
2789
2790
2791














2792
2793
2794
2795
2796
2797
2798
2799





2800
2801
2802
2803





2804



2805
2806


















2807
2808
2809
2810
2811
2812
2813
2814
2815


2816
2817
2818
2819
2820
2821
2822
2823
      zCurBranch = branch_of_rid(vid);
    }
    fossil_free(zNewBranch);

    /* Always exit the loop on the second pass */
    if( bRecheck ) break;
















    /* Get the check-in comment.  This might involve prompting the
    ** user for the check-in comment, in which case we should resync
    ** to renew the check-in lock and repeat the checks for conflicts.
    */
    if( zComment ){
      blob_zero(&comment);
      blob_append(&comment, zComment, -1);





    }else if( zComFile ){
      blob_zero(&comment);
      blob_read_from_file(&comment, zComFile, ExtFILE);
      blob_to_utf8_no_bom(&comment, 1);





    }else if( !noPrompt ){



      char *zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'");
      prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag);


















      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_fatal("Commit aborted.");
        }
      }
      free(zInit);


      db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
      db_end_transaction(0);
      db_begin_transaction();
      if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){
        /* Do another auto-pull, renewing the check-in lock.  Then set
        ** bRecheck so that we loop back above to verify that the check-in
        ** is still not against a closed branch and still won't fork. */
        int syncFlags = SYNC_PULL|SYNC_CKIN_LOCK;







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








>
>
>
>
>




>
>
>
>
>

>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
|
|
|
|
>
>
|







2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
      zCurBranch = branch_of_rid(vid);
    }
    fossil_free(zNewBranch);

    /* Always exit the loop on the second pass */
    if( bRecheck ) break;


    /* Figure out how much comment verification is requested */
    if( noVerifyCom ){
      ckComFlgs = 0;
    }else{
      const char *zVerComs = db_get("verify-comments","on");
      if( is_false(zVerComs) ){
        ckComFlgs = 0;
      }else if( strcmp(zVerComs,"preview")==0 ){
        ckComFlgs = COMCK_PREVIEW | COMCK_MARKUP;
      }else{
        ckComFlgs = COMCK_MARKUP;
      }
    }

    /* Get the check-in comment.  This might involve prompting the
    ** user for the check-in comment, in which case we should resync
    ** to renew the check-in lock and repeat the checks for conflicts.
    */
    if( zComment ){
      blob_zero(&comment);
      blob_append(&comment, zComment, -1);
      ckComFlgs &= ~COMCK_PREVIEW;
      if( verify_comment(&comment, ckComFlgs) ){
        fossil_fatal("Commit aborted; "
                     "use --no-verify-comment to override");
      }
    }else if( zComFile ){
      blob_zero(&comment);
      blob_read_from_file(&comment, zComFile, ExtFILE);
      blob_to_utf8_no_bom(&comment, 1);
      ckComFlgs &= ~COMCK_PREVIEW;
      if( verify_comment(&comment, ckComFlgs) ){
        fossil_fatal("Commit aborted; "
                     "use --no-verify-comment to override");
      }
    }else if( !noPrompt ){
      while(  1/*exit-by-break*/ ){
        int rc;
        char *zInit;
        zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'");
        prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag);
        db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
        if( (rc = verify_comment(&comment, ckComFlgs))!=0 ){
          if( rc==COMCK_PREVIEW ){
            prompt_user("Continue, abort, or edit? (C/a/e)? ", &ans);
          }else{
            prompt_user("Edit, abort, or continue (E/a/c)? ", &ans);
          }
          cReply = blob_str(&ans)[0];
          cReply = fossil_tolower(cReply);
          blob_reset(&ans);
          if( cReply=='a' ){
            fossil_fatal("Commit aborted.");
          }
          if( cReply=='e' || (cReply!='c' && rc!=COMCK_PREVIEW) ){
            fossil_free(zInit);
            continue;
          }
        }
        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_fatal("Commit aborted.");
          }
        }
        fossil_free(zInit);
        break;
      }

      db_end_transaction(0);
      db_begin_transaction();
      if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){
        /* Do another auto-pull, renewing the check-in lock.  Then set
        ** bRecheck so that we loop back above to verify that the check-in
        ** is still not against a closed branch and still won't fork. */
        int syncFlags = SYNC_PULL|SYNC_CKIN_LOCK;
Changes to src/checkout.c.
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
** each character as a flag to enable writing "manifest", "manifest.uuid" or
** "manifest.tags".
*/
void manifest_to_disk(int vid){
  char *zManFile;
  int flg;

  flg = db_get_manifest_setting();

  if( flg & MFESTFLG_RAW ){
    Blob manifest = BLOB_INITIALIZER;
    content_get(vid, &manifest);
    sterilize_manifest(&manifest, CFTYPE_MANIFEST);
    zManFile = mprintf("%smanifest", g.zLocalRoot);
    blob_write_to_file(&manifest, zManFile);







|







171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
** each character as a flag to enable writing "manifest", "manifest.uuid" or
** "manifest.tags".
*/
void manifest_to_disk(int vid){
  char *zManFile;
  int flg;

  flg = db_get_manifest_setting(0);

  if( flg & MFESTFLG_RAW ){
    Blob manifest = BLOB_INITIALIZER;
    content_get(vid, &manifest);
    sterilize_manifest(&manifest, CFTYPE_MANIFEST);
    zManFile = mprintf("%smanifest", g.zLocalRoot);
    blob_write_to_file(&manifest, zManFile);
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
** leaves files on disk unchanged, except the manifest and manifest.uuid
** files.
**
** The --latest flag can be used in place of VERSION to check-out the
** latest version in the repository.
**
** Options:
**    --force           Ignore edited files in the current check-out
**    --keep            Only update the manifest file(s)
**    --force-missing   Force check-out even if content is missing

**    --setmtime        Set timestamps of all files to match their SCM-side
**                      times (the timestamp of the last check-in which modified
**                      them)
**
** See also: [[update]]
*/
void checkout_cmd(void){
  int forceFlag;                 /* Force check-out even if edits exist */
  int forceMissingFlag;          /* Force check-out even if missing content */
  int keepFlag;                  /* Do not change any files on disk */
  int latestFlag;                /* Check out the latest version */
  char *zVers;                   /* Version to check out */
  int promptFlag;                /* True to prompt before overwriting */
  int vid, prior;
  int setmtimeFlag;              /* --setmtime.  Set mtimes on files */
  Blob cksum1, cksum1b, cksum2;

  db_must_be_within_tree();
  db_begin_transaction();
  forceFlag = find_option("force","f",0)!=0;
  forceMissingFlag = find_option("force-missing",0,0)!=0;
  keepFlag = find_option("keep",0,0)!=0;

  latestFlag = find_option("latest",0,0)!=0;
  promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0;
  setmtimeFlag = find_option("setmtime",0,0)!=0;






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

  if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){
     usage("VERSION|--latest ?--force? ?--keep?");
  }







|
|

>



















<

|
>



>
>
>
>
>







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
** leaves files on disk unchanged, except the manifest and manifest.uuid
** files.
**
** The --latest flag can be used in place of VERSION to check-out the
** latest version in the repository.
**
** Options:
**    -f|--force        Ignore edited files in the current check-out
**    -k|--keep         Only update the manifest file(s)
**    --force-missing   Force check-out even if content is missing
**    --prompt          Prompt before overwriting when --force is used
**    --setmtime        Set timestamps of all files to match their SCM-side
**                      times (the timestamp of the last check-in which modified
**                      them)
**
** See also: [[update]]
*/
void checkout_cmd(void){
  int forceFlag;                 /* Force check-out even if edits exist */
  int forceMissingFlag;          /* Force check-out even if missing content */
  int keepFlag;                  /* Do not change any files on disk */
  int latestFlag;                /* Check out the latest version */
  char *zVers;                   /* Version to check out */
  int promptFlag;                /* True to prompt before overwriting */
  int vid, prior;
  int setmtimeFlag;              /* --setmtime.  Set mtimes on files */
  Blob cksum1, cksum1b, cksum2;

  db_must_be_within_tree();
  db_begin_transaction();

  forceMissingFlag = find_option("force-missing",0,0)!=0;
  keepFlag = find_option("keep","k",0)!=0;
  forceFlag = find_option("force","f",0)!=0;
  latestFlag = find_option("latest",0,0)!=0;
  promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0;
  setmtimeFlag = find_option("setmtime",0,0)!=0;

  if( keepFlag != 0 ){
    /* After flag collection, in order not to affect promptFlag */
    forceFlag=1;
  }

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

  if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){
     usage("VERSION|--latest ?--force? ?--keep?");
  }
Changes to src/clone.c.
127
128
129
130
131
132
133

134
135
136
137
138
139
140
**    -B|--httpauth USER:PASS    Add HTTP Basic Authorization to requests
**    --nested                   Allow opening a repository inside an opened
**                               check-out
**    --nocompress               Omit extra delta compression
**    --no-open                  Clone only.  Do not open a check-out.
**    --once                     Don't remember the URI.
**    --private                  Also clone private branches

**    --save-http-password       Remember the HTTP password without asking
**    -c|--ssh-command SSH       Use SSH as the "ssh" command
**    --ssl-identity FILENAME    Use the SSL identity if requested by the server
**    --transport-command CMD    Use CMD to move messages to the server and back
**    -u|--unversioned           Also sync unversioned content
**    -v|--verbose               Show more statistics in output
**    --workdir DIR              Also open a check-out in DIR







>







127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
**    -B|--httpauth USER:PASS    Add HTTP Basic Authorization to requests
**    --nested                   Allow opening a repository inside an opened
**                               check-out
**    --nocompress               Omit extra delta compression
**    --no-open                  Clone only.  Do not open a check-out.
**    --once                     Don't remember the URI.
**    --private                  Also clone private branches
**    --proxy PROXY              Use the specified HTTP proxy
**    --save-http-password       Remember the HTTP password without asking
**    -c|--ssh-command SSH       Use SSH as the "ssh" command
**    --ssl-identity FILENAME    Use the SSL identity if requested by the server
**    --transport-command CMD    Use CMD to move messages to the server and back
**    -u|--unversioned           Also sync unversioned content
**    -v|--verbose               Show more statistics in output
**    --workdir DIR              Also open a check-out in DIR
Changes to src/color.c.
18
19
20
21
22
23
24




























































































































































































































































25
26
27
28
29
30
31
** This file contains code used to select colors based on branch and
** user names.
**
*/
#include "config.h"
#include <string.h>
#include "color.h"





























































































































































































































































/*
** Compute a hash on a branch or user name
*/
static unsigned int hash_of_name(const char *z){
  unsigned int h = 0;
  int i;







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







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
** This file contains code used to select colors based on branch and
** user names.
**
*/
#include "config.h"
#include <string.h>
#include "color.h"

/*
** 140 standard CSS color names and their corresponding RGB values,
** in alphabetical order by name so that we can do a binary search
** for lookup.
*/
static const struct CssColors {
  const char *zName;     /* CSS Color name, lower case */
  unsigned int iRGB;     /* Corresponding RGB value */
} aCssColors[] = {
  { "aliceblue",            0xf0f8ff },
  { "antiquewhite",         0xfaebd7 },
  { "aqua",                 0x00ffff },
  { "aquamarine",           0x7fffd4 },
  { "azure",                0xf0ffff },
  { "beige",                0xf5f5dc },
  { "bisque",               0xffe4c4 },
  { "black",                0x000000 },
  { "blanchedalmond",       0xffebcd },
  { "blue",                 0x0000ff },
  { "blueviolet",           0x8a2be2 },
  { "brown",                0xa52a2a },
  { "burlywood",            0xdeb887 },
  { "cadetblue",            0x5f9ea0 },
  { "chartreuse",           0x7fff00 },
  { "chocolate",            0xd2691e },
  { "coral",                0xff7f50 },
  { "cornflowerblue",       0x6495ed },
  { "cornsilk",             0xfff8dc },
  { "crimson",              0xdc143c },
  { "cyan",                 0x00ffff },
  { "darkblue",             0x00008b },
  { "darkcyan",             0x008b8b },
  { "darkgoldenrod",        0xb8860b },
  { "darkgray",             0xa9a9a9 },
  { "darkgreen",            0x006400 },
  { "darkkhaki",            0xbdb76b },
  { "darkmagenta",          0x8b008b },
  { "darkolivegreen",       0x556b2f },
  { "darkorange",           0xff8c00 },
  { "darkorchid",           0x9932cc },
  { "darkred",              0x8b0000 },
  { "darksalmon",           0xe9967a },
  { "darkseagreen",         0x8fbc8f },
  { "darkslateblue",        0x483d8b },
  { "darkslategray",        0x2f4f4f },
  { "darkturquoise",        0x00ced1 },
  { "darkviolet",           0x9400d3 },
  { "deeppink",             0xff1493 },
  { "deepskyblue",          0x00bfff },
  { "dimgray",              0x696969 },
  { "dodgerblue",           0x1e90ff },
  { "firebrick",            0xb22222 },
  { "floralwhite",          0xfffaf0 },
  { "forestgreen",          0x228b22 },
  { "fuchsia",              0xff00ff },
  { "gainsboro",            0xdcdcdc },
  { "ghostwhite",           0xf8f8ff },
  { "gold",                 0xffd700 },
  { "goldenrod",            0xdaa520 },
  { "gray",                 0x808080 },
  { "green",                0x008000 },
  { "greenyellow",          0xadff2f },
  { "honeydew",             0xf0fff0 },
  { "hotpink",              0xff69b4 },
  { "indianred",            0xcd5c5c },
  { "indigo",               0x4b0082 },
  { "ivory",                0xfffff0 },
  { "khaki",                0xf0e68c },
  { "lavender",             0xe6e6fa },
  { "lavenderblush",        0xfff0f5 },
  { "lawngreen",            0x7cfc00 },
  { "lemonchiffon",         0xfffacd },
  { "lightblue",            0xadd8e6 },
  { "lightcoral",           0xf08080 },
  { "lightcyan",            0xe0ffff },
  { "lightgoldenrodyellow", 0xfafad2 },
  { "lightgrey",            0xd3d3d3 },
  { "lightgreen",           0x90ee90 },
  { "lightpink",            0xffb6c1 },
  { "lightsalmon",          0xffa07a },
  { "lightseagreen",        0x20b2aa },
  { "lightskyblue",         0x87cefa },
  { "lightslategray",       0x778899 },
  { "lightsteelblue",       0xb0c4de },
  { "lightyellow",          0xffffe0 },
  { "lime",                 0x00ff00 },
  { "limegreen",            0x32cd32 },
  { "linen",                0xfaf0e6 },
  { "magenta",              0xff00ff },
  { "maroon",               0x800000 },
  { "mediumaquamarine",     0x66cdaa },
  { "mediumblue",           0x0000cd },
  { "mediumorchid",         0xba55d3 },
  { "mediumpurple",         0x9370d8 },
  { "mediumseagreen",       0x3cb371 },
  { "mediumslateblue",      0x7b68ee },
  { "mediumspringgreen",    0x00fa9a },
  { "mediumturquoise",      0x48d1cc },
  { "mediumvioletred",      0xc71585 },
  { "midnightblue",         0x191970 },
  { "mintcream",            0xf5fffa },
  { "mistyrose",            0xffe4e1 },
  { "moccasin",             0xffe4b5 },
  { "navajowhite",          0xffdead },
  { "navy",                 0x000080 },
  { "oldlace",              0xfdf5e6 },
  { "olive",                0x808000 },
  { "olivedrab",            0x6b8e23 },
  { "orange",               0xffa500 },
  { "orangered",            0xff4500 },
  { "orchid",               0xda70d6 },
  { "palegoldenrod",        0xeee8aa },
  { "palegreen",            0x98fb98 },
  { "paleturquoise",        0xafeeee },
  { "palevioletred",        0xd87093 },
  { "papayawhip",           0xffefd5 },
  { "peachpuff",            0xffdab9 },
  { "peru",                 0xcd853f },
  { "pink",                 0xffc0cb },
  { "plum",                 0xdda0dd },
  { "powderblue",           0xb0e0e6 },
  { "purple",               0x800080 },
  { "red",                  0xff0000 },
  { "rosybrown",            0xbc8f8f },
  { "royalblue",            0x4169e1 },
  { "saddlebrown",          0x8b4513 },
  { "salmon",               0xfa8072 },
  { "sandybrown",           0xf4a460 },
  { "seagreen",             0x2e8b57 },
  { "seashell",             0xfff5ee },
  { "sienna",               0xa0522d },
  { "silver",               0xc0c0c0 },
  { "skyblue",              0x87ceeb },
  { "slateblue",            0x6a5acd },
  { "slategray",            0x708090 },
  { "snow",                 0xfffafa },
  { "springgreen",          0x00ff7f },
  { "steelblue",            0x4682b4 },
  { "tan",                  0xd2b48c },
  { "teal",                 0x008080 },
  { "thistle",              0xd8bfd8 },
  { "tomato",               0xff6347 },
  { "turquoise",            0x40e0d0 },
  { "violet",               0xee82ee },
  { "wheat",                0xf5deb3 },
  { "white",                0xffffff },
  { "whitesmoke",           0xf5f5f5 },
  { "yellow",               0xffff00 },
  { "yellowgreen",          0x9acd32 },
};

/*
** Attempt to translate a CSS color name into an integer that
** represents the equivalent RGB value.  Ignore alpha if provided.
** If the name cannot be translated, return -1.
*/
int color_name_to_rgb(const char *zName){
  if( zName==0 || zName[0]==0 ) return -1;
  if( zName[0]=='#' ){
    int i, v = 0;
    for(i=1; i<=6 && fossil_isxdigit(zName[i]); i++){
      v = v*16 + fossil_hexvalue(zName[i]);
    }
    if( i==4 ){
      v = fossil_hexvalue(zName[1])*0x110000 +
          fossil_hexvalue(zName[2])*0x1100 +
          fossil_hexvalue(zName[3])*0x11;
      return v;
    }
    if( i==7 ){
      return v;
    }
    return -1;
  }else{
    int iMin = 0;
    int iMax = count(aCssColors)-1;
    while( iMin<=iMax ){
      int iMid = (iMin+iMax)/2;
      int c = sqlite3_stricmp(aCssColors[iMid].zName, zName);
      if( c==0 ) return aCssColors[iMid].iRGB;
      if( c<0 ){
        iMin = iMid+1;
      }else{
        iMax = iMid-1;
      }
    }
    return -1;
  }
}

/*
** SETTING: raw-bgcolor                  boolean default=off
**
** Fossil usually tries to adjust user-specified background colors
** for checkins so that the text is readable and so that the color
** is not too garish. This setting disables that filter.  When
** this setting is on, the user-selected background colors are shown
** exactly as requested.
*/

/*
** Shift a color provided by the user so that it is suitable
** for use as a background color in the current skin.
**
** The return value is a #HHHHHH color name contained in
** static space that is overwritten on the next call.
**
** If we cannot make sense of the background color recommendation
** that is the input, then return NULL.
**
** The iFgClr parameter is normally 0.  But for testing purposes, set
** it to 1 for a black foregrounds and 2 for a white foreground.
*/
const char *reasonable_bg_color(const char *zRequested, int iFgClr){
  int iRGB = color_name_to_rgb(zRequested);
  int r, g, b;               /* RGB components of requested color */
  static int systemFg = 0;   /* 1==black-foreground 2==white-foreground */
  int fg;                    /* Foreground color to actually use */
  static char zColor[10];    /* Return value */

  if( iFgClr ){
    fg = iFgClr;
  }else if( systemFg==0 ){
    if( db_get_boolean("raw-bgcolor",0) ){
      fg = systemFg = 3;
    }else{
      fg = systemFg = skin_detail_boolean("white-foreground") ? 2 : 1;
    }
  }else{
    fg = systemFg;
  }
  if( fg>=3 ) return zRequested;

  if( iRGB<0 ) return 0;
  r = (iRGB>>16) & 0xff;
  g = (iRGB>>8) & 0xff;
  b = iRGB & 0xff;
  if( fg==1 ){
    const int K = 70;
    r = (K*r)/255 + (255-K);
    g = (K*g)/255 + (255-K);
    b = (K*b)/255 + (255-K);
  }else{
    const int K = 90;
    r = (K*r)/255;
    g = (K*g)/255;
    b = (K*b)/255;
  }
  sqlite3_snprintf(8, zColor, "#%02x%02x%02x", r,g,b);
  return zColor;
}

/*
** Compute a hash on a branch or user name
*/
static unsigned int hash_of_name(const char *z){
  unsigned int h = 0;
  int i;
183
184
185
186
187
188
189

















































































    @ <input type="text" size="30" name='%s(zNm)' value='%h(PD(zNm,""))'><br>
  }
  @ <input type="submit" value="Submit">
  @ <input type="submit" name="rand" value="Random">
  @ </form>
  style_finish_page();
}
























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
    @ <input type="text" size="30" name='%s(zNm)' value='%h(PD(zNm,""))'><br>
  }
  @ <input type="submit" value="Submit">
  @ <input type="submit" name="rand" value="Random">
  @ </form>
  style_finish_page();
}

/*
** WEBPAGE: test-bgcolor
**
** Show how user-specified background colors will be rendered
** using the reasonable_bg_color() algorithm.
*/
void test_bgcolor_page(void){
  const char *zReq;      /* Requested color name */
  const char *zBG;       /* Actual color provided */
  const char *zBg1;
  char zNm[10];
  static const char *azDflt[] = {
    "red", "orange", "yellow", "green", "blue", "indigo", "violet",
    "tan", "brown", "gray"
  };
  int i, cnt, iClr, r, g, b;
  char *zFg;
  login_check_credentials();
  style_set_current_feature("test");
  style_header("Background Color Test");
  for(i=cnt=0; i<10; i++){
    sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i);
    zReq = PD(zNm,azDflt[i]);
    if( zReq==0 || zReq[0]==0 ) continue;
    if( cnt==0 ){
      @ <table border="1" cellspacing="0" cellpadding="10">
      @ <tr>
      @ <th>Requested Background
      @ <th>Light mode
      @ <th>Dark mode
      @ </tr>
    }
    cnt++;
    zBG = reasonable_bg_color(zReq, 0);
    if( zBG==0 ){
      @ <tr><td colspan="3" align="center">\
      @ "%h(zReq)" is not a recognized color name</td></tr>
      continue;
    }
    iClr = color_name_to_rgb(zReq);
    r = (iClr>>16) & 0xff;
    g = (iClr>>8) & 0xff;
    b = iClr & 0xff;
    if( 3*r + 7*g + b > 6*255 ){
      zFg = "black";
    }else{
      zFg = "white";
    }
    if( zReq[0]!='#' ){
      char zReqRGB[12];
      sqlite3_snprintf(sizeof(zReqRGB),zReqRGB,"#%06x",color_name_to_rgb(zReq));
      @ <tr><td style='color:%h(zFg);background-color:%h(zReq);'>\
      @ Requested color "%h(zReq)" (%h(zReqRGB))</td>
    }else{
      @ <tr><td style='color:%h(zFg);background-color:%s(zReq);'>\
      @ Requested color "%h(zReq)"</td>
    }
    zBg1 = reasonable_bg_color(zReq,1);
    @ <td style='color:black;background-color:%h(zBg1);'>\
    @ Background color for dark text: %h(zBg1)</td>
    zBg1 = reasonable_bg_color(zReq,2);
    @ <td style='color:white;background-color:%h(zBg1);'>\
    @ Background color for light text: %h(zBg1)</td></tr>
  }
  if( cnt ){
    @ </table>
    @ <hr>
  }
  @ <form method="POST">
  @ <p>Enter CSS color names below and see them shifted into corresponding
  @ background colors above.</p>
  for(i=0; i<10; i++){
    sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i);
    @ <input type="text" size="30" name='%s(zNm)' \
    @ value='%h(PD(zNm,azDflt[i]))'><br>
  }
  @ <input type="submit" value="Submit">
  @ </form>
  style_finish_page();
}
Changes to src/comformat.c.
19
20
21
22
23
24
25
26



27



28
29
30
31
32
33
34
35
36
37
38
39
40
** text on a TTY.
*/
#include "config.h"
#include "comformat.h"
#include <assert.h>

#if INTERFACE
#define COMMENT_PRINT_NONE       ((u32)0x00000000) /* No flags = non-legacy. */



#define COMMENT_PRINT_LEGACY     ((u32)0x00000001) /* Use legacy algorithm. */



#define COMMENT_PRINT_TRIM_CRLF  ((u32)0x00000002) /* Trim leading CR/LF. */
#define COMMENT_PRINT_TRIM_SPACE ((u32)0x00000004) /* Trim leading/trailing. */
#define COMMENT_PRINT_WORD_BREAK ((u32)0x00000008) /* Break lines on words. */
#define COMMENT_PRINT_ORIG_BREAK ((u32)0x00000010) /* Break before original. */
#define COMMENT_PRINT_DEFAULT    (COMMENT_PRINT_LEGACY) /* Defaults. */
#define COMMENT_PRINT_UNSET      (-1)              /* Not initialized. */
#endif

/********* Code copied from SQLite src/shell.c.in on 2024-09-30 **********/
/* Lookup table to estimate the number of columns consumed by a Unicode
** character.
*/
static const struct {







|
>
>
>
|
>
>
>




<
<







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
** text on a TTY.
*/
#include "config.h"
#include "comformat.h"
#include <assert.h>

#if INTERFACE
#define COMMENT_PRINT_NONE       ((u32)0x00000000) /* No flags */
#define COMMENT_PRINT_CANONICAL  ((u32)0x00000001) /* Use canonical algorithm */
#define COMMENT_PRINT_DEFAULT    COMMENT_PRINT_CANONICAL  /* Default */
#define COMMENT_PRINT_UNSET      (-1)              /* Not initialized */

/* The canonical comment printing algorithm is recommended.  We make
** no promise of on-going support for any of the following flags:
*/
#define COMMENT_PRINT_TRIM_CRLF  ((u32)0x00000002) /* Trim leading CR/LF. */
#define COMMENT_PRINT_TRIM_SPACE ((u32)0x00000004) /* Trim leading/trailing. */
#define COMMENT_PRINT_WORD_BREAK ((u32)0x00000008) /* Break lines on words. */
#define COMMENT_PRINT_ORIG_BREAK ((u32)0x00000010) /* Break before original. */


#endif

/********* Code copied from SQLite src/shell.c.in on 2024-09-30 **********/
/* Lookup table to estimate the number of columns consumed by a Unicode
** character.
*/
static const struct {
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
  const char *zLine, /* [in] The comment line being printed. */
  int index,         /* [in] The current character index being handled. */
  int maxChars,      /* [in] Optimization hint to abort before space found. */
  int *sumWidth      /* [out] Summated width of all characters to next space. */
){
  int cchUTF8, utf32, wcwidth = 0;
  int nextIndex = index;

  for(;;){
    char_info_utf8(&zLine[nextIndex],&cchUTF8,&utf32);
    nextIndex += cchUTF8;
    wcwidth += cli_wcwidth(utf32);
    if( zLine[nextIndex]==0 || fossil_isspace(zLine[nextIndex]) ||
        wcwidth>maxChars ){
      *sumWidth = wcwidth;
      return nextIndex;
    }
  }
  return 0; /* NOT REACHED */
}

/*
** Return information about the next (single- or multi-byte) character in the


** specified UTF-8 string: The number of UTF-8 code units (in this case: bytes)


** and the decoded UTF-32 code point. Incomplete, ill-formed and overlong
** sequences are consumed together as one invalid code point. The invalid lead
** bytes 0xC0 to 0xC1 and 0xF5 to 0xF7 are allowed to initiate (ill-formed) 2-
** and 4-byte sequences, respectively, the other invalid lead bytes 0xF8 to 0xFF
** are treated as invalid 1-byte sequences (as lone trail bytes), all resulting
** in one invalid code point. Invalid UTF-8 sequences encoding a non-scalar code
** point (UTF-16 surrogates U+D800 to U+DFFF) are allowed.



*/
void char_info_utf8(
  const char *z,
  int *pCchUTF8,
  int *pUtf32
){
  int i = 0;                              /* Counted bytes. */
  int cchUTF8 = 1;                        /* Code units consumed. */
  int maxUTF8 = 1;                        /* Expected sequence length. */
  char c = z[i++];










  if( (c&0x80)==0x00 ){                   /* 7-bit ASCII character. */
    *pCchUTF8 = 1;
    *pUtf32 = (int)z[0];
    return;
  }
  else if( (c&0xe0)==0xc0 ) maxUTF8 = 2;  /* UTF-8 lead byte 110vvvvv */
  else if( (c&0xf0)==0xe0 ) maxUTF8 = 3;  /* UTF-8 lead byte 1110vvvv */







>














|
>
>
|
>
>
|
|
|
|
|
|
|
>
>
>


|
|
|





>
>
>
>
>
>
>
>
>
>







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
  const char *zLine, /* [in] The comment line being printed. */
  int index,         /* [in] The current character index being handled. */
  int maxChars,      /* [in] Optimization hint to abort before space found. */
  int *sumWidth      /* [out] Summated width of all characters to next space. */
){
  int cchUTF8, utf32, wcwidth = 0;
  int nextIndex = index;
  if( zLine[index]==0 ) return index;
  for(;;){
    char_info_utf8(&zLine[nextIndex],&cchUTF8,&utf32);
    nextIndex += cchUTF8;
    wcwidth += cli_wcwidth(utf32);
    if( zLine[nextIndex]==0 || fossil_isspace(zLine[nextIndex]) ||
        wcwidth>maxChars ){
      *sumWidth = wcwidth;
      return nextIndex;
    }
  }
  return 0; /* NOT REACHED */
}

/*
** Return information about the next (single- or multi-byte) character in
** z[0].  Two values are computed:
**
**     *   The number of bytes needed to represent the character.
**     *   The UTF code point value.
**
** Incomplete, ill-formed and overlong sequences are consumed together as
** one invalid code point. The invalid lead bytes 0xC0 to 0xC1 and 0xF5 to
** 0xF7 are allowed to initiate (ill-formed) 2- and 4-byte sequences,
** respectively, the other invalid lead bytes 0xF8 to 0xFF are treated
** as invalid 1-byte sequences (as lone trail bytes), all resulting
** in one invalid code point. Invalid UTF-8 sequences encoding a
** non-scalar code point (UTF-16 surrogates U+D800 to U+DFFF) are allowed.
**
** ANSI escape sequences of the form "\033[...X" are interpreted as a
** zero-width character.
*/
void char_info_utf8(
  const char *z,       /* The character to be analyzed */
  int *pCchUTF8,       /* OUT: The number of bytes used by this character */
  int *pUtf32          /* OUT: The UTF8 code point (used to determine width) */
){
  int i = 0;                              /* Counted bytes. */
  int cchUTF8 = 1;                        /* Code units consumed. */
  int maxUTF8 = 1;                        /* Expected sequence length. */
  char c = z[i++];
  if( c==0x1b && z[i]=='[' ){
    i++;
    while( z[i]>=0x30 && z[i]<=0x3f ){ i++; }
    while( z[i]>=0x20 && z[i]<=0x2f ){ i++; }
    if( z[i]>=0x40 && z[i]<=0x7e ){
      *pCchUTF8 = i+1;
      *pUtf32 = 0x301;  /* A zero-width character */
      return;
    }
  }
  if( (c&0x80)==0x00 ){                   /* 7-bit ASCII character. */
    *pCchUTF8 = 1;
    *pUtf32 = (int)z[0];
    return;
  }
  else if( (c&0xe0)==0xc0 ) maxUTF8 = 2;  /* UTF-8 lead byte 110vvvvv */
  else if( (c&0xf0)==0xe0 ) maxUTF8 = 3;  /* UTF-8 lead byte 1110vvvv */
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
  }
  if( pzLine ){
    *pzLine = zLine + index;
  }
}

/*
** This is the legacy comment printing algorithm.  It is being retained
** for backward compatibility.

**
** Given a comment string, format that string for printing on a TTY.
** Assume that the output cursors is indent spaces from the left margin
** and that a single line can contain no more than 'width' characters.
** Indent all subsequent lines by 'indent'.
**







** Returns the number of new lines emitted.
*/
static int comment_print_legacy(
  const char *zText, /* The comment text to be printed. */
  int indent,        /* Number of spaces to indent each non-initial line. */
  int width          /* Maximum number of characters per line. */
){
  int maxChars = width - indent;
  int si, sk, i, k, kc;
  int doIndent = 0;







|
|
>


|



>
>
>
>
>
>
>


|







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
  }
  if( pzLine ){
    *pzLine = zLine + index;
  }
}

/*
** This is the canonical comment printing algorithm.  This is the algorithm
** that is recommended and that is used unless the administrator has made
** special arrangements to use a customized algorithm.
**
** Given a comment string, format that string for printing on a TTY.
** Assume that the output cursor is indent spaces from the left margin
** and that a single line can contain no more than 'width' characters.
** Indent all subsequent lines by 'indent'.
**
** Formatting features:
**
**   *  Leading whitespace is removed.
**   *  Internal whitespace sequences are changed into a single space (0x20)
**      character.
**   *  Lines are broken at a space, or at a hyphen ("-") whenever possible.
**
** Returns the number of new lines emitted.
*/
static int comment_print_canonical(
  const char *zText, /* The comment text to be printed. */
  int indent,        /* Number of spaces to indent each non-initial line. */
  int width          /* Maximum number of characters per line. */
){
  int maxChars = width - indent;
  int si, sk, i, k, kc;
  int doIndent = 0;
562
563
564
565
566
567
568
569




570

571
572
573
574
575
576
577
578

/*
** This is the comment printing function.  The comment printing algorithm
** contained within it attempts to preserve the formatting present within
** the comment string itself while honoring line width limitations.  There
** are several flags that modify the default behavior of this function:
**
**         COMMENT_PRINT_LEGACY: Forces use of the legacy comment printing




**                               algorithm.  For backward compatibility,

**                               this is the default.
**
**      COMMENT_PRINT_TRIM_CRLF: Trims leading and trailing carriage-returns
**                               and line-feeds where they do not materially
**                               impact pre-existing formatting (i.e. at the
**                               start of the comment string -AND- right
**                               before line indentation).  This flag does
**                               not apply to the legacy comment printing







|
>
>
>
>
|
>
|







592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613

/*
** This is the comment printing function.  The comment printing algorithm
** contained within it attempts to preserve the formatting present within
** the comment string itself while honoring line width limitations.  There
** are several flags that modify the default behavior of this function:
**
**      COMMENT_PRINT_CANONICAL: Use the canonical printing algorithm:
**                                  *  Omit leading and trailing whitespace
**                                  *  Collapse internal whitespace into a
**                                     single space (0x20) character.
**                                  *  Attempt to break lines at whitespace
**                                     or hyphens.
**                               This is the recommended algorithm and is
**                               used in most cases.
**
**      COMMENT_PRINT_TRIM_CRLF: Trims leading and trailing carriage-returns
**                               and line-feeds where they do not materially
**                               impact pre-existing formatting (i.e. at the
**                               start of the comment string -AND- right
**                               before line indentation).  This flag does
**                               not apply to the legacy comment printing
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
  const char *zText,     /* The comment text to be printed. */
  const char *zOrigText, /* Original comment text ONLY, may be NULL. */
  int indent,            /* Spaces to indent each non-initial line. */
  int width,             /* Maximum number of characters per line. */
  int flags              /* Zero or more "COMMENT_PRINT_*" flags. */
){
  int maxChars = width - indent;

  int legacy = flags & COMMENT_PRINT_LEGACY;







  int trimCrLf = flags & COMMENT_PRINT_TRIM_CRLF;
  int trimSpace = flags & COMMENT_PRINT_TRIM_SPACE;
  int wordBreak = flags & COMMENT_PRINT_WORD_BREAK;
  int origBreak = flags & COMMENT_PRINT_ORIG_BREAK;
  int lineCnt = 0;
  const char *zLine;

  if( legacy ){
    return comment_print_legacy(zText, indent, width);
  }
  if( width<0 ){
    comment_set_maxchars(indent, &maxChars);
  }
  if( zText==0 ) zText = "(NULL)";
  if( maxChars<=0 ){
    maxChars = strlen(zText);
  }
  if( trimSpace ){
    while( fossil_isspace(zText[0]) ){ zText++; }
  }
  if( zText[0]==0 ){
    fossil_print("\n");
    lineCnt++;
    return lineCnt;
  }
  zLine = zText;
  for(;;){
    comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0,
                       maxChars, trimCrLf, trimSpace, wordBreak, origBreak,
                       &lineCnt, &zLine);
    if( zLine==0 ) break;
    while( fossil_isspace(zLine[0]) ) zLine++;
    if( zLine[0]==0 ) break;
  }
  return lineCnt;

}

/*
** Return the "COMMENT_PRINT_*" flags specified by the following sources,
** evaluated in the following cascading order:
**
**    1. The global --comfmtflags (alias --comment-format) command-line option.
**    2. The local (per-repository) "comment-format" setting.
**    3. The global (all-repositories) "comment-format" setting.
**    4. The default value COMMENT_PRINT_DEFAULT.
*/
int get_comment_format(){
  int comFmtFlags;

  /* We must cache this result, else running the timeline can end up
  ** querying the comment-format setting from the global db once per
  ** timeline entry, which brings it to a crawl if that db is







>
|
>
>
>
>
>
>
>
|
|
|
|
|
|

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






<
|
|
|







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
  const char *zText,     /* The comment text to be printed. */
  const char *zOrigText, /* Original comment text ONLY, may be NULL. */
  int indent,            /* Spaces to indent each non-initial line. */
  int width,             /* Maximum number of characters per line. */
  int flags              /* Zero or more "COMMENT_PRINT_*" flags. */
){
  int maxChars = width - indent;

  if( flags & COMMENT_PRINT_CANONICAL ){
    /* Use the canonical algorithm.  This is what happens in almost
    ** all cases. */
    return comment_print_canonical(zText, indent, width);
  }else{
    /* The remaining is a more complex formatting algorithm that is very
    ** seldom used and is considered deprecated.
    */
    int trimCrLf = flags & COMMENT_PRINT_TRIM_CRLF;
    int trimSpace = flags & COMMENT_PRINT_TRIM_SPACE;
    int wordBreak = flags & COMMENT_PRINT_WORD_BREAK;
    int origBreak = flags & COMMENT_PRINT_ORIG_BREAK;
    int lineCnt = 0;
    const char *zLine;




    if( width<0 ){
      comment_set_maxchars(indent, &maxChars);
    }
    if( zText==0 ) zText = "(NULL)";
    if( maxChars<=0 ){
      maxChars = strlen(zText);
    }
    if( trimSpace ){
      while( fossil_isspace(zText[0]) ){ zText++; }
    }
    if( zText[0]==0 ){
      fossil_print("\n");
      lineCnt++;
      return lineCnt;
    }
    zLine = zText;
    for(;;){
      comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0,
                         maxChars, trimCrLf, trimSpace, wordBreak, origBreak,
                         &lineCnt, &zLine);
      if( zLine==0 ) break;
      while( fossil_isspace(zLine[0]) ) zLine++;
      if( zLine[0]==0 ) break;
    }
    return lineCnt;
  }
}

/*
** Return the "COMMENT_PRINT_*" flags specified by the following sources,
** evaluated in the following cascading order:
**

**    1. The local (per-repository) "comment-format" setting.
**    2. The global (all-repositories) "comment-format" setting.
**    3. The default value COMMENT_PRINT_DEFAULT.
*/
int get_comment_format(){
  int comFmtFlags;

  /* We must cache this result, else running the timeline can end up
  ** querying the comment-format setting from the global db once per
  ** timeline entry, which brings it to a crawl if that db is
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
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
  return g.comFmtFlags;
}

/*
**
** COMMAND: test-comment-format
**
** Usage: %fossil test-comment-format ?OPTIONS? PREFIX TEXT ?ORIGTEXT?
**
** Test comment formatting and printing.  Use for testing only.
**










** Options:
**   --file           The comment text is really just a file name to
**                    read it from
**   --decode         Decode the text using the same method used when
**                    handling the value of a C-card from a manifest.
**   --legacy         Use the legacy comment printing algorithm
**   --trimcrlf       Enable trimming of leading/trailing CR/LF
**   --trimspace      Enable trimming of leading/trailing spaces
**   --wordbreak      Attempt to break lines on word boundaries
**   --origbreak      Attempt to break when the original comment text
**                    is detected
**   --indent         Number of spaces to indent (default (-1) is to



**                    auto-detect).  Zero means no indent.




**   -W|--width NUM   Width of lines (default (-1) is to auto-detect).
**                    Zero means no limit.
*/
void test_comment_format(void){
  const char *zWidth;
  const char *zIndent;
  const char *zPrefix;
  char *zText;
  char *zOrigText;
  int indent, width;

  int fromFile = find_option("file", 0, 0)!=0;
  int decode = find_option("decode", 0, 0)!=0;
  int flags = COMMENT_PRINT_NONE;

  if( find_option("legacy", 0, 0) ){
    flags |= COMMENT_PRINT_LEGACY;
  }
  if( find_option("trimcrlf", 0, 0) ){
    flags |= COMMENT_PRINT_TRIM_CRLF;
  }
  if( find_option("trimspace", 0, 0) ){
    flags |= COMMENT_PRINT_TRIM_SPACE;

  }
  if( find_option("wordbreak", 0, 0) ){
    flags |= COMMENT_PRINT_WORD_BREAK;

  }
  if( find_option("origbreak", 0, 0) ){
    flags |= COMMENT_PRINT_ORIG_BREAK;

  }
  zWidth = find_option("width","W",1);
  if( zWidth ){
    width = atoi(zWidth);
  }else{
    width = -1; /* automatic */
  }
  zIndent = find_option("indent",0,1);
  if( zIndent ){
    indent = atoi(zIndent);
  }else{
    indent = -1; /* automatic */
  }
  verify_all_options();
  if( g.argc!=4 && g.argc!=5 ){
    usage("?OPTIONS? PREFIX TEXT ?ORIGTEXT?");
  }
  zPrefix = g.argv[2];
  zText = g.argv[3];
  if( g.argc==5 ){
    zOrigText = g.argv[4];
  }else{
    zOrigText = 0;
  }
  if( fromFile ){
    Blob fileData;
    blob_read_from_file(&fileData, zText, ExtFILE);
    zText = mprintf("%s", blob_str(&fileData));
    blob_reset(&fileData);

    if( zOrigText ){

      blob_read_from_file(&fileData, zOrigText, ExtFILE);
      zOrigText = mprintf("%s", blob_str(&fileData));
      blob_reset(&fileData);
    }














  }
  if( decode ){
    zText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zText);
    defossilize(zText);
    if( zOrigText ){
      zOrigText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zOrigText);
      defossilize(zOrigText);
    }
  }

  if( indent<0 ){
    indent = strlen(zPrefix);
  }
  if( zPrefix && *zPrefix ){
    fossil_print("%s", zPrefix);
  }
  fossil_print("(%d lines output)\n",
               comment_print(zText, zOrigText, indent, width, flags));
  if( zOrigText && zOrigText!=g.argv[4] ) fossil_free(zOrigText);
  if( zText && zText!=g.argv[3] ) fossil_free(zText);
}







|



>
>
>
>
>
>
>
>
>
>

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




|
|
|

>
|

|
>
|
|


|



>



>



>














<
<
<
<
<
<
<
<
|
<


|


>
|
>
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>









>








<
<

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
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852


853
  return g.comFmtFlags;
}

/*
**
** COMMAND: test-comment-format
**
** Usage: %fossil test-comment-format [OPTIONS] TEXT [PREFIX] [ORIGTEXT]
**
** Test comment formatting and printing.  Use for testing only.
**
** The default (canonical) formatting algorithm is:
**
**    *  Omit leading/trailing whitespace
**    *  Collapse internal whitespace into a single space character.
**    *  Attempt to break lines at whitespace or at a hyphen.
**
** Use --whitespace, --origbreak, --trimcrlf, --trimspace,
** and/or --wordbreak to disable the canonical processing and do
** the special processing specified by those other options.
**
** Options:


**   --decode           Decode the text using the same method used when
**                      handling the value of a C-card from a manifest.




**   --file FILE        Omit the TEXT argument and read the comment text
**                      from FILE.
**   --indent           Number of spaces to indent (default (-1) is to
**                      auto-detect).  Zero means no indent.
**   --orig FILE        Take the value for the ORIGTEXT argumetn from FILE.
**   --origbreak        Attempt to break when the original comment text
**                      is detected.
**   --trimcrlf         Enable trimming of leading/trailing CR/LF.
**   --trimspace        Enable trimming of leading/trailing spaces.
**   --whitespace       Keep all internal whitespace.
**   --wordbreak        Attempt to break lines on word boundaries.
**   -W|--width NUM     Width of lines (default (-1) is to auto-detect).
**                      Zero means no limit.
*/
void test_comment_format(void){
  const char *zWidth;
  const char *zIndent;
  const char *zPrefix = 0;
  char *zText = 0;
  char *zOrigText = 0;
  int indent, width;
  int i;
  const char *fromFile = find_option("file", 0, 1);
  int decode = find_option("decode", 0, 0)!=0;
  int flags = COMMENT_PRINT_CANONICAL;
  const char *fromOrig = find_option("orig", 0, 1);
  if( find_option("whitespace",0,0) ){
    flags = 0;
  }
  if( find_option("trimcrlf", 0, 0) ){
    flags = COMMENT_PRINT_TRIM_CRLF;
  }
  if( find_option("trimspace", 0, 0) ){
    flags |= COMMENT_PRINT_TRIM_SPACE;
    flags &= COMMENT_PRINT_CANONICAL;
  }
  if( find_option("wordbreak", 0, 0) ){
    flags |= COMMENT_PRINT_WORD_BREAK;
    flags &= COMMENT_PRINT_CANONICAL;
  }
  if( find_option("origbreak", 0, 0) ){
    flags |= COMMENT_PRINT_ORIG_BREAK;
    flags &= COMMENT_PRINT_CANONICAL;
  }
  zWidth = find_option("width","W",1);
  if( zWidth ){
    width = atoi(zWidth);
  }else{
    width = -1; /* automatic */
  }
  zIndent = find_option("indent",0,1);
  if( zIndent ){
    indent = atoi(zIndent);
  }else{
    indent = -1; /* automatic */
  }
  verify_all_options();








  zPrefix = zText = zOrigText = 0;

  if( fromFile ){
    Blob fileData;
    blob_read_from_file(&fileData, fromFile, ExtFILE);
    zText = mprintf("%s", blob_str(&fileData));
    blob_reset(&fileData);
  }
  if( fromOrig ){
    Blob fileData;
    blob_read_from_file(&fileData, fromOrig, ExtFILE);
    zOrigText = mprintf("%s", blob_str(&fileData));
    blob_reset(&fileData);
  }
  for(i=2; i<g.argc; i++){
    if( zText==0 ){
      zText = g.argv[i];
      continue;
    }
    if( zPrefix==0 ){
      zPrefix = g.argv[i];
      continue;
    }
    if( zOrigText==0 ){
      zOrigText = g.argv[i];
      continue;
    }
    usage("[OPTIONS] TEXT [PREFIX] [ORIGTEXT]");
  }
  if( decode ){
    zText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zText);
    defossilize(zText);
    if( zOrigText ){
      zOrigText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zOrigText);
      defossilize(zOrigText);
    }
  }
  if( zPrefix==0 ) zPrefix = "00:00:00 ";
  if( indent<0 ){
    indent = strlen(zPrefix);
  }
  if( zPrefix && *zPrefix ){
    fossil_print("%s", zPrefix);
  }
  fossil_print("(%d lines output)\n",
               comment_print(zText, zOrigText, indent, width, flags));


}
Changes to src/configure.c.
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
  { "default-skin",           CONFIGSET_SKIN },
  { "logo-mimetype",          CONFIGSET_SKIN },
  { "logo-image",             CONFIGSET_SKIN },
  { "background-mimetype",    CONFIGSET_SKIN },
  { "background-image",       CONFIGSET_SKIN },
  { "icon-mimetype",          CONFIGSET_SKIN },
  { "icon-image",             CONFIGSET_SKIN },
  { "timeline-block-markup",  CONFIGSET_SKIN },
  { "timeline-date-format",   CONFIGSET_SKIN },
  { "timeline-default-style", CONFIGSET_SKIN },
  { "timeline-dwelltime",     CONFIGSET_SKIN },
  { "timeline-closetime",     CONFIGSET_SKIN },
  { "timeline-hard-newlines", CONFIGSET_SKIN },
  { "timeline-max-comment",   CONFIGSET_SKIN },
  { "timeline-plaintext",     CONFIGSET_SKIN },







<







98
99
100
101
102
103
104

105
106
107
108
109
110
111
  { "default-skin",           CONFIGSET_SKIN },
  { "logo-mimetype",          CONFIGSET_SKIN },
  { "logo-image",             CONFIGSET_SKIN },
  { "background-mimetype",    CONFIGSET_SKIN },
  { "background-image",       CONFIGSET_SKIN },
  { "icon-mimetype",          CONFIGSET_SKIN },
  { "icon-image",             CONFIGSET_SKIN },

  { "timeline-date-format",   CONFIGSET_SKIN },
  { "timeline-default-style", CONFIGSET_SKIN },
  { "timeline-dwelltime",     CONFIGSET_SKIN },
  { "timeline-closetime",     CONFIGSET_SKIN },
  { "timeline-hard-newlines", CONFIGSET_SKIN },
  { "timeline-max-comment",   CONFIGSET_SKIN },
  { "timeline-plaintext",     CONFIGSET_SKIN },
815
816
817
818
819
820
821


822
823
824
825
826
827
828
**
** >  fossil configuration sync AREA ?URL?
**
**         Synchronize configuration changes in the local repository with
**         the remote repository at URL.
**
** Options:


**    -R|--repository REPO       Affect repository REPO with changes
**
** See also: [[settings]], [[unset]]
*/
void configuration_cmd(void){
  int n;
  const char *zMethod;







>
>







814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
**
** >  fossil configuration sync AREA ?URL?
**
**         Synchronize configuration changes in the local repository with
**         the remote repository at URL.
**
** Options:
**    --proxy PROXY              Use PROXY as http proxy during sync operation
**                               (used by pull, push and sync subcommands)
**    -R|--repository REPO       Affect repository REPO with changes
**
** See also: [[settings]], [[unset]]
*/
void configuration_cmd(void){
  int n;
  const char *zMethod;
887
888
889
890
891
892
893

894
895
896
897
898
899
900
    if( g.argc==5 ){
      zServer = g.argv[4];
    }
    url_parse(zServer, URL_PROMPT_PW|URL_USE_CONFIG);
    if( g.url.protocol==0 ) fossil_fatal("no server URL specified");
    user_select();
    url_enable_proxy("via proxy: ");

    if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
    if( strncmp(zMethod, "push", n)==0 ){
      client_sync(0,0,(unsigned)mask,0,0);
    }else if( strncmp(zMethod, "pull", n)==0 ){
      if( overwriteFlag ) db_unprotect(PROTECT_USER);
      client_sync(0,(unsigned)mask,0,0,0);
      if( overwriteFlag ) db_protect_pop();







>







888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
    if( g.argc==5 ){
      zServer = g.argv[4];
    }
    url_parse(zServer, URL_PROMPT_PW|URL_USE_CONFIG);
    if( g.url.protocol==0 ) fossil_fatal("no server URL specified");
    user_select();
    url_enable_proxy("via proxy: ");
    g.zHttpAuth = get_httpauth();
    if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
    if( strncmp(zMethod, "push", n)==0 ){
      client_sync(0,0,(unsigned)mask,0,0);
    }else if( strncmp(zMethod, "pull", n)==0 ){
      if( overwriteFlag ) db_unprotect(PROTECT_USER);
      client_sync(0,(unsigned)mask,0,0,0);
      if( overwriteFlag ) db_protect_pop();
Changes to src/db.c.
1627
1628
1629
1630
1631
1632
1633


1634
1635
1636
1637
1638
1639
1640
  sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0,
                          url_nouser_func,0,0);
  sqlite3_create_function(db, "chat_msg_from_event", 4,
        SQLITE_UTF8 | SQLITE_INNOCUOUS, 0,
        chat_msg_from_event, 0, 0);
  sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0,
                          file_inode_sql_func,0,0);



}

#if USE_SEE
/*
** This is a pointer to the saved database encryption key string.
*/







>
>







1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
  sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0,
                          url_nouser_func,0,0);
  sqlite3_create_function(db, "chat_msg_from_event", 4,
        SQLITE_UTF8 | SQLITE_INNOCUOUS, 0,
        chat_msg_from_event, 0, 0);
  sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0,
                          file_inode_sql_func,0,0);
  sqlite3_create_function(db, "artifact_to_json", 1, SQLITE_UTF8, 0,
                          artifact_to_json_sql_func,0,0);

}

#if USE_SEE
/*
** This is a pointer to the saved database encryption key string.
*/
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
  Blob hash;
  Blob manifest;

  db_unprotect(PROTECT_ALL);
  db_set("content-schema", CONTENT_SCHEMA, 0);
  db_set("aux-schema", AUX_SCHEMA_MAX, 0);
  db_set("rebuilt", get_version(), 0);
  db_set("admin-log", "1", 0);
  db_set("access-log", "1", 0);
  db_multi_exec(
      "INSERT INTO config(name,value,mtime)"
      " VALUES('server-code', lower(hex(randomblob(20))),now());"
      "INSERT INTO config(name,value,mtime)"
      " VALUES('project-code', lower(hex(randomblob(20))),now());"
  );
  if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0);
  if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0);
  if( !db_is_global("timeline-plaintext") ){
    db_set_int("timeline-plaintext", 1, 0);
  }
  db_create_default_users(0, zDefaultUser);
  if( zDefaultUser ) g.zLogin = zDefaultUser;
  user_select();

  if( zTemplate ){
    /*
    ** Copy all settings from the supplied template repository.







<
<






<
<
<
<
<







3204
3205
3206
3207
3208
3209
3210


3211
3212
3213
3214
3215
3216





3217
3218
3219
3220
3221
3222
3223
  Blob hash;
  Blob manifest;

  db_unprotect(PROTECT_ALL);
  db_set("content-schema", CONTENT_SCHEMA, 0);
  db_set("aux-schema", AUX_SCHEMA_MAX, 0);
  db_set("rebuilt", get_version(), 0);


  db_multi_exec(
      "INSERT INTO config(name,value,mtime)"
      " VALUES('server-code', lower(hex(randomblob(20))),now());"
      "INSERT INTO config(name,value,mtime)"
      " VALUES('project-code', lower(hex(randomblob(20))),now());"
  );





  db_create_default_users(0, zDefaultUser);
  if( zDefaultUser ) g.zLogin = zDefaultUser;
  user_select();

  if( zTemplate ){
    /*
    ** Copy all settings from the supplied template repository.
3645
3646
3647
3648
3649
3650
3651







3652
3653




3654
3655
3656
3657
3658
3659
3660
3661
3662
3663



3664

3665
3666
3667
3668
3669
3670
3671
3672


3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684


3685
3686

3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707

3708
3709
3710
3711
3712
3713
3714
3715
3716
3717

3718

3719
3720
3721
3722
3723
3724


3725
3726
3727



3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739

3740
3741
3742
3743
3744
3745
3746
** Return the text of the string if it is found.  Return NULL if not
** found.
**
** If the zNonVersionedSetting parameter is not NULL then it holds the
** non-versioned value for this setting.  If both a versioned and a
** non-versioned value exist and are not equal, then a warning message
** might be generated.







*/
char *db_get_versioned(const char *zName, char *zNonVersionedSetting){




  char *zVersionedSetting = 0;
  int noWarn = 0;
  int found = 0;
  struct _cacheEntry {
    struct _cacheEntry *next;
    const char *zName, *zValue;
  } *cacheEntry = 0;
  static struct _cacheEntry *cache = 0;

  if( !g.localOpen && g.zOpenRevision==0 ) return zNonVersionedSetting;



  /* Look up name in cache */

  cacheEntry = cache;
  while( cacheEntry!=0 ){
    if( fossil_strcmp(cacheEntry->zName, zName)==0 ){
      zVersionedSetting = fossil_strdup(cacheEntry->zValue);
      break;
    }
    cacheEntry = cacheEntry->next;
  }


  /* Attempt to read value from file in check-out if there wasn't a cache hit.*/
  if( cacheEntry==0 ){
    Blob versionedPathname;
    Blob setting;
    blob_zero(&versionedPathname);
    blob_zero(&setting);
    blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
                 g.zLocalRoot, zName);
    if( !g.localOpen ){
      /* Repository is in the process of being opened, but files have not been
       * written to disk. Load from the database. */
      Blob noWarnFile;


      if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname),
          &setting, 0) ){

        found = 1;
      }
      /* See if there's a no-warn flag */
      blob_append(&versionedPathname, ".no-warn", -1);
      blob_zero(&noWarnFile);
      if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname),
          &noWarnFile, 0) ){
        noWarn = 1;
      }
      blob_reset(&noWarnFile);
    }else if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
      /* File exists, and contains the value for this setting. Load from
      ** the file. */
      const char *zFile = blob_str(&versionedPathname);
      if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){
        found = 1;
      }
      /* See if there's a no-warn flag */
      blob_append(&versionedPathname, ".no-warn", -1);
      if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
        noWarn = 1;

      }
    }
    blob_reset(&versionedPathname);
    if( found ){
      blob_strip_comment_lines(&setting, &setting);
      blob_trim(&setting); /* Avoid non-obvious problems with line endings
                           ** on boolean properties */
      zVersionedSetting = fossil_strdup(blob_str(&setting));
    }
    blob_reset(&setting);

    /* Store result in cache, which can be the value or 0 if not found */

    cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(struct _cacheEntry));
    cacheEntry->next = cache;
    cacheEntry->zName = zName;
    cacheEntry->zValue = fossil_strdup(zVersionedSetting);
    cache = cacheEntry;
  }


  /* Display a warning? */
  if( zVersionedSetting!=0 && zNonVersionedSetting!=0
   && zNonVersionedSetting[0]!='\0' && !noWarn



  ){
    /* There's a versioned setting, and a non-versioned setting. Tell
    ** the user about the conflict */
    fossil_warning(
        "setting %s has both versioned and non-versioned values: using "
        "versioned value from file \"%/.fossil-settings/%s\" (to silence "
        "this warning, either create an empty file named "
        "\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete "
        "the non-versioned setting with \"fossil unset %s\")", zName,
        g.zLocalRoot, zName, g.zLocalRoot, zName, zName
    );
  }

  /* Prefer the versioned setting */
  return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting;
}


/*
** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the







>
>
>
>
>
>
>

|
>
>
>
>









|
>
>
>

>
|
|
|
|
|
|
|
|
>
>




|
|
<
<
|


<
>
>
|
|
>


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










>

>
|
|
|
|
|
|
>
>

|
|
>
>
>












>







3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690


3691
3692
3693

3694
3695
3696
3697
3698
3699
3700
3701
3702
3703





3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
** Return the text of the string if it is found.  Return NULL if not
** found.
**
** If the zNonVersionedSetting parameter is not NULL then it holds the
** non-versioned value for this setting.  If both a versioned and a
** non-versioned value exist and are not equal, then a warning message
** might be generated.
**
** zCkin is normally NULL.  In that case, the versioned setting is
** take from the local check-out, if a local checkout exists, or from
** checkin named by the g.zOpenRevision global variable.  If zCkin is
** not NULL, then zCkin is the name of the specific checkin from which
** versioned setting value is taken.  When zCkin is not NULL, the cache
** is bypassed.
*/
char *db_get_versioned(
  const char *zName,
  char *zNonVersionedSetting,
  const char *zCkin
){
  char *zVersionedSetting = 0;
  int noWarn = 0;
  int found = 0;
  struct _cacheEntry {
    struct _cacheEntry *next;
    const char *zName, *zValue;
  } *cacheEntry = 0;
  static struct _cacheEntry *cache = 0;

  if( !g.localOpen && g.zOpenRevision==0 && zCkin==0 ){
    return zNonVersionedSetting;
  }

  /* Look up name in cache */
  if( zCkin==0 ){
    cacheEntry = cache;
    while( cacheEntry!=0 ){
      if( fossil_strcmp(cacheEntry->zName, zName)==0 ){
        zVersionedSetting = fossil_strdup(cacheEntry->zValue);
        break;
      }
      cacheEntry = cacheEntry->next;
    }
  }

  /* Attempt to read value from file in check-out if there wasn't a cache hit.*/
  if( cacheEntry==0 ){
    Blob versionedPathname;
    Blob setting;
    blob_init(&versionedPathname, 0, 0);
    blob_init(&setting, 0, 0);


    if( !g.localOpen || zCkin!=0 ){
      /* Repository is in the process of being opened, but files have not been
       * written to disk. Load from the database. */

      blob_appendf(&versionedPathname, ".fossil-settings/%s", zName);
      if( historical_blob(zCkin ? zCkin : g.zOpenRevision,
                          blob_str(&versionedPathname),
                          &setting, 0)
      ){
        found = 1;
      }
    }else{
      blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
                   g.zLocalRoot, zName);





      if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
        /* File exists, and contains the value for this setting. Load from
        ** the file. */
        const char *zFile = blob_str(&versionedPathname);
        if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){
          found = 1;
        }
        /* See if there's a no-warn flag */
        blob_append(&versionedPathname, ".no-warn", -1);
        if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
          noWarn = 1;
        }
      }
    }
    blob_reset(&versionedPathname);
    if( found ){
      blob_strip_comment_lines(&setting, &setting);
      blob_trim(&setting); /* Avoid non-obvious problems with line endings
                           ** on boolean properties */
      zVersionedSetting = fossil_strdup(blob_str(&setting));
    }
    blob_reset(&setting);

    /* Store result in cache, which can be the value or 0 if not found */
    if( zCkin==0 ){
      cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(*cacheEntry));
      cacheEntry->next = cache;
      cacheEntry->zName = zName;
      cacheEntry->zValue = fossil_strdup(zVersionedSetting);
      cache = cacheEntry;
    }
  }

  /* Display a warning? */
  if( zVersionedSetting!=0
   && zNonVersionedSetting!=0
   && zNonVersionedSetting[0]!='\0'
   && zCkin==0
   && !noWarn
  ){
    /* There's a versioned setting, and a non-versioned setting. Tell
    ** the user about the conflict */
    fossil_warning(
        "setting %s has both versioned and non-versioned values: using "
        "versioned value from file \"%/.fossil-settings/%s\" (to silence "
        "this warning, either create an empty file named "
        "\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete "
        "the non-versioned setting with \"fossil unset %s\")", zName,
        g.zLocalRoot, zName, g.zLocalRoot, zName, zName
    );
  }

  /* Prefer the versioned setting */
  return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting;
}


/*
** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
    }
    db_reset(&q2);
  }
  if( pSetting!=0 && pSetting->versionable ){
    /* This is a versionable setting, try and get the info from a
    ** checked-out file */
    char * zZ = z;
    z = db_get_versioned(zName, z);
    if(zZ != z){
      fossil_free(zZ);
    }
  }
  if( z==0 ){
    if( zDefault==0 && pSetting && pSetting->def[0] ){
      z = fossil_strdup(pSetting->def);







|







3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
    }
    db_reset(&q2);
  }
  if( pSetting!=0 && pSetting->versionable ){
    /* This is a versionable setting, try and get the info from a
    ** checked-out file */
    char * zZ = z;
    z = db_get_versioned(zName, z, 0);
    if(zZ != z){
      fossil_free(zZ);
    }
  }
  if( z==0 ){
    if( zDefault==0 && pSetting && pSetting->def[0] ){
      z = fossil_strdup(pSetting->def);
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
  }else if( is_false(zVal) ){
    dflt = 0;
  }
  fossil_free(zVal);
  return dflt;
}
int db_get_versioned_boolean(const char *zName, int dflt){
  char *zVal = db_get_versioned(zName, 0);
  if( zVal==0 ) return dflt;
  if( is_truth(zVal) ) return 1;
  if( is_false(zVal) ) return 0;
  return dflt;
}
char *db_lget(const char *zName, const char *zDefault){
  return db_text(zDefault,







|







3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
  }else if( is_false(zVal) ){
    dflt = 0;
  }
  fossil_free(zVal);
  return dflt;
}
int db_get_versioned_boolean(const char *zName, int dflt){
  char *zVal = db_get_versioned(zName, 0, 0);
  if( zVal==0 ) return dflt;
  if( is_truth(zVal) ) return 1;
  if( is_false(zVal) ) return 0;
  return dflt;
}
char *db_lget(const char *zName, const char *zDefault){
  return db_text(zDefault,
4046
4047
4048
4049
4050
4051
4052








4053
4054
4055
4056








4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072



























4073
4074
4075
4076
4077
4078
4079

/*
** Get the manifest setting.  For backwards compatibility first check if the
** value is a boolean.  If it's not a boolean, treat each character as a flag
** to enable a manifest type.  This system puts certain boundary conditions on
** which letters can be used to represent flags (any permutation of flags must
** not be able to fully form one of the boolean values).








*/
int db_get_manifest_setting(void){
  int flg;
  char *zVal = db_get("manifest", 0);








  if( zVal==0 || is_false(zVal) ){
    return 0;
  }else if( is_truth(zVal) ){
    return MFESTFLG_RAW|MFESTFLG_UUID;
  }
  flg = 0;
  while( *zVal ){
    switch( *zVal ){
      case 'r': flg |= MFESTFLG_RAW;  break;
      case 'u': flg |= MFESTFLG_UUID; break;
      case 't': flg |= MFESTFLG_TAGS; break;
    }
    zVal++;
  }
  return flg;
}





























/*
** Record the name of a local repository in the global_config() database.
** The repository filename %s is recorded as an entry with a "name" field
** of the following form:
**







>
>
>
>
>
>
>
>

|

|
>
>
>
>
>
>
>
>
















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







4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138

/*
** Get the manifest setting.  For backwards compatibility first check if the
** value is a boolean.  If it's not a boolean, treat each character as a flag
** to enable a manifest type.  This system puts certain boundary conditions on
** which letters can be used to represent flags (any permutation of flags must
** not be able to fully form one of the boolean values).
**
** "manifest" is a versionable setting.  But we do not issue a warning
** if there is a conflict.  Instead, the value returned is the value for
** the versioned setting if the versioned setting exists, or the ordinary
** setting otherwise.
**
** The argument zCkin is the specific check-in for which we want the
** manifest setting.
*/
int db_get_manifest_setting(const char *zCkin){
  int flg;
  char *zVal;
  
  /* Look for the versioned setting first */
  zVal = db_get_versioned("manifest", 0, zCkin);

  if( zVal==0 && g.repositoryOpen ){
    /* No versioned setting, look for the repository setting second */
    zVal = db_text(0, "SELECT value FROM config WHERE name='manifest'");
  }
  if( zVal==0 || is_false(zVal) ){
    return 0;
  }else if( is_truth(zVal) ){
    return MFESTFLG_RAW|MFESTFLG_UUID;
  }
  flg = 0;
  while( *zVal ){
    switch( *zVal ){
      case 'r': flg |= MFESTFLG_RAW;  break;
      case 'u': flg |= MFESTFLG_UUID; break;
      case 't': flg |= MFESTFLG_TAGS; break;
    }
    zVal++;
  }
  return flg;
}

/*
** COMMAND: test-manifest-setting
**
** Usage: %fossil test-manifest-setting VERSION VERSION ...
**
** Display the value for the "manifest" setting for various versions
** of the repository.
*/
void test_manfest_setting_cmd(void){
  int i;
  db_find_and_open_repository(0, 0);
  for(i=2; i<g.argc; i++){
    int m = db_get_manifest_setting(g.argv[i]);
    fossil_print("%s:\n", g.argv[i]);
    fossil_print("   flags = 0x%02x\n", m);
    if( m & MFESTFLG_RAW ){
      fossil_print("   manifest\n");
    }
    if( m & MFESTFLG_UUID ){
      fossil_print("   manifest.uuid\n");
    }
    if( m & MFESTFLG_TAGS ){
      fossil_print("   manifest.tags\n");
    }
  }
}


/*
** Record the name of a local repository in the global_config() database.
** The repository filename %s is recorded as an entry with a "name" field
** of the following form:
**
4179
4180
4181
4182
4183
4184
4185

4186
4187
4188
4189
4190
4191
4192
**   -f|--force        Continue with the open even if the working directory is
**                     not empty, or if auto-sync fails.
**   --force-missing   Force opening a repository with missing content
**   -k|--keep         Only modify the manifest file(s)
**   --nested          Allow opening a repository inside an opened check-out
**   --nosync          Do not auto-sync the repository prior to opening even
**                     if the autosync setting is on.

**   --repodir DIR     If REPOSITORY is a URI that will be cloned, store
**                     the clone in DIR rather than in "."
**   --setmtime        Set timestamps of all files to match their SCM-side
**                     times (the timestamp of the last check-in which modified
**                     them).
**   --verbose         If passed a URI then this flag is passed on to the clone
**                     operation, otherwise it has no effect







>







4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
**   -f|--force        Continue with the open even if the working directory is
**                     not empty, or if auto-sync fails.
**   --force-missing   Force opening a repository with missing content
**   -k|--keep         Only modify the manifest file(s)
**   --nested          Allow opening a repository inside an opened check-out
**   --nosync          Do not auto-sync the repository prior to opening even
**                     if the autosync setting is on.
**   --proxy PROXY     Use PROXY as http proxy during sync operation
**   --repodir DIR     If REPOSITORY is a URI that will be cloned, store
**                     the clone in DIR rather than in "."
**   --setmtime        Set timestamps of all files to match their SCM-side
**                     times (the timestamp of the last check-in which modified
**                     them).
**   --verbose         If passed a URI then this flag is passed on to the clone
**                     operation, otherwise it has no effect
4368
4369
4370
4371
4372
4373
4374
















4375
4376
4377
4378





4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394


4395



4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411






4412
4413
4414
4415
4416















4417






4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
    if(vid!=0){
      vfile_check_signature(vid, CKSIG_SETMTIME);
    }
  }
  g.argc = 2;
  info_cmd();
}

















/*
** Print the current value of a setting identified by the pSetting
** pointer.





*/
void print_setting(const Setting *pSetting, int valueOnly){
  Stmt q;
  int versioned = 0;
  if( pSetting->versionable && g.localOpen ){
    /* Check to see if this is overridden by a versionable settings file */
    Blob versionedPathname;
    blob_zero(&versionedPathname);
    blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
                 g.zLocalRoot, pSetting->name);
    if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
      versioned = 1;
    }
    blob_reset(&versionedPathname);
  }
  if( valueOnly && versioned ){


    fossil_print("%s\n", db_get_versioned(pSetting->name, NULL));



    return;
  }
  if( g.repositoryOpen ){
    db_prepare(&q,
       "SELECT '(local)', value FROM config WHERE name=%Q"
       " UNION ALL "
       "SELECT '(global)', value FROM global_config WHERE name=%Q",
       pSetting->name, pSetting->name
    );
  }else{
    db_prepare(&q,
      "SELECT '(global)', value FROM global_config WHERE name=%Q",
      pSetting->name
    );
  }
  if( db_step(&q)==SQLITE_ROW ){






    if( valueOnly ){
      fossil_print("%s\n", db_column_text(&q, 1));
    }else{
      fossil_print("%-20s %-8s %s\n", pSetting->name, db_column_text(&q, 0),
          db_column_text(&q, 1));















    }






  }else if( valueOnly ){
    fossil_print("\n");
  }else{
    fossil_print("%-20s\n", pSetting->name);
  }
  if( versioned ){
    fossil_print("  (overridden by contents of file .fossil-settings/%s)\n",
                 pSetting->name);
  }
  db_finalize(&q);
}







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




>
>
>
>
>

|














>
>
|
>
>
>
















>
>
>
>
>
>
|


|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>



|







4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
    if(vid!=0){
      vfile_check_signature(vid, CKSIG_SETMTIME);
    }
  }
  g.argc = 2;
  info_cmd();
}

/*
** Return true if pSetting has its default value assuming its
** current value is zVal.
*/
int setting_has_default_value(const Setting *pSetting, const char *zVal){
  if( zVal==0 ) return 1;
  if( pSetting->def==0 ) return 0;
  if( pSetting->width==0 ){
    return is_false(pSetting->def)==is_false(zVal);
  }
  if( fossil_strcmp(pSetting->def, zVal)==0 ) return 1;
  if( is_false(zVal) && is_false(pSetting->def) ) return 1;
  if( is_truth(zVal) && is_truth(pSetting->def) ) return 1;
  return 0;
}

/*
** Print the current value of a setting identified by the pSetting
** pointer.
**
** Only show the value, not the setting name, if valueOnly is true.
**
** Show nothing if bIfChng is true and the setting is not currently set
** or is set to its default value.
*/
void print_setting(const Setting *pSetting, int valueOnly, int bIfChng){
  Stmt q;
  int versioned = 0;
  if( pSetting->versionable && g.localOpen ){
    /* Check to see if this is overridden by a versionable settings file */
    Blob versionedPathname;
    blob_zero(&versionedPathname);
    blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
                 g.zLocalRoot, pSetting->name);
    if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
      versioned = 1;
    }
    blob_reset(&versionedPathname);
  }
  if( valueOnly && versioned ){
    const char *zVal = db_get_versioned(pSetting->name, NULL, NULL);
    if( !bIfChng || (zVal!=0 && fossil_strcmp(zVal, pSetting->def)!=0) ){
      fossil_print("%s\n", db_get_versioned(pSetting->name, NULL, NULL));
    }else{
      versioned = 0;
    }
    return;
  }
  if( g.repositoryOpen ){
    db_prepare(&q,
       "SELECT '(local)', value FROM config WHERE name=%Q"
       " UNION ALL "
       "SELECT '(global)', value FROM global_config WHERE name=%Q",
       pSetting->name, pSetting->name
    );
  }else{
    db_prepare(&q,
      "SELECT '(global)', value FROM global_config WHERE name=%Q",
      pSetting->name
    );
  }
  if( db_step(&q)==SQLITE_ROW ){
    const char *zVal = db_column_text(&q,1);
    if( bIfChng && setting_has_default_value(pSetting,zVal) ){
      if( versioned ){
        fossil_print("%-24s (versioned)\n", pSetting->name);
        versioned = 0;
      }
    }else if( valueOnly ){
      fossil_print("%s\n", db_column_text(&q, 1));
    }else{
      const char *zVal = (const char*)db_column_text(&q,1);
      const char *zName = (const char*)db_column_text(&q,0);
      if( zVal==0 ) zVal = "NULL";
      if( strchr(zVal,'\n')==0 ){
        fossil_print("%-24s %-11s %s\n", pSetting->name, zName, zVal);
      }else{
        fossil_print("%-24s %-11s\n", pSetting->name, zName);
        while( zVal[0] ){
          char *zNL = strchr(zVal, '\n');
          if( zNL==0 ){
            fossil_print("    %s\n", zVal);
            break;
          }else{
            int n = (int)(zNL - zVal);
            while( n>0 && fossil_isspace(zVal[n-1]) ){ n--; }
            fossil_print("    %.*s\n", n, zVal);
            zVal = zNL+1;
          }
        }
      }
    }
  }else if( bIfChng ){
    /* Display nothing */
    versioned = 0;
  }else if( valueOnly ){
    fossil_print("\n");
  }else{
    fossil_print("%-24s\n", pSetting->name);
  }
  if( versioned ){
    fossil_print("  (overridden by contents of file .fossil-settings/%s)\n",
                 pSetting->name);
  }
  db_finalize(&q);
}
4451
4452
4453
4454
4455
4456
4457

4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
                        ** negative for values which should not appear
                        ** on the /setup_settings page. */
  char versionable;     /* Is this setting versionable? */
  char forceTextArea;   /* Force using a text area for display? */
  char sensitive;       /* True if this a security-sensitive setting */
  const char *def;      /* Default value */
};

#endif /* INTERFACE */

/*
** SETTING: access-log      boolean default=off
**
** When the access-log setting is enabled, all login attempts (successful
** and unsuccessful) on the web interface are recorded in the "access" table
** of the repository.
*/
/*
** SETTING: admin-log       boolean default=off
**
** When the admin-log setting is enabled, configuration changes are recorded
** in the "admin_log" table of the repository.
*/
/*
** SETTING: allow-symlinks  boolean default=off sensitive
**







>



|






|







4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
                        ** negative for values which should not appear
                        ** on the /setup_settings page. */
  char versionable;     /* Is this setting versionable? */
  char forceTextArea;   /* Force using a text area for display? */
  char sensitive;       /* True if this a security-sensitive setting */
  const char *def;      /* Default value */
};

#endif /* INTERFACE */

/*
** SETTING: access-log      boolean default=on
**
** When the access-log setting is enabled, all login attempts (successful
** and unsuccessful) on the web interface are recorded in the "access" table
** of the repository.
*/
/*
** SETTING: admin-log       boolean default=on
**
** When the admin-log setting is enabled, configuration changes are recorded
** in the "admin_log" table of the repository.
*/
/*
** SETTING: allow-symlinks  boolean default=off sensitive
**
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651




4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
/*
** SETTING: clearsign       boolean default=off
** When enabled, fossil will attempt to sign all commits
** with gpg or ssh.  When disabled, commits will be unsigned.
*/
/*
** SETTING: comment-format  width=16 default=1
** Set the default options for printing timeline comments to the console.
**
** The global --comfmtflags command-line option (or alias --comment-format)
** overrides this setting.
**
** Possible values are:
**    1     Activate the legacy comment printing format (default).




**
** Or a bitwise combination of the following flags:
**    0     Activate the newer (non-legacy) comment printing format.
**    2     Trim leading and trailing CR and LF characters.
**    4     Trim leading and trailing white space characters.
**    8     Attempt to break lines on word boundaries.
**   16     Break lines before the original comment embedded in other text.
**
** Note: To preserve line breaks, activate the newer (non-legacy) comment
** printing format (i.e. set to "0", or a combination not including "1").
**
** Note: The options for timeline comments displayed on the web UI can be
** configured through the /setup_timeline web page.
*/
/*
** SETTING: crlf-glob       width=40 versionable block-text
** The VALUE of this setting is a list of GLOB patterns matching files
** in which it is allowed to have CR, CR+LF or mixed line endings,
** suppressing Fossil's normal warning about this. Set it to "*" to
** disable CR+LF checking entirely.  Example: *.md,*.txt







|

<
<
<

|
>
>
>
>


<





|
|
<
<
<







4752
4753
4754
4755
4756
4757
4758
4759
4760



4761
4762
4763
4764
4765
4766
4767
4768

4769
4770
4771
4772
4773
4774
4775



4776
4777
4778
4779
4780
4781
4782
/*
** SETTING: clearsign       boolean default=off
** When enabled, fossil will attempt to sign all commits
** with gpg or ssh.  When disabled, commits will be unsigned.
*/
/*
** SETTING: comment-format  width=16 default=1
** Set the algorithm for printing timeline comments to the console.
**



** Possible values are:
**    1     Use the original comment printing algorithm:
**             *   Leading and trialing whitespace is removed
**             *   Internal whitespace is converted into a single space (0x20)
**             *   Line breaks occurs at whitespace or hyphens if possible
**          This is the recommended value and the default.
**
** Or a bitwise combination of the following flags:

**    2     Trim leading and trailing CR and LF characters.
**    4     Trim leading and trailing white space characters.
**    8     Attempt to break lines on word boundaries.
**   16     Break lines before the original comment embedded in other text.
**
** Note: To preserve line breaks and/or other whitespace within comment text,
** make this setting some integer value that omits the "1" bit.



*/
/*
** SETTING: crlf-glob       width=40 versionable block-text
** The VALUE of this setting is a list of GLOB patterns matching files
** in which it is allowed to have CR, CR+LF or mixed line endings,
** suppressing Fossil's normal warning about this. Set it to "*" to
** disable CR+LF checking entirely.  Example: *.md,*.txt
4707
4708
4709
4710
4711
4712
4713








4714
4715
4716
4717
4718
4719
4720
** SETTING: dotfiles        boolean versionable default=off
** If enabled, include --dotfiles option for all compatible commands.
*/
/*
** SETTING: editor          width=32 sensitive
** The value is an external command that will launch the
** text editor command used for check-in comments.








*/
/*
** SETTING: empty-dirs      width=40 versionable block-text
** The value is a list of pathnames parsed according to the same rules as
** the *-glob settings.  On update and checkout commands, if no directory
** exists with that name, an empty directory will be be created, even if
** it must create one or more parent directories.







>
>
>
>
>
>
>
>







4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
** SETTING: dotfiles        boolean versionable default=off
** If enabled, include --dotfiles option for all compatible commands.
*/
/*
** SETTING: editor          width=32 sensitive
** The value is an external command that will launch the
** text editor command used for check-in comments.
**
** If this value is not set, then environment variables VISUAL and
** EDITOR are consulted, in that order.  If neither of those are set,
** then a search is made for common text editors, including
** "notepad", "nano", "pico", "jove", "edit", "vi", "vim", and "ed".
**
** If this setting is false ("off", "no", "false", or "0") then no
** text editor is used.
*/
/*
** SETTING: empty-dirs      width=40 versionable block-text
** The value is a list of pathnames parsed according to the same rules as
** the *-glob settings.  On update and checkout commands, if no directory
** exists with that name, an empty directory will be be created, even if
** it must create one or more parent directories.
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766

4767
4768
4769
4770
4771
4772
4773
** commits.  If enabled on a server, whenever a client attempts
** to obtain a check-in lock during auto-sync, the server will
** send the "pragma avoid-delta-manifests" statement in its reply,
** which will cause the client to avoid generating a delta
** manifest.
*/
/*
** SETTING: gdiff-command    width=40 default=gdiff sensitive
** The value is an external command to run when performing a graphical
** diff. If undefined, text diff will be used.

*/
/*
** SETTING: gmerge-command   width=40 sensitive
** The value is a graphical merge conflict resolver command operating
** on four files.  Examples:
**
**     kdiff3 "%baseline" "%original" "%merge" -o "%output"







|

|
>







4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
** commits.  If enabled on a server, whenever a client attempts
** to obtain a check-in lock during auto-sync, the server will
** send the "pragma avoid-delta-manifests" statement in its reply,
** which will cause the client to avoid generating a delta
** manifest.
*/
/*
** SETTING: gdiff-command    width=40 sensitive
** The value is an external command to run when performing a graphical
** diff. If undefined, a --tk diff is done if commands "tclsh" and "wish"
** are on PATH, or a --by diff is done if "tclsh" or "wish" are unavailable.
*/
/*
** SETTING: gmerge-command   width=40 sensitive
** The value is a graphical merge conflict resolver command operating
** on four files.  Examples:
**
**     kdiff3 "%baseline" "%original" "%merge" -o "%output"
4947
4948
4949
4950
4951
4952
4953
4954

4955
4956
4957
4958
4959
4960
4961
**    5)  fossil all ui
**    6)  fossil all server
**
** All repositories are searched (in lexicographical order) and the first
** repository with a non-zero "repolist-skin" value is used as the skin
** for the repository list page.  If none of the repositories on the list
** have a non-zero "repolist-skin" setting then the repository list is
** displayed using unadorned HTML ("skinless").

**
** If repolist-skin has a value of 2, then the repository is omitted from
** the list in use cases 1 through 4, but not for 5 and 6.
*/
/*
** SETTING: self-pw-reset    boolean default=off sensitive
** Allow users to request that an email containing a hyperlink







|
>







5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
**    5)  fossil all ui
**    6)  fossil all server
**
** All repositories are searched (in lexicographical order) and the first
** repository with a non-zero "repolist-skin" value is used as the skin
** for the repository list page.  If none of the repositories on the list
** have a non-zero "repolist-skin" setting then the repository list is
** displayed using unadorned HTML ("skinless"), with the page title taken
** from the FOSSIL_REPOLIST_TITLE environment variable.
**
** If repolist-skin has a value of 2, then the repository is omitted from
** the list in use cases 1 through 4, but not for 5 and 6.
*/
/*
** SETTING: self-pw-reset    boolean default=off sensitive
** Allow users to request that an email containing a hyperlink
5152
5153
5154
5155
5156
5157
5158


5159
5160
5161
5162
5163
5164
5165
5166
5167
5168

5169
5170
5171
5172
5173
5174
5175
** that applies to all repositories.  The local values are stored in the
** "config" table of the repository and the global values are stored in the
** configuration database.  If both a local and a global value exists for a
** setting, the local value takes precedence.  This command normally operates
** on the local settings.  Use the --global option to change global settings.
**
** 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
**   --value    Only show the value of a given property (implies --exact)
**
** 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 valueFlag = find_option("value",0,0)!=0;
  /* Undocumented "--test-for-subsystem SUBSYS" option used to test
  ** the db_get_for_subsystem() interface: */
  const char *zSubsys = find_option("test-for-subsystem",0,1);
  int unsetFlag = g.argv[1][0]=='u';
  int nSetting;







>
>


<







>







5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283

5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
** that applies to all repositories.  The local values are stored in the
** "config" table of the repository and the global values are stored in the
** configuration database.  If both a local and a global value exists for a
** setting, the local value takes precedence.  This command normally operates
** on the local settings.  Use the --global option to change global settings.
**
** Options:
**   --changed  Only show settings if the value differs from the default
**   --exact    Only consider exact name matches
**   --global   Set or unset the given property globally instead of
**              setting or unsetting it for the open repository only

**   --value    Only show the value of a given property (implies --exact)
**
** See also: [[configuration]]
*/
void setting_cmd(void){
  int i;
  int globalFlag = find_option("global","g",0)!=0;
  int bIfChng = find_option("changed",0,0)!=0;
  int exactFlag = find_option("exact",0,0)!=0;
  int valueFlag = find_option("value",0,0)!=0;
  /* Undocumented "--test-for-subsystem SUBSYS" option used to test
  ** the db_get_for_subsystem() interface: */
  const char *zSubsys = find_option("test-for-subsystem",0,1);
  int unsetFlag = g.argv[1][0]=='u';
  int nSetting;
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
  if( unsetFlag && g.argc!=3 ){
    usage("PROPERTY ?-global?");
  }
  if( valueFlag ){
    if( g.argc!=3 ){
      fossil_fatal("--value is only supported when qurying a given property");
    }
    exactFlag = 1;
  }

  if( g.argc==2 ){
    for(i=0; i<nSetting; i++){
      print_setting(&aSetting[i], 0);
    }
  }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);







<




|







5309
5310
5311
5312
5313
5314
5315

5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
  if( unsetFlag && g.argc!=3 ){
    usage("PROPERTY ?-global?");
  }
  if( valueFlag ){
    if( g.argc!=3 ){
      fossil_fatal("--value is only supported when qurying a given property");
    }

  }

  if( g.argc==2 ){
    for(i=0; i<nSetting; i++){
      print_setting(&aSetting[i], 0, bIfChng);
    }
  }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);
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
          fossil_print("%s (subsystem %s) ->",  pSetting->name, zSubsys);
          if( zValue ){
            fossil_print(" [%s]", zValue);
            fossil_free(zValue);
          }
          fossil_print("\n");
        }else{
          print_setting(pSetting, valueFlag);
        }
        pSetting++;
      }
    }
  }else{
    usage("?PROPERTY? ?VALUE? ?-global?");
  }







|







5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
          fossil_print("%s (subsystem %s) ->",  pSetting->name, zSubsys);
          if( zValue ){
            fossil_print(" [%s]", zValue);
            fossil_free(zValue);
          }
          fossil_print("\n");
        }else{
          print_setting(pSetting, valueFlag, bIfChng);
        }
        pSetting++;
      }
    }
  }else{
    usage("?PROPERTY? ?VALUE? ?-global?");
  }
Changes to src/default.css.
748
749
750
751
752
753
754

755
756
757
758
759
760
761
  border-bottom: 3px solid gold;
}
body.tkt div.content ol.tkt-changes > li:target > ol {
  border-left: 1px solid gold;
}
body.cpage-ckout .file-change-line,
body.cpage-info .file-change-line,

body.cpage-vdiff .file-change-line {
  margin-top: 16px;
  margin-bottom: 16px;
  margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
  display: flex;
  flex-direction: row;
  justify-content: space-between;







>







748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
  border-bottom: 3px solid gold;
}
body.tkt div.content ol.tkt-changes > li:target > ol {
  border-left: 1px solid gold;
}
body.cpage-ckout .file-change-line,
body.cpage-info .file-change-line,
body.cpage-vinfo .file-change-line,
body.cpage-vdiff .file-change-line {
  margin-top: 16px;
  margin-bottom: 16px;
  margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
Changes to src/descendants.c.
154
155
156
157
158
159
160

















161
162
163
164
165
166
167
      "    WHERE tagxref.rid=leaves.rid "
      "      AND tagxref.tagid=%d"
      "      AND tagxref.tagtype>0)",
      TAG_CLOSED
    );
  }
}


















/*
** Load the record ID rid and up to |N|-1 closest ancestors into
** the "ok" table.  If N is zero, no limit.  If ridBackTo is not zero
** then stop the search upon reaching the ancestor with rid==ridBackTo.
*/
void compute_ancestors(int rid, int N, int directOnly, int ridBackTo){







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







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
      "    WHERE tagxref.rid=leaves.rid "
      "      AND tagxref.tagid=%d"
      "      AND tagxref.tagtype>0)",
      TAG_CLOSED
    );
  }
}

/*
** If RID refers to a check-in, return the mtime of that check-in - the
** julian day number of when the check-in occurred.
*/
double mtime_of_rid(int rid, double mtime){
  static Stmt q;
  db_static_prepare(&q,"SELECT mtime FROM event WHERE objid=:rid");
  db_bind_int(&q, ":rid", rid);
  if( db_step(&q)==SQLITE_ROW ){
    mtime = db_column_double(&q,0);
  }
  db_reset(&q);
  return mtime;
}



/*
** Load the record ID rid and up to |N|-1 closest ancestors into
** the "ok" table.  If N is zero, no limit.  If ridBackTo is not zero
** then stop the search upon reaching the ancestor with rid==ridBackTo.
*/
void compute_ancestors(int rid, int N, int directOnly, int ridBackTo){
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
    **    (1)  Primary parents
    **    (2)  Merge parents
    **    (3)  Cherrypick merge parents.
    **    (4)  All ancestores of 1 and 2 but not of 3.
    */
    double rLimitMtime = 0.0;
    if( ridBackTo ){
      rLimitMtime = db_double(0.0,
         "SELECT mtime FROM event WHERE objid=%d",
         ridBackTo);
    }
    db_multi_exec(
      "WITH RECURSIVE\n"
      "  parent(pid,cid,isCP) AS (\n"
      "    SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink\n"
      "    UNION ALL\n"
      "    SELECT parentid, childid, 1 FROM cherrypick WHERE NOT isExclude\n"







|
<
<







212
213
214
215
216
217
218
219


220
221
222
223
224
225
226
    **    (1)  Primary parents
    **    (2)  Merge parents
    **    (3)  Cherrypick merge parents.
    **    (4)  All ancestores of 1 and 2 but not of 3.
    */
    double rLimitMtime = 0.0;
    if( ridBackTo ){
      rLimitMtime = mtime_of_rid(ridBackTo, 0.0);


    }
    db_multi_exec(
      "WITH RECURSIVE\n"
      "  parent(pid,cid,isCP) AS (\n"
      "    SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink\n"
      "    UNION ALL\n"
      "    SELECT parentid, childid, 1 FROM cherrypick WHERE NOT isExclude\n"
Changes to src/diff.c.
3173
3174
3175
3176
3177
3178
3179

3180

3181
3182
3183
3184
3185
3186
3187
        nDel += c.aEdit[i+1];
        nIns += c.aEdit[i+2];
      }
      g.diffCnt[1] += nIns;
      g.diffCnt[2] += nDel;
      if( nIns+nDel ){
        g.diffCnt[0]++;

        blob_appendf(pOut, "%10d %10d", nIns, nDel);

      }
    }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
      const int *R = c.aEdit;
      unsigned int r;
      for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
        blob_appendf(pOut, " copy %6d  delete %6d  insert %6d\n",
                     R[r], R[r+1], R[r+2]);







>
|
>







3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
        nDel += c.aEdit[i+1];
        nIns += c.aEdit[i+2];
      }
      g.diffCnt[1] += nIns;
      g.diffCnt[2] += nDel;
      if( nIns+nDel ){
        g.diffCnt[0]++;
        if( !(pCfg->diffFlags & DIFF_BRIEF) ){
          blob_appendf(pOut, "%10d %10d", nIns, nDel);
        }
      }
    }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
      const int *R = c.aEdit;
      unsigned int r;
      for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
        blob_appendf(pOut, " copy %6d  delete %6d  insert %6d\n",
                     R[r], R[r+1], R[r+2]);
3333
3334
3335
3336
3337
3338
3339




3340
3341
3342
3343
3344
3345
3346
      pCfg->zBinGlob = diff_get_binary_glob();
      zDiffBinary = find_option("diff-binary", 0, 1);
      if( zDiffBinary ){
        if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY;
      }else if( db_get_boolean("diff-binary", 1) ){
        diffFlags |= DIFF_INCBINARY;
      }




    }
  }
  if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE;
  /* Deprecated, but retained for script compatibility. */
  else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE;

  pCfg->diffFlags = diffFlags;







>
>
>
>







3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
      pCfg->zBinGlob = diff_get_binary_glob();
      zDiffBinary = find_option("diff-binary", 0, 1);
      if( zDiffBinary ){
        if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY;
      }else if( db_get_boolean("diff-binary", 1) ){
        diffFlags |= DIFF_INCBINARY;
      }
    }else if( isGDiff) {
      /* No external gdiff command found, using --by */
      diffFlags |= DIFF_HTML|DIFF_WEBPAGE|DIFF_LINENO|DIFF_BROWSER
                     |DIFF_SIDEBYSIDE;
    }
  }
  if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE;
  /* Deprecated, but retained for script compatibility. */
  else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE;

  pCfg->diffFlags = diffFlags;
Changes to src/diff.tcl.
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
  upvar $iivar ii
  if {$ii>=$N} {return -1}
  set x [lindex $difftxt $ii]
  incr ii
  return $x
}










proc readDiffs {fossilcmd} {
  global difftxt debug
  if {![info exists difftxt]} {
    if {$debug} {
      puts "# [list open $fossilcmd r]"
      flush stdout
    }

    set in [open $fossilcmd r]
    fconfigure $in -encoding utf-8
    set difftxt [split [read $in] \n]
    close $in









  }
  set N [llength $difftxt]
  set ii 0
  set nDiffs 0
  set n1 0
  set n2 0  
  array set widths {txt 3 ln 3 mkr 1}









  
  
  set fromIndex [lsearch -glob $fossilcmd *-from]
  set toIndex [lsearch -glob $fossilcmd *-to]
  set branchIndex [lsearch -glob $fossilcmd *-branch]
  set checkinIndex [lsearch -glob $fossilcmd *-checkin]
  if {[lsearch -glob $fossilcmd *-label]>=0







>
>
>
>
>
>
>
>
>
|






>
|
|
|
|
>
>
>
>
>
>
>
>
>







>
>
>
>
>
>
>
>
>







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
  upvar $iivar ii
  if {$ii>=$N} {return -1}
  set x [lindex $difftxt $ii]
  incr ii
  return $x
}

proc reloadDiff {} {
  global fossilcmd difftxt
  unset -nocomplain difftxt
  set idx [.txtA index @0,0]
  readDiffs $fossilcmd 1
  update
  viewDiff $idx
}

proc readDiffs {fossilcmd redo} {
  global difftxt debug
  if {![info exists difftxt]} {
    if {$debug} {
      puts "# [list open $fossilcmd r]"
      flush stdout
    }
    if {[catch {
      set in [open $fossilcmd r]
      fconfigure $in -encoding utf-8
      set difftxt [split [read $in] \n]
      close $in
    } msg]} {
      if {$redo} {
        tk_messageBox -type ok -title Error -message "Unable to refresh:\n$msg"
        return 0
      } else {
        puts $msg
        exit 1
      }
    }
  }
  set N [llength $difftxt]
  set ii 0
  set nDiffs 0
  set n1 0
  set n2 0  
  array set widths {txt 3 ln 3 mkr 1}
  if {$redo} {
    foreach c [cols] {$c config -state normal}
    .lnA delete 1.0 end
    .txtA delete 1.0 end
    .lnB delete 1.0 end
    .txtB delete 1.0 end
    .mkr delete 1.0 end
    .wfiles.lb delete 0 end
  }
  
  
  set fromIndex [lsearch -glob $fossilcmd *-from]
  set toIndex [lsearch -glob $fossilcmd *-to]
  set branchIndex [lsearch -glob $fossilcmd *-branch]
  set checkinIndex [lsearch -glob $fossilcmd *-checkin]
  if {[lsearch -glob $fossilcmd *-label]>=0
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
}

::ttk::scrollbar .sby -command {.txtA yview} -orient vertical
::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal
::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal
frame .spacer

if {[readDiffs $fossilcmd] == 0} {
  tk_messageBox -type ok -title $CFG(TITLE) -message "No changes"
  exit
}
update idletasks

proc saveDiff {} {
  set fn [tk_getSaveFile]







|







485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
}

::ttk::scrollbar .sby -command {.txtA yview} -orient vertical
::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal
::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal
frame .spacer

if {[readDiffs $fossilcmd 0] == 0} {
  tk_messageBox -type ok -title $CFG(TITLE) -message "No changes"
  exit
}
update idletasks

proc saveDiff {} {
  set fn [tk_getSaveFile]
572
573
574
575
576
577
578

579
580
581
582
583
584
585
586
587
588
589
    $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







>



|







600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
    $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.reload -text {Reload} -command reloadDiff
::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.reload .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
Changes to src/diffcmd.c.
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
void diff_print_versions(const char *zFrom, const char *zTo, DiffConfig *pCfg){
  if( (pCfg->diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF|DIFF_NUMSTAT|
        DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){
    fossil_print("Fossil-Diff-From:  %s\n",
      zFrom[0]=='(' ? zFrom : mprintf("%S %s",
        rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")),
        db_text("","SELECT datetime(%f)||' UTC'",
          symbolic_name_to_mtime(zFrom, 0))));
    fossil_print("Fossil-Diff-To:    %s\n",
      zTo[0]=='(' ? zTo : mprintf("%S %s",
        rid_to_uuid(symbolic_name_to_rid(zTo, "ci")),
        db_text("","SELECT datetime(%f)||' UTC'",
          symbolic_name_to_mtime(zTo, 0))));
    fossil_print("%.66c\n", '-');
  }
}

/*
** Print the "Index:" message that patches wants to see at the top of a diff.
*/







|




|







125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
void diff_print_versions(const char *zFrom, const char *zTo, DiffConfig *pCfg){
  if( (pCfg->diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF|DIFF_NUMSTAT|
        DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){
    fossil_print("Fossil-Diff-From:  %s\n",
      zFrom[0]=='(' ? zFrom : mprintf("%S %s",
        rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")),
        db_text("","SELECT datetime(%f)||' UTC'",
          symbolic_name_to_mtime(zFrom, 0, 0))));
    fossil_print("Fossil-Diff-To:    %s\n",
      zTo[0]=='(' ? zTo : mprintf("%S %s",
        rid_to_uuid(symbolic_name_to_rid(zTo, "ci")),
        db_text("","SELECT datetime(%f)||' UTC'",
          symbolic_name_to_mtime(zTo, 0, 1))));
    fossil_print("%.66c\n", '-');
  }
}

/*
** Print the "Index:" message that patches wants to see at the top of a diff.
*/
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619

620

621
622
623
624
625
626
627
      zName2 = NULL_DEVICE;
    }else{
      blob_read_from_file(&file2, zFile2, ExtFILE);
      zName2 = zName;
    }

    /* Compute and output the differences */
    if( pCfg->diffFlags & DIFF_BRIEF ){
      if( blob_compare(pFile1, &file2) ){
        fossil_print("CHANGED  %s\n", zName);
      }
    }else{
      blob_zero(&out);
      text_diff(pFile1, &file2, &out, pCfg);
      if( blob_size(&out) ){
        if( pCfg->diffFlags & DIFF_NUMSTAT ){

          blob_appendf(pOut, "%s %s\n", blob_str(&out), zName);

        }else{
          diff_print_filenames(zName, zName2, pCfg, pOut);
          blob_appendf(pOut, "%s\n", blob_str(&out));
        }
      }
      blob_reset(&out);
    }







|








>
|
>







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
      zName2 = NULL_DEVICE;
    }else{
      blob_read_from_file(&file2, zFile2, ExtFILE);
      zName2 = zName;
    }

    /* Compute and output the differences */
    if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
      if( blob_compare(pFile1, &file2) ){
        fossil_print("CHANGED  %s\n", zName);
      }
    }else{
      blob_zero(&out);
      text_diff(pFile1, &file2, &out, pCfg);
      if( blob_size(&out) ){
        if( pCfg->diffFlags & DIFF_NUMSTAT ){
          if( !(pCfg->diffFlags & DIFF_BRIEF) ){
            blob_appendf(pOut, "%s %s\n", blob_str(&out), zName);
          }
        }else{
          diff_print_filenames(zName, zName2, pCfg, pOut);
          blob_appendf(pOut, "%s\n", blob_str(&out));
        }
      }
      blob_reset(&out);
    }
682
683
684
685
686
687
688
689





690
691
692
693
694
695
696
      blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
    }else{
      blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
      blob_append_escaped_arg(&cmd, zFile2, 1);
    }

    /* Run the external diff command */
    fossil_system(blob_str(&cmd));






    /* Delete the temporary file and clean up memory used */
    if( useTempfile ) file_delete(blob_str(&nameFile1));
    blob_reset(&nameFile1);
    blob_reset(&cmd);
  }
}







|
>
>
>
>
>







684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
      blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
    }else{
      blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
      blob_append_escaped_arg(&cmd, zFile2, 1);
    }

    /* Run the external diff command */
    if( fossil_system(blob_str(&cmd)) ){
#if !defined(_WIN32)
      /* On Windows, exit codes are unreliable. */     
      fossil_warning("External diff command failed: %b\n", &cmd);
#endif                     
    }

    /* Delete the temporary file and clean up memory used */
    if( useTempfile ) file_delete(blob_str(&nameFile1));
    blob_reset(&nameFile1);
    blob_reset(&cmd);
  }
}
710
711
712
713
714
715
716
717


718
719
720
721
722
723

724

725
726
727
728
729
730
731
*/
void diff_file_mem(
  Blob *pFile1,             /* In memory content to compare from */
  Blob *pFile2,             /* In memory content to compare to */
  const char *zName,        /* Display name of the file */
  DiffConfig *pCfg          /* Diff flags */
){
  if( pCfg->diffFlags & DIFF_BRIEF ) return;


  if( pCfg->zDiffCmd==0 ){
    Blob out;      /* Diff output text */

    blob_zero(&out);
    text_diff(pFile1, pFile2, &out, pCfg);
    if( pCfg->diffFlags & DIFF_NUMSTAT ){

      fossil_print("%s %s\n", blob_str(&out), zName);

    }else{
      diff_print_filenames(zName, zName, pCfg, 0);
      fossil_print("%s\n", blob_str(&out));
    }

    /* Release memory resources */
    blob_reset(&out);







|
>
>






>
|
>







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
*/
void diff_file_mem(
  Blob *pFile1,             /* In memory content to compare from */
  Blob *pFile2,             /* In memory content to compare to */
  const char *zName,        /* Display name of the file */
  DiffConfig *pCfg          /* Diff flags */
){
  if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
    return;
  }
  if( pCfg->zDiffCmd==0 ){
    Blob out;      /* Diff output text */

    blob_zero(&out);
    text_diff(pFile1, pFile2, &out, pCfg);
    if( pCfg->diffFlags & DIFF_NUMSTAT ){
      if( !(pCfg->diffFlags & DIFF_BRIEF) ){
        fossil_print("%s %s\n", blob_str(&out), zName);
      }
    }else{
      diff_print_filenames(zName, zName, pCfg, 0);
      fossil_print("%s\n", blob_str(&out));
    }

    /* Release memory resources */
    blob_reset(&out);
1007
1008
1009
1010
1011
1012
1013
1014


1015
1016
1017
1018
1019
1020
1021
  if( pFrom ){
    zName = pFrom->zName;
  }else if( pTo ){
    zName = pTo->zName;
  }else{
    zName = DIFF_NO_NAME;
  }
  if( pCfg->diffFlags & DIFF_BRIEF ) return;


  diff_print_index(zName, pCfg, 0);
  if( pFrom ){
    rid = uuid_to_rid(pFrom->zUuid, 0);
    content_get(rid, &f1);
  }else{
    blob_zero(&f1);
  }







|
>
>







1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
  if( pFrom ){
    zName = pFrom->zName;
  }else if( pTo ){
    zName = pTo->zName;
  }else{
    zName = DIFF_NO_NAME;
  }
  if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
    return;
  }
  diff_print_index(zName, pCfg, 0);
  if( pFrom ){
    rid = uuid_to_rid(pFrom->zUuid, 0);
    content_get(rid, &f1);
  }else{
    blob_zero(&f1);
  }
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
    }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
      /* No changes */
      (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */
      pFromFile = manifest_file_next(pFrom,0);
      pToFile = manifest_file_next(pTo,0);
    }else{
      if( file_dir_match(pFileDir, pToFile->zName) ){
        if( pCfg->diffFlags & DIFF_BRIEF ){
          fossil_print("CHANGED %s\n", pFromFile->zName);
        }else{
          diff_manifest_entry(pFromFile, pToFile, pCfg);
        }
      }
      pFromFile = manifest_file_next(pFrom,0);
      pToFile = manifest_file_next(pTo,0);







|







1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
    }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
      /* No changes */
      (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */
      pFromFile = manifest_file_next(pFrom,0);
      pToFile = manifest_file_next(pTo,0);
    }else{
      if( file_dir_match(pFileDir, pToFile->zName) ){
        if((pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT)){
          fossil_print("CHANGED %s\n", pFromFile->zName);
        }else{
          diff_manifest_entry(pFromFile, pToFile, pCfg);
        }
      }
      pFromFile = manifest_file_next(pFrom,0);
      pToFile = manifest_file_next(pTo,0);
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


/*
** Return the name of the external diff command, or return NULL if
** no external diff command is defined.
*/
const char *diff_command_external(int guiDiff){
  const char *zDefault;

  const char *zName;

  if( guiDiff ){
#if defined(_WIN32)





    zDefault = "WinDiff.exe";
#else
    zDefault = 0;





#endif
    zName = "gdiff-command";



  }else{
    zDefault = 0;
    zName = "diff-command";
  }
  return db_get(zName, zDefault);
}

/*
** Show diff output in a Tcl/Tk window, in response to the --tk option
** to the diff command.
**
** If fossil has direct access to a Tcl interpreter (either loaded







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

|







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


/*
** Return the name of the external diff command, or return NULL if
** no external diff command is defined.
*/
const char *diff_command_external(int guiDiff){
  const char *zName;
  zName = guiDiff ? "gdiff-command" : "diff-command";
  return db_get(zName, 0);
}


/*
** Return true if it reasonable to run "diff -tk" for "gdiff".
**
** Details: Return true if all of the following are true:
**
**     (1)   The isGDiff flags is true


**     (2)   The "gdiff-command" setting is undefined
**     (3)   There is a "tclsh" on PATH
**     (4)   There is a "wish" on PATH
*/
int gdiff_using_tk(int isGdiff){
  if( isGdiff
   && db_get("gdiff-command","")[0]==0
   && fossil_app_on_path("tclsh",0)
   && fossil_app_on_path("wish",0)
  ){
    return 1;


  }
  return 0;
}

/*
** Show diff output in a Tcl/Tk window, in response to the --tk option
** to the diff command.
**
** If fossil has direct access to a Tcl interpreter (either loaded
1207
1208
1209
1210
1211
1212
1213

1214
1215
1216
1217
1218
1219
1220
  int i;
  Blob script;
  const char *zTempFile = 0;
  char *zCmd;
  const char *zTclsh;
  int bDebug = find_option("tkdebug",0,0)!=0;
  int bDarkMode = find_option("dark",0,0)!=0;

  blob_zero(&script);
  /* Caution:  When this routine is called from the merge-info command,
  ** the --tcl argument requires an argument.  But merge-info does not
  ** use -i, so we can take -i as that argument.  This routine needs to
  ** always have -i after --tcl.
  **                                                CAUTION!
  **                                                vvvvvvv            */







>







1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
  int i;
  Blob script;
  const char *zTempFile = 0;
  char *zCmd;
  const char *zTclsh;
  int bDebug = find_option("tkdebug",0,0)!=0;
  int bDarkMode = find_option("dark",0,0)!=0;
  (void)find_option("debug",0,0);
  blob_zero(&script);
  /* Caution:  When this routine is called from the merge-info command,
  ** the --tcl argument requires an argument.  But merge-info does not
  ** use -i, so we can take -i as that argument.  This routine needs to
  ** always have -i after --tcl.
  **                                                CAUTION!
  **                                                vvvvvvv            */
1288
1289
1290
1291
1292
1293
1294
1295

1296
1297
1298
1299
1300
1301
1302
** COMMAND: gdiff
**
** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...?
**
** Show the difference between the current version of each of the FILEs
** specified (as they exist on disk) and that same file as it was checked-
** out.  Or if the FILE arguments are omitted, show all unsaved changes
** currently in the working check-out.

**
** The default output format is a "unified patch" (the same as the
** output of "diff -u" on most unix systems).  Many alternative formats
** are available.  A few of the more useful alternatives:
**
**    --tk              Pop up a Tcl/Tk-based GUI to show the diff
**    --by              Show a side-by-side diff in the default web browser







|
>







1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
** COMMAND: gdiff
**
** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...?
**
** Show the difference between the current version of each of the FILEs
** specified (as they exist on disk) and that same file as it was checked-
** out.  Or if the FILE arguments are omitted, show all unsaved changes
** currently in the working check-out.  The "gdiff" variant means to
** to use a GUI diff.
**
** The default output format is a "unified patch" (the same as the
** output of "diff -u" on most unix systems).  Many alternative formats
** are available.  A few of the more useful alternatives:
**
**    --tk              Pop up a Tcl/Tk-based GUI to show the diff
**    --by              Show a side-by-side diff in the default web browser
1354
1355
1356
1357
1358
1359
1360
1361


1362
1363
1364
1365
1366
1367
1368
**                               that directory as the baseline.
**   -w|--ignore-all-space       Ignore white space when comparing lines
**   -i|--internal               Use internal diff logic
**   --invert                    Invert the diff
**   --json                      Output formatted as JSON
**   -n|--linenum                Show line numbers
**   -N|--new-file               Alias for --verbose
**   --numstat                   Show only the number of added and deleted lines


**   -y|--side-by-side           Side-by-side diff
**   --strip-trailing-cr         Strip trailing CR
**   --tcl                       Tcl-formatted output used internally by --tk
**   --tclsh PATH                Tcl/Tk shell used for --tk (default: "tclsh")
**   --tk                        Launch a Tcl/Tk GUI for display
**   --to VERSION                Select VERSION as target for the diff
**   --undo                      Use the undo buffer as the baseline







|
>
>







1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
**                               that directory as the baseline.
**   -w|--ignore-all-space       Ignore white space when comparing lines
**   -i|--internal               Use internal diff logic
**   --invert                    Invert the diff
**   --json                      Output formatted as JSON
**   -n|--linenum                Show line numbers
**   -N|--new-file               Alias for --verbose
**   --numstat                   Show the number of added and deleted lines per
**                               file, omitting the diff. When combined with
**                                 --brief, show only the total row.
**   -y|--side-by-side           Side-by-side diff
**   --strip-trailing-cr         Strip trailing CR
**   --tcl                       Tcl-formatted output used internally by --tk
**   --tclsh PATH                Tcl/Tk shell used for --tk (default: "tclsh")
**   --tk                        Launch a Tcl/Tk GUI for display
**   --to VERSION                Select VERSION as target for the diff
**   --undo                      Use the undo buffer as the baseline
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
  const char *zCheckin;      /* Check-in version number */
  const char *zBranch;       /* Branch to diff */
  int againstUndo = 0;       /* Diff against files in the undo buffer */
  FileDirList *pFileDir = 0; /* Restrict the diff to these files */
  DiffConfig DCfg;           /* Diff configuration object */
  int bFromIsDir = 0;        /* True if zFrom is a directory name */


  if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
    diff_tk("diff", 2);
    return;
  }
  isGDiff = g.argv[1][0]=='g';
  zFrom = find_option("from", "r", 1);
  zTo = find_option("to", 0, 1);
  zCheckin = find_option("checkin", "ci", 1);
  zBranch = find_option("branch", 0, 1);
  againstUndo = find_option("undo",0,0)!=0;
  if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
    fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
                 " or --branch");
  }
  if( zBranch ){
    if( zTo || zFrom || zCheckin ){
      fossil_fatal("cannot use --from, --to, or --checkin with --branch");
    }
    zTo = zBranch;
    zFrom = mprintf("root:%s", zBranch);

  }
  if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
    fossil_fatal("cannot use --checkin together with --from or --to");
  }
  if( 0==zCheckin ){
    if( zTo==0 || againstUndo ){
      db_must_be_within_tree();
    }else if( zFrom==0 ){
      fossil_fatal("must use --from if --to is present");
    }else{
      db_find_and_open_repository(0, 0);
    }
  }else{
    db_find_and_open_repository(0, 0);









  }
  determine_exec_relative_option(1);
  if( zFrom!=file_tail(zFrom)
   && file_isdir(zFrom, ExtFILE)==1
   && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
  ){
    bFromIsDir = 1;







>
|



<















>














>
>
>
>
>
>
>
>
>







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
1456
1457
1458
1459
1460
1461
1462
1463
  const char *zCheckin;      /* Check-in version number */
  const char *zBranch;       /* Branch to diff */
  int againstUndo = 0;       /* Diff against files in the undo buffer */
  FileDirList *pFileDir = 0; /* Restrict the diff to these files */
  DiffConfig DCfg;           /* Diff configuration object */
  int bFromIsDir = 0;        /* True if zFrom is a directory name */

  isGDiff = g.argv[1][0]=='g';
  if( find_option("tk",0,0)!=0|| has_option("tclsh") ){
    diff_tk("diff", 2);
    return;
  }

  zFrom = find_option("from", "r", 1);
  zTo = find_option("to", 0, 1);
  zCheckin = find_option("checkin", "ci", 1);
  zBranch = find_option("branch", 0, 1);
  againstUndo = find_option("undo",0,0)!=0;
  if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
    fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
                 " or --branch");
  }
  if( zBranch ){
    if( zTo || zFrom || zCheckin ){
      fossil_fatal("cannot use --from, --to, or --checkin with --branch");
    }
    zTo = zBranch;
    zFrom = mprintf("root:%s", zBranch);
    zBranch = 0;
  }
  if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
    fossil_fatal("cannot use --checkin together with --from or --to");
  }
  if( 0==zCheckin ){
    if( zTo==0 || againstUndo ){
      db_must_be_within_tree();
    }else if( zFrom==0 ){
      fossil_fatal("must use --from if --to is present");
    }else{
      db_find_and_open_repository(0, 0);
    }
  }else{
    db_find_and_open_repository(0, 0);
  }
  if( gdiff_using_tk(isGDiff) ){
    restore_option("--from", zFrom, 1);
    restore_option("--to", zTo, 1);
    restore_option("--checkin", zCheckin, 1);
    restore_option("--branch", zBranch, 1);
    if( againstUndo ) restore_option("--undo", 0, 0);
    diff_tk("diff", 2);
    return;
  }
  determine_exec_relative_option(1);
  if( zFrom!=file_tail(zFrom)
   && file_isdir(zFrom, ExtFILE)==1
   && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
  ){
    bFromIsDir = 1;
1446
1447
1448
1449
1450
1451
1452



1453
1454
1455
1456
1457
1458
1459
1460
        break;
      }
      pFileDir[i-2].nName = blob_size(&fname);
      pFileDir[i-2].nUsed = 0;
      blob_reset(&fname);
    }
  }



  if ( zCheckin!=0 ){
    int ridTo = name_to_typed_rid(zCheckin, "ci");
    zTo = zCheckin;
    zFrom = db_text(0,
      "SELECT uuid FROM blob, plink"
      " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid",
      ridTo);
    if( zFrom==0 ){







>
>
>
|







1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
        break;
      }
      pFileDir[i-2].nName = blob_size(&fname);
      pFileDir[i-2].nUsed = 0;
      blob_reset(&fname);
    }
  }
  if( DCfg.diffFlags & DIFF_NUMSTAT ){
    fossil_print("%10s %10s\n", "INSERTED", "DELETED");
  }
  if( zCheckin!=0 ){
    int ridTo = name_to_typed_rid(zCheckin, "ci");
    zTo = zCheckin;
    zFrom = db_text(0,
      "SELECT uuid FROM blob, plink"
      " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid",
      ridTo);
    if( zFrom==0 ){
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
      }
      fossil_free(pFileDir[i].zName);
    }
    fossil_free(pFileDir);
  }
  diff_end(&DCfg, 0);
  if ( DCfg.diffFlags & DIFF_NUMSTAT ){
    fossil_print("%10d %10d TOTAL over %d changed files\n",
                 g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]);
  }
}

/*
** WEBPAGE: vpatch
** URL: /vpatch?from=FROM&to=TO
**







|
|







1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
      }
      fossil_free(pFileDir[i].zName);
    }
    fossil_free(pFileDir);
  }
  diff_end(&DCfg, 0);
  if ( DCfg.diffFlags & DIFF_NUMSTAT ){
    fossil_print("%10d %10d TOTAL over %d changed file%s\n",
           g.diffCnt[1], g.diffCnt[2], g.diffCnt[0], g.diffCnt[0]!=1 ? "s": "");
  }
}

/*
** WEBPAGE: vpatch
** URL: /vpatch?from=FROM&to=TO
**
Changes to src/dispatch.c.
52
53
54
55
56
57
58

59
60
61
62
63
64
65
#define CMDFLAG_BOOLEAN      0x0100     /* A boolean setting */
#define CMDFLAG_RAWCONTENT   0x0200     /* Do not interpret POST content */
/* NOTE:                     0x0400 = CMDFLAG_SENSITIVE in mkindex.c! */
#define CMDFLAG_HIDDEN       0x0800     /* Elide from most listings */
#define CMDFLAG_LDAVG_EXEMPT 0x1000     /* Exempt from load_control() */
#define CMDFLAG_ALIAS        0x2000     /* Command aliases */
#define CMDFLAG_KEEPEMPTY    0x4000     /* Do not unset empty settings */

/**************************************************************************/

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

#endif /* INTERFACE */







>







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#define CMDFLAG_BOOLEAN      0x0100     /* A boolean setting */
#define CMDFLAG_RAWCONTENT   0x0200     /* Do not interpret POST content */
/* NOTE:                     0x0400 = CMDFLAG_SENSITIVE in mkindex.c! */
#define CMDFLAG_HIDDEN       0x0800     /* Elide from most listings */
#define CMDFLAG_LDAVG_EXEMPT 0x1000     /* Exempt from load_control() */
#define CMDFLAG_ALIAS        0x2000     /* Command aliases */
#define CMDFLAG_KEEPEMPTY    0x4000     /* Do not unset empty settings */
#define CMDFLAG_ABBREVSUBCMD 0x8000     /* Help text abbreviates subcommands */
/**************************************************************************/

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

#endif /* INTERFACE */
520
521
522
523
524
525
526
527
528
529








530
531
532
533
534
535
536
    blob_appendf(pHtml, "%s\n", azEnd[iLevel--]);
  }
}

/*
** Format help text for TTY display.
*/
static void help_to_text(const char *zHelp, Blob *pText){
  int i, x;
  char c;








  for(i=0; (c = zHelp[i])!=0; i++){
    if( c=='%' && strncmp(zHelp+i,"%fossil",7)==0 ){
      if( i>0 ) blob_append(pText, zHelp, i);
      blob_append(pText, "fossil", 6);
      zHelp += i+7;
      i = -1;
      continue;







|


>
>
>
>
>
>
>
>







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
    blob_appendf(pHtml, "%s\n", azEnd[iLevel--]);
  }
}

/*
** Format help text for TTY display.
*/
static void help_to_text(const char *zHelp, Blob *pText, int bUsage){
  int i, x;
  char c;
  if( zHelp[0]=='>' ){
    if( !bUsage ){
      blob_appendf(pText, "Usage:");
    }else{
      blob_append_char(pText, ' ');
    }
    zHelp++;
  }
  for(i=0; (c = zHelp[i])!=0; i++){
    if( c=='%' && strncmp(zHelp+i,"%fossil",7)==0 ){
      if( i>0 ) blob_append(pText, zHelp, i);
      blob_append(pText, "fossil", 6);
      zHelp += i+7;
      i = -1;
      continue;
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
      }else if( rawOut ){
        for(j=0; j<occHelp[aCommand[i].iHelp]; j++)
          fossil_print("# %s\n", aCommand[bktHelp[aCommand[i].iHelp][j]].zName);
        fossil_print("%s\n\n", aCommand[i].zHelp);
      }else{
          Blob txt;
          blob_init(&txt, 0, 0);
          help_to_text(aCommand[i].zHelp, &txt);
          for(j=0; j<occHelp[aCommand[i].iHelp]; j++){
            fossil_print("# %s%s\n",
              aCommand[bktHelp[aCommand[i].iHelp][j]].zName,
              (aCommand[i].eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ?
              " (versionable)" : "");
          }
          fossil_print("%s\n\n", blob_str(&txt));







|







610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
      }else if( rawOut ){
        for(j=0; j<occHelp[aCommand[i].iHelp]; j++)
          fossil_print("# %s\n", aCommand[bktHelp[aCommand[i].iHelp][j]].zName);
        fossil_print("%s\n\n", aCommand[i].zHelp);
      }else{
          Blob txt;
          blob_init(&txt, 0, 0);
          help_to_text(aCommand[i].zHelp, &txt, 0);
          for(j=0; j<occHelp[aCommand[i].iHelp]; j++){
            fossil_print("# %s%s\n",
              aCommand[bktHelp[aCommand[i].iHelp][j]].zName,
              (aCommand[i].eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ?
              " (versionable)" : "");
          }
          fossil_print("%s\n\n", blob_str(&txt));
800
801
802
803
804
805
806






















807
808
809
810
811
812
813
    n = dispatch_approx_match(g.argv[i], 20, az);
    for(j=0; j<n; j++){
      fossil_print("   %s\n", az[j]);
    }
  }
}























/*
** 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 or a setting.
** Query parameters:







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







809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
    n = dispatch_approx_match(g.argv[i], 20, az);
    for(j=0; j<n; j++){
      fossil_print("   %s\n", az[j]);
    }
  }
}


/*
** Returns 1 if the command or page name zName is known to be a
** command/page which is only available in certain builds/platforms,
** else returns 0.
*/
static int help_is_platform_command(const char *zName){
  const char *aList[] = {
    /* List of commands/pages which are known to only be available in
    ** certain builds/platforms. */
    "winsrv",
    "json", "/json",
    NULL /* end-of-list sentinel */
  };
  int i = 0;
  const char *z;
  for( z = aList[0]; z ; z = aList[++i] ){
    if( 0==fossil_strcmp(zName, z) ) return 1;
  }
  return 0;
}

/*
** 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 or a setting.
** Query parameters:
827
828
829
830
831
832
833
834


835
836
837







838
839
840
841
842
843
844
845
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
872
873
874
875
876
877

878
879
880
881
882
883
884
  if( zCmd==0 ) zCmd = P("name");
  cgi_check_for_malice();
  if( zCmd && *zCmd ){
    int rc;
    const CmdOrPage *pCmd = 0;

    style_set_current_feature("tkt");
    style_header("Help: %s", zCmd);



    style_submenu_element("Command-List", "%R/help");
    rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd);







    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 "%h(zCmd)" page:</h1>
    }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){
      @ <h1>The "%h(pCmd->zName)" setting:</h1>
    }else{
      @ <h1>The "%h(zCmd)" command:</h1>
    }
    if( rc==1 ){



      @ unknown command: %h(zCmd)

    }else if( rc==2 ){
      @ ambiguous command prefix: %h(zCmd)
    }else{
      if( pCmd->zHelp[0]==0 ){
        @ No help available for "%h(pCmd->zName)"
      }else if( P("plaintext") ){
        Blob txt;
        blob_init(&txt, 0, 0);
        help_to_text(pCmd->zHelp, &txt);
        @ <pre class="helpPage">
        @ %h(blob_str(&txt))
        @ </pre>
        blob_reset(&txt);
      }else if( P("raw") ){
        @ <pre class="helpPage">
        @ %h(pCmd->zHelp)
        @ </pre>
      }else{
        @ <div class="helpPage">
        help_to_html(pCmd->zHelp, cgi_output_blob());
        @ </div>
      }
    }
  }else{
    int i;
    const char *zWidth = "28ex";
    unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0};   /* Help str occurrences */
    int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */
    style_header("Help");


    @ <a name='commands'></a>
    @ <h1>Available commands:</h1>
    @ <div class="columns" style="column-width: %s(zWidth);">
    @ <ul>
    /* Fill in help string buckets */
    for(i=0; i<MX_COMMAND; i++){







|
>
>
|
<

>
>
>
>
>
>
>
|


|



|

|
>
>
>
|
>

|






|




















>







858
859
860
861
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
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
  if( zCmd==0 ) zCmd = P("name");
  cgi_check_for_malice();
  if( zCmd && *zCmd ){
    int rc;
    const CmdOrPage *pCmd = 0;

    style_set_current_feature("tkt");
    style_submenu_element("Topic-List", "%R/help");
    if( search_restrict(SRCH_HELP)!=0 ){
      style_submenu_element("Search","%R/search?y=h");
    }

    rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd);
    if( pCmd ){
      style_header("Help: %s", pCmd->zName);
    }else{
      style_header("Help");
    }
    if( pCmd==0 ){
      /* No <h1> line in this case */
    }else 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 "%h(pCmd->zName)" page:</h1>
    }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){
      @ <h1>The "%h(pCmd->zName)" setting:</h1>
    }else{
      @ <h1>The "%h(pCmd->zName)" command:</h1>
    }
    if( rc==1 || (rc==2 && zCmd[0]=='/') ){
      if( zCmd && help_is_platform_command(zCmd) ){
        @ Not available in this build: "%h(zCmd)"
      }else{
        @ Unknown topic: "%h(zCmd)"
      }
    }else if( rc==2 ){
      @ Ambiguous prefix: "%h(zCmd)"
    }else{
      if( pCmd->zHelp[0]==0 ){
        @ No help available for "%h(pCmd->zName)"
      }else if( P("plaintext") ){
        Blob txt;
        blob_init(&txt, 0, 0);
        help_to_text(pCmd->zHelp, &txt, 0);
        @ <pre class="helpPage">
        @ %h(blob_str(&txt))
        @ </pre>
        blob_reset(&txt);
      }else if( P("raw") ){
        @ <pre class="helpPage">
        @ %h(pCmd->zHelp)
        @ </pre>
      }else{
        @ <div class="helpPage">
        help_to_html(pCmd->zHelp, cgi_output_blob());
        @ </div>
      }
    }
  }else{
    int i;
    const char *zWidth = "28ex";
    unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0};   /* Help str occurrences */
    int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */
    style_header("Help");
    search_screen(SRCH_HELP, 0x02);

    @ <a name='commands'></a>
    @ <h1>Available commands:</h1>
    @ <div class="columns" style="column-width: %s(zWidth);">
    @ <ul>
    /* Fill in help string buckets */
    for(i=0; i<MX_COMMAND; i++){
1053
1054
1055
1056
1057
1058
1059



























































































































































































































1060
1061
1062
1063
1064
1065
1066
      occHelp[aCommand[i].iHelp] = 0;
    }
  }
  @ </dl>
  blob_reset(&buf);
  style_finish_page();
}




























































































































































































































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++){







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







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
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
      occHelp[aCommand[i].iHelp] = 0;
    }
  }
  @ </dl>
  blob_reset(&buf);
  style_finish_page();
}

/*
** Analyze p and return one of three values:
**
**     0     p is the continuation of a prior subcommand.
**
**     1     p is text past the end of a prior subcommand.
**
**     2     p is the start of a new subcommand.
*/
static int is_subcommand(Blob *p, int bAbbrevSubcmd){
  int i, sz;
  const unsigned char *z = (const unsigned char*)blob_buffer(p);
  sz = blob_size(p);
  if( sz>6 ) sz = 6;
  for(i=0; i<sz && fossil_isspace(z[i]); i++){}
  if( i>=sz ) return 0;

  if( bAbbrevSubcmd==0 ){
    if( i>1 ) return 0;
    return z[0]=='>' ? 2 : 1;
  }else{
    return (i==3 && fossil_isalpha(z[3])) ? 2 : 1;
  }
}

/*
** Input z[] is help text for zTopic.  If zTopic has sub-command zSub,
** then cut out all portions of the original help text that do not
** directly pertain to zSub and write the zSub-relevant parts into
** pOut.
**
** Return the number of lines of z[] written into pOut.  A return of
** zero means no simplification occurred.
*/
static int simplify_to_subtopic(
  const char *z,            /* Full original help text */
  Blob *pOut,               /* Write simplified help text here */
  const char *zTopic,       /* TOPIC */
  const char *zSubtopic,    /* SUBTOPIC */
  int bAbbrevSubcmd         /* True if z[] contains abbreviated subcommands */
){
  Blob in, line; //, subsection;
  int n = 0;
  char *zQTop = re_quote(zTopic);
  char *zQSub = re_quote(zSubtopic);
  char *zPattern;
  ReCompiled *pRe = 0;

  if( bAbbrevSubcmd ){
    zPattern = mprintf("   ([a-z]+ ?\\| ?)*%s\\b", zQSub);
  }else{
    zPattern = mprintf(">  ?fossil [-a-z]+ .*\\b%s\\b", zQSub);
  }
  fossil_free(zQTop);
  fossil_free(zQSub);
  re_compile(&pRe, zPattern, 0);
  fossil_free(zPattern);
  blob_init(&in, z, -1);
  while( blob_line(&in, &line) ){
    if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_size(&line)) ){
      int atStart = 1;
      blob_appendb(pOut, &line);
      n++;
      while( blob_line(&in, &line) ){
        if( re_match(pRe,(unsigned char*)blob_buffer(&line),blob_size(&line)) ){
          blob_appendb(pOut, &line);
          n++;
          atStart = 1;
        }else{
          int x = is_subcommand(&line,bAbbrevSubcmd);
          if( x==2 ){
            if( atStart ){
              blob_appendb(pOut, &line);
              n++;
            }else{
              break;
            }
          }else if( x==1 ){
            break;
          }else{
            blob_appendb(pOut, &line);
            n++;
            atStart = 0;
          }
        }
      }
    }
  }
  blob_reset(&line);
  re_free(pRe);
  if( n ){
    blob_trim(pOut);
    blob_reset(&in);
  }
  return n;
}

/*
** Input p is a "Usage:" line or a subcommand line.  Simplify this line
** for the --usage option and write it into pOut.
*/
static void simplify_usage_line(
  Blob *p,
  Blob *pOut,
  int bAbbrevSubcmd,
  const char *zCmd
){
  const char *z = blob_buffer(p);
  int sz = blob_size(p);
  int i = 0;
  if( sz>6 && z[0]=='U' ){
    for(i=1; i<sz && !fossil_isspace(z[i]); i++){}
  }else if( sz>0 && z[0]=='>' ){
    i = 1;
  }else if( sz>4 && bAbbrevSubcmd
         && memcmp(z,"   ",3)==0 && !fossil_isspace(z[3]) ){
    int j;
    for(j=3; j<sz-1 && (z[j]!=' ' || z[j+1]!=' '); j++){}
    blob_appendf(pOut, "fossil %s %.*s\n", zCmd, j-3, &z[3]);
    return;
  }else{
    while( i<sz && fossil_isspace(z[i]) ) i++;
    if( i+2<sz && (z[i]=='o' || z[i]=='O') && z[i+1]=='r' ){
      while( i<sz && !fossil_isspace(z[i]) ) i++;
    }
  }
  while( i<sz && fossil_isspace(z[i]) ) i++;
  blob_append(pOut, &z[i], sz-i);
}

/*
** Input z[] is help text for a command zTopic.  Write into pOut all lines of
** z[] that show the command-line syntax for that command.  Lines written
** to pOut are lines that begin with out of:
**
**     Usage:
**     or:
**     > fossil TOPIC
**
** Return the number of lines written into pOut.
*/
static int simplify_to_usage(
  const char *z,            /* Full original help text */
  Blob *pOut,               /* Write simplified help text here */
  const char *zTopic,       /* The command for which z[] is full help text */
  int bAbbrevSubcmd         /* z[] uses abbreviated subcommands */
){
  ReCompiled *pRe = 0;
  Blob in, line;
  int n = 0;

  if( bAbbrevSubcmd ){
    re_compile(&pRe, "^(Usage: |   [a-z][-a-z|]+ .*)", 0);
  }else{
    re_compile(&pRe, "^(Usage: | *[Oo]r: +%fossi |>  ?fossil )", 0);
  }
  blob_init(&in, z, -1);
  while( blob_line(&in, &line) ){
    if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_strlen(&line)) ){
      simplify_usage_line(&line, pOut, bAbbrevSubcmd, zTopic);
      n++;
    }
  }
  re_free(pRe);
  if( n ) blob_trim(pOut);
  return n;
}

/*
** Input z[] is help text.  Write into pOut all lines of z[] that show
** command-line options.  Return the number of lines written.
*/
static int simplify_to_options(
  const char *z,            /* Full original help text */
  Blob *pOut,               /* Write simplified help text here */
  int bAbbrevSubcmd,        /* z[] uses abbreviated subcommands */
  const char *zCmd          /* Name of the command that z[] describes */
){
  ReCompiled *pRe = 0;
  Blob txt, line, subsection;
  int n = 0;
  int bSubsectionSeen = 0;

  blob_init(&txt, z, -1);
  blob_init(&subsection, 0, 0);
  re_compile(&pRe, "^ +-.*  ", 0);
  while( blob_line(&txt, &line) ){
    int len = blob_size(&line);
    unsigned char *zLine = (unsigned char *)blob_buffer(&line);
    if( re_match(pRe, zLine, len) ){
      if( blob_size(&subsection) ){
        simplify_usage_line(&subsection, pOut, bAbbrevSubcmd, zCmd);
        blob_reset(&subsection);
      }
      blob_appendb(pOut, &line);
    }else if( len>7 && !fossil_isspace(zLine[0]) && bSubsectionSeen 
           && sqlite3_strlike("%options:%",blob_str(&line),0)==0 ){
      subsection = line;
    }else if( !bAbbrevSubcmd && len>9
           && (memcmp(zLine,"> fossil ",9)==0 
               || memcmp(zLine,">  fossil",9)==0) ){
      subsection = line;
      bSubsectionSeen = 1;
    }else if( bAbbrevSubcmd && len>5 && memcmp(zLine,"   ",3)==0
           && fossil_isalpha(zLine[3]) ){
      subsection = line;
      bSubsectionSeen = 1;
    }else if( len>1 && !fossil_isspace(zLine[0]) && bSubsectionSeen ){
      blob_reset(&subsection);
    }
  }
  re_free(pRe);
  blob_trim(pOut);
  blob_reset(&subsection);
  return n;
}



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++){
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
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
static const char zOptions[] =
@ Command-line options common to all commands:
@
@   --args FILENAME         Read additional arguments and options from FILENAME
@   --case-sensitive BOOL   Set case sensitivity for file names
@   --cgitrace              Active CGI tracing
@   --chdir PATH            Change to PATH before performing any operations
@   --comfmtflags VALUE     Set comment formatting flags to VALUE
@   --comment-format VALUE  Alias for --comfmtflags
@   --errorlog FILENAME     Log errors to FILENAME
@   --help                  Show help on the command rather than running it
@   --httptrace             Trace outbound HTTP requests
@   --localtime             Display times using the local timezone
@   --nocgi                 Do not act as CGI
@   --no-th-hook            Do not run TH1 hooks
@   --quiet                 Reduce the amount of output
@   --sqlstats              Show SQL usage statistics when done
@   --sqltrace              Trace all SQL commands
@   --sshtrace              Trace SSH activity
@   --ssl-identity NAME     Set the SSL identity to NAME
@   --systemtrace           Trace calls to system()
@   -U|--user USER          Make the default user be USER
@   --utc                   Display times using UTC
@   --vfs NAME              Cause SQLite to use the NAME VFS
;

/*
** COMMAND: help
**
** Usage: %fossil help [OPTIONS] [TOPIC]
**
** Display information on how to use TOPIC, which may be a command, webpage, or
** setting.  Webpage names begin with "/".  If TOPIC is omitted, a list of
** topics is returned.


**
** The following options can be used when TOPIC is omitted:
**
**    -a|--all          List both common and auxiliary commands




**    -o|--options      List command-line options common to all commands
**    -s|--setting      List setting names
**    -t|--test         List unsupported "test" commands
**    -v|--verbose      List both names and help text
**    -x|--aux          List only auxiliary commands
**    -w|--www          List all web pages
**    -f|--full         List full set of commands (including auxiliary
**                      and unsupported "test" commands), options,
**                      settings, and web pages
**    -e|--everything   List all help on all topics
**
** These options can be used when TOPIC is present:
**

**    -h|--html         Format output as HTML rather than plain text
**    -c|--commands     Restrict TOPIC search to commands




*/
void help_cmd(void){
  int rc;
  int mask = CMDFLAG_ANY;
  int isPage = 0;
  int verboseFlag = 0;
  int commandsFlag = 0;
  const char *z;
  const char *zCmdOrPage;
  const CmdOrPage *pCmd = 0;
  int useHtml = 0;



  const char *zTopic;


  Blob txt;


  verboseFlag = find_option("verbose","v",0)!=0;
  commandsFlag = find_option("commands","c",0)!=0;
  useHtml = find_option("html","h",0)!=0;

  if( find_option("options","o",0) ){
    fossil_print("%s", zOptions);
    return;
  }
  else if( find_option("all","a",0) ){
    command_list(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER, verboseFlag, useHtml);
    return;
  }
  else if( find_option("www","w",0) ){
    command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml);
    return;
  }







<
<




















|



|
>
>




>
>
>
>






<
<
<
<



>

|
>
>
>
>



|
|
|
|
|
|
|
|
>
>
>
|
>
>
|
>
>



>
|
|
<
<
|







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




1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469


1470
1471
1472
1473
1474
1475
1476
1477
static const char zOptions[] =
@ Command-line options common to all commands:
@
@   --args FILENAME         Read additional arguments and options from FILENAME
@   --case-sensitive BOOL   Set case sensitivity for file names
@   --cgitrace              Active CGI tracing
@   --chdir PATH            Change to PATH before performing any operations


@   --errorlog FILENAME     Log errors to FILENAME
@   --help                  Show help on the command rather than running it
@   --httptrace             Trace outbound HTTP requests
@   --localtime             Display times using the local timezone
@   --nocgi                 Do not act as CGI
@   --no-th-hook            Do not run TH1 hooks
@   --quiet                 Reduce the amount of output
@   --sqlstats              Show SQL usage statistics when done
@   --sqltrace              Trace all SQL commands
@   --sshtrace              Trace SSH activity
@   --ssl-identity NAME     Set the SSL identity to NAME
@   --systemtrace           Trace calls to system()
@   -U|--user USER          Make the default user be USER
@   --utc                   Display times using UTC
@   --vfs NAME              Cause SQLite to use the NAME VFS
;

/*
** COMMAND: help
**
** Usage: %fossil help [OPTIONS] [TOPIC] [SUBCOMMAND]
**
** Display information on how to use TOPIC, which may be a command, webpage, or
** setting.  Webpage names begin with "/".  If TOPIC is omitted, a list of
** topics is returned.  If there is an extra argument after TOPIC, it is
** the name of a subcommand, in which case only the help text for that one
** subcommand is shown.
**
** The following options can be used when TOPIC is omitted:
**
**    -a|--all          List both common and auxiliary commands
**    -e|--everything   List all help on all topics
**    -f|--full         List full set of commands (including auxiliary
**                      and unsupported "test" commands), options,
**                      settings, and web pages
**    -o|--options      List command-line options common to all commands
**    -s|--setting      List setting names
**    -t|--test         List unsupported "test" commands
**    -v|--verbose      List both names and help text
**    -x|--aux          List only auxiliary commands
**    -w|--www          List all web pages




**
** These options can be used when TOPIC is present:
**
**    -c|--commands     Restrict TOPIC search to commands
**    -h|--html         Format output as HTML rather than plain text
**    -o|--options      Show command-line options for TOPIC
**    --raw             Output raw, unformatted help text
**    -u|--usage        Show a succinct usage summary, not full help text
**
** See also:  [[usage]], [[options]], [[search]] with the -h option
*/
void help_cmd(void){
  int rc;
  int mask = CMDFLAG_ANY;        /* Mask of help topic types */
  int isPage = 0;                /* True if TOPIC is a page */
  int verboseFlag = 0;           /* -v option */
  int commandsFlag = 0;          /* -c option */
  const char *z;                 /* Original, untranslated help text */
  const char *zCmdOrPage;        /* "command" or "page" or "setting" */
  const CmdOrPage *pCmd = 0;     /* ptr to aCommand[] entry for TOPIC */
  int useHtml = 0;               /* -h option */
  int bUsage;                    /* --usage */
  int bRaw;                      /* --raw option */
  int bOptions;                  /* --options */
  const char *zTopic;            /* TOPIC argument */
  const char *zSubtopic = 0;     /* SUBTOPIC argument */
  Blob subtext1, subtext2, s3;   /* Subsets of z[] containing subtopic/usage */
  Blob txt;                      /* Text after rendering */
  int bAbbrevSubcmd = 0;         /* Help text uses abbreviated subcommands */

  verboseFlag = find_option("verbose","v",0)!=0;
  commandsFlag = find_option("commands","c",0)!=0;
  useHtml = find_option("html","h",0)!=0;
  bRaw = find_option("raw",0,0)!=0;
  bOptions = find_option("options","o",0)!=0;
  bUsage = find_option("usage","u",0)!=0;


  if( find_option("all","a",0) ){
    command_list(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER, verboseFlag, useHtml);
    return;
  }
  else if( find_option("www","w",0) ){
    command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml);
    return;
  }
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
    fossil_print("\n%s", zOptions);
    fossil_print("\nfossil settings:\n\n");
    command_list(CMDFLAG_SETTING, verboseFlag, useHtml);
    fossil_print("\nfossil web pages:\n\n");
    command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml);
    fossil_print("\nfossil test commands (unsupported):\n\n");
    command_list(CMDFLAG_TEST, verboseFlag, useHtml);

    fossil_print("\n");
    version_cmd();

    return;
  }
  else if( find_option("everything","e",0) ){
    display_all_help(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE |
                     CMDFLAG_SETTING | CMDFLAG_TEST, useHtml, 0);
    return;
  }
  verify_all_options();
  if( g.argc<3 ){




    z = g.argv[0];
    fossil_print(
      "Usage: %s help TOPIC\n"

      "Try \"%s help help\" or \"%s help -a\" for more options\n"



      "Frequently used commands:\n",
      z, z, z);
    command_list(CMDFLAG_1ST_TIER,verboseFlag,useHtml);
    if( !verboseFlag ) version_cmd();
    return;
  }
  zTopic = g.argv[2];

  isPage = ('/' == zTopic[0]) ? 1 : 0;
  if(isPage){
    zCmdOrPage = "page";
  }else if( commandsFlag ){
    mask = CMDFLAG_COMMAND;
    zCmdOrPage = "command";
  }else{
    zCmdOrPage = "command or setting";
  }
  rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd);
  if( rc ){
    int i, n;
    const char *az[5];
    if( rc==1 ){




      fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]);
    }else{
      fossil_print("ambiguous %s prefix: %s\n",
                 zCmdOrPage, g.argv[2]);
    }
    fossil_print("Did you mean one of these TOPICs:\n");
    n = dispatch_approx_match(g.argv[2], 5, az);
    for(i=0; i<n; i++){
      fossil_print("  *  %s\n", az[i]);
    }
    fossil_print("Also consider using:\n");
    fossil_print("   fossil help TOPIC     ;# show help on TOPIC\n");
    fossil_print("   fossil help -a        ;# show all commands\n");
    fossil_print("   fossil help -w        ;# show all web-pages\n");
    fossil_print("   fossil help -s        ;# show all settings\n");
    fossil_print("   fossil help -o        ;# show global options\n");
    fossil_exit(1);
  }

  z = pCmd->zHelp;
  if( z==0 ){
    fossil_fatal("no help available for the %s %s",
                 pCmd->zName, zCmdOrPage);
  }























  if( pCmd->eCmdFlags & CMDFLAG_SETTING ){
    const Setting *pSetting = db_find_setting(pCmd->zName, 0);
    char *zDflt = 0;
    if( pSetting!=0 && pSetting->def!=0 && *pSetting->def!=0 ){
      zDflt = mprintf(" (default: %s)", pSetting->def);
    }
    fossil_print("Setting: \"%s\"%s%s\n\n",
         pCmd->zName, zDflt!=0 ? zDflt : "",
         (pCmd->eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ? " (versionable)" : ""
    );
    fossil_free(zDflt);
  }
  blob_init(&txt, 0, 0);


  if( useHtml ){
    help_to_html(z, &txt);
  }else{
    help_to_text(z, &txt);
  }
  fossil_print("%s\n", blob_str(&txt));
  blob_reset(&txt);


}

/*
** Return a pointer to the setting information array.
**
** This routine provides access to the aSetting[] array which is created
** by the mkindex utility program and included with <page_index.h>.







>
|
|
>









>
>
>
>



>
|
>
>
>
|
|





>

|












>
>
>
>










|
|
|
|
|
|
|

>





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|












>
>
|


|

|

>
>







1495
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
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
    fossil_print("\n%s", zOptions);
    fossil_print("\nfossil settings:\n\n");
    command_list(CMDFLAG_SETTING, verboseFlag, useHtml);
    fossil_print("\nfossil web pages:\n\n");
    command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml);
    fossil_print("\nfossil test commands (unsupported):\n\n");
    command_list(CMDFLAG_TEST, verboseFlag, useHtml);
    if ( !verboseFlag ) {
      fossil_print("\n");
      version_cmd();
    }
    return;
  }
  else if( find_option("everything","e",0) ){
    display_all_help(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE |
                     CMDFLAG_SETTING | CMDFLAG_TEST, useHtml, 0);
    return;
  }
  verify_all_options();
  if( g.argc<3 ){
    if( bOptions ){
      fossil_print("%s", zOptions);
      return;
    }
    z = g.argv[0];
    fossil_print(
      "Usage: %s help TOPIC\n"
      "Things to try:\n\n"
      "   %s help help\n"
      "   %s help -o\n"
      "   %s help -a\n"
      "   %s search -h TOPIC\n\n"
      "Other common values for TOPIC:\n\n",
      z, z, z, z, z);
    command_list(CMDFLAG_1ST_TIER,verboseFlag,useHtml);
    if( !verboseFlag ) version_cmd();
    return;
  }
  zTopic = g.argv[2];
  zSubtopic = g.argc>=4 ? g.argv[3] : 0;
  isPage = ('/' == zTopic[0]) ? 1 : 0;
  if( isPage ){
    zCmdOrPage = "page";
  }else if( commandsFlag ){
    mask = CMDFLAG_COMMAND;
    zCmdOrPage = "command";
  }else{
    zCmdOrPage = "command or setting";
  }
  rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd);
  if( rc ){
    int i, n;
    const char *az[5];
    if( rc==1 ){
      if( help_is_platform_command(g.argv[2]) ){
        fossil_print("Not available in this build: %s\n", g.argv[2]);
        return;
      }
      fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]);
    }else{
      fossil_print("ambiguous %s prefix: %s\n",
                 zCmdOrPage, g.argv[2]);
    }
    fossil_print("Did you mean one of these TOPICs:\n");
    n = dispatch_approx_match(g.argv[2], 5, az);
    for(i=0; i<n; i++){
      fossil_print("  *  %s\n", az[i]);
    }
    fossil_print("Other commands to try:\n");
    fossil_print("   fossil search -h PATTERN  ;# search all help text\n");
    fossil_print("   fossil help -a            ;# show all commands\n");
    fossil_print("   fossil help -w            ;# show all web-pages\n");
    fossil_print("   fossil help -s            ;# show all settings\n");
    fossil_print("   fossil help -o            ;# show global options\n");
    return;
  }
  bAbbrevSubcmd = (pCmd->eCmdFlags & CMDFLAG_ABBREVSUBCMD)!=0;
  z = pCmd->zHelp;
  if( z==0 ){
    fossil_fatal("no help available for the %s %s",
                 pCmd->zName, zCmdOrPage);
  }
  blob_init(&subtext1, 0, 0);
  blob_init(&subtext2, 0, 0);
  blob_init(&s3, 0, 0);
  if( zSubtopic!=0 ){
    if( simplify_to_subtopic(z, &subtext1, zTopic, zSubtopic, bAbbrevSubcmd) ){
      z = blob_str(&subtext1);
    }else{
      fossil_print("No subtopic \"%s\" for \"%s\".\n", zSubtopic, zTopic);
      bUsage = 1;
      zSubtopic = 0;
    }
  }
  if( bUsage ){
    if( simplify_to_usage(z, &subtext2, zTopic, bAbbrevSubcmd) ){
      z = blob_str(&subtext2);
    }else{
      bUsage = 0;
    }
  }
  if( bOptions ){
    simplify_to_options(z, &s3, bAbbrevSubcmd, zTopic);
    z = blob_str(&s3);
  }
  if( pCmd && pCmd->eCmdFlags & CMDFLAG_SETTING ){
    const Setting *pSetting = db_find_setting(pCmd->zName, 0);
    char *zDflt = 0;
    if( pSetting!=0 && pSetting->def!=0 && *pSetting->def!=0 ){
      zDflt = mprintf(" (default: %s)", pSetting->def);
    }
    fossil_print("Setting: \"%s\"%s%s\n\n",
         pCmd->zName, zDflt!=0 ? zDflt : "",
         (pCmd->eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ? " (versionable)" : ""
    );
    fossil_free(zDflt);
  }
  blob_init(&txt, 0, 0);
  if( bRaw ){
    blob_append(&txt, z, -1);
  }else if( useHtml ){
    help_to_html(z, &txt);
  }else{
    help_to_text(z, &txt, bUsage || zSubtopic!=0);
  }
  if( blob_strlen(&txt)>0 ) fossil_print("%s\n", blob_str(&txt));
  blob_reset(&txt);
  blob_reset(&subtext1);
  blob_reset(&subtext2);
}

/*
** Return a pointer to the setting information array.
**
** This routine provides access to the aSetting[] array which is created
** by the mkindex utility program and included with <page_index.h>.
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
      break;
    case 3:  /* helptext */
      sqlite3_result_text(ctx, pPage->zHelp, -1, SQLITE_STATIC);
      break;
    case 4: { /* formatted */
      Blob txt;
      blob_init(&txt, 0, 0);
      help_to_text(pPage->zHelp, &txt);
      sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free);
      break;
    }
    case 5: { /* formatted */
      Blob txt;
      blob_init(&txt, 0, 0);
      help_to_html(pPage->zHelp, &txt);







|







1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
      break;
    case 3:  /* helptext */
      sqlite3_result_text(ctx, pPage->zHelp, -1, SQLITE_STATIC);
      break;
    case 4: { /* formatted */
      Blob txt;
      blob_init(&txt, 0, 0);
      help_to_text(pPage->zHelp, &txt, 0);
      sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free);
      break;
    }
    case 5: { /* formatted */
      Blob txt;
      blob_init(&txt, 0, 0);
      help_to_html(pPage->zHelp, &txt);
Changes to src/doc.c.
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
    if( nAttr==10 && fossil_strnicmp(zAttr,"data-title",10)==0 ){
      /* The text argument to data-title="" will have had any characters that
      ** are special to HTML encoded.  We need to decode these before turning
      ** the text into a title, as the title text will be reencoded later */
      char *zTitle = mprintf("%.*s", nValue, zValue);
      int i;
      for(i=0; fossil_isspace(zTitle[i]); i++){}
      html_to_plaintext(zTitle+i, pTitle);
      fossil_free(zTitle);
      seenTitle = 1;
      if( seenClass ) return 1;
    }
  }
  return seenClass;
}







|







637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
    if( nAttr==10 && fossil_strnicmp(zAttr,"data-title",10)==0 ){
      /* The text argument to data-title="" will have had any characters that
      ** are special to HTML encoded.  We need to decode these before turning
      ** the text into a title, as the title text will be reencoded later */
      char *zTitle = mprintf("%.*s", nValue, zValue);
      int i;
      for(i=0; fossil_isspace(zTitle[i]); i++){}
      html_to_plaintext(zTitle+i, pTitle, 0);
      fossil_free(zTitle);
      seenTitle = 1;
      if( seenClass ) return 1;
    }
  }
  return seenClass;
}
805
806
807
808
809
810
811

812
813
814
815
816
817
818
    }
    blob_reset(&tail);
  }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
    Blob tail = BLOB_INITIALIZER;
    markdown_to_html(pBody, &title, &tail);
    if( !isPopup ){
      if( blob_size(&title)>0 ){

        style_header("%s", blob_str(&title));
      }else{
        style_header("%s", zDefaultTitle);
      }
    }
    convert_href_and_output(&tail);
    if( !isPopup ){







>







805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
    }
    blob_reset(&tail);
  }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
    Blob tail = BLOB_INITIALIZER;
    markdown_to_html(pBody, &title, &tail);
    if( !isPopup ){
      if( blob_size(&title)>0 ){
        markdown_dehtmlize_blob(&title);
        style_header("%s", blob_str(&title));
      }else{
        style_header("%s", zDefaultTitle);
      }
    }
    convert_href_and_output(&tail);
    if( !isPopup ){
Changes to src/encode.c.
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  *zOut = 0;
  return zRet;
}

/*
** Convert a single HEX digit to an integer
*/
static int AsciiToHex(int c){
  if( c>='a' && c<='f' ){
    c += 10 - 'a';
  }else if( c>='A' && c<='F' ){
    c += 10 - 'A';
  }else if( c>='0' && c<='9' ){
    c -= '0';
  }else{







|







242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  *zOut = 0;
  return zRet;
}

/*
** Convert a single HEX digit to an integer
*/
int fossil_hexvalue(int c){
  if( c>='a' && c<='f' ){
    c += 10 - 'a';
  }else if( c>='A' && c<='F' ){
    c += 10 - 'A';
  }else if( c>='0' && c<='9' ){
    c -= '0';
  }else{
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
  if( !z ) return 0;

  i = j = 0;
  while( z[i] ){
    switch( z[i] ){
      case '%':
        if( z[i+1] && z[i+2] ){
          z[j] = AsciiToHex(z[i+1]) << 4;
          z[j] |= AsciiToHex(z[i+2]);
          i += 2;
        }
        break;
      case '+':
        z[j] = ' ';
        break;
      default:







|
|







270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
  if( !z ) return 0;

  i = j = 0;
  while( z[i] ){
    switch( z[i] ){
      case '%':
        if( z[i+1] && z[i+2] ){
          z[j] = fossil_hexvalue(z[i+1]) << 4;
          z[j] |= fossil_hexvalue(z[i+2]);
          i += 2;
        }
        break;
      case '+':
        z[j] = ' ';
        break;
      default:
Changes to src/export.c.
478
479
480
481
482
483
484


485
486
487
488
489
490
491
**   --rename-trunk NAME          Use NAME as name of exported trunk branch
**   -R|--repository REPO         Export the given REPOSITORY
**
** See also: import
*/
/*
** COMMAND: export*


**
** This command is deprecated.  Use "fossil git export" instead.
*/
void export_cmd(void){
  Stmt q, q2, q3;
  Bag blobs, vers;
  unsigned int unused_mark = 1;







>
>







478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
**   --rename-trunk NAME          Use NAME as name of exported trunk branch
**   -R|--repository REPO         Export the given REPOSITORY
**
** See also: import
*/
/*
** COMMAND: export*
**
** Usage: %fossil export --git [REPOSITORY]
**
** This command is deprecated.  Use "fossil git export" instead.
*/
void export_cmd(void){
  Stmt q, q2, q3;
  Bag blobs, vers;
  unsigned int unused_mark = 1;
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
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
**
** Return zero on success and non-zero if the export should be stopped.
*/
static int gitmirror_send_checkin(
  FILE *xCmd,           /* Write fast-import text on this pipe */
  int rid,              /* BLOB.RID for the check-in to export */
  const char *zUuid,    /* BLOB.UUID for the check-in to export */
  int *pnLimit,         /* Stop when the counter reaches zero */
  int fManifest         /* MFESTFLG_* values */
){
  Manifest *pMan;       /* The check-in to be output */
  int i;                /* Loop counter */
  int iParent;          /* Which immediate ancestor is primary.  -1 for none */
  Stmt q;               /* An SQL query */
  char *zBranch;        /* The branch of the check-in */
  char *zMark;          /* The Git-name of the check-in */
  Blob sql;             /* String of SQL for part of the query */
  Blob comment;         /* The comment text for the check-in */
  int nErr = 0;         /* Number of errors */
  int bPhantomOk;       /* True if phantom files should be ignored */
  char buf[24];
  char *zEmail;         /* Contact info for Git committer field */



  pMan = manifest_get(rid, CFTYPE_MANIFEST, 0);
  if( pMan==0 ){
    /* Must be a phantom.  Return without doing anything, and in particular
    ** without creating a mark for this check-in. */
    gitmirror_message(VERB_NORMAL, "missing check-in: %s\n", zUuid);
    return 0;
  }

  /* Check to see if any parent logins have not yet been processed, and
  ** if so, create them */
  for(i=0; i<pMan->nParent; i++){
    char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0);
    if( zPMark==0 ){
      int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q",
                        pMan->azParent[i]);
      int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i],
                                      pnLimit, fManifest);
      if( rc || *pnLimit<=0 ){
        manifest_destroy(pMan);
        return 1;
      }
    }
    fossil_free(zPMark);
  }







|
<













>
>

















|







1072
1073
1074
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
**
** Return zero on success and non-zero if the export should be stopped.
*/
static int gitmirror_send_checkin(
  FILE *xCmd,           /* Write fast-import text on this pipe */
  int rid,              /* BLOB.RID for the check-in to export */
  const char *zUuid,    /* BLOB.UUID for the check-in to export */
  int *pnLimit          /* Stop when the counter reaches zero */

){
  Manifest *pMan;       /* The check-in to be output */
  int i;                /* Loop counter */
  int iParent;          /* Which immediate ancestor is primary.  -1 for none */
  Stmt q;               /* An SQL query */
  char *zBranch;        /* The branch of the check-in */
  char *zMark;          /* The Git-name of the check-in */
  Blob sql;             /* String of SQL for part of the query */
  Blob comment;         /* The comment text for the check-in */
  int nErr = 0;         /* Number of errors */
  int bPhantomOk;       /* True if phantom files should be ignored */
  char buf[24];
  char *zEmail;         /* Contact info for Git committer field */
  int fManifest;        /* Should the manifest files be included? */
  int fPManifest = 0;   /* OR of the manifest files for all parents */

  pMan = manifest_get(rid, CFTYPE_MANIFEST, 0);
  if( pMan==0 ){
    /* Must be a phantom.  Return without doing anything, and in particular
    ** without creating a mark for this check-in. */
    gitmirror_message(VERB_NORMAL, "missing check-in: %s\n", zUuid);
    return 0;
  }

  /* Check to see if any parent logins have not yet been processed, and
  ** if so, create them */
  for(i=0; i<pMan->nParent; i++){
    char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0);
    if( zPMark==0 ){
      int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q",
                        pMan->azParent[i]);
      int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i],
                                      pnLimit);
      if( rc || *pnLimit<=0 ){
        manifest_destroy(pMan);
        return 1;
      }
    }
    fossil_free(zPMark);
  }
1211
1212
1213
1214
1215
1216
1217

1218
1219
1220
1221
1222
1223
1224
  blob_appendf(&comment, "\n\nFossilOrigin-Name: %s", zUuid);
  fprintf(xCmd, "data %d\n%s\n", blob_strlen(&comment), blob_str(&comment));
  blob_reset(&comment);
  iParent = -1;  /* Which ancestor is the primary parent */
  for(i=0; i<pMan->nParent; i++){
    char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0);
    if( zOther==0 ) continue;

    if( iParent<0 ){
      iParent = i;
      fprintf(xCmd, "from %s\n", zOther);
    }else{
      fprintf(xCmd, "merge %s\n", zOther);
    }
    fossil_free(zOther);







>







1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
  blob_appendf(&comment, "\n\nFossilOrigin-Name: %s", zUuid);
  fprintf(xCmd, "data %d\n%s\n", blob_strlen(&comment), blob_str(&comment));
  blob_reset(&comment);
  iParent = -1;  /* Which ancestor is the primary parent */
  for(i=0; i<pMan->nParent; i++){
    char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0);
    if( zOther==0 ) continue;
    fPManifest |= db_get_manifest_setting(pMan->azParent[i]);
    if( iParent<0 ){
      iParent = i;
      fprintf(xCmd, "from %s\n", zOther);
    }else{
      fprintf(xCmd, "merge %s\n", zOther);
    }
    fossil_free(zOther);
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
    fossil_free(zFNQuoted);
  }
  db_finalize(&q);
  manifest_destroy(pMan);
  pMan = 0;

  /* Include Fossil-generated auxiliary files in the check-in */

  if( fManifest & MFESTFLG_RAW ){
    Blob manifest;
    content_get(rid, &manifest);
    sterilize_manifest(&manifest, CFTYPE_MANIFEST);
    fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n",
      blob_strlen(&manifest), blob_str(&manifest));
    blob_reset(&manifest);


  }
  if( fManifest & MFESTFLG_UUID ){
    int n = (int)strlen(zUuid);
    fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n\n", n+1, zUuid);


  }
  if( fManifest & MFESTFLG_TAGS ){
    Blob tagslist;
    blob_init(&tagslist, 0, 0);
    get_checkin_taglist(rid, &tagslist);
    fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n",
      blob_strlen(&tagslist), blob_str(&tagslist));
    blob_reset(&tagslist);


  }

  /* The check-in is finished, so decrement the counter */
  (*pnLimit)--;
  return 0;
}








>







>
>




>
>








>
>







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
    fossil_free(zFNQuoted);
  }
  db_finalize(&q);
  manifest_destroy(pMan);
  pMan = 0;

  /* Include Fossil-generated auxiliary files in the check-in */
  fManifest = db_get_manifest_setting(zUuid);
  if( fManifest & MFESTFLG_RAW ){
    Blob manifest;
    content_get(rid, &manifest);
    sterilize_manifest(&manifest, CFTYPE_MANIFEST);
    fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n",
      blob_strlen(&manifest), blob_str(&manifest));
    blob_reset(&manifest);
  }else if( fPManifest & MFESTFLG_RAW ){
    fprintf(xCmd, "D manifest\n");
  }
  if( fManifest & MFESTFLG_UUID ){
    int n = (int)strlen(zUuid);
    fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n\n", n+1, zUuid);
  }else if( fPManifest & MFESTFLG_UUID ){
    fprintf(xCmd, "D manifest.uuid\n");
  }
  if( fManifest & MFESTFLG_TAGS ){
    Blob tagslist;
    blob_init(&tagslist, 0, 0);
    get_checkin_taglist(rid, &tagslist);
    fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n",
      blob_strlen(&tagslist), blob_str(&tagslist));
    blob_reset(&tagslist);
  }else if( fPManifest & MFESTFLG_TAGS ){
    fprintf(xCmd, "D manifest.tags\n");
  }

  /* The check-in is finished, so decrement the counter */
  (*pnLimit)--;
  return 0;
}

1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
  const char *zAutoPush = 0;      /* Value of the --autopush flag */
  char *zMainBr = 0;              /* Value of the --mainbranch flag */
  char *zPushUrl;                 /* URL to sync the mirror to */
  double rEnd;                    /* time of most recent export */
  int rc;                         /* Result code */
  int bForce;                     /* Do the export and sync even if no changes*/
  int bNeedRepack = 0;            /* True if we should run repack at the end */
  int fManifest;                  /* Current "manifest" setting */
  int bIfExists;                  /* The --if-mirrored flag */
  FILE *xCmd;                     /* Pipe to the "git fast-import" command */
  FILE *pMarks;                   /* Git mark files */
  Stmt q;                         /* Queries */
  char zLine[200];                /* One line of a mark file */

  zDebug = find_option("debug",0,1);







<







1390
1391
1392
1393
1394
1395
1396

1397
1398
1399
1400
1401
1402
1403
  const char *zAutoPush = 0;      /* Value of the --autopush flag */
  char *zMainBr = 0;              /* Value of the --mainbranch flag */
  char *zPushUrl;                 /* URL to sync the mirror to */
  double rEnd;                    /* time of most recent export */
  int rc;                         /* Result code */
  int bForce;                     /* Do the export and sync even if no changes*/
  int bNeedRepack = 0;            /* True if we should run repack at the end */

  int bIfExists;                  /* The --if-mirrored flag */
  FILE *xCmd;                     /* Pipe to the "git fast-import" command */
  FILE *pMarks;                   /* Git mark files */
  Stmt q;                         /* Queries */
  char zLine[200];                /* One line of a mark file */

  zDebug = find_option("debug",0,1);
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
                                        " WHERE key='start'),0.0)")
  ){
    gitmirror_message(VERB_NORMAL, "no changes\n");
    db_commit_transaction();
    return;
  }

  /* Do we need to include manifest files in the clone? */
  fManifest = db_get_manifest_setting();

  /* Change to the MIRROR directory so that the Git commands will work */
  rc = file_chdir(zMirror, 0);
  if( rc ) fossil_fatal("cannot change the working directory to \"%s\"",
                        zMirror);

  /* Start up the git fast-import command */
  if( zDebug ){







<
<
<







1527
1528
1529
1530
1531
1532
1533



1534
1535
1536
1537
1538
1539
1540
                                        " WHERE key='start'),0.0)")
  ){
    gitmirror_message(VERB_NORMAL, "no changes\n");
    db_commit_transaction();
    return;
  }




  /* Change to the MIRROR directory so that the Git commands will work */
  rc = file_chdir(zMirror, 0);
  if( rc ) fossil_fatal("cannot change the working directory to \"%s\"",
                        zMirror);

  /* Start up the git fast-import command */
  if( zDebug ){
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
    "SELECT objid, mtime, uuid FROM tomirror ORDER BY mtime"
  );
  while( nLimit && db_step(&q)==SQLITE_ROW ){
    int rid = db_column_int(&q, 0);
    double rMTime = db_column_double(&q, 1);
    const char *zUuid = db_column_text(&q, 2);
    if( rMTime>rEnd ) rEnd = rMTime;
    rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit, fManifest);
    if( rc ) break;
    gitmirror_message(VERB_NORMAL,"%d/%d      \r", nTotal-nLimit, nTotal);
    fflush(stdout);
  }
  db_finalize(&q);
  fprintf(xCmd, "done\n");
  if( zDebug ){







|







1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
    "SELECT objid, mtime, uuid FROM tomirror ORDER BY mtime"
  );
  while( nLimit && db_step(&q)==SQLITE_ROW ){
    int rid = db_column_int(&q, 0);
    double rMTime = db_column_double(&q, 1);
    const char *zUuid = db_column_text(&q, 2);
    if( rMTime>rEnd ) rEnd = rMTime;
    rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit);
    if( rc ) break;
    gitmirror_message(VERB_NORMAL,"%d/%d      \r", nTotal-nLimit, nTotal);
    fflush(stdout);
  }
  db_finalize(&q);
  fprintf(xCmd, "done\n");
  if( zDebug ){
Changes to src/file.c.
1570
1571
1572
1573
1574
1575
1576







1577
1578
1579
1580
1581
1582
1583
/*
** COMMAND: test-which
**
** Usage: %fossil test-which ARGS...
**
** For each argument, search the PATH for the executable with the name
** and print its full pathname.







*/
void test_which_cmd(void){
  int i;
  for(i=2; i<g.argc; i++){
    char *z = file_fullexename(g.argv[i]);
    fossil_print("%z\n", z);
  }







>
>
>
>
>
>
>







1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
/*
** COMMAND: test-which
**
** Usage: %fossil test-which ARGS...
**
** For each argument, search the PATH for the executable with the name
** and print its full pathname.
**
** See also the "which" command (without the "test-" prefix).  The plain
** "which" command is more convenient to use since it provides the -a/-all
** option, and because it is shorter.  The "fossil which" command without
** the "test-" prefix is recommended for day-to-day use.  This command is
** retained because it tests the internal file_fullexename() function
** whereas plain "which" does not.
*/
void test_which_cmd(void){
  int i;
  for(i=2; i<g.argc; i++){
    char *z = file_fullexename(g.argv[i]);
    fossil_print("%z\n", z);
  }
Changes to src/finfo.c.
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
    " LEFT JOIN filename ON filename.fnid=clade.fnid\n"
    "WHERE mlink.fnid=clade.fnid AND mlink.fid=clade.fid\n"
    "  AND event.objid=mlink.mid\n",
    TAG_BRANCH
  );
  if( (zA = P("a"))!=0 ){
    blob_append_sql(&sql, "  AND event.mtime>=%.16g\n",
         symbolic_name_to_mtime(zA,0));
    url_add_parameter(&url, "a", zA);
  }
  if( (zB = P("b"))!=0 ){
    blob_append_sql(&sql, "  AND event.mtime<=%.16g\n",
         symbolic_name_to_mtime(zB,0));
    url_add_parameter(&url, "b", zB);
  }
  if( ridFrom ){
    blob_append_sql(&sql,
      "  AND mlink.mid IN (SELECT rid FROM ancestor)\n"
      "GROUP BY mlink.fid\n"
    );







|




|







496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
    " LEFT JOIN filename ON filename.fnid=clade.fnid\n"
    "WHERE mlink.fnid=clade.fnid AND mlink.fid=clade.fid\n"
    "  AND event.objid=mlink.mid\n",
    TAG_BRANCH
  );
  if( (zA = P("a"))!=0 ){
    blob_append_sql(&sql, "  AND event.mtime>=%.16g\n",
         symbolic_name_to_mtime(zA,0,0));
    url_add_parameter(&url, "a", zA);
  }
  if( (zB = P("b"))!=0 ){
    blob_append_sql(&sql, "  AND event.mtime<=%.16g\n",
         symbolic_name_to_mtime(zB,0,1));
    url_add_parameter(&url, "b", zB);
  }
  if( ridFrom ){
    blob_append_sql(&sql,
      "  AND mlink.mid IN (SELECT rid FROM ancestor)\n"
      "GROUP BY mlink.fid\n"
    );
634
635
636
637
638
639
640


641
642
643
644
645
646
647
    }
    db_reset(&qparent);
    if( zBr==0 ) zBr = "trunk";
    if( uBg ){
      zBgClr = user_color(zUser);
    }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
      zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);


    }
    gidx = graph_add_row(pGraph,
                   frid>0 ? (GraphRowId)frid*(mxfnid+1)+fnid : fpid+1000000000,
                   nParent, 0, aParent, zBr, zBgClr,
                   zUuid, 0);
    if( strncmp(zDate, zPrevDate, 10) ){
      sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);







>
>







634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
    }
    db_reset(&qparent);
    if( zBr==0 ) zBr = "trunk";
    if( uBg ){
      zBgClr = user_color(zUser);
    }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
      zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
    }else if( zBgClr ){
      zBgClr = reasonable_bg_color(zBgClr,0);
    }
    gidx = graph_add_row(pGraph,
                   frid>0 ? (GraphRowId)frid*(mxfnid+1)+fnid : fpid+1000000000,
                   nParent, 0, aParent, zBr, zBgClr,
                   zUuid, 0);
    if( strncmp(zDate, zPrevDate, 10) ){
      sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
Changes to src/fossil.page.pikchrshowasm.js.
118
119
120
121
122
123
124



125


126
127
128
129
130
131
132
      if(PS._config.hasOwnProperty(k)){
        PS.config[k] = PS._config[k];
      }
    });
    delete PS._config;
  }




  PS.worker = new Worker('builtin/extsrc/pikchr-worker.js');


  PS.worker.onmessage = (ev)=>PS.runMsgHandlers(ev.data);
  PS.addMsgHandler('stdout', console.log.bind(console));
  PS.addMsgHandler('stderr', console.error.bind(console));

  /** Handles status updates from the Module object. */
  PS.addMsgHandler('module', function f(ev){
    ev = ev.data;







>
>
>
|
>
>







118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
      if(PS._config.hasOwnProperty(k)){
        PS.config[k] = PS._config[k];
      }
    });
    delete PS._config;
  }

  /* Randomize the name of the worker script so that it is never cached.
  ** The Fossil /builtin method will automatically remove the "-v000000000"
  ** part of the filename, resolving it to just "pikchr-worker.js". */
  PS.worker = new Worker('builtin/extsrc/pikchr-worker-v'+
                         (Math.floor(Math.random()*10000000000) + 1000000000)+
                        '.js');
  PS.worker.onmessage = (ev)=>PS.runMsgHandlers(ev.data);
  PS.addMsgHandler('stdout', console.log.bind(console));
  PS.addMsgHandler('stderr', console.error.bind(console));

  /** Handles status updates from the Module object. */
  PS.addMsgHandler('module', function f(ev){
    ev = ev.data;
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

  PS.e.previewModeLabel.innerText =
    PS.renderModeLabels[PS.renderModes[PS.renderModes.selectedIndex]];

  /**
     The 'pikchr-ready' event is fired (with no payload) when the
     wasm module has finished loading. */
  PS.addMsgHandler('pikchr-ready', function(){
    PS.clearMsgHandlers('pikchr-ready');
    F.page.onPikchrshowLoaded();
  });

  /**
     Performs all app initialization which must wait until after the
     worker module is loaded. This function removes itself when it's
     called.
  */
  F.page.onPikchrshowLoaded = function(){
    delete this.onPikchrshowLoaded;
    // Unhide all elements which start out hidden
    EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden'));
    const taInput = E('#input');
    const btnClearIn = E('#btn-clear');
    btnClearIn.addEventListener('click',function(){
      taInput.value = '';







|

|







|







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

  PS.e.previewModeLabel.innerText =
    PS.renderModeLabels[PS.renderModes[PS.renderModes.selectedIndex]];

  /**
     The 'pikchr-ready' event is fired (with no payload) when the
     wasm module has finished loading. */
  PS.addMsgHandler('pikchr-ready', function(event){
    PS.clearMsgHandlers('pikchr-ready');
    F.page.onPikchrshowLoaded(event.data);
  });

  /**
     Performs all app initialization which must wait until after the
     worker module is loaded. This function removes itself when it's
     called.
  */
  F.page.onPikchrshowLoaded = function(pikchrVersion){
    delete this.onPikchrshowLoaded;
    // Unhide all elements which start out hidden
    EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden'));
    const taInput = E('#input');
    const btnClearIn = E('#btn-clear');
    btnClearIn.addEventListener('click',function(){
      taInput.value = '';
437
438
439
440
441
442
443



444
445
446
447
448
449
450
         link in the forum. */
      const src = window.sessionStorage.getItem('pikchr-xfer');
      if( src && (new URL(self.location.href).searchParams).has('fromSession') ){
        taInput.value =  src;
        window.sessionStorage.removeItem('pikchr-xfer');
      }
    }




    PS.e.btnRender.click();

    /** Debounce handler for auto-rendering while typing. */
    const debounceAutoRender = F.debounce(function f(){
      if(!PS._isDirty) return;
      const text = getCurrentText();







>
>
>







442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
         link in the forum. */
      const src = window.sessionStorage.getItem('pikchr-xfer');
      if( src && (new URL(self.location.href).searchParams).has('fromSession') ){
        taInput.value =  src;
        window.sessionStorage.removeItem('pikchr-xfer');
      }
    }
    D.append(E('fieldset.options > div'),
             D.append(D.addClass(D.span(), 'labeled-input'),
                      'pikchr v. '+pikchrVersion));

    PS.e.btnRender.click();

    /** Debounce handler for auto-rendering while typing. */
    const debounceAutoRender = F.debounce(function f(){
      if(!PS._isDirty) return;
      const text = getCurrentText();
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536

    delete ForceResizeKludge.$disabled;
    ForceResizeKludge();
  }/*onPikchrshowLoaded()*/;


  /**
     Predefined scripts. Each entry is an object:

     {
     name: required string,
     code: optional code string. An entry with a falsy code is treated
           like a separator in the resulting SELECT element (a
           disabled OPTION).
     }







|







530
531
532
533
534
535
536
537
538
539
540
541
542
543
544

    delete ForceResizeKludge.$disabled;
    ForceResizeKludge();
  }/*onPikchrshowLoaded()*/;


  /**
     Predefined example pikchr scripts. Each entry is an object:

     {
     name: required string,
     code: optional code string. An entry with a falsy code is treated
           like a separator in the resulting SELECT element (a
           disabled OPTION).
     }
Changes to src/fossil.page.wikiedit.js.
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
     - Event 'wiki-page-loaded': passes on information when it
     loads a wiki (whether from the network or its internal local-edit
     cache), in the form of an "winfo" object:

     {
       name: string,
       mimetype: mimetype string,
       type: "normal" | "tag" | "checkin" | "branch" | "sandbox",
       version: UUID string or null for a sandbox page or new page,
       parent: parent UUID string or null if no parent,
       isEmpty: true if page has no content (is "deleted").
       content: string, optional in most contexts
     }

     The internal docs and code frequently use the term "winfo", and such







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
     - Event 'wiki-page-loaded': passes on information when it
     loads a wiki (whether from the network or its internal local-edit
     cache), in the form of an "winfo" object:

     {
       name: string,
       mimetype: mimetype string,
       type: "normal" | "tag" | "checkin" | "branch" | "ticket" | "sandbox",
       version: UUID string or null for a sandbox page or new page,
       parent: parent UUID string or null if no parent,
       isEmpty: true if page has no content (is "deleted").
       content: string, optional in most contexts
     }

     The internal docs and code frequently use the term "winfo", and such
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
            old = ndx[key];
      const record = old || (ndx[key]={
        name: winfo.name
      });
      record.mimetype = winfo.mimetype;
      record.type = winfo.type;
      record.parent = winfo.parent;
      record.version = winfo.version;      
      record.stashTime = new Date().getTime();
      record.isEmpty = !!winfo.isEmpty;
      record.attachments = winfo.attachments;
      this.storeIndex();
      if(arguments.length>1){
        if(content) delete record.isEmpty;
        F.storage.set(this.contentKey(key), content);
      }
      this._fireStashEvent();
      return this;
    },
    /**
       Returns the stashed content, if any, for the given winfo
       object.
    */       
    stashedContent: function(winfo){
      return F.storage.get(this.contentKey(this.indexKey(winfo)));
    },
    /** Returns true if we have stashed content for the given winfo
        record or page name. */
    hasStashedContent: function(winfo){
      if('string'===typeof winfo) winfo = {name: winfo};







|














|







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
            old = ndx[key];
      const record = old || (ndx[key]={
        name: winfo.name
      });
      record.mimetype = winfo.mimetype;
      record.type = winfo.type;
      record.parent = winfo.parent;
      record.version = winfo.version;
      record.stashTime = new Date().getTime();
      record.isEmpty = !!winfo.isEmpty;
      record.attachments = winfo.attachments;
      this.storeIndex();
      if(arguments.length>1){
        if(content) delete record.isEmpty;
        F.storage.set(this.contentKey(key), content);
      }
      this._fireStashEvent();
      return this;
    },
    /**
       Returns the stashed content, if any, for the given winfo
       object.
    */
    stashedContent: function(winfo){
      return F.storage.get(this.contentKey(this.indexKey(winfo)));
    },
    /** Returns true if we have stashed content for the given winfo
        record or page name. */
    hasStashedContent: function(winfo){
      if('string'===typeof winfo) winfo = {name: winfo};
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
        console.warn("Pruned oldest local file edit entry:",e);
      }
      if(n) this._fireStashEvent();
    }
  };
  $stash.prune.defaultMaxCount = P.config.defaultMaxStashSize || 10;
  P.$stash = $stash /* we have to expose this for the new-page case :/ */;
  
  /**
     Internal workaround to select the current preview mode
     and fire a change event if the value actually changes
     or if forceEvent is truthy.
  */
  P.selectMimetype = function(modeValue, forceEvent){
    const s = this.e.selectMimetype;







|







268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
        console.warn("Pruned oldest local file edit entry:",e);
      }
      if(n) this._fireStashEvent();
    }
  };
  $stash.prune.defaultMaxCount = P.config.defaultMaxStashSize || 10;
  P.$stash = $stash /* we have to expose this for the new-page case :/ */;

  /**
     Internal workaround to select the current preview mode
     and fire a change event if the value actually changes
     or if forceEvent is truthy.
  */
  P.selectMimetype = function(modeValue, forceEvent){
    const s = this.e.selectMimetype;
534
535
536
537
538
539
540

541
542
543
544
545
546
547
    */
    addNewPage: function(name){
      name = name.trim();
      if(!this.validatePageName(name)) return false;
      var wtype = 'normal';
      if(0===name.indexOf('checkin/')) wtype = 'checkin';
      else if(0===name.indexOf('branch/')) wtype = 'branch';

      else if(0===name.indexOf('tag/')) wtype = 'tag';
      /* ^^^ note that we're not validating that, e.g., checkin/XYZ
         has a full artifact ID after "checkin/". */
      const winfo = {
        name: name, type: wtype, mimetype: 'text/x-markdown',
        version: null, parent: null
      };







>







534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
    */
    addNewPage: function(name){
      name = name.trim();
      if(!this.validatePageName(name)) return false;
      var wtype = 'normal';
      if(0===name.indexOf('checkin/')) wtype = 'checkin';
      else if(0===name.indexOf('branch/')) wtype = 'branch';
      else if(0===name.indexOf('ticket/')) wtype = 'ticket';
      else if(0===name.indexOf('tag/')) wtype = 'tag';
      /* ^^^ note that we're not validating that, e.g., checkin/XYZ
         has a full artifact ID after "checkin/". */
      const winfo = {
        name: name, type: wtype, mimetype: 'text/x-markdown',
        version: null, parent: null
      };
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
      D.attr(sel, 'size', 12);
      D.option(D.disable(D.clearElement(sel)), undefined, "Loading...");

      /** Set up filter checkboxes for the various types
          of wiki pages... */
      const fsFilter = D.addClass(D.fieldset("Page types"),"page-types-list"),
            fsFilterBody = D.div(),
            filters = ['normal', 'branch/...', 'tag/...', 'checkin/...']
      ;
      D.append(fsFilter, fsFilterBody);
      D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch');

      // Add filters by page type...
      const self = this;
      const filterByType = function(wtype, show){







|







572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
      D.attr(sel, 'size', 12);
      D.option(D.disable(D.clearElement(sel)), undefined, "Loading...");

      /** Set up filter checkboxes for the various types
          of wiki pages... */
      const fsFilter = D.addClass(D.fieldset("Page types"),"page-types-list"),
            fsFilterBody = D.div(),
            filters = ['normal', 'branch/...', 'tag/...', 'checkin/...', 'ticket/...']
      ;
      D.append(fsFilter, fsFilterBody);
      D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch');

      // Add filters by page type...
      const self = this;
      const filterByType = function(wtype, show){
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
      });
    }else{
      P.e.btnSave.addEventListener('click', ()=>doSave(), false);
      P.e.btnSaveClose.addEventListener('click', ()=>doSave(true), false);
    }

    P.e.taEditor.addEventListener('change', ()=>P.notifyOfChange(), false);
    
    P.selectMimetype(false, true);
    P.e.selectMimetype.addEventListener(
      'change',
      function(e){
        if(P.winfo && P.winfo.mimetype !== e.target.value){
          P.winfo.mimetype = e.target.value;
          P._isDirty = true;
          P.stashContentChange(true);
        }
      },
      false
    );
    
    const selectFontSize = E('select[name=editor_font_size]');
    if(selectFontSize){
      selectFontSize.addEventListener(
        "change",function(e){
          const ed = P.e.taEditor;
          ed.className = ed.className.replace(
              /\bfont-size-\d+/g, '' );







|












|







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
      });
    }else{
      P.e.btnSave.addEventListener('click', ()=>doSave(), false);
      P.e.btnSaveClose.addEventListener('click', ()=>doSave(true), false);
    }

    P.e.taEditor.addEventListener('change', ()=>P.notifyOfChange(), false);

    P.selectMimetype(false, true);
    P.e.selectMimetype.addEventListener(
      'change',
      function(e){
        if(P.winfo && P.winfo.mimetype !== e.target.value){
          P.winfo.mimetype = e.target.value;
          P._isDirty = true;
          P.stashContentChange(true);
        }
      },
      false
    );

    const selectFontSize = E('select[name=editor_font_size]');
    if(selectFontSize){
      selectFontSize.addEventListener(
        "change",function(e){
          const ed = P.e.taEditor;
          ed.className = ed.className.replace(
              /\bfont-size-\d+/g, '' );
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
    ).fetch('wikiajax/save',{
      payload: fd,
      responseType: 'json',
      onload: callee.onload
    });
    return this;
  };
  
  /**
     Updates P.winfo for certain state and stashes P.winfo, with the
     current content fetched via P.wikiContent().

     If passed truthy AND the stash already has stashed content for
     the current page, only the stashed winfo record is updated, else
     both the winfo and content are updated.







|







1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
    ).fetch('wikiajax/save',{
      payload: fd,
      responseType: 'json',
      onload: callee.onload
    });
    return this;
  };

  /**
     Updates P.winfo for certain state and stashes P.winfo, with the
     current content fetched via P.wikiContent().

     If passed truthy AND the stash already has stashed content for
     the current page, only the stashed winfo record is updated, else
     both the winfo and content are updated.
Changes to src/fuzz.c.
58
59
60
61
62
63
64

65
66
67
68
69
70
71
/*
** Type of fuzzing:
*/
#define FUZZ_WIKI       0      /* The Fossil-Wiki formatter */
#define FUZZ_MARKDOWN   1      /* The Markdown formatter */
#define FUZZ_ARTIFACT   2      /* Fuzz the artifact parser */
#define FUZZ_WIKI2      3      /* FOSSIL_WIKI and FOSSIL_MARKDOWN */

#endif

/* The type of fuzzing to do */
static int eFuzzType = FUZZ_WIKI;

/* The fuzzer invokes this routine once for each fuzzer input
*/







>







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*
** Type of fuzzing:
*/
#define FUZZ_WIKI       0      /* The Fossil-Wiki formatter */
#define FUZZ_MARKDOWN   1      /* The Markdown formatter */
#define FUZZ_ARTIFACT   2      /* Fuzz the artifact parser */
#define FUZZ_WIKI2      3      /* FOSSIL_WIKI and FOSSIL_MARKDOWN */
#define FUZZ_COMFORMAT  4      /* comment_print() */
#endif

/* The type of fuzzing to do */
static int eFuzzType = FUZZ_WIKI;

/* The fuzzer invokes this routine once for each fuzzer input
*/
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
      Blob title = BLOB_INITIALIZER;
      wiki_convert(&in, &out, 0);
      blob_reset(&out);
      markdown_to_html(&in, &title, &out);
      blob_reset(&title);
      break;
    }
    case FUZZ_ARTIFACT:
      fossil_fatal("FUZZ_ARTIFACT is not implemented.");
      break;







  }
  blob_reset(&in);
  blob_reset(&out);
  return 0;
}

/*
** Check fuzzer command-line options.
*/
static void fuzzer_options(void){
  const char *zType;
  db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
  db_multi_exec("PRAGMA query_only=1;");
  zType = find_option("fuzztype",0,1);
  if( zType==0 || fossil_strcmp(zType,"wiki")==0 ){
    eFuzzType = FUZZ_WIKI;
  }else if( fossil_strcmp(zType,"markdown")==0 ){
    eFuzzType = FUZZ_MARKDOWN;
  }else if( fossil_strcmp(zType,"wiki2")==0 ){
    eFuzzType = FUZZ_WIKI2;


  }else{
    fossil_fatal("unknown fuzz type: \"%s\"", zType);
  }
}

/* Libfuzzer invokes this routine once prior to start-up to
** process command-line options.







|


>
>
>
>
>
>
>




















>
>







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
      Blob title = BLOB_INITIALIZER;
      wiki_convert(&in, &out, 0);
      blob_reset(&out);
      markdown_to_html(&in, &title, &out);
      blob_reset(&title);
      break;
    }
    case FUZZ_ARTIFACT: {
      fossil_fatal("FUZZ_ARTIFACT is not implemented.");
      break;
    }
    case FUZZ_COMFORMAT: {
      if( nByte>=3 && aData[1]!=0 && memchr(&aData[1], 0, nByte-1)!=0 ){
        int flags = (int)aData[0];
        comment_print((const char*)&aData[1],0,15,80,flags);
      }
    }
  }
  blob_reset(&in);
  blob_reset(&out);
  return 0;
}

/*
** Check fuzzer command-line options.
*/
static void fuzzer_options(void){
  const char *zType;
  db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
  db_multi_exec("PRAGMA query_only=1;");
  zType = find_option("fuzztype",0,1);
  if( zType==0 || fossil_strcmp(zType,"wiki")==0 ){
    eFuzzType = FUZZ_WIKI;
  }else if( fossil_strcmp(zType,"markdown")==0 ){
    eFuzzType = FUZZ_MARKDOWN;
  }else if( fossil_strcmp(zType,"wiki2")==0 ){
    eFuzzType = FUZZ_WIKI2;
  }else if( fossil_strcmp(zType,"comformat")==0 ){
    eFuzzType = FUZZ_COMFORMAT;
  }else{
    fossil_fatal("unknown fuzz type: \"%s\"", zType);
  }
}

/* Libfuzzer invokes this routine once prior to start-up to
** process command-line options.
137
138
139
140
141
142
143

144
145
146
147
148
149
150
/*
** COMMAND: test-fuzz
**
** Usage: %fossil test-fuzz [-fuzztype TYPE] INPUTFILE...
**
** Run a fuzz test using INPUTFILE as the test data.  TYPE can be one of:
**

**     wiki                  Fuzz the Fossil-wiki translator
**     markdown              Fuzz the markdown translator
**     artifact              Fuzz the artifact parser
**     wiki2                 Fuzz the Fossil-wiki and markdown translator
*/
void fuzz_command(void){
  Blob in;







>







147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/*
** COMMAND: test-fuzz
**
** Usage: %fossil test-fuzz [-fuzztype TYPE] INPUTFILE...
**
** Run a fuzz test using INPUTFILE as the test data.  TYPE can be one of:
**
**     comformat             Fuzz the comment_print() routine
**     wiki                  Fuzz the Fossil-wiki translator
**     markdown              Fuzz the markdown translator
**     artifact              Fuzz the artifact parser
**     wiki2                 Fuzz the Fossil-wiki and markdown translator
*/
void fuzz_command(void){
  Blob in;
Changes to src/graph.c.
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
** the identifier is a combination of the BLOB.RID and the FILENAME.FNID
** values, and so it can become quite large for repos that have both many
** check-ins and many files.  For this reason, we make the identifier
** a 64-bit integer, to dramatically reduce the risk of an overflow.
*/
typedef sqlite3_int64 GraphRowId;

#define GR_MAX_RAIL   40      /* Max number of "rails" to display */

/* The graph appears vertically beside a timeline.  Each row in the
** timeline corresponds to a row in the graph.  GraphRow.idx is 0 for
** the top-most row and increases moving down.  Hence (in the absence of
** time skew) parents have a larger index than their children.
**
** The nParent field is -1 for entires that do not participate in the graph







|







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
** the identifier is a combination of the BLOB.RID and the FILENAME.FNID
** values, and so it can become quite large for repos that have both many
** check-ins and many files.  For this reason, we make the identifier
** a 64-bit integer, to dramatically reduce the risk of an overflow.
*/
typedef sqlite3_int64 GraphRowId;

#define GR_MAX_RAIL   64      /* Max number of "rails" to display */

/* The graph appears vertically beside a timeline.  Each row in the
** timeline corresponds to a row in the graph.  GraphRow.idx is 0 for
** the top-most row and increases moving down.  Hence (in the absence of
** time skew) parents have a larger index than their children.
**
** The nParent field is -1 for entires that do not participate in the graph
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  char *zBgClr;               /* Background Color */
  char zUuid[HNAME_MAX+1];    /* Check-in for file ID */

  GraphRow *pNext;            /* Next row down in the list of all rows */
  GraphRow *pPrev;            /* Previous row */

  int idx;                    /* Row index.  Top row is smallest. */
  int idxTop;                 /* Direct descendent highest up on the graph */
  GraphRow *pChild;           /* Child immediately above this node */
  u8 isDup;                   /* True if this is duplicate of a prior entry */
  u8 isLeaf;                  /* True if this is a leaf node */
  u8 isStepParent;            /* pChild is actually a step-child. The thick
                              ** arrow up to the child is dashed, not solid */
  u8 hasNormalOutMerge;       /* Is parent of at laest 1 non-cherrypick merge */
  u8 timeWarp;                /* Child is earlier in time */







|







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  char *zBgClr;               /* Background Color */
  char zUuid[HNAME_MAX+1];    /* Check-in for file ID */

  GraphRow *pNext;            /* Next row down in the list of all rows */
  GraphRow *pPrev;            /* Previous row */

  int idx;                    /* Row index.  Top row is smallest. */
  int idxTop;                 /* Direct descendant highest up on the graph */
  GraphRow *pChild;           /* Child immediately above this node */
  u8 isDup;                   /* True if this is duplicate of a prior entry */
  u8 isLeaf;                  /* True if this is a leaf node */
  u8 isStepParent;            /* pChild is actually a step-child. The thick
                              ** arrow up to the child is dashed, not solid */
  u8 hasNormalOutMerge;       /* Is parent of at laest 1 non-cherrypick merge */
  u8 timeWarp;                /* Child is earlier in time */
116
117
118
119
120
121
122

123
124
125
126
127
128
129
  GraphRow *pLast;           /* Last row in the list. Bottom row of graph. */
  int nBranch;               /* Number of distinct branches */
  char **azBranch;           /* Names of the branches */
  int nRow;                  /* Number of rows */
  int nHash;                 /* Number of slots in apHash[] */
  u8 hasOffsetMergeRiser;    /* Merge arrow from leaf goes up on a different
                             ** rail that the node */

  u64 mergeRail;             /* Rails used for merge lines */
  GraphRow **apHash;         /* Hash table of GraphRow objects.  Key: rid */
  u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */
};

#endif








>







116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  GraphRow *pLast;           /* Last row in the list. Bottom row of graph. */
  int nBranch;               /* Number of distinct branches */
  char **azBranch;           /* Names of the branches */
  int nRow;                  /* Number of rows */
  int nHash;                 /* Number of slots in apHash[] */
  u8 hasOffsetMergeRiser;    /* Merge arrow from leaf goes up on a different
                             ** rail that the node */
  u8 bOverfull;              /* Unable to allocate sufficient rails */
  u64 mergeRail;             /* Rails used for merge lines */
  GraphRow **apHash;         /* Hash table of GraphRow objects.  Key: rid */
  u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */
};

#endif

334
335
336
337
338
339
340
341







342
343
344
345
346
347
348
      if( dist<0 ) dist = -dist;
      if( dist<iBestDist ){
        iBestDist = dist;
        iBest = i;
      }
    }
  }
  if( iBestDist>1000 ) p->nErr++;







  if( iBest>p->mxRail ) p->mxRail = iBest;
  if( bMergeRail ) p->mergeRail |= BIT(iBest);
  return iBest;
}

/*
** Assign all children of node pBottom to the same rail as pBottom.







|
>
>
>
>
>
>
>







335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
      if( dist<0 ) dist = -dist;
      if( dist<iBestDist ){
        iBestDist = dist;
        iBest = i;
      }
    }
  }
  if( iBestDist>1000 ){
    p->bOverfull = 1;
    iBest = GR_MAX_RAIL;
  }
  if( iBest>GR_MAX_RAIL ){
    p->bOverfull = 1;
    iBest = GR_MAX_RAIL;
  }
  if( iBest>p->mxRail ) p->mxRail = iBest;
  if( bMergeRail ) p->mergeRail |= BIT(iBest);
  return iBest;
}

/*
** Assign all children of node pBottom to the same rail as pBottom.
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
    for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
      if( i==0 && pRow->zBranch!=zTrunk ) continue;
      if( pRow->iRail>=0 ) continue;
      if( pRow->isDup ) continue;
      if( pRow->nParent<0 ) continue;
      if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
        pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0);
        if( p->mxRail>=GR_MAX_RAIL ) return;
        mask = BIT(pRow->iRail);
        if( !omitDescenders ){
          int n = RISER_MARGIN;
          pRow->bDescender = pRow->nParent>0;
          for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){
            pLoop->railInUse |= mask;
          }







|







715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
    for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
      if( i==0 && pRow->zBranch!=zTrunk ) continue;
      if( pRow->iRail>=0 ) continue;
      if( pRow->isDup ) continue;
      if( pRow->nParent<0 ) continue;
      if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
        pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0);
        /* if( p->mxRail>=GR_MAX_RAIL ) return; */
        mask = BIT(pRow->iRail);
        if( !omitDescenders ){
          int n = RISER_MARGIN;
          pRow->bDescender = pRow->nParent>0;
          for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){
            pLoop->railInUse |= mask;
          }
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
      continue;
    }else{
      assert( pRow->nParent>0 );
      parentRid = pRow->aParent[0];
      pParent = hashFind(p, parentRid);
      if( pParent==0 ){
        pRow->iRail = ++p->mxRail;
        if( p->mxRail>=GR_MAX_RAIL ) return;



        pRow->railInUse = BIT(pRow->iRail);
        continue;
      }
      if( pParent->idx>pRow->idx ){
        /* Common case:  Child occurs after parent and is above the
        ** parent in the timeline */
        pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx,
                                   pParent->iRail, 0);
        if( p->mxRail>=GR_MAX_RAIL ) return;
        pParent->aiRiser[pRow->iRail] = pRow->idx;
      }else{
        /* Timewarp case:  Child occurs earlier in time than parent and
        ** appears below the parent in the timeline. */
        int iDownRail = ++p->mxRail;
        if( iDownRail<1 ) iDownRail = ++p->mxRail;




        pRow->iRail = ++p->mxRail;
        if( p->mxRail>=GR_MAX_RAIL ) return;



        pRow->railInUse = BIT(pRow->iRail);
        pParent->aiRiser[iDownRail] = pRow->idx;
        mask = BIT(iDownRail);
        for(pLoop=p->pFirst; pLoop; pLoop=pLoop->pNext){
          pLoop->railInUse |= mask;
        }
      }







|
>
>
>








|






>
>
>
>

|
>
>
>







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
      continue;
    }else{
      assert( pRow->nParent>0 );
      parentRid = pRow->aParent[0];
      pParent = hashFind(p, parentRid);
      if( pParent==0 ){
        pRow->iRail = ++p->mxRail;
        if( p->mxRail>=GR_MAX_RAIL ){
          pRow->iRail = p->mxRail = GR_MAX_RAIL;
          p->bOverfull = 1;
        }
        pRow->railInUse = BIT(pRow->iRail);
        continue;
      }
      if( pParent->idx>pRow->idx ){
        /* Common case:  Child occurs after parent and is above the
        ** parent in the timeline */
        pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx,
                                   pParent->iRail, 0);
        /* if( p->mxRail>=GR_MAX_RAIL ) return; */
        pParent->aiRiser[pRow->iRail] = pRow->idx;
      }else{
        /* Timewarp case:  Child occurs earlier in time than parent and
        ** appears below the parent in the timeline. */
        int iDownRail = ++p->mxRail;
        if( iDownRail<1 ) iDownRail = ++p->mxRail;
        if( p->mxRail>GR_MAX_RAIL ){
          iDownRail = p->mxRail = GR_MAX_RAIL;
          p->bOverfull = 1;
        }
        pRow->iRail = ++p->mxRail;
        if( p->mxRail>=GR_MAX_RAIL ){
          pRow->iRail = p->mxRail = GR_MAX_RAIL;
          p->bOverfull = 1;
        }
        pRow->railInUse = BIT(pRow->iRail);
        pParent->aiRiser[iDownRail] = pRow->idx;
        mask = BIT(iDownRail);
        for(pLoop=p->pFirst; pLoop; pLoop=pLoop->pNext){
          pLoop->railInUse |= mask;
        }
      }
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
          if( mergeRiserFrom[j]==parentRid ){
            iMrail = j;
            break;
          }
        }
        if( iMrail==-1 ){
          iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0, 1);
          if( p->mxRail>=GR_MAX_RAIL ) return;
          mergeRiserFrom[iMrail] = parentRid;
        }
        iReuseIdx = p->nRow+1;
        iReuseRail = iMrail;
        mask = BIT(iMrail);
        if( i>=pRow->nNonCherrypick ){
          pRow->mergeIn[iMrail] = 2;







|







840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
          if( mergeRiserFrom[j]==parentRid ){
            iMrail = j;
            break;
          }
        }
        if( iMrail==-1 ){
          iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0, 1);
          /*if( p->mxRail>=GR_MAX_RAIL ) return;*/
          mergeRiserFrom[iMrail] = parentRid;
        }
        iReuseIdx = p->nRow+1;
        iReuseRail = iMrail;
        mask = BIT(iMrail);
        if( i>=pRow->nNonCherrypick ){
          pRow->mergeIn[iMrail] = 2;
854
855
856
857
858
859
860
861
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
896
897
898
899
900
901
902
903
904
905
          }else{
            pDesc->hasNormalOutMerge = 1;
            pDesc->mergeUpto = pDesc->idx;
          }
        }else{
          /* Create a new merge for an on-screen node */
          createMergeRiser(p, pDesc, pRow, isCherrypick);
          if( p->mxRail>=GR_MAX_RAIL ) return;
          if( iReuseIdx<0
           && pDesc->nMergeChild==1
           && (pDesc->iRail!=pDesc->mergeOut || pDesc->isLeaf)
          ){
            iReuseIdx = pDesc->idx;
            iReuseRail = pDesc->mergeOut;
          }
        }
      }
    }
  }

  /*
  ** Insert merge rails from primaries to duplicates.
  */
  if( hasDup ){
    int dupRail;
    int mxRail;
    find_max_rail(p);
    mxRail = p->mxRail;
    dupRail = mxRail+1;
    if( p->mxRail>=GR_MAX_RAIL ) return;
    for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
      if( !pRow->isDup ) continue;
      pRow->iRail = dupRail;
      pDesc = hashFind(p, pRow->rid);
      assert( pDesc!=0 && pDesc!=pRow );
      createMergeRiser(p, pDesc, pRow, 0);
      if( pDesc->mergeOut>mxRail ) mxRail = pDesc->mergeOut;
    }
    if( dupRail<=mxRail ){
      dupRail = mxRail+1;
      for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
        if( pRow->isDup ) pRow->iRail = dupRail;
      }
    }
    if( mxRail>=GR_MAX_RAIL ) return;
  }

  /*
  ** Find the maximum rail number.
  */
  find_max_rail(p);








|















|





|














|







872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
          }else{
            pDesc->hasNormalOutMerge = 1;
            pDesc->mergeUpto = pDesc->idx;
          }
        }else{
          /* Create a new merge for an on-screen node */
          createMergeRiser(p, pDesc, pRow, isCherrypick);
          /* if( p->mxRail>=GR_MAX_RAIL ) return; */
          if( iReuseIdx<0
           && pDesc->nMergeChild==1
           && (pDesc->iRail!=pDesc->mergeOut || pDesc->isLeaf)
          ){
            iReuseIdx = pDesc->idx;
            iReuseRail = pDesc->mergeOut;
          }
        }
      }
    }
  }

  /*
  ** Insert merge rails from primaries to duplicates.
  */
  if( hasDup && p->mxRail<GR_MAX_RAIL ){
    int dupRail;
    int mxRail;
    find_max_rail(p);
    mxRail = p->mxRail;
    dupRail = mxRail+1;
    /* if( p->mxRail>=GR_MAX_RAIL ) return; */
    for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
      if( !pRow->isDup ) continue;
      pRow->iRail = dupRail;
      pDesc = hashFind(p, pRow->rid);
      assert( pDesc!=0 && pDesc!=pRow );
      createMergeRiser(p, pDesc, pRow, 0);
      if( pDesc->mergeOut>mxRail ) mxRail = pDesc->mergeOut;
    }
    if( dupRail<=mxRail ){
      dupRail = mxRail+1;
      for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
        if( pRow->isDup ) pRow->iRail = dupRail;
      }
    }
    /* if( mxRail>=GR_MAX_RAIL ) return; */
  }

  /*
  ** Find the maximum rail number.
  */
  find_max_rail(p);

Changes to src/hook.c.
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
/*
** COMMAND: hook*
**
** Usage: %fossil hook COMMAND ...
**
** Commands include:
**
** >  fossil hook add --command COMMAND --type TYPE --sequence NUMBER
**
**        Create a new hook.  The --command and --type arguments are
**        required.  --sequence is optional.
**
** >  fossil hook delete ID ...
**
**        Delete one or more hooks by their IDs.  ID can be "all"
**        to delete all hooks.  Caution:  There is no "undo" for
**        this operation.  Deleted hooks are permanently lost.
**
** >  fossil hook edit --command COMMAND --type TYPE --sequence NUMBER ID ...
**
**        Make changes to one or more existing hooks.  The ID argument
**        is either a hook-id, or a list of hook-ids, or the keyword
**        "all".  For example, to disable hook number 2, use:
**
**            fossil hook edit --type disabled 2
**
** >  fossil hook list
**
**        Show all current hooks
**
** >  fossil hook status
**
**        Print the values of CONFIG table entries that are relevant to
**        hook processing.  Used for debugging.
**
** >  fossil hook test [OPTIONS] ID
**
**        Run the hook script given by ID for testing purposes.
**        Options:
**
**            --dry-run          Print the script on stdout rather than run it
**            --base-rcvid N     Pretend that the hook-last-rcvid value is N
**            --new-rcvid M      Pretend that the last rcvid value is M







|




|





|







|



|




|







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
/*
** COMMAND: hook*
**
** Usage: %fossil hook COMMAND ...
**
** Commands include:
**
** > fossil hook add --command COMMAND --type TYPE --sequence NUMBER
**
**        Create a new hook.  The --command and --type arguments are
**        required.  --sequence is optional.
**
** > fossil hook delete ID ...
**
**        Delete one or more hooks by their IDs.  ID can be "all"
**        to delete all hooks.  Caution:  There is no "undo" for
**        this operation.  Deleted hooks are permanently lost.
**
** > fossil hook edit --command COMMAND --type TYPE --sequence NUMBER ID ...
**
**        Make changes to one or more existing hooks.  The ID argument
**        is either a hook-id, or a list of hook-ids, or the keyword
**        "all".  For example, to disable hook number 2, use:
**
**            fossil hook edit --type disabled 2
**
** > fossil hook list
**
**        Show all current hooks
**
** > fossil hook status
**
**        Print the values of CONFIG table entries that are relevant to
**        hook processing.  Used for debugging.
**
** > fossil hook test [OPTIONS] ID
**
**        Run the hook script given by ID for testing purposes.
**        Options:
**
**            --dry-run          Print the script on stdout rather than run it
**            --base-rcvid N     Pretend that the hook-last-rcvid value is N
**            --new-rcvid M      Pretend that the last rcvid value is M
Changes to src/http.c.
510
511
512
513
514
515
516



517
518
519
520
521
522
523
                  blob_size(&hdr), blob_size(&payload));
  }
  transport_send(&g.url, &hdr);
  transport_send(&g.url, &payload);
  blob_reset(&hdr);
  blob_reset(&payload);
  transport_flip(&g.url);




  /*
  ** Read and interpret the server reply
  */
  closeConnection = 1;
  iLength = -1;
  iHttpVersion = -1;







>
>
>







510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
                  blob_size(&hdr), blob_size(&payload));
  }
  transport_send(&g.url, &hdr);
  transport_send(&g.url, &payload);
  blob_reset(&hdr);
  blob_reset(&payload);
  transport_flip(&g.url);
  if( mHttpFlags & HTTP_VERBOSE ){
    fossil_print("IP-Address: %s\n", g.zIpAddr);
  }

  /*
  ** Read and interpret the server reply
  */
  closeConnection = 1;
  iLength = -1;
  iHttpVersion = -1;
570
571
572
573
574
575
576

577
578
579
580
581
582
583
      }else if( sqlite3_strlike("%keep-alive%", &zLine[11], 0)==0 ){
        closeConnection = 0;
      }
    }else if( ( rc==301 || rc==302 || rc==307 || rc==308 ) &&
                fossil_strnicmp(zLine, "location:", 9)==0 ){
      int i, j;
      int wasHttps;


      if ( --maxRedirect == 0){
        fossil_warning("redirect limit exceeded");
        goto write_err;
      }
      for(i=9; zLine[i] && zLine[i]==' '; i++){}
      if( zLine[i]==0 ){







>







573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
      }else if( sqlite3_strlike("%keep-alive%", &zLine[11], 0)==0 ){
        closeConnection = 0;
      }
    }else if( ( rc==301 || rc==302 || rc==307 || rc==308 ) &&
                fossil_strnicmp(zLine, "location:", 9)==0 ){
      int i, j;
      int wasHttps;
      int priorUrlFlags;

      if ( --maxRedirect == 0){
        fossil_warning("redirect limit exceeded");
        goto write_err;
      }
      for(i=9; zLine[i] && zLine[i]==' '; i++){}
      if( zLine[i]==0 ){
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
      }
      if( g.url.isFile || g.url.isSsh ){
        fossil_warning("cannot redirect from %s to %s", g.url.canonical,
                       &zLine[i]);
        goto write_err;
      }
      wasHttps = g.url.isHttps;

      url_parse(&zLine[i], 0);
      if( wasHttps && !g.url.isHttps ){
        fossil_warning("cannot redirect from HTTPS to HTTP");
        goto write_err;
      }
      if( g.url.isSsh || g.url.isFile ){
        fossil_warning("cannot redirect to %s", &zLine[i]);
        goto write_err;
      }
      transport_close(&g.url);
      transport_global_shutdown(&g.url);
      fSeenHttpAuth = 0;
      if( g.zHttpAuth ) free(g.zHttpAuth);
      g.zHttpAuth = get_httpauth();


      if( rc==301 || rc==308 ) url_remember();

      return http_exchange(pSend, pReply, mHttpFlags,
                           maxRedirect, zAltMimetype);
    }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){
      if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){
        isCompressed = 0;
      }else if( fossil_strnicmp(&zLine[14],
                          "application/x-fossil-uncompressed", -1)==0 ){







>














>
>
|
>







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
      }
      if( g.url.isFile || g.url.isSsh ){
        fossil_warning("cannot redirect from %s to %s", g.url.canonical,
                       &zLine[i]);
        goto write_err;
      }
      wasHttps = g.url.isHttps;
      priorUrlFlags = g.url.flags;
      url_parse(&zLine[i], 0);
      if( wasHttps && !g.url.isHttps ){
        fossil_warning("cannot redirect from HTTPS to HTTP");
        goto write_err;
      }
      if( g.url.isSsh || g.url.isFile ){
        fossil_warning("cannot redirect to %s", &zLine[i]);
        goto write_err;
      }
      transport_close(&g.url);
      transport_global_shutdown(&g.url);
      fSeenHttpAuth = 0;
      if( g.zHttpAuth ) free(g.zHttpAuth);
      g.zHttpAuth = get_httpauth();
      if( (rc==301 || rc==308) && (priorUrlFlags & URL_REMEMBER)!=0 ){
        g.url.flags |= URL_REMEMBER;
        url_remember();
      }
      return http_exchange(pSend, pReply, mHttpFlags,
                           maxRedirect, zAltMimetype);
    }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){
      if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){
        isCompressed = 0;
      }else if( fossil_strnicmp(&zLine[14],
                          "application/x-fossil-uncompressed", -1)==0 ){
791
792
793
794
795
796
797

798
799
800
801
802
803
804
    ssl_disable_cert_verification();
    #endif
  }
  if( find_option("xfer",0,0)!=0 ){
    mHttpFlags |= HTTP_USE_LOGIN;
    mHttpFlags &= ~HTTP_GENERIC;
  }

  verify_all_options();
  if( g.argc<3 || g.argc>5 ){
    usage("URL ?PAYLOAD? ?OUTPUT?");
  }
  zInFile = g.argc>=4 ? g.argv[3] : 0;
  if( g.argc==5 ){
    if( zOutFile ){







>







799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
    ssl_disable_cert_verification();
    #endif
  }
  if( find_option("xfer",0,0)!=0 ){
    mHttpFlags |= HTTP_USE_LOGIN;
    mHttpFlags &= ~HTTP_GENERIC;
  }
  if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
  verify_all_options();
  if( g.argc<3 || g.argc>5 ){
    usage("URL ?PAYLOAD? ?OUTPUT?");
  }
  zInFile = g.argc>=4 ? g.argv[3] : 0;
  if( g.argc==5 ){
    if( zOutFile ){
Changes to src/http_ssl.c.
449
450
451
452
453
454
455







456
457
458
459
460
461
462
463
  const char *zRemoteHost;

  ssl_global_init_client();
  if( pUrlData->useProxy ){
    int rc;
    char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port);
    BIO *sBio = BIO_new_connect(connStr);







    free(connStr);
    if( BIO_do_connect(sBio)<=0 ){
      ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
            pUrlData->name, pUrlData->port,
            ERR_reason_error_string(ERR_get_error()));
      ssl_close_client();
      return 1;
    }







>
>
>
>
>
>
>
|







449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
  const char *zRemoteHost;

  ssl_global_init_client();
  if( pUrlData->useProxy ){
    int rc;
    char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port);
    BIO *sBio = BIO_new_connect(connStr);
    if( g.fIPv4 ){
#ifdef BIO_FAMILY_IPV4
      BIO_set_conn_ip_family(sBio, BIO_FAMILY_IPV4);
#else
      fossil_warning("The --ipv4 option is not supported in this build\n");
#endif
    }
    fossil_free(connStr);
    if( BIO_do_connect(sBio)<=0 ){
      ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
            pUrlData->name, pUrlData->port,
            ERR_reason_error_string(ERR_get_error()));
      ssl_close_client();
      return 1;
    }
501
502
503
504
505
506
507
508







509
510
511
512
513
514
515
    /* SSL_set_verify(ssl, SSL_VERIFY_PEER, 0); */
  }
#endif

  if( !pUrlData->useProxy ){
    char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port);
    BIO_set_conn_hostname(iBio, connStr);
    free(connStr);







    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_client();
      return 1;
    }







|
>
>
>
>
>
>
>







508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
    /* SSL_set_verify(ssl, SSL_VERIFY_PEER, 0); */
  }
#endif

  if( !pUrlData->useProxy ){
    char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port);
    BIO_set_conn_hostname(iBio, connStr);
    fossil_free(connStr);
    if( g.fIPv4 ){
#ifdef BIO_FAMILY_IPV4
      BIO_set_conn_ip_family(iBio, BIO_FAMILY_IPV4);
#else
      fossil_warning("The --ipv4 option is not supported in this build\n");
#endif
    }
    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_client();
      return 1;
    }
908
909
910
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
937
938
939
940
941
942
static void trust_location_usable(const char *zPath, const char **pzStore){
  if( *pzStore!=0 ) return;
  if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath;
}
#endif /* FOSSIL_ENABLE_SSL */

/*
** COMMAND: tls-config*
** COMMAND: ssl-config
**
** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...]
**
** This command is used to view or modify the TLS (Transport Layer
** Security) configuration for Fossil.  TLS (formerly SSL) is the
** encryption technology used for secure HTTPS transport.
**
** Sub-commands:
**
**   remove-exception DOMAINS    Remove TLS cert exceptions for the domains
**                               listed.  Or remove them all if the --all
**                               option is specified.
**
**   scrub ?--force?             Remove all SSL configuration data from the
**                               repository. Use --force to omit the
**                               confirmation.
**
**   show ?-v?                   Show the TLS configuration. Add -v to see
**                               additional explanation
*/
void test_tlsconfig_info(void){
  const char *zCmd;
  size_t nCmd;
  int nHit = 0;

  db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);







|
|









|
|
|

|
|
|

|
|







922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
static void trust_location_usable(const char *zPath, const char **pzStore){
  if( *pzStore!=0 ) return;
  if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath;
}
#endif /* FOSSIL_ENABLE_SSL */

/*
** COMMAND: tls-config*                       abbrv-subcom
** COMMAND: ssl-config                        abbrv-subcom
**
** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...]
**
** This command is used to view or modify the TLS (Transport Layer
** Security) configuration for Fossil.  TLS (formerly SSL) is the
** encryption technology used for secure HTTPS transport.
**
** Sub-commands:
**
**    remove-exception DOMAINS    Remove TLS cert exceptions for the domains
**                                listed.  Or remove them all if the --all
**                                option is specified.
**
**    scrub ?--force?             Remove all SSL configuration data from the
**                                repository. Use --force to omit the
**                                confirmation.
**
**    show ?-v?                   Show the TLS configuration. Add -v to see
**                                additional explanation
*/
void test_tlsconfig_info(void){
  const char *zCmd;
  size_t nCmd;
  int nHit = 0;

  db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
Changes to src/info.c.
255
256
257
258
259
260
261
262
263
264
265
266
267
268


269
270
271
272
273
274
275
      }else{
        z = blob_str(&vx);
      }
      fossil_print("fossil:       %z\n", file_fullexename(g.nameOfExe));
      fossil_print("version:      %s", z);
      blob_reset(&vx);
    }
  }else{
    int rid;
    rid = name_to_rid(g.argv[2]);
    if( rid==0 ){
      fossil_fatal("no such object: %s", g.argv[2]);
    }
    show_common_info(rid, "hash:", 1, 1);


  }
}

/*
** Show the context graph (immediate parents and children) for
** check-in rid and rid2
*/







|






>
>







255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
      }else{
        z = blob_str(&vx);
      }
      fossil_print("fossil:       %z\n", file_fullexename(g.nameOfExe));
      fossil_print("version:      %s", z);
      blob_reset(&vx);
    }
  }else if( g.repositoryOpen ){
    int rid;
    rid = name_to_rid(g.argv[2]);
    if( rid==0 ){
      fossil_fatal("no such object: %s", g.argv[2]);
    }
    show_common_info(rid, "hash:", 1, 1);
  }else{
    fossil_fatal("Could not find or open a Fossil repository");
  }
}

/*
** Show the context graph (immediate parents and children) for
** check-in rid and rid2
*/
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
  zParent = db_text(0,
    "SELECT uuid FROM plink, blob"
    " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
    rid
  );
  isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
  db_prepare(&q1,
     "SELECT uuid, datetime(mtime,toLocal()), user, comment,"
     "       datetime(omtime,toLocal()), mtime"
     "  FROM blob, event"
     " WHERE blob.rid=%d"
     "   AND event.objid=%d",
     rid, rid
  );
  zBrName = branch_of_rid(rid);








|
|







925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
  zParent = db_text(0,
    "SELECT uuid FROM plink, blob"
    " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
    rid
  );
  isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
  db_prepare(&q1,
     "SELECT uuid, datetime(mtime,toLocal(),'subsec'), user, comment,"
     "       datetime(omtime,toLocal(),'subsec'), mtime"
     "  FROM blob, event"
     " WHERE blob.rid=%d"
     "   AND event.objid=%d",
     rid, rid
  );
  zBrName = branch_of_rid(rid);

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
1434
1435
1436
1437
1438
1439
  }
  if( !is_a_version(rid) ){
    webpage_error("Artifact %s is not a check-in.", P(zParam));
    return 0;
  }
  return manifest_get(rid, CFTYPE_MANIFEST, 0);
}

#if 0 /* not used */
/*
** Output a description of a check-in
*/
static void checkin_description(int rid){
  Stmt q;
  db_prepare(&q,
    "SELECT datetime(mtime), coalesce(euser,user),"
    "       coalesce(ecomment,comment), uuid,"
    "      (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref"
    "        WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
    "          AND tagxref.rid=blob.rid AND tagxref.tagtype>0)"
    "  FROM event, blob"
    " WHERE event.objid=%d AND type='ci'"
    "   AND blob.rid=%d",
    rid, rid
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zDate = db_column_text(&q, 0);
    const char *zUser = db_column_text(&q, 1);
    const char *zUuid = db_column_text(&q, 3);
    const char *zTagList = db_column_text(&q, 4);
    Blob comment;
    int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS;
    if( db_get_boolean("timeline-block-markup", 0)==0 ){
      wikiFlags |= WIKI_NOBLOCK;
    }
    hyperlink_to_version(zUuid);
    blob_zero(&comment);
    db_column_blob(&q, 2, &comment);
    wiki_convert(&comment, 0, wikiFlags);
    blob_reset(&comment);
    @ (user:
    hyperlink_to_user(zUser,zDate,",");
    if( zTagList && zTagList[0] && g.perm.Hyperlink ){
      int i;
      const char *z = zTagList;
      Blob links;
      blob_zero(&links);
      while( z && z[0] ){
        for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
        blob_appendf(&links,
              "%z%#h</a>%.2s",
              href("%R/timeline?r=%#t&nd&c=%t",i,z,zDate), i,z, &z[i]
        );
        if( z[i]==0 ) break;
        z += i+2;
      }
      @ tags: %s(blob_str(&links)),
      blob_reset(&links);
    }else{
      @ tags: %h(zTagList),
    }
    @ date:
    hyperlink_to_date(zDate, ")");
    tag_private_status(rid);
  }
  db_finalize(&q);
}
#endif /* not used */


/*
** WEBPAGE: vdiff
** URL: /vdiff?from=TAG&to=TAG
**
** Show the difference between two check-ins identified by the from= and
** to= query parameters.







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







1366
1367
1368
1369
1370
1371
1372






























































1373
1374
1375
1376
1377
1378
1379
  }
  if( !is_a_version(rid) ){
    webpage_error("Artifact %s is not a check-in.", P(zParam));
    return 0;
  }
  return manifest_get(rid, CFTYPE_MANIFEST, 0);
}































































/*
** WEBPAGE: vdiff
** URL: /vdiff?from=TAG&to=TAG
**
** Show the difference between two check-ins identified by the from= and
** to= query parameters.
2680
2681
2682
2683
2684
2685
2686

2687
2688
2689
2690
2691
2692

2693
2694
2695
2696
2697
2698
2699
2700


2701
2702
2703
2704
2705
2706
2707

2708
2709
2710
2711
2712
2713
2714
  fossil_print("%b\n", cgi_output_blob());
}

/*
** WEBPAGE: artifact
** WEBPAGE: file
** WEBPAGE: whatis

**
** Typical usage:
**
**    /artifact/HASH
**    /whatis/HASH
**    /file/NAME

**
** Additional query parameters:
**
**   ln              - show line numbers
**   ln=N            - highlight line number N
**   ln=M-N          - highlight lines M through N inclusive
**   ln=M-N+Y-Z      - highlight lines M through N and Y through Z (inclusive)
**   verbose         - show more detail in the description


**   download        - redirect to the download (artifact page only)
**   name=NAME       - filename or hash as a query parameter
**   filename=NAME   - alternative spelling for "name="
**   fn=NAME         - alternative spelling for "name="
**   ci=VERSION      - The specific check-in to use with "name=" to
**                     identify the file.
**   txt             - Force display of unformatted source text

**
** The /artifact page show the complete content of a file
** identified by HASH.  The /whatis page shows only a description
** of how the artifact is used.  The /file page shows the most recent
** version of the file or directory called NAME, or a list of the
** top-level directory if NAME is omitted.
**







>






>








>
>







>







2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
  fossil_print("%b\n", cgi_output_blob());
}

/*
** WEBPAGE: artifact
** WEBPAGE: file
** WEBPAGE: whatis
** WEBPAGE: docfile
**
** Typical usage:
**
**    /artifact/HASH
**    /whatis/HASH
**    /file/NAME
**    /docfile/NAME
**
** Additional query parameters:
**
**   ln              - show line numbers
**   ln=N            - highlight line number N
**   ln=M-N          - highlight lines M through N inclusive
**   ln=M-N+Y-Z      - highlight lines M through N and Y through Z (inclusive)
**   verbose         - show more detail in the description
**   brief           - show just the document, not the metadata.  The
**                     /docfile page is an alias for /file?brief
**   download        - redirect to the download (artifact page only)
**   name=NAME       - filename or hash as a query parameter
**   filename=NAME   - alternative spelling for "name="
**   fn=NAME         - alternative spelling for "name="
**   ci=VERSION      - The specific check-in to use with "name=" to
**                     identify the file.
**   txt             - Force display of unformatted source text
**   hash            - Output only the hash of the artifact
**
** The /artifact page show the complete content of a file
** identified by HASH.  The /whatis page shows only a description
** of how the artifact is used.  The /file page shows the most recent
** version of the file or directory called NAME, or a list of the
** top-level directory if NAME is omitted.
**
2723
2724
2725
2726
2727
2728
2729

2730
2731
2732
2733
2734
2735
2736
2737


2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751




2752
2753
2754
2755
2756
2757
2758
** a default value of "tip" is used for ci= if ci= is omitted.
*/
void artifact_page(void){
  int rid = 0;
  Blob content;
  const char *zMime;
  Blob downloadName;

  int renderAsWiki = 0;
  int renderAsHtml = 0;
  int renderAsSvg = 0;
  int objType;
  int asText;
  const char *zUuid = 0;
  u32 objdescFlags = OBJDESC_BASE;
  int descOnly = fossil_strcmp(g.zPath,"whatis")==0;


  int isFile = fossil_strcmp(g.zPath,"file")==0;
  const char *zLn = P("ln");
  const char *zName = P("name");
  const char *zCI = P("ci");
  HQuery url;
  char *zCIUuid = 0;
  int isSymbolicCI = 0;  /* ci= exists and is a symbolic name, not a hash */
  int isBranchCI = 0;    /* ci= refers to a branch name */
  char *zHeader = 0;

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  cgi_check_for_malice();
  style_set_current_feature("artifact");





  /* Capture and normalize the name= and ci= query parameters */
  if( zName==0 ){
    zName = P("filename");
    if( zName==0 ){
      zName = P("fn");
    }







>








>
>














>
>
>
>







2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
** a default value of "tip" is used for ci= if ci= is omitted.
*/
void artifact_page(void){
  int rid = 0;
  Blob content;
  const char *zMime;
  Blob downloadName;
  Blob uuid;
  int renderAsWiki = 0;
  int renderAsHtml = 0;
  int renderAsSvg = 0;
  int objType;
  int asText;
  const char *zUuid = 0;
  u32 objdescFlags = OBJDESC_BASE;
  int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
  int hashOnly = P("hash")!=0;
  int docOnly = P("brief")!=0;
  int isFile = fossil_strcmp(g.zPath,"file")==0;
  const char *zLn = P("ln");
  const char *zName = P("name");
  const char *zCI = P("ci");
  HQuery url;
  char *zCIUuid = 0;
  int isSymbolicCI = 0;  /* ci= exists and is a symbolic name, not a hash */
  int isBranchCI = 0;    /* ci= refers to a branch name */
  char *zHeader = 0;

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  cgi_check_for_malice();
  style_set_current_feature("artifact");
  if( fossil_strcmp(g.zPath, "docfile")==0 ){
    isFile = 1;
    docOnly = 1;
  }

  /* Capture and normalize the name= and ci= query parameters */
  if( zName==0 ){
    zName = P("filename");
    if( zName==0 ){
      zName = P("fn");
    }
2847
2848
2849
2850
2851
2852
2853







2854
2855
2856


2857
2858
2859
2860
2861
2862
2863
2864

  if( descOnly || P("verbose")!=0 ){
    url_add_parameter(&url, "verbose", "1");
    objdescFlags |= OBJDESC_DETAIL;
  }
  zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
  etag_check(ETAG_HASH, zUuid);








  asText = P("txt")!=0;
  if( isFile ){


    if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
      zCI = "tip";
      @ <h2>File %z(href("%R/finfo?name=%T&m&ci=tip",zName))%h(zName)</a>
      @ from the %z(href("%R/info/tip"))latest check-in</a></h2>
    }else{
      const char *zPath;
      Blob path;
      blob_zero(&path);







>
>
>
>
>
>
>



>
>
|







2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825

  if( descOnly || P("verbose")!=0 ){
    url_add_parameter(&url, "verbose", "1");
    objdescFlags |= OBJDESC_DETAIL;
  }
  zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
  etag_check(ETAG_HASH, zUuid);

  if( descOnly && hashOnly ){
    blob_set(&uuid, zUuid);
    cgi_set_content_type("text/plain");
    cgi_set_content(&uuid);
    return;
  }

  asText = P("txt")!=0;
  if( isFile ){
    if( docOnly ){
      /* No header */
    }else if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
      zCI = "tip";
      @ <h2>File %z(href("%R/finfo?name=%T&m&ci=tip",zName))%h(zName)</a>
      @ from the %z(href("%R/info/tip"))latest check-in</a></h2>
    }else{
      const char *zPath;
      Blob path;
      blob_zero(&path);
2872
2873
2874
2875
2876
2877
2878
2879
2880


2881
2882
2883
2884
2885

2886
2887
2888
2889
2890
2891
2892
      }else if( isSymbolicCI ){
        @ part of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2>
      }else{
        @ part of check-in %z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a></h2>
      }
      blob_reset(&path);
    }
    style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
    zMime = mimetype_from_name(zName);


    style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
                          zName, zCI);
    style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
                          zName, zCI);
    style_submenu_element("Doc", "%R/doc/%T/%T", zCI, zName);

    blob_init(&downloadName, zName, -1);
    objType = OBJTYPE_CONTENT;
  }else{
    @ <h2>Artifact
    style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
    if( g.perm.Setup ){
      @  (%d(rid)):</h2>







<

>
>
|
|
|
|
|
>







2833
2834
2835
2836
2837
2838
2839

2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
      }else if( isSymbolicCI ){
        @ part of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2>
      }else{
        @ part of check-in %z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a></h2>
      }
      blob_reset(&path);
    }

    zMime = mimetype_from_name(zName);
    if( !docOnly ){
      style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
      style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
                            zName, zCI);
      style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
                            zName, zCI);
      style_submenu_element("Doc", "%R/doc/%T/%T", zCI, zName);
    }
    blob_init(&downloadName, zName, -1);
    objType = OBJTYPE_CONTENT;
  }else{
    @ <h2>Artifact
    style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
    if( g.perm.Setup ){
      @  (%d(rid)):</h2>
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
  }
  if( !descOnly && P("download")!=0 ){
    cgi_redirectf("%R/raw/%s?at=%T",
          db_text("x", "SELECT uuid FROM blob WHERE rid=%d", rid),
          file_tail(blob_str(&downloadName)));
    /*NOTREACHED*/
  }
  if( g.perm.Admin ){
    const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
    if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){
      style_submenu_element("Unshun", "%R/shun?accept=%s&sub=1#accshun", zUuid);
    }else{
      style_submenu_element("Shun", "%R/shun?shun=%s#addshun",zUuid);
    }
  }







|







2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
  }
  if( !descOnly && P("download")!=0 ){
    cgi_redirectf("%R/raw/%s?at=%T",
          db_text("x", "SELECT uuid FROM blob WHERE rid=%d", rid),
          file_tail(blob_str(&downloadName)));
    /*NOTREACHED*/
  }
  if( g.perm.Admin && !docOnly ){
    const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
    if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){
      style_submenu_element("Unshun", "%R/shun?accept=%s&sub=1#accshun", zUuid);
    }else{
      style_submenu_element("Shun", "%R/shun?shun=%s#addshun",zUuid);
    }
  }
2946
2947
2948
2949
2950
2951
2952

2953
2954
2955

2956
2957
2958
2959
2960
2961
2962

2963

2964
2965
2966
2967
2968
2969
2970
2971
2972

2973

2974
2975
2976
2977
2978
2979

2980
2981
2982

2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994

2995

2996
2997
2998
2999
3000
3001
3002
      const char *zUser = db_column_text(&q,0);
      const char *zDate = db_column_text(&q,1);
      const char *zIp = db_column_text(&q,2);
      @ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
    }
    db_finalize(&q);
  }

  style_submenu_element("Download", "%R/raw/%s?at=%T", zUuid, file_tail(zName));
  if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
    style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid);

  }
  if( zMime ){
    if( fossil_strcmp(zMime, "text/html")==0 ){
      if( asText ){
        style_submenu_element("Html", "%s", url_render(&url, "txt", 0, 0, 0));
      }else{
        renderAsHtml = 1;

        style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0));

      }
    }else if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0
           || fossil_strcmp(zMime, "text/x-markdown")==0
           || fossil_strcmp(zMime, "text/x-pikchr")==0 ){
      if( asText ){
        style_submenu_element(zMime[7]=='p' ? "Pikchr" : "Wiki",
                              "%s", url_render(&url, "txt", 0, 0, 0));
      }else{
        renderAsWiki = 1;

        style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0));

      }
    }else if( fossil_strcmp(zMime, "image/svg+xml")==0 ){
      if( asText ){
        style_submenu_element("Svg", "%s", url_render(&url, "txt", 0, 0, 0));
      }else{
        renderAsSvg = 1;

        style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0));
      }
    }

    if( fileedit_is_editable(zName) ){
      style_submenu_element("Edit",
                            "%R/fileedit?filename=%T&checkin=%!S",
                            zName, zCI);
    }
  }
  if( (objType & (OBJTYPE_WIKI|OBJTYPE_TICKET))!=0 ){
    style_submenu_element("Parsed", "%R/info/%s", zUuid);
  }
  if( descOnly ){
    style_submenu_element("Content", "%R/artifact/%s", zUuid);
  }else{

    @ <hr>

    content_get(rid, &content);
    if( renderAsWiki ){
      safe_html_context(DOCSRC_FILE);
      wiki_render_by_mimetype(&content, zMime);
      document_emit_js();
    }else if( renderAsHtml ){
      @ <iframe src="%R/raw/%s(zUuid)"







>
|
|
|
>







>
|
>









>
|
>






>
|
|
|
>
|











>
|
>







2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
      const char *zUser = db_column_text(&q,0);
      const char *zDate = db_column_text(&q,1);
      const char *zIp = db_column_text(&q,2);
      @ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
    }
    db_finalize(&q);
  }
  if( !docOnly ){
    style_submenu_element("Download", "%R/raw/%s?at=%T",zUuid,file_tail(zName));
    if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
      style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid);
    }
  }
  if( zMime ){
    if( fossil_strcmp(zMime, "text/html")==0 ){
      if( asText ){
        style_submenu_element("Html", "%s", url_render(&url, "txt", 0, 0, 0));
      }else{
        renderAsHtml = 1;
        if( !docOnly ){
          style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0));
        }
      }
    }else if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0
           || fossil_strcmp(zMime, "text/x-markdown")==0
           || fossil_strcmp(zMime, "text/x-pikchr")==0 ){
      if( asText ){
        style_submenu_element(zMime[7]=='p' ? "Pikchr" : "Wiki",
                              "%s", url_render(&url, "txt", 0, 0, 0));
      }else{
        renderAsWiki = 1;
        if( !docOnly ){
          style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0));
        }
      }
    }else if( fossil_strcmp(zMime, "image/svg+xml")==0 ){
      if( asText ){
        style_submenu_element("Svg", "%s", url_render(&url, "txt", 0, 0, 0));
      }else{
        renderAsSvg = 1;
        if( !docOnly ){
          style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0));
        }
      }
    }
    if( !docOnly && fileedit_is_editable(zName) ){
      style_submenu_element("Edit",
                            "%R/fileedit?filename=%T&checkin=%!S",
                            zName, zCI);
    }
  }
  if( (objType & (OBJTYPE_WIKI|OBJTYPE_TICKET))!=0 ){
    style_submenu_element("Parsed", "%R/info/%s", zUuid);
  }
  if( descOnly ){
    style_submenu_element("Content", "%R/artifact/%s", zUuid);
  }else{
    if( !docOnly || !isFile ){
      @ <hr>
    }
    content_get(rid, &content);
    if( renderAsWiki ){
      safe_html_context(DOCSRC_FILE);
      wiki_render_by_mimetype(&content, zMime);
      document_emit_js();
    }else if( renderAsHtml ){
      @ <iframe src="%R/raw/%s(zUuid)"
3658
3659
3660
3661
3662
3663
3664

3665
3666
3667
3668
3669
3670
3671
                              rid, TAG_BGCOLOR)==2;
  fNewPropagateColor = P("clr")!=0 ? P("pclr")!=0 : fPropagateColor;
  zNewColorFlag = P("newclr") ? " checked" : "";
  zNewTagFlag = P("newtag") ? " checked" : "";
  zNewTag = PDT("tagname","");
  zNewBrFlag = P("newbr") ? " checked" : "";
  zNewBranch = PDT("brname","");

  zCloseFlag = P("close") ? " checked" : "";
  zHideFlag = P("hide") ? " checked" : "";
  if( P("apply") && cgi_csrf_safe(2) ){
    Blob ctrl;
    char *zNow;

    blob_zero(&ctrl);







>







3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
                              rid, TAG_BGCOLOR)==2;
  fNewPropagateColor = P("clr")!=0 ? P("pclr")!=0 : fPropagateColor;
  zNewColorFlag = P("newclr") ? " checked" : "";
  zNewTagFlag = P("newtag") ? " checked" : "";
  zNewTag = PDT("tagname","");
  zNewBrFlag = P("newbr") ? " checked" : "";
  zNewBranch = PDT("brname","");
  zBranchName = branch_of_rid(rid);
  zCloseFlag = P("close") ? " checked" : "";
  zHideFlag = P("hide") ? " checked" : "";
  if( P("apply") && cgi_csrf_safe(2) ){
    Blob ctrl;
    char *zNow;

    blob_zero(&ctrl);
3705
3706
3707
3708
3709
3710
3711






3712
3713
3714
3715
3716
3717


3718
3719
3720
3721
3722
3723
3724
3725
  blob_zero(&comment);
  blob_append(&comment, zNewComment, -1);
  zUuid[10] = 0;
  style_header("Edit Check-in [%s]", zUuid);
  if( P("preview") ){
    Blob suffix;
    int nTag = 0;






    @ <b>Preview:</b>
    @ <blockquote>
    @ <table border=0>
    if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){
      @ <tr><td style="background-color: %h(zNewColor);">
    }else if( zColor[0] ){


      @ <tr><td style="background-color: %h(zColor);">
    }else{
      @ <tr><td>
    }
    @ %!W(blob_str(&comment))
    blob_zero(&suffix);
    blob_appendf(&suffix, "(user: %h", zNewUser);
    db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"







>
>
>
>
>
>




|

>
>
|







3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
  blob_zero(&comment);
  blob_append(&comment, zNewComment, -1);
  zUuid[10] = 0;
  style_header("Edit Check-in [%s]", zUuid);
  if( P("preview") ){
    Blob suffix;
    int nTag = 0;
    const char *zDplyBr;   /* Branch name used to determine BG color */
    if( zNewBrFlag[0] && zNewBranch[0] ){
      zDplyBr = zNewBranch;
    }else{
      zDplyBr = zBranchName;
    }
    @ <b>Preview:</b>
    @ <blockquote>
    @ <table border=0>
    if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){
      @ <tr><td style="background-color:%h(reasonable_bg_color(zNewColor,0));">
    }else if( zColor[0] ){
      @ <tr><td style="background-color:%h(reasonable_bg_color(zColor,0));">
    }else if( zDplyBr && fossil_strcmp(zDplyBr,"trunk")!=0 ){
      @ <tr><td style="background-color:%h(hash_color(zDplyBr));">
    }else{
      @ <tr><td>
    }
    @ %!W(blob_str(&comment))
    blob_zero(&suffix);
    blob_appendf(&suffix, "(user: %h", zNewUser);
    db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
  @ </td></tr>

  @ <tr><th align="right" valign="top">Tags:</th>
  @ <td valign="top">
  @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag)>
  @ Add the following new tag name to this check-in:</label>
  @ <input size="15" name="tagname" id="tagname" value="%h(zNewTag)">
  zBranchName = db_text(0, "SELECT value FROM tagxref, tag"
     " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
     " AND tagxref.tagid=%d", rid, TAG_BRANCH);
  db_prepare(&q,
     "SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag"
     " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
     " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
     "               ELSE tagname END /*sort*/",
     rid
  );







<
<
<







3778
3779
3780
3781
3782
3783
3784



3785
3786
3787
3788
3789
3790
3791
  @ </td></tr>

  @ <tr><th align="right" valign="top">Tags:</th>
  @ <td valign="top">
  @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag)>
  @ Add the following new tag name to this check-in:</label>
  @ <input size="15" name="tagname" id="tagname" value="%h(zNewTag)">



  db_prepare(&q,
     "SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag"
     " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
     " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
     "               ELSE tagname END /*sort*/",
     rid
  );
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951

3952
3953
3954

3955


3956
3957

3958

3959
3960
3961
3962
3963
3964
3965
3966
3967
** COMMAND: amend
**
** Usage: %fossil amend HASH OPTION ?OPTION ...?
**
** Amend the tags on check-in HASH to change how it displays in the timeline.
**
** 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           Rename branch of check-in to NAME


**    --hide                  Hide branch starting from this check-in
**    --close                 Mark this "leaf" as closed

**    -n|--dry-run            Print control artifact, but make no changes

**    --date-override DATETIME  Set the change time on the control artifact
**    --user-override USER      Set the user name on the control artifact
**
** 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.
*/







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







3918
3919
3920
3921
3922
3923
3924
3925




3926
3927
3928

3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
** COMMAND: amend
**
** Usage: %fossil amend HASH OPTION ?OPTION ...?
**
** Amend the tags on check-in HASH to change how it displays in the timeline.
**
** Options:
**    --author USER              Make USER the author for check-in




**    --bgcolor COLOR            Apply COLOR to this check-in
**    --branch NAME              Rename branch of check-in to NAME
**    --branchcolor COLOR        Apply and propagate COLOR to the branch

**    --cancel TAG               Cancel TAG from this check-in
**    --close                    Mark this "leaf" as closed
**    --date DATETIME            Make DATETIME the check-in time
**    --date-override DATETIME   Set the change time on the control artifact
**    -e|--edit-comment          Launch editor to revise comment
**    --hide                     Hide branch starting from this check-in
**    -m|--comment COMMENT       Make COMMENT the check-in comment
**    -M|--message-file FILE     Read the amended comment from FILE
**    -n|--dry-run               Print control artifact, but make no changes
**    --no-verify-comment        Do not validate the check-in comment
**    --tag TAG                  Add new TAG to this check-in
**    --user-override USER       Set the user name on the control artifact
**
** 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.
*/
3984
3985
3986
3987
3988
3989
3990

3991
3992
3993
3994
3995
3996
3997
3998
3999


4000
4001
4002
4003
4004
4005
4006
  int fHide;                    /* True if branch should be hidden */
  int fPropagateColor;          /* True if color propagates before amend */
  int fNewPropagateColor = 0;   /* True if color propagates after amend */
  int fHasHidden = 0;           /* True if hidden tag already set */
  int fHasClosed = 0;           /* True if closed tag already set */
  int fEditComment;             /* True if editor to be used for comment */
  int fDryRun;                  /* Print control artifact, make no changes */

  const char *zChngTime;        /* The change time on the control artifact */
  const char *zUserOvrd;        /* The user name on the control artifact */
  const char *zUuid;
  Blob ctrl;
  Blob comment;
  char *zNow;
  int nTags, nCancels;
  int i;
  Stmt q;



  fEditComment = find_option("edit-comment","e",0)!=0;
  zNewComment = find_option("comment","m",1);
  zComFile = find_option("message-file","M",1);
  zNewBranch = find_option("branch",0,1);
  zNewColor = find_option("bgcolor",0,1);
  zNewBrColor = find_option("branchcolor",0,1);







>









>
>







3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
  int fHide;                    /* True if branch should be hidden */
  int fPropagateColor;          /* True if color propagates before amend */
  int fNewPropagateColor = 0;   /* True if color propagates after amend */
  int fHasHidden = 0;           /* True if hidden tag already set */
  int fHasClosed = 0;           /* True if closed tag already set */
  int fEditComment;             /* True if editor to be used for comment */
  int fDryRun;                  /* Print control artifact, make no changes */
  int noVerifyCom = 0;          /* Allow suspicious check-in comments */
  const char *zChngTime;        /* The change time on the control artifact */
  const char *zUserOvrd;        /* The user name on the control artifact */
  const char *zUuid;
  Blob ctrl;
  Blob comment;
  char *zNow;
  int nTags, nCancels;
  int i;
  Stmt q;
  int ckComFlgs;                /* Flags passed to verify_comment() */


  fEditComment = find_option("edit-comment","e",0)!=0;
  zNewComment = find_option("comment","m",1);
  zComFile = find_option("message-file","M",1);
  zNewBranch = find_option("branch",0,1);
  zNewColor = find_option("bgcolor",0,1);
  zNewBrColor = find_option("branchcolor",0,1);
4014
4015
4016
4017
4018
4019
4020

4021
4022
4023
4024
4025
4026
4027
  pzCancelTags = find_repeatable_option("cancel",0,&nCancels);
  fClose = find_option("close",0,0)!=0;
  fHide = find_option("hide",0,0)!=0;
  fDryRun = find_option("dry-run","n",0)!=0;
  zChngTime = find_option("date-override",0,1);
  if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1);
  zUserOvrd = find_option("user-override",0,1);

  db_find_and_open_repository(0,0);
  user_select();
  verify_all_options();
  if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
  rid = name_to_typed_rid(g.argv[2], "ci");
  if( rid==0 && !is_a_version(rid) ) fossil_fatal("no such check-in");
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);







>







3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
  pzCancelTags = find_repeatable_option("cancel",0,&nCancels);
  fClose = find_option("close",0,0)!=0;
  fHide = find_option("hide",0,0)!=0;
  fDryRun = find_option("dry-run","n",0)!=0;
  zChngTime = find_option("date-override",0,1);
  if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1);
  zUserOvrd = find_option("user-override",0,1);
  noVerifyCom = find_option("no-verify-comment",0,0)!=0;
  db_find_and_open_repository(0,0);
  user_select();
  verify_all_options();
  if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
  rid = name_to_typed_rid(g.argv[2], "ci");
  if( rid==0 && !is_a_version(rid) ) fossil_fatal("no such check-in");
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
4072
4073
4074
4075
4076
4077
4078
















4079
4080
4081
4082
4083
4084
4085
4086

4087
4088
4089

































4090
4091
4092
4093
4094
4095
4096
      ),
      fNewPropagateColor
    );
  }
  if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){
    cancel_color();
  }
















  if( fEditComment ){
    prepare_amend_comment(&comment, zComment, zUuid);
    zNewComment = blob_str(&comment);
  }else if( zComFile ){
    blob_zero(&comment);
    blob_read_from_file(&comment, zComFile, ExtFILE);
    blob_to_utf8_no_bom(&comment, 1);
    zNewComment = blob_str(&comment);

  }
  if( zNewComment && zNewComment[0]
      && comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment);

































  if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){
    if( is_datetime(zNewDate) ){
      add_date(zNewDate);
    }else{
      fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS");
    }
  }







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







4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080

4081

4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
      ),
      fNewPropagateColor
    );
  }
  if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){
    cancel_color();
  }
  if( fEditComment || zNewComment || zComFile ){
    blob_init(&comment, 0, 0);

    /* Figure out how much comment verification is requested */
    if( noVerifyCom ){
      ckComFlgs = 0;
    }else{
      const char *zVerComs = db_get("verify-comments","on");
      if( is_false(zVerComs) ){
        ckComFlgs = 0;
      }else if( strcmp(zVerComs,"preview")==0 ){
        ckComFlgs = COMCK_PREVIEW | COMCK_MARKUP;
      }else{
        ckComFlgs = COMCK_MARKUP;
      }
    }
    if( fEditComment ){
      prepare_amend_comment(&comment, zComment, zUuid);

    }else if( zComFile ){

      blob_read_from_file(&comment, zComFile, ExtFILE);
      blob_to_utf8_no_bom(&comment, 1);
    }else if( zNewComment ){
      blob_init(&comment, zNewComment, -1);
    }
    if( blob_size(&comment)>0
     && comment_compare(zComment, blob_str(&comment))==0
    ){
      int rc;
      while( (rc = verify_comment(&comment, ckComFlgs))!=0 ){
        char cReply;
        Blob ans;
        if( !fEditComment ){
          fossil_fatal("Amend aborted; "
                       "use --no-verify-comment to override");
        }
        if( rc==COMCK_PREVIEW ){
          prompt_user("Continue, abort, or edit (C/a/e)? ", &ans);
        }else{
          prompt_user("Edit, abort, or continue (E/a/c)? ", &ans);
        }
        cReply = blob_str(&ans)[0];
        cReply = fossil_tolower(cReply);
        blob_reset(&ans);
        if( cReply=='a' ){
          fossil_fatal("Amend aborted.");
        }
        if( cReply=='e' || (cReply!='c' && rc!=COMCK_PREVIEW) ){
          char *zPrior = blob_materialize(&comment);
          blob_init(&comment, 0, 0);
          prepare_amend_comment(&comment, zPrior, zUuid);
          fossil_free(zPrior);
          continue;
        }else{
          break;
        }
      }
    }
    add_comment(blob_str(&comment));
  }
  if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){
    if( is_datetime(zNewDate) ){
      add_date(zNewDate);
    }else{
      fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS");
    }
  }
Changes to src/interwiki.c.
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
** COMMAND: interwiki*
**
** Usage: %fossil interwiki COMMAND ...
**
** Manage the "intermap" that defines the mapping from interwiki tags
** to complete URLs for interwiki links.
**
** >  fossil interwiki delete TAG ...
**
**        Delete one or more interwiki maps.
**
** >  fossil interwiki edit TAG --base URL --hash PATH --wiki PATH
**
**        Create an interwiki referenced call TAG.  The base URL is
**        the --base option, which is required.  The --hash and --wiki
**        paths are optional.  The TAG must be lower-case alphanumeric
**        and must be unique.  A new entry is created if it does not
**        already exit.
**
** >  fossil interwiki list
**
**        Show all interwiki mappings.
*/
void interwiki_cmd(void){
  const char *zCmd;
  int nCmd;
  db_find_and_open_repository(0, 0);







|



|







|







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
** COMMAND: interwiki*
**
** Usage: %fossil interwiki COMMAND ...
**
** Manage the "intermap" that defines the mapping from interwiki tags
** to complete URLs for interwiki links.
**
** > fossil interwiki delete TAG ...
**
**        Delete one or more interwiki maps.
**
** > fossil interwiki edit TAG --base URL --hash PATH --wiki PATH
**
**        Create an interwiki referenced call TAG.  The base URL is
**        the --base option, which is required.  The --hash and --wiki
**        paths are optional.  The TAG must be lower-case alphanumeric
**        and must be unique.  A new entry is created if it does not
**        already exit.
**
** > fossil interwiki list
**
**        Show all interwiki mappings.
*/
void interwiki_cmd(void){
  const char *zCmd;
  int nCmd;
  db_find_and_open_repository(0, 0);
Changes to src/json_config.c.
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
{ "default-skin",           CONFIGSET_SKIN },
{ "logo-mimetype",          CONFIGSET_SKIN },
{ "logo-image",             CONFIGSET_SKIN },
{ "background-mimetype",    CONFIGSET_SKIN },
{ "background-image",       CONFIGSET_SKIN },
{ "icon-mimetype",          CONFIGSET_SKIN },
{ "icon-image",             CONFIGSET_SKIN },
{ "timeline-block-markup",  CONFIGSET_SKIN },
{ "timeline-date-format",   CONFIGSET_SKIN },
{ "timeline-default-style", CONFIGSET_SKIN },
{ "timeline-dwelltime",     CONFIGSET_SKIN },
{ "timeline-closetime",     CONFIGSET_SKIN },
{ "timeline-hard-newlines", CONFIGSET_SKIN },
{ "timeline-max-comment",   CONFIGSET_SKIN },
{ "timeline-plaintext",     CONFIGSET_SKIN },







<







84
85
86
87
88
89
90

91
92
93
94
95
96
97
{ "default-skin",           CONFIGSET_SKIN },
{ "logo-mimetype",          CONFIGSET_SKIN },
{ "logo-image",             CONFIGSET_SKIN },
{ "background-mimetype",    CONFIGSET_SKIN },
{ "background-image",       CONFIGSET_SKIN },
{ "icon-mimetype",          CONFIGSET_SKIN },
{ "icon-image",             CONFIGSET_SKIN },

{ "timeline-date-format",   CONFIGSET_SKIN },
{ "timeline-default-style", CONFIGSET_SKIN },
{ "timeline-dwelltime",     CONFIGSET_SKIN },
{ "timeline-closetime",     CONFIGSET_SKIN },
{ "timeline-hard-newlines", CONFIGSET_SKIN },
{ "timeline-max-comment",   CONFIGSET_SKIN },
{ "timeline-plaintext",     CONFIGSET_SKIN },
Changes to src/main.c.
637
638
639
640
641
642
643
644
645

646
647

648
649
650
651
652
653
654
    }
  }
  fossil_warning("%s", blob_str(&msg));
  blob_reset(&msg);
}

/*
** 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)
** containing option-settable bitwise flag fields must be initialized.

*/
static void fossil_init_flags_from_options(void){
  const char *zValue = find_option("comfmtflags", 0, 1);
  if( zValue==0 ){
    zValue = find_option("comment-format", 0, 1);
  }
  if( zValue ){







<
|
>
|
|
>







637
638
639
640
641
642
643

644
645
646
647
648
649
650
651
652
653
654
655
    }
  }
  fossil_warning("%s", blob_str(&msg));
  blob_reset(&msg);
}

/*

** Initialize the g.comFmtFlags global variable.
**
** Global command-line options --comfmtflags or --comment-format can be
** used for this.  However, those command-line options are undocumented
** and deprecated.   They are here for backwards compatibility only.
*/
static void fossil_init_flags_from_options(void){
  const char *zValue = find_option("comfmtflags", 0, 1);
  if( zValue==0 ){
    zValue = find_option("comment-format", 0, 1);
  }
  if( zValue ){
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740

  fossil_printf_selfcheck();
  fossil_limit_memory(1);

  /* When updating the minimum SQLite version, change the number here,
  ** and also MINIMUM_SQLITE_VERSION value set in ../auto.def.  Take
  ** care that both places agree! */
  if( sqlite3_libversion_number()<3046000
   || strncmp(sqlite3_sourceid(),"2024-08-16",10)<0
  ){
    fossil_panic("Unsuitable SQLite version %s, must be at least 3.43.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);







|
|

|







724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741

  fossil_printf_selfcheck();
  fossil_limit_memory(1);

  /* When updating the minimum SQLite version, change the number here,
  ** and also MINIMUM_SQLITE_VERSION value set in ../auto.def.  Take
  ** care that both places agree! */
  if( sqlite3_libversion_number()<3049000
   || strncmp(sqlite3_sourceid(),"2025-02-06",10)<0
  ){
    fossil_panic("Unsuitable SQLite version %s, must be at least 3.49.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);
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
  fossil_fatal("Usage: %s %s %s", g.argv[0], g.argv[1], zFormat);
}

/*
** Remove n elements from g.argv beginning with the i-th element.
*/
static void remove_from_argv(int i, int n){
  int j;
  for(j=i+n; j<g.argc; i++, j++){
    g.argv[i] = g.argv[j];
  }
  g.argc = i;
}


/*
** Look for a command-line option.  If present, remove it from the
** argument list and return a pointer to either the flag's name (if
** hasArg==0), sans leading - or --, or its value (if hasArg==1).







<
<
|
<
|







996
997
998
999
1000
1001
1002


1003

1004
1005
1006
1007
1008
1009
1010
1011
  fossil_fatal("Usage: %s %s %s", g.argv[0], g.argv[1], zFormat);
}

/*
** Remove n elements from g.argv beginning with the i-th element.
*/
static void remove_from_argv(int i, int n){


  memmove(&g.argv[i], &g.argv[i+n], sizeof(g.argv[i])*(g.argc-i-n));

  g.argc -= n;
}


/*
** Look for a command-line option.  If present, remove it from the
** argument list and return a pointer to either the flag's name (if
** hasArg==0), sans leading - or --, or its value (if hasArg==1).
1062
1063
1064
1065
1066
1067
1068









1069
1070
1071
1072
1073
1074
1075
      zReturn = g.argv[i+hasArg];
      remove_from_argv(i, 1+hasArg);
      break;
    }
  }
  return zReturn;
}










/* Return true if zOption exists in the command-line arguments,
** but do not remove it from the list or otherwise process it.
*/
int has_option(const char *zOption){
  int i;
  int n = (int)strlen(zOption);







>
>
>
>
>
>
>
>
>







1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
      zReturn = g.argv[i+hasArg];
      remove_from_argv(i, 1+hasArg);
      break;
    }
  }
  return zReturn;
}

/*
** Restore an option previously removed by find_option().
*/
void restore_option(const char *zName, const char *zValue, int hasOpt){
  if( zValue==0 && hasOpt ) return;
  g.argv[g.argc++] = (char*)zName;
  if( hasOpt ) g.argv[g.argc++] = (char*)zValue;
}

/* Return true if zOption exists in the command-line arguments,
** but do not remove it from the list or otherwise process it.
*/
int has_option(const char *zOption){
  int i;
  int n = (int)strlen(zOption);
1846
1847
1848
1849
1850
1851
1852





1853
1854
1855
1856
1857
1858
1859
      ** then szFile will become zero (for an empty file) or positive.
      ** Special case:  Assume any file with a basename of ".fossil" does
      ** not exist.
      */
      zCleanRepo = file_cleanup_fullpath(zRepo);
      if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
        szFile = file_size(zCleanRepo, ExtFILE);





        if( g.fHttpTrace ){
          sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
          @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
          fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
        }
      }








>
>
>
>
>







1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
      ** then szFile will become zero (for an empty file) or positive.
      ** Special case:  Assume any file with a basename of ".fossil" does
      ** not exist.
      */
      zCleanRepo = file_cleanup_fullpath(zRepo);
      if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
        szFile = file_size(zCleanRepo, ExtFILE);
        if( szFile>0 && !file_isfile(zCleanRepo, ExtFILE) ){
          /* Only let szFile be non-negative if zCleanRepo really is a file
          ** and not a directory or some other filesystem object. */
          szFile = -1;
        }
        if( g.fHttpTrace ){
          sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
          @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
          fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
        }
      }

2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
        json_bootstrap_late();
        jsonOnce = 1;
      }
    }
#endif
    if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){
      cgi_decode_post_parameters();
      if( !cgi_same_origin() ){
        isReadonly = 1;
        db_protect(PROTECT_READONLY);
      }
    }
    if( g.fCgiTrace ){
      fossil_trace("######## Calling %s #########\n", pCmd->zName);
      cgi_print_all(1, 1, 0);







|







2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
        json_bootstrap_late();
        jsonOnce = 1;
      }
    }
#endif
    if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){
      cgi_decode_post_parameters();
      if( !cgi_same_origin(0) ){
        isReadonly = 1;
        db_protect(PROTECT_READONLY);
      }
    }
    if( g.fCgiTrace ){
      fossil_trace("######## Calling %s #########\n", pCmd->zName);
      cgi_print_all(1, 1, 0);
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
** use any other option.
**
** The lines are processed in the order they are read, which is most
** significant for "errorlog:", which should be set before "repository:"
** so that any warnings from the database when opening the repository
** go to that log file.
**
** See also: [[http]], [[server]], [[winsrv]]
*/
void cmd_cgi(void){
  const char *zNotFound = 0;
  char **azRedirect = 0;             /* List of repositories to redirect to */
  int nRedirect = 0;                 /* Number of entries in azRedirect */
  Glob *pFileGlob = 0;               /* Pattern for files */
  int allowRepoList = 0;             /* Allow lists of repository files */







|







2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
** use any other option.
**
** The lines are processed in the order they are read, which is most
** significant for "errorlog:", which should be set before "repository:"
** so that any warnings from the database when opening the repository
** go to that log file.
**
** See also: [[http]], [[server]], [[winsrv]] [Windows only]
*/
void cmd_cgi(void){
  const char *zNotFound = 0;
  char **azRedirect = 0;             /* List of repositories to redirect to */
  int nRedirect = 0;                 /* Number of entries in azRedirect */
  Glob *pFileGlob = 0;               /* Pattern for files */
  int allowRepoList = 0;             /* Allow lists of repository files */
2528
2529
2530
2531
2532
2533
2534

2535



2536
2537
2538
2539
2540
2541
2542
2543
    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, "errorlog:") && blob_token(&line, &value) ){
      /* errorlog: FILENAME
      **







>
|
>
>
>
|







2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
    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.
      */
      char *zValue;
      blob_tail(&line,&value2);
      blob_trim(&value2);
      zValue = blob_str(&value2);
      while( fossil_isspace(zValue[0]) ){ zValue++; }
      fossil_setenv(blob_str(&value), zValue);
      blob_reset(&value);
      blob_reset(&value2);
      continue;
    }
    if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){
      /* errorlog: FILENAME
      **
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
**   --scgi              Interpret input as SCGI rather than HTTP
**   --skin LABEL        Use override skin LABEL. Use an empty string ("")
**                       to force use of the current local skin config.
**   --th-trace          Trace TH1 execution (for debugging purposes)
**   --usepidkey         Use saved encryption key from parent process. This is
**                       only necessary when using SEE on Windows or Linux.
**
** 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;







|







2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
**   --scgi              Interpret input as SCGI rather than HTTP
**   --skin LABEL        Use override skin LABEL. Use an empty string ("")
**                       to force use of the current local skin config.
**   --th-trace          Trace TH1 execution (for debugging purposes)
**   --usepidkey         Use saved encryption key from parent process. This is
**                       only necessary when using SEE on Windows or Linux.
**
** See also: [[cgi]], [[server]], [[winsrv]] [Windows only]
*/
void cmd_http(void){
  const char *zIpAddr = 0;
  const char *zNotFound;
  const char *zHost;
  const char *zAltBase;
  const char *zFileGlob;
3186
3187
3188
3189
3190
3191
3192



3193
3194
3195
3196
3197
3198
3199
**   --cert FILE         Use TLS (HTTPS) encryption with the certificate (the
**                       fullchain.pem) taken from FILE.
**   --chroot DIR        Use directory for chroot instead of repository path
**   --ckout-alias NAME  Treat URIs of the form /doc/NAME/... as if they were
**                       /doc/ckout/...
**   --create            Create a new REPOSITORY if it does not already exist
**   --errorlog FILE     Append HTTP error messages to FILE



**   --extroot DIR       Document root for the /ext extension mechanism
**   --files GLOBLIST    Comma-separated list of glob patterns for static files
**   --fossilcmd PATH    The pathname of the "fossil" executable on the remote
**                       system when REPOSITORY is remote.
**   --from PATH         Use PATH as the diff baseline for the /ckout page
**   --localauth         Enable automatic login for requests from localhost
**   --localhost         Listen on 127.0.0.1 only (always true for "ui")







>
>
>







3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
**   --cert FILE         Use TLS (HTTPS) encryption with the certificate (the
**                       fullchain.pem) taken from FILE.
**   --chroot DIR        Use directory for chroot instead of repository path
**   --ckout-alias NAME  Treat URIs of the form /doc/NAME/... as if they were
**                       /doc/ckout/...
**   --create            Create a new REPOSITORY if it does not already exist
**   --errorlog FILE     Append HTTP error messages to FILE
**   --extpage FILE      Shortcut for "--extroot DIR --page ext/TAIL" where
**                       DIR is the directory holding FILE and TAIL is the
**                       filename at the end of FILE.  Only works for "ui".
**   --extroot DIR       Document root for the /ext extension mechanism
**   --files GLOBLIST    Comma-separated list of glob patterns for static files
**   --fossilcmd PATH    The pathname of the "fossil" executable on the remote
**                       system when REPOSITORY is remote.
**   --from PATH         Use PATH as the diff baseline for the /ckout page
**   --localauth         Enable automatic login for requests from localhost
**   --localhost         Listen on 127.0.0.1 only (always true for "ui")
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
**   --socket-owner USR  Try to set the owner of the unix socket to USR.
**                       USR can be of the form USER:GROUP to set both
**                       user and group.
**   --th-trace          Trace TH1 execution (for debugging purposes)
**   --usepidkey         Use saved encryption key from parent process.  This is
**                       only necessary when using SEE on Windows or Linux.
**
** See also: [[cgi]], [[http]], [[winsrv]]
*/
void cmd_webserver(void){
  int iPort, mxPort;        /* Range of TCP ports allowed */
  const char *zPort;        /* Value of the --port option */
  const char *zBrowser;     /* Name of web browser program */
  char *zBrowserCmd = 0;    /* Command to launch the web browser */
  int isUiCmd;              /* True if command is "ui", not "server' */







|







3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
**   --socket-owner USR  Try to set the owner of the unix socket to USR.
**                       USR can be of the form USER:GROUP to set both
**                       user and group.
**   --th-trace          Trace TH1 execution (for debugging purposes)
**   --usepidkey         Use saved encryption key from parent process.  This is
**                       only necessary when using SEE on Windows or Linux.
**
** See also: [[cgi]], [[http]], [[winsrv]] [Windows only]
*/
void cmd_webserver(void){
  int iPort, mxPort;        /* Range of TCP ports allowed */
  const char *zPort;        /* Value of the --port option */
  const char *zBrowser;     /* Name of web browser program */
  char *zBrowserCmd = 0;    /* Command to launch the web browser */
  int isUiCmd;              /* True if command is "ui", not "server' */
3264
3265
3266
3267
3268
3269
3270

3271
3272
3273
3274
3275
3276
3277
  int fNoBrowser = 0;        /* Do not auto-launch web-browser */
  const char *zInitPage = 0; /* Start on this page.  --page option */
  int findServerArg = 2;     /* argv index for find_server_repository() */
  char *zRemote = 0;         /* Remote host on which to run "fossil ui" */
  const char *zJsMode;       /* The --jsmode parameter */
  const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
  const char *zFrom;         /* Value for --from */



#if USE_SEE
  db_setup_for_saved_encryption_key();
#endif

#if defined(_WIN32)







>







3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
  int fNoBrowser = 0;        /* Do not auto-launch web-browser */
  const char *zInitPage = 0; /* Start on this page.  --page option */
  int findServerArg = 2;     /* argv index for find_server_repository() */
  char *zRemote = 0;         /* Remote host on which to run "fossil ui" */
  const char *zJsMode;       /* The --jsmode parameter */
  const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
  const char *zFrom;         /* Value for --from */
  const char *zExtPage = 0;  /* Argument to --extpage */


#if USE_SEE
  db_setup_for_saved_encryption_key();
#endif

#if defined(_WIN32)
3305
3306
3307
3308
3309
3310
3311







3312
3313

3314
3315
3316
3317
3318
3319
3320
  isUiCmd = g.argv[1][0]=='u';
  if( isUiCmd ){
    zFrom = find_option("from", 0, 1);
    if( zFrom && zFrom==file_tail(zFrom) ){
      fossil_fatal("the argument to --from must be a pathname for"
                   " the \"ui\" command");
    }







    zInitPage = find_option("page", "p", 1);
    if( zInitPage && zInitPage[0]=='/' ) zInitPage++;

    zFossilCmd = find_option("fossilcmd", 0, 1);
    if( zFrom && zInitPage==0 ){
      zInitPage = mprintf("ckout?exbase=%H", zFrom);
    }
  }
  zNotFound = find_option("notfound", 0, 1);
  allowRepoList = find_option("repolist",0,0)!=0;







>
>
>
>
>
>
>
|
|
>







3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
  isUiCmd = g.argv[1][0]=='u';
  if( isUiCmd ){
    zFrom = find_option("from", 0, 1);
    if( zFrom && zFrom==file_tail(zFrom) ){
      fossil_fatal("the argument to --from must be a pathname for"
                   " the \"ui\" command");
    }
    zExtPage = find_option("extpage",0,1);
    if( zExtPage ){
      char *zFullPath = file_canonical_name_dup(zExtPage);
      g.zExtRoot = file_dirname(zFullPath);
      zInitPage = mprintf("ext/%s",file_tail(zFullPath));
      fossil_free(zFullPath);
    }else{
      zInitPage = find_option("page", "p", 1);
      if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
    }
    zFossilCmd = find_option("fossilcmd", 0, 1);
    if( zFrom && zInitPage==0 ){
      zInitPage = mprintf("ckout?exbase=%H", zFrom);
    }
  }
  zNotFound = find_option("notfound", 0, 1);
  allowRepoList = find_option("repolist",0,0)!=0;
3479
3480
3481
3482
3483
3484
3485






3486

3487
3488
3489
3490
3491
3492
3493
      }else{
        blob_appendf(&ssh, " %$", zFossilCmd);
      }
      blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort);
      if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound);
      if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob);
      if( g.zCkoutAlias ) blob_appendf(&ssh," --ckout-alias %!$",g.zCkoutAlias);






      if( g.zExtRoot ) blob_appendf(&ssh, " --extroot %$", g.zExtRoot);

      if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use());
      if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode);
      if( fCreate ) blob_appendf(&ssh, " --create");
      blob_appendf(&ssh, " %$", g.argv[2]);
      if( isRetry ){
        fossil_print("First attempt to run \"fossil\" on %s failed\n"
                     "Retry: ", zRemote);







>
>
>
>
>
>
|
>







3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
      }else{
        blob_appendf(&ssh, " %$", zFossilCmd);
      }
      blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort);
      if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound);
      if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob);
      if( g.zCkoutAlias ) blob_appendf(&ssh," --ckout-alias %!$",g.zCkoutAlias);
      if( zExtPage ){
        if( !file_is_absolute_path(zExtPage) ){
          zExtPage = mprintf("%s/%s", g.argv[2], zExtPage);
        }
        blob_appendf(&ssh, " --extpage %$", zExtPage);
      }else if( g.zExtRoot ){
        blob_appendf(&ssh, " --extroot %$", g.zExtRoot);
      }
      if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use());
      if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode);
      if( fCreate ) blob_appendf(&ssh, " --create");
      blob_appendf(&ssh, " %$", g.argv[2]);
      if( isRetry ){
        fossil_print("First attempt to run \"fossil\" on %s failed\n"
                     "Retry: ", zRemote);
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
  if( g.httpUseSSL && g.httpSSLConn ){
    ssl_close_server(g.httpSSLConn);
    g.httpSSLConn = 0;
  }
#endif /* FOSSIL_ENABLE_SSL */

#else /* WIN32 */
  find_server_repository(2, 0);
  if( fossil_strcmp(g.zRepositoryName,"/")==0 ){
    allowRepoList = 1;
  }
  /* Win32 implementation */
  if( allowRepoList ){
    flags |= HTTP_SERVER_REPOLIST;
  }
  if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){
    win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile,
                      zAltBase, zNotFound, zFileGlob, zIpAddr, flags);
  }







|



<







3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642

3643
3644
3645
3646
3647
3648
3649
  if( g.httpUseSSL && g.httpSSLConn ){
    ssl_close_server(g.httpSSLConn);
    g.httpSSLConn = 0;
  }
#endif /* FOSSIL_ENABLE_SSL */

#else /* WIN32 */
  /* Win32 implementation */
  if( fossil_strcmp(g.zRepositoryName,"/")==0 ){
    allowRepoList = 1;
  }

  if( allowRepoList ){
    flags |= HTTP_SERVER_REPOLIST;
  }
  if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){
    win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile,
                      zAltBase, zNotFound, zFileGlob, zIpAddr, flags);
  }
Changes to src/main.mk.
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146

2147
2148
2149
2150
2151
2152
2153

$(OBJDIR)/pikchr.o:	$(SRCDIR_extsrc)/pikchr.c
	$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@

$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c
	$(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@

$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c
	$(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \
        -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore \
        -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c \
        -sENVIRONMENT=web \
        -sMODULARIZE \
        -sEXPORT_NAME=initPikchrModule \
        --minify 0

	@chmod -x $(SRCDIR_extsrc)/pikchr.wasm
wasm: $(SRCDIR_extsrc)/pikchr.js

#
# compile_commands.json support...
#
# We have to avoid applying compile_commands support to the in-tree







|

|
|




>







2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154

$(OBJDIR)/pikchr.o:	$(SRCDIR_extsrc)/pikchr.c
	$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@

$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c
	$(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@

$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST)
	$(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \
        -sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore \
        -sEXPORTED_FUNCTIONS=_pikchr,_pikchr_version $(SRCDIR_extsrc)/pikchr.c \
        -sENVIRONMENT=web \
        -sMODULARIZE \
        -sEXPORT_NAME=initPikchrModule \
        --minify 0
	$(TCLSH) $(TOPDIR)/tools/randomize-js-names.tcl $(SRCDIR_extsrc)
	@chmod -x $(SRCDIR_extsrc)/pikchr.wasm
wasm: $(SRCDIR_extsrc)/pikchr.js

#
# compile_commands.json support...
#
# We have to avoid applying compile_commands support to the in-tree
Changes to src/manifest.c.
2909
2910
2911
2912
2913
2914
2915







































































































































































































































































































  Blob content;
  db_find_and_open_repository(0, 0);
  if( g.argc!=3 ) usage("RECORDID");
  rid = name_to_rid(g.argv[2]);
  content_get(rid, &content);
  manifest_crosslink(rid, &content, MC_NONE);
}














































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
  Blob content;
  db_find_and_open_repository(0, 0);
  if( g.argc!=3 ) usage("RECORDID");
  rid = name_to_rid(g.argv[2]);
  content_get(rid, &content);
  manifest_crosslink(rid, &content, MC_NONE);
}

/*
** For a given CATYPE_... value, returns a human-friendly name, or
** NULL if typeId is unknown or is CFTYPE_ANY. The names returned by
** this function are geared towards use with artifact_to_json(), and
** may differ from some historical uses. e.g. CFTYPE_CONTROL artifacts
** are called "tag" artifacts by this function.
*/
const char * artifact_type_to_name(int typeId){
  switch(typeId){
    case CFTYPE_MANIFEST: return "checkin";
    case CFTYPE_CLUSTER: return "cluster";
    case CFTYPE_CONTROL: return "tag";
    case CFTYPE_WIKI: return "wiki";
    case CFTYPE_TICKET: return "ticket";
    case CFTYPE_ATTACHMENT: return "attachment";
    case CFTYPE_EVENT: return "technote";
    case CFTYPE_FORUM: return "forumpost";
  }
  return NULL;
}

/*
** Creates a JSON representation of p, appending it to b.
**
** b is not cleared before rendering, so the caller needs to do that
** if it's important for their use case.
**
** Pedantic note: this routine traverses p->aFile directly, rather
** than using manifest_file_next(), so that delta manifests are
** rendered as-is instead of containing their derived F-cards. If that
** policy is ever changed, p will need to be non-const.
*/
void artifact_to_json(Manifest const *p, Blob *b){
  int i;

  blob_append_literal(b, "{");
  blob_appendf(b, "\"uuid\":\"%z\"", rid_to_uuid(p->rid));
  /*blob_appendf(b, ", \"rid\": %d", p->rid); not portable across repos*/
  blob_appendf(b, ",\"type\":%!j", artifact_type_to_name(p->type));
#define ISA(TYPE) if( p->type==TYPE )
#define CARD_LETTER(LETTER) \
  blob_append_literal(b, ",\"" #LETTER "\":")
#define CARD_STR(LETTER, VAL) \
  assert( VAL ); CARD_LETTER(LETTER); blob_appendf(b, "%!j", VAL)
#define CARD_STR2(LETTER, VAL) \
  if( VAL ) { CARD_STR(LETTER, VAL); } (void)0
#define STR_OR_NULL(VAL)                 \
  if( VAL ) blob_appendf(b, "%!j", VAL); \
  else blob_append(b, "null", 4)
#define KVP_STR(ADDCOMMA, KEY,VAL)  \
  if(ADDCOMMA) blob_append_char(b, ','); \
  blob_appendf(b, "%!j:", #KEY);   \
  STR_OR_NULL(VAL)

  ISA( CFTYPE_ATTACHMENT ){
    CARD_LETTER(A);
    blob_append_char(b, '{');
    KVP_STR(0, filename, p->zAttachName);
    KVP_STR(1, target, p->zAttachTarget);
    KVP_STR(1, source, p->zAttachSrc);
    blob_append_char(b, '}');
  }
  CARD_STR2(B, p->zBaseline);
  CARD_STR2(C, p->zComment);
  CARD_LETTER(D); blob_appendf(b, "%f", p->rDate);
  ISA( CFTYPE_EVENT ){
    blob_appendf(b, ", \"E\":{\"time\":%f,\"id\":%!j}",
                 p->rEventDate, p->zEventId);
  }
  ISA( CFTYPE_MANIFEST ){
    CARD_LETTER(F);
    blob_append_char(b, '[');
    for( i = 0; i < p->nFile; ++i ){
      ManifestFile const * const pF = &p->aFile[i];
      if( i>0 ) blob_append_char(b, ',');
      blob_append_char(b, '{');
      KVP_STR(0, name, pF->zName);
      KVP_STR(1, uuid, pF->zUuid);
      KVP_STR(1, perm, pF->zPerm);
      KVP_STR(1, rename, pF->zPrior);
      blob_append_char(b, '}');
    }
    /* Special case: model checkins with no F-card as having an empty
    ** array, rather than no F-cards, to hypothetically simplify
    ** handling in JSON queries. */
    blob_append_char(b, ']');
  }
  CARD_STR2(G, p->zThreadRoot);
  ISA( CFTYPE_FORUM ){
    CARD_LETTER(H);
    STR_OR_NULL( (p->zThreadTitle && *p->zThreadTitle) ? p->zThreadTitle : NULL);
    CARD_STR2(I, p->zInReplyTo);
  }
  if( p->nField ){
    CARD_LETTER(J);
    blob_append_char(b, '[');
    for( i = 0; i < p->nField; ++i ){
      const char * zName = p->aField[i].zName;
      if( i>0 ) blob_append_char(b, ',');
      blob_append_char(b, '{');
      KVP_STR(0, name, '+'==*zName ? &zName[1] : zName);
      KVP_STR(1, value, p->aField[i].zValue);
      blob_appendf(b, ",\"append\":%s", '+'==*zName ? "true" : "false");
      blob_append_char(b, '}');
    }
    blob_append_char(b, ']');
  }
  CARD_STR2(K, p->zTicketUuid);
  CARD_STR2(L, p->zWikiTitle);
  ISA( CFTYPE_CLUSTER ){
    CARD_LETTER(M);
    blob_append_char(b, '[');
    for( int i = 0; i < p->nCChild; ++i ){
      if( i>0 ) blob_append_char(b, ',');
      blob_appendf(b, "%!j", p->azCChild[i]);
    }
    blob_append_char(b, ']');
  }
  CARD_STR2(N, p->zMimetype);
  ISA( CFTYPE_MANIFEST || p->nParent>0 ){
    CARD_LETTER(P);
    blob_append_char(b, '[');
    for( i = 0; i < p->nParent; ++i ){
      if( i>0 ) blob_append_char(b, ',');
      blob_appendf(b, "%!j", p->azParent[i]);
    }
    /* Special case: model checkins with no P-card as having an empty
    ** array, as per F-cards. */
    blob_append_char(b, ']');
  }
  if( p->nCherrypick ){
    CARD_LETTER(Q);
    blob_append_char(b, '[');
    for( i = 0; i < p->nCherrypick; ++i ){
      if( i>0 ) blob_append_char(b, ',');
      blob_append_char(b, '{');
      blob_appendf(b, "\"type\":\"%c\"", p->aCherrypick[i].zCPTarget[0]);
      KVP_STR(1, target, &p->aCherrypick[i].zCPTarget[1]);
      KVP_STR(1, base, p->aCherrypick[i].zCPBase);
      blob_append_char(b, '}');
    }
    blob_append_char(b, ']');
  }
  CARD_STR2(R, p->zRepoCksum);
  if( p->nTag ){
    CARD_LETTER(T);
    blob_append_char(b, '[');
    for( int i = 0; i < p->nTag; ++i ){
      const char *zName = p->aTag[i].zName;
      if( i>0 ) blob_append_char(b, ',');
      blob_append_char(b, '{');
      blob_appendf(b, "\"type\":\"%c\"", *zName);
      KVP_STR(1, name, &zName[1]);
      KVP_STR(1, target, p->aTag[i].zUuid ? p->aTag[i].zUuid : "*")
        /* We could arguably resolve the "*" as null or p's uuid. */;
      KVP_STR(1, value, p->aTag[i].zValue);
      blob_append_char(b, '}');
    }
    blob_append_char(b, ']');
  }
  CARD_STR2(U, p->zUser);
  if( p->zWiki || CFTYPE_WIKI==p->type || CFTYPE_FORUM==p->type
      || CFTYPE_EVENT==p->type ){
    CARD_LETTER(W);
    STR_OR_NULL((p->zWiki && *p->zWiki) ? p->zWiki : NULL);
  }
  blob_append_literal(b, "}");
#undef CARD_FMT
#undef CARD_LETTER
#undef CARD_STR
#undef CARD_STR2
#undef ISA
#undef KVP_STR
#undef STR_OR_NULL
}

/*
** Convenience wrapper around artifact_to_json() which expects rid to
** be the blob.rid of any artifact type. If it can load a Manifest
** with that rid, it returns rid, else it returns 0.
*/
int artifact_to_json_by_rid(int rid, Blob *pOut){
  Manifest * const p = manifest_get(rid, CFTYPE_ANY, 0);
  if( p ){
    artifact_to_json(p, pOut);
    manifest_destroy(p);
  }else{
    rid = 0;
  }
  return rid;
}

/*
** Convenience wrapper around artifact_to_json() which accepts any
** artifact name which is legal for symbolic_name_to_rid(). On success
** it returns the rid of the artifact. Returns 0 if no such artifact
** exists and a negative value if the name is ambiguous.
**
** pOut is not cleared before rendering, so the caller needs to do
** that if it's important for their use case.
*/
int artifact_to_json_by_name(const char *zName, Blob *pOut){
  const int rid = symbolic_name_to_rid(zName, 0);
  return rid>0
    ? artifact_to_json_by_rid(rid, pOut)
    : rid;
}

/*
** SQLite UDF for artifact_to_json(). Its single argument should be
** either an INTEGER (blob.rid value) or a TEXT symbolic artifact
** name, as per symbolic_name_to_rid(). If an artifact is found then
** the result of the UDF is that JSON as a string, else it evaluates
** to NULL.
*/
void artifact_to_json_sql_func(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  int rid = 0;
  Blob b = empty_blob;

  if(1 != argc){
    goto error_usage;
  }
  switch( sqlite3_value_type(argv[0]) ){
    case SQLITE_INTEGER:
      rid = artifact_to_json_by_rid(sqlite3_value_int(argv[0]), &b);
      break;
    case SQLITE_TEXT:{
      const char * z = (const char *)sqlite3_value_text(argv[0]);
      if( z ){
        rid = artifact_to_json_by_name(z, &b);
      }
      break;
    }
    default:
      goto error_usage;
  }
  if( rid>0 ){
    sqlite3_result_text(context, blob_str(&b), blob_size(&b),
                        SQLITE_TRANSIENT);
    blob_reset(&b);
  }else{
    /* We should arguably error out if rid<0 (ambiguous name) */
    sqlite3_result_null(context);
  }
  return;
error_usage:
  sqlite3_result_error(context, "Expecting one argument: blob.rid or "
                       "artifact symbolic name", -1);
}



/*
** COMMAND: test-artifact-to-json
**
** Usage:  %fossil test-artifact-to-json ?-pretty|-p? symbolic-name [...names]
**
** Tests the artifact_to_json() and artifact_to_json_by_name() APIs.
*/
void test_manifest_to_json(void){
  int i;
  Blob b = empty_blob;
  Stmt q;
  const int bPretty = find_option("pretty","p",0)!=0;
  int nErr = 0;

  db_find_and_open_repository(0,0);
  db_prepare(&q, "select json_pretty(:json)");
  for( i=2; i<g.argc; ++i ){
    char const *zName = g.argv[i];
    const int rc = artifact_to_json_by_name(zName, &b);
    if( rc<=0 ){
      ++nErr;
      fossil_warning("Error reading artifact %Q", zName);
      continue;
    }else if( bPretty ){
      db_bind_blob(&q, ":json", &b);
      b.nUsed = 0;
      db_step(&q);
      db_column_blob(&q, 0, &b);
      db_reset(&q);
    }
    fossil_print("%b\n", &b);
    blob_reset(&b);
  }
  db_finalize(&q);
  if( nErr ){
    fossil_warning("Error count: %d", nErr);
  }
}
Changes to src/markdown_html.c.
909
910
911
912
913
914
915

























































    blob_set( &context.reqURI, zRU );
  #endif
  html_renderer.opaque = &context;
  if( output_title ) blob_reset(output_title);
  blob_reset(output_body);
  markdown(output_body, input_markdown, &html_renderer);
}
































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
909
910
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
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
962
963
964
965
966
967
968
969
970
971
972
    blob_set( &context.reqURI, zRU );
  #endif
  html_renderer.opaque = &context;
  if( output_title ) blob_reset(output_title);
  blob_reset(output_body);
  markdown(output_body, input_markdown, &html_renderer);
}

/*
** Undo HTML escapes in Blob p.  In other words convert:
**
**     &amp;     ->     &
**     &lt;      ->     <
**     &gt;      ->     >
**     &quot;    ->     "
**     &#NNN;    ->     ascii character NNN
*/
void markdown_dehtmlize_blob(Blob *p){
  char *z;
  unsigned int j, k;

  z = p->aData;
  for(j=k=0; j<p->nUsed; j++){
    char c = z[j];
    if( c=='&' ){
      if( z[j+1]=='#' && fossil_isdigit(z[j+2]) ){
        int n = 3;
        int x = z[j+2] - '0';
        if( fossil_isdigit(z[j+3]) ){
          x = x*10 + z[j+3] - '0';
          n++;
          if( fossil_isdigit(z[j+4]) ){
            x = x*10 + z[j+4] - '0';
            n++;
          }
        }
        if( z[j+n]==';' ){
          z[k++] = (char)x;
          j += n;
        }else{
          z[k++] = c;
        }
      }else if( memcmp(&z[j],"&lt;",4)==0 ){
        z[k++] = '<';
        j += 3;
      }else if( memcmp(&z[j],"&gt;",4)==0 ){
        z[k++] = '>';
        j += 3;
      }else if( memcmp(&z[j],"&quot;",6)==0 ){
        z[k++] = '"';
        j += 5;
      }else if( memcmp(&z[j],"&amp;",5)==0 ){
        z[k++] = '&';
        j += 4;
      }else{
        z[k++] = c;
      }
    }else{
      z[k++] = c;
    }
  }
  z[k] = 0;
  p->nUsed = k;
}
Changes to src/name.c.
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

/*
** Check to see if the string might be a compact date/time that omits
** the punctuation.  Example:  "20190327084549" instead of
** "2019-03-27 08:45:49".  If the string is of the appropriate form,
** then return an alternative string (in static space) that is the same
** string with punctuation inserted.
**













** If the bVerifyNotAHash flag is true, then a check is made to see if
** the string is a hash prefix and NULL is returned if it is.  If the
** bVerifyNotAHash flag is false, then the result is determined by syntax
** of the input string only, without reference to the artifact table.
*/
char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){
  static char zEDate[24];
  static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
  int n = (int)strlen(zIn);
  int i, j;
  int addZulu = 0;

  /* These forms are allowed:
  **
  **        123456789 1234           123456789 123456789
  **   (1)  YYYYMMDD            =>   YYYY-MM-DD
  **   (2)  YYYYMMDDHHMM        =>   YYYY-MM-DD HH:MM
  **   (3)  YYYYMMDDHHMMSS      =>   YYYY-MM-DD HH:MM:SS
  **
  ** An optional "Z" zulu timezone designator is allowed at the end.
  */
  if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){
    n--;
    addZulu = 1;
  }








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

|



|








|
|
|
|







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

/*
** Check to see if the string might be a compact date/time that omits
** the punctuation.  Example:  "20190327084549" instead of
** "2019-03-27 08:45:49".  If the string is of the appropriate form,
** then return an alternative string (in static space) that is the same
** string with punctuation inserted.
**
** If the bRoundUp parameter is true, then round the resulting date-time
** up to the largest date/time that is consistent with the input value.
** This is because the result will be used for an mtime<=julianday($DATE)
** comparison.  In other words:
**
**     20250317123421 -> 2025-03-17 12:34:21.999
**                                          ^^^^--- Added
**
**     202503171234   -> 2025-03-17 12:34:59.999
**                                       ^^^^^^^--- Added
**     20250317       -> 2025-03-17 23:59:59.999
**                                 ^^^^^^^^^^^^^--- Added
**
** If the bVerifyNotAHash flag is true, then a check is made to see if
** the input string is a hash prefix and NULL is returned if it is.  If the
** bVerifyNotAHash flag is false, then the result is determined by syntax
** of the input string only, without reference to the artifact table.
*/
char *fossil_expand_datetime(const char *zIn,int bVerifyNotAHash,int bRoundUp){
  static char zEDate[24];
  static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
  int n = (int)strlen(zIn);
  int i, j;
  int addZulu = 0;

  /* These forms are allowed:
  **
  **        123456789 1234           123456789 123456789 1234
  **   (1)  YYYYMMDD            =>   YYYY-MM-DD 23:59:59.999
  **   (2)  YYYYMMDDHHMM        =>   YYYY-MM-DD HH:MM:59.999
  **   (3)  YYYYMMDDHHMMSS      =>   YYYY-MM-DD HH:MM:SS.999
  **
  ** An optional "Z" zulu timezone designator is allowed at the end.
  */
  if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){
    n--;
    addZulu = 1;
  }
97
98
99
100
101
102
103
104
105
106
107






108


109
110
111
112
113
114
115
  /* Expand the date */
  for(i=j=0; i<n; i++){
    if( i>=4 && (i%2)==0 ){
      zEDate[j++] = aPunct[i/2];
    }
    zEDate[j++] = zIn[i];
  }
  if( addZulu ){
    if( j==10 ){
      memcpy(&zEDate[10]," 00:00", 6);
      j += 6;






    }


    zEDate[j++] = 'Z';
  }
  zEDate[j] = 0;

  /* Check for reasonable date values.
  ** Offset references:
  **    YYYY-MM-DD HH:MM:SS







|

|
|
>
>
>
>
>
>

>
>







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
  /* Expand the date */
  for(i=j=0; i<n; i++){
    if( i>=4 && (i%2)==0 ){
      zEDate[j++] = aPunct[i/2];
    }
    zEDate[j++] = zIn[i];
  }
  if( bRoundUp ){
    if( j==10 ){
      memcpy(&zEDate[10], " 23:59:59.999", 13);
      j += 13;
    }else if( j==16 ){
      memcpy(&zEDate[16], ":59.999",7);
      j += 7;
    }else if( j==19 ){
      memcpy(&zEDate[19], ".999", 4);
      j += 4;
    }
  }
  if( addZulu ){
    zEDate[j++] = 'Z';
  }
  zEDate[j] = 0;

  /* Check for reasonable date values.
  ** Offset references:
  **    YYYY-MM-DD HH:MM:SS
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
** But if the zDate parameter omits the fractional seconds or the
** seconds, or the time, that might mess up the == part of the
** comparison.  So add in missing factional seconds or seconds or time.
**
** The returned string is held in a static buffer that is overwritten
** with each call, or else is just a copy of its input if there are
** no changes.





*/
const char *fossil_roundup_date(const char *zDate){
  static char zUp[24];
  int n = (int)strlen(zDate);





  if( n==19 ){  /* YYYY-MM-DD HH:MM:SS */
    memcpy(zUp, zDate, 19);
    memcpy(zUp+19, ".999", 5);

    return zUp;
  }
  if( n==16 ){ /* YYYY-MM-DD HH:MM */
    memcpy(zUp, zDate, 16);
    memcpy(zUp+16, ":59.999", 8);

    return zUp;
  }
  if( n==10 ){ /* YYYY-MM-DD */
    memcpy(zUp, zDate, 10);
    memcpy(zUp+10, " 23:59:59.999", 14);

    return zUp;
  }
  return zDate;
}


/*







>
>
>
>
>


|

>
>
>
>
>


|
>




|
>




|
>







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
** But if the zDate parameter omits the fractional seconds or the
** seconds, or the time, that might mess up the == part of the
** comparison.  So add in missing factional seconds or seconds or time.
**
** The returned string is held in a static buffer that is overwritten
** with each call, or else is just a copy of its input if there are
** no changes.
**
** For reference:
**
**        0123456789 123456789 1234
**        YYYY-MM-DD HH:MM:SS.SSSz
*/
const char *fossil_roundup_date(const char *zDate){
  static char zUp[28];
  int n = (int)strlen(zDate);
  int addZ = 0;
  if( n>10 && (zDate[n-1]=='z' || zDate[n-1]=='Z') ){
    n--;
    addZ = 1;
  }
  if( n==19 ){  /* YYYY-MM-DD HH:MM:SS */
    memcpy(zUp, zDate, 19);
    memcpy(zUp+19, ".999z", 6);
    if( !addZ ) zUp[23] = 0;
    return zUp;
  }
  if( n==16 ){ /* YYYY-MM-DD HH:MM */
    memcpy(zUp, zDate, 16);
    memcpy(zUp+16, ":59.999z", 8);
    if( !addZ ) zUp[23] = 0;
    return zUp;
  }
  if( n==10 ){ /* YYYY-MM-DD */
    memcpy(zUp, zDate, 10);
    memcpy(zUp+10, " 23:59:59.999z", 14);
    if( !addZ ) zUp[23] = 0;
    return zUp;
  }
  return zDate;
}


/*
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
** Return 0 if there are no matches.
**
** This is a tricky query to do efficiently.
** If the tag is very common (ex: "trunk") then
** we want to use the query identified below as Q1 - which searches
** the most recent EVENT table entries for the most recent with the tag.
** But if the tag is relatively scarce (anything other than "trunk", basically)
** then we want to do the indexed search show below as Q2.
*/
static int most_recent_event_with_tag(const char *zTag, const char *zType){
  return db_int(0,
    "SELECT objid FROM ("
      /* Q1:  Begin by looking for the tag in the 30 most recent events */
      "SELECT objid"
       " FROM (SELECT * FROM event ORDER BY mtime DESC LIMIT 30) AS ex"







|







263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
** Return 0 if there are no matches.
**
** This is a tricky query to do efficiently.
** If the tag is very common (ex: "trunk") then
** we want to use the query identified below as Q1 - which searches
** the most recent EVENT table entries for the most recent with the tag.
** But if the tag is relatively scarce (anything other than "trunk", basically)
** then we want to do the indexed search shown below as Q2.
*/
static int most_recent_event_with_tag(const char *zTag, const char *zType){
  return db_int(0,
    "SELECT objid FROM ("
      /* Q1:  Begin by looking for the tag in the 30 most recent events */
      "SELECT objid"
       " FROM (SELECT * FROM event ORDER BY mtime DESC LIMIT 30) AS ex"
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
      g.localOpen = ridCkout;
    }
    if( rid ) return rid;
  }

  /* Date and times */
  if( memcmp(zTag, "date:", 5)==0 ){
    zDate = fossil_expand_datetime(&zTag[5],0);
    if( zDate==0 ) zDate = &zTag[5];
    rid = db_int(0,
      "SELECT objid FROM event"
      " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
      " ORDER BY mtime DESC LIMIT 1",
      fossil_roundup_date(zDate), zType);
    return rid;







|







511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
      g.localOpen = ridCkout;
    }
    if( rid ) return rid;
  }

  /* Date and times */
  if( memcmp(zTag, "date:", 5)==0 ){
    zDate = fossil_expand_datetime(&zTag[5],0,1);
    if( zDate==0 ) zDate = &zTag[5];
    rid = db_int(0,
      "SELECT objid FROM event"
      " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
      " ORDER BY mtime DESC LIMIT 1",
      fossil_roundup_date(zDate), zType);
    return rid;
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
    return start_of_branch(rid, 2);
  }

  /* symbolic-name ":" date-time */
  nTag = strlen(zTag);
  for(i=0; i<nTag-8 && zTag[i]!=':'; i++){}
  if( zTag[i]==':'
   && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0)!=0)
  ){
    char *zDate = mprintf("%s", &zTag[i+1]);
    char *zTagBase = mprintf("%.*s", i, zTag);
    char *zXDate;
    int nDate = strlen(zDate);
    if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
      zDate[nDate-3] = 'z';
      zDate[nDate-2] = 0;
    }
    zXDate = fossil_expand_datetime(zDate,0);
    if( zXDate==0 ) zXDate = zDate;
    rid = db_int(0,
      "SELECT event.objid, max(event.mtime)"
      "  FROM tag, tagxref, event"
      " WHERE tag.tagname='sym-%q' "
      "   AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
      "   AND event.objid=tagxref.rid "







|









|







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
    return start_of_branch(rid, 2);
  }

  /* symbolic-name ":" date-time */
  nTag = strlen(zTag);
  for(i=0; i<nTag-8 && zTag[i]!=':'; i++){}
  if( zTag[i]==':'
   && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0,0)!=0)
  ){
    char *zDate = mprintf("%s", &zTag[i+1]);
    char *zTagBase = mprintf("%.*s", i, zTag);
    char *zXDate;
    int nDate = strlen(zDate);
    if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
      zDate[nDate-3] = 'z';
      zDate[nDate-2] = 0;
    }
    zXDate = fossil_expand_datetime(zDate,0,1);
    if( zXDate==0 ) zXDate = zDate;
    rid = db_int(0,
      "SELECT event.objid, max(event.mtime)"
      "  FROM tag, tagxref, event"
      " WHERE tag.tagname='sym-%q' "
      "   AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
      "   AND event.objid=tagxref.rid "
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643

  if( rid>0 ){
    if( startOfBranch ) rid = start_of_branch(rid,1);
    return rid;
  }

  /* Pure numeric date/time */
  zDate = fossil_expand_datetime(zTag, 0);
  if( zDate ){
    rid = db_int(0,
      "SELECT objid FROM event"
      " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
      " ORDER BY mtime DESC LIMIT 1",
      fossil_roundup_date(zDate), zType);
    if( rid) return rid;







|







663
664
665
666
667
668
669
670
671
672
673
674
675
676
677

  if( rid>0 ){
    if( startOfBranch ) rid = start_of_branch(rid,1);
    return rid;
  }

  /* Pure numeric date/time */
  zDate = fossil_expand_datetime(zTag, 0,1);
  if( zDate ){
    rid = db_int(0,
      "SELECT objid FROM event"
      " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
      " ORDER BY mtime DESC LIMIT 1",
      fossil_roundup_date(zDate), zType);
    if( rid) return rid;
682
683
684
685
686
687
688























































689
690
691
692
693
694
695
  ){
    char *zNew = fossil_strndup(zTag, nTag-2);
    rid = symbolic_name_to_rid(zNew,zType);
    fossil_free(zNew);
  }
  return rid;
}
























































/*
** This routine takes a user-entered string and tries to convert it to
** an artifact hash.
**
** We first try to treat the string as an artifact hash, or at least a
** unique prefix of an artifact hash. The input may be in mixed case.







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







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
  ){
    char *zNew = fossil_strndup(zTag, nTag-2);
    rid = symbolic_name_to_rid(zNew,zType);
    fossil_free(zNew);
  }
  return rid;
}

/*
** Convert a symbolic name used as an argument to the a=, b=, or c=
** query parameters of timeline into a julianday mtime value.
**
** If pzDisplay is not null, then display text for the symbolic name might
** be written into *pzDisplay.  But that is not guaranteed.
**
** If bRoundUp is true and the symbolic name is a timestamp with less
** than millisecond resolution, then the timestamp is rounding up to the
** largest millisecond consistent with that timestamp.  If bRoundUp is
** false, then the resulting time is obtained by extending the timestamp
** with zeros (hence rounding down).  Use bRoundUp==1 if the result
** will be used in mtime<=$RESULT and use bRoundUp==0 if the result
** will be used in mtime>=$RESULT.
*/
double symbolic_name_to_mtime(
  const char *z,              /* Input symbolic name */
  const char **pzDisplay,     /* Perhaps write display text here, if not NULL */
  int bRoundUp                /* Round up if true */
){
  double mtime;
  int rid;
  const char *zDate;
  if( z==0 ) return -1.0;
  if( fossil_isdate(z) ){
    mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z);
    if( mtime>0.0 ) return mtime;
  }
  zDate = fossil_expand_datetime(z, 1, bRoundUp);
  if( zDate!=0 ){
    mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())",
                      bRoundUp ? fossil_roundup_date(zDate) : zDate);
    if( mtime>0.0 ){
      if( pzDisplay ){
        zDate = fossil_expand_datetime(z,0,0);
        *pzDisplay = fossil_strdup(zDate);
      }
      return mtime;
    }
  }
  rid = symbolic_name_to_rid(z, "*");
  if( rid ){
    mtime = mtime_of_rid(rid, 0.0);
  }else{
    mtime = db_double(-1.0,
        "SELECT max(event.mtime) FROM event, tag, tagxref"
        " WHERE tag.tagname GLOB 'event-%q*'"
        "   AND tagxref.tagid=tag.tagid AND tagxref.tagtype"
        "   AND event.objid=tagxref.rid",
        z
    );
  }
  return mtime;
}

/*
** This routine takes a user-entered string and tries to convert it to
** an artifact hash.
**
** We first try to treat the string as an artifact hash, or at least a
** unique prefix of an artifact hash. The input may be in mixed case.
Changes to src/patch.c.
696
697
698
699
700
701
702
















703
704
705
706
707
708
709
  }
  if( zDir && file_chdir(zDir,0) ){
    fossil_fatal("cannot change to directory \"%s\"", zDir);
  }
  fossil_free(zToFree);
  return zPatchFile;
}

















/*
** Create a FILE* that will execute the remote side of a push or pull
** using ssh (probably) or fossil for local pushes and pulls.  Return
** a FILE* obtained from popen() into which we write the patch, or from
** which we read the patch, depending on whether this is a push or pull.
*/







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







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
  }
  if( zDir && file_chdir(zDir,0) ){
    fossil_fatal("cannot change to directory \"%s\"", zDir);
  }
  fossil_free(zToFree);
  return zPatchFile;
}

/*
** Resolves a patch-command remote system name, accounting for patch
** aliases.
**
** If a CONFIG table entry matching name='patch-alias:$zKey' is found,
** the corresponding value is returned, else a fossil_strdup() of zKey
** is returned. The caller is responsible for passing the resulting
** string to fossil_free().
*/
static char *patch_resolve_remote(const char *zKey){
  char *zAlias = db_text(0, "SELECT value FROM config "
                            "WHERE name = 'patch-alias:%q'",
                            zKey);
  return zAlias ? zAlias : fossil_strdup(zKey);
}

/*
** Create a FILE* that will execute the remote side of a push or pull
** using ssh (probably) or fossil for local pushes and pulls.  Return
** a FILE* obtained from popen() into which we write the patch, or from
** which we read the patch, depending on whether this is a push or pull.
*/
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
  if( mFlags & PATCH_FORCE )  blob_appendf(&flgs, " -f");
  if( mFlags & PATCH_VERBOSE )  blob_appendf(&flgs, " -v");
  if( mFlags & PATCH_DRYRUN )  blob_appendf(&flgs, " -n");
  zForce = blob_size(&flgs)>0 ? blob_str(&flgs) : "";
  if( g.argc!=4 ){
    usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd));
  }
  zRemote = fossil_strdup(g.argv[3]);
  zDir = (char*)file_skip_userhost(zRemote);
  if( zDir==0 ){
    if( isRetry ) goto remote_command_error;
    zDir = zRemote;
    blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
    blob_appendf(&cmd, " patch %s%s %$ -", zRemoteCmd, zForce, zDir);
  }else{







|







743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
  if( mFlags & PATCH_FORCE )  blob_appendf(&flgs, " -f");
  if( mFlags & PATCH_VERBOSE )  blob_appendf(&flgs, " -v");
  if( mFlags & PATCH_DRYRUN )  blob_appendf(&flgs, " -n");
  zForce = blob_size(&flgs)>0 ? blob_str(&flgs) : "";
  if( g.argc!=4 ){
    usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd));
  }
  zRemote = patch_resolve_remote(g.argv[3]);
  zDir = (char*)file_skip_userhost(zRemote);
  if( zDir==0 ){
    if( isRetry ) goto remote_command_error;
    zDir = zRemote;
    blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
    blob_appendf(&cmd, " patch %s%s %$ -", zRemoteCmd, zForce, zDir);
  }else{
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
}

/*
** Toggle the use-path-for-ssh setting for the remote host defined
** by g.argv[3].
*/
static void patch_toggle_ssh_needs_path(void){
  char *zRemote = fossil_strdup(g.argv[3]);
  char *zDir = (char*)file_skip_userhost(zRemote);
  if( zDir ){
    *(char*)(zDir - 1) =  0;
    ssh_needs_path_argument(zRemote, 99);
  }
  fossil_free(zRemote);
}







|







792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
}

/*
** Toggle the use-path-for-ssh setting for the remote host defined
** by g.argv[3].
*/
static void patch_toggle_ssh_needs_path(void){
  char *zRemote = patch_resolve_remote(g.argv[3]);
  char *zDir = (char*)file_skip_userhost(zRemote);
  if( zDir ){
    *(char*)(zDir - 1) =  0;
    ssh_needs_path_argument(zRemote, 99);
  }
  fossil_free(zRemote);
}
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920












921
922
923
924
925
926
927
    }
  }
  db_finalize(&q);
  diff_end(pCfg, nErr);
  if( nErr ) fossil_fatal("abort due to prior errors");
}


/*
** COMMAND: patch
**
** Usage: %fossil patch SUBCOMMAND ?ARGS ..?
**
** This command is used to create, view, and apply Fossil binary patches.
** A Fossil binary patch is a single (binary) file that captures all of the
** uncommitted changes of a check-out.  Use Fossil binary patches to transfer
** proposed or incomplete changes between machines for testing or analysis.
**












** > fossil patch create [DIRECTORY] PATCHFILE
**
**       Create a new binary patch in PATCHFILE that captures all uncommitted
**       changes in the check-out at DIRECTORY, or the current directory if
**       DIRECTORY is omitted.  If PATCHFILE is "-" then the binary patch
**       is written to standard output.
**







<










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







919
920
921
922
923
924
925

926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
    }
  }
  db_finalize(&q);
  diff_end(pCfg, nErr);
  if( nErr ) fossil_fatal("abort due to prior errors");
}


/*
** COMMAND: patch
**
** Usage: %fossil patch SUBCOMMAND ?ARGS ..?
**
** This command is used to create, view, and apply Fossil binary patches.
** A Fossil binary patch is a single (binary) file that captures all of the
** uncommitted changes of a check-out.  Use Fossil binary patches to transfer
** proposed or incomplete changes between machines for testing or analysis.
**
** > fossil patch alias add|rm|ls|list ?ARGS?
**
**       Manage remote-name aliases, which act as short-form
**       equivalents to REMOTE-CHECKOUT strings. Aliases are local to
**       a given repository and do not sync. Subcommands:
**
**         ... add ALIAS REMOTE-CHECKOUT       Add ALIAS as an alias
**                                             for REMOTE-CHECKOUT.
**         ... ls|list                         List all local aliases.
**         ... rm ALIAS [ALIAS...]             Remove named aliases
**         ... rm --all                        Remove all aliases
**
** > fossil patch create [DIRECTORY] PATCHFILE
**
**       Create a new binary patch in PATCHFILE that captures all uncommitted
**       changes in the check-out at DIRECTORY, or the current directory if
**       DIRECTORY is omitted.  If PATCHFILE is "-" then the binary patch
**       is written to standard output.
**
994
995
996
997
998
999
1000
1001
1002
1003
1004






























































1005
1006
1007
1008
1009
1010
1011
**
*/
void patch_cmd(void){
  const char *zCmd;
  size_t n;
  if( g.argc<3 ){
    patch_usage:
    usage("apply|create|diff|gdiff|pull|push|view");
  }
  zCmd = g.argv[2];
  n = strlen(zCmd);






























































  if( strncmp(zCmd, "apply", n)==0 ){
    char *zIn;
    unsigned flags = 0;
    if( find_option("dry-run","n",0) )  flags |= PATCH_DRYRUN;
    if( find_option("verbose","v",0) )  flags |= PATCH_VERBOSE;
    if( find_option("force","f",0) )    flags |= PATCH_FORCE;
    zIn = patch_find_patch_filename("apply");







|



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







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
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
1096
1097
1098
1099
1100
**
*/
void patch_cmd(void){
  const char *zCmd;
  size_t n;
  if( g.argc<3 ){
    patch_usage:
    usage("alias|apply|create|diff|gdiff|pull|push|view");
  }
  zCmd = g.argv[2];
  n = strlen(zCmd);
  if( strncmp(zCmd, "alias", n)==0 ){
    const char * zArg = g.argc>3 ? g.argv[3] : 0;
    db_must_be_within_tree();
    if( 0==zArg ){
      goto usage_patch_alias;
    }else if( 0==strcmp("ls",zArg) || 0==strcmp("list",zArg) ){
      /* alias ls|list */
      Stmt q;
      int nAlias = 0;

      verify_all_options();
      db_prepare(&q, "SELECT substr(name,13), value FROM config "
                 "WHERE name GLOB 'patch-alias:*' ORDER BY name");
      while( SQLITE_ROW==db_step(&q) ){
        const char *zName = db_column_text(&q, 0);
        const char *zVal = db_column_text(&q, 1);
        ++nAlias;
        fossil_print("%s = %s\n", zName, zVal);
      }
      db_finalize(&q);
      if( 0==nAlias ){
        fossil_print("No patch aliases defined\n");
      }
    }else if( 0==strcmp("add", zArg) ){
      /* alias add localName remote */
      verify_all_options();
      if( 6!=g.argc ){
        usage("alias add localName remote");
      }
      db_unprotect(PROTECT_CONFIG);
      db_multi_exec("REPLACE INTO config (name, value, mtime) "
                    "VALUES ('patch-alias:%q', %Q, unixepoch())",
                    g.argv[4], g.argv[5]);
      db_protect_pop();
    }else if( 0==strcmp("rm", zArg) ){
      /* alias rm */
      const int fAll = 0!=find_option("all", 0, 0);
      if( fAll ? g.argc<4 : g.argc<5 ){
        usage("alias rm [-all] [aliasGlob [...aliasGlobN]]");
      }
      verify_all_options();
      db_unprotect(PROTECT_CONFIG);
      if( 0!=fAll ){
        db_multi_exec("DELETE FROM config WHERE name GLOB 'patch-alias:*'");
      }else{
        Stmt q;
        int i;
        db_prepare(&q, "DELETE FROM config WHERE name "
                   "GLOB 'patch-alias:' || :pattern");
        for(i = 4; i < g.argc; ++i){
          db_bind_text(&q, ":pattern", g.argv[i]);
          db_step(&q);
          db_reset(&q);
        }
        db_finalize(&q);
      }
      db_protect_pop();
    }else{
    usage_patch_alias:
      usage("alias ls|list|add|rm ...");
    }
  }else
  if( strncmp(zCmd, "apply", n)==0 ){
    char *zIn;
    unsigned flags = 0;
    if( find_option("dry-run","n",0) )  flags |= PATCH_DRYRUN;
    if( find_option("verbose","v",0) )  flags |= PATCH_VERBOSE;
    if( find_option("force","f",0) )    flags |= PATCH_FORCE;
    zIn = patch_find_patch_filename("apply");
1031
1032
1033
1034
1035
1036
1037




1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048

    if( find_option("tk",0,0)!=0 ){
      db_close(0);
      diff_tk("patch diff", 3);
      return;
    }
    db_find_and_open_repository(0, 0);




    if( find_option("force","f",0) )    flags |= PATCH_FORCE;
    diff_options(&DCfg, zCmd[0]=='g', 0);
    verify_all_options();
    zIn = patch_find_patch_filename("apply");
    patch_attach(zIn, stdin, 0);
    patch_diff(flags, &DCfg);
    fossil_free(zIn);
  }else
  if( strncmp(zCmd, "pull", n)==0 ){
    FILE *pIn = 0;
    unsigned flags = 0;







>
>
>
>



|







1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141

    if( find_option("tk",0,0)!=0 ){
      db_close(0);
      diff_tk("patch diff", 3);
      return;
    }
    db_find_and_open_repository(0, 0);
    if( gdiff_using_tk(zCmd[0]=='g') ){
      diff_tk("patch diff", 3);
      return;
    }
    if( find_option("force","f",0) )    flags |= PATCH_FORCE;
    diff_options(&DCfg, zCmd[0]=='g', 0);
    verify_all_options();
    zIn = patch_find_patch_filename("diff");
    patch_attach(zIn, stdin, 0);
    patch_diff(flags, &DCfg);
    fossil_free(zIn);
  }else
  if( strncmp(zCmd, "pull", n)==0 ){
    FILE *pIn = 0;
    unsigned flags = 0;
Changes to src/path.c.
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
**
** This file contains code used to trace paths of through the
** directed acyclic graph (DAG) of check-ins.
*/
#include "config.h"
#include "path.h"
#include <assert.h>


#if INTERFACE
/* Nodes for the paths through the DAG.
*/
struct PathNode {
  int rid;                 /* ID for this node */
  u8 fromIsParent;         /* True if pFrom is the parent of rid */
  u8 isPrim;               /* True if primary side of common ancestor */
  u8 isHidden;             /* Abbreviate output in "fossil bisect ls" */


  PathNode *pFrom;         /* Node we came from */
  union {
    PathNode *pPeer;       /* List of nodes of the same generation */
    PathNode *pTo;         /* Next on path from beginning to end */
  } u;
  PathNode *pAll;        /* List of all nodes */
};
#endif

/*
** Local variables for this module
*/
static struct {
  PathNode *pCurrent;   /* Current generation of nodes */
  PathNode *pAll;       /* All nodes */
  Bag seen;             /* Nodes seen before */
  int nStep;            /* Number of steps from first to last */
  int nNotHidden;       /* Number of steps not counting hidden nodes */


  PathNode *pStart;     /* Earliest node */
  PathNode *pEnd;       /* Most recent */
} path;


/*
** Return the first (last) element of the computed path.
*/
PathNode *path_first(void){ return path.pStart; }
PathNode *path_last(void){ return path.pEnd; }

/*
** Return the number of steps in the computed path.
*/
int path_length(void){ return path.nStep; }

/*
** Return the number of non-hidden steps in the computed path.
*/
int path_length_not_hidden(void){ return path.nNotHidden; }

/*




























** Create a new node
*/
static PathNode *path_new_node(int rid, PathNode *pFrom, int isParent){
  PathNode *p;

  p = fossil_malloc( sizeof(*p) );
  memset(p, 0, sizeof(*p));


  p->rid = rid;
  p->fromIsParent = isParent;
  p->pFrom = pFrom;
  p->u.pPeer = path.pCurrent;
  path.pCurrent = p;





  p->pAll = path.pAll;









  path.pAll = p;


  bag_insert(&path.seen, rid);
  return p;
}

/*
** Reset memory used by the shortest path algorithm.
*/
void path_reset(void){
  PathNode *p;
  while( path.pAll ){
    p = path.pAll;
    path.pAll = p->pAll;

    fossil_free(p);
  }
  bag_clear(&path.seen);
  memset(&path, 0, sizeof(path));
}

/*
** Construct the path from path.pStart to path.pEnd in the u.pTo fields.
*/
static void path_reverse_path(void){







>









>
>


|


|







|

<


>
>



>


















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|






>
>



|
|
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
|
>
>
|











>


|







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
**
** This file contains code used to trace paths of through the
** directed acyclic graph (DAG) of check-ins.
*/
#include "config.h"
#include "path.h"
#include <assert.h>
#include <math.h>

#if INTERFACE
/* Nodes for the paths through the DAG.
*/
struct PathNode {
  int rid;                 /* ID for this node */
  u8 fromIsParent;         /* True if pFrom is the parent of rid */
  u8 isPrim;               /* True if primary side of common ancestor */
  u8 isHidden;             /* Abbreviate output in "fossil bisect ls" */
  char *zBranch;           /* Branch name for this node.  Might be NULL */
  double mtime;            /* Date/time of this check-in */
  PathNode *pFrom;         /* Node we came from */
  union {
    double rCost;          /* Cost of getting to this node from pStart */
    PathNode *pTo;         /* Next on path from beginning to end */
  } u;
  PathNode *pAll;          /* List of all nodes */
};
#endif

/*
** Local variables for this module
*/
static struct {
  PQueue pending;       /* Nodes pending review for inclusion in the graph */
  PathNode *pAll;       /* All nodes */

  int nStep;            /* Number of steps from first to last */
  int nNotHidden;       /* Number of steps not counting hidden nodes */
  int brCost;           /* Extra cost for moving to a different branch */
  int revCost;          /* Extra cost for changing directions */
  PathNode *pStart;     /* Earliest node */
  PathNode *pEnd;       /* Most recent */
} path;
static int path_debug = 0;  /* Flag to enable debugging */

/*
** Return the first (last) element of the computed path.
*/
PathNode *path_first(void){ return path.pStart; }
PathNode *path_last(void){ return path.pEnd; }

/*
** Return the number of steps in the computed path.
*/
int path_length(void){ return path.nStep; }

/*
** Return the number of non-hidden steps in the computed path.
*/
int path_length_not_hidden(void){ return path.nNotHidden; }

/*
** Used for debugging only.
**
** Given a RID, return the ISO date/time string and branch for the
** corresponding check-in.  Memory is held locally and is overwritten
** with each call.
*/
char *path_rid_desc(int rid){
  static Stmt q;
  static char *zDesc = 0;
  db_static_prepare(&q, 
    "SELECT concat(strftime('%%Y%%m%%d%%H%%M',event.mtime),'/',value)"
    "  FROM event, tagxref"
    " WHERE event.objid=:rid"
    "   AND tagxref.rid=:rid"
    "   AND tagxref.tagid=%d"
    "   AND tagxref.tagtype>0",
    TAG_BRANCH
  );
  fossil_free(zDesc);
  db_bind_int(&q, ":rid", rid);
  if( db_step(&q)==SQLITE_ROW ){
    zDesc = fossil_strdup(db_column_text(&q,0));
  }
  db_reset(&q);
  return zDesc ? zDesc : "???";
}

/*
** Create a new node and insert it into the path.pending queue.
*/
static PathNode *path_new_node(int rid, PathNode *pFrom, int isParent){
  PathNode *p;

  p = fossil_malloc( sizeof(*p) );
  memset(p, 0, sizeof(*p));
  p->pAll = path.pAll;
  path.pAll = p;
  p->rid = rid;
  p->fromIsParent = isParent;
  p->pFrom = pFrom;
  p->u.rCost = pFrom ? pFrom->u.rCost : 0.0;
  if( path.brCost ){
    p->zBranch = branch_of_rid(rid);
    p->mtime = mtime_of_rid(rid, 0.0);
    if( pFrom ){
      p->u.rCost +=  fabs(pFrom->mtime - p->mtime);
      if( fossil_strcmp(p->zBranch, pFrom->zBranch)!=0 ){
        p->u.rCost += path.brCost;
      }
    }
  }else{
    /* When brCost==0, we try to minimize the number of nodes
    ** along the path.  The cost is just the number of nodes back
    ** to the start.  We do not need to know the branch name nor
    ** the mtime */
    p->u.rCost += 1.0;
  }
  if( path_debug ){
    fossil_print("PUSH %-50s cost = %g\n", path_rid_desc(p->rid), p->u.rCost);
  }
  pqueuex_insert_ptr(&path.pending, (void*)p, p->u.rCost);
  return p;
}

/*
** Reset memory used by the shortest path algorithm.
*/
void path_reset(void){
  PathNode *p;
  while( path.pAll ){
    p = path.pAll;
    path.pAll = p->pAll;
    fossil_free(p->zBranch);
    fossil_free(p);
  }
  pqueuex_clear(&path.pending);
  memset(&path, 0, sizeof(path));
}

/*
** Construct the path from path.pStart to path.pEnd in the u.pTo fields.
*/
static void path_reverse_path(void){
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
** Return NULL if no path is found.
*/
PathNode *path_shortest(
  int iFrom,          /* Path starts here */
  int iTo,            /* Path ends here */
  int directOnly,     /* No merge links if true */
  int oneWayOnly,     /* Parent->child only if true */
  Bag *pHidden        /* Hidden nodes */

){
  Stmt s;
  PathNode *pPrev;

  PathNode *p;

  path_reset();

  path.pStart = path_new_node(iFrom, 0, 0);
  if( iTo==iFrom ){
    path.pEnd = path.pStart;
    return path.pStart;
  }
  if( oneWayOnly && directOnly ){
    db_prepare(&s,
        "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim"
    );
  }else if( oneWayOnly ){
    db_prepare(&s,
        "SELECT cid, 1 FROM plink WHERE pid=:pid "
    );
  }else if( directOnly ){
    db_prepare(&s,
        "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim "
        "UNION ALL "
        "SELECT pid, 0 FROM plink WHERE cid=:pid AND isprim"
    );
  }else{
    db_prepare(&s,
        "SELECT cid, 1 FROM plink WHERE pid=:pid "
        "UNION ALL "
        "SELECT pid, 0 FROM plink WHERE cid=:pid"
    );
  }
  while( path.pCurrent ){
    path.nStep++;
    pPrev = path.pCurrent;
    path.pCurrent = 0;
    while( pPrev ){
      db_bind_int(&s, ":pid", pPrev->rid);
      while( db_step(&s)==SQLITE_ROW ){
        int cid = db_column_int(&s, 0);
        int isParent = db_column_int(&s, 1);
        if( bag_find(&path.seen, cid) ) continue;
        p = path_new_node(cid, pPrev, isParent);
        if( pHidden && bag_find(pHidden,cid) ) p->isHidden = 1;

        if( cid==iTo ){
          db_finalize(&s);
          path.pEnd = p;
          path_reverse_path();
          for(p=path.pStart->u.pTo; p; p=p->u.pTo ){
            if( !p->isHidden ) path.nNotHidden++;
          }
          return path.pStart;
        }











      }
      db_reset(&s);
      pPrev = pPrev->u.pPeer;
    }
  }
  db_finalize(&s);
  path_reset();
  return 0;
}

/*







|
>


<
>



>

















|





|


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







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
** Return NULL if no path is found.
*/
PathNode *path_shortest(
  int iFrom,          /* Path starts here */
  int iTo,            /* Path ends here */
  int directOnly,     /* No merge links if true */
  int oneWayOnly,     /* Parent->child only if true */
  Bag *pHidden,       /* Hidden nodes */
  int branchCost      /* Add extra cost to changing branches */
){
  Stmt s;

  Bag seen;
  PathNode *p;

  path_reset();
  path.brCost = branchCost;
  path.pStart = path_new_node(iFrom, 0, 0);
  if( iTo==iFrom ){
    path.pEnd = path.pStart;
    return path.pStart;
  }
  if( oneWayOnly && directOnly ){
    db_prepare(&s,
        "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim"
    );
  }else if( oneWayOnly ){
    db_prepare(&s,
        "SELECT cid, 1 FROM plink WHERE pid=:pid "
    );
  }else if( directOnly ){
    db_prepare(&s,
        "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim "
        "UNION ALL "
        "SELECT pid, 0 FROM plink WHERE :back AND cid=:pid AND isprim"
    );
  }else{
    db_prepare(&s,
        "SELECT cid, 1 FROM plink WHERE pid=:pid "
        "UNION ALL "
        "SELECT pid, 0 FROM plink WHERE :back AND cid=:pid"
    );
  }
  bag_init(&seen);
  while( (p = pqueuex_extract_ptr(&path.pending))!=0 ){
    if( path_debug ){


      printf("PULL %s %g\n", path_rid_desc(p->rid), p->u.rCost);






    }
    if( p->rid==iTo ){
      db_finalize(&s);
      path.pEnd = p;
      path_reverse_path();
      for(p=path.pStart->u.pTo; p; p=p->u.pTo ){
        if( !p->isHidden ) path.nNotHidden++;
      }
      return path.pStart;
    }
    if( bag_find(&seen, p->rid) ) continue;
    bag_insert(&seen, p->rid);
    db_bind_int(&s, ":pid", p->rid);
    if( !oneWayOnly ) db_bind_int(&s, ":back", !p->fromIsParent);
    while( db_step(&s)==SQLITE_ROW ){
      int cid = db_column_int(&s, 0);
      int isParent = db_column_int(&s, 1);
      PathNode *pNew;
      if( bag_find(&seen, cid) ) continue;
      pNew = path_new_node(cid, p, isParent);
      if( pHidden && bag_find(pHidden,cid) ) pNew->isHidden = 1;
    }
    db_reset(&s);


  }
  db_finalize(&s);
  path_reset();
  return 0;
}

/*
213
214
215
216
217
218
219












220
221
222
223
224
225
226
*/
PathNode *path_next(void){
  PathNode *p;
  p = path.pStart;
  if( p ) p = p->u.pTo;
  return p;
}













/*
** Return an estimate of the number of comparisons remaining in order
** to bisect path.  This is based on the log2() of path.nStep.
*/
int path_search_depth(void){
  int i, j;







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







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
*/
PathNode *path_next(void){
  PathNode *p;
  p = path.pStart;
  if( p ) p = p->u.pTo;
  return p;
}

/*
** Return the branch for a path node.
**
** Storage space is managed by the path subsystem.  The returned value
** is valid until the path is reset.
*/
const char *path_branch(PathNode *p){
  if( p==0 ) return 0;
  if( p->zBranch==0 ) p->zBranch = branch_of_rid(p->rid);
  return p->zBranch;
}

/*
** Return an estimate of the number of comparisons remaining in order
** to bisect path.  This is based on the log2() of path.nStep.
*/
int path_search_depth(void){
  int i, j;
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
void path_shortest_stored_in_ancestor_table(
  int origid,     /* RID for check-in at start of the path */
  int cid         /* RID for check-in at the end of the path */
){
  PathNode *pPath;
  int gen = 0;
  Stmt ins;
  pPath = path_shortest(cid, origid, 1, 0, 0);
  db_multi_exec(
    "CREATE TEMP TABLE IF NOT EXISTS ancestor("
    "  rid INT UNIQUE,"
    "  generation INTEGER PRIMARY KEY"
    ");"
    "DELETE FROM ancestor;"
  );







|







304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
void path_shortest_stored_in_ancestor_table(
  int origid,     /* RID for check-in at start of the path */
  int cid         /* RID for check-in at the end of the path */
){
  PathNode *pPath;
  int gen = 0;
  Stmt ins;
  pPath = path_shortest(cid, origid, 1, 0, 0, 0);
  db_multi_exec(
    "CREATE TEMP TABLE IF NOT EXISTS ancestor("
    "  rid INT UNIQUE,"
    "  generation INTEGER PRIMARY KEY"
    ");"
    "DELETE FROM ancestor;"
  );
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
  db_finalize(&ins);
  path_reset();
}

/*
** COMMAND: test-shortest-path
**
** Usage: %fossil test-shortest-path ?--no-merge? VERSION1 VERSION2
**
** Report the shortest path between two check-ins.  If the --no-merge flag




** is used, follow only direct parent-child paths and omit merge links.

*/
void shortest_path_test_cmd(void){
  int iFrom;
  int iTo;
  PathNode *p;
  int n;
  int directOnly;
  int oneWay;


  db_find_and_open_repository(0,0);
  directOnly = find_option("no-merge",0,0)!=0;
  oneWay = find_option("one-way",0,0)!=0;


  if( g.argc!=4 ) usage("VERSION1 VERSION2");
  iFrom = name_to_rid(g.argv[2]);
  iTo = name_to_rid(g.argv[3]);
  p = path_shortest(iFrom, iTo, directOnly, oneWay, 0);

  if( p==0 ){
    fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]);
  }
  for(n=1, p=path.pStart; p; p=p->u.pTo, n++){
    char *z;
    z = db_text(0,
      "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)"
      "  FROM blob, event"
      " WHERE blob.rid=%d AND event.objid=%d AND event.type='ci'",
      p->rid, p->rid);
    fossil_print("%4d: %5d %s", n, p->rid, z);
    fossil_free(z);
    if( p->u.pTo ){
      fossil_print(" is a %s of\n",
                   p->u.pTo->fromIsParent ? "parent" : "child");
    }else{
      fossil_print("\n");
    }
  }

}

/*
** Find the closest common ancestor of two nodes.  "Closest" means the
** fewest number of arcs.
*/
int path_common_ancestor(int iMe, int iYou){
  Stmt s;
  PathNode *pPrev;
  PathNode *p;
  Bag me, you;

  if( iMe==iYou ) return iMe;
  if( iMe==0 || iYou==0 ) return 0;
  path_reset();
  path.pStart = path_new_node(iMe, 0, 0);
  path.pStart->isPrim = 1;
  path.pEnd = path_new_node(iYou, 0, 0);
  db_prepare(&s, "SELECT pid FROM plink WHERE cid=:cid");
  bag_init(&me);
  bag_insert(&me, iMe);
  bag_init(&you);
  bag_insert(&you, iYou);
  while( path.pCurrent ){
    pPrev = path.pCurrent;
    path.pCurrent = 0;
    while( pPrev ){
      db_bind_int(&s, ":cid", pPrev->rid);
      while( db_step(&s)==SQLITE_ROW ){
        int pid = db_column_int(&s, 0);
        if( bag_find(pPrev->isPrim ? &you : &me, pid) ){
          /* pid is the common ancestor */
          PathNode *pNext;
          for(p=path.pAll; p && p->rid!=pid; p=p->pAll){}
          assert( p!=0 );
          pNext = p;
          while( pNext ){
            pNext = p->pFrom;
            p->pFrom = pPrev;
            pPrev = p;
            p = pNext;
          }
          if( pPrev==path.pStart ) path.pStart = path.pEnd;
          path.pEnd = pPrev;
          path_reverse_path();
          db_finalize(&s);
          return pid;
        }else if( bag_find(&path.seen, pid) ){
          /* pid is just an alternative path on one of the legs */
          continue;
        }
        p = path_new_node(pid, pPrev, 0);
        p->isPrim = pPrev->isPrim;
        bag_insert(pPrev->isPrim ? &me : &you, pid);
      }
      db_reset(&s);
      pPrev = pPrev->u.pPeer;
    }
  }
  db_finalize(&s);
  path_reset();
  return 0;
}

/*







|

|
>
>
>
>
|
>








>




>
>



|
>




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








|














<
<
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
<







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
  db_finalize(&ins);
  path_reset();
}

/*
** COMMAND: test-shortest-path
**
** Usage: %fossil test-shortest-path [OPTIONS] VERSION1 VERSION2
**
** Report the shortest path between two check-ins.  Options:
**
**    --branch-cost N    Additional cost N for changing branches
**    --debug            Show debugging output
**    --one-way          One-way forwards in time, parent->child only
**    --no-merge         Follow only direct parent-child paths and omit
**                       merge links.
*/
void shortest_path_test_cmd(void){
  int iFrom;
  int iTo;
  PathNode *p;
  int n;
  int directOnly;
  int oneWay;
  const char *zBrCost;

  db_find_and_open_repository(0,0);
  directOnly = find_option("no-merge",0,0)!=0;
  oneWay = find_option("one-way",0,0)!=0;
  zBrCost = find_option("branch-cost",0,1);
  if( find_option("debug",0,0)!=0 ) path_debug = 1;
  if( g.argc!=4 ) usage("VERSION1 VERSION2");
  iFrom = name_to_rid(g.argv[2]);
  iTo = name_to_rid(g.argv[3]);
  p = path_shortest(iFrom, iTo, directOnly, oneWay, 0,
                    zBrCost ? atoi(zBrCost) : 0);
  if( p==0 ){
    fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]);
  }
  for(n=1, p=path.pStart; p; p=p->u.pTo, n++){












    fossil_print("%4d: %s\n", n, path_rid_desc(p->rid));
  }

  path_debug = 0;
}

/*
** Find the closest common ancestor of two nodes.  "Closest" means the
** fewest number of arcs.
*/
int path_common_ancestor(int iMe, int iYou){
  Stmt s;
  PathNode *pThis;
  PathNode *p;
  Bag me, you;

  if( iMe==iYou ) return iMe;
  if( iMe==0 || iYou==0 ) return 0;
  path_reset();
  path.pStart = path_new_node(iMe, 0, 0);
  path.pStart->isPrim = 1;
  path.pEnd = path_new_node(iYou, 0, 0);
  db_prepare(&s, "SELECT pid FROM plink WHERE cid=:cid");
  bag_init(&me);
  bag_insert(&me, iMe);
  bag_init(&you);
  bag_insert(&you, iYou);



  while( (pThis = pqueuex_extract_ptr(&path.pending))!=0 ){
    db_bind_int(&s, ":cid", pThis->rid);
    while( db_step(&s)==SQLITE_ROW ){
      int pid = db_column_int(&s, 0);
      if( bag_find(pThis->isPrim ? &you : &me, pid) ){
        /* pid is the common ancestor */
        PathNode *pNext;
        for(p=path.pAll; p && p->rid!=pid; p=p->pAll){}
        assert( p!=0 );
        pNext = p;
        while( pNext ){
          pNext = p->pFrom;
          p->pFrom = pThis;
          pThis = p;
          p = pNext;
        }
        if( pThis==path.pStart ) path.pStart = path.pEnd;
        path.pEnd = pThis;
        path_reverse_path();
        db_finalize(&s);
        return pid;
      }else if( bag_find(pThis->isPrim ? &me : &you, pid) ){
        /* pid is just an alternative path to a node we've already visited */
        continue;
      }
      p = path_new_node(pid, pThis, 0);
      p->isPrim = pThis->isPrim;
      bag_insert(pThis->isPrim ? &me : &you, pid);
    }
    db_reset(&s);


  }
  db_finalize(&s);
  path_reset();
  return 0;
}

/*
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
  if(0==iFrom){
    fossil_fatal("Invalid 'from' RID: 0");
  }else if(0==iTo){
    fossil_fatal("Invalid 'to' RID: 0");
  }
  if( iFrom==iTo ) return;
  path_reset();
  p = path_shortest(iFrom, iTo, 1, revOK==0, 0);
  if( p==0 ) return;
  path_reverse_path();
  db_prepare(&q1,
     "SELECT pfnid, fnid FROM mlink"
     " WHERE mid=:mid AND (pfnid>0 OR fid==0)"
     " ORDER BY pfnid"
  );







|







511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
  if(0==iFrom){
    fossil_fatal("Invalid 'from' RID: 0");
  }else if(0==iTo){
    fossil_fatal("Invalid 'to' RID: 0");
  }
  if( iFrom==iTo ) return;
  path_reset();
  p = path_shortest(iFrom, iTo, 1, revOK==0, 0, 0);
  if( p==0 ) return;
  path_reverse_path();
  db_prepare(&q1,
     "SELECT pfnid, fnid FROM mlink"
     " WHERE mid=:mid AND (pfnid>0 OR fid==0)"
     " ORDER BY pfnid"
  );
Changes to src/pqueue.c.
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
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to implement a priority queue.
** A priority queue is a list of items order by a floating point
** value.  We can insert integers with each integer tied to its
** value then extract the integer with the smallest value.

**
** The way this queue is used, we never expect it to contain more
** than 2 or 3 elements, so a simple array is sufficient as the
** implementation.  This could give worst case O(N) insert times,








** but because of the nature of the problem we expect O(1) performance.
**
** Compatibility note:  Some versions of OpenSSL export a symbols
** like "pqueue_insert".  This is, technically, a bug in OpenSSL.
** We work around it here by using "pqueuex_" instead of "pqueue_".
*/
#include "config.h"
#include "pqueue.h"
#include <assert.h>


#if INTERFACE
/*
** An integer can appear in the bag at most once.
** Integers must be positive.
*/
struct PQueue {
  int cnt;   /* Number of entries in the queue */
  int sz;    /* Number of slots in a[] */
  struct QueueElement {

    int id;          /* ID of the element */


    double value;    /* Value of element.  Kept in ascending order */
  } *a;
};
#endif

/*
** Initialize a PQueue structure







|
|
>

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



















>
|
>
>







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
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to implement a priority queue.
** A priority queue is a list of items order by a floating point
** value.  Each value can be associated with either a pointer or
** an integer.  Items are inserted into the queue in an arbitrary
** order, but are returned in order of the floating point value.
**


** This implementation uses a heap of QueueElement objects.  The
** root of the heap is PQueue.a[0].  Each node a[x] has two daughter
** nodes a[x*2+1] and a[x*2+2].  The mother node of a[y] is a[(y-1)/2]
** (assuming integer division rounded down).  The following is always true:
**
**    The value of any node is less than or equal two the values
**    of both daughter nodes.  (The Heap Property).
**
** A consequence of the heap property is that a[0] always contains
** the node with the smallest value.
**
** Compatibility note:  Some versions of OpenSSL export a symbols
** like "pqueue_insert".  This is, technically, a bug in OpenSSL.
** We work around it here by using "pqueuex_" instead of "pqueue_".
*/
#include "config.h"
#include "pqueue.h"
#include <assert.h>


#if INTERFACE
/*
** An integer can appear in the bag at most once.
** Integers must be positive.
*/
struct PQueue {
  int cnt;   /* Number of entries in the queue */
  int sz;    /* Number of slots in a[] */
  struct QueueElement {
    union {
      int id;          /* ID of the element */
      void *p;         /* Pointer to an object */
    } u;
    double value;    /* Value of element.  Kept in ascending order */
  } *a;
};
#endif

/*
** Initialize a PQueue structure
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






























































/*
** Change the size of the queue so that it contains N slots
*/
static void pqueuex_resize(PQueue *p, int N){
  p->a = fossil_realloc(p->a, sizeof(p->a[0])*N);
  p->sz = N;
}

/*


























** Insert element e into the queue.
*/
void pqueuex_insert(PQueue *p, int e, double v){

















  int i, j;



  if( p->cnt+1>p->sz ){
    pqueuex_resize(p, p->cnt+5);
  }
  for(i=0; i<p->cnt; i++){

    if( p->a[i].value>v ){
      for(j=p->cnt; j>i; j--){


        p->a[j] = p->a[j-1];

      }
      break;
    }
  }
  p->a[i].id = e;
  p->a[i].value = v;
  p->cnt++;
}

/*
** Extract the first element from the queue (the element with
** the smallest value) and return its ID.  Return 0 if the queue
** is empty.
*/
int pqueuex_extract(PQueue *p){
  int e, i;
  if( p->cnt==0 ){
    return 0;
  }
  e = p->a[0].id;
  for(i=0; i<p->cnt-1; i++){
    p->a[i] = p->a[i+1];
  }


  p->cnt--;




  return e;
}







































































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



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

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








|



|
|
|
|
>
>
|
>
>
>
>
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
** Change the size of the queue so that it contains N slots
*/
static void pqueuex_resize(PQueue *p, int N){
  p->a = fossil_realloc(p->a, sizeof(p->a[0])*N);
  p->sz = N;
}

/*
** Allocate a new queue entry and return a pointer to it.
*/
static struct QueueElement *pqueuex_new_entry(PQueue *p){
  if( p->cnt+1>p->sz ){
    pqueuex_resize(p, p->cnt+7);
  }
  return &p->a[p->cnt++];
}

/*
** Element p->a[p->cnt-1] has just been inserted.  Shift entries
** around so as to preserve the heap property.
*/
static void pqueuex_rebalance(PQueue *p){
  int i, j;
  struct QueueElement *a = p->a;
  i = p->cnt-1;
  while( (j = (i-1)/2)>=0 && a[j].value>a[i].value ){
    struct QueueElement t = a[j];
    a[j] = a[i];
    a[i] = t;
    i = j;
  }
}

/*
** Insert element e into the queue.
*/
void pqueuex_insert(PQueue *p, int e, double v){
  struct QueueElement *pE = pqueuex_new_entry(p);
  pE->value = v;
  pE->u.id = e;
  pqueuex_rebalance(p);
}
void pqueuex_insert_ptr(PQueue *p, void *pPtr, double v){
  struct QueueElement *pE = pqueuex_new_entry(p);
  pE->value = v;
  pE->u.p = pPtr;
  pqueuex_rebalance(p);
}

/*
** Remove and discard p->a[0] element from the queue.  Rearrange
** nodes to preserve the heap property.
*/
static void pqueuex_pop(PQueue *p){
  int i, j;
  struct QueueElement *a = p->a;
  struct QueueElement tmp;
  i = 0;
  a[0] = a[p->cnt-1];
  p->cnt--;

  while( (j = i*2+1)<p->cnt ){
    if( j+1<p->cnt && a[j].value > a[j+1].value ) j++;
    if( a[i].value < a[j].value ) break;

    tmp = a[i];
    a[i] = a[j];
    a[j] = tmp;
    i = j;
  }






}

/*
** Extract the first element from the queue (the element with
** the smallest value) and return its ID.  Return 0 if the queue
** is empty.
*/
int pqueuex_extract(PQueue *p){
  int e;
  if( p->cnt==0 ){
    return 0;
  }
  e = p->a[0].u.id;
  pqueuex_pop(p);
  return e;
}
void *pqueuex_extract_ptr(PQueue *p){
  void *pPtr;
  if( p->cnt==0 ){
    return 0;
  }
  pPtr = p->a[0].u.p;
  pqueuex_pop(p);
  return pPtr;
}

/*
** Print the entire heap associated with the test-pqueue command.
*/
static void pqueuex_test_print(PQueue *p){
  int j;
  for(j=0; j<p->cnt; j++){
    fossil_print("(%d) %g/%s ",j,p->a[j].value,p->a[j].u.p);
  }
  fossil_print("\n");
}

/*
** COMMAND: test-pqueue
**
** This command is used for testing the PQueue object.  There are one
** or more arguments, each of the form:
**
**     (1)    NUMBER/TEXT
**     (2)    ^
**     (3)    -v
**
** Form (1) arguments add an entry to the queue with value NUMBER and
** content TEXT.  Form (2) pops off the queue entry with the smallest
** value.  Form (3) (the -v option) causes the heap to be displayed after
** each subsequent operation.
*/
void pqueuex_test_cmd(void){
  int i;
  PQueue x;
  const char *zId;
  int bDebug = 0;

  pqueuex_init(&x);
  for(i=2; i<g.argc; i++){
    const char *zArg = g.argv[i];
    if( strcmp(zArg,"-v")==0 ){
      bDebug = 1;
    }else if( strcmp(zArg, "^")==0 ){
      zId = pqueuex_extract_ptr(&x);
      if( zId==0 ){
        fossil_print("%2d: POP     NULL\n", i);
      }else{
        fossil_print("%2d: POP     \"%s\"\n", i, zId);
      }
      if( bDebug) pqueuex_test_print(&x);
    }else{
      double r = atof(zArg);
      zId = strchr(zArg,'/');
      if( zId==0 ) zId = zArg;
      if( zId[0]=='/' ) zId++;
      pqueuex_insert_ptr(&x, (void*)zId, r);
      fossil_print("%2d: INSERT  \"%s\"\n", i, zId);
      if( bDebug) pqueuex_test_print(&x);
    }
  }
  while( (zId = pqueuex_extract_ptr(&x))!=0 ){
    fossil_print("... POP     \"%s\"\n", zId);
    if( bDebug) pqueuex_test_print(&x);
  }
  pqueuex_clear(&x);
}
Changes to src/printf.c.
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
static int StrNLen32(const char *z, int N){
  int n = 0;
  while( (N-- != 0) && *(z++)!=0 ){ n++; }
  return n;
}
#endif


















/*
** Return an appropriate set of flags for wiki_convert() for displaying
** comments on a timeline.  These flag settings are determined by
** configuration parameters.
**
** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2
** flags) and is false for plain "%W".  The ! indicates that the text is

** to be rendered on a form rather than the timeline and that block markup
** is acceptable even if the "timeline-block-markup" setting is false.


*/
static int wiki_convert_flags(int altForm2){
  static int wikiFlags = 0;

  if( wikiFlags==0 ){
    if( altForm2 || db_get_boolean("timeline-block-markup", 0) ){
      wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;
    }else{
      wikiFlags = WIKI_INLINE | WIKI_NOBLOCK | WIKI_NOBADLINKS;
    }
    if( db_get_boolean("timeline-plaintext", 0) ){
      wikiFlags |= WIKI_LINKSONLY;
    }
    if( db_get_boolean("timeline-hard-newlines", 0) ){
      wikiFlags |= WIKI_NEWLINE;
    }
  }







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






|
>
|
<
>
>

|

>

<
|
<
<
<







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
static int StrNLen32(const char *z, int N){
  int n = 0;
  while( (N-- != 0) && *(z++)!=0 ){ n++; }
  return n;
}
#endif

/*
** SETTING: timeline-plaintext         boolean default=off
**
** If enabled, no wiki-formatting is done for timeline comment messages.
** Hyperlinks are activated, but they show up on screen using the 
** complete input text, not just the display text.  No other formatting
** is done.
*/
/*
** SETTING: timeline-hard-newlines     boolean default=off
**
** If enabled, the timeline honors newline characters in check-in comments.
** In other words, newlines are coverted into <br> for HTML display.
** The default behavior, when this setting is off, is that newlines are
** treated like any other whitespace character.
*/

/*
** Return an appropriate set of flags for wiki_convert() for displaying
** comments on a timeline.  These flag settings are determined by
** configuration parameters.
**
** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2
** flags) and is false for plain "%W".  The ! flag indicates that the
** formatting is for display of a check-in comment on the timeline.  Such
** comments used to be renderedd differently, but ever since 2020, they

** have been rendered identially, so the ! flag does not make any different
** in the output any more.
*/
int wiki_convert_flags(int altForm2){
  static int wikiFlags = 0;
  (void)altForm2;
  if( wikiFlags==0 ){

    wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;



    if( db_get_boolean("timeline-plaintext", 0) ){
      wikiFlags |= WIKI_LINKSONLY;
    }
    if( db_get_boolean("timeline-hard-newlines", 0) ){
      wikiFlags |= WIKI_NEWLINE;
    }
  }
Changes to src/rebuild.c.
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
               zPassword);
  hash_user_password(g.zLogin);
}

/*
** COMMAND: deconstruct*
**
** Usage %fossil deconstruct ?OPTIONS? DESTINATION
**
** This command exports all artifacts of a given repository and writes all
** artifacts to the file system.  The DESTINATION directory will be populated
** with subdirectories AA and files AA/BBBBBBBBB.., where AABBBBBBBBB.. is the
** 40+ character artifact ID, AA the first 2 characters.
** If -L|--prefixlength is given, the length (default 2) of the directory prefix
** can be set to 0,1,..,9 characters.







|







1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
               zPassword);
  hash_user_password(g.zLogin);
}

/*
** COMMAND: deconstruct*
**
** Usage: %fossil deconstruct ?OPTIONS? DESTINATION
**
** This command exports all artifacts of a given repository and writes all
** artifacts to the file system.  The DESTINATION directory will be populated
** with subdirectories AA and files AA/BBBBBBBBB.., where AABBBBBBBBB.. is the
** 40+ character artifact ID, AA the first 2 characters.
** If -L|--prefixlength is given, the length (default 2) of the directory prefix
** can be set to 0,1,..,9 characters.
Changes to src/regexp.c.
680
681
682
683
684
685
686







































687
688
689
690
691
692
693
      }
    }
    if( j>0 && pRe->zInit[j-1]==0 ) j--;
    pRe->nInit = j;
  }
  return pRe->zErr;
}








































/*
** Implementation of the regexp() SQL function.  This function implements
** the build-in REGEXP operator.  The first argument to the function is the
** pattern and the second argument is the string.  So, the SQL statements:
**
**       A REGEXP B







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







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
      }
    }
    if( j>0 && pRe->zInit[j-1]==0 ) j--;
    pRe->nInit = j;
  }
  return pRe->zErr;
}

/*
** The input zIn is a string that we want to match exactly as part of
** a regular expression.  Return a new string (in space obtained from
** fossil_malloc() or the equivalent) that escapes all regexp syntax
** characters in zIn.
*/
char *re_quote(const char *zIn){
  Blob out;
  blob_init(&out, 0, 0);
  while( zIn[0] ){
    switch( zIn[0] ){
      case '.':
      case '?':
      case '*':
      case '+':
      case '\\':
      case '(':
      case ')':
      case '[':
      case ']':
      case '|':
      case '^':
      case '$':
      case '{':
      case '}': {
        blob_appendf(&out,"\\x%02x", (unsigned char)zIn[0]);
        break;
      }
      default: {
        blob_append_char(&out, zIn[0]);
        break;
      }
    }
    zIn++;
  }
  blob_materialize(&out);
  return out.aData;
}

/*
** Implementation of the regexp() SQL function.  This function implements
** the build-in REGEXP operator.  The first argument to the function is the
** pattern and the second argument is the string.  So, the SQL statements:
**
**       A REGEXP B
Changes to src/repolist.c.
314
315
316
317
318
319
320

321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
    login_check_credentials();
    style_set_current_feature("repolist");
    style_header("Repository List");
    @ %s(blob_str(&html))
    style_table_sorter();
    style_finish_page();
  }else{

    /* If no repositories were found that had the "repolist_skin"
    ** property set, then use a default skin */
    @ <html>
    @ <head>
    @ <base href="%s(g.zBaseURL)/">
    @ <meta name="viewport" content="width=device-width, initial-scale=1.0">
    @ <title>Repository List</title>
    @ </head>
    @ <body>
    @ <h1 align="center">Fossil Repositories</h1>
    @ %s(blob_str(&html))
    @ <script>%s(builtin_text("sorttable.js"))</script>
    @ </body>
    @ </html>
  }
  blob_reset(&html);
  cgi_reply();







>






|


|







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
    login_check_credentials();
    style_set_current_feature("repolist");
    style_header("Repository List");
    @ %s(blob_str(&html))
    style_table_sorter();
    style_finish_page();
  }else{
    const char *zTitle = PD("FOSSIL_REPOLIST_TITLE","Repository List");
    /* If no repositories were found that had the "repolist_skin"
    ** property set, then use a default skin */
    @ <html>
    @ <head>
    @ <base href="%s(g.zBaseURL)/">
    @ <meta name="viewport" content="width=device-width, initial-scale=1.0">
    @ <title>%h(zTitle)</title>
    @ </head>
    @ <body>
    @ <h1 align="center">%h(zTitle)</h1>
    @ %s(blob_str(&html))
    @ <script>%s(builtin_text("sorttable.js"))</script>
    @ </body>
    @ </html>
  }
  blob_reset(&html);
  cgi_reply();
Changes to src/schema.c.
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523

/*
** Predefined tagid values
*/
#if INTERFACE
# define TAG_BGCOLOR    1     /* Set the background color for display */
# define TAG_COMMENT    2     /* The check-in comment */
# define TAG_USER       3     /* User who made a checking */
# define TAG_DATE       4     /* The date of a check-in */
# define TAG_HIDDEN     5     /* Do not display in timeline */
# define TAG_PRIVATE    6     /* Do not sync */
# define TAG_CLUSTER    7     /* A cluster */
# define TAG_BRANCH     8     /* Value is name of the current branch */
# define TAG_CLOSED     9     /* Do not display this check-in as a leaf */
# define TAG_PARENT     10    /* Change to parentage on a check-in */







|







509
510
511
512
513
514
515
516
517
518
519
520
521
522
523

/*
** Predefined tagid values
*/
#if INTERFACE
# define TAG_BGCOLOR    1     /* Set the background color for display */
# define TAG_COMMENT    2     /* The check-in comment */
# define TAG_USER       3     /* User who made a check-in */
# define TAG_DATE       4     /* The date of a check-in */
# define TAG_HIDDEN     5     /* Do not display in timeline */
# define TAG_PRIVATE    6     /* Do not sync */
# define TAG_CLUSTER    7     /* A cluster */
# define TAG_BRANCH     8     /* Value is name of the current branch */
# define TAG_CLOSED     9     /* Do not display this check-in as a leaf */
# define TAG_PARENT     10    /* Change to parentage on a check-in */
Changes to src/search.c.
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
680
681
682
683
684
685
}

/*
** Testing the search function.
**
** COMMAND: search*
**
** Usage: %fossil search [-a|-all] [-n|-limit #] [-W|-width #] pattern...
**
** Search for timeline entries matching all words provided on the
** command line. Whole-word matches scope more highly than partial
** matches.
**
** Note:  This command only searches the EVENT table.  So it will only
** display check-in comments or other comments that appear on an
** unaugmented timeline.  It does not search document text or forum






** messages.


**
** Outputs, by default, some top-N fraction of the results. The -all
** option can be used to output all matches, regardless of their search
** score.  The -limit option can be used to limit the number of entries
** returned.  The -width option can be used to set the output width used


** when printing matches.
**
** Options:
**     -a|--all          Output all matches, not just best matches
**     --debug           Show additional debug content on --fts search
**     --fts             Use the full-text search mechanism (testing only)



**     -n|--limit N      Limit output to N matches
**     --scope SCOPE     Scope of search.  Valid for --fts only.  One or
**                       more of: all, c, d, e, f, t, w.  Defaults to all.
**     -W|--width WIDTH  Set display width to WIDTH columns, 0 for
**                       unlimited. Defaults the terminal's width.

*/
void search_cmd(void){
  Blob pattern;
  int i;
  Blob sql = empty_blob;
  Stmt q;
  int iBest;


  char fAll = NULL != find_option("all", "a", 0);
  const char *zLimit = find_option("limit","n",1);

  const char *zWidth = find_option("width","W",1);
  const char *zScope = find_option("scope",0,1);
  int bDebug = find_option("debug",0,0)!=0;
  int nLimit = zLimit ? atoi(zLimit) : -1000;
  int width;












  int bFts = find_option("fts",0,0)!=0;


  if( zWidth ){
    width = atoi(zWidth);
    if( (width!=0) && (width<=20) ){
      fossil_fatal("-W|--width value must be >20 or 0");
    }
  }else{
    width = -1;
  }




























  db_find_and_open_repository(0, 0);

  if( g.argc<3 ) return;

















  blob_init(&pattern, g.argv[2], -1);
  for(i=3; i<g.argc; i++){
    blob_appendf(&pattern, " %s", g.argv[i]);
  }
  if( bFts ){
    /* Search using FTS */
    Blob com;
    Blob snip;
    const char *zPattern = blob_str(&pattern);
    int srchFlags;
    unsigned int j;
    if( zScope==0 ){
      srchFlags = SRCH_ALL;
    }else{
      srchFlags = 0;
      for(i=0; zScope[i]; i++){
        switch( zScope[i] ){
          case 'a':  srchFlags = SRCH_ALL;  break;
          case 'c':  srchFlags |= SRCH_CKIN;     break;
          case 'd':  srchFlags |= SRCH_DOC;      break;
          case 'e':  srchFlags |= SRCH_TECHNOTE; break;
          case 'f':  srchFlags |= SRCH_FORUM;    break;
          case 't':  srchFlags |= SRCH_TKT;      break;
          case 'w':  srchFlags |= SRCH_WIKI;     break;
        }
      }
    }
    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 */
      search_indexed(zPattern, srchFlags);   /* Indexed search */



    }
    db_prepare(&q, "SELECT snip, label, score, id, date"
                   "  FROM x"
                   " ORDER BY score DESC, date DESC;");
    blob_init(&com, 0, 0);
    blob_init(&snip, 0, 0);
    if( width<0 ) width = 80;
    while( db_step(&q)==SQLITE_ROW ){
      const char *zSnippet = db_column_text(&q, 0);
      const char *zLabel = db_column_text(&q, 1);
      const char *zDate = db_column_text(&q, 4);
      const char *zScore = db_column_text(&q, 2);
      const char *zId = db_column_text(&q, 3);

      blob_appendf(&snip, "%s", zSnippet);
      for(j=0; j<snip.nUsed; j++){
        if( snip.aData[j]=='\n' ){
          if( j>0 && snip.aData[j-1]=='\r' ) snip.aData[j-1] = ' ';
          snip.aData[j] = ' ';
        }
      }

      blob_appendf(&com, "%s\n%s\n%s", zLabel, blob_str(&snip), zDate);
      if( bDebug ){
        blob_appendf(&com," score: %s id: %s", zScore, zId);
      }
      comment_print(blob_str(&com), 0, 5, width,
            COMMENT_PRINT_TRIM_CRLF |
            COMMENT_PRINT_WORD_BREAK |







|

|
|
|

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

|
<
<
<
>
>
|


|
|
|
>
>
>

|
|

|
>







>
>


>

<
|


>
>
>
>
>
>
>
>
>
>
>
>
|
>









>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>

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









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










>
>
>






|






>

|
|
<
|
<
<
>







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
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
}

/*
** Testing the search function.
**
** COMMAND: search*
**
** Usage: %fossil search [OPTIONS] PATTERN...
**
** Search the repository for PATTERN and show matches.  Depending on
** options and how the administrator has search configured for the
** repository, the search can cover:
**

**    *   check-in comments (-c)
**    *   embedded documentation (--docs)
**    *   forum posts (--forum)
**    *   tickets (--tickets)
**    *   tech notes (--technotes)
**    *   wiki pages (--wiki)
**    *   built-in fossil help text (-h)
**    *   all of the above (-a)
**
** Use options below to select the scope of the search.  The
** default is check-in comments only (-c).
**
** Output is colorized if writing to a TTY and if the NO_COLOR environment



** variable is not set.  Use the "--highlight 0" option to disable colorization
** or use "--highlight 91" to force it on.  Change the argument to --highlight
** to change the color.
**
** Options:
**     -a|--all          Search everything
**     -c|--checkins     Search checkin comments
**     --docs            Search embedded documentation
**     --forum           Search forum posts
**     -h|--bi-help      Search built-in help
**     --highlight N     Used VT100 color N for matching text.  0 means "off".
**     -n|--limit N      Limit output to N matches
**     --technotes       Search tech notes
**     --tickets         Search tickets
**     -W|--width WIDTH  Set display width to WIDTH columns, 0 for
**                       unlimited. Defaults to the terminal's width.
**     --wiki            Search wiki
*/
void search_cmd(void){
  Blob pattern;
  int i;
  Blob sql = empty_blob;
  Stmt q;
  int iBest;
  int srchFlags = 0;
  int bFts = 1;          /* Use FTS search by default now */
  char fAll = NULL != find_option("all", "a", 0);
  const char *zLimit = find_option("limit","n",1);
  const char *zScope = 0;
  const char *zWidth = find_option("width","W",1);

  int bDebug = find_option("debug",0,0)!=0;     /* Undocumented */
  int nLimit = zLimit ? atoi(zLimit) : -1000;
  int width;
  int nTty = 0;          /* VT100 highlight color for matching text */
  const char *zHighlight = 0;

  nTty =  terminal_is_vt100();

  /* Undocumented option to change highlight color */
  zHighlight = find_option("highlight",0,1);
  if( zHighlight ) nTty = atoi(zHighlight);

  /* Undocumented option (legacy) */
  zScope = find_option("scope",0,1);

  if( find_option("fts",0,0)!=0 ) bFts = 1;      /* Undocumented legacy */
  if( find_option("legacy",0,0)!=0 ) bFts = 0;   /* Undocumented */

  if( zWidth ){
    width = atoi(zWidth);
    if( (width!=0) && (width<=20) ){
      fossil_fatal("-W|--width value must be >20 or 0");
    }
  }else{
    width = -1;
  }
  if( zScope ){
    for(i=0; zScope[i]; i++){
      switch( zScope[i] ){
        case 'a':  srchFlags = SRCH_ALL;       break;
        case 'c':  srchFlags |= SRCH_CKIN;     break;
        case 'd':  srchFlags |= SRCH_DOC;      break;
        case 'e':  srchFlags |= SRCH_TECHNOTE; break;
        case 'f':  srchFlags |= SRCH_FORUM;    break;
        case 'h':  srchFlags |= SRCH_HELP;     break;
        case 't':  srchFlags |= SRCH_TKT;      break;
        case 'w':  srchFlags |= SRCH_WIKI;     break;
      }
    }
    bFts = 1;
  }
  if( find_option("all","a",0) ){      srchFlags |= SRCH_ALL;      bFts = 1; }
  if( find_option("bi-help","h",0) ){  srchFlags |= SRCH_HELP;     bFts = 1; }
  if( find_option("checkins","c",0) ){ srchFlags |= SRCH_CKIN;     bFts = 1; }
  if( find_option("docs",0,0) ){       srchFlags |= SRCH_DOC;      bFts = 1; }
  if( find_option("forum",0,0) ){      srchFlags |= SRCH_FORUM;    bFts = 1; }
  if( find_option("technotes",0,0) ){  srchFlags |= SRCH_TECHNOTE; bFts = 1; }
  if( find_option("tickets",0,0) ){    srchFlags |= SRCH_TKT;      bFts = 1; }
  if( find_option("wiki",0,0) ){       srchFlags |= SRCH_WIKI;     bFts = 1; }

  /* If no search objects are specified, default to "check-in comments" */
  if( srchFlags==0 ) srchFlags = SRCH_CKIN;


  db_find_and_open_repository(0, 0);
  verify_all_options();
  if( g.argc<3 ) return;
  login_set_capabilities("s", 0);
  if( search_restrict(srchFlags)==0 && (srchFlags & SRCH_HELP)==0 ){
    const char *zC1 = 0, *zPlural = "s";
    if( srchFlags & SRCH_TECHNOTE ){  zC1 = "technote"; }
    if( srchFlags & SRCH_TKT ){       zC1 = "ticket";   }
    if( srchFlags & SRCH_FORUM ){     zC1 = "forum";    zPlural = ""; }
    if( srchFlags & SRCH_DOC ){       zC1 = "document"; }
    if( srchFlags & SRCH_WIKI ){      zC1 = "wiki";     zPlural = ""; }
    if( srchFlags & SRCH_CKIN ){      zC1 = "check-in"; }
    fossil_print(
      "Search of %s%s is disabled on this repository.\n"
      "Enable using \"fossil fts-config enable %s\".\n",
      zC1, zPlural, zC1
    );
    return;
  }

  blob_init(&pattern, g.argv[2], -1);
  for(i=3; i<g.argc; i++){
    blob_appendf(&pattern, " %s", g.argv[i]);
  }
  if( bFts ){
    /* Search using FTS */
    Blob com;
    Blob snip;
    const char *zPattern = blob_str(&pattern);


















    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 */
      search_indexed(zPattern, srchFlags);   /* Indexed search */
      if( srchFlags & SRCH_HELP ){
        search_fullscan(zPattern, SRCH_HELP);
      }
    }
    db_prepare(&q, "SELECT snip, label, score, id, date"
                   "  FROM x"
                   " ORDER BY score DESC, date DESC;");
    blob_init(&com, 0, 0);
    blob_init(&snip, 0, 0);
    if( width<0 ) width = terminal_get_width(80);
    while( db_step(&q)==SQLITE_ROW ){
      const char *zSnippet = db_column_text(&q, 0);
      const char *zLabel = db_column_text(&q, 1);
      const char *zDate = db_column_text(&q, 4);
      const char *zScore = db_column_text(&q, 2);
      const char *zId = db_column_text(&q, 3);
      char *zOrig;
      blob_appendf(&snip, "%s", zSnippet);
      zOrig = blob_materialize(&snip);
      blob_init(&snip, 0, 0);

      html_to_plaintext(zOrig, &snip, (nTty?HTOT_VT100:0)|HTOT_FLOW|HTOT_TRIM);


      fossil_free(zOrig);
      blob_appendf(&com, "%s\n%s\n%s", zLabel, blob_str(&snip), zDate);
      if( bDebug ){
        blob_appendf(&com," score: %s id: %s", zScore, zId);
      }
      comment_print(blob_str(&com), 0, 5, width,
            COMMENT_PRINT_TRIM_CRLF |
            COMMENT_PRINT_WORD_BREAK |
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
/* What to search for */
#define SRCH_CKIN     0x0001    /* Search over check-in comments */
#define SRCH_DOC      0x0002    /* Search over embedded documents */
#define SRCH_TKT      0x0004    /* Search over tickets */
#define SRCH_WIKI     0x0008    /* Search over wiki */
#define SRCH_TECHNOTE 0x0010    /* Search over tech notes */
#define SRCH_FORUM    0x0020    /* Search over forum messages */

#define SRCH_ALL      0x003f    /* Search over everything */
#endif

/*
** Remove bits from srchFlags which are disallowed by either the
** current server configuration or by user permissions.  Return
** the revised search flags mask.



*/
unsigned int search_restrict(unsigned int srchFlags){
  static unsigned int knownGood = 0;
  static unsigned int knownBad = 0;
  static const struct { unsigned m; const char *zKey; } aSetng[] = {
     { SRCH_CKIN,     "search-ci"   },
     { SRCH_DOC,      "search-doc"  },
     { SRCH_TKT,      "search-tkt"  },
     { SRCH_WIKI,     "search-wiki" },
     { SRCH_TECHNOTE, "search-technote" },
     { SRCH_FORUM,    "search-forum" },

  };
  int i;
  if( g.perm.Read==0 )   srchFlags &= ~(SRCH_CKIN|SRCH_DOC|SRCH_TECHNOTE);
  if( g.perm.RdTkt==0 )  srchFlags &= ~(SRCH_TKT);
  if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI);
  if( g.perm.RdForum==0) srchFlags &= ~(SRCH_FORUM);
  for(i=0; i<count(aSetng); i++){







>
|






>
>
>





|
|
|
|

|
>







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
814
815
816
817
818
819
/* What to search for */
#define SRCH_CKIN     0x0001    /* Search over check-in comments */
#define SRCH_DOC      0x0002    /* Search over embedded documents */
#define SRCH_TKT      0x0004    /* Search over tickets */
#define SRCH_WIKI     0x0008    /* Search over wiki */
#define SRCH_TECHNOTE 0x0010    /* Search over tech notes */
#define SRCH_FORUM    0x0020    /* Search over forum messages */
#define SRCH_HELP     0x0040    /* Search built-in help (full-scan only) */
#define SRCH_ALL      0x007f    /* Search over everything */
#endif

/*
** Remove bits from srchFlags which are disallowed by either the
** current server configuration or by user permissions.  Return
** the revised search flags mask.
**
** If bFlex is true, that means allow through the SRCH_HELP option
** even if it is not explicitly enabled.
*/
unsigned int search_restrict(unsigned int srchFlags){
  static unsigned int knownGood = 0;
  static unsigned int knownBad = 0;
  static const struct { unsigned m; const char *zKey; } aSetng[] = {
     { SRCH_CKIN,     "search-ci"       },
     { SRCH_DOC,      "search-doc"      },
     { SRCH_TKT,      "search-tkt"      },
     { SRCH_WIKI,     "search-wiki"     },
     { SRCH_TECHNOTE, "search-technote" },
     { SRCH_FORUM,    "search-forum"    },
     { SRCH_HELP,     "search-help"     },
  };
  int i;
  if( g.perm.Read==0 )   srchFlags &= ~(SRCH_CKIN|SRCH_DOC|SRCH_TECHNOTE);
  if( g.perm.RdTkt==0 )  srchFlags &= ~(SRCH_TKT);
  if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI);
  if( g.perm.RdForum==0) srchFlags &= ~(SRCH_FORUM);
  for(i=0; i<count(aSetng); i++){
910
911
912
913
914
915
916





















917
918
919
920
921
922
923
      "         'f'||rid,"
      "         datetime(event.mtime),"
      "         search_snippet()"
      "    FROM event JOIN blob on event.objid=blob.rid"
      "   WHERE search_match('',body('f',rid,NULL));"
    );
  }





















}

/*
** Number of significant bits in a u32
*/
static int nbits(u32 x){
  int n = 0;







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







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
      "         'f'||rid,"
      "         datetime(event.mtime),"
      "         search_snippet()"
      "    FROM event JOIN blob on event.objid=blob.rid"
      "   WHERE search_match('',body('f',rid,NULL));"
    );
  }
  if( (srchFlags & SRCH_HELP)!=0 ){
    const char *zPrefix;
    helptext_vtab_register(g.db);
    if( srchFlags==SRCH_HELP ){
      zPrefix = "The";
    }else{
      zPrefix = "Built-in help for the";
    }
    db_multi_exec(
      "INSERT INTO x(label,url,score,id,snip)"
      "  SELECT format('%q \"%%s\" %%s',name,type),"
      "         '/help?cmd='||name,"
      "         search_score(),"
      "         'h'||rowid,"
      "         search_snippet()"
      "    FROM helptext"
      "   WHERE search_match(format('the \"%%s\" %%s',name,type),"
      "                      helptext.helptext);",
      zPrefix
    );
  }
}

/*
** Number of significant bits in a u32
*/
static int nbits(u32 x){
  int n = 0;
1066
1067
1068
1069
1070
1071
1072

1073
1074
1075
1076
1077
1078
1079
    static const struct { unsigned m; char c; } aMask[] = {
       { SRCH_CKIN,     'c' },
       { SRCH_DOC,      'd' },
       { SRCH_TKT,      't' },
       { SRCH_WIKI,     'w' },
       { SRCH_TECHNOTE, 'e' },
       { SRCH_FORUM,    'f' },

    };
    int i;
    for(i=0; i<count(aMask); i++){
      if( srchFlags & aMask[i].m ){
        blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c);
        zSep = " OR ";
      }







>







1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
    static const struct { unsigned m; char c; } aMask[] = {
       { SRCH_CKIN,     'c' },
       { SRCH_DOC,      'd' },
       { SRCH_TKT,      't' },
       { SRCH_WIKI,     'w' },
       { SRCH_TECHNOTE, 'e' },
       { SRCH_FORUM,    'f' },
       { SRCH_HELP,     'h' },
    };
    int i;
    for(i=0; i<count(aMask); i++){
      if( srchFlags & aMask[i].m ){
        blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c);
        zSep = " OR ";
      }
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
  Stmt q;
  int nRow = 0;
  int nLimit = db_get_int("search-limit", 100);

  if( P("searchlimit")!=0 ){
    nLimit = atoi(P("searchlimit"));
  }
  srchFlags = search_restrict(srchFlags);
  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, substr(date,1,10)"
                 "  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);







|











>
>
>







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
  Stmt q;
  int nRow = 0;
  int nLimit = db_get_int("search-limit", 100);

  if( P("searchlimit")!=0 ){
    nLimit = atoi(P("searchlimit"));
  }
  srchFlags = search_restrict(srchFlags) | (srchFlags & SRCH_HELP);
  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 */
    if( srchFlags & SRCH_HELP ){
      search_fullscan(zPattern, SRCH_HELP);
    }
  }
  db_prepare(&q, "SELECT url, snip, label, score, id, substr(date,1,10)"
                 "  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);
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
**     0x01    If the y= query parameter is present, use it as an addition
**             restriction what to search.
**
**     0x02    Show nothing if search is disabled.
**
** Return true if there are search results.
*/
int search_screen(unsigned srchFlags, int mFlags){
  const char *zType = 0;
  const char *zClass = 0;
  const char *zDisable1;
  const char *zDisable2;
  const char *zPattern;
  int fDebug = PB("debug");
  int haveResult = 0;





  srchFlags = search_restrict(srchFlags);

  switch( srchFlags ){
    case SRCH_CKIN:     zType = " Check-ins";  zClass = "Ckin"; break;
    case SRCH_DOC:      zType = " Docs";       zClass = "Doc";  break;
    case SRCH_TKT:      zType = " Tickets";    zClass = "Tkt";  break;
    case SRCH_WIKI:     zType = " Wiki";       zClass = "Wiki"; break;
    case SRCH_TECHNOTE: zType = " Tech Notes"; zClass = "Note"; break;
    case SRCH_FORUM:    zType = " Forum";      zClass = "Frm";  break;

  }
  if( srchFlags==0 ){
    if( mFlags & 0x02 ) return 0;
    zDisable1 = " disabled";
    zDisable2 = " disabled";
    zPattern = "";
  }else{
    zDisable1 = ""; /* Was: " 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( (mFlags & 0x01)!=0 && (srchFlags & (srchFlags-1))!=0 ){
    static const struct { const char *z; const char *zNm; unsigned m; } aY[] = {




       { "all",  "All",        SRCH_ALL      },
       { "c",    "Check-ins",  SRCH_CKIN     },
       { "d",    "Docs",       SRCH_DOC      },
       { "t",    "Tickets",    SRCH_TKT      },
       { "w",    "Wiki",       SRCH_WIKI     },
       { "e",    "Tech Notes", SRCH_TECHNOTE },
       { "f",    "Forum",      SRCH_FORUM    },

    };
    const char *zY = PD("y","all");
    unsigned newFlags = srchFlags;
    int i;
    @ <select size='1' name='y'>
    for(i=0; i<count(aY); i++){
      if( (aY[i].m & srchFlags)==0 ) continue;


      cgi_printf("<option value='%s'", aY[i].z);
      if( fossil_strcmp(zY,aY[i].z)==0 ){
        newFlags &= aY[i].m;
        cgi_printf(" selected");
      }
      cgi_printf(">%s</option>\n", aY[i].zNm);
    }
    @ </select>
    srchFlags = newFlags;
  }
  if( fDebug ){
    @ <input type="hidden" name="debug" value="1">
  }
  @ <input type="submit" value="Search%s(zType)"%s(zDisable2)>
  if( srchFlags==0 ){
    @ <p class="generalError">Search is disabled</p>
  }
  @ </div></form>
  while( fossil_isspace(zPattern[0]) ) zPattern++;
  if( zPattern[0] ){
    if( zClass ){
      @ <div class='searchResult searchResult%s(zClass)'>
    }else{
      @ <div class='searchResult'>
    }
    if( search_run_and_output(zPattern, srchFlags, fDebug)==0 ){
      @ <p class='searchEmpty'>No matches for: <span>%h(zPattern)</span></p>
    }
    @ </div>
    haveResult = 1;
  }
  return haveResult;
}

/*
** WEBPAGE: search
**
** Search for check-in comments, documents, tickets, or wiki that
** match a user-supplied pattern.
**
**    s=PATTERN       Specify the full-text pattern to search for
**    y=TYPE          What to search.
**                      c -> check-ins
**                      d -> documentation
**                      t -> tickets
**                      w -> wiki
**                      e -> tech notes
**                      f -> forum

**                    all -> everything
*/
void search_page(void){
  const int isSearch = P("s")!=0;
  login_check_credentials();
  style_header("Search%s", isSearch ? " Results" : "");
  cgi_check_for_malice();
  search_screen(SRCH_ALL, 1);







|







>
>
>
>
>
|
>
|






>

|
















>
|
|
>
>
>
>







>

<
<



|
>
>


|





<





|










|
















|
|
|
|
|
|
>
|







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
**     0x01    If the y= query parameter is present, use it as an addition
**             restriction what to search.
**
**     0x02    Show nothing if search is disabled.
**
** Return true if there are search results.
*/
int search_screen(unsigned srchAllowed, int mFlags){
  const char *zType = 0;
  const char *zClass = 0;
  const char *zDisable1;
  const char *zDisable2;
  const char *zPattern;
  int fDebug = PB("debug");
  int haveResult = 0;
  int srchThisTime;
  const char *zY = PD("y","all");
  if( zY[0]=='h' && zY[1]==0 ){
    srchAllowed = search_restrict(srchAllowed) | (srchAllowed & SRCH_HELP);
  }else{
    srchAllowed = search_restrict(srchAllowed);
  }
  switch( srchAllowed ){
    case SRCH_CKIN:     zType = " Check-ins";  zClass = "Ckin"; break;
    case SRCH_DOC:      zType = " Docs";       zClass = "Doc";  break;
    case SRCH_TKT:      zType = " Tickets";    zClass = "Tkt";  break;
    case SRCH_WIKI:     zType = " Wiki";       zClass = "Wiki"; break;
    case SRCH_TECHNOTE: zType = " Tech Notes"; zClass = "Note"; break;
    case SRCH_FORUM:    zType = " Forum";      zClass = "Frm";  break;
    case SRCH_HELP:     zType = " Help";       zClass = "Hlp";  break;
  }
  if( srchAllowed==0 ){
    if( mFlags & 0x02 ) return 0;
    zDisable1 = " disabled";
    zDisable2 = " disabled";
    zPattern = "";
  }else{
    zDisable1 = ""; /* Was: " 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)>
  srchThisTime = srchAllowed;
  if( (mFlags & 0x01)!=0 && (srchAllowed & (srchAllowed-1))!=0 ){
    static const struct {
      const char *z;
      const char *zNm;
      unsigned m;
    } aY[] = {
       { "all",  "All",        SRCH_ALL      },
       { "c",    "Check-ins",  SRCH_CKIN     },
       { "d",    "Docs",       SRCH_DOC      },
       { "t",    "Tickets",    SRCH_TKT      },
       { "w",    "Wiki",       SRCH_WIKI     },
       { "e",    "Tech Notes", SRCH_TECHNOTE },
       { "f",    "Forum",      SRCH_FORUM    },
       { "h",    "Help",       SRCH_HELP     },
    };


    int i;
    @ <select size='1' name='y'>
    for(i=0; i<count(aY); i++){
      if( (aY[i].m & srchAllowed)==0 ) continue;
      if( aY[i].m==SRCH_HELP && fossil_strcmp(zY,"h")!=0
       && search_restrict(SRCH_HELP)==0 ) continue;
      cgi_printf("<option value='%s'", aY[i].z);
      if( fossil_strcmp(zY,aY[i].z)==0 ){
        srchThisTime &= aY[i].m;
        cgi_printf(" selected");
      }
      cgi_printf(">%s</option>\n", aY[i].zNm);
    }
    @ </select>

  }
  if( fDebug ){
    @ <input type="hidden" name="debug" value="1">
  }
  @ <input type="submit" value="Search%s(zType)"%s(zDisable2)>
  if( srchAllowed==0 && srchThisTime==0 ){
    @ <p class="generalError">Search is disabled</p>
  }
  @ </div></form>
  while( fossil_isspace(zPattern[0]) ) zPattern++;
  if( zPattern[0] ){
    if( zClass ){
      @ <div class='searchResult searchResult%s(zClass)'>
    }else{
      @ <div class='searchResult'>
    }
    if( search_run_and_output(zPattern, srchThisTime, fDebug)==0 ){
      @ <p class='searchEmpty'>No matches for: <span>%h(zPattern)</span></p>
    }
    @ </div>
    haveResult = 1;
  }
  return haveResult;
}

/*
** WEBPAGE: search
**
** Search for check-in comments, documents, tickets, or wiki that
** match a user-supplied pattern.
**
**    s=PATTERN       Specify the full-text pattern to search for
**    y=TYPE          What to search.
**                      c -> check-ins,
**                      d -> documentation,
**                      t -> tickets,
**                      w -> wiki,
**                      e -> tech notes,
**                      f -> forum,
**                      h -> built-in help,
**                    all -> everything.
*/
void search_page(void){
  const int isSearch = P("s")!=0;
  login_check_credentials();
  style_header("Search%s", isSearch ? " Results" : "");
  cgi_check_for_malice();
  search_screen(SRCH_ALL, 1);
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
        wiki_convert(&tail, &html, 0);
        blob_reset(&tail);
      }else{
        blob_append(pOut, "\n", 1);
        wiki_convert(pIn, &html, 0);
      }
    }
    html_to_plaintext(blob_str(&html), pOut);
  }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){
    markdown_to_html(pIn, blob_size(&title) ? NULL : &title, &html);
  }else if( fossil_strcmp(zMimetype,"text/html")==0 ){
    if( blob_size(&title)==0 ) doc_is_embedded_html(pIn, &title);
    pHtml = pIn;
  }
  blob_appendf(pOut, "%s\n", blob_str(&title));
  if( blob_size(pHtml) ){
    html_to_plaintext(blob_str(pHtml), pOut);
  }else{
    blob_append(pOut, blob_buffer(pIn), blob_size(pIn));
  }
  blob_reset(&html);
  blob_reset(&title);
}








|








|







1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
        wiki_convert(&tail, &html, 0);
        blob_reset(&tail);
      }else{
        blob_append(pOut, "\n", 1);
        wiki_convert(pIn, &html, 0);
      }
    }
    html_to_plaintext(blob_str(&html), pOut, 0);
  }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){
    markdown_to_html(pIn, blob_size(&title) ? NULL : &title, &html);
  }else if( fossil_strcmp(zMimetype,"text/html")==0 ){
    if( blob_size(&title)==0 ) doc_is_embedded_html(pIn, &title);
    pHtml = pIn;
  }
  blob_appendf(pOut, "%s\n", blob_str(&title));
  if( blob_size(pHtml) ){
    html_to_plaintext(blob_str(pHtml), pOut, 0);
  }else{
    blob_append(pOut, blob_buffer(pIn), blob_size(pIn));
  }
  blob_reset(&html);
  blob_reset(&title);
}

2106
2107
2108
2109
2110
2111
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
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160

2161
2162
2163
2164
2165
2166
2167
  if( db_table_exists("repository","chat") ){
    chat_rebuild_index(1);
  }
  fossil_print(" done\n");
}

/*
** COMMAND: fts-config*
**
** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT?
**
** The "fossil fts-config" command configures the full-text search capabilities
** of the repository.  Subcommands:
**
**     reindex            Rebuild the search index.  This is a no-op if
**                        index search is disabled
**
**     index (on|off)     Turn the search index on or off
**
**     enable cdtwef      Enable various kinds of search. c=Check-ins,
**                        d=Documents, t=Tickets, w=Wiki, e=Tech Notes,
**                        f=Forum.
**
**     disable cdtwef     Disable various kinds of search
**
**     tokenizer VALUE    Select a tokenizer for indexed search. VALUE
**                        may be one of (porter, on, off, trigram, unicode61),
**                        and "on" is equivalent to "porter". Unindexed
**                        search never uses tokenization or stemming.
**
** The current search settings are displayed after any changes are applied.
** Run this command with no arguments to simply see the settings.
*/
void fts_config_cmd(void){
  static const struct {
    int iCmd;
    const char *z;
  } aCmd[] = {
     { 1,  "reindex"  },
     { 2,  "index"    },
     { 3,  "disable"  },
     { 4,  "enable"   },
     { 5,  "tokenizer"},
  };
  static const struct {
    const char *zSetting;
    const char *zName;
    const char *zSw;
  } aSetng[] = {
     { "search-ci",       "check-in search:",  "c" },
     { "search-doc",      "document search:",  "d" },
     { "search-tkt",      "ticket search:",    "t" },
     { "search-wiki",     "wiki search:",      "w" },
     { "search-technote", "tech note search:", "e" },
     { "search-forum",    "forum search:",     "f" },

  };
  char *zSubCmd = 0;
  int i, j, n;
  int iCmd = 0;
  int iAction = 0;
  db_find_and_open_repository(0, 0);
  if( g.argc>2 ){







|






|
|

|

|
|
|

|

|
|
|
|




















|
|
|
|
|
|
>







2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
  if( db_table_exists("repository","chat") ){
    chat_rebuild_index(1);
  }
  fossil_print(" done\n");
}

/*
** COMMAND: fts-config*                    abbrv-subcom
**
** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT?
**
** The "fossil fts-config" command configures the full-text search capabilities
** of the repository.  Subcommands:
**
**    reindex            Rebuild the search index.  This is a no-op if
**                       index search is disabled
**
**    index (on|off)     Turn the search index on or off
**
**    enable TYPE ..     Enable search for TYPE.  TYPE is one of:
**                       check-in, document, ticket, wiki, technote, 
**                       forum, help, or all
**
**    disable TYPE ...   Disable search for TYPE
**
**    tokenizer VALUE    Select a tokenizer for indexed search. VALUE
**                       may be one of (porter, on, off, trigram, unicode61),
**                       and "on" is equivalent to "porter". Unindexed
**                       search never uses tokenization or stemming.
**
** The current search settings are displayed after any changes are applied.
** Run this command with no arguments to simply see the settings.
*/
void fts_config_cmd(void){
  static const struct {
    int iCmd;
    const char *z;
  } aCmd[] = {
     { 1,  "reindex"  },
     { 2,  "index"    },
     { 3,  "disable"  },
     { 4,  "enable"   },
     { 5,  "tokenizer"},
  };
  static const struct {
    const char *zSetting;
    const char *zName;
    const char *zSw;
  } aSetng[] = {
     { "search-ci",       "check-in search:",       "c" },
     { "search-doc",      "document search:",       "d" },
     { "search-tkt",      "ticket search:",         "t" },
     { "search-wiki",     "wiki search:",           "w" },
     { "search-technote", "technote search:",       "e" },
     { "search-forum",    "forum search:",          "f" },
     { "search-help",     "built-in help search:",  "h" },
  };
  char *zSubCmd = 0;
  int i, j, n;
  int iCmd = 0;
  int iAction = 0;
  db_find_and_open_repository(0, 0);
  if( g.argc>2 ){
2190
2191
2192
2193
2194
2195
2196

2197


2198


2199













2200










2201
2202


2203
2204
2205
2206
2207
2208
2209
    if( g.argc<3 ) usage("index (on|off)");
    iAction = 1 + is_truth(g.argv[3]);
  }
  db_begin_transaction();

  /* Adjust search settings */
  if( iCmd==3 || iCmd==4 ){

    const char *zCtrl;


    if( g.argc<4 ) usage(mprintf("%s STRING",zSubCmd));


    zCtrl = g.argv[3];













    for(j=0; j<count(aSetng); j++){










      if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){
        db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0);


      }
    }
  }else if( iCmd==5 ){
    int iOldTokenizer, iNewTokenizer;
    if( g.argc<4 ) usage("tokenizer porter|on|off|trigram|unicode61");
    iOldTokenizer = search_tokenizer_type(0);
    db_set("search-tokenizer",







>

>
>
|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
|
|
>
>







2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
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
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
    if( g.argc<3 ) usage("index (on|off)");
    iAction = 1 + is_truth(g.argv[3]);
  }
  db_begin_transaction();

  /* Adjust search settings */
  if( iCmd==3 || iCmd==4 ){
    int k;
    const char *zCtrl;
    for(k=2; k<g.argc; k++){
      if( k==2 ){
        if( g.argc<4 ){
          zCtrl = "all";
        }else{
          zCtrl = g.argv[3];
          k++;
        }
      }else{
        zCtrl = g.argv[k];
      }
      if( fossil_strcmp(zCtrl,"all")==0 ){
        zCtrl = "cdtwefh";
      }
      if( strlen(zCtrl)>=4 ){
        /* If the argument to "enable" or "disable" is a string of at least
        ** 4 characters which matches part of any aSetng.zName, then use that
        ** one aSetng value only. */
        char *zGlob = mprintf("*%s*", zCtrl);
        for(j=0; j<count(aSetng); j++){
          if( sqlite3_strglob(zGlob, aSetng[j].zName)==0 ){
            db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0);
            zCtrl = 0;
            break;
          }
        }
        fossil_free(zGlob);
      }
      if( zCtrl ){
        for(j=0; j<count(aSetng); j++){
          if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){
            db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0);
          }
        }
      }
    }
  }else if( iCmd==5 ){
    int iOldTokenizer, iNewTokenizer;
    if( g.argc<4 ) usage("tokenizer porter|on|off|trigram|unicode61");
    iOldTokenizer = search_tokenizer_type(0);
    db_set("search-tokenizer",
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
  }
  if( iAction>=2 ){
    search_rebuild_index();
  }

  /* Always show the status before ending */
  for(i=0; i<count(aSetng); i++){
    fossil_print("%-17s %s\n", aSetng[i].zName,
       db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off");
  }
  fossil_print("%-17s %s\n", "tokenizer:",
       search_tokenizer_for_string(0));
  if( search_index_exists() ){
    int pgsz = db_int64(0, "PRAGMA repository.page_size;");
    i64 nTotal = db_int64(0, "PRAGMA repository.page_count;")*pgsz;
    i64 nFts = db_int64(0, "SELECT count(*) FROM dbstat"
                               " WHERE schema='repository'"
                               " AND name LIKE 'fts%%'")*pgsz;
    char zSize[50];
    fossil_print("%-17s FTS%d\n", "full-text index:", search_index_type(1));
    fossil_print("%-17s %d\n", "documents:",
       db_int(0, "SELECT count(*) FROM ftsdocs"));
    approxSizeName(sizeof(zSize), zSize, nFts);
    fossil_print("%-17s %s (%.1f%% of repository)\n", "space used",
       zSize, 100.0*((double)nFts/(double)nTotal));
  }else{
    fossil_print("%-17s disabled\n", "full-text index:");
  }
  db_end_transaction(0);
}

/*
** WEBPAGE: test-ftsdocs
**







|


|








|
|


|


|







2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
  }
  if( iAction>=2 ){
    search_rebuild_index();
  }

  /* Always show the status before ending */
  for(i=0; i<count(aSetng); i++){
    fossil_print("%-21s %s\n", aSetng[i].zName,
       db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off");
  }
  fossil_print("%-21s %s\n", "tokenizer:",
       search_tokenizer_for_string(0));
  if( search_index_exists() ){
    int pgsz = db_int64(0, "PRAGMA repository.page_size;");
    i64 nTotal = db_int64(0, "PRAGMA repository.page_count;")*pgsz;
    i64 nFts = db_int64(0, "SELECT count(*) FROM dbstat"
                               " WHERE schema='repository'"
                               " AND name LIKE 'fts%%'")*pgsz;
    char zSize[50];
    fossil_print("%-21s FTS%d\n", "full-text index:", search_index_type(1));
    fossil_print("%-21s %d\n", "documents:",
       db_int(0, "SELECT count(*) FROM ftsdocs"));
    approxSizeName(sizeof(zSize), zSize, nFts);
    fossil_print("%-21s %s (%.1f%% of repository)\n", "space used",
       zSize, 100.0*((double)nFts/(double)nTotal));
  }else{
    fossil_print("%-21s disabled\n", "full-text index:");
  }
  db_end_transaction(0);
}

/*
** WEBPAGE: test-ftsdocs
**
Changes to src/security_audit.c.
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
    @ Fossil's built-in authentication mechanism is bypassed.
    @ Fix this by deactivating the "Allow HTTP_AUTHENTICATION authentication"
    @ checkbox on the <a href="setup_access">Access Control</a> page.
  }

  /* Logging should be turned on
  */
  if( db_get_boolean("access-log",0)==0 ){
    @ <li><p>
    @ The <a href="access_log">User Log</a> is disabled.  The user log
    @ keeps a record of successful and unsuccessful login attempts and is
    @ useful for security monitoring.
  }
  if( db_get_boolean("admin-log",0)==0 ){
    @ <li><p>
    @ The <a href="admin_log">Administrative Log</a> is disabled.
    @ The administrative log provides a record of configuration changes
    @ and is useful for security monitoring.
  }

#if !defined(_WIN32) && !defined(FOSSIL_OMIT_LOAD_AVERAGE)







|





|







551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
    @ Fossil's built-in authentication mechanism is bypassed.
    @ Fix this by deactivating the "Allow HTTP_AUTHENTICATION authentication"
    @ checkbox on the <a href="setup_access">Access Control</a> page.
  }

  /* Logging should be turned on
  */
  if( db_get_boolean("access-log",1)==0 ){
    @ <li><p>
    @ The <a href="access_log">User Log</a> is disabled.  The user log
    @ keeps a record of successful and unsuccessful login attempts and is
    @ useful for security monitoring.
  }
  if( db_get_boolean("admin-log",1)==0 ){
    @ <li><p>
    @ The <a href="admin_log">Administrative Log</a> is disabled.
    @ The administrative log provides a record of configuration changes
    @ and is useful for security monitoring.
  }

#if !defined(_WIN32) && !defined(FOSSIL_OMIT_LOAD_AVERAGE)
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818









819
820
821
822
823










824


825
826
827
828
829



830
831
832


833

834
835

836
837
838
839
840
841
842
    @ <blockquote><pre>
    @ errorlog: <i>FILENAME</i>
    @ </pre></blockquote>
    blob_reset(&fullname);
  }
}

/*
** The maximum number of bytes of the error log to show by default.
*/
#define MXSHOWLOG 500000

/*
** WEBPAGE: errorlog
**
** Show the content of the error log.  Only the administrator can view
** this page.









*/
void errorlog_page(void){
  i64 szFile;
  FILE *in;
  char *zLog;










  char z[10000];


  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }



  style_header("Server Error Log");
  style_submenu_element("Test", "%R/test-warning");
  style_submenu_element("Refresh", "%R/errorlog");


  style_submenu_element("Log-Menu", "%R/setup-logmenu");

  style_submenu_element("Panics", "%R/paniclog");
  style_submenu_element("Non-Hacks", "%R/hacklog?not");


  if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
    no_error_log_available();
    style_finish_page();
    return;
  }
  if( P("truncate1") && cgi_csrf_safe(2) ){







<
<
<
<
<





>
>
>
>
>
>
>
>
>





>
>
>
>
>
>
>
>
>
>

>
>





>
>
>



>
>

>
|
<
>







802
803
804
805
806
807
808





809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856

857
858
859
860
861
862
863
864
    @ <blockquote><pre>
    @ errorlog: <i>FILENAME</i>
    @ </pre></blockquote>
    blob_reset(&fullname);
  }
}






/*
** WEBPAGE: errorlog
**
** Show the content of the error log.  Only the administrator can view
** this page.
**
**    y=0x01          Show only hack attempts
**    y=0x02          Show only panics and assertion faults
**    y=0x04          Show hung backoffice processes
**    y=0x08          Show POST requests from a different origin
**    y=0x40          Show other uncategorized messages
**
** If y is omitted or is zero, a count of the various message types is
** shown.
*/
void errorlog_page(void){
  i64 szFile;
  FILE *in;
  char *zLog;
  const char *zType = P("y");
  static const int eAllTypes = 0x4f;
  long eType = 0;
  int bOutput = 0;
  int prevWasTime = 0;
  int nHack = 0;
  int nPanic = 0;
  int nOther = 0;
  int nHang = 0;
  int nXPost = 0;
  char z[10000];
  char zTime[10000];

  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  if( zType ){
    eType = strtol(zType,0,0) & eAllTypes;
  }
  style_header("Server Error Log");
  style_submenu_element("Test", "%R/test-warning");
  style_submenu_element("Refresh", "%R/errorlog");
  style_submenu_element("Download", "%R/errorlog?download");
  style_submenu_element("Truncate", "%R/errorlog?truncate");
  style_submenu_element("Log-Menu", "%R/setup-logmenu");
  if( eType ){
    style_submenu_element("Summary", "%R/errorlog");

  }

  if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
    no_error_log_available();
    style_finish_page();
    return;
  }
  if( P("truncate1") && cgi_csrf_safe(2) ){
859
860
861
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

896
897
898
899
900
901
902
903
904
905


906
907
908
909
910
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
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
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
    @ </form>
    style_finish_page();
    return;
  }
  zLog = file_canonical_name_dup(g.zErrlog);
  @ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size.
  fossil_free(zLog);
  style_submenu_element("Download", "%R/errorlog?download");
  style_submenu_element("Truncate", "%R/errorlog?truncate");
  in = fossil_fopen(g.zErrlog, "rb");
  if( in==0 ){
    @ <p class='generalError'>Unable to open that file for reading!</p>
    style_finish_page();
    return;
  }
  if( szFile>MXSHOWLOG && P("all")==0 ){
    @ <form action="%R/errorlog" method="POST">

    @ <p>Only the last %,d(MXSHOWLOG) bytes are shown.
    @ <input type="submit" name="all" value="Show All">
    @ </form>
    fseek(in, -MXSHOWLOG, SEEK_END);

  }
  @ <hr>
  @ <pre>
  while( fgets(z, sizeof(z), in) ){
    @ %h(z)\
  }
  fclose(in);

  @ </pre>
  style_finish_page();
}



/*
** WEBPAGE: paniclog
**
** Scan the error log for panics.  Show all panic messages, ignoring all

** other error log entries.

*/
void paniclog_page(void){
  i64 szFile;
  char *zLog;
  FILE *in;
  int bOutput = 0;
  int prevWasTime = 0;
  char z[10000];
  char zTime[10000];



  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  style_header("Server Panic Log");
  style_submenu_element("Log-Menu", "%R/setup-logmenu");

  if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
    no_error_log_available();
    style_finish_page();
    return;
  }
  in = fossil_fopen(g.zErrlog, "rb");
  if( in==0 ){
    @ <p class='generalError'>Unable to open that file for reading!</p>
    style_finish_page();
    return;
  }
  szFile = file_size(g.zErrlog, ExtFILE);
  zLog = file_canonical_name_dup(g.zErrlog);
  @ Panic messages contained within the %lld(szFile)-byte 
  @ <a href="%R/errorlog?all">error log</a> found at
  @ "%h(zLog)".
  fossil_free(zLog);
  @ <hr>
  @ <pre>
  while( fgets(z, sizeof(z), in) ){
    if( prevWasTime




     && (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0)















    ){
      @ %h(zTime)\
      bOutput = 1;

    }
    if( strncmp(z, "--------", 8)==0 ){
      size_t n = strlen(z);
      memcpy(zTime, z, n+1);
      prevWasTime = 1;
      bOutput = 0;
    }else{
      prevWasTime = 0;
    }
    if( bOutput ){
      @ %h(z)\
    }
  }
  fclose(in);

  @ </pre>
  style_finish_page();
}

/*
** WEBPAGE: hacklog
**
** Scan the error log for "possible hack attempt" entries  Show hack
** attempt messages only, omitting all others.  Or if the "not" query
** parameter is present, show only messages that are not hack attempts.
*/
void hacklog_page(void){
  i64 szFile;
  char *zLog;
  FILE *in;
  int bOutput = 0;
  int prevWasTime = 0;
  int isNot = P("not")!=0;
  char z[10000];
  char zTime[10000];

  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;


  }
  style_header("Server Hack Log");
  style_submenu_element("Log-Menu", "%R/setup-logmenu");



  if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
    no_error_log_available();
    style_finish_page();
    return;


  }
  in = fossil_fopen(g.zErrlog, "rb");
  if( in==0 ){
    @ <p class='generalError'>Unable to open that file for reading!</p>
    style_finish_page();
    return;
  }
  szFile = file_size(g.zErrlog, ExtFILE);
  zLog = file_canonical_name_dup(g.zErrlog);
  @ %s(isNot?"Non-hack":"Hack") messages contained within the %lld(szFile)-byte 
  @ <a href="%R/errorlog?all">error log</a> found at
  @ "%h(zLog)".
  fossil_free(zLog);
  @ <hr>
  @ <pre>
  while( fgets(z, sizeof(z), in) ){


    if( prevWasTime 

     && ((strncmp(z,"possible hack attempt - 418 ", 27)==0) ^ isNot)
    ){
      @ %h(zTime)\
      bOutput = 1;
    }

    if( strncmp(z, "--------", 8)==0 ){
      size_t n = strlen(z);
      memcpy(zTime, z, n+1);
      prevWasTime = 1;
      bOutput = 0;

    }else{
      prevWasTime = 0;

    }
    if( bOutput ){
      @ %h(z)\
    }
  }
  fclose(in);
  @ </pre>
  style_finish_page();
}







<
<






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

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

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









|




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

>
|
<
<
<
<
>

<
>

<
|
|
<
<
<


881
882
883
884
885
886
887


888
889
890
891
892
893
894
895
896
897

898
899
900
901

902

903
904

905
906

907
908
909
910




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
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
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
    @ </form>
    style_finish_page();
    return;
  }
  zLog = file_canonical_name_dup(g.zErrlog);
  @ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size.
  fossil_free(zLog);


  in = fossil_fopen(g.zErrlog, "rb");
  if( in==0 ){
    @ <p class='generalError'>Unable to open that file for reading!</p>
    style_finish_page();
    return;
  }
  if( eType==0 ){
    /* will do a summary */
  }else if( (eType&eAllTypes)!=eAllTypes ){
    @ Only the following types of messages displayed:

    @ <ul>
    if( eType & 0x01 ){
      @ <li>Hack attempts
    }

    if( eType & 0x02 ){

      @ <li>Panics and assertion faults
    }

    if( eType & 0x04 ){
      @ <li>Hung backoffice processes

    }
    if( eType & 0x08 ){
      @ <li>POST requests from different origin
    }




    if( eType & 0x40 ){
      @ <li>Other uncategorized messages
    }
    @ </ul>








  }
  @ <hr>
  if( eType ){







    @ <pre>




  }














  while( fgets(z, sizeof(z), in) ){
    if( prevWasTime ){
      if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){
        bOutput = (eType & 0x01)!=0;
        nHack++;
      }else
      if( (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) ){
        bOutput = (eType & 0x02)!=0;
        nPanic++;
      }else
      if( sqlite3_strglob("warning: backoffice process * still *",z)==0 ){
        bOutput = (eType & 0x04)!=0;
        nHang++;
      }else
      if( sqlite3_strglob("warning: POST from different origin*",z)==0 ){
        bOutput = (eType & 0x08)!=0;
        nXPost++;
      }else
      {
        bOutput = (eType & 0x40)!=0;
        nOther++;
      }
      if( bOutput ){
        @ %h(zTime)\

      }
    }
    if( strncmp(z, "--------", 8)==0 ){
      size_t n = strlen(z);
      memcpy(zTime, z, n+1);
      prevWasTime = 1;
      bOutput = 0;
    }else{
      prevWasTime = 0;
    }
    if( bOutput && eType ){
      @ %h(z)\
    }
  }
  fclose(in);
  if( eType ){
    @ </pre>

  }
  if( eType==0 ){











    int nNonHack = nPanic + nHang + nOther;
    int nTotal = nNonHack + nHack + nXPost;



    @ <p><table border="a" cellspacing="0" cellpadding="5">

    if( nPanic>0 ){


      @ <tr><td align="right">%d(nPanic)</td>
      @     <td><a href="./errorlog?y=2">Panics</a></td>
    }
    if( nHack>0 ){

      @ <tr><td align="right">%d(nHack)</td>
      @     <td><a href="./errorlog?y=1">Hack Attempts</a></td>
    }



    if( nHang>0 ){
      @ <tr><td align="right">%d(nHang)</td>
      @     <td><a href="./errorlog?y=4/">Hung Backoffice</a></td>
    }

    if( nXPost>0 ){

      @ <tr><td align="right">%d(nXPost)</td>
      @     <td><a href="./errorlog?y=8/">POSTs from different origin</a></td>
    }






    if( nOther>0 ){
      @ <tr><td align="right">%d(nOther)</td>

      @     <td><a href="./errorlog?y=64/">Other</a></td>
    }
    if( nHack+nXPost>0 && nNonHack>0 ){
      @ <tr><td align="right">%d(nNonHack)</td>
      @ <td><a href="%R/errorlog?y=70">Other than hack attempts</a></td>



    }
    @ <tr><td align="right">%d(nTotal)</td>
    if( nTotal>0 ){




      @     <td><a href="./errorlog?y=255">All Messages</a></td>
    }else{

      @     <td>All Messages</td>
    }

    @ </table>
  }



  style_finish_page();
}
Changes to src/setup.c.
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
  style_finish_page();
}


/*
** WEBPAGE: setup-logmenu
**
** Show a menu of available log renderings accessible to an administrator, 
** together with a succinct explanation of each.
**
** This page is only accessible by administrators.
*/
void setup_logmenu_page(void){
  Blob desc;
  int bErrLog;                 /* True if Error Log enabled */
  blob_init(&desc, 0, 0);

  /* Administrator access only */
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  style_header("Log Menu");
  @ <table border="0" cellspacing="3">
  
  if( db_get_boolean("admin-log",0)==0 ){
    blob_appendf(&desc,
      "The admin log records configuration changes to the repository.\n"
      "<b>Disabled</b>:  Turn on the "
      " <a href='%R/setup_settings'>admin-log setting</a> to enable."
    );
    setup_menu_entry("Admin Log", 0, blob_str(&desc));
    blob_reset(&desc);
  }else{
    setup_menu_entry("Admin Log", "admin_log",
      "The admin log records configuration changes to the repository\n"
      "in the \"admin_log\" table.\n"
    );
  }
  setup_menu_entry("Artifact Log", "rcvfromlist",
    "The artifact log records when new content is added in the\n"
    "\"rcvfrom\" table.\n"
  );
  if( db_get_boolean("access-log",0) ){
    setup_menu_entry("User Log", "user_log",
      "Login attempts recorded in the \"accesslog\" table."
    );
  }else{
    blob_appendf(&desc,
      "Login attempts recorded in the \"accesslog\" table.\n"
      "<b>Disabled</b>:  Turn on the "







|


















|

















|







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
  style_finish_page();
}


/*
** WEBPAGE: setup-logmenu
**
** Show a menu of available log renderings accessible to an administrator,
** together with a succinct explanation of each.
**
** This page is only accessible by administrators.
*/
void setup_logmenu_page(void){
  Blob desc;
  int bErrLog;                 /* True if Error Log enabled */
  blob_init(&desc, 0, 0);

  /* Administrator access only */
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  style_header("Log Menu");
  @ <table border="0" cellspacing="3">
  
  if( db_get_boolean("admin-log",1)==0 ){
    blob_appendf(&desc,
      "The admin log records configuration changes to the repository.\n"
      "<b>Disabled</b>:  Turn on the "
      " <a href='%R/setup_settings'>admin-log setting</a> to enable."
    );
    setup_menu_entry("Admin Log", 0, blob_str(&desc));
    blob_reset(&desc);
  }else{
    setup_menu_entry("Admin Log", "admin_log",
      "The admin log records configuration changes to the repository\n"
      "in the \"admin_log\" table.\n"
    );
  }
  setup_menu_entry("Artifact Log", "rcvfromlist",
    "The artifact log records when new content is added in the\n"
    "\"rcvfrom\" table.\n"
  );
  if( db_get_boolean("access-log",1) ){
    setup_menu_entry("User Log", "user_log",
      "Login attempts recorded in the \"accesslog\" table."
    );
  }else{
    blob_appendf(&desc,
      "Login attempts recorded in the \"accesslog\" table.\n"
      "<b>Disabled</b>:  Turn on the "
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
    blob_appendf(&desc,"In this repository, the error log is the file "
       "named \"%s\".", g.zErrlog);
    bErrLog = 1;
  }
  setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc));
  blob_reset(&desc);

  @ <tr><td><td><td>
  @ &mdash;&mdash;
  @ <i>The remaining links are subsets of the Error Log</i>
  @ &mdash;&mdash;
  @ </td>  

  setup_menu_entry("Panic Log", bErrLog ? "paniclog" : 0,
    "Only the most important messages in the Error Log:\n"
    "assertion faults, segmentation faults, and similar malfunctions.\n"
  );
  setup_menu_entry("Hack Log", bErrLog ? "hacklog" : 0,
    "All code-418 hack attempts in the Error Log"
  );
  setup_menu_entry("Non-Hack Log", bErrLog ? "hacklog?not" : 0,
    "All log messages that are not code-418 hack attempts"
  );

  @ </table>
  style_finish_page();
}

/*
** Generate a checkbox for an attribute.
*/







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







262
263
264
265
266
267
268

















269
270
271
272
273
274
275
    blob_appendf(&desc,"In this repository, the error log is the file "
       "named \"%s\".", g.zErrlog);
    bErrLog = 1;
  }
  setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc));
  blob_reset(&desc);


















  @ </table>
  style_finish_page();
}

/*
** Generate a checkbox for an attribute.
*/
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
  @ <li><p><b>project-description</b> &rarr;
  @ A description of project in this repository.  This is a verbose form
  @ of project-name.  This description can be edited in the second entry
  @ box on the <a href="./setup_config">Setup/Configuration page</a>.
  @
  @ <li><p><b>project-name</b> &rarr;
  @ The human-readable name for the project.  The project-name can be
  @ modified in the first entry on the 
  @ <a href="./setup_config">Setup/Configuration page</a>.
  @
  @ <li><p><b>peer-repo-<i>CODE</i></b> &rarr;
  @ <i>CODE</i> is 16-character prefix of the project-code for another
  @ repository that is part of the same login-group.  The value is the
  @ filename for the peer repository.
  @







|







933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
  @ <li><p><b>project-description</b> &rarr;
  @ A description of project in this repository.  This is a verbose form
  @ of project-name.  This description can be edited in the second entry
  @ box on the <a href="./setup_config">Setup/Configuration page</a>.
  @
  @ <li><p><b>project-name</b> &rarr;
  @ The human-readable name for the project.  The project-name can be
  @ modified in the first entry on the
  @ <a href="./setup_config">Setup/Configuration page</a>.
  @
  @ <li><p><b>peer-repo-<i>CODE</i></b> &rarr;
  @ <i>CODE</i> is 16-character prefix of the project-code for another
  @ repository that is part of the same login-group.  The value is the
  @ filename for the peer repository.
  @
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
  style_set_current_feature("setup");
  style_header("Timeline Display Preferences");
  db_begin_transaction();
  @ <form action="%R/setup_timeline" method="post"><div>
  login_insert_csrf_secret();
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>

  @ <hr>
  onoff_attribute("Allow block-markup in timeline",
                  "timeline-block-markup", "tbm", 0, 0);
  @ <p>In timeline displays, check-in comments can be displayed with or
  @ without block markup such as paragraphs, tables, etc.
  @ (Property: "timeline-block-markup")</p>

  @ <hr>
  onoff_attribute("Plaintext comments on timelines",
                  "timeline-plaintext", "tpt", 0, 0);
  @ <p>In timeline displays, check-in comments are displayed literally,
  @ without any wiki or HTML interpretation.  Use CSS to change
  @ display formatting features such as fonts and line-wrapping behavior.
  @ (Property: "timeline-plaintext")</p>







<
<
<
<
<
<
<







979
980
981
982
983
984
985







986
987
988
989
990
991
992
  style_set_current_feature("setup");
  style_header("Timeline Display Preferences");
  db_begin_transaction();
  @ <form action="%R/setup_timeline" method="post"><div>
  login_insert_csrf_secret();
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>








  @ <hr>
  onoff_attribute("Plaintext comments on timelines",
                  "timeline-plaintext", "tpt", 0, 0);
  @ <p>In timeline displays, check-in comments are displayed literally,
  @ without any wiki or HTML interpretation.  Use CSS to change
  @ display formatting features such as fonts and line-wrapping behavior.
  @ (Property: "timeline-plaintext")</p>
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



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
** Change or view miscellaneous settings.  Part of the
** /setup pages requiring Setup privileges.
*/
void setup_settings(void){
  int nSetting;
  int i;
  Setting const *pSet;

  const Setting *aSetting = setting_info(&nSetting);

  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }

  style_set_current_feature("setup");
  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();




  @ <p>Settings marked with (v) are "versionable" and will be overridden
  @ by the contents of managed files named
  @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>".
  @ If the file for a versionable setting exists, the value cannot be
  @ changed on this screen.</p><hr><p>
  @
  @ <form action="%R/setup_settings" method="post"><div>






  @ <table border="0"><tr><td valign="top">
  login_insert_csrf_secret();
  for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
    if( pSet->width==0 ){
      int hasVersionableValue = pSet->versionable &&
          (db_get_versioned(pSet->name, NULL)!=0);



      onoff_attribute("", pSet->name,
                      pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
                      is_truth(pSet->def), hasVersionableValue);
      @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
      if( pSet->versionable ){
        @  (v)<br>
      } else {
        @ <br>
      }
    }
  }
  @ <br><input type="submit"  name="submit" value="Apply Changes">
  @ </td><td style="width:50px;"></td><td valign="top">
  @ <table>
  for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
    if( pSet->width>0 && !pSet->forceTextArea ){
      int hasVersionableValue = pSet->versionable &&
          (db_get_versioned(pSet->name, NULL)!=0);



      @ <tr><td>
      @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
      if( pSet->versionable ){
        @  (v)
      } else {
        @
      }
      @</td><td>
      entry_attribute("", /*pSet->width*/ 25, pSet->name,
                      pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
                      (char*)pSet->def, hasVersionableValue);
      @</td></tr>
    }
  }
  @</table>
  @ </td><td style="width:50px;"></td><td valign="top">
  for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
    if( pSet->width>0 && pSet->forceTextArea ){
      int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0;



      @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
      if( pSet->versionable ){
        @  (v)<br>
      } else {
        @ <br>
      }
      textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name,







>
















>
>
>
>







>
>
>
>
>
>





|
>
>
>

















|
>
>
>


















|
>
>
>







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
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
** Change or view miscellaneous settings.  Part of the
** /setup pages requiring Setup privileges.
*/
void setup_settings(void){
  int nSetting;
  int i;
  Setting const *pSet;
  int bIfChng = P("all")==0;
  const Setting *aSetting = setting_info(&nSetting);

  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }

  style_set_current_feature("setup");
  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();
  if( bIfChng ){
    @ <p>Only settings whose value is different from the default are shown.
    @ Click the "All" button above to set all settings.
  }
  @ <p>Settings marked with (v) are "versionable" and will be overridden
  @ by the contents of managed files named
  @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>".
  @ If the file for a versionable setting exists, the value cannot be
  @ changed on this screen.</p><hr><p>
  @
  @ <form action="%R/setup_settings" method="post"><div>
  if( bIfChng ){
    style_submenu_element("All", "%R/setup_settings?all");
  }else{
    @ <input type="hidden" name="all" value="1">
    style_submenu_element("Changes-Only", "%R/setup_settings");
  }
  @ <table border="0"><tr><td valign="top">
  login_insert_csrf_secret();
  for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
    if( pSet->width==0 ){
      int hasVersionableValue = pSet->versionable &&
          (db_get_versioned(pSet->name, NULL, NULL)!=0);
      if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
        continue;
      }
      onoff_attribute("", pSet->name,
                      pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
                      is_truth(pSet->def), hasVersionableValue);
      @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
      if( pSet->versionable ){
        @  (v)<br>
      } else {
        @ <br>
      }
    }
  }
  @ <br><input type="submit"  name="submit" value="Apply Changes">
  @ </td><td style="width:50px;"></td><td valign="top">
  @ <table>
  for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
    if( pSet->width>0 && !pSet->forceTextArea ){
      int hasVersionableValue = pSet->versionable &&
          (db_get_versioned(pSet->name, NULL, NULL)!=0);
      if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
        continue;
      }
      @ <tr><td>
      @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
      if( pSet->versionable ){
        @  (v)
      } else {
        @
      }
      @</td><td>
      entry_attribute("", /*pSet->width*/ 25, pSet->name,
                      pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
                      (char*)pSet->def, hasVersionableValue);
      @</td></tr>
    }
  }
  @</table>
  @ </td><td style="width:50px;"></td><td valign="top">
  for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
    if( pSet->width>0 && pSet->forceTextArea ){
      int hasVersionableValue = db_get_versioned(pSet->name, NULL, NULL)!=0;
      if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
        continue;
      }
      @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
      if( pSet->versionable ){
        @  (v)<br>
      } else {
        @ <br>
      }
      textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name,
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
  style_set_current_feature("setup");
  style_header("Wiki Configuration");
  db_begin_transaction();
  @ <form action="%R/setup_wiki" method="post"><div>
  login_insert_csrf_secret();
  @ <input type="submit"  name="submit" value="Apply Changes"></p>
  @ <hr>
  onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins",
                  "wiki-about", "wiki-about", 1, 0);
  @ <p>
  @ Associate wiki pages with branches, tags, or checkins, based on
  @ the wiki page name.  Wiki pages that begin with "branch/", "checkin/"
  @ or "tag/" and which continue with the name of an existing branch, check-in
  @ or tag are treated specially when this feature is enabled.
  @ <ul>
  @ <li> <b>branch/</b><i>branch-name</i>
  @ <li> <b>checkin/</b><i>full-check-in-hash</i>
  @ <li> <b>tag/</b><i>tag-name</i>

  @ </ul>
  @ (Property: "wiki-about")</p>
  @ <hr>
  entry_attribute("Allow Unsafe HTML In Markdown", 6,
                  "safe-html", "safe-html", "", 0);
  @ <p>Allow "unsafe" HTML (ex: &lt;script&gt;, &lt;form&gt;, etc) to be
  @ generated by <a href="%R/md_rules">Markdown-formatted</a> documents.







|


|
|
|
|




>







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
  style_set_current_feature("setup");
  style_header("Wiki Configuration");
  db_begin_transaction();
  @ <form action="%R/setup_wiki" method="post"><div>
  login_insert_csrf_secret();
  @ <input type="submit"  name="submit" value="Apply Changes"></p>
  @ <hr>
  onoff_attribute("Associate Wiki Pages With Branches, Tags, Tickets, or Checkins",
                  "wiki-about", "wiki-about", 1, 0);
  @ <p>
  @ Associate wiki pages with branches, tags, tickets, or checkins, based on
  @ the wiki page name.  Wiki pages that begin with "branch/", "checkin/",
  @ "tag/" or "ticket" and which continue with the name of an existing branch,
  @ check-in, tag or ticket are treated specially when this feature is enabled.
  @ <ul>
  @ <li> <b>branch/</b><i>branch-name</i>
  @ <li> <b>checkin/</b><i>full-check-in-hash</i>
  @ <li> <b>tag/</b><i>tag-name</i>
  @ <li> <b>ticket/</b><i>full-ticket-hash</i>
  @ </ul>
  @ (Property: "wiki-about")</p>
  @ <hr>
  entry_attribute("Allow Unsafe HTML In Markdown", 6,
                  "safe-html", "safe-html", "", 0);
  @ <p>Allow "unsafe" HTML (ex: &lt;script&gt;, &lt;form&gt;, etc) to be
  @ generated by <a href="%R/md_rules">Markdown-formatted</a> documents.
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
  }
  style_set_current_feature("setup");
  style_header("Admin Log");
  style_submenu_element("Log-Menu", "setup-logmenu");
  create_admin_log_table();
  limit = atoi(PD("n","200"));
  ofst = atoi(PD("x","0"));
  fLogEnabled = db_get_boolean("admin-log", 0);
  @ <div>Admin logging is %s(fLogEnabled?"on":"off").
  @ (Change this on the <a href="setup_settings">settings</a> page.)</div>

  if( ofst>0 ){
    int prevx = ofst - limit;
    if( prevx<0 ) prevx = 0;
    @ <p><a href="admin_log?n=%d(limit)&x=%d(prevx)">[Newer]</a></p>







|







2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
  }
  style_set_current_feature("setup");
  style_header("Admin Log");
  style_submenu_element("Log-Menu", "setup-logmenu");
  create_admin_log_table();
  limit = atoi(PD("n","200"));
  ofst = atoi(PD("x","0"));
  fLogEnabled = db_get_boolean("admin-log", 1);
  @ <div>Admin logging is %s(fLogEnabled?"on":"off").
  @ (Change this on the <a href="setup_settings">settings</a> page.)</div>

  if( ofst>0 ){
    int prevx = ofst - limit;
    if( prevx<0 ) prevx = 0;
    @ <p><a href="admin_log?n=%d(limit)&x=%d(prevx)">[Newer]</a></p>
2249
2250
2251
2252
2253
2254
2255


2256
2257
2258
2259
2260
2261
2262
  onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0);
  @ <br>
  onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0);
  @ <br>
  onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0);
  @ <br>
  onoff_attribute("Search Forum", "search-forum", "sf", 0, 0);


  @ <hr>
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ <hr>
  if( P("fts0") ){
    search_drop_index();
  }else if( P("fts1") ){
    const char *zTokenizer = PD("ftstok","off");







>
>







2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
  onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0);
  @ <br>
  onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0);
  @ <br>
  onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0);
  @ <br>
  onoff_attribute("Search Forum", "search-forum", "sf", 0, 0);
  @ <br>
  onoff_attribute("Search Built-in Help Text", "search-help", "sh", 0, 0);
  @ <hr>
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ <hr>
  if( P("fts0") ){
    search_drop_index();
  }else if( P("fts1") ){
    const char *zTokenizer = PD("ftstok","off");
Changes to src/setupuser.c.
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
*/
static int isValidPwString(const char *zPw){
  if( zPw==0 ) return 0;
  if( zPw[0]==0 ) return 1;
  while( zPw[0]=='*' ){ zPw++; }
  return zPw[0]!=0;
}




























































































/*
** WEBPAGE: setup_uedit
**
** Edit information about a user or create a new user.
** Requires Admin privileges.
*/
void user_edit(void){
  const char *zId, *zLogin, *zInfo, *zCap, *zPw;
  const char *zGroup;
  const char *zOldLogin;
  int uid, i;

  char *zDeleteVerify = 0;   /* Delete user verification text */
  int higherUser = 0;  /* True if user being edited is SETUP and the */
                       /* user doing the editing is ADMIN.  Disallow editing */
  const char *inherit[128];
  int a[128];
  const char *oa[128];

  /* Must have ADMIN privileges to access this page
  */
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }

  /* Check to see if an ADMIN user is trying to edit a SETUP account.
  ** Don't allow that.
  */
  zId = PD("id", "0");
  uid = atoi(zId);
  if( zId && !g.perm.Setup && uid>0 ){
    char *zOldCaps;
    zOldCaps = db_text(0, "SELECT cap FROM user WHERE uid=%d",uid);

    higherUser = zOldCaps && strchr(zOldCaps,'s');

  }

  if( P("can") ){
    /* User pressed the cancel button */
    cgi_redirect(cgi_referer("setup_ulist"));
    return;
  }







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












>

















|
<
|
>
|
>







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
*/
static int isValidPwString(const char *zPw){
  if( zPw==0 ) return 0;
  if( zPw[0]==0 ) return 1;
  while( zPw[0]=='*' ){ zPw++; }
  return zPw[0]!=0;
}

/*
** Return true if user capability string zNew contains any capability
** letter which is not in user capability string zOrig, else 0.  This
** does not take inherited permissions into account. Either argument
** may be NULL.
*/
static int userHasNewCaps(const char *zOrig, const char *zNew){
  for( ; zNew && *zNew; ++zNew ){
    if( !zOrig || strchr(zOrig,*zNew)==0 ){
      return *zNew;
    }
  }
  return 0;
}

/*
** Sends notification of user permission elevation changes to all
** subscribers with a "u" subscription. This is a no-op if alerts are
** not enabled.
**
** These subscriptions differ from most, in that:
**
** - They currently lack an "unsubscribe" link.
**
** - Only an admin can assign this subscription, but if a non-admin
**   edits their subscriptions after an admin assigns them this one,
**   this particular one will be lost.  "Feature or bug?" is unclear,
**   but it would be odd for a non-admin to be assigned this
**   capability.
*/
static void alert_user_elevation(const char *zLogin,   /*Affected user*/
                                 int uid,              /*[user].uid*/
                                 int bIsNew,           /*true if new user*/
                                 const char *zOrigCaps,/*Old caps*/
                                 const char *zNewCaps  /*New caps*/){
  Blob hdr, body;
  Stmt q;
  int nBody;
  AlertSender *pSender;
  char *zSubname;
  char *zURL;
  char * zSubject;

  if( !alert_enabled() ) return;
  zSubject = bIsNew
    ? mprintf("New user created: [%q]", zLogin)
    : mprintf("User [%q] permissions elevated", zLogin);
  zURL = db_get("email-url",0);
  zSubname = db_get("email-subname", "[Fossil Repo]");
  blob_init(&body, 0, 0);
  blob_init(&hdr, 0, 0);
  if( bIsNew ){
    blob_appendf(&body, "User [%q] was created by with "
                 "permissions [%q] by user [%q].\n",
                 zLogin, zNewCaps, g.zLogin);
  } else {
    blob_appendf(&body, "Permissions for user [%q] where elevated "
                 "from [%q] to [%q] by user [%q].\n",
                 zLogin, zOrigCaps, zNewCaps, g.zLogin);
  }
  if( zURL ){
    blob_appendf(&body, "\nUser editor: %s/setup_uedit?uid=%d\n", zURL, uid);
  }
  nBody = blob_size(&body);
  pSender = alert_sender_new(0, 0);
  db_prepare(&q,
        "SELECT semail, hex(subscriberCode)"
        "  FROM subscriber, user "
        " WHERE sverified AND NOT sdonotcall"
        "   AND suname=login"
        "   AND ssub GLOB '*u*'");
  while( !pSender->zErr && db_step(&q)==SQLITE_ROW ){
    const char *zTo = db_column_text(&q, 0);
    blob_truncate(&hdr, 0);
    blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n",
                 zTo, zSubname, zSubject);
    if( zURL ){
      const char *zCode = db_column_text(&q, 1);
      blob_truncate(&body, nBody);
      blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
                   zURL, zCode);
    }
    alert_send(pSender, &hdr, &body, 0);
  }
  db_finalize(&q);
  alert_sender_free(pSender);
  fossil_free(zURL);
  fossil_free(zSubname);
  fossil_free(zSubject);
}

/*
** WEBPAGE: setup_uedit
**
** Edit information about a user or create a new user.
** Requires Admin privileges.
*/
void user_edit(void){
  const char *zId, *zLogin, *zInfo, *zCap, *zPw;
  const char *zGroup;
  const char *zOldLogin;
  int uid, i;
  char *zOldCaps = 0;        /* Capabilities before edit */
  char *zDeleteVerify = 0;   /* Delete user verification text */
  int higherUser = 0;  /* True if user being edited is SETUP and the */
                       /* user doing the editing is ADMIN.  Disallow editing */
  const char *inherit[128];
  int a[128];
  const char *oa[128];

  /* Must have ADMIN privileges to access this page
  */
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }

  /* Check to see if an ADMIN user is trying to edit a SETUP account.
  ** Don't allow that.
  */
  zId = PD("id", "0");
  uid = atoi(zId);
  if( uid>0 ){

    zOldCaps = db_text("", "SELECT cap FROM user WHERE uid=%d",uid);
    if( zId && !g.perm.Setup ){
      higherUser = zOldCaps && strchr(zOldCaps,'s');
    }
  }

  if( P("can") ){
    /* User pressed the cancel button */
    cgi_redirect(cgi_referer("setup_ulist"));
    return;
  }
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
  }else if( zDeleteVerify!=0 ){
    /* Need to verify a delete request */
  }else if( !cgi_csrf_safe(2) ){
    /* This might be a cross-site request forgery, so ignore it */
  }else{
    /* We have all the information we need to make the change to the user */
    char c;


    char zCap[70], zNm[4];
    zNm[0] = 'a';
    zNm[2] = 0;
    for(i=0, c='a'; c<='z'; c++){
      zNm[1] = c;
      a[c&0x7f] = ((c!='s' && c!='y') || g.perm.Setup) && P(zNm)!=0;
      if( a[c&0x7f] ) zCap[i++] = c;
    }
    for(c='0'; c<='9'; c++){
      zNm[1] = c;
      a[c&0x7f] = P(zNm)!=0;
      if( a[c&0x7f] ) zCap[i++] = c;
    }
    for(c='A'; c<='Z'; c++){
      zNm[1] = c;
      a[c&0x7f] = P(zNm)!=0;
      if( a[c&0x7f] ) zCap[i++] = c;
    }

    zCap[i] = 0;

    zPw = P("pw");
    zLogin = P("login");
    if( strlen(zLogin)==0 ){
      const char *zRef = cgi_referer("setup_ulist");
      style_header("User Creation Error");
      @ <span class="loginError">Empty login not allowed.</span>
      @







>
>
|





|




|




|


|
>







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
  }else if( zDeleteVerify!=0 ){
    /* Need to verify a delete request */
  }else if( !cgi_csrf_safe(2) ){
    /* This might be a cross-site request forgery, so ignore it */
  }else{
    /* We have all the information we need to make the change to the user */
    char c;
    int bHasNewCaps = 0 /* 1 if user's permissions are increased */;
    const int bIsNew = uid<=0;
    char aCap[70], zNm[4];
    zNm[0] = 'a';
    zNm[2] = 0;
    for(i=0, c='a'; c<='z'; c++){
      zNm[1] = c;
      a[c&0x7f] = ((c!='s' && c!='y') || g.perm.Setup) && P(zNm)!=0;
      if( a[c&0x7f] ) aCap[i++] = c;
    }
    for(c='0'; c<='9'; c++){
      zNm[1] = c;
      a[c&0x7f] = P(zNm)!=0;
      if( a[c&0x7f] ) aCap[i++] = c;
    }
    for(c='A'; c<='Z'; c++){
      zNm[1] = c;
      a[c&0x7f] = P(zNm)!=0;
      if( a[c&0x7f] ) aCap[i++] = c;
    }

    aCap[i] = 0;
    bHasNewCaps = bIsNew || userHasNewCaps(zOldCaps, &aCap[0]);
    zPw = P("pw");
    zLogin = P("login");
    if( strlen(zLogin)==0 ){
      const char *zRef = cgi_referer("setup_ulist");
      style_header("User Creation Error");
      @ <span class="loginError">Empty login not allowed.</span>
      @
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
      @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
      @ [Bummer]</a></p>
      style_finish_page();
      return;
    }
    cgi_csrf_verify();
    db_unprotect(PROTECT_USER);
    db_multi_exec(
       "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
       "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())",

      uid, zLogin, P("info"), zPw, zCap
    );
    if( zOldLogin && fossil_strcmp(zLogin, zOldLogin)!=0 ){
      if( alert_tables_exist() ){
        /* Rename matching subscriber entry, else the user cannot
           re-subscribe with their same email address. */
        db_multi_exec("UPDATE subscriber SET suname=%Q WHERE suname=%Q",
                      zLogin, zOldLogin);
      }
      admin_log( "Renamed user [%q] to [%q].", zOldLogin, zLogin );
    }
    db_protect_pop();
    setup_incr_cfgcnt();
    admin_log( "Updated user [%q] with capabilities [%q].",

               zLogin, zCap );
    if( atoi(PD("all","0"))>0 ){
      Blob sql;
      char *zErr = 0;
      blob_zero(&sql);
      if( zOldLogin==0 ){
        blob_appendf(&sql,
          "INSERT INTO user(login)"







|
|
|
>
|
|











|
>
|







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
      @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
      @ [Bummer]</a></p>
      style_finish_page();
      return;
    }
    cgi_csrf_verify();
    db_unprotect(PROTECT_USER);
    uid = db_int(0,
                 "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
                 "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now()) "
                 "RETURNING uid",
                 uid, zLogin, P("info"), zPw, &aCap[0]);
    assert( uid>0 );
    if( zOldLogin && fossil_strcmp(zLogin, zOldLogin)!=0 ){
      if( alert_tables_exist() ){
        /* Rename matching subscriber entry, else the user cannot
           re-subscribe with their same email address. */
        db_multi_exec("UPDATE subscriber SET suname=%Q WHERE suname=%Q",
                      zLogin, zOldLogin);
      }
      admin_log( "Renamed user [%q] to [%q].", zOldLogin, zLogin );
    }
    db_protect_pop();
    setup_incr_cfgcnt();
    admin_log( "%s user [%q] with capabilities [%q].",
               bIsNew ? "Added" : "Updated",
               zLogin, &aCap[0] );
    if( atoi(PD("all","0"))>0 ){
      Blob sql;
      char *zErr = 0;
      blob_zero(&sql);
      if( zOldLogin==0 ){
        blob_appendf(&sql,
          "INSERT INTO user(login)"
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
        "UPDATE user SET login=%Q,"
        "  pw=coalesce(shared_secret(%Q,%Q,"
                "(SELECT value FROM config WHERE name='project-code')),pw),"
        "  info=%Q,"
        "  cap=%Q,"
        "  mtime=now()"
        " WHERE login=%Q;",
        zLogin, P("pw"), zLogin, P("info"), zCap,
        zOldLogin
      );
      db_unprotect(PROTECT_USER);
      login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr);
      db_protect_pop();
      blob_reset(&sql);
      admin_log( "Updated user [%q] in all login groups "
                 "with capabilities [%q].",
                 zLogin, zCap );
      if( zErr ){
        const char *zRef = cgi_referer("setup_ulist");
        style_header("User Change Error");
        admin_log( "Error updating user '%q': %s'.", zLogin, zErr );
        @ <span class="loginError">%h(zErr)</span>
        @
        @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
        @ [Bummer]</a></p>
        style_finish_page();



        return;
      }
    }



    cgi_redirect(cgi_referer("setup_ulist"));
    return;
  }

  /* Load the existing information about the user, if any
  */
  zLogin = "";
  zInfo = "";
  zCap = "";
  zPw = "";
  for(i='a'; i<='z'; i++) oa[i] = "";
  for(i='0'; i<='9'; i++) oa[i] = "";
  for(i='A'; i<='Z'; i++) oa[i] = "";
  if( uid ){

    zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
    zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
    zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
    zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
    for(i=0; zCap[i]; i++){
      char c = zCap[i];
      if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){
        oa[c&0x7f] = " checked=\"checked\"";
      }
    }







|








|









>
>
>



>
>
>








|





>


<







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
        "UPDATE user SET login=%Q,"
        "  pw=coalesce(shared_secret(%Q,%Q,"
                "(SELECT value FROM config WHERE name='project-code')),pw),"
        "  info=%Q,"
        "  cap=%Q,"
        "  mtime=now()"
        " WHERE login=%Q;",
        zLogin, P("pw"), zLogin, P("info"), &aCap[0],
        zOldLogin
      );
      db_unprotect(PROTECT_USER);
      login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr);
      db_protect_pop();
      blob_reset(&sql);
      admin_log( "Updated user [%q] in all login groups "
                 "with capabilities [%q].",
                 zLogin, &aCap[0] );
      if( zErr ){
        const char *zRef = cgi_referer("setup_ulist");
        style_header("User Change Error");
        admin_log( "Error updating user '%q': %s'.", zLogin, zErr );
        @ <span class="loginError">%h(zErr)</span>
        @
        @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
        @ [Bummer]</a></p>
        style_finish_page();
        if( bHasNewCaps ){
          alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]);
        }
        return;
      }
    }
    if( bHasNewCaps ){
      alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]);
    }
    cgi_redirect(cgi_referer("setup_ulist"));
    return;
  }

  /* Load the existing information about the user, if any
  */
  zLogin = "";
  zInfo = "";
  zCap = zOldCaps;
  zPw = "";
  for(i='a'; i<='z'; i++) oa[i] = "";
  for(i='0'; i<='9'; i++) oa[i] = "";
  for(i='A'; i<='Z'; i++) oa[i] = "";
  if( uid ){
    assert( zCap );
    zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
    zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);

    zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
    for(i=0; zCap[i]; i++){
      char c = zCap[i];
      if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){
        oa[c&0x7f] = " checked=\"checked\"";
      }
    }
Changes to src/sitemap.c.
291
292
293
294
295
296
297

298
299
300
301
302
303
304
  }
  if( g.perm.Read ){
    @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li>
  }
  @ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li>
  @ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li>
  @ <li>%z(href("%R/hash-color-test"))Hash color test</a>

  if( g.perm.Admin ){
    @ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li>
    @ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li>
    @ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li>
    @ <li>%z(href("%R/test-warning"))Error Log test page</a></li>
    @ <li>%z(href("%R/repo_stat1"))Repository <tt>sqlite_stat1</tt> table</a>
    @ <li>%z(href("%R/repo_schema"))Repository schema</a></li>







>







291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
  }
  if( g.perm.Read ){
    @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li>
  }
  @ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li>
  @ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li>
  @ <li>%z(href("%R/hash-color-test"))Hash color test</a>
  @ <li>%z(href("%R/test-bgcolor"))Background color test</a>
  if( g.perm.Admin ){
    @ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li>
    @ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li>
    @ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li>
    @ <li>%z(href("%R/test-warning"))Error Log test page</a></li>
    @ <li>%z(href("%R/repo_stat1"))Repository <tt>sqlite_stat1</tt> table</a>
    @ <li>%z(href("%R/repo_schema"))Repository schema</a></li>
Changes to src/sqlcmd.c.
233
234
235
236
237
238
239

240
241
242
243
244
245
246
  }
  if( g.zConfigDbName ){
    char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''",
                                 g.zConfigDbName);
    sqlite3_exec(db, zSql, 0, 0, 0);
    sqlite3_free(zSql);
  }

  /* Arrange to trace close operations so that static prepared statements
  ** will get cleaned up when the shell closes the database connection */
  if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE;
  sqlite3_trace_v2(db, mTrace, db_sql_trace, 0);
  db_protect_only(PROTECT_NONE);
  sqlite3_set_authorizer(db, db_top_authorizer, db);
  if( local_bSqlCmdTest ){







>







233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
  }
  if( g.zConfigDbName ){
    char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''",
                                 g.zConfigDbName);
    sqlite3_exec(db, zSql, 0, 0, 0);
    sqlite3_free(zSql);
  }
  (void)timeline_query_for_tty();  /* Registers wiki_to_text() as side-effect */
  /* Arrange to trace close operations so that static prepared statements
  ** will get cleaned up when the shell closes the database connection */
  if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE;
  sqlite3_trace_v2(db, mTrace, db_sql_trace, 0);
  db_protect_only(PROTECT_NONE);
  sqlite3_set_authorizer(db, db_top_authorizer, db);
  if( local_bSqlCmdTest ){
Changes to src/stash.c.
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
  ){
    int fBaseline = 0;
    DiffConfig DCfg;

    if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){
      fBaseline = 1;
    }
    if( find_option("tk",0,0)!=0 ){
      db_close(0);
      diff_tk(fBaseline ? "stash show" : "stash diff", 3);
      return;
    }
    diff_options(&DCfg, zCmd[0]=='g', 0);
    stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
    stash_diff(stashid, fBaseline, &DCfg);







|







754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
  ){
    int fBaseline = 0;
    DiffConfig DCfg;

    if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){
      fBaseline = 1;
    }
    if( find_option("tk",0,0)!=0 || gdiff_using_tk(zCmd[0]=='g') ){
      db_close(0);
      diff_tk(fBaseline ? "stash show" : "stash diff", 3);
      return;
    }
    diff_options(&DCfg, zCmd[0]=='g', 0);
    stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
    stash_diff(stashid, fBaseline, &DCfg);
Changes to src/statrep.c.
316
317
318
319
320
321
322

323
324
325
326
327
328
329
330
331
332
333
334
     && rNowFraction>0.05
     && nCount>0
     && nMaxEvents>0
    ){
      /* If the timespan covered by this row contains "now", then project
      ** the number of changes until the completion of the timespan and
      ** show a dashed box of that projection. */

      int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
      int nXSize = (100 * nExtra)/nMaxEvents;
      @ <span class='statistics-report-graph-line' \
      @  style='display:inline-block;min-width:%d(nSize)%%;'>&nbsp;</span>\
      @ <span class='statistics-report-graph-extra' \
      @  style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
    }else{
      @ <div class='statistics-report-graph-line' \
      @  style='width:%d(nSize)%%;'>&nbsp;</div> \
    }
    @ </td>
    @ </tr>







>




|







316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
     && rNowFraction>0.05
     && nCount>0
     && nMaxEvents>0
    ){
      /* If the timespan covered by this row contains "now", then project
      ** the number of changes until the completion of the timespan and
      ** show a dashed box of that projection. */
      int nProj = (int)(((double)nCount)/rNowFraction);
      int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
      int nXSize = (100 * nExtra)/nMaxEvents;
      @ <span class='statistics-report-graph-line' \
      @  style='display:inline-block;min-width:%d(nSize)%%;'>&nbsp;</span>\
      @ <span class='statistics-report-graph-extra' title='%d(nProj)' \
      @  style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
    }else{
      @ <div class='statistics-report-graph-line' \
      @  style='width:%d(nSize)%%;'>&nbsp;</div> \
    }
    @ </td>
    @ </tr>
745
746
747
748
749
750
751
752
753
754

755
756
757
758
759
760
761
762
763
764
765
766
    cgi_printf("<td style='white-space: nowrap;'>");
    if( nCount ){
      if( zCurrentWeek!=0
      && strcmp(zWeek, zCurrentWeek)==0
      && rNowFraction>0.05
      && nMaxEvents>0
      ){
        /* If the covered covered by this row contains "now", then project
        ** the number of changes until the completion of the week and
        ** show a dashed box of that projection. */

        int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
        int nXSize = (100 * nExtra)/nMaxEvents;
        @ <span class='statistics-report-graph-line' \
        @  style='display:inline-block;min-width:%d(nSize)%%;'>&nbsp;</span>\
        @ <span class='statistics-report-graph-extra' \
        @  style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
      }else{
        @ <div class='statistics-report-graph-line' \
        @  style='width:%d(nSize)%%;'>&nbsp;</div> \
      }
    }
    cgi_printf("</td></tr>\n");







|


>




|







746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
    cgi_printf("<td style='white-space: nowrap;'>");
    if( nCount ){
      if( zCurrentWeek!=0
      && strcmp(zWeek, zCurrentWeek)==0
      && rNowFraction>0.05
      && nMaxEvents>0
      ){
        /* If the timespan covered by this row contains "now", then project
        ** the number of changes until the completion of the week and
        ** show a dashed box of that projection. */
        int nProj = (int)(((double)nCount)/rNowFraction);
        int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
        int nXSize = (100 * nExtra)/nMaxEvents;
        @ <span class='statistics-report-graph-line' \
        @  style='display:inline-block;min-width:%d(nSize)%%;'>&nbsp;</span>\
        @ <span class='statistics-report-graph-extra' title='%d(nProj)' \
        @  style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
      }else{
        @ <div class='statistics-report-graph-line' \
        @  style='width:%d(nSize)%%;'>&nbsp;</div> \
      }
    }
    cgi_printf("</td></tr>\n");
Changes to src/style.c.
1338
1339
1340
1341
1342
1343
1344

































1345
1346
1347
1348
1349
1350
1351
  for(i=j=0; (c = allCap[j])!=0; j++){
    if( login_has_capability(&c, 1, LOGIN_ANON)
      && !login_has_capability(&c, 1, 0) ) zCap[i++] = c;
  }
  zCap[i] = 0;
  return zCap;
}


































/*
** WEBPAGE: test_env
**
** Display CGI-variables and other aspects of the run-time
** environment, for debugging and trouble-shooting purposes.
*/







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







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
  for(i=j=0; (c = allCap[j])!=0; j++){
    if( login_has_capability(&c, 1, LOGIN_ANON)
      && !login_has_capability(&c, 1, 0) ) zCap[i++] = c;
  }
  zCap[i] = 0;
  return zCap;
}

/*
** WEBPAGE: test-title
**
** Render a test page in which the page title is set by the "title"
** query parameter.  This can be used to show that HTML or Javascript
** content in the title does not leak through into generated page, resulting
** in an XSS issue.
**
** Due to the potential for abuse, this webpage is only available to
** administrators.
*/
void page_test_title(void){
  const char *zTitle;
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
  }
  zTitle = P("title");
  if( zTitle==0 ){
    zTitle = "(No Title)";
  }
  style_header("%s", zTitle);
  @ <p>
  @ This page sets its title to the value of the "title" query parameter.
  @ The form below is a convenient way to set the title query parameter:
  @
  @ <form method="GET">
  @ Title: <input type="text" size="50" name="title" value="%h(zTitle)">
  @ <input type="submit" value="Submit">
  @ </form>
  style_finish_page();
}

/*
** WEBPAGE: test_env
**
** Display CGI-variables and other aspects of the run-time
** environment, for debugging and trouble-shooting purposes.
*/
Changes to src/tar.c.
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
  nPrefix = blob_size(&filename);

  pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
  if( pManifest ){
    int flg, eflg = 0;
    mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0);
    if( pTar ) tar_begin(mTime);
    flg = db_get_manifest_setting();
    if( flg ){
      /* eflg is the effective flags, taking include/exclude into account */
      if( (pInclude==0 || glob_match(pInclude, "manifest"))
       && !glob_match(pExclude, "manifest")
       && (flg & MFESTFLG_RAW) ){
        eflg |= MFESTFLG_RAW;
      }







|







497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
  nPrefix = blob_size(&filename);

  pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
  if( pManifest ){
    int flg, eflg = 0;
    mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0);
    if( pTar ) tar_begin(mTime);
    flg = db_get_manifest_setting(blob_str(&hash));
    if( flg ){
      /* eflg is the effective flags, taking include/exclude into account */
      if( (pInclude==0 || glob_match(pInclude, "manifest"))
       && !glob_match(pExclude, "manifest")
       && (flg & MFESTFLG_RAW) ){
        eflg |= MFESTFLG_RAW;
      }
Changes to src/terminal.c.
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
*/
int terminal_get_size(TerminalSize *t){
  memset(t, 0, sizeof(*t));

#if defined(TIOCGSIZE)
  {
    struct ttysize ts;
    if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)!=-1 ){



      t->nColumns = ts.ts_cols;
      t->nLines = ts.ts_lines;
      return 1;
    }
    return 0;
  }
#elif defined(TIOCGWINSZ)
  {
    struct winsize ws;
    if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)!=-1 ){



      t->nColumns = ws.ws_col;
      t->nLines = ws.ws_row;
      return 1;
    }
    return 0;
  }
#elif defined(_WIN32)
  {
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){



      t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
      t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
      return 1;
    }
    return 0;
  }
#else







|
>
>
>









|
>
>
>









|
>
>
>







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
*/
int terminal_get_size(TerminalSize *t){
  memset(t, 0, sizeof(*t));

#if defined(TIOCGSIZE)
  {
    struct ttysize ts;
    if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)>=0
     || ioctl(STDOUT_FILENO, TIOCGSIZE, &ts)>=0
     || ioctl(STDERR_FILENO, TIOCGSIZE, &ts)>=0
    ){
      t->nColumns = ts.ts_cols;
      t->nLines = ts.ts_lines;
      return 1;
    }
    return 0;
  }
#elif defined(TIOCGWINSZ)
  {
    struct winsize ws;
    if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)>=0
     || ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)>=0
     || ioctl(STDERR_FILENO, TIOCGWINSZ, &ws)>=0
    ){
      t->nColumns = ws.ws_col;
      t->nLines = ws.ws_row;
      return 1;
    }
    return 0;
  }
#elif defined(_WIN32)
  {
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)
     || GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi)
     || GetConsoleScreenBufferInfo(GetStdHandle(STD_INPUT_HANDLE), &csbi)
    ){
      t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
      t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
      return 1;
    }
    return 0;
  }
#else
127
128
129
130
131
132
133




















































** If the size cannot be determined, two zeros are shown.
*/
void test_terminal_size_cmd(void){
  TerminalSize ts;
  terminal_get_size(&ts);
  fossil_print("%d %d\n", ts.nColumns, ts.nLines);
}



























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
** If the size cannot be determined, two zeros are shown.
*/
void test_terminal_size_cmd(void){
  TerminalSize ts;
  terminal_get_size(&ts);
  fossil_print("%d %d\n", ts.nColumns, ts.nLines);
}

/*
** Return true if it is reasonable is emit VT100 escape codes.
*/
int terminal_is_vt100(void){
  char *zNoColor;
#ifdef _WIN32
  if( !win32_terminal_is_vt100(1) ) return 0;
#endif /* _WIN32 */
  if( !fossil_isatty(1) ) return 0;
  zNoColor =fossil_getenv("NO_COLOR");
  if( zNoColor==0 ) return 1;
  if( zNoColor[0]==0 ) return 1;
  if( is_false(zNoColor) ) return 1;
  return 0;
}

#ifdef _WIN32
/*
** Return true if the Windows console supports VT100 escape codes.
**
** Support for VT100 escape codes is enabled by default in Windows Terminal
** on Windows 10 and Windows 11, and disabled by default in Legacy Consoles
** and on older versions of Windows. Programs can turn on VT100 support for
** Legacy Consoles using the ENABLE_VIRTUAL_TERMINAL_PROCESSING flag.
**
** NOTE: If this function needs to be called in more complex scenarios with
** reassigned stdout and stderr streams, the following CRT calls are useful
** to translate from CRT streams to file descriptors and to Win32 handles:
**
**    HANDLE hOutputHandle = (HANDLE)_get_osfhandle(_fileno(<FILE*>));
*/
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
int win32_terminal_is_vt100(int fd){
  HANDLE hConsole = NULL;
  DWORD dwConsoleMode;
  switch( fd ){
    case 1:
      hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
      break;
    case 2:
      hConsole = GetStdHandle(STD_ERROR_HANDLE);
      break;
  }
  if( GetConsoleMode(hConsole,&dwConsoleMode) ){
    return (dwConsoleMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)!=0;
  }
  return 0;
}
#endif /* _WIN32 */
Changes to src/th_main.c.
694
695
696
697
698
699
700





















701
702
703
704
705
706
707
    Blob src;
    blob_init(&src, (char*)argv[1], argl[1]);
    wiki_convert(&src, 0, flags);
    blob_reset(&src);
  }
  return TH_OK;
}






















/*
** TH1 command: htmlize STRING
**
** Escape all characters of STRING which have special meaning in HTML.
** Return a new string result.
*/







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







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
    Blob src;
    blob_init(&src, (char*)argv[1], argl[1]);
    wiki_convert(&src, 0, flags);
    blob_reset(&src);
  }
  return TH_OK;
}

/*
** TH1 command: wiki_assoc STRING STRING
**
** Render an associated wiki page.  The first string is the namespace
** (e.g. "checkin", "branch", "ticket"). The second is the ID of the
** associated object. See wiki_render_associated().
*/
static int wikiAssocCmd(
  Th_Interp *interp,
  void *p,
  int argc,
  const char **argv,
  int *argl
){
  if( argc!=3 ){
    return Th_WrongNumArgs(interp, "wiki_assoc STRING STRING");
  }
  wiki_render_associated((char*)argv[1], (char*)argv[2], WIKIASSOC_FULL_TITLE);
  return TH_OK;
}

/*
** TH1 command: htmlize STRING
**
** Escape all characters of STRING which have special meaning in HTML.
** Return a new string result.
*/
2372
2373
2374
2375
2376
2377
2378

2379
2380
2381
2382
2383
2384
2385
    {"trace",         traceCmd,             0},
    {"stime",         stimeCmd,             0},
    {"unversioned",   unversionedCmd,       0},
    {"utime",         utimeCmd,             0},
    {"verifyCsrf",    verifyCsrfCmd,        0},
    {"verifyLogin",   verifyLoginCmd,       0},
    {"wiki",          wikiCmd,              (void*)&aFlags[0]},

    {0, 0, 0}
  };
  if( g.thTrace ){
    Th_Trace("th1-init 0x%x => 0x%x<br>\n", g.th1Flags, flags);
  }
  if( needConfig ){
    /*







>







2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
    {"trace",         traceCmd,             0},
    {"stime",         stimeCmd,             0},
    {"unversioned",   unversionedCmd,       0},
    {"utime",         utimeCmd,             0},
    {"verifyCsrf",    verifyCsrfCmd,        0},
    {"verifyLogin",   verifyLoginCmd,       0},
    {"wiki",          wikiCmd,              (void*)&aFlags[0]},
    {"wiki_assoc",    wikiAssocCmd,         0},
    {0, 0, 0}
  };
  if( g.thTrace ){
    Th_Trace("th1-init 0x%x => 0x%x<br>\n", g.th1Flags, flags);
  }
  if( needConfig ){
    /*
Changes to src/timeline.c.
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
  int prevTagid = 0;
  int suppressCnt = 0;
  char zPrevDate[20];
  GraphContext *pGraph = 0;
  int prevWasDivider = 0;     /* True if previous output row was <hr> */
  int fchngQueryInit = 0;     /* True if fchngQuery is initialized */
  Stmt fchngQuery;            /* Query for file changes on check-ins */
  static Stmt qbranch;
  int pendingEndTr = 0;       /* True if a </td></tr> is needed */
  int vid = 0;                /* Current check-out version */
  int dateFormat = 0;         /* 0: HH:MM (default) */
  int bCommentGitStyle = 0;   /* Only show comments through first blank line */
  const char *zStyle;         /* Sub-name for classes for the style */
  const char *zDateFmt;
  int iTableId = timeline_tableid();
  int bTimestampLinksToInfo;  /* True if timestamp hyperlinks go to the /info
                              ** page rather than the /timeline page */

  if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
    vid = db_lget_int("checkout", 0);
  }
  zPrevDate[0] = 0;
  mxWikiLen = db_get_int("timeline-max-comment", 0);
  dateFormat = db_get_int("timeline-date-format", 0);








  bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);









  bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
  if( (tmFlags & TIMELINE_VIEWS)==0 ){
    tmFlags |= timeline_ss_cookie();
  }
  if( tmFlags & TIMELINE_COLUMNAR ){
    zStyle = "Columnar";
  }else if( tmFlags & TIMELINE_COMPACT ){
    zStyle = "Compact";
  }else if( tmFlags & TIMELINE_VERBOSE ){
    zStyle = "Verbose";
  }else if( tmFlags & TIMELINE_CLASSIC ){
    zStyle = "Classic";
  }else{
    zStyle = "Modern";
  }
  zDateFmt = P("datefmt");
  if( zDateFmt ) dateFormat = atoi(zDateFmt);
  if( tmFlags & TIMELINE_GRAPH ){
    pGraph = graph_init();
  }
  db_static_prepare(&qbranch,
    "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
    TAG_BRANCH
  );
  if( (tmFlags & TIMELINE_CHPICK)!=0
   && !db_table_exists("repository","cherrypick")
  ){
    tmFlags &= ~TIMELINE_CHPICK;
  }
  @ <table id="timelineTable%d(iTableId)" class="timelineTable"> \
  @ <!-- tmFlags: 0x%x(tmFlags) -->
  blob_zero(&comment);
  while( db_step(pQuery)==SQLITE_ROW ){
    int rid = db_column_int(pQuery, 0);
    const char *zUuid = db_column_text(pQuery, 1);
    int isLeaf = db_column_int(pQuery, 5);
    const char *zBgClr = db_column_text(pQuery, 6);
    const char *zDate = db_column_text(pQuery, 2);
    const char *zType = db_column_text(pQuery, 7);
    const char *zUser = db_column_text(pQuery, 4);
    const char *zTagList = db_column_text(pQuery, 8);
    int tagid = db_column_int(pQuery, 9);
    const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
    const char *zBr = 0;      /* Branch */
    int commentColumn = 3;    /* Column containing comment text */
    int modPending;           /* Pending moderation */
    char *zDateLink;          /* URL for the link on the timestamp */
    int drawDetailEllipsis;   /* True to show ellipsis in place of detail */
    int gidx = 0;             /* Graph row identifier */
    int isSelectedOrCurrent = 0;  /* True if current row is selected */
    const char *zExtraClass = "";







<
















>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>




















<
<
<
<



















|







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
  int prevTagid = 0;
  int suppressCnt = 0;
  char zPrevDate[20];
  GraphContext *pGraph = 0;
  int prevWasDivider = 0;     /* True if previous output row was <hr> */
  int fchngQueryInit = 0;     /* True if fchngQuery is initialized */
  Stmt fchngQuery;            /* Query for file changes on check-ins */

  int pendingEndTr = 0;       /* True if a </td></tr> is needed */
  int vid = 0;                /* Current check-out version */
  int dateFormat = 0;         /* 0: HH:MM (default) */
  int bCommentGitStyle = 0;   /* Only show comments through first blank line */
  const char *zStyle;         /* Sub-name for classes for the style */
  const char *zDateFmt;
  int iTableId = timeline_tableid();
  int bTimestampLinksToInfo;  /* True if timestamp hyperlinks go to the /info
                              ** page rather than the /timeline page */

  if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
    vid = db_lget_int("checkout", 0);
  }
  zPrevDate[0] = 0;
  mxWikiLen = db_get_int("timeline-max-comment", 0);
  dateFormat = db_get_int("timeline-date-format", 0);
  /*
  ** SETTING: timeline-truncate-at-blank  boolean default=off
  **
  ** If enabled, check-in comments displayed on the timeline are truncated
  ** at the first blank line of the comment text.  The comment text after
  ** the first blank line is only seen in the /info or similar pages that
  ** show details about the check-in.
  */
  bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
  /*
  ** SETTING: timeline-tslink-info       boolean default=off
  **
  ** The hyperlink on the timestamp associated with each timeline entry,
  ** on the far left-hand side of the screen, normally targets another
  ** /timeline page that shows the entry in context.  However, if this
  ** option is turned on, that hyperlink targets the /info page showing
  ** the details of the entry.
  */
  bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
  if( (tmFlags & TIMELINE_VIEWS)==0 ){
    tmFlags |= timeline_ss_cookie();
  }
  if( tmFlags & TIMELINE_COLUMNAR ){
    zStyle = "Columnar";
  }else if( tmFlags & TIMELINE_COMPACT ){
    zStyle = "Compact";
  }else if( tmFlags & TIMELINE_VERBOSE ){
    zStyle = "Verbose";
  }else if( tmFlags & TIMELINE_CLASSIC ){
    zStyle = "Classic";
  }else{
    zStyle = "Modern";
  }
  zDateFmt = P("datefmt");
  if( zDateFmt ) dateFormat = atoi(zDateFmt);
  if( tmFlags & TIMELINE_GRAPH ){
    pGraph = graph_init();
  }




  if( (tmFlags & TIMELINE_CHPICK)!=0
   && !db_table_exists("repository","cherrypick")
  ){
    tmFlags &= ~TIMELINE_CHPICK;
  }
  @ <table id="timelineTable%d(iTableId)" class="timelineTable"> \
  @ <!-- tmFlags: 0x%x(tmFlags) -->
  blob_zero(&comment);
  while( db_step(pQuery)==SQLITE_ROW ){
    int rid = db_column_int(pQuery, 0);
    const char *zUuid = db_column_text(pQuery, 1);
    int isLeaf = db_column_int(pQuery, 5);
    const char *zBgClr = db_column_text(pQuery, 6);
    const char *zDate = db_column_text(pQuery, 2);
    const char *zType = db_column_text(pQuery, 7);
    const char *zUser = db_column_text(pQuery, 4);
    const char *zTagList = db_column_text(pQuery, 8);
    int tagid = db_column_int(pQuery, 9);
    const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
    char *zBr = 0;            /* Branch */
    int commentColumn = 3;    /* Column containing comment text */
    int modPending;           /* Pending moderation */
    char *zDateLink;          /* URL for the link on the timestamp */
    int drawDetailEllipsis;   /* True to show ellipsis in place of detail */
    int gidx = 0;             /* Graph row identifier */
    int isSelectedOrCurrent = 0;  /* True if current row is selected */
    const char *zExtraClass = "";
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
      }
    }else{
      zDateLink = mprintf("<a>");
    }
    @ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td>
    @ <td class="timelineGraph">
    if( tmFlags & (TIMELINE_UCOLOR|TIMELINE_DELTA|TIMELINE_NOCOLOR) ){


      if( tmFlags & TIMELINE_UCOLOR ){
        zBgClr = zUser ? user_color(zUser) : 0;
      }else if( tmFlags & TIMELINE_NOCOLOR ){
        zBgClr = 0;
      }else if( zType[0]=='c' ){
        static Stmt qdelta;
        db_static_prepare(&qdelta, "SELECT baseid IS NULL FROM plink"
                                   " WHERE cid=:rid");
        db_bind_int(&qdelta, ":rid", rid);
        if( db_step(&qdelta)!=SQLITE_ROW ){
          zBgClr = 0; /* Not a check-in */
        }else if( db_column_int(&qdelta, 0) ){
          zBgClr = hash_color("b");  /* baseline manifest */
        }else{
          zBgClr = hash_color("f");  /* delta manifest */
        }
        db_reset(&qdelta);
      }



    }
    if( zType[0]=='c'
    && (pGraph || zBgClr==0 || (tmFlags & (TIMELINE_BRCOLOR|TIMELINE_DELTA))!=0)
    ){
      db_reset(&qbranch);
      db_bind_int(&qbranch, ":rid", rid);
      if( db_step(&qbranch)==SQLITE_ROW ){
        zBr = db_column_text(&qbranch, 0);
      }else{
        zBr = "trunk";
      }
      if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){


        if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
        }else if( zBr==0 || strcmp(zBr,"trunk")==0 ){
          zBgClr = 0;
        }else{
          zBgClr = hash_color(zBr);
        }
      }







>
>


















>
>
>




<
<
<
<
<
|
<

>
>







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
      }
    }else{
      zDateLink = mprintf("<a>");
    }
    @ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td>
    @ <td class="timelineGraph">
    if( tmFlags & (TIMELINE_UCOLOR|TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
      /* Don't use the requested background color.  Use the background color
      ** override from query parameters instead. */
      if( tmFlags & TIMELINE_UCOLOR ){
        zBgClr = zUser ? user_color(zUser) : 0;
      }else if( tmFlags & TIMELINE_NOCOLOR ){
        zBgClr = 0;
      }else if( zType[0]=='c' ){
        static Stmt qdelta;
        db_static_prepare(&qdelta, "SELECT baseid IS NULL FROM plink"
                                   " WHERE cid=:rid");
        db_bind_int(&qdelta, ":rid", rid);
        if( db_step(&qdelta)!=SQLITE_ROW ){
          zBgClr = 0; /* Not a check-in */
        }else if( db_column_int(&qdelta, 0) ){
          zBgClr = hash_color("b");  /* baseline manifest */
        }else{
          zBgClr = hash_color("f");  /* delta manifest */
        }
        db_reset(&qdelta);
      }
    }else{
      /* Make sure the user-specified background color is reasonable */
      zBgClr = reasonable_bg_color(zBgClr, 0);
    }
    if( zType[0]=='c'
    && (pGraph || zBgClr==0 || (tmFlags & (TIMELINE_BRCOLOR|TIMELINE_DELTA))!=0)
    ){





      zBr = branch_of_rid(rid);

      if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){
        /* If no background color is specified, use a color based on the
        ** branch name */
        if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
        }else if( zBr==0 || strcmp(zBr,"trunk")==0 ){
          zBgClr = 0;
        }else{
          zBgClr = hash_color(zBr);
        }
      }
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472

473
474
475
476
477
478
479
          nCherrypick++;
        }
        db_reset(&qcherrypick);
      }
      gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent,
                           zBr, zBgClr, zUuid,
                           isLeaf ? isLeaf + 2 * has_closed_tag(rid) : 0);
      db_reset(&qbranch);
      @ <div id="m%d(gidx)" class="tl-nodemark"></div>
    }else if( zType[0]=='e' && pGraph && zBgClr && zBgClr[0] ){
      /* For technotes, make a graph node with nParent==(-1).  This will
      ** not actually draw anything on the graph, but it will set the
      ** background color of the timeline entry */
      gidx = graph_add_row(pGraph, rid, -1, 0, 0, zBr, zBgClr, zUuid, 0);
      @ <div id="m%d(gidx)" class="tl-nodemark"></div>
    }

    @</td>
    if( !isSelectedOrCurrent ){
      @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'>
    }else{
      @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)">
    }
    if( pGraph ){







<








>







470
471
472
473
474
475
476

477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
          nCherrypick++;
        }
        db_reset(&qcherrypick);
      }
      gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent,
                           zBr, zBgClr, zUuid,
                           isLeaf ? isLeaf + 2 * has_closed_tag(rid) : 0);

      @ <div id="m%d(gidx)" class="tl-nodemark"></div>
    }else if( zType[0]=='e' && pGraph && zBgClr && zBgClr[0] ){
      /* For technotes, make a graph node with nParent==(-1).  This will
      ** not actually draw anything on the graph, but it will set the
      ** background color of the timeline entry */
      gidx = graph_add_row(pGraph, rid, -1, 0, 0, zBr, zBgClr, zUuid, 0);
      @ <div id="m%d(gidx)" class="tl-nodemark"></div>
    }
    fossil_free(zBr);
    @</td>
    if( !isSelectedOrCurrent ){
      @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'>
    }else{
      @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)">
    }
    if( pGraph ){
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
    @   event.mtime AS mtime
    @  FROM event CROSS JOIN blob
    @ WHERE blob.rid=event.objid
  ;
  return zBase;
}

/*
** Convert a symbolic name used as an argument to the a=, b=, or c=
** query parameters of timeline into a julianday mtime value.
*/
double symbolic_name_to_mtime(const char *z, const char **pzDisplay){
  double mtime;
  int rid;
  const char *zDate;
  if( z==0 ) return -1.0;
  if( fossil_isdate(z) ){
    mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z);
    if( mtime>0.0 ) return mtime;
  }
  zDate = fossil_expand_datetime(z, 1);
  if( zDate!=0 ){
    mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())",
                      fossil_roundup_date(zDate));
    if( mtime>0.0 ){
      if( pzDisplay ) *pzDisplay = fossil_strdup(zDate);
      return mtime;
    }
  }
  rid = symbolic_name_to_rid(z, "*");
  if( rid ){
    mtime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
  }else{
    mtime = db_double(-1.0,
        "SELECT max(event.mtime) FROM event, tag, tagxref"
        " WHERE tag.tagname GLOB 'event-%q*'"
        "   AND tagxref.tagid=tag.tagid AND tagxref.tagtype"
        "   AND event.objid=tagxref.rid",
        z
    );
  }
  return mtime;
}

/*
** zDate is a localtime date.  Insert records into the
** "timeline" table to cause <hr> to be inserted on zDate.
*/
static int timeline_add_divider(double rDate){
  int rid = db_int(-1,
    "SELECT rid FROM timeline ORDER BY abs(sortby-%.16g) LIMIT 1", rDate







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







1111
1112
1113
1114
1115
1116
1117





































1118
1119
1120
1121
1122
1123
1124
    @   event.mtime AS mtime
    @  FROM event CROSS JOIN blob
    @ WHERE blob.rid=event.objid
  ;
  return zBase;
}






































/*
** zDate is a localtime date.  Insert records into the
** "timeline" table to cause <hr> to be inserted on zDate.
*/
static int timeline_add_divider(double rDate){
  int rid = db_int(-1,
    "SELECT rid FROM timeline ORDER BY abs(sortby-%.16g) LIMIT 1", rDate
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
** first check-in.  If there are no check-ins in the decendent
** or ancestor set of check-in iFrom that match the tag, then
** return 0.
*/
static int timeline_endpoint(
  int iFrom,         /* Starting point */
  const char *zEnd,  /* Tag we are searching for */   
  int bForward       /* 1: forwards in time (descendents) 0: backwards */
){
  int tagId;
  int endId = 0;
  Stmt q;
  int ans = 0;

  tagId = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zEnd);







|







1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
** first check-in.  If there are no check-ins in the decendent
** or ancestor set of check-in iFrom that match the tag, then
** return 0.
*/
static int timeline_endpoint(
  int iFrom,         /* Starting point */
  const char *zEnd,  /* Tag we are searching for */   
  int bForward       /* 1: forwards in time (descendants) 0: backwards */
){
  int tagId;
  int endId = 0;
  Stmt q;
  int ans = 0;

  tagId = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zEnd);
1417
1418
1419
1420
1421
1422
1423
1424

1425
1426
1427
1428
1429
1430
1431
        "    FROM dx, plink"
        "   WHERE plink.pid=dx.id"
        "     AND plink.mtime<=(SELECT max(event.mtime) FROM tagxref, event"
                               " WHERE tagxref.tagid=%d AND tagxref.tagtype>0"
                               " AND event.objid=tagxref.rid)"
        "   ORDER BY plink.mtime)"
        "SELECT id FROM dx, tagxref"
        " WHERE tagid=%d AND tagtype>0 AND rid=id LIMIT 1",

        iFrom, iFrom, tagId, tagId
      );
    }else{
      db_prepare(&q,
        "WITH RECURSIVE dx(id,mtime) AS ("
        "  SELECT %d, event.mtime FROM event WHERE objid=%d"
        "  UNION"







|
>







1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
        "    FROM dx, plink"
        "   WHERE plink.pid=dx.id"
        "     AND plink.mtime<=(SELECT max(event.mtime) FROM tagxref, event"
                               " WHERE tagxref.tagid=%d AND tagxref.tagtype>0"
                               " AND event.objid=tagxref.rid)"
        "   ORDER BY plink.mtime)"
        "SELECT id FROM dx, tagxref"
        " WHERE tagid=%d AND tagtype>0 AND rid=id"
        " ORDER BY dx.mtime LIMIT 1",
        iFrom, iFrom, tagId, tagId
      );
    }else{
      db_prepare(&q,
        "WITH RECURSIVE dx(id,mtime) AS ("
        "  SELECT %d, event.mtime FROM event WHERE objid=%d"
        "  UNION"
1448
1449
1450
1451
1452
1453
1454
1455

1456
1457
1458
1459
1460
1461
1462
        "    FROM dx, plink, event"
        "   WHERE plink.cid=dx.id AND event.objid=plink.pid"
        "     AND event.mtime>=(SELECT min(event.mtime) FROM tagxref, event"
                               " WHERE tagxref.tagid=%d AND tagxref.tagtype>0"
                               " AND event.objid=tagxref.rid)"
        "   ORDER BY event.mtime DESC)"
        "SELECT id FROM dx, tagxref"
        " WHERE tagid=%d AND tagtype>0 AND rid=id LIMIT 1",

        iFrom, iFrom, tagId, tagId
      );
    }else{
      db_prepare(&q,
        "WITH RECURSIVE dx(id,mtime) AS ("
        "  SELECT %d, event.mtime FROM event WHERE objid=%d"
        "  UNION"







|
>







1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
        "    FROM dx, plink, event"
        "   WHERE plink.cid=dx.id AND event.objid=plink.pid"
        "     AND event.mtime>=(SELECT min(event.mtime) FROM tagxref, event"
                               " WHERE tagxref.tagid=%d AND tagxref.tagtype>0"
                               " AND event.objid=tagxref.rid)"
        "   ORDER BY event.mtime DESC)"
        "SELECT id FROM dx, tagxref"
        " WHERE tagid=%d AND tagtype>0 AND rid=id"
        " ORDER BY dx.mtime DESC LIMIT 1",
        iFrom, iFrom, tagId, tagId
      );
    }else{
      db_prepare(&q,
        "WITH RECURSIVE dx(id,mtime) AS ("
        "  SELECT %d, event.mtime FROM event WHERE objid=%d"
        "  UNION"
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
}

/*
** COMMAND: test-endpoint
**
** Usage: fossil test-endpoint BASE TAG ?OPTIONS?
**
** Show the first check-in with TAG that is a descendent or ancestor
** of BASE.  The first descendent checkin is shown by default.  Use
** the --backto to see the first ancestor checkin.
**
** Options:
**
**      --backto            Show ancestor.  Others defaults to descendents.
*/
void timeline_test_endpoint(void){
  int bForward = find_option("backto",0,0)==0;
  int from_rid;
  int ans;
  db_find_and_open_repository(0, 0);
  verify_all_options();







|
|




|







1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
}

/*
** COMMAND: test-endpoint
**
** Usage: fossil test-endpoint BASE TAG ?OPTIONS?
**
** Show the first check-in with TAG that is a descendant or ancestor
** of BASE.  The first descendant checkin is shown by default.  Use
** the --backto to see the first ancestor checkin.
**
** Options:
**
**      --backto            Show ancestor.  Others defaults to descendants.
*/
void timeline_test_endpoint(void){
  int bForward = find_option("backto",0,0)==0;
  int from_rid;
  int ans;
  db_find_and_open_repository(0, 0);
  verify_all_options();
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
**                       d2=CKIN2        ... Use CKIN2 if CHECKIN is not found
**                       ft=DESCENDANT   ... going forward to DESCENDANT
**    dp=CHECKIN      Same as 'd=CHECKIN&p=CHECKIN'
**    dp2=CKIN2       Same as 'd2=CKIN2&p2=CKIN2'
**    df=CHECKIN      Same as 'd=CHECKIN&n1=all&nd'.  Mnemonic: "Derived From"
**    bt=CHECKIN      "Back To".  Show ancenstors going back to CHECKIN
**                       p=CX       ... from CX back to time of CHECKIN
**                       from=CX    ... shortest path from CX back to CHECKIN
**    ft=CHECKIN      "Forward To":  Show decendents forward to CHECKIN
**                       d=CX       ... from CX up to the time of CHECKIN
**                       from=CX    ... shortest path from CX up to CHECKIN
**    t=TAG           Show only check-ins with the given TAG
**    r=TAG           Same as 't=TAG&rel'.  Mnemonic: "Related"
**    tl=TAGLIST      Same as 't=TAGLIST&ms=brlist'.  Mnemonic: "Tag List"
**    rl=TAGLIST      Same as 'r=TAGLIST&ms=brlist'.  Mnemonic: "Related List"
**    ml=TAGLIST      Same as 'tl=TAGLIST&mionly'.  Mnemonic: "Merge-in List"
**    sl=TAGLIST      "Sort List". Draw TAGLIST branches ordered left to right.
**    rel             Show related check-ins as well as those matching t=TAG







|


|







1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
**                       d2=CKIN2        ... Use CKIN2 if CHECKIN is not found
**                       ft=DESCENDANT   ... going forward to DESCENDANT
**    dp=CHECKIN      Same as 'd=CHECKIN&p=CHECKIN'
**    dp2=CKIN2       Same as 'd2=CKIN2&p2=CKIN2'
**    df=CHECKIN      Same as 'd=CHECKIN&n1=all&nd'.  Mnemonic: "Derived From"
**    bt=CHECKIN      "Back To".  Show ancenstors going back to CHECKIN
**                       p=CX       ... from CX back to time of CHECKIN
**                       from=CX    ... path from CX back to CHECKIN
**    ft=CHECKIN      "Forward To":  Show decendents forward to CHECKIN
**                       d=CX       ... from CX up to the time of CHECKIN
**                       from=CX    ... path from CX up to CHECKIN
**    t=TAG           Show only check-ins with the given TAG
**    r=TAG           Same as 't=TAG&rel'.  Mnemonic: "Related"
**    tl=TAGLIST      Same as 't=TAGLIST&ms=brlist'.  Mnemonic: "Tag List"
**    rl=TAGLIST      Same as 'r=TAGLIST&ms=brlist'.  Mnemonic: "Related List"
**    ml=TAGLIST      Same as 'tl=TAGLIST&mionly'.  Mnemonic: "Merge-in List"
**    sl=TAGLIST      "Sort List". Draw TAGLIST branches ordered left to right.
**    rel             Show related check-ins as well as those matching t=TAG
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620

1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
**    ncp             Omit cherrypick merges
**    nd              Do not highlight the focus check-in
**    nsm             Omit the submenu
**    nc              Omit all graph colors other than highlights
**    v               Show details of files changed
**    vfx             Show complete text of forum messages
**    f=CHECKIN       Family (immediate parents and children) of CHECKIN
**    from=CHECKIN    Path through common ancestor from...
**                       to=CHECKIN      ... to this
**                       to2=CHECKIN     ... backup name if to= doesn't resolve
**                       shortest        ... show only the shortest path
**                       rel             ... also show related checkins

**                       bt=PRIOR        ... path from CHECKIN back to PRIOR
**                       ft=LATER        ... path from CHECKIN forward to LATER
**    me=CHECKIN      Most direct path from...
**                       you=CHECKIN     ... to this
**                       rel             ... also show related checkins
**    uf=FILE_HASH    Show only check-ins that contain the given file version
**                    All qualifying check-ins are shown unless there is
**                    also an n= or n1= query parameter.
**    chng=GLOBLIST   Show only check-ins that involve changes to a file whose
**                    name matches one of the comma-separate GLOBLIST
**    brbg            Background color determined by branch name
**    ubg             Background color determined by user







|
|
|
|
|
>
|
|
|
|
|







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
**    ncp             Omit cherrypick merges
**    nd              Do not highlight the focus check-in
**    nsm             Omit the submenu
**    nc              Omit all graph colors other than highlights
**    v               Show details of files changed
**    vfx             Show complete text of forum messages
**    f=CHECKIN       Family (immediate parents and children) of CHECKIN
**    from=CHECKIN    Path through common ancestor from CHECKIN...
**                       to=CHECKIN   ... to this
**                       to2=CHECKIN  ... backup name if to= doesn't resolve
**                       shortest     ... pick path with least number of nodes
**                       rel          ... also show related checkins
**                       min          ... hide long sequences along same branch
**                       bt=PRIOR     ... path from CHECKIN back to PRIOR
**                       ft=LATER     ... path from CHECKIN forward to LATER
**    me=CHECKIN      Most direct path from CHECKIN...
**                       you=CHECKIN  ... to this
**                       rel          ... also show related checkins
**    uf=FILE_HASH    Show only check-ins that contain the given file version
**                    All qualifying check-ins are shown unless there is
**                    also an n= or n1= query parameter.
**    chng=GLOBLIST   Show only check-ins that involve changes to a file whose
**                    name matches one of the comma-separate GLOBLIST
**    brbg            Background color determined by branch name
**    ubg             Background color determined by user
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732

1733
1734
1735
1736
1737
1738
1739
  int tmFlags = 0;                   /* Timeline flags */
  const char *zThisTag = 0;          /* Suppress links to this tag */
  const char *zThisUser = 0;         /* Suppress links to this user */
  HQuery url;                        /* URL for various branch links */
  int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
  const char *zTo2 = 0;
  int to_rid = name_choice("to","to2",&zTo2);    /* to= for path timelines */
  int noMerge = P("shortest")==0;           /* Follow merge links if shorter */
  int me_rid = name_to_typed_rid(P("me"),"ci");  /* me= for common ancestory */
  int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
  int pd_rid;
  const char *zDPName;                /* Value of p=, d=, or dp= params */
  double rBefore, rAfter, rCirca;     /* Boundary times */
  const char *z;
  char *zOlderButton = 0;             /* URL for Older button at the bottom */
  char *zOlderButtonLabel = 0;        /* Label for the Older Button */
  char *zNewerButton = 0;             /* URL for Newer button at the top */
  char *zNewerButtonLabel = 0;        /* Label for the Newer button */
  int selectedRid = 0;                /* Show a highlight on this RID */
  int secondaryRid = 0;               /* Show secondary highlight */
  int disableY = 0;                   /* Disable type selector on submenu */
  int advancedMenu = 0;               /* Use the advanced menu design */
  char *zPlural;                      /* Ending for plural forms */
  int showCherrypicks = 1;            /* True to show cherrypick merges */
  int haveParameterN;                 /* True if n= query parameter present */
  int from_to_mode = 0;               /* 0: from,to. 1: from,ft 2: from,bt */
  int showSql = PB("showsql");        /* True to show the SQL */
  Blob allSql;                        /* Copy of all SQL text */


  login_check_credentials();
  url_initialize(&url, "timeline");
  cgi_query_parameters_to_url(&url);
  blob_init(&allSql, 0, 0);

  /* The "mionly" query parameter is like "rel", but shows merge-ins only */







|



|
















>







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
1712
1713
1714
1715
1716
1717
1718
1719
  int tmFlags = 0;                   /* Timeline flags */
  const char *zThisTag = 0;          /* Suppress links to this tag */
  const char *zThisUser = 0;         /* Suppress links to this user */
  HQuery url;                        /* URL for various branch links */
  int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
  const char *zTo2 = 0;
  int to_rid = name_choice("to","to2",&zTo2);    /* to= for path timelines */
  int bShort = P("shortest")!=0;                 /* shortest possible path */
  int me_rid = name_to_typed_rid(P("me"),"ci");  /* me= for common ancestory */
  int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
  int pd_rid;
  const char *zDPNameP, *zDPNameD;    /* Value of p=, d=, or dp= params */
  double rBefore, rAfter, rCirca;     /* Boundary times */
  const char *z;
  char *zOlderButton = 0;             /* URL for Older button at the bottom */
  char *zOlderButtonLabel = 0;        /* Label for the Older Button */
  char *zNewerButton = 0;             /* URL for Newer button at the top */
  char *zNewerButtonLabel = 0;        /* Label for the Newer button */
  int selectedRid = 0;                /* Show a highlight on this RID */
  int secondaryRid = 0;               /* Show secondary highlight */
  int disableY = 0;                   /* Disable type selector on submenu */
  int advancedMenu = 0;               /* Use the advanced menu design */
  char *zPlural;                      /* Ending for plural forms */
  int showCherrypicks = 1;            /* True to show cherrypick merges */
  int haveParameterN;                 /* True if n= query parameter present */
  int from_to_mode = 0;               /* 0: from,to. 1: from,ft 2: from,bt */
  int showSql = PB("showsql");        /* True to show the SQL */
  Blob allSql;                        /* Copy of all SQL text */
  int bMin = P("min")!=0;             /* True if "min" query parameter used */

  login_check_credentials();
  url_initialize(&url, "timeline");
  cgi_query_parameters_to_url(&url);
  blob_init(&allSql, 0, 0);

  /* The "mionly" query parameter is like "rel", but shows merge-ins only */
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
      }
    }
  }else{
    nEntry = 50;
  }

  /* Query parameters d=, p=, and f= and variants */
  p_rid = name_choice("p","p2", &zDPName);
  d_rid = name_choice("d","d2", &zDPName);
  z = P("f");
  f_rid = z ? name_to_typed_rid(z,"ci") : 0;
  z = P("df");
  if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){
    nEntry = 0;
    useDividers = 0;
    cgi_replace_query_parameter("d",fossil_strdup(z));
    zDPName = z;
  }

  /* Undocumented query parameter to set JS mode */
  builtin_set_js_delivery_mode(P("jsmode"),1);

  secondaryRid = name_to_typed_rid(P("sel2"),"ci");
  selectedRid = name_to_typed_rid(P("sel1"),"ci");







|
|







|







1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
      }
    }
  }else{
    nEntry = 50;
  }

  /* Query parameters d=, p=, and f= and variants */
  p_rid = name_choice("p","p2", &zDPNameP);
  d_rid = name_choice("d","d2", &zDPNameD);
  z = P("f");
  f_rid = z ? name_to_typed_rid(z,"ci") : 0;
  z = P("df");
  if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){
    nEntry = 0;
    useDividers = 0;
    cgi_replace_query_parameter("d",fossil_strdup(z));
    zDPNameD = zDPNameP = z;
  }

  /* Undocumented query parameter to set JS mode */
  builtin_set_js_delivery_mode(P("jsmode"),1);

  secondaryRid = name_to_typed_rid(P("sel2"),"ci");
  selectedRid = name_to_typed_rid(P("sel1"),"ci");
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817

1818
1819
1820
1821
1822
1823
1824
  ** present or if this repository lacks a "cherrypick" table. */
  if( PB("ncp") || !db_table_exists("repository","cherrypick") ){
    showCherrypicks = 0;
  }

  /* To view the timeline, must have permission to read project data.
  */
  pd_rid = name_choice("dp","dp2",&zDPName);
  if( pd_rid ){
    p_rid = d_rid = pd_rid;

  }
  if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
   || (bisectLocal && !g.perm.Setup)
  ){
    login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
    return;
  }







|


>







1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
  ** present or if this repository lacks a "cherrypick" table. */
  if( PB("ncp") || !db_table_exists("repository","cherrypick") ){
    showCherrypicks = 0;
  }

  /* To view the timeline, must have permission to read project data.
  */
  pd_rid = name_choice("dp","dp2",&zDPNameP);
  if( pd_rid ){
    p_rid = d_rid = pd_rid;
    zDPNameD = zDPNameP;
  }
  if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
   || (bisectLocal && !g.perm.Setup)
  ){
    login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
    return;
  }
2081
2082
2083
2084
2085
2086
2087


2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
    PathNode *p = 0;
    const char *zFrom = 0;
    const char *zTo = 0;
    Blob ins;
    int nNodeOnPath = 0;
    int commonAncs = 0;    /* Common ancestors of me_rid and you_rid. */
    int earlierRid = 0, laterRid = 0;



    if( from_rid && to_rid ){
      if( from_to_mode==0 ){
        p = path_shortest(from_rid, to_rid, noMerge, 0, 0);
      }else if( from_to_mode==1 ){
        p = path_shortest(from_rid, to_rid, 0, 1, 0);
        earlierRid = commonAncs = from_rid;
        laterRid = to_rid;
      }else{
        p = path_shortest(to_rid, from_rid, 0, 1, 0);
        earlierRid = commonAncs = to_rid;
        laterRid = from_rid;
      }
      zFrom = P("from");
      zTo = zTo2 ? zTo2 : P("to");
    }else{
      commonAncs = path_common_ancestor(me_rid, you_rid);







>
>



|

|



|







2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
    PathNode *p = 0;
    const char *zFrom = 0;
    const char *zTo = 0;
    Blob ins;
    int nNodeOnPath = 0;
    int commonAncs = 0;    /* Common ancestors of me_rid and you_rid. */
    int earlierRid = 0, laterRid = 0;
    int cost = bShort ? 0 : 1;
    int nSkip = 0;

    if( from_rid && to_rid ){
      if( from_to_mode==0 ){
        p = path_shortest(from_rid, to_rid, 0, 0, 0, cost);
      }else if( from_to_mode==1 ){
        p = path_shortest(from_rid, to_rid, 0, 1, 0, cost);
        earlierRid = commonAncs = from_rid;
        laterRid = to_rid;
      }else{
        p = path_shortest(to_rid, from_rid, 0, 1, 0, cost);
        earlierRid = commonAncs = to_rid;
        laterRid = from_rid;
      }
      zFrom = P("from");
      zTo = zTo2 ? zTo2 : P("to");
    }else{
      commonAncs = path_common_ancestor(me_rid, you_rid);
2121
2122
2123
2124
2125
2126
2127



2128


2129

2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
    db_multi_exec(
      "CREATE TEMP TABLE IF NOT EXISTS pathnode(x INTEGER PRIMARY KEY);"
    );
    if( p ){
      int cnt = 4;
      blob_init(&ins, 0, 0);
      blob_append_sql(&ins, "INSERT INTO pathnode(x) VALUES(%d)", p->rid);



      p = p->u.pTo;


      while( p ){

        if( cnt==8 ){
          blob_append_sql(&ins, ",\n  (%d)", p->rid);
          cnt = 0;
        }else{
          cnt++;
          blob_append_sql(&ins, ",(%d)", p->rid);
        }
        p = p->u.pTo;
      }
    }
    path_reset();
    db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/);
    blob_reset(&ins);
    if( related ){
      db_multi_exec(







>
>
>
|
>
>
|
>
|






<







2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125

2126
2127
2128
2129
2130
2131
2132
    db_multi_exec(
      "CREATE TEMP TABLE IF NOT EXISTS pathnode(x INTEGER PRIMARY KEY);"
    );
    if( p ){
      int cnt = 4;
      blob_init(&ins, 0, 0);
      blob_append_sql(&ins, "INSERT INTO pathnode(x) VALUES(%d)", p->rid);
      if( p->u.pTo==0 ) bMin = 0;
      for(p=p->u.pTo; p; p=p->u.pTo){
        if( bMin
         && p->u.pTo!=0
         && fossil_strcmp(path_branch(p->pFrom),path_branch(p))==0
         && fossil_strcmp(path_branch(p),path_branch(p->u.pTo))==0
        ){
          nSkip++;
        }else if( cnt==8 ){
          blob_append_sql(&ins, ",\n  (%d)", p->rid);
          cnt = 0;
        }else{
          cnt++;
          blob_append_sql(&ins, ",(%d)", p->rid);
        }

      }
    }
    path_reset();
    db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/);
    blob_reset(&ins);
    if( related ){
      db_multi_exec(
2194
2195
2196
2197
2198
2199
2200
2201
2202

2203
2204
2205
2206
2207
2208
2209
    db_multi_exec("%s", blob_sql_text(&sql));
    if( advancedMenu ){
      style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
    }
    nNodeOnPath = db_int(0, "SELECT count(*) FROM temp.pathnode");
    if( nNodeOnPath==1 && from_to_mode>0 ){
      blob_appendf(&desc,"Check-in ");
    }else if( from_to_mode>0 ){
      blob_appendf(&desc, "%d check-ins on the shorted path from ",nNodeOnPath);

    }else{
      blob_appendf(&desc, "%d check-ins going from ", nNodeOnPath);
    }
    if( from_rid==selectedRid ){
      blob_appendf(&desc, "<span class='timelineSelected'>");
    }
    blob_appendf(&desc, "%z%h</a>", href("%R/info/%h", zFrom), zFrom);







|
|
>







2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
    db_multi_exec("%s", blob_sql_text(&sql));
    if( advancedMenu ){
      style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
    }
    nNodeOnPath = db_int(0, "SELECT count(*) FROM temp.pathnode");
    if( nNodeOnPath==1 && from_to_mode>0 ){
      blob_appendf(&desc,"Check-in ");
    }else if( bMin ){
      blob_appendf(&desc, "%d of %d check-ins along the path from ",
                   nNodeOnPath, nNodeOnPath+nSkip);
    }else{
      blob_appendf(&desc, "%d check-ins going from ", nNodeOnPath);
    }
    if( from_rid==selectedRid ){
      blob_appendf(&desc, "<span class='timelineSelected'>");
    }
    blob_appendf(&desc, "%z%h</a>", href("%R/info/%h", zFrom), zFrom);
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237



2238
2239
2240





2241
2242
2243
2244
2245
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
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284










2285
2286
2287
2288
2289
2290
2291

2292
2293
2294







2295
2296
2297
2298
2299
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

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
          blob_appendf(&desc, " and %d related check-in%s", nRelated,
                       nRelated>1 ? "s" : "");
        }
      }
    }
    addFileGlobDescription(zChng, &desc);
  }else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){
    /* If p= or d= is present, ignore all other parameters other than n= */
    char *zUuid;
    const char *zCiName;
    int np = 0, nd;
    const char *zBackTo = 0;
    const char *zFwdTo = 0;
    int ridBackTo = 0;
    int ridFwdTo = 0;




    tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
    if( p_rid && d_rid ){





      if( p_rid!=d_rid ) p_rid = d_rid;
      if( !haveParameterN ) nEntry = 10;
    }
    db_multi_exec(
       "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
    );
    add_extra_rids("ok", P("x"));
    zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
                         p_rid ? p_rid : d_rid);
    zCiName = zDPName;
    if( zCiName==0 ) zCiName = zUuid;
    blob_append_sql(&sql, " AND event.objid IN ok");
    nd = 0;
    if( d_rid ){
      double rStopTime = 9e99;
      zFwdTo = P("ft");







      if( zFwdTo ){
        double rStartDate = db_double(0.0,
           "SELECT mtime FROM event WHERE objid=%d", d_rid);
        ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
        if( ridFwdTo==0 ){
          ridFwdTo = name_to_typed_rid(zBackTo,"ci");
        }
        if( ridFwdTo ){
          if( !haveParameterN ) nEntry = 0;
          rStopTime = db_double(9e99,
            "SELECT mtime FROM event WHERE objid=%d", ridFwdTo);
        }



      }
      if( rStopTime<9e99 ){
        rStopTime += 5.8e-6;  /* Round up by 1/2 second */
      }
      db_multi_exec(
        "WITH RECURSIVE dx(rid,mtime) AS (\n"
        "  SELECT %d, 0\n"
        "  UNION\n"
        "  SELECT plink.cid, plink.mtime FROM dx, plink\n"
        "   WHERE plink.pid=dx.rid\n"
        "     AND plink.mtime<=%.*g\n"
        "   ORDER BY 2\n"
        ")\n"
        "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
        d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1
      );










      nd = db_int(0, "SELECT count(*)-1 FROM ok");
      if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
      if( nd>0 || p_rid==0 ){
        blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
      }
      if( useDividers && !selectedRid ) selectedRid = d_rid;
      db_multi_exec("DELETE FROM ok");

    }
    if( p_rid ){
      zBackTo = P("bt");







      if( zBackTo ){
        double rDateLimit = db_double(0.0,
           "SELECT mtime FROM event WHERE objid=%d", p_rid);
        ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
        if( ridBackTo==0 ){
          ridBackTo = name_to_typed_rid(zBackTo,"ci");
        }
        if( ridBackTo && !haveParameterN ) nEntry = 0;



      }
      compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);








      np = db_int(0, "SELECT count(*)-1 FROM ok");
      if( np>0 || nd==0 ){
        if( nd>0 ) blob_appendf(&desc, " and ");
        blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
        db_multi_exec("%s", blob_sql_text(&sql));
      }
      if( useDividers && !selectedRid ) selectedRid = p_rid;
    }














    blob_appendf(&desc, " of %z%h</a>",
                   href("%R/info?name=%h", zCiName), zCiName);

    if( ridBackTo ){
      if( np==0 ){
        blob_reset(&desc);
        blob_appendf(&desc,
                    "Check-in %z%h</a> only (%z%h</a> is not an ancestor)",
                     href("%R/info?name=%h",zCiName), zCiName,
                     href("%R/info?name=%h",zBackTo), zBackTo);
      }else{
        blob_appendf(&desc, " back to %z%h</a>",
                     href("%R/info?name=%h",zBackTo), zBackTo);

        if( ridFwdTo && zFwdTo ){
          blob_appendf(&desc, " and up to %z%h</a>",
                     href("%R/info?name=%h",zFwdTo), zFwdTo);

        }
      }
    }else if( ridFwdTo ){
      if( nd==0 ){
        blob_reset(&desc);
        blob_appendf(&desc,
                    "Check-in %z%h</a> only (%z%h</a> is not an descendant)",
                     href("%R/info?name=%h",zCiName), zCiName,
                     href("%R/info?name=%h",zFwdTo), zFwdTo);
      }else{
        blob_appendf(&desc, " up to %z%h</a>",
                     href("%R/info?name=%h",zFwdTo), zFwdTo);
      }
    }
    if( d_rid ){
      if( p_rid ){
        /* If both p= and d= are set, we don't have the uuid of d yet. */
        zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid);

      }
    }
    if( advancedMenu ){
      style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
    }
    style_submenu_entry("n","Max:",4,0);
    timeline_y_submenu(1);







|
|
|





>
>
>


|
>
>
>
>
>
|
<


|


<
<
<
<





>
>
>
>
>
>
>

|
<






|
<

>
>
>
















>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
|
|
>



>
>
>
>
>
>
>

|
<





>
>
>


>
>
>
>
>
>
>
>
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
>




|
|


|
|
>

|
|
>






|
|


|
|
<
<
<
<
<
<
>







2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238

2239
2240
2241
2242
2243




2244
2245
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
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
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
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
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384






2385
2386
2387
2388
2389
2390
2391
2392
          blob_appendf(&desc, " and %d related check-in%s", nRelated,
                       nRelated>1 ? "s" : "");
        }
      }
    }
    addFileGlobDescription(zChng, &desc);
  }else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){
    /* If either p= or d= or both are present, ignore all other parameters
    ** other than n=, ft=, and bt= */
    const char *zBaseName = 0;
    int np = 0, nd;
    const char *zBackTo = 0;
    const char *zFwdTo = 0;
    int ridBackTo = 0;
    int ridFwdTo = 0;
    int bBackAdded = 0;       /* True if the zBackTo node was added */
    int bFwdAdded = 0;        /* True if the zBackTo node was added */
    int bSeparateDandP = 0;   /* p_rid & d_rid both exist and are distinct */

    tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
    if( p_rid && d_rid && p_rid!=d_rid ){
      bSeparateDandP = 1;
      db_multi_exec(
        "CREATE TEMP TABLE IF NOT EXISTS ok_d(rid INTEGER PRIMARY KEY)"
      );
    }else{
      zBaseName = p_rid ? zDPNameP : zDPNameD;

    }
    db_multi_exec(
      "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
    );
    add_extra_rids("ok", P("x"));




    blob_append_sql(&sql, " AND event.objid IN ok");
    nd = 0;
    if( d_rid ){
      double rStopTime = 9e99;
      zFwdTo = P("ft");
      if( zFwdTo && bSeparateDandP ){
        if( zError==0 ){
          zError = "Cannot use the ft= query parameter when both p= and d= "
                   "are used and have distinct values.";
        }
        zFwdTo = 0;
      }        
      if( zFwdTo ){
        double rStartDate = mtime_of_rid(d_rid, 0.0);

        ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
        if( ridFwdTo==0 ){
          ridFwdTo = name_to_typed_rid(zBackTo,"ci");
        }
        if( ridFwdTo ){
          if( !haveParameterN ) nEntry = 0;
          rStopTime = mtime_of_rid(ridFwdTo, 9e99);

        }
      }else if( bSeparateDandP ){
        rStopTime = mtime_of_rid(p_rid, 9e99);
        nEntry = 0;
      }
      if( rStopTime<9e99 ){
        rStopTime += 5.8e-6;  /* Round up by 1/2 second */
      }
      db_multi_exec(
        "WITH RECURSIVE dx(rid,mtime) AS (\n"
        "  SELECT %d, 0\n"
        "  UNION\n"
        "  SELECT plink.cid, plink.mtime FROM dx, plink\n"
        "   WHERE plink.pid=dx.rid\n"
        "     AND plink.mtime<=%.*g\n"
        "   ORDER BY 2\n"
        ")\n"
        "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
        d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1
      );
      if( ridFwdTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridFwdTo) ){
        db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridFwdTo);
        bFwdAdded = 1;
      }
      if( bSeparateDandP ){
        db_multi_exec(
           "INSERT INTO ok_d SELECT rid FROM ok;"
           "DELETE FROM ok;"
        );
      }else{
        nd = db_int(0, "SELECT count(*)-1 FROM ok");
        if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
        if( nd>0 || p_rid==0 ){
          blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
        }
        if( useDividers && !selectedRid ) selectedRid = d_rid;
        db_multi_exec("DELETE FROM ok");
      }
    }
    if( p_rid ){
      zBackTo = P("bt");
      if( zBackTo && bSeparateDandP ){
        if( zError==0 ){
          zError = "Cannot use the bt= query parameter when both p= and d= "
                   "are used and have distinct values.";
        }
        zBackTo = 0;
      }        
      if( zBackTo ){
        double rDateLimit = mtime_of_rid(p_rid, 0.0);

        ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
        if( ridBackTo==0 ){
          ridBackTo = name_to_typed_rid(zBackTo,"ci");
        }
        if( ridBackTo && !haveParameterN ) nEntry = 0;
      }else if( bSeparateDandP ){
        ridBackTo = d_rid;
        nEntry = 0;
      }
      compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
      if( ridBackTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridBackTo) ){
        db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo);
        bBackAdded = 1;
      }
      if( bSeparateDandP ){
        db_multi_exec("DELETE FROM ok WHERE rid NOT IN ok_d;");
        db_multi_exec("%s", blob_sql_text(&sql));
      }else{
        np = db_int(0, "SELECT count(*)-1 FROM ok");
        if( np>0 || nd==0 ){
          if( nd>0 ) blob_appendf(&desc, " and ");
          blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
          db_multi_exec("%s", blob_sql_text(&sql));
        }
        if( useDividers && !selectedRid ) selectedRid = p_rid;
      }
    }
    if( bSeparateDandP ){
      int n = db_int(0, "SELECT count(*) FROM ok");
      blob_reset(&desc);
      blob_appendf(&desc,
          "%d check-ins that are both ancestors of %z%h</a>"
          " and descendants of %z%h</a>",
          n,
          href("%R/info?name=%h",zDPNameP),zDPNameP,
          href("%R/info?name=%h",zDPNameD),zDPNameD
      );
      ridBackTo = 0;
      ridFwdTo = 0;
    }else{
      blob_appendf(&desc, " of %z%h</a>",
                   href("%R/info?name=%h", zBaseName), zBaseName);
    }
    if( ridBackTo ){
      if( np==0 ){
        blob_reset(&desc);
        blob_appendf(&desc,
                    "Check-in %z%h</a> only (%z%h</a> does not precede it)",
                     href("%R/info?name=%h",zBaseName), zBaseName,
                     href("%R/info?name=%h",zBackTo), zBackTo);
      }else{
        blob_appendf(&desc, " back to %z%h</a>%s",
                     href("%R/info?name=%h",zBackTo), zBackTo,
                     bBackAdded ? " (not a direct anscestor)" : "");
        if( ridFwdTo && zFwdTo ){
          blob_appendf(&desc, " and up to %z%h</a>%s",
                     href("%R/info?name=%h",zFwdTo), zFwdTo,
                     bFwdAdded ? " (not a direct descendant)" : "");
        }
      }
    }else if( ridFwdTo ){
      if( nd==0 ){
        blob_reset(&desc);
        blob_appendf(&desc,
                    "Check-in %z%h</a> only (%z%h</a> does not follow it)",
                     href("%R/info?name=%h",zBaseName), zBaseName,
                     href("%R/info?name=%h",zFwdTo), zFwdTo);
      }else{
        blob_appendf(&desc, " up to %z%h</a>%s",
                     href("%R/info?name=%h",zFwdTo), zFwdTo,






                     bFwdAdded ? " (not a direct descendant)":"");
      }
    }
    if( advancedMenu ){
      style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
    }
    style_submenu_entry("n","Max:",4,0);
    timeline_y_submenu(1);
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
          zSearch, zSearch, zSearch);
      }else{
        blob_append_sql(&cond,
          " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
          zSearch, zSearch);
      }
    }
    rBefore = symbolic_name_to_mtime(zBefore, &zBefore);
    rAfter = symbolic_name_to_mtime(zAfter, &zAfter);
    rCirca = symbolic_name_to_mtime(zCirca, &zCirca);
    blob_append_sql(&sql, "%s", blob_sql_text(&cond));
    if( rAfter>0.0 ){
      if( rBefore>0.0 ){
        blob_append_sql(&sql,
           " AND event.mtime>=%.17g AND event.mtime<=%.17g\n"
           " ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND);
        nEntry = -1;







|
|
|







2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
          zSearch, zSearch, zSearch);
      }else{
        blob_append_sql(&cond,
          " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
          zSearch, zSearch);
      }
    }
    rBefore = symbolic_name_to_mtime(zBefore, &zBefore, 1);
    rAfter = symbolic_name_to_mtime(zAfter, &zAfter, 0);
    rCirca = symbolic_name_to_mtime(zCirca, &zCirca, 0);
    blob_append_sql(&sql, "%s", blob_sql_text(&cond));
    if( rAfter>0.0 ){
      if( rBefore>0.0 ){
        blob_append_sql(&sql,
           " AND event.mtime>=%.17g AND event.mtime<=%.17g\n"
           " ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND);
        nEntry = -1;
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
      };
      double rDate;
      zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
      if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
        zDate = mprintf("%s", (zAfter ? zAfter : zBefore));
      }
      if( zDate ){
        rDate = symbolic_name_to_mtime(zDate, 0);
        if( db_int(0,
            "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
            " WHERE blob.rid=event.objid AND mtime<=%.17g%s)",
            rDate-ONE_SECOND, blob_sql_text(&cond))
        ){
          zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0));
          zOlderButtonLabel = "More";
        }
        free(zDate);
      }
      zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
      if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
        zDate = mprintf("%s", (zBefore ? zBefore : zAfter));
      }
      if( zDate ){
        rDate = symbolic_name_to_mtime(zDate, 0);
        if( db_int(0,
            "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
            " WHERE blob.rid=event.objid AND mtime>=%.17g%s)",
            rDate+ONE_SECOND, blob_sql_text(&cond))
        ){
          zNewerButton = fossil_strdup(url_render(&url, "a", zDate, "b", 0));
          zNewerButtonLabel = "More";







|















|







2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
      };
      double rDate;
      zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
      if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
        zDate = mprintf("%s", (zAfter ? zAfter : zBefore));
      }
      if( zDate ){
        rDate = symbolic_name_to_mtime(zDate, 0, 0);
        if( db_int(0,
            "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
            " WHERE blob.rid=event.objid AND mtime<=%.17g%s)",
            rDate-ONE_SECOND, blob_sql_text(&cond))
        ){
          zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0));
          zOlderButtonLabel = "More";
        }
        free(zDate);
      }
      zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
      if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
        zDate = mprintf("%s", (zBefore ? zBefore : zAfter));
      }
      if( zDate ){
        rDate = symbolic_name_to_mtime(zDate, 0, 0);
        if( db_int(0,
            "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
            " WHERE blob.rid=event.objid AND mtime>=%.17g%s)",
            rDate+ONE_SECOND, blob_sql_text(&cond))
        ){
          zNewerButton = fossil_strdup(url_render(&url, "a", zDate, "b", 0));
          zNewerButtonLabel = "More";
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
        url_render(&url, "advm", "0", "udc", "1"));
  }else{
    style_submenu_element("Advanced", "%s",
        url_render(&url, "advm", "1", "udc", "1"));
  }
  if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID;
  if( useDividers && zMark && zMark[0] ){
    double r = symbolic_name_to_mtime(zMark, 0);
    if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r);
  }
  blob_zero(&sql);
  if( PB("oldestfirst") ){
    db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/");
  }else{
    db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");







|







3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
        url_render(&url, "advm", "0", "udc", "1"));
  }else{
    style_submenu_element("Advanced", "%s",
        url_render(&url, "advm", "1", "udc", "1"));
  }
  if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID;
  if( useDividers && zMark && zMark[0] ){
    double r = symbolic_name_to_mtime(zMark, 0, 0);
    if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r);
  }
  blob_zero(&sql);
  if( PB("oldestfirst") ){
    db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/");
  }else{
    db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
3381
3382
3383
3384
3385
3386
3387

































3388
3389
3390
3391
3392
3393

3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
      fossil_print("+++ end of timeline (%d) +++\n", nEntry);
    }else{
      fossil_print("+++ no more data (%d) +++\n", nEntry);
    }
  }
  if( fchngQueryInit ) db_finalize(&fchngQuery);
}


































/*
** Return a pointer to a static string that forms the basis for
** a timeline query for display on a TTY.
*/
const char *timeline_query_for_tty(void){

  static const char zBaseSql[] =
    @ SELECT
    @   blob.rid AS rid,
    @   uuid,
    @   datetime(event.mtime,toLocal()) AS mDateTime,
    @   coalesce(ecomment,comment)
    @     || ' (user: ' || coalesce(euser,user,'?')
    @     || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
    @           FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
    @                   FROM tag, tagxref
    @                  WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
    @                    AND tagxref.rid=blob.rid AND tagxref.tagtype>0))
    @     || ')' as comment,







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






>





|







3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
      fossil_print("+++ end of timeline (%d) +++\n", nEntry);
    }else{
      fossil_print("+++ no more data (%d) +++\n", nEntry);
    }
  }
  if( fchngQueryInit ) db_finalize(&fchngQuery);
}

/*
**    wiki_to_text(TEXT)
**
** Return a text rendering of Fossil-Wiki TEXT, intended for display
** on a timeline.  The timeline-plaintext and timeline-hard-newlines
** settings are considered when doing this rendering.
*/
static void wiki_to_text_sqlfunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  const char *zIn, *zOut;
  int nIn, nOut;
  Blob in, html, txt;
  zIn = (const char*)sqlite3_value_text(argv[0]);
  if( zIn==0 ) return;
  nIn = sqlite3_value_bytes(argv[0]);
  blob_init(&in, zIn, nIn);
  blob_init(&html, 0, 0);
  wiki_convert(&in, &html, wiki_convert_flags(0));
  blob_reset(&in);
  blob_init(&txt, 0, 0);
  html_to_plaintext(blob_str(&html), &txt, 0);
  blob_reset(&html);
  nOut = blob_size(&txt);
  zOut = blob_str(&txt);
  while( fossil_isspace(zOut[0]) ){ zOut++; nOut--; }
  while( nOut>0 && fossil_isspace(zOut[nOut-1]) ){ nOut--; }
  sqlite3_result_text(context, zOut, nOut, SQLITE_TRANSIENT);
  blob_reset(&txt);
}

/*
** Return a pointer to a static string that forms the basis for
** a timeline query for display on a TTY.
*/
const char *timeline_query_for_tty(void){
  static int once = 0;
  static const char zBaseSql[] =
    @ SELECT
    @   blob.rid AS rid,
    @   uuid,
    @   datetime(event.mtime,toLocal()) AS mDateTime,
    @   wiki_to_text(coalesce(ecomment,comment))
    @     || ' (user: ' || coalesce(euser,user,'?')
    @     || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
    @           FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
    @                   FROM tag, tagxref
    @                  WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
    @                    AND tagxref.rid=blob.rid AND tagxref.tagtype>0))
    @     || ')' as comment,
3420
3421
3422
3423
3424
3425
3426





3427
3428
3429
3430
3431
3432
3433
    @ FROM tag CROSS JOIN event CROSS JOIN blob
    @      LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
    @   AND tagxref.tagtype>0
    @   AND tagxref.rid=blob.rid
    @ WHERE blob.rid=event.objid
    @   AND tag.tagname='branch'
  ;





  return zBaseSql;
}

/*
** Return true if the input string is a date in the ISO 8601 format:
** YYYY-MM-DD.
*/







>
>
>
>
>







3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
    @ FROM tag CROSS JOIN event CROSS JOIN blob
    @      LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
    @   AND tagxref.tagtype>0
    @   AND tagxref.rid=blob.rid
    @ WHERE blob.rid=event.objid
    @   AND tag.tagname='branch'
  ;
  if( !once && g.db ){
    once = 1;
    sqlite3_create_function(g.db, "wiki_to_text", 1, SQLITE_UTF8, 0,
                            wiki_to_text_sqlfunc, 0, 0);
  }
  return zBaseSql;
}

/*
** Return true if the input string is a date in the ISO 8601 format:
** YYYY-MM-DD.
*/
Changes to src/tkt.c.
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
    }else{
      showTimeline = 0;
    }
  }
  if( !showTimeline && g.perm.Hyperlink ){
    style_submenu_element("Timeline", "%R/info/%T", zUuid);
  }






  if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1);
  ticket_init();
  initializeVariablesFromCGI();
  getAllTicketFields();
  initializeVariablesFromDb();
  zScript = ticket_viewpage_code();
  if( P("showfields")!=0 ) showAllFields();
  if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br>\n", -1);
  safe_html_context(DOCSRC_TICKET);
  Th_Render(zScript);
  if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);

  zFullName = db_text(0,
       "SELECT tkt_uuid FROM ticket"
       " WHERE tkt_uuid GLOB '%q*'", zUuid);
  if( zFullName ){
    attachment_list(zFullName, "<h2>Attachments:</h2>", 1);
  }

  style_finish_page();
}








>
>
>
>
>
>












<
<
<







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
    }else{
      showTimeline = 0;
    }
  }
  if( !showTimeline && g.perm.Hyperlink ){
    style_submenu_element("Timeline", "%R/info/%T", zUuid);
  }
  zFullName = db_text(0,
       "SELECT tkt_uuid FROM ticket"
       " WHERE tkt_uuid GLOB '%q*'", zUuid);
  if( g.perm.WrWiki && g.perm.WrTkt ){
    style_submenu_element("Edit Description", "%R/wikiedit?name=ticket/%T", zFullName);
  }
  if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1);
  ticket_init();
  initializeVariablesFromCGI();
  getAllTicketFields();
  initializeVariablesFromDb();
  zScript = ticket_viewpage_code();
  if( P("showfields")!=0 ) showAllFields();
  if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br>\n", -1);
  safe_html_context(DOCSRC_TICKET);
  Th_Render(zScript);
  if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);




  if( zFullName ){
    attachment_list(zFullName, "<h2>Attachments:</h2>", 1);
  }

  style_finish_page();
}

Changes to src/tktsetup.c.
503
504
505
506
507
508
509

510





511
512
513
514
515
516
517
@   </td>
@ <th1>enable_output 1</th1>
@ </tr>
@ <tr><td class="tktDspLabel">Version&nbsp;Found&nbsp;In:</td>
@ <td colspan="3" valign="top" class="tktDspValue">
@ $<foundin>
@ </td></tr>

@





@ <th1>
@ if {[info exists comment]} {
@   if {[string length $comment]>10} {
@     html {
@       <tr><td class="tktDspLabel">Description:</td></tr>
@       <tr><td colspan="5" class="tktDspValue">
@     }







>

>
>
>
>
>







503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
@   </td>
@ <th1>enable_output 1</th1>
@ </tr>
@ <tr><td class="tktDspLabel">Version&nbsp;Found&nbsp;In:</td>
@ <td colspan="3" valign="top" class="tktDspValue">
@ $<foundin>
@ </td></tr>
@ </table>
@
@ <th1>
@ wiki_assoc "ticket" $tkt_uuid
@ </th1>
@
@ <table cellpadding="5" style="min-width:100%">
@ <th1>
@ if {[info exists comment]} {
@   if {[string length $comment]>10} {
@     html {
@       <tr><td class="tktDspLabel">Description:</td></tr>
@       <tr><td colspan="5" class="tktDspValue">
@     }
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
@               mimetype as xmimetype, icomment AS xcomment,
@               username AS xusername
@          FROM ticketchng
@         WHERE tkt_id=$tkt_id AND length(icomment)>0} {
@   if {$seenRow} {
@     html "<hr>\n"
@   } else {
@     html "<tr><td class='tktDspLabel'>User Comments:</td></tr>\n"
@     html "<tr><td colspan='5' class='tktDspValue'>\n"
@     set seenRow 1
@   }
@   html "<span class='tktDspCommenter'>"
@   html "[htmlize $xlogin]"
@   if {$xlogin ne $xusername && [string length $xusername]>0} {
@     html " (claiming to be [htmlize $xusername])"







|







535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
@               mimetype as xmimetype, icomment AS xcomment,
@               username AS xusername
@          FROM ticketchng
@         WHERE tkt_id=$tkt_id AND length(icomment)>0} {
@   if {$seenRow} {
@     html "<hr>\n"
@   } else {
@     html "<tr><td class='tktDspLabel' style='text-align:left'>User Comments:</td></tr>\n"
@     html "<tr><td colspan='5' class='tktDspValue'>\n"
@     set seenRow 1
@   }
@   html "<span class='tktDspCommenter'>"
@   html "[htmlize $xlogin]"
@   if {$xlogin ne $xusername && [string length $xusername]>0} {
@     html " (claiming to be [htmlize $xusername])"
Changes to src/unversioned.c.
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
    if( fossil_isspace(zName[0]) ) return 1;
    zName++;
  }
  return 0;
}

/*
** COMMAND: uv#
** COMMAND: unversioned
**
** Usage: %fossil unversioned SUBCOMMAND ARGS...
**    or: %fossil uv SUBCOMMAND ARGS..
**
** Unversioned files (UV-files) are artifacts that are synced and are available
** for download but which do not preserve history.  Only the most recent version
** of each UV-file is retained.  Changes to an UV-file are permanent and cannot







|
|







216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
    if( fossil_isspace(zName[0]) ) return 1;
    zName++;
  }
  return 0;
}

/*
** COMMAND: uv#                           abbrv-subcom
** COMMAND: unversioned                   abbrv-subcom
**
** Usage: %fossil unversioned SUBCOMMAND ARGS...
**    or: %fossil uv SUBCOMMAND ARGS..
**
** Unversioned files (UV-files) are artifacts that are synced and are available
** for download but which do not preserve history.  Only the most recent version
** of each UV-file is retained.  Changes to an UV-file are permanent and cannot
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
**    revert ?URL?           Restore the state of all unversioned files in the
**                           local repository to match the remote repository
**                           URL.
**
**                           Options:
**                              -v|--verbose     Extra diagnostic output
**                              -n|--dry-run     Show what would have happened

**
**    remove|rm|delete FILE ...
**                           Remove unversioned files from the local repository.
**                           Changes are not pushed to other repositories until
**                           the next sync.
**
**                           Options:
**                              --glob PATTERN   Remove files that match
**                              --like PATTERN   Remove files that match
**
**    sync ?URL?             Synchronize the state of all unversioned files with
**                           the remote repository URL.  The most recent version
**                           of each file is propagated to all repositories and
**                           all prior versions are permanently forgotten.
**                           The remote account requires the 'y' capability.
**
**                           Options:
**                              -v|--verbose     Extra diagnostic output
**                              -n|--dry-run     Show what would have happened

**
**    touch FILE ...         Update the TIMESTAMP on all of the listed files
**
** Options:
**   --mtime TIMESTAMP       Use TIMESTAMP instead of "now" for the "add",
**                           "edit", "remove", and "touch" subcommands.
**   -R|--repository REPO    Use REPO as the repository







>



















>







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
**    revert ?URL?           Restore the state of all unversioned files in the
**                           local repository to match the remote repository
**                           URL.
**
**                           Options:
**                              -v|--verbose     Extra diagnostic output
**                              -n|--dry-run     Show what would have happened
**                              --proxy PROXY    Use the specified HTTP proxy
**
**    remove|rm|delete FILE ...
**                           Remove unversioned files from the local repository.
**                           Changes are not pushed to other repositories until
**                           the next sync.
**
**                           Options:
**                              --glob PATTERN   Remove files that match
**                              --like PATTERN   Remove files that match
**
**    sync ?URL?             Synchronize the state of all unversioned files with
**                           the remote repository URL.  The most recent version
**                           of each file is propagated to all repositories and
**                           all prior versions are permanently forgotten.
**                           The remote account requires the 'y' capability.
**
**                           Options:
**                              -v|--verbose     Extra diagnostic output
**                              -n|--dry-run     Show what would have happened
**                              --proxy PROXY    Use the specified HTTP proxy
**
**    touch FILE ...         Update the TIMESTAMP on all of the listed files
**
** Options:
**   --mtime TIMESTAMP       Use TIMESTAMP instead of "now" for the "add",
**                           "edit", "remove", and "touch" subcommands.
**   -R|--repository REPO    Use REPO as the repository
Changes to src/update.c.
99
100
101
102
103
104
105

106
107
108
109
110
111
112
**   --force-missing         Force update if missing content after sync
**   -K|--keep-merge-files   On merge conflict, retain the temporary files
**                           used for merging, named *-baseline, *-original,
**                           and *-merge.
**   --latest                Acceptable in place of VERSION, update to
**                           latest version
**   --nosync                Do not auto-sync prior to update

**   --setmtime              Set timestamps of all files to match their
**                           SCM-side times (the timestamp of the last
**                           check-in which modified them).
**   -v|--verbose            Print status information about all files
**   -W|--width WIDTH        Width of lines (default is to auto-detect).
**                           Must be more than 20 or 0 (= no limit,
**                           resulting in a single line per entry).







>







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
**   --force-missing         Force update if missing content after sync
**   -K|--keep-merge-files   On merge conflict, retain the temporary files
**                           used for merging, named *-baseline, *-original,
**                           and *-merge.
**   --latest                Acceptable in place of VERSION, update to
**                           latest version
**   --nosync                Do not auto-sync prior to update
**   --proxy PROXY           Use PROXY as http proxy during sync operation
**   --setmtime              Set timestamps of all files to match their
**                           SCM-side times (the timestamp of the last
**                           check-in which modified them).
**   -v|--verbose            Print status information about all files
**   -W|--width WIDTH        Width of lines (default is to auto-detect).
**                           Must be more than 20 or 0 (= no limit,
**                           resulting in a single line per entry).
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
       if( tid==0 || !is_a_version(tid) ){
        fossil_fatal("no such check-in: %s", g.argv[2]);
      }
    }
  }

  /* If no VERSION is specified on the command-line, then look for a
  ** descendent of the current version.  If there are multiple descendants,
  ** look for one from the same branch as the current version.  If there
  ** are still multiple descendants, show them all and refuse to update
  ** until the user selects one.
  */
  if( tid==0 ){
    int closeCode = 1;
    compute_leaves(vid, closeCode);







|







197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
       if( tid==0 || !is_a_version(tid) ){
        fossil_fatal("no such check-in: %s", g.argv[2]);
      }
    }
  }

  /* If no VERSION is specified on the command-line, then look for a
  ** descendant of the current version.  If there are multiple descendants,
  ** look for one from the same branch as the current version.  If there
  ** are still multiple descendants, show them all and refuse to update
  ** until the user selects one.
  */
  if( tid==0 ){
    int closeCode = 1;
    compute_leaves(vid, closeCode);
Changes to src/user.c.
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310

/*
** Prompt the user to enter a single line of text.
*/
void prompt_user(const char *zPrompt, Blob *pIn){
  char *z;
  char zLine[1000];
  blob_zero(pIn);
  fossil_force_newline();
  fossil_print("%s", zPrompt);
  fflush(stdout);
  z = fgets(zLine, sizeof(zLine), stdin);
  if( z ){
    int n = (int)strlen(z);
    if( n>0 && z[n-1]=='\n' ) fossil_new_line_started();







|







296
297
298
299
300
301
302
303
304
305
306
307
308
309
310

/*
** Prompt the user to enter a single line of text.
*/
void prompt_user(const char *zPrompt, Blob *pIn){
  char *z;
  char zLine[1000];
  blob_init(pIn, 0, 0);
  fossil_force_newline();
  fossil_print("%s", zPrompt);
  fflush(stdout);
  z = fgets(zLine, sizeof(zLine), stdin);
  if( z ){
    int n = (int)strlen(z);
    if( n>0 && z[n-1]=='\n' ) fossil_new_line_started();
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
**        Query or set contact information for user USERNAME
**
** > fossil user default ?USERNAME?
**
**        Query or set the default user.  The default user is the
**        user for command-line interaction.
**
** > fossil user list
** > fossil user ls
**
**        List all users known to the repository
**
** > fossil user new ?USERNAME? ?CONTACT-INFO? ?PASSWORD?
**
**        Create a new user in the repository.  Users can never be
**        deleted.  They can be denied all access but they must continue







|
<







329
330
331
332
333
334
335
336

337
338
339
340
341
342
343
**        Query or set contact information for user USERNAME
**
** > fossil user default ?USERNAME?
**
**        Query or set the default user.  The default user is the
**        user for command-line interaction.
**
** > fossil user list | ls

**
**        List all users known to the repository
**
** > fossil user new ?USERNAME? ?CONTACT-INFO? ?PASSWORD?
**
**        Create a new user in the repository.  Users can never be
**        deleted.  They can be denied all access but they must continue
Changes to src/utf8.c.
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include "utf8.h"
#include <sqlite3.h>
#ifdef _WIN32
# include <windows.h>
#endif
#include "cygsup.h"

#if defined(_WIN32) || defined(__CYGWIN__)
/*
** Translate MBCS to UTF-8.  Return a pointer to the translated text.
** Call fossil_mbcs_free() to deallocate any memory used to store the
** returned pointer when done.
*/
char *fossil_mbcs_to_utf8(const char *zMbcs){
  extern char *sqlite3_win32_mbcs_to_utf8(const char*);







|







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include "utf8.h"
#include <sqlite3.h>
#ifdef _WIN32
# include <windows.h>
#endif
#include "cygsup.h"

#if defined(_WIN32)
/*
** Translate MBCS to UTF-8.  Return a pointer to the translated text.
** Call fossil_mbcs_free() to deallocate any memory used to store the
** returned pointer when done.
*/
char *fossil_mbcs_to_utf8(const char *zMbcs){
  extern char *sqlite3_win32_mbcs_to_utf8(const char*);
Changes to src/util.c.
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
** not found.
**
** Search algorithm:
** (1) The local "editor" setting
** (2) The global "editor" setting
** (3) The VISUAL environment variable
** (4) The EDITOR environment variable
** (5) (Windows only:) "notepad.exe"

*/
const char *fossil_text_editor(void){
  const char *zEditor = db_get("editor", 0);




  if( zEditor==0 ){
    zEditor = fossil_getenv("VISUAL");
  }
  if( zEditor==0 ){
    zEditor = fossil_getenv("EDITOR");
  }
#if defined(_WIN32) || defined(__CYGWIN__)
  if( zEditor==0 ){
    zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SYSTEMROOT"));
#if defined(__CYGWIN__)
    zEditor = fossil_utf8_to_path(zEditor, 0);

#endif
  }

#endif
  return zEditor;
}

/*
** Construct a temporary filename.
**
** The returned string is obtained from sqlite3_malloc() and must be







|
>



>
>
>
>






<
|
|
<
|
>
|
|
>
|







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
** not found.
**
** Search algorithm:
** (1) The local "editor" setting
** (2) The global "editor" setting
** (3) The VISUAL environment variable
** (4) The EDITOR environment variable
** (5) Any of the following programs that are available:
**        notepad, nano, pico, jove, edit, vi, vim, ed,
*/
const char *fossil_text_editor(void){
  const char *zEditor = db_get("editor", 0);
  const char *azStdEd[] = {
    "notepad", "nano", "pico", "jove", "edit", "vi", "vim", "ed"
  };
  int i = 0;
  if( zEditor==0 ){
    zEditor = fossil_getenv("VISUAL");
  }
  if( zEditor==0 ){
    zEditor = fossil_getenv("EDITOR");
  }

  while( zEditor==0 && i<count(azStdEd) ){
    if( fossil_app_on_path(azStdEd[i],0) ){

      zEditor = azStdEd[i];
    }else{
      i++;
    }
  }
  if( zEditor && is_false(zEditor) ) zEditor = 0;
  return zEditor;
}

/*
** Construct a temporary filename.
**
** The returned string is obtained from sqlite3_malloc() and must be
893
894
895
896
897
898
899
900
901
902
903
904



905
906
907
908
909
910

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
937
938
939
940
941
942
943
944
945
946
947
948
949
950
*/
int fossil_num_digits(int n){
  return n<      10 ? 1 : n<      100 ? 2 : n<      1000 ? 3
       : n<   10000 ? 4 : n<   100000 ? 5 : n<   1000000 ? 6
       : n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10;
}

#if !defined(_WIN32)
#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
/*
** Search for an executable on the PATH environment variable.
** Return true (1) if found and false (0) if not found.



*/
static int binaryOnPath(const char *zBinary){
  const char *zPath = fossil_getenv("PATH");
  char *zFull;
  int i;
  int bExists;

  while( zPath && zPath[0] ){











    while( zPath[0]==':' ) zPath++;
    for(i=0; zPath[i] && zPath[i]!=':'; i++){}
    zFull = mprintf("%.*s/%s", i, zPath, zBinary);
    bExists = file_access(zFull, X_OK);




    fossil_free(zFull);
    if( bExists==0 ) return 1;



    zPath += i;
  }
  return 0;
}
#endif













#endif










/*
** Return the name of a command that will launch a web-browser.
*/
const char *fossil_web_browser(void){
  const char *zBrowser = 0;
#if defined(_WIN32)
  zBrowser = db_get("web-browser", "start \"\"");
#elif defined(__DARWIN__) || defined(__APPLE__) || defined(__HAIKU__)
  zBrowser = db_get("web-browser", "open");
#else
  zBrowser = db_get("web-browser", 0);
  if( zBrowser==0 ){
    static const char *const azBrowserProg[] =
        { "xdg-open", "gnome-open", "firefox", "google-chrome" };
    int i;
    zBrowser = "echo";
    for(i=0; i<count(azBrowserProg); i++){
      if( binaryOnPath(azBrowserProg[i]) ){
        zBrowser = azBrowserProg[i];
        break;
      }
    }
    zBrowser = mprintf("%s 2>/dev/null", zBrowser);
  }
#endif







<
<



>
>
>

|




>

>
>
>
>
>
>
>
>
>
>
>




>
>
>
>

|
>
>
>


|

|
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
|
>
>


















|







898
899
900
901
902
903
904


905
906
907
908
909
910
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
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
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
*/
int fossil_num_digits(int n){
  return n<      10 ? 1 : n<      100 ? 2 : n<      1000 ? 3
       : n<   10000 ? 4 : n<   100000 ? 5 : n<   1000000 ? 6
       : n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10;
}



/*
** Search for an executable on the PATH environment variable.
** Return true (1) if found and false (0) if not found.
**
** Print the full pathname of the first location if ePrint==1.  Print
** all pathnames for the executable if ePrint==2 or more.
*/
int fossil_app_on_path(const char *zBinary, int ePrint){
  const char *zPath = fossil_getenv("PATH");
  char *zFull;
  int i;
  int bExists;
  int bFound = 0;
  while( zPath && zPath[0] ){
#ifdef _WIN32
    while( zPath[0]==';' ) zPath++;
    for(i=0; zPath[i] && zPath[i]!=';'; i++){}
    zFull = mprintf("%.*s\\%s.exe", i, zPath, zBinary);
    bExists = file_access(zFull, R_OK);
    if( bExists!=0 ){
      fossil_free(zFull);
      zFull = mprintf("%.*s\\%s.bat", i, zPath, zBinary);
      bExists = file_access(zFull, R_OK);
    }
#else
    while( zPath[0]==':' ) zPath++;
    for(i=0; zPath[i] && zPath[i]!=':'; i++){}
    zFull = mprintf("%.*s/%s", i, zPath, zBinary);
    bExists = file_access(zFull, X_OK);
#endif
    if( bExists==0 && ePrint ){
      fossil_print("%s\n", zFull);
    }
    fossil_free(zFull);
    if( bExists==0 ){
      if( ePrint<2 ) return 1;
      bFound = 1;
    }
    zPath += i;
  }
  return bFound;
}

/*
** COMMAND: which*
**
** Usage: fossil which [-a] NAME ...
**
** For each NAME mentioned as an argument, print the first location on the
** on PATH of the executable with that name.  Or, show all locations on PATH
** for each argument if the -a option is used.
**
** This command is a substitute for the unix "which" command, which is not
** always available, especially on Windows.
*/
void test_app_on_path(void){
  int i;
  int ePrint = 1;
  if( find_option("all","a",0)!=0 ) ePrint = 2;
  verify_all_options();
  for(i=2; i<g.argc; i++){
    if( fossil_app_on_path(g.argv[i], ePrint)==0 ){
      fossil_print("NOT FOUND: %s\n", g.argv[i]);
    }
  }
}

/*
** Return the name of a command that will launch a web-browser.
*/
const char *fossil_web_browser(void){
  const char *zBrowser = 0;
#if defined(_WIN32)
  zBrowser = db_get("web-browser", "start \"\"");
#elif defined(__DARWIN__) || defined(__APPLE__) || defined(__HAIKU__)
  zBrowser = db_get("web-browser", "open");
#else
  zBrowser = db_get("web-browser", 0);
  if( zBrowser==0 ){
    static const char *const azBrowserProg[] =
        { "xdg-open", "gnome-open", "firefox", "google-chrome" };
    int i;
    zBrowser = "echo";
    for(i=0; i<count(azBrowserProg); i++){
      if( fossil_app_on_path(azBrowserProg[i],0) ){
        zBrowser = azBrowserProg[i];
        break;
      }
    }
    zBrowser = mprintf("%s 2>/dev/null", zBrowser);
  }
#endif
Changes to src/wiki.c.
19
20
21
22
23
24
25



26
27
28
29
30
31
32
** This file contains code to do formatting of wiki text.
*/
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include "wiki.h"




/*
** Return true if the input string is a well-formed wiki page name.
**
** Well-formed wiki page names do not begin or end with whitespace,
** and do not contain tabs or other control characters and do not
** contain more than a single space character in a row.  Well-formed
** names must be between 1 and 100 characters in length, inclusive.







>
>
>







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
** This file contains code to do formatting of wiki text.
*/
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include "wiki.h"

#define has_prefix(literal_prfx, zStr) \
  (fossil_strncmp((zStr), "" literal_prfx, (sizeof literal_prfx)-1)==0)

/*
** Return true if the input string is a well-formed wiki page name.
**
** Well-formed wiki page names do not begin or end with whitespace,
** and do not contain tabs or other control characters and do not
** contain more than a single space character in a row.  Well-formed
** names must be between 1 and 100 characters in length, inclusive.
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
/* Return values from wiki_page_type() */
#if INTERFACE
# define WIKITYPE_UNKNOWN    (-1)
# define WIKITYPE_NORMAL     0
# define WIKITYPE_BRANCH     1
# define WIKITYPE_CHECKIN    2
# define WIKITYPE_TAG        3

#endif

/*
** Figure out what type of wiki page we are dealing with.
*/
int wiki_page_type(const char *zPageName){
  if( db_get_boolean("wiki-about",1)==0 ){
    return WIKITYPE_NORMAL;
  }else
  if( sqlite3_strglob("checkin/*", zPageName)==0
   && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
  ){
    return WIKITYPE_CHECKIN;
  }else
  if( sqlite3_strglob("branch/*", zPageName)==0 ){
    return WIKITYPE_BRANCH;
  }else
  if( sqlite3_strglob("tag/*", zPageName)==0 ){
    return WIKITYPE_TAG;



  }
  return WIKITYPE_NORMAL;
}

/*
** Returns a JSON-friendly string form of the integer value returned
** by wiki_page_type(zPageName).
*/
const char * wiki_page_type_name(const char *zPageName){
  switch(wiki_page_type(zPageName)){
    case WIKITYPE_CHECKIN: return "checkin";
    case WIKITYPE_BRANCH: return "branch";
    case WIKITYPE_TAG: return "tag";

    case WIKITYPE_NORMAL:
    default: return "normal";
  }
}

/*
** Add an appropriate style_header() for either the /wiki or /wikiedit page







>









|




|


|

>
>
>













>







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
/* Return values from wiki_page_type() */
#if INTERFACE
# define WIKITYPE_UNKNOWN    (-1)
# define WIKITYPE_NORMAL     0
# define WIKITYPE_BRANCH     1
# define WIKITYPE_CHECKIN    2
# define WIKITYPE_TAG        3
# define WIKITYPE_TICKET     4
#endif

/*
** Figure out what type of wiki page we are dealing with.
*/
int wiki_page_type(const char *zPageName){
  if( db_get_boolean("wiki-about",1)==0 ){
    return WIKITYPE_NORMAL;
  }else
  if( has_prefix("checkin/", zPageName)
   && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
  ){
    return WIKITYPE_CHECKIN;
  }else
  if( has_prefix("branch/", zPageName) ){
    return WIKITYPE_BRANCH;
  }else
  if( has_prefix("tag/", zPageName) ){
    return WIKITYPE_TAG;
  }else
  if( has_prefix("ticket/", zPageName) ){
    return WIKITYPE_TICKET;
  }
  return WIKITYPE_NORMAL;
}

/*
** Returns a JSON-friendly string form of the integer value returned
** by wiki_page_type(zPageName).
*/
const char * wiki_page_type_name(const char *zPageName){
  switch(wiki_page_type(zPageName)){
    case WIKITYPE_CHECKIN: return "checkin";
    case WIKITYPE_BRANCH: return "branch";
    case WIKITYPE_TAG: return "tag";
    case WIKITYPE_TICKET: return "ticket";
    case WIKITYPE_NORMAL:
    default: return "normal";
  }
}

/*
** Add an appropriate style_header() for either the /wiki or /wikiedit page
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
        cgi_redirectf("%R/timeline?t=%t",zPageName);
      }else{
        style_header("Notes About Tag %h", zPageName);
        style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName);
      }
      break;
    }










  }
  return eType;
}

/*
** Wiki pages with special names "branch/...", "checkin/...", and "tag/..."
** requires perm.Write privilege in addition to perm.WrWiki in order
** to write.  This function determines whether the extra perm.Write
** is required and available.  Return true if writing to the wiki page
** may proceed, and return false if permission is lacking.
*/
static int wiki_special_permission(const char *zPageName){
  if( strncmp(zPageName,"branch/",7)!=0
   && strncmp(zPageName,"checkin/",8)!=0
   && strncmp(zPageName,"tag/",4)!=0

  ){
    return 1;
  }
  if( db_get_boolean("wiki-about",1)==0 ){
    return 1;



  }
  return g.perm.Write;
}

/*
** WEBPAGE: wiki
**







>
>
>
>
>
>
>
>
>
>















>





>
>
>







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
        cgi_redirectf("%R/timeline?t=%t",zPageName);
      }else{
        style_header("Notes About Tag %h", zPageName);
        style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName);
      }
      break;
    }
    case WIKITYPE_TICKET: {
      zPageName += 7;
      if( zExtra[0]==0 && !P("p") ){
        cgi_redirectf("%R/tktview/%s",zPageName);
      }else{
        style_header("Notes About Ticket %h", zPageName);
        style_submenu_element("Ticket","%R/tktview/%s",zPageName);
      }
      break;
    }
  }
  return eType;
}

/*
** Wiki pages with special names "branch/...", "checkin/...", and "tag/..."
** requires perm.Write privilege in addition to perm.WrWiki in order
** to write.  This function determines whether the extra perm.Write
** is required and available.  Return true if writing to the wiki page
** may proceed, and return false if permission is lacking.
*/
static int wiki_special_permission(const char *zPageName){
  if( strncmp(zPageName,"branch/",7)!=0
   && strncmp(zPageName,"checkin/",8)!=0
   && strncmp(zPageName,"tag/",4)!=0
   && strncmp(zPageName,"ticket/",7)!=0
  ){
    return 1;
  }
  if( db_get_boolean("wiki-about",1)==0 ){
    return 1;
  }
  if( strncmp(zPageName,"ticket/",7)==0 ){
    return g.perm.WrTkt;
  }
  return g.perm.Write;
}

/*
** WEBPAGE: wiki
**
1966
1967
1968
1969
1970
1971
1972
1973

1974
1975
1976
1977
1978
1979
1980
    style_submenu_element("Active", "%R/wcontent");
  }else{
    style_submenu_element("All", "%R/wcontent?all=1");
  }
  cgi_check_for_malice();
  showCkBr = db_exists(
    "SELECT tag.tagname AS tn FROM tag JOIN tagxref USING(tagid) "
    "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' ) "

    "  AND TYPEOF(tagxref.value+0)='integer'" );
  if( showCkBr ){
    showCkBr = P("showckbr")!=0;
    style_submenu_checkbox("showckbr", "Show associated wikis", 0, 0);
  }
  wiki_standard_submenu(W_ALL_BUT(W_LIST));
  db_prepare(&q, listAllWikiPages/*works-like:""*/);







|
>







1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
    style_submenu_element("Active", "%R/wcontent");
  }else{
    style_submenu_element("All", "%R/wcontent?all=1");
  }
  cgi_check_for_malice();
  showCkBr = db_exists(
    "SELECT tag.tagname AS tn FROM tag JOIN tagxref USING(tagid) "
    "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' OR "
    "        tn GLOB 'wiki-tag/*'     OR tn GLOB 'wiki-ticket/*' ) "
    "  AND TYPEOF(tagxref.value+0)='integer'" );
  if( showCkBr ){
    showCkBr = P("showckbr")!=0;
    style_submenu_checkbox("showckbr", "Show associated wikis", 0, 0);
  }
  wiki_standard_submenu(W_ALL_BUT(W_LIST));
  db_prepare(&q, listAllWikiPages/*works-like:""*/);
1996
1997
1998
1999
2000
2001
2002
2003
2004


2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
    double rWmtime = db_column_double(&q, 3);
    sqlite3_int64 iMtime = (sqlite3_int64)(rWmtime*86400.0);
    char *zAge;
    int wcnt = db_column_int(&q, 4);
    char *zWDisplayName;

    if( !showCkBr &&
        (sqlite3_strglob("checkin/*", zWName)==0 ||
         sqlite3_strglob("branch/*", zWName)==0) ){


      continue;
    }
    if( sqlite3_strglob("checkin/*", zWName)==0 ){
      zWDisplayName = mprintf("%.25s...", zWName);
    }else{
      zWDisplayName = mprintf("%s", zWName);
    }
    if( wrid==0 ){
      if( !showAll ) continue;
      @ <tr><td data-sortkey="%h(zSort)">\
      @ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td>
    }else{
      @ <tr><td data-sortkey="%h(zSort)">\







|
|
>
>


|


|







2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
    double rWmtime = db_column_double(&q, 3);
    sqlite3_int64 iMtime = (sqlite3_int64)(rWmtime*86400.0);
    char *zAge;
    int wcnt = db_column_int(&q, 4);
    char *zWDisplayName;

    if( !showCkBr &&
        (has_prefix("checkin/", zWName) ||
         has_prefix("branch/",  zWName) ||
         has_prefix("tag/",     zWName) ||
         has_prefix("ticket/",  zWName) )){
      continue;
    }
    if( has_prefix("checkin/",zWName) || has_prefix("ticket/",zWName) ){
      zWDisplayName = mprintf("%.25s...", zWName);
    }else{
      zWDisplayName = fossil_strdup(zWName);
    }
    if( wrid==0 ){
      if( !showAll ) continue;
      @ <tr><td data-sortkey="%h(zSort)">\
      @ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td>
    }else{
      @ <tr><td data-sortkey="%h(zSort)">\
2499
2500
2501
2502
2503
2504
2505
2506
2507


2508
2509
2510
2511
2512
2513
2514
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q, 0);
      const int wrid = db_column_int(&q, 2);
      if(!showAll && !wrid){
        continue;
      }
      if( !showCkBr &&
          (sqlite3_strglob("checkin/*", zName)==0 ||
           sqlite3_strglob("branch/*", zName)==0) ){


        continue;
      }
      if( showIds ){
        const char *zUuid = db_column_text(&q, 1);
        fossil_print("%s ",zUuid);
      }
      fossil_print( "%s\n",zName);







|
|
>
>







2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q, 0);
      const int wrid = db_column_int(&q, 2);
      if(!showAll && !wrid){
        continue;
      }
      if( !showCkBr &&
          (has_prefix("checkin/", zName) ||
           has_prefix("branch/",  zName) ||
           has_prefix("tag/",     zName) ||
           has_prefix("ticket/",  zName) ) ){
        continue;
      }
      if( showIds ){
        const char *zUuid = db_column_text(&q, 1);
        fossil_print("%s ",zUuid);
      }
      fossil_print( "%s\n",zName);
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562













2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
    @ <div class="section accordion">About %s(zPrefix) %h(zName)</div>
  }
}

/*
** Add an "Wiki" button in a submenu that links to the read-wiki page.
*/
static void wiki_submenu_to_edit_wiki(
  const char *zPrefix,   /* "branch", "tag", or "checkin" */
  const char *zName,     /* Name of the object */
  unsigned int mFlags    /* Zero or more WIKIASSOC_* flags */
){
  if( g.perm.RdWiki && (mFlags & WIKIASSOC_MENU_READ)!=0 ){
    style_submenu_element("Wiki", "%R/wikiedit?name=%s/%t", zPrefix, zName);













  }
}

/*
** Check to see if there exists a wiki page with a name zPrefix/zName.
** If there is, then render a <div class='section'>..</div> and
** return true.
**
** If there is no such wiki page, return false.
*/
int wiki_render_associated(
  const char *zPrefix,   /* "branch", "tag", or "checkin" */
  const char *zName,     /* Name of the object */
  unsigned int mFlags    /* Zero or more WIKIASSOC_* flags */
){
  int rid;
  Manifest *pWiki;
  if( !db_get_boolean("wiki-about",1) ) return 0;
  rid = db_int(0,







|





|
>
>
>
>
>
>
>
>
>
>
>
>
>











|







2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
    @ <div class="section accordion">About %s(zPrefix) %h(zName)</div>
  }
}

/*
** Add an "Wiki" button in a submenu that links to the read-wiki page.
*/
static void wiki_submenu_to_read_wiki(
  const char *zPrefix,   /* "branch", "tag", or "checkin" */
  const char *zName,     /* Name of the object */
  unsigned int mFlags    /* Zero or more WIKIASSOC_* flags */
){
  if( g.perm.RdWiki && (mFlags & WIKIASSOC_MENU_READ)!=0 ){
    style_submenu_element("Wiki", "%R/wiki?name=%s/%t", zPrefix, zName);
  }
}

/*
** Add an "Edit Wiki" button in a submenu that links to the edit-wiki page.
*/
static void wiki_submenu_to_edit_wiki(
  const char *zPrefix,   /* "branch", "tag", or "checkin" */
  const char *zName,     /* Name of the object */
  unsigned int mFlags   /* Zero or more WIKIASSOC_* flags */
){
  if( g.perm.WrWiki && (mFlags & WIKIASSOC_MENU_WRITE)!=0 ){
    style_submenu_element("Edit Wiki", "%R/wikiedit?name=%s/%t", zPrefix, zName);
  }
}

/*
** Check to see if there exists a wiki page with a name zPrefix/zName.
** If there is, then render a <div class='section'>..</div> and
** return true.
**
** If there is no such wiki page, return false.
*/
int wiki_render_associated(
  const char *zPrefix,   /* "branch", "tag", "ticket", or "checkin" */
  const char *zName,     /* Name of the object */
  unsigned int mFlags    /* Zero or more WIKIASSOC_* flags */
){
  int rid;
  Manifest *pWiki;
  if( !db_get_boolean("wiki-about",1) ) return 0;
  rid = db_int(0,
2599
2600
2601
2602
2603
2604
2605

2606
2607
2608
2609
2610
2611
2612
    blob_init(&markdown, pWiki->zWiki, -1);
    markdown_to_html(&markdown, &title, &tail);
    if( blob_size(&title) ){
      @ <div class="section accordion">%h(blob_str(&title))</div>
    }else{
      wiki_section_label(zPrefix, zName, mFlags);
    }

    wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags);
    @ <div class="accordion_panel">
    safe_html_context(DOCSRC_WIKI);
    safe_html(&tail);
    convert_href_and_output(&tail);
    @ </div>
    blob_reset(&tail);







>







2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
    blob_init(&markdown, pWiki->zWiki, -1);
    markdown_to_html(&markdown, &title, &tail);
    if( blob_size(&title) ){
      @ <div class="section accordion">%h(blob_str(&title))</div>
    }else{
      wiki_section_label(zPrefix, zName, mFlags);
    }
    wiki_submenu_to_read_wiki(zPrefix, zName, mFlags);
    wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags);
    @ <div class="accordion_panel">
    safe_html_context(DOCSRC_WIKI);
    safe_html(&tail);
    convert_href_and_output(&tail);
    @ </div>
    blob_reset(&tail);
Changes to src/wikiformat.c.
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
#include <assert.h>
#include "wikiformat.h"

#if INTERFACE
/*
** Allowed wiki transformation operations
*/
#define WIKI_HTMLONLY       0x001  /* HTML markup only.  No wiki */
#define WIKI_INLINE         0x002  /* Do not surround with <p>..</p> */
#define WIKI_NOBLOCK        0x004  /* No block markup of any kind */
#define WIKI_BUTTONS        0x008  /* Allow sub-menu buttons */
#define WIKI_NOBADLINKS     0x010  /* Ignore broken hyperlinks */
#define WIKI_LINKSONLY      0x020  /* No markup.  Only decorate links */
#define WIKI_NEWLINE        0x040  /* Honor \n - break lines at each \n */
#define WIKI_MARKDOWNLINKS  0x080  /* Resolve hyperlinks as in markdown */
#define WIKI_SAFE           0x100  /* Make the result safe for embedding */
#define WIKI_TARGET_BLANK   0x200  /* Hyperlinks go to a new window */
#define WIKI_NOBRACKET      0x400  /* Omit extra [..] around hyperlinks */





















#endif


/*
** These are the only markup attributes allowed.
*/
enum allowed_attr_t {
  ATTR_ALIGN = 1,







|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|







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
#include <assert.h>
#include "wikiformat.h"

#if INTERFACE
/*
** Allowed wiki transformation operations
*/
#define WIKI_HTMLONLY      0x0001  /* HTML markup only.  No wiki */
#define WIKI_INLINE        0x0002  /* Do not surround with <p>..</p> */
/* avalable for reuse:     0x0004  ---  formerly WIKI_NOBLOCK */
#define WIKI_BUTTONS       0x0008  /* Allow sub-menu buttons */
#define WIKI_NOBADLINKS    0x0010  /* Ignore broken hyperlinks */
#define WIKI_LINKSONLY     0x0020  /* No markup.  Only decorate links */
#define WIKI_NEWLINE       0x0040  /* Honor \n - break lines at each \n */
#define WIKI_MARKDOWNLINKS 0x0080  /* Resolve hyperlinks as in markdown */
#define WIKI_SAFE          0x0100  /* Make the result safe for embedding */
#define WIKI_TARGET_BLANK  0x0200  /* Hyperlinks go to a new window */
#define WIKI_NOBRACKET     0x0400  /* Omit extra [..] around hyperlinks */
#define WIKI_ADMIN         0x0800  /* Ignore g.perm.Hyperlink */
#define WIKI_MARK          0x1000  /* Add <mark>..</mark> around problems */

/*
** Return values from wiki_convert
*/
#define RENDER_LINK       0x0001  /* One or more hyperlinks rendered */
#define RENDER_ENTITY     0x0002  /* One or more HTML entities (ex: &lt;) */
#define RENDER_TAG        0x0004  /* One or more HTML tags */
#define RENDER_BLOCKTAG   0x0008  /* One or more HTML block tags (ex: <p>) */
#define RENDER_BLOCK      0x0010  /* Block wiki (paragraphs, etc.) */
#define RENDER_MARK       0x0020  /* Output contains <mark>..</mark> */
#define RENDER_BADLINK    0x0100  /* Bad hyperlink syntax seen */
#define RENDER_BADTARGET  0x0200  /* Bad hyperlink target */
#define RENDER_BADTAG     0x0400  /* Bad HTML tag or tag syntax */
#define RENDER_BADENTITY  0x0800  /* Bad HTML entity syntax */
#define RENDER_BADHTML    0x1000  /* Bad HTML seen */
#define RENDER_ERROR      0x8000  /* Some other kind of error */
/* Composite values: */
#define RENDER_ANYERROR   0x9f00  /* Mask for any kind of error */

#endif /* INTERFACE */


/*
** These are the only markup attributes allowed.
*/
enum allowed_attr_t {
  ATTR_ALIGN = 1,
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458

459
460
461
462
463
464
465
** State flags.  Save the lower 16 bits for the WIKI_* flags.
*/
#define AT_NEWLINE          0x0010000  /* At start of a line */
#define AT_PARAGRAPH        0x0020000  /* At start of a paragraph */
#define ALLOW_WIKI          0x0040000  /* Allow wiki markup */
#define ALLOW_LINKS         0x0080000  /* Allow [...] hyperlinks */
#define FONT_MARKUP_ONLY    0x0100000  /* Only allow MUTYPE_FONT markup */
#define INLINE_MARKUP_ONLY  0x0200000  /* Allow only "inline" markup */
#define IN_LIST             0x0400000  /* Within wiki <ul> or <ol> */

/*
** Current state of the rendering engine
*/
typedef struct Renderer Renderer;
struct Renderer {
  Blob *pOut;                 /* Output appended to this blob */
  int state;                  /* Flag that govern rendering */

  unsigned renderFlags;       /* Flags from the client */
  int wikiList;               /* Current wiki list type */
  int inVerbatim;             /* True in <verbatim> mode */
  int preVerbState;           /* Value of state prior to verbatim */
  int wantAutoParagraph;      /* True if a <p> is desired */
  int inAutoParagraph;        /* True if within an automatic paragraph */
  int pikchrHtmlFlags;        /* Flags for pikchr_to_html() */







<
|








>







463
464
465
466
467
468
469

470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
** State flags.  Save the lower 16 bits for the WIKI_* flags.
*/
#define AT_NEWLINE          0x0010000  /* At start of a line */
#define AT_PARAGRAPH        0x0020000  /* At start of a paragraph */
#define ALLOW_WIKI          0x0040000  /* Allow wiki markup */
#define ALLOW_LINKS         0x0080000  /* Allow [...] hyperlinks */
#define FONT_MARKUP_ONLY    0x0100000  /* Only allow MUTYPE_FONT markup */

#define IN_LIST             0x0200000  /* Within wiki <ul> or <ol> */

/*
** Current state of the rendering engine
*/
typedef struct Renderer Renderer;
struct Renderer {
  Blob *pOut;                 /* Output appended to this blob */
  int state;                  /* Flag that govern rendering */
  int mRender;                /* Mask of RENDER_* values to return */
  unsigned renderFlags;       /* Flags from the client */
  int wikiList;               /* Current wiki list type */
  int inVerbatim;             /* True in <verbatim> mode */
  int preVerbState;           /* Value of state prior to verbatim */
  int wantAutoParagraph;      /* True if a <p> is desired */
  int inAutoParagraph;        /* True if within an automatic paragraph */
  int pikchrHtmlFlags;        /* Flags for pikchr_to_html() */
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
** characters in that token.  Write the token type into *pTokenType.
*/
static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){
  int n;
  if( z[0]=='<' ){
    n = html_tag_length(z);
    if( n>0 ){

      *pTokenType = TOKEN_MARKUP;
      return n;
    }else{








      *pTokenType = TOKEN_CHARACTER;
      return 1;
    }
  }
  if( z[0]=='&' && (p->inVerbatim || !isElement(z)) ){
    *pTokenType = TOKEN_CHARACTER;
    return 1;
  }
  if( (p->state & ALLOW_WIKI)!=0 ){
    if( z[0]=='\n' ){
      n = paragraphBreakLength(z);
      if( n>0 ){
        *pTokenType = TOKEN_PARAGRAPH;
        return n;
      }else{







>



>
>
>
>
>
>
>
>




<
<
<
<







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
** characters in that token.  Write the token type into *pTokenType.
*/
static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){
  int n;
  if( z[0]=='<' ){
    n = html_tag_length(z);
    if( n>0 ){
      p->mRender |= RENDER_TAG;
      *pTokenType = TOKEN_MARKUP;
      return n;
    }else{
      p->mRender |= RENDER_BADTAG;
      *pTokenType = TOKEN_CHARACTER;
      return 1;
    }
  }
  if( z[0]=='&' ){
    p->mRender |= RENDER_ENTITY;
    if( (p->inVerbatim || !isElement(z)) ){
      *pTokenType = TOKEN_CHARACTER;
      return 1;
    }
  }




  if( (p->state & ALLOW_WIKI)!=0 ){
    if( z[0]=='\n' ){
      n = paragraphBreakLength(z);
      if( n>0 ){
        *pTokenType = TOKEN_PARAGRAPH;
        return n;
      }else{
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
    if( (p->state & AT_PARAGRAPH)!=0 && fossil_isspace(z[0]) ){
      n = indentLength(z);
      if( n>0 ){
        *pTokenType = TOKEN_INDENT;
        return n;
      }
    }
    if( z[0]=='[' && (n = linkLength(z))>0 ){

      *pTokenType = TOKEN_LINK;
      return n;





    }

  }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' && (n = linkLength(z))>0 ){

    *pTokenType = TOKEN_LINK;
    return n;






  }
  *pTokenType = TOKEN_TEXT;
  return 1 + textLength(z+1, p->state);
}

/*
** Parse only Wiki links, return everything else as TOKEN_RAW.
**
** z points to the start of a token.  Return the number of
** characters in that token. Write the token type into *pTokenType.
*/
static int nextRawToken(const char *z, Renderer *p, int *pTokenType){
  int n;
  if( z[0]=='[' && (n = linkLength(z))>0 ){

    *pTokenType = TOKEN_LINK;
    return n;






  }
  *pTokenType = TOKEN_RAW;
  return 1 + textLength(z+1, p->state);
}

/*
** A single markup is parsed into an instance of the following







|
>
|
|
>
>
>
>
>
|
>
|
>
|
|
>
>
>
>
>
>













|
>
|
|
>
>
>
>
>
>







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
    if( (p->state & AT_PARAGRAPH)!=0 && fossil_isspace(z[0]) ){
      n = indentLength(z);
      if( n>0 ){
        *pTokenType = TOKEN_INDENT;
        return n;
      }
    }
    if( z[0]=='[' ){
      if( (n = linkLength(z))>0 ){
        *pTokenType = TOKEN_LINK;
        return n;
      }else if( p->state & WIKI_MARK ){
        blob_append_string(p->pOut, "<mark>");
        p->mRender |= RENDER_BADLINK|RENDER_MARK;
      }else{
        p->mRender |= RENDER_BADLINK;
      }
    }
  }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' ){
    if( (n = linkLength(z))>0 ){
      *pTokenType = TOKEN_LINK;
      return n;
    }else if( p->state & WIKI_MARK ){
      blob_append_string(p->pOut, "<mark>");
      p->mRender |= RENDER_BADLINK|RENDER_MARK;
    }else{
      p->mRender |= RENDER_BADLINK;
    }
  }
  *pTokenType = TOKEN_TEXT;
  return 1 + textLength(z+1, p->state);
}

/*
** Parse only Wiki links, return everything else as TOKEN_RAW.
**
** z points to the start of a token.  Return the number of
** characters in that token. Write the token type into *pTokenType.
*/
static int nextRawToken(const char *z, Renderer *p, int *pTokenType){
  int n;
  if( z[0]=='[' ){
    if( (n = linkLength(z))>0 ){
      *pTokenType = TOKEN_LINK;
      return n;
   }else if( p->state & WIKI_MARK ){
     blob_append_string(p->pOut, "<mark>");
     p->mRender |= RENDER_BADLINK|RENDER_MARK;
   }else{
      p->mRender |= RENDER_BADLINK;
    }
  }
  *pTokenType = TOKEN_RAW;
  return 1 + textLength(z+1, p->state);
}

/*
** A single markup is parsed into an instance of the following
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
**
**    [WikiPageName]
**    [wiki:WikiPageName]
**
**    [2010-02-27 07:13]
**
**    [InterMap:Link]  ->  Interwiki link




*/
void wiki_resolve_hyperlink(
  Blob *pOut,             /* Write the HTML output here */
  int mFlags,             /* Rendering option flags */
  const char *zTarget,    /* Hyperlink target; text within [...] */
  char *zClose,           /* Write hyperlink closing text here */
  int nClose,             /* Bytes available in zClose[] */
  const char *zOrig,      /* Complete document text */
  const char *zTitle      /* Title of the link */
){
  const char *zTerm = "</a>";
  const char *z;
  char *zExtra = 0;
  const char *zExtraNS = 0;
  char *zRemote = 0;


  if( zTitle ){
    zExtra = mprintf(" title='%h'", zTitle);
    zExtraNS = zExtra+1;
  }else if( mFlags & WIKI_TARGET_BLANK ){
    zExtra = mprintf(" target='_blank'");
    zExtraNS = zExtra+1;







>
>
>
>

|













>







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
**
**    [WikiPageName]
**    [wiki:WikiPageName]
**
**    [2010-02-27 07:13]
**
**    [InterMap:Link]  ->  Interwiki link
**
** The return value is a mask of RENDER_* values indicating what happened.
** Probably the return value is 0 on success and RENDER_BADTARGET or
** RENDER_BADLINK if there are problems.
*/
int wiki_resolve_hyperlink(
  Blob *pOut,             /* Write the HTML output here */
  int mFlags,             /* Rendering option flags */
  const char *zTarget,    /* Hyperlink target; text within [...] */
  char *zClose,           /* Write hyperlink closing text here */
  int nClose,             /* Bytes available in zClose[] */
  const char *zOrig,      /* Complete document text */
  const char *zTitle      /* Title of the link */
){
  const char *zTerm = "</a>";
  const char *z;
  char *zExtra = 0;
  const char *zExtraNS = 0;
  char *zRemote = 0;
  int rc = 0;

  if( zTitle ){
    zExtra = mprintf(" title='%h'", zTitle);
    zExtraNS = zExtra+1;
  }else if( mFlags & WIKI_TARGET_BLANK ){
    zExtra = mprintf(" target='_blank'");
    zExtraNS = zExtra+1;
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
          blob_appendf(pOut, "%s", zLB);
          zTerm = "]";
        }
      }
    }else if( !in_this_repo(zTarget) ){
      if( (mFlags & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
        zTerm = "";




      }else{
        blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB);
        zTerm = "]</span>";
      }

    }else if( g.perm.Hyperlink ){
      blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB);
      zTerm = "]</a>";
    }else{
      zTerm = "";
    }
    if( zTerm[0]==']' && (mFlags & WIKI_NOBRACKET)!=0 ) zTerm++;
  }else if( (zRemote = interwiki_url(zTarget))!=0 ){
    blob_appendf(pOut, "<a href=\"%z\"%s>", zRemote, zExtra);
    zTerm = "</a>";
  }else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){
    /* The link is to a valid wiki page name */
    const char *zOverride = wiki_is_overridden(zTarget);
    if( zOverride ){
      blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra);
    }else{
      blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra);
    }
  }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
            && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
    /* Dates or date-and-times in ISO8610 resolve to a link to the
    ** timeline for that date */
    blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
  }else if( mFlags & WIKI_MARKDOWNLINKS ){
    /* If none of the above, and if rendering links for markdown, then
    ** create a link to the literal text of the target */
    blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra);




  }else if( zOrig && zTarget>=&zOrig[2]
        && zTarget[-1]=='[' && !fossil_isspace(zTarget[-2]) ){
    /* If the hyperlink markup is not preceded by whitespace, then it
    ** is probably a C-language subscript or similar, not really a
    ** hyperlink.  Just ignore it. */
    zTerm = "";
  }else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
    /* Also ignore the link if various flags are set */
    zTerm = "";

  }else{
    blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
    zTerm = "</span>";

  }
  if( zExtra ) fossil_free(zExtra);
  assert( (int)strlen(zTerm)<nClose );
  sqlite3_snprintf(nClose, zClose, "%s", zTerm);















}

/*
** Check to see if the given parsed markup is the correct
** </verbatim> tag.
*/
static int endVerbatim(Renderer *p, ParsedMarkup *pMarkup){







>
>
>
>




>
|



















|






>
>
>
>









>



>




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







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
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
          blob_appendf(pOut, "%s", zLB);
          zTerm = "]";
        }
      }
    }else if( !in_this_repo(zTarget) ){
      if( (mFlags & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
        zTerm = "";
      }else if( (mFlags & WIKI_MARK)!=0 ){
        blob_appendf(pOut, "<mark>%s", zLB);
        zTerm = "]</mark>";
        rc |= RENDER_MARK;
      }else{
        blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB);
        zTerm = "]</span>";
      }
      rc |= RENDER_BADTARGET;
    }else if( g.perm.Hyperlink || (mFlags & WIKI_ADMIN)!=0 ){
      blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB);
      zTerm = "]</a>";
    }else{
      zTerm = "";
    }
    if( zTerm[0]==']' && (mFlags & WIKI_NOBRACKET)!=0 ) zTerm++;
  }else if( (zRemote = interwiki_url(zTarget))!=0 ){
    blob_appendf(pOut, "<a href=\"%z\"%s>", zRemote, zExtra);
    zTerm = "</a>";
  }else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){
    /* The link is to a valid wiki page name */
    const char *zOverride = wiki_is_overridden(zTarget);
    if( zOverride ){
      blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra);
    }else{
      blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra);
    }
  }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
            && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
    /* Dates or date-and-times in ISO8601 resolve to a link to the
    ** timeline for that date */
    blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
  }else if( mFlags & WIKI_MARKDOWNLINKS ){
    /* If none of the above, and if rendering links for markdown, then
    ** create a link to the literal text of the target */
    blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra);
  }else if( mFlags & WIKI_MARK ){
    blob_appendf(pOut, "<mark>[");
    zTerm = "]</mark>";
    rc |= RENDER_BADTARGET|RENDER_MARK;
  }else if( zOrig && zTarget>=&zOrig[2]
        && zTarget[-1]=='[' && !fossil_isspace(zTarget[-2]) ){
    /* If the hyperlink markup is not preceded by whitespace, then it
    ** is probably a C-language subscript or similar, not really a
    ** hyperlink.  Just ignore it. */
    zTerm = "";
  }else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
    /* Also ignore the link if various flags are set */
    zTerm = "";
    rc |= RENDER_BADTARGET;
  }else{
    blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
    zTerm = "</span>";
    rc |= RENDER_BADTARGET;
  }
  if( zExtra ) fossil_free(zExtra);
  assert( (int)strlen(zTerm)<nClose );
  sqlite3_snprintf(nClose, zClose, "%s", zTerm);
  return rc;
}

/*
** Check zTarget to see if it looks like a valid hyperlink target.
** Return true if it does seem valid and false if not.
*/
int wiki_valid_link_target(char *zTarget){
  char zClose[30];
  Blob notUsed;
  blob_init(&notUsed, 0, 0);
  wiki_resolve_hyperlink(&notUsed, WIKI_NOBADLINKS|WIKI_ADMIN,
       zTarget, zClose, sizeof(zClose)-1, 0, 0);
  blob_reset(&notUsed);
  return zClose[0]!=0;
}

/*
** Check to see if the given parsed markup is the correct
** </verbatim> tag.
*/
static int endVerbatim(Renderer *p, ParsedMarkup *pMarkup){
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
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
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
**
** This routine will probably modify the content of z[].
*/
static void wiki_render(Renderer *p, char *z){
  int tokenType;
  ParsedMarkup markup;
  int n;
  int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0;
  int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0;
  int linksOnly = (p->state & WIKI_LINKSONLY)!=0;
  char *zOrig = z;

  /* Make sure the attribute constants and names still align
  ** following changes in the attribute list. */
  assert( fossil_strcmp(aAttribute[ATTR_WIDTH].zName, "width")==0 );

  while( z[0] ){
    if( wikiHtmlOnly ){
      n = nextRawToken(z, p, &tokenType);
    }else{
      n = nextWikiToken(z, p, &tokenType);
    }
    p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
    switch( tokenType ){
      case TOKEN_PARAGRAPH: {
        if( inlineOnly ){
          /* blob_append_string(p->pOut, " &para; "); */
          blob_append_string(p->pOut, " &nbsp;&nbsp; ");
        }else{
          if( p->wikiList ){
            popStackToTag(p, p->wikiList);
            p->wikiList = 0;
          }
          endAutoParagraph(p);
          blob_append_string(p->pOut, "\n\n");
          p->wantAutoParagraph = 1;
        }
        p->state |= AT_PARAGRAPH|AT_NEWLINE;
        break;
      }
      case TOKEN_NEWLINE: {
        if( p->renderFlags & WIKI_NEWLINE ){
          blob_append_string(p->pOut, "<br>\n");
        }else{
          blob_append_string(p->pOut, "\n");
        }
        p->state |= AT_NEWLINE;
        break;
      }
      case TOKEN_BUL_LI: {
        if( inlineOnly ){
          blob_append_string(p->pOut, " &bull; ");
        }else{
          if( p->wikiList!=MARKUP_UL ){
            if( p->wikiList ){
              popStackToTag(p, p->wikiList);
            }
            endAutoParagraph(p);
            pushStack(p, MARKUP_UL);
            blob_append_string(p->pOut, "<ul>");
            p->wikiList = MARKUP_UL;
          }
          popStackToTag(p, MARKUP_LI);
          startAutoParagraph(p);
          pushStack(p, MARKUP_LI);
          blob_append_string(p->pOut, "<li>");
        }
        break;
      }
      case TOKEN_NUM_LI: {
        if( inlineOnly ){
          blob_append_string(p->pOut, " # ");
        }else{
          if( p->wikiList!=MARKUP_OL ){
            if( p->wikiList ){
              popStackToTag(p, p->wikiList);
            }
            endAutoParagraph(p);
            pushStack(p, MARKUP_OL);
            blob_append_string(p->pOut, "<ol>");
            p->wikiList = MARKUP_OL;
          }
          popStackToTag(p, MARKUP_LI);
          startAutoParagraph(p);
          pushStack(p, MARKUP_LI);
          blob_append_string(p->pOut, "<li>");
        }
        break;
      }
      case TOKEN_ENUM: {
        if( inlineOnly ){
          blob_appendf(p->pOut, " (%d) ", atoi(z));
        }else{
          if( p->wikiList!=MARKUP_OL ){
            if( p->wikiList ){
              popStackToTag(p, p->wikiList);
            }
            endAutoParagraph(p);
            pushStack(p, MARKUP_OL);
            blob_append_string(p->pOut, "<ol>");
            p->wikiList = MARKUP_OL;
          }
          popStackToTag(p, MARKUP_LI);
          startAutoParagraph(p);
          pushStack(p, MARKUP_LI);
          blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z));
        }
        break;
      }
      case TOKEN_INDENT: {
        if( !inlineOnly ){

          assert( p->wikiList==0 );
          pushStack(p, MARKUP_BLOCKQUOTE);
          blob_append_string(p->pOut, "<blockquote>");
          p->wantAutoParagraph = 0;
          p->wikiList = MARKUP_BLOCKQUOTE;
        }
        break;
      }
      case TOKEN_CHARACTER: {
        startAutoParagraph(p);




        if( z[0]=='<' ){

          blob_append_string(p->pOut, "&lt;");
        }else if( z[0]=='&' ){

          blob_append_string(p->pOut, "&amp;");
        }









        break;
      }
      case TOKEN_LINK: {
        char *zTarget;
        char *zDisplay = 0;
        int i, j;
        int savedState;
        char zClose[20];
        char cS1 = 0;
        int iS1 = 0;

        startAutoParagraph(p);

        zTarget = &z[1];
        for(i=1; z[i] && z[i]!=']'; i++){
          if( z[i]=='|' && zDisplay==0 ){
            zDisplay = &z[i+1];
            for(j=i; j>0 && fossil_isspace(z[j-1]); j--){}
            iS1 = j;
            cS1 = z[j];
            z[j] = 0;
          }
        }
        z[i] = 0;
        if( zDisplay==0 ){
          zDisplay = zTarget + interwiki_removable_prefix(zTarget);
        }else{
          while( fossil_isspace(*zDisplay) ) zDisplay++;
        }
        wiki_resolve_hyperlink(p->pOut, p->state,
                               zTarget, zClose, sizeof(zClose), zOrig, 0);
        if( linksOnly || zClose[0]==0 || p->inVerbatim ){
          if( cS1 ) z[iS1] = cS1;
          if( zClose[0]!=']' ){
            blob_appendf(p->pOut, "[%h]%s", zTarget, zClose);
          }else{
            blob_appendf(p->pOut, "%h%s", zTarget, zClose);







<

















<
<
<
<
|
|
|
|
|
|
|
<













|
<
<
|
|
|
|
|
|
|
|
|
|
|
|
|
<



<
|
<
|
|
|
|
|
|
|
|
|
|
|
|
|
<



|
<
<
|
|
|
|
|
|
|
|
|
|
|
|
|
<



<
>
|
|
|
|
|
<




>
>
>
>

>


>


>
>
>
>
>
>
>
>
>












>
















|







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
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
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
**
** This routine will probably modify the content of z[].
*/
static void wiki_render(Renderer *p, char *z){
  int tokenType;
  ParsedMarkup markup;
  int n;

  int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0;
  int linksOnly = (p->state & WIKI_LINKSONLY)!=0;
  char *zOrig = z;

  /* Make sure the attribute constants and names still align
  ** following changes in the attribute list. */
  assert( fossil_strcmp(aAttribute[ATTR_WIDTH].zName, "width")==0 );

  while( z[0] ){
    if( wikiHtmlOnly ){
      n = nextRawToken(z, p, &tokenType);
    }else{
      n = nextWikiToken(z, p, &tokenType);
    }
    p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
    switch( tokenType ){
      case TOKEN_PARAGRAPH: {




        if( p->wikiList ){
          popStackToTag(p, p->wikiList);
          p->wikiList = 0;
        }
        endAutoParagraph(p);
        blob_append_string(p->pOut, "\n\n");
        p->wantAutoParagraph = 1;

        p->state |= AT_PARAGRAPH|AT_NEWLINE;
        break;
      }
      case TOKEN_NEWLINE: {
        if( p->renderFlags & WIKI_NEWLINE ){
          blob_append_string(p->pOut, "<br>\n");
        }else{
          blob_append_string(p->pOut, "\n");
        }
        p->state |= AT_NEWLINE;
        break;
      }
      case TOKEN_BUL_LI: {
        p->mRender |= RENDER_BLOCK;


        if( p->wikiList!=MARKUP_UL ){
          if( p->wikiList ){
            popStackToTag(p, p->wikiList);
          }
          endAutoParagraph(p);
          pushStack(p, MARKUP_UL);
          blob_append_string(p->pOut, "<ul>");
          p->wikiList = MARKUP_UL;
        }
        popStackToTag(p, MARKUP_LI);
        startAutoParagraph(p);
        pushStack(p, MARKUP_LI);
        blob_append_string(p->pOut, "<li>");

        break;
      }
      case TOKEN_NUM_LI: {

        p->mRender |= RENDER_BLOCK;

        if( p->wikiList!=MARKUP_OL ){
          if( p->wikiList ){
            popStackToTag(p, p->wikiList);
          }
          endAutoParagraph(p);
          pushStack(p, MARKUP_OL);
          blob_append_string(p->pOut, "<ol>");
          p->wikiList = MARKUP_OL;
        }
        popStackToTag(p, MARKUP_LI);
        startAutoParagraph(p);
        pushStack(p, MARKUP_LI);
        blob_append_string(p->pOut, "<li>");

        break;
      }
      case TOKEN_ENUM: {
        p->mRender |= RENDER_BLOCK;


        if( p->wikiList!=MARKUP_OL ){
          if( p->wikiList ){
            popStackToTag(p, p->wikiList);
          }
          endAutoParagraph(p);
          pushStack(p, MARKUP_OL);
          blob_append_string(p->pOut, "<ol>");
          p->wikiList = MARKUP_OL;
        }
        popStackToTag(p, MARKUP_LI);
        startAutoParagraph(p);
        pushStack(p, MARKUP_LI);
        blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z));

        break;
      }
      case TOKEN_INDENT: {

        p->mRender |= RENDER_BLOCK;
        assert( p->wikiList==0 );
        pushStack(p, MARKUP_BLOCKQUOTE);
        blob_append_string(p->pOut, "<blockquote>");
        p->wantAutoParagraph = 0;
        p->wikiList = MARKUP_BLOCKQUOTE;

        break;
      }
      case TOKEN_CHARACTER: {
        startAutoParagraph(p);
        if( p->state & WIKI_MARK ){
          blob_append_string(p->pOut, "<mark>");
          p->mRender |= RENDER_MARK;
        }
        if( z[0]=='<' ){
          p->mRender |= RENDER_BADTAG;
          blob_append_string(p->pOut, "&lt;");
        }else if( z[0]=='&' ){
          p->mRender |= RENDER_BADENTITY;
          blob_append_string(p->pOut, "&amp;");
        }
        if( p->state & WIKI_MARK ){
          if( fossil_isalnum(z[1]) || (z[1]=='/' && fossil_isalnum(z[2])) ){
            int kk;
            for(kk=2; fossil_isalnum(z[kk]); kk++){}
            blob_append(p->pOut, &z[1], kk-1);
            n = kk;
          }
          blob_append_string(p->pOut, "</mark>");
        }
        break;
      }
      case TOKEN_LINK: {
        char *zTarget;
        char *zDisplay = 0;
        int i, j;
        int savedState;
        char zClose[20];
        char cS1 = 0;
        int iS1 = 0;

        startAutoParagraph(p);
        p->mRender |= RENDER_LINK;
        zTarget = &z[1];
        for(i=1; z[i] && z[i]!=']'; i++){
          if( z[i]=='|' && zDisplay==0 ){
            zDisplay = &z[i+1];
            for(j=i; j>0 && fossil_isspace(z[j-1]); j--){}
            iS1 = j;
            cS1 = z[j];
            z[j] = 0;
          }
        }
        z[i] = 0;
        if( zDisplay==0 ){
          zDisplay = zTarget + interwiki_removable_prefix(zTarget);
        }else{
          while( fossil_isspace(*zDisplay) ) zDisplay++;
        }
        p->mRender |= wiki_resolve_hyperlink(p->pOut, p->state,
                               zTarget, zClose, sizeof(zClose), zOrig, 0);
        if( linksOnly || zClose[0]==0 || p->inVerbatim ){
          if( cS1 ) z[iS1] = cS1;
          if( zClose[0]!=']' ){
            blob_appendf(p->pOut, "[%h]%s", zTarget, zClose);
          }else{
            blob_appendf(p->pOut, "%h%s", zTarget, zClose);
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
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
          }
        }else

        /* Render invalid markup literally.  The markup appears in the
        ** final output as plain text.
        */
        if( markup.iCode==MARKUP_INVALID ){

          unparseMarkup(&markup);
          startAutoParagraph(p);






          blob_append_string(p->pOut, "&lt;");
          n = 1;


        }else

        /* If the markup is not font-change markup ignore it if the
        ** font-change-only flag is set.
        */
        if( (markup.iType&MUTYPE_FONT)==0 && (p->state & FONT_MARKUP_ONLY)!=0 ){
          /* Do nothing */
        }else

        if( markup.iCode==MARKUP_NOWIKI ){
          if( markup.endTag ){
            p->state |= ALLOW_WIKI;
          }else{
            p->state &= ~ALLOW_WIKI;
          }
        }else

        /* Ignore block markup for in-line rendering.
        */
        if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
          /* Do nothing */
        }else

        /* Generate end-tags */
        if( markup.endTag ){
          popStackToTag(p, markup.iCode);
        }else

        /* Push <div> markup onto the stack together with the id=ID attribute.
        */







>


>
>
>
>
>
>
|
<
>
>

















<
<
<
<
<
<







1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779

1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798






1799
1800
1801
1802
1803
1804
1805
          }
        }else

        /* Render invalid markup literally.  The markup appears in the
        ** final output as plain text.
        */
        if( markup.iCode==MARKUP_INVALID ){
          p->mRender |= RENDER_BADTAG;
          unparseMarkup(&markup);
          startAutoParagraph(p);
          if( p->state & WIKI_MARK ){
            p->mRender |= RENDER_MARK;
            blob_append_string(p->pOut, "<mark>");
            htmlize_to_blob(p->pOut, z, n);
            blob_append_string(p->pOut, "</mark>");
          }else{
            blob_append_string(p->pOut, "&lt;");

            htmlize_to_blob(p->pOut, z+1, n-1);
          }
        }else

        /* If the markup is not font-change markup ignore it if the
        ** font-change-only flag is set.
        */
        if( (markup.iType&MUTYPE_FONT)==0 && (p->state & FONT_MARKUP_ONLY)!=0 ){
          /* Do nothing */
        }else

        if( markup.iCode==MARKUP_NOWIKI ){
          if( markup.endTag ){
            p->state |= ALLOW_WIKI;
          }else{
            p->state &= ~ALLOW_WIKI;
          }
        }else







        /* Generate end-tags */
        if( markup.endTag ){
          popStackToTag(p, markup.iCode);
        }else

        /* Push <div> markup onto the stack together with the id=ID attribute.
        */
1797
1798
1799
1800
1801
1802
1803

1804
1805
1806
1807
1808
1809
1810
            pushStack(p, markup.iCode);
          }
        }else
        {
          if( markup.iType==MUTYPE_FONT ){
            startAutoParagraph(p);
          }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){

            p->wantAutoParagraph = 0;
          }
          if(   markup.iCode==MARKUP_HR
             || markup.iCode==MARKUP_H1
             || markup.iCode==MARKUP_H2
             || markup.iCode==MARKUP_H3
             || markup.iCode==MARKUP_H4







>







1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
            pushStack(p, markup.iCode);
          }
        }else
        {
          if( markup.iType==MUTYPE_FONT ){
            startAutoParagraph(p);
          }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
            p->mRender |= RENDER_BLOCKTAG;
            p->wantAutoParagraph = 0;
          }
          if(   markup.iCode==MARKUP_HR
             || markup.iCode==MARKUP_H1
             || markup.iCode==MARKUP_H2
             || markup.iCode==MARKUP_H3
             || markup.iCode==MARKUP_H4
1827
1828
1829
1830
1831
1832
1833


1834
1835
1836
1837
1838
1839
1840
1841
1842

/*
** Transform the text in the pIn blob.  Write the results
** into the pOut blob.  The pOut blob should already be
** initialized.  The output is merely appended to pOut.
** If pOut is NULL, then the output is appended to the CGI
** reply.


*/
void wiki_convert(Blob *pIn, Blob *pOut, int flags){
  Renderer renderer;

  memset(&renderer, 0, sizeof(renderer));
  renderer.renderFlags = flags;
  renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
  if( flags & WIKI_INLINE ){
    renderer.wantAutoParagraph = 0;







>
>

|







1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925

/*
** Transform the text in the pIn blob.  Write the results
** into the pOut blob.  The pOut blob should already be
** initialized.  The output is merely appended to pOut.
** If pOut is NULL, then the output is appended to the CGI
** reply.
**
** Return a mask of RENDER_ flags indicating what happened.
*/
int wiki_convert(Blob *pIn, Blob *pOut, int flags){
  Renderer renderer;

  memset(&renderer, 0, sizeof(renderer));
  renderer.renderFlags = flags;
  renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
  if( flags & WIKI_INLINE ){
    renderer.wantAutoParagraph = 0;
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
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
1931
1932
1933
1934







1935
1936
1937
1938
1939
1940
1941
  wiki_render(&renderer, blob_str(pIn));
  endAutoParagraph(&renderer);
  while( renderer.nStack ){
    popStack(&renderer);
  }
  blob_append_char(renderer.pOut, '\n');
  free(renderer.aStack);

}

/*
** COMMAND: test-wiki-render
**
** Usage: %fossil test-wiki-render FILE [OPTIONS]
**
** Translate the input FILE from Fossil-wiki into HTML and write
** the resulting HTML on standard output.
**
** Options:
**    --buttons        Set the WIKI_BUTTONS flag


**    --htmlonly       Set the WIKI_HTMLONLY flag

**    --linksonly      Set the WIKI_LINKSONLY flag


**    --nobadlinks     Set the WIKI_NOBADLINKS flag
**    --inline         Set the WIKI_INLINE flag
**    --noblock        Set the WIKI_NOBLOCK flag
**    --dark-pikchr    Render pikchrs in dark mode
*/
void test_wiki_render(void){
  Blob in, out;
  int flags = 0;





  if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
  if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
  if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
  if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
  if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
  if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
  if( find_option("dark-pikchr",0,0)!=0 ){
    pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE );
  }




  db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
  verify_all_options();
  if( g.argc!=3 ) usage("FILE");
  blob_zero(&out);



  blob_read_from_file(&in, g.argv[2], ExtFILE);

  wiki_convert(&in, &out, flags);















  blob_write_to_file(&out, "-");

















}

/*
** COMMAND: test-markdown-render
**
** Usage: %fossil test-markdown-render FILE ...
**
** Render markdown in FILE as HTML on stdout.
** Options:
**
**    --safe            Restrict the output to use only "safe" HTML
**    --lint-footnotes  Print stats for footnotes-related issues
**    --dark-pikchr     Render pikchrs in dark mode

*/
void test_markdown_render(void){
  Blob in, out;
  int i;
  int bSafe = 0, bFnLint = 0;
  db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
  bSafe = find_option("safe",0,0)!=0;
  bFnLint = find_option("lint-footnotes",0,0)!=0;
  if( find_option("dark-pikchr",0,0)!=0 ){
    pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE );
  }

  verify_all_options();
  for(i=2; i<g.argc; i++){
    blob_zero(&out);
    blob_read_from_file(&in, g.argv[i], ExtFILE);
    if( g.argc>3 ){
      fossil_print("<!------ %h ------->\n", g.argv[i]);
    }
    markdown_to_html(&in, 0, &out);
    safe_html_context( bSafe ? DOCSRC_UNTRUSTED : DOCSRC_TRUSTED );
    safe_html(&out);







    blob_write_to_file(&out, "-");
    blob_reset(&in);
    blob_reset(&out);
  }
  if( bFnLint && (g.ftntsIssues[0] || g.ftntsIssues[1]
      || g.ftntsIssues[2] || g.ftntsIssues[3] )){
    fossil_fatal("There were issues with footnotes:\n"







>












>
>

>

>
>

|
|
<




>
>
>
>
>





|



>
>
>
>


|

>
>
>
|
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>










|

|
>




|






>










>
>
>
>
>
>
>







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
1965
1966
1967
1968

1969
1970
1971
1972
1973
1974
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
2014
2015
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
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
  wiki_render(&renderer, blob_str(pIn));
  endAutoParagraph(&renderer);
  while( renderer.nStack ){
    popStack(&renderer);
  }
  blob_append_char(renderer.pOut, '\n');
  free(renderer.aStack);
  return renderer.mRender;
}

/*
** COMMAND: test-wiki-render
**
** Usage: %fossil test-wiki-render FILE [OPTIONS]
**
** Translate the input FILE from Fossil-wiki into HTML and write
** the resulting HTML on standard output.
**
** Options:
**    --buttons        Set the WIKI_BUTTONS flag
**    --dark-pikchr    Render pikchrs in dark mode
**    --flow           Render as text using comment_format
**    --htmlonly       Set the WIKI_HTMLONLY flag
**    --inline         Set the WIKI_INLINE flag
**    --linksonly      Set the WIKI_LINKSONLY flag
**    -m TEXT          Use TEXT in place of the content of FILE
**    --mark           Add <mark>...</mark> around problems
**    --nobadlinks     Set the WIKI_NOBADLINKS flag
**    --text           Run the output through html_to_plaintext()
**    --type           Break down the return code from wiki_convert()

*/
void test_wiki_render(void){
  Blob in, out;
  int flags = 0;
  int bText;
  int bFlow = 0;
  int showType = 0;
  int mType;
  const char *zIn;
  if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
  if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
  if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
  if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
  if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
  if( find_option("mark",0,0)!=0 ) flags |= WIKI_MARK;
  if( find_option("dark-pikchr",0,0)!=0 ){
    pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE );
  }
  bText = find_option("text",0,0)!=0;
  bFlow = find_option("flow",0,0)!=0;
  showType = find_option("type",0,0)!=0;
  zIn = find_option("msg","m",1);
  db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
  verify_all_options();
  if( (zIn==0 && g.argc!=3) || (zIn!=0 && g.argc!=2) ) usage("FILE");
  blob_zero(&out);
  if( zIn ){
    blob_init(&in, zIn, -1);
  }else{
    blob_read_from_file(&in, g.argv[2], ExtFILE);
  }
  mType = wiki_convert(&in, &out, flags);
  if( bText ){
    Blob txt;
    int htot = HTOT_TRIM;
    if( terminal_is_vt100() ) htot |= HTOT_VT100;
    if( bFlow ) htot |= HTOT_FLOW;
    blob_init(&txt, 0, 0);
    html_to_plaintext(blob_str(&out),&txt, htot);
    blob_reset(&out);
    out = txt;
  }
  if( bFlow ){
    fossil_print("   ");
    comment_print(blob_str(&out), 0, 3, terminal_get_width(80)-3,
                  get_comment_format());
  }else{
    blob_write_to_file(&out, "-");
  }
  if( showType ){
    fossil_print("%.*c\nResult Codes:", terminal_get_width(80)-1, '*');
    if( mType & RENDER_LINK )      fossil_print(" LINK");
    if( mType & RENDER_ENTITY )    fossil_print(" ENTITY");
    if( mType & RENDER_TAG )       fossil_print(" TAG");
    if( mType & RENDER_BLOCKTAG )  fossil_print(" BLOCKTAG");
    if( mType & RENDER_BLOCK )     fossil_print(" BLOCK");
    if( mType & RENDER_MARK )      fossil_print(" MARK");
    if( mType & RENDER_BADLINK )   fossil_print(" BADLINK");
    if( mType & RENDER_BADTARGET ) fossil_print(" BADTARGET");
    if( mType & RENDER_BADTAG )    fossil_print(" BADTAG");
    if( mType & RENDER_BADENTITY ) fossil_print(" BADENTITY");
    if( mType & RENDER_BADHTML )   fossil_print(" BADHTML");
    if( mType & RENDER_ERROR )     fossil_print(" ERROR");
    fossil_print("\n");
  }
}

/*
** COMMAND: test-markdown-render
**
** Usage: %fossil test-markdown-render FILE ...
**
** Render markdown in FILE as HTML on stdout.
** Options:
**
**    --dark-pikchr     Render pikchrs in dark mode
**    --lint-footnotes  Print stats for footnotes-related issues
**    --safe            Restrict the output to use only "safe" HTML
**    --text            Run the output through html_to_plaintext().
*/
void test_markdown_render(void){
  Blob in, out;
  int i;
  int bSafe = 0, bFnLint = 0, bText = 0;
  db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
  bSafe = find_option("safe",0,0)!=0;
  bFnLint = find_option("lint-footnotes",0,0)!=0;
  if( find_option("dark-pikchr",0,0)!=0 ){
    pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE );
  }
  bText = find_option("text",0,0)!=0;
  verify_all_options();
  for(i=2; i<g.argc; i++){
    blob_zero(&out);
    blob_read_from_file(&in, g.argv[i], ExtFILE);
    if( g.argc>3 ){
      fossil_print("<!------ %h ------->\n", g.argv[i]);
    }
    markdown_to_html(&in, 0, &out);
    safe_html_context( bSafe ? DOCSRC_UNTRUSTED : DOCSRC_TRUSTED );
    safe_html(&out);
    if( bText ){
      Blob txt;
      blob_init(&txt, 0, 0);
      html_to_plaintext(blob_str(&out), &txt, HTOT_VT100);
      blob_reset(&out);
      out = txt;
    }
    blob_write_to_file(&out, "-");
    blob_reset(&in);
    blob_reset(&out);
  }
  if( bFnLint && (g.ftntsIssues[0] || g.ftntsIssues[1]
      || g.ftntsIssues[2] || g.ftntsIssues[3] )){
    fossil_fatal("There were issues with footnotes:\n"
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
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
**
**       [target]
**       [target|...]
**
** Where "target" can be either an artifact ID prefix or a wiki page
** name.  For each such hyperlink found, add an entry to the
** backlink table.


*/
void wiki_extract_links(
  char *z,           /* The wiki text from which to extract links */
  Backlink *pBklnk,  /* Backlink extraction context */
  int flags          /* wiki parsing flags */
){
  Renderer renderer;
  int tokenType;
  ParsedMarkup markup;
  int n;
  int inlineOnly;
  int wikiHtmlOnly = 0;

  memset(&renderer, 0, sizeof(renderer));
  renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
  if( flags & WIKI_NOBLOCK ){
    renderer.state |= INLINE_MARKUP_ONLY;
  }
  if( wikiUsesHtml() ){
    renderer.state |= WIKI_HTMLONLY;
    wikiHtmlOnly = 1;
  }
  inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;

  while( z[0] ){
    if( wikiHtmlOnly ){
      n = nextRawToken(z, &renderer, &tokenType);
    }else{
      n = nextWikiToken(z, &renderer, &tokenType);
    }







>
>

|








<




<
<
<




<







2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148

2149
2150
2151
2152



2153
2154
2155
2156

2157
2158
2159
2160
2161
2162
2163
**
**       [target]
**       [target|...]
**
** Where "target" can be either an artifact ID prefix or a wiki page
** name.  For each such hyperlink found, add an entry to the
** backlink table.
**
** The return value is a mask of RENDER_ flags.
*/
int wiki_extract_links(
  char *z,           /* The wiki text from which to extract links */
  Backlink *pBklnk,  /* Backlink extraction context */
  int flags          /* wiki parsing flags */
){
  Renderer renderer;
  int tokenType;
  ParsedMarkup markup;
  int n;

  int wikiHtmlOnly = 0;

  memset(&renderer, 0, sizeof(renderer));
  renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;



  if( wikiUsesHtml() ){
    renderer.state |= WIKI_HTMLONLY;
    wikiHtmlOnly = 1;
  }


  while( z[0] ){
    if( wikiHtmlOnly ){
      n = nextRawToken(z, &renderer, &tokenType);
    }else{
      n = nextWikiToken(z, &renderer, &tokenType);
    }
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
          if( markup.endTag ){
            renderer.state |= ALLOW_WIKI;
          }else{
            renderer.state &= ~ALLOW_WIKI;
          }
        }else

        /* Ignore block markup for in-line rendering.
        */
        if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
          /* Do nothing */
        }else

        /* Generate end-tags */
        if( markup.endTag ){
          popStackToTag(&renderer, markup.iCode);
        }else

        /* Push <div> markup onto the stack together with the id=ID attribute.
        */







<
<
<
<
<
<







2230
2231
2232
2233
2234
2235
2236






2237
2238
2239
2240
2241
2242
2243
          if( markup.endTag ){
            renderer.state |= ALLOW_WIKI;
          }else{
            renderer.state &= ~ALLOW_WIKI;
          }
        }else







        /* Generate end-tags */
        if( markup.endTag ){
          popStackToTag(&renderer, markup.iCode);
        }else

        /* Push <div> markup onto the stack together with the id=ID attribute.
        */
2139
2140
2141
2142
2143
2144
2145

2146
2147
2148
2149
2150
2151
2152
      default: {
        break;
      }
    }
    z += n;
  }
  free(renderer.aStack);

}

/*
** Return the length, in bytes, of the HTML token that z is pointing to.
*/
int html_token_length(const char *z){
  int n;







>







2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
      default: {
        break;
      }
    }
    z += n;
  }
  free(renderer.aStack);
  return renderer.mRender;
}

/*
** Return the length, in bytes, of the HTML token that z is pointing to.
*/
int html_token_length(const char *z){
  int n;
2391
2392
2393
2394
2395
2396
2397






















2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413





2414
2415
2416
2417
2418
2419
2420

2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437











2438
2439



2440

2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452

2453
2454
2455

2456
2457
2458
2459
2460

2461
2462
2463
2464
2465
2466
2467
2468
2469

2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481


2482



2483
2484
2485
2486


2487
2488
2489
2490
2491
2492
2493
2494
2495
2496

2497

2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510





2511
2512
2513
2514


2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
    blob_zero(&out);
    htmlTidy(blob_str(&in), &out);
    blob_reset(&in);
    fossil_puts(blob_buffer(&out), 0, blob_size(&out));
    blob_reset(&out);
  }
}























/*
** Remove all HTML markup from the input text.  The output written into
** pOut is pure text.
**
** Put the title on the first line, if there is any <title> markup.
** If there is no <title>, then create a blank first line.
*/
void html_to_plaintext(const char *zIn, Blob *pOut){
  int n;
  int i, j;
  int inTitle = 0;          /* True between <title>...</title> */
  int seenText = 0;         /* True after first non-whitespace seen */
  int nNL = 0;              /* Number of \n characters at the end of pOut */
  int nWS = 0;              /* True if pOut ends with whitespace */
  while( fossil_isspace(zIn[0]) ) zIn++;





  while( zIn[0] ){
    n = html_token_length(zIn);
    if( zIn[0]=='<' && n>1 ){
      int isCloseTag;
      int eTag;
      int eType;
      char zTag[32];

      isCloseTag = zIn[1]=='/';
      for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){
         zTag[i] = fossil_tolower(zIn[j]);
      }
      zTag[i] = 0;
      eTag = findTag(zTag);
      eType = aMarkup[eTag].iType;
      if( eTag==MARKUP_INVALID && fossil_strnicmp(zIn,"<style",6)==0 ){
        zIn += n;
        while( zIn[0] ){
          n = html_token_length(zIn);
          if( fossil_strnicmp(zIn, "</style",7)==0 ) break;
          zIn += n;
        }
        if( zIn[0]=='<' ) zIn += n;
        continue;
      }











      if( eTag==MARKUP_TITLE ){
        inTitle = !isCloseTag;



      }

      if( !isCloseTag && seenText && (eType & (MUTYPE_BLOCK|MUTYPE_TABLE))!=0 ){
        if( nNL==0 ){
          blob_append_char(pOut, '\n');
          nNL++;
        }
        nWS = 1;
      }
    }else if( fossil_isspace(zIn[0]) ){
      if( seenText ){
        nNL = 0;
        if( !inTitle ){ /* '\n' -> ' ' within <title> */
          for(i=0; i<n; i++) if( zIn[i]=='\n' ) nNL++;

        }
        if( !nWS ){
          blob_append_char(pOut, nNL ? '\n' : ' ');

          nWS = 1;
        }
      }
    }else if( zIn[0]=='&' ){
      char c = '?';

      if( zIn[1]=='#' ){
        int x = atoi(&zIn[1]);
        if( x>0 && x<=127 ) c = x;
      }else{
        static const struct { int n; char c; char *z; } aEntity[] = {
           { 5, '&', "&amp;"   },
           { 4, '<', "&lt;"    },
           { 4, '>', "&gt;"    },
           { 6, ' ', "&nbsp;"  },

        };
        int jj;
        for(jj=0; jj<count(aEntity); jj++){
          if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){
            c = aEntity[jj].c;
            break;
          }
        }
      }
      if( fossil_isspace(c) ){
        if( nWS==0 && seenText ) blob_append_char(pOut, c);
        nWS = 1;


        nNL = c=='\n';



      }else{
        if( !seenText && !inTitle ) blob_append_char(pOut, '\n');
        seenText = 1;
        nNL = nWS = 0;


        blob_append_char(pOut, c);
      }
    }else{
      if( !seenText && !inTitle ) blob_append_char(pOut, '\n');
      seenText = 1;
      nNL = nWS = 0;
      blob_append(pOut, zIn, n);
    }
    zIn += n;
  }

  if( nNL==0 ) blob_append_char(pOut, '\n');

}

/*
** COMMAND: test-html-to-text
**
** Usage: %fossil test-html-to-text FILE ...
**
** Read all files named on the command-line.  Convert the file
** content from HTML to text and write the results on standard
** output.
**
** This command is intended as a test and debug interface for
** the html_to_plaintext() routine.





*/
void test_html_to_text(void){
  Blob in, out;
  int i;



  for(i=2; i<g.argc; i++){
    blob_read_from_file(&in, g.argv[i], ExtFILE);
    blob_zero(&out);
    html_to_plaintext(blob_str(&in), &out);
    blob_reset(&in);
    fossil_puts(blob_buffer(&out), 0, blob_size(&out));
    blob_reset(&out);
  }
}

/****************************************************************************







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








|


|
|
|
|
|
>
>
>
>
>







>

















>
>
>
>
>
>
>
>
>
>
>

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

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

|
>

|
|

|




>









|
|
|
>
>
|
>
>
>

|
<
<
>
>
|


<
<
|




>
|
>





|







>
>
>
>
>




>
>




|







2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618

2619

2620


2621
2622


2623
2624
2625
2626
2627
2628
2629
2630

2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663


2664
2665
2666
2667
2668


2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
    blob_zero(&out);
    htmlTidy(blob_str(&in), &out);
    blob_reset(&in);
    fossil_puts(blob_buffer(&out), 0, blob_size(&out));
    blob_reset(&out);
  }
}

#if INTERFACE
/*
** Allowed flag options for html_to_plaintext().
*/
#define HTOT_VT100   0x01 /* <mark> becomes ^[[91m */
#define HTOT_FLOW    0x02 /* Collapse internal whitespace to a single space */
#define HTOT_TRIM    0x04 /* Trim off leading and trailing whitespace */

#endif /* INTERFACE */

/*
** Add <MARK> or </MARK> to the output, or similar VT-100 escape
** codes.
*/
static void addMark(Blob *pOut, int mFlags, int isClose){
  static const char *az[4] = { "<MARK>", "</MARK>", "\033[91m", "\033[0m" };
  int i = 0;
  if( isClose ) i++;
  if( mFlags & HTOT_VT100 ) i += 2;
  blob_append(pOut, az[i], -1);
}

/*
** Remove all HTML markup from the input text.  The output written into
** pOut is pure text.
**
** Put the title on the first line, if there is any <title> markup.
** If there is no <title>, then create a blank first line.
*/
void html_to_plaintext(const char *zIn, Blob *pOut, int mFlags){
  int n;
  int i, j;
  int bFlow = 0;          /* Transform internal WS into a single space */
  int prevWS = 1;         /* Previous output was whitespace or start of msg */
  int nMark = 0;          /* True if inside of <mark>..</mark> */

  for(i=0; fossil_isspace(zIn[i]); i++){}
  if( i>0 && (mFlags & HTOT_TRIM)==0 ){
    blob_append(pOut, zIn, i);
  }
  zIn += i;
  if( mFlags & HTOT_FLOW ) bFlow = 1;
  while( zIn[0] ){
    n = html_token_length(zIn);
    if( zIn[0]=='<' && n>1 ){
      int isCloseTag;
      int eTag;
      int eType;
      char zTag[32];
      prevWS = 0;
      isCloseTag = zIn[1]=='/';
      for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){
         zTag[i] = fossil_tolower(zIn[j]);
      }
      zTag[i] = 0;
      eTag = findTag(zTag);
      eType = aMarkup[eTag].iType;
      if( eTag==MARKUP_INVALID && fossil_strnicmp(zIn,"<style",6)==0 ){
        zIn += n;
        while( zIn[0] ){
          n = html_token_length(zIn);
          if( fossil_strnicmp(zIn, "</style",7)==0 ) break;
          zIn += n;
        }
        if( zIn[0]=='<' ) zIn += n;
        continue;
      }
      if( eTag==MARKUP_INVALID && strcmp(zTag,"mark")==0 ){
        if( isCloseTag && nMark ){
          addMark(pOut, mFlags, 1);
          nMark = 0;
        }else if( !isCloseTag && !nMark ){
          addMark(pOut, mFlags, 0);
          nMark = 1;
        }
        zIn += n;
        continue;            
      }
      if( eTag==MARKUP_TITLE ){
        if( isCloseTag && (mFlags & HTOT_FLOW)==0 ){
          bFlow = 0;
        }else{
          bFlow = 1;
        }
      }
      if( !isCloseTag && (eType & (MUTYPE_BLOCK|MUTYPE_TABLE))!=0 ){

        blob_append_char(pOut, '\n');

      }


    }else if( fossil_isspace(zIn[0]) ){
      if( bFlow==0 ){


        if( zIn[n]==0 && (mFlags & HTOT_TRIM) ) break;
        blob_append(pOut, zIn, n);
      }else if( !prevWS ){
        prevWS = 1;
        blob_append_char(pOut, ' ');
        zIn += n;
        n = 0;
      }

    }else if( zIn[0]=='&' ){
      u32 c = '?';
      prevWS = 0;
      if( zIn[1]=='#' ){
        c = atoi(&zIn[2]);
        if( c==0 ) c = '?';
      }else{
        static const struct { int n; u32 c; char *z; } aEntity[] = {
           { 5, '&', "&amp;"   },
           { 4, '<', "&lt;"    },
           { 4, '>', "&gt;"    },
           { 6, ' ', "&nbsp;"  },
           { 6, '"', "&quot;"  },
        };
        int jj;
        for(jj=0; jj<count(aEntity); jj++){
          if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){
            c = aEntity[jj].c;
            break;
          }
        }
      }
      if( c<0x00080 ){
        blob_append_char(pOut, c & 0xff);
      }else if( c<0x00800 ){
        blob_append_char(pOut, 0xc0 + (u8)((c>>6)&0x1f));
        blob_append_char(pOut, 0x80 + (u8)(c&0x3f));
      }else if( c<0x10000 ){
        blob_append_char(pOut, 0xe0 + (u8)((c>>12)&0x0f));
        blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f));
        blob_append_char(pOut, 0x80 + (u8)(c&0x3f));
      }else{
        blob_append_char(pOut, 0xf0 + (u8)((c>>18)&0x07));


        blob_append_char(pOut, 0x80 + (u8)((c>>12)&0x3f));
        blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f));
        blob_append_char(pOut, 0x80 + (u8)(c&0x3f));
      }
    }else{


      prevWS = 0;
      blob_append(pOut, zIn, n);
    }
    zIn += n;
  }
  if( nMark ){
    addMark(pOut, mFlags, 1);
  }
}

/*
** COMMAND: test-html-to-text
**
** Usage: %fossil test-html-to-text [OPTIONS] FILE ...
**
** Read all files named on the command-line.  Convert the file
** content from HTML to text and write the results on standard
** output.
**
** This command is intended as a test and debug interface for
** the html_to_plaintext() routine.
**
** Options:
**
**     --vt100              Translate <mark> and </mark> into ANSI/VT100
**                          escapes to highlight the contained text.
*/
void test_html_to_text(void){
  Blob in, out;
  int i;
  int mFlags = 0;
  if( find_option("vt100",0,0)!=0 ) mFlags |= HTOT_VT100;

  for(i=2; i<g.argc; i++){
    blob_read_from_file(&in, g.argv[i], ExtFILE);
    blob_zero(&out);
    html_to_plaintext(blob_str(&in), &out, mFlags);
    blob_reset(&in);
    fossil_puts(blob_buffer(&out), 0, blob_size(&out));
    blob_reset(&out);
  }
}

/****************************************************************************
Changes to src/winhttp.c.
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
**
**         Creates a service. Available options include:
**
**         -D|--display DISPLAY-NAME
**
**              Sets the display name of the service. This name is shown
**              by graphical interface programs. By default, the display name
**              equals to the service name.
**
**         -S|--start TYPE
**
**              Sets the start type of the service. TYPE can be "manual",
**              which means you need to start the service yourself with the
**              'fossil winsrv start' command or with the "net start" command
**              from the operating system. If TYPE is set to "auto", the service







|







994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
**
**         Creates a service. Available options include:
**
**         -D|--display DISPLAY-NAME
**
**              Sets the display name of the service. This name is shown
**              by graphical interface programs. By default, the display name
**              is equal to the service name.
**
**         -S|--start TYPE
**
**              Sets the start type of the service. TYPE can be "manual",
**              which means you need to start the service yourself with the
**              'fossil winsrv start' command or with the "net start" command
**              from the operating system. If TYPE is set to "auto", the service
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
**              used.
**
**         -W|--password PASSWORD
**
**              Password for the user account.
**
**         The following options are more or less the same as for the "server"
**         command and influence the behaviour of the http server:
**
**         --baseurl URL
**
**              Use URL as the base (useful for reverse proxies)
**
**         -P|--port TCPPORT
**







|







1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
**              used.
**
**         -W|--password PASSWORD
**
**              Password for the user account.
**
**         The following options are more or less the same as for the "server"
**         command and influence the behavior of the http server:
**
**         --baseurl URL
**
**              Use URL as the base (useful for reverse proxies)
**
**         -P|--port TCPPORT
**
Changes to src/zip.c.
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
  nPrefix = blob_size(&filename);

  pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
  if( pManifest ){
    int flg, eflg = 0;
    char *zName = 0;
    zip_set_timedate(pManifest->rDate);
    flg = db_get_manifest_setting();
    if( flg ){
      /* eflg is the effective flags, taking include/exclude into account */
      if( (pInclude==0 || glob_match(pInclude, "manifest"))
       && !glob_match(pExclude, "manifest")
       && (flg & MFESTFLG_RAW) ){
        eflg |= MFESTFLG_RAW;
      }







|







650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
  nPrefix = blob_size(&filename);

  pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
  if( pManifest ){
    int flg, eflg = 0;
    char *zName = 0;
    zip_set_timedate(pManifest->rDate);
    flg = db_get_manifest_setting(blob_str(&hash));
    if( flg ){
      /* eflg is the effective flags, taking include/exclude into account */
      if( (pInclude==0 || glob_match(pInclude, "manifest"))
       && !glob_match(pExclude, "manifest")
       && (flg & MFESTFLG_RAW) ){
        eflg |= MFESTFLG_RAW;
      }
Deleted test/comment.test.
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
#
# Copyright (c) 2014 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/
#
############################################################################
#
# Test comment formatting and printing.
#

test_setup ""

###############################################################################

fossil test-comment-format "" ""
test comment-1 {$RESULT eq "\n(1 lines output)"}

###############################################################################

fossil test-comment-format --decode "" ""
test comment-2 {$RESULT eq "\n(1 lines output)"}

###############################################################################

fossil test-comment-format --width 26 " " "this is a short comment."
test comment-3 {$RESULT eq " this is a short comment.\n(1 lines output)"}

###############################################################################

fossil test-comment-format --width 26 --decode " " "this is a short comment."
test comment-4 {$RESULT eq " this is a short comment.\n(1 lines output)"}

###############################################################################

fossil test-comment-format --width 26 "*PREFIX* " "this is a short comment."
test comment-5 {$RESULT eq "*PREFIX* this is a short c\n         omment.\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 26 --decode "*PREFIX* " "this is a short comment."
test comment-6 {$RESULT eq "*PREFIX* this is a short c\n         omment.\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 26 "" "this\\sis\\sa\\sshort\\scomment."
test comment-7 {$RESULT eq "this\\sis\\sa\\sshort\\scommen\nt.\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 26 --decode "" "this\\sis\\sa\\sshort\\scomment."
test comment-8 {$RESULT eq "this is a short comment.\n(1 lines output)"}

###############################################################################

fossil test-comment-format --width 78 --decode --trimspace "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly."
test comment-9 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test is\n         working correctly.\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 78 --decode --trimspace "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly.  more text here describing the issue.\\nanother line here..................................................................................*"
test comment-10 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test is\n         working correctly.  more text here describing the issue.\n         another line here....................................................\n         ..............................*\n(4 lines output)"}

###############################################################################

fossil test-comment-format --width 78 "HH:MM:SS " "....................................................................................*"
test comment-11 {$RESULT eq "HH:MM:SS .....................................................................\n         ...............*\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 78 "HH:MM:SS " ".....................................................................*" 78
test comment-12 {$RESULT eq "HH:MM:SS .....................................................................\n         *\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 26 "*TEST* " "this\tis a test."
test comment-13 {$RESULT eq "*TEST* this\tis a te\n       st.\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 60 "*TEST* " "this is a test......................................................................................................................."
test comment-14 {$RESULT eq "*TEST* this is a test.......................................\n       .....................................................\n       ...........................\n(3 lines output)"}

###############################################################################

fossil test-comment-format --width 60 --wordbreak "*TEST* " "this is a test......................................................................................................................."
test comment-15 {$RESULT eq "*TEST* this is a\n       test.................................................\n       .....................................................\n       .................\n(4 lines output)"}

###############################################################################

fossil test-comment-format --width 60 "*TEST* " "this	is	a	test......................................................................................................................."
test comment-16 {$RESULT eq "*TEST* this	is	a\n       test.................................................\n       .....................................................\n       .................\n(4 lines output)"}

###############################################################################

fossil test-comment-format --width 60 --wordbreak "*TEST* " "this	is	a	test......................................................................................................................."
test comment-17 {$RESULT eq "*TEST* this	is	a\n       test.................................................\n       .....................................................\n       .................\n(4 lines output)"}

###############################################################################

fossil test-comment-format --width 60 "*TEST* " "one two three four five six seven eight nine ten eleven twelve"
test comment-18 {$RESULT eq "*TEST* one two three four five six seven eight nine ten elev\n       en twelve\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 60 --wordbreak "*TEST* " "one two three four five six seven eight nine ten eleven twelve"
test comment-19 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n       eleven twelve\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 60 "*TEST* " "one	two	three	four	five	six	seven	eight	nine	ten	eleven	twelve"
test comment-20 {$RESULT eq "*TEST* one	two	three	four	five\n       six	seven	eight	nine	ten\n       eleven	twelve\n(3 lines output)"}

###############################################################################

fossil test-comment-format --width 60 --wordbreak "*TEST* " "one	two	three	four	five	six	seven	eight	nine	ten	eleven	twelve"
test comment-21 {$RESULT eq "*TEST* one	two	three	four	five\n       six	seven	eight	nine	ten\n       eleven	twelve\n(3 lines output)"}

###############################################################################

fossil test-comment-format --legacy "" ""
test comment-22 {$RESULT eq "\n(1 lines output)"}

###############################################################################

fossil test-comment-format --legacy --decode "" ""
test comment-23 {$RESULT eq "\n(1 lines output)"}

###############################################################################

fossil test-comment-format --width 26 --legacy " " "this is a short comment."
test comment-24 {$RESULT eq " this is a short comment.\n(1 lines output)"}

###############################################################################

fossil test-comment-format --width 26 --legacy --decode " " "this is a short comment."
test comment-25 {$RESULT eq " this is a short comment.\n(1 lines output)"}

###############################################################################

fossil test-comment-format --width 25 --legacy "*PREFIX* " "this is a short comment."
test comment-26 {$RESULT eq "*PREFIX* this is a short\n         comment.\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 25 --legacy --decode "*PREFIX* " "this is a short comment."
test comment-27 {$RESULT eq "*PREFIX* this is a short\n         comment.\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 26 --legacy "" "this\\sis\\sa\\sshort\\scomment."
test comment-28 {$RESULT eq "this\\sis\\sa\\sshort\\scommen\nt.\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 26 --legacy --decode "" "this\\sis\\sa\\sshort\\scomment."
test comment-29 {$RESULT eq "this is a short comment.\n(1 lines output)"}

###############################################################################

fossil test-comment-format --width 78 --legacy --decode "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly."
test comment-30 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test\n         is working correctly.\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 78 --legacy --decode "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly.  more text here describing the issue.\\nanother line here..................................................................................*"
test comment-31 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test\n         is working correctly. more text here describing the issue. another\n         line\n         here.................................................................\n         .................*\n(5 lines output)"}

###############################################################################

fossil test-comment-format --width 78 --legacy "HH:MM:SS " "....................................................................................*"
test comment-32 {$RESULT eq "HH:MM:SS .....................................................................\n         ...............*\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 78 --legacy "HH:MM:SS " ".....................................................................*"
test comment-33 {$RESULT eq "HH:MM:SS .....................................................................\n         *\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 26 --legacy "*TEST* " "this\tis a test."
test comment-34 {$RESULT eq "*TEST* this is a test.\n(1 lines output)"}

###############################################################################

fossil test-comment-format --width 60 --legacy "*TEST* " "this is a test......................................................................................................................."
test comment-35 {$RESULT eq "*TEST* this is a\n       test.................................................\n       .....................................................\n       .................\n(4 lines output)"}

###############################################################################

fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "this is a test......................................................................................................................."
test comment-36 {$RESULT eq "*TEST* this is a\n       test.................................................\n       .....................................................\n       .................\n(4 lines output)"}

###############################################################################

fossil test-comment-format --width 60 --legacy "*TEST* " "this	is	a	test......................................................................................................................."
test comment-37 {$RESULT eq "*TEST* this is a\n       test.................................................\n       .....................................................\n       .................\n(4 lines output)"}

###############################################################################

fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "this	is	a	test......................................................................................................................."
test comment-38 {$RESULT eq "*TEST* this is a\n       test.................................................\n       .....................................................\n       .................\n(4 lines output)"}

###############################################################################

fossil test-comment-format --width 60 --legacy "*TEST* " "one two three four five six seven eight nine ten eleven twelve"
test comment-39 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n       eleven twelve\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "one two three four five six seven eight nine ten eleven twelve"
test comment-40 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n       eleven twelve\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 60 --legacy "*TEST* " "one	two	three	four	five	six	seven	eight	nine	ten	eleven	twelve"
test comment-41 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n       eleven twelve\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "one	two	three	four	five	six	seven	eight	nine	ten	eleven	twelve"
test comment-42 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n       eleven twelve\n(2 lines output)"}

###############################################################################

set orig "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\\nxxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\\nxxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\\nxxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\\nxxxxxxx."
fossil test-comment-format --width 73 --decode --origbreak "" $orig
test comment-43 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\nxxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\nxxxxxxx.\n(5 lines output)"}

###############################################################################

fossil test-comment-format --width 73 --decode --origbreak "" $orig $orig
test comment-44 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\nxxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\nxxxxxxx.\n(5 lines output)"}

###############################################################################

fossil test-comment-format --width 73 --decode --origbreak "" "00:00:00 \[0000000000\] *CURRENT* $orig" $orig
test comment-45 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \nxxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\nxxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\nxxxxxxx.\n(6 lines output)"}

###############################################################################

fossil test-comment-format --width 82 --indent 9 --decode --origbreak "         " $orig
test comment-46 {$RESULT eq "         xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n         xxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\n         xxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\n         xxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\n         xxxxxxx.\n(5 lines output)"}

###############################################################################

fossil test-comment-format --width 82 --indent 9 --decode --origbreak "         " $orig $orig
test comment-47 {$RESULT eq "         xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n         xxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\n         xxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\n         xxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\n         xxxxxxx.\n(5 lines output)"}

###############################################################################

fossil test-comment-format --width 82 --indent 9 --decode --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig
test comment-48 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n         xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n         xxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\n         xxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\n         xxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\n         xxxxxxx.\n(6 lines output)"}

###############################################################################

fossil test-comment-format --width 72 --decode --trimspace --origbreak "" $orig
test comment-49 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\nxxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\nxxxxxxx.\n(5 lines output)"}

###############################################################################

fossil test-comment-format --width 72 --decode --trimspace --origbreak "" $orig $orig
test comment-50 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\nxxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\nxxxxxxx.\n(5 lines output)"}

###############################################################################

fossil test-comment-format --width 72 --decode --trimspace --origbreak "" "00:00:00 \[0000000000\] *CURRENT* $orig" $orig
test comment-51 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \nxxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\nxxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\nxxxxxxx.\n(6 lines output)"}

###############################################################################

fossil test-comment-format --width 81 --indent 9 --decode --trimspace --origbreak "         " $orig
test comment-52 {$RESULT eq "         xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n         xxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\n         xxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\n         xxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\n         xxxxxxx.\n(5 lines output)"}

###############################################################################

fossil test-comment-format --width 81 --indent 9 --decode --trimspace --origbreak "         " $orig $orig
test comment-53 {$RESULT eq "         xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n         xxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\n         xxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\n         xxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\n         xxxxxxx.\n(5 lines output)"}

###############################################################################

fossil test-comment-format --width 81 --indent 9 --decode --trimspace --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig
test comment-54 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n         xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n         xxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\n         xxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\n         xxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\n         xxxxxxx.\n(6 lines output)"}

###############################################################################

fossil test-comment-format --width 72 --decode --trimcrlf --origbreak "" $orig
test comment-55 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\nxxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\nxxxxxxx.\n(5 lines output)"}

###############################################################################

fossil test-comment-format --width 72 --decode --trimcrlf --origbreak "" $orig $orig
test comment-56 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\nxxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\nxxxxxxx.\n(5 lines output)"}

###############################################################################

fossil test-comment-format --width 72 --decode --trimcrlf --origbreak "" "00:00:00 \[0000000000\] *CURRENT* $orig" $orig
test comment-57 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \nxxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\nxxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\nxxxxxxx.\n(6 lines output)"}

###############################################################################

fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak "         " $orig
test comment-58 {$RESULT eq "         xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n         xxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\n         xxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\n         xxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\n         xxxxxxx.\n(5 lines output)"}

###############################################################################

fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak "         " $orig $orig
test comment-59 {$RESULT eq "         xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n         xxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\n         xxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\n         xxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\n         xxxxxxx.\n(5 lines output)"}

###############################################################################

fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig
test comment-60 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n         xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n         xxxxxxx xxxxx xxxx  xxxx xx xxxxxxx xxxxxxx (xxxxxx  xxxxxxxxx x xxxxx).\n         xxx'x xxx xxx xx  xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx  xx xx xxxx. x\n         xxxxx  x xxxxxx  xxxx xxxx  xxxx xxxx  xxxx x  xxxxx xx  xxx x  xxxxxxxx\n         xxxxxxx.\n(6 lines output)"}

###############################################################################

fossil test-comment-format --width 72 --file "" [file join $testdir "utf8-comment.txt"]
test comment-61 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\] an\nd symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 72 --wordbreak --file "" [file join $testdir "utf8-comment.txt"]
test comment-62 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 72 --legacy --file "" [file join $testdir "utf8-comment.txt"]
test comment-63 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"}

###############################################################################

fossil test-comment-format --width 72 --legacy --wordbreak --file "" [file join $testdir "utf8-comment.txt"]
test comment-64 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"}

###############################################################################

test_cleanup
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















































































































































































































































































































































































































































































































































































































































































































Changes to tools/makemake.tcl.
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579

580
581
582
583
584
585
586
writeln [string map [list <<<NEXT_LINE>>> \\] {
$(OBJDIR)/pikchr.o:	$(SRCDIR_extsrc)/pikchr.c
	$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@

$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c
	$(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@

$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c
	$(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry <<<NEXT_LINE>>>
        -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore <<<NEXT_LINE>>>
        -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c <<<NEXT_LINE>>>
        -sENVIRONMENT=web <<<NEXT_LINE>>>
        -sMODULARIZE <<<NEXT_LINE>>>
        -sEXPORT_NAME=initPikchrModule <<<NEXT_LINE>>>
        --minify 0

	@chmod -x $(SRCDIR_extsrc)/pikchr.wasm
wasm: $(SRCDIR_extsrc)/pikchr.js

#
# compile_commands.json support...
#
# We have to avoid applying compile_commands support to the in-tree







|

|
|




>







565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
writeln [string map [list <<<NEXT_LINE>>> \\] {
$(OBJDIR)/pikchr.o:	$(SRCDIR_extsrc)/pikchr.c
	$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@

$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c
	$(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@

$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST)
	$(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry <<<NEXT_LINE>>>
        -sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore <<<NEXT_LINE>>>
        -sEXPORTED_FUNCTIONS=_pikchr,_pikchr_version $(SRCDIR_extsrc)/pikchr.c <<<NEXT_LINE>>>
        -sENVIRONMENT=web <<<NEXT_LINE>>>
        -sMODULARIZE <<<NEXT_LINE>>>
        -sEXPORT_NAME=initPikchrModule <<<NEXT_LINE>>>
        --minify 0
	$(TCLSH) $(TOPDIR)/tools/randomize-js-names.tcl $(SRCDIR_extsrc)
	@chmod -x $(SRCDIR_extsrc)/pikchr.wasm
wasm: $(SRCDIR_extsrc)/pikchr.js

#
# compile_commands.json support...
#
# We have to avoid applying compile_commands support to the in-tree
Changes to tools/mkindex.c.
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
#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 */
#define CMDFLAG_SETTING      0x0020     /* A setting */
#define CMDFLAG_VERSIONABLE  0x0040     /* A versionable setting */
#define CMDFLAG_BLOCKTEXT    0x0080     /* Multi-line text setting */
#define CMDFLAG_BOOLEAN      0x0100     /* A boolean setting */
#define CMDFLAG_RAWCONTENT   0x0200     /* Do not interpret webpage content */
#define CMDFLAG_SENSITIVE    0x0400     /* Security-sensitive setting */
#define CMDFLAG_HIDDEN       0x0800     /* Elide from most listings */
#define CMDFLAG_LDAVG_EXEMPT 0x1000     /* Exempt from load_control() */
#define CMDFLAG_ALIAS        0x2000     /* Command aliases */
#define CMDFLAG_KEEPEMPTY    0x4000     /* Do not unset empty settings */

/**************************************************************************/

/*
** Each entry looks like this:
*/
typedef struct Entry {
  int eType;        /* CMDFLAG_* values */







|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>







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
#include <assert.h>
#include <string.h>

/***************************************************************************
** These macros must match similar macros in dispatch.c.
**
** Allowed values for CmdOrPage.eCmdFlags. */
#define CMDFLAG_1ST_TIER     0x00001    /* Most important commands */
#define CMDFLAG_2ND_TIER     0x00002    /* Obscure and seldom used commands */
#define CMDFLAG_TEST         0x00004    /* Commands for testing only */
#define CMDFLAG_WEBPAGE      0x00008    /* Web pages */
#define CMDFLAG_COMMAND      0x00010    /* A command */
#define CMDFLAG_SETTING      0x00020    /* A setting */
#define CMDFLAG_VERSIONABLE  0x00040    /* A versionable setting */
#define CMDFLAG_BLOCKTEXT    0x00080    /* Multi-line text setting */
#define CMDFLAG_BOOLEAN      0x00100    /* A boolean setting */
#define CMDFLAG_RAWCONTENT   0x00200    /* Do not interpret webpage content */
#define CMDFLAG_SENSITIVE    0x00400    /* Security-sensitive setting */
#define CMDFLAG_HIDDEN       0x00800    /* Elide from most listings */
#define CMDFLAG_LDAVG_EXEMPT 0x01000    /* Exempt from load_control() */
#define CMDFLAG_ALIAS        0x02000    /* Command aliases */
#define CMDFLAG_KEEPEMPTY    0x04000    /* Do not unset empty settings */
#define CMDFLAG_ABBREVSUBCMD 0x08000    /* Abbreviated subcmd in help text */
/**************************************************************************/

/*
** Each entry looks like this:
*/
typedef struct Entry {
  int eType;        /* CMDFLAG_* values */
278
279
280
281
282
283
284



285
286
287
288
289
290
291
      aEntry[nUsed].zDflt = string_dup(&zLine[i+8], j-8);
    }else if( j>9 && strncmp(&zLine[i], "variable=", 9)==0 ){
      aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9);
    }else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){
      aEntry[nUsed].eType |= CMDFLAG_HIDDEN;
    }else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){
      aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT;



    }else{
      fprintf(stderr, "%s:%d: unknown option: '%.*s'\n",
              zFile, nLine, j, &zLine[i]);
      nErr++;
    }
  }








>
>
>







279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
      aEntry[nUsed].zDflt = string_dup(&zLine[i+8], j-8);
    }else if( j>9 && strncmp(&zLine[i], "variable=", 9)==0 ){
      aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9);
    }else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){
      aEntry[nUsed].eType |= CMDFLAG_HIDDEN;
    }else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){
      aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT;
    }else if( (j==23 && strncmp(&zLine[i], "abbreviated-subcommands", 23)==0)
           || (j==12 && strncmp(&zLine[i], "abbrv-subcom", 12)==0) ){
      aEntry[nUsed].eType |= CMDFLAG_ABBREVSUBCMD;
    }else{
      fprintf(stderr, "%s:%d: unknown option: '%.*s'\n",
              zFile, nLine, j, &zLine[i]);
      nErr++;
    }
  }

Added tools/randomize-js-names.tcl.








































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#!/usr/bin/tclsh
#
# This script is run as part of "make wasm".  After emcc has
# run to generate extsrc/pikchr.wasm and extsrc/pikchr.js from
# extsrc/pikchr.c, we need to make changes to these filenames to
# work around caching problems.
#
#    (1)  in extsrc/pikchr.js ->  change "pikchr.wasm" into
#         "pikchr-vNNNNNNNN.wasm" where Ns are random digits.
#
#    (2)  in extsrc/pikchr-worker.js -> change "pikchr-vNNNNNNNN.js"
#         by altering the random digits N.
#
set DIR extsrc
if {[llength $argv]>0} {
  set DIR [lindex $argv 0]
}

set R [expr {int(rand()*10000000000)+1000000000}]
set in [open $DIR/pikchr.js rb]
set f1 [read $in]
close $in
set f1mod [regsub {\ypikchr(-v\d+)?\.wasm\y} $f1 "pikchr-v$R.wasm"]
set out [open $DIR/pikchr.js wb]
puts -nonewline $out $f1mod
close $out
puts "modified $DIR/pikchr.js to reference \"pikchr-v$R.wasm\""

set in [open $DIR/pikchr-worker.js rb]
set f1 [read $in]
close $in
set f1mod [regsub {\ypikchr(-v\d+)?\.js\y} $f1 "pikchr-v$R.js"]
set out [open $DIR/pikchr-worker.js wb]
puts -nonewline $out $f1mod
close $out
puts "modified $DIR/pikchr-worker.js to reference \"pikchr-v$R.js\""
Added win/build32.bat.






>
>
>
1
2
3
REM Based on /wiki/Release%20Build%20How-To
nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_ENABLE_WINXP=1 OPTIMIZATIONS=4 clean fossil.exe
dumpbin /dependents fossil.exe
Added win/build64.bat.






>
>
>
1
2
3
REM Based on /wiki/Release%20Build%20How-To
nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 OPTIMIZATIONS=4 clean fossil.exe
dumpbin /dependents fossil.exe
Changes to www/alerts.md.
1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
# Email Alerts

## Overview

Beginning with version 2.7, Fossil can send email messages to
subscribers to alert them to changes in the repository:

  *  New [checkins](/help?cmd=ci)
  *  [Ticket](./tickets.wiki) changes
  *  [Wiki](./wikitheory.wiki) page changes
  *  New and edited [forum](./forum.wiki) posts

  *  Announcements

Subscribers can elect to receive emails as soon as these events happen,
or they can receive a daily digest of the events instead.

Email alerts are sent by a [Fossil server](./server/), which must be
[set up](#quick) by the Fossil administrator to send email.











>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Email Alerts

## Overview

Beginning with version 2.7, Fossil can send email messages to
subscribers to alert them to changes in the repository:

  *  New [checkins](/help?cmd=ci)
  *  [Ticket](./tickets.wiki) changes
  *  [Wiki](./wikitheory.wiki) page changes
  *  New and edited [forum](./forum.wiki) posts
  *  Users receiving [new permissions](./caps/index.md) (admins only)
  *  Announcements

Subscribers can elect to receive emails as soon as these events happen,
or they can receive a daily digest of the events instead.

Email alerts are sent by a [Fossil server](./server/), which must be
[set up](#quick) by the Fossil administrator to send email.
513
514
515
516
517
518
519




520
521
522
523
524
525
526

*  The Verified checkbox is initially unchecked for subscriber-only
   email addresses until the user clicks the link in the verification
   email. This checkbox lets the Fossil Admin user manually verify the
   user, such as in the case where the verification email message got
   lost.  Unchecking this box does not cause another verification email
   to be sent.





This screen also allows a Fossil Admin user to perform other activities
on behalf of a subscriber which they could do themselves, such as to
[unsubscribe](#unsub) them.


<a id="backup"></a>







>
>
>
>







514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531

*  The Verified checkbox is initially unchecked for subscriber-only
   email addresses until the user clicks the link in the verification
   email. This checkbox lets the Fossil Admin user manually verify the
   user, such as in the case where the verification email message got
   lost.  Unchecking this box does not cause another verification email
   to be sent.

*  Admin users (only) may activate the "user elevation" subscription,
   which sends a notification when a user is created or is explicitly
   assigned permission they did not formerly have.

This screen also allows a Fossil Admin user to perform other activities
on behalf of a subscriber which they could do themselves, such as to
[unsubscribe](#unsub) them.


<a id="backup"></a>
Changes to www/build.wiki.
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
Fossil has builtin support for processing specific features using
<tt>libfuzzer</tt>. The features which can be tested this way are
found in the help text for the [/help?cmd=test-fuzz|test-fuzz
command].

Fuzzing requires:

  *  Customizing the build of fossil a small bit.
  *  The clang C compiler.
  *  libfuzzer. On Ubuntu-derived systems, it can be installed with
     <tt>apt install libfuzzer-XYZ</tt>, where XYZ is a version number
     (several versions may be available on any given system)


First, modify the top-level <tt>Makefile.in</tt>:

  *  Extend the <tt>TCCFLAGS</tt> variable with: <tt>-fsanitize=fuzzer
   -DFOSSIL_FUZZ</tt> (and see [/finfo/src/fuzz.c | src/fuzz.c] for
   more options).
  *  Rename <tt>APPNAME</tt> from <tt>fossil</tt> to <tt>fossil-fuzz</tt>.

Then rebuild:

<pre></code>$ make clean
$ ./configure CC=/path/to/clang
$ make

</code></pre>

If clang is your default compiler, the <tt>CC</tt> configure option is
not required.

The resulting <tt>fossil-fuzz</tt> binary differs from the standard
one primarily in that it runs the <tt>test-fuzz</tt> command by
default. It needs to be told what to fuzz and needs to be given a
directory of input files to seed the fuzzer with:


<pre></code>$ mkdir cases
  # Copy input files into ./cases. e.g. when fuzzing the markdown
  # processor, copy any to-be-tested .md files into that directory.
  # Then start the fuzzer:
$ ./fossil-fuzz --fuzztype markdown cases
</code></pre>

As it works, it writes its mutated test files into the test-input
directory, each one named in the form of a hash. When it finds a
problem it will produce a stack trace for the offending code, will
output the name of the file which triggered the crash (named
<tt>cases/SOME_HASH</tt>) and may, depending on the nature of the
problem, produce a file named <tt>crash-SOMETHING</tt>.  In theory the
crash file can be fed directly back into the fuzzer to reproduce the
problem:







<






<
|
<
<
<
<

<
<
|
<
<
>


<
<
<
|





|






|







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
Fossil has builtin support for processing specific features using
<tt>libfuzzer</tt>. The features which can be tested this way are
found in the help text for the [/help?cmd=test-fuzz|test-fuzz
command].

Fuzzing requires:


  *  The clang C compiler.
  *  libfuzzer. On Ubuntu-derived systems, it can be installed with
     <tt>apt install libfuzzer-XYZ</tt>, where XYZ is a version number
     (several versions may be available on any given system)



Compile as follows:







<pre><code>make clean


make TCCFLAGS='-DFOSSIL_FUZZ -fsanitize=fuzzer,address,undefined -O0 -g' CC=clang
</code></pre>




The resulting <tt>fossil</tt> binary differs from the standard
one primarily in that it runs the <tt>test-fuzz</tt> command by
default. It needs to be told what to fuzz and needs to be given a
directory of input files to seed the fuzzer with:


<pre><code>$ mkdir cases
  # Copy input files into ./cases. e.g. when fuzzing the markdown
  # processor, copy any to-be-tested .md files into that directory.
  # Then start the fuzzer:
$ ./fossil-fuzz --fuzztype markdown cases
</code></pre>

As it works, it writes its mutated test files into the "cases"
directory, each one named in the form of a hash. When it finds a
problem it will produce a stack trace for the offending code, will
output the name of the file which triggered the crash (named
<tt>cases/SOME_HASH</tt>) and may, depending on the nature of the
problem, produce a file named <tt>crash-SOMETHING</tt>.  In theory the
crash file can be fed directly back into the fuzzer to reproduce the
problem:
501
502
503
504
505
506
507


508
509
510
511
512
513
514
515
      to give JS code access to the API exported by the WASM file.

When a new version of <tt>extsrc/pikchr.c</tt> is installed, the
files <tt>pikchr.{js,wasm}</tt> will need to be recompiled to account
for that. Running <tt>make wasm</tt> will, if the build is set up for
the emsdk, recompile those:



<pre><code>$ make wasm
./tools/emcc.sh -o extsrc/pikchr.js ...
$ ls -la extsrc/pikchr.{js,wasm}
-rw-rw-r-- 1 stephan stephan 17263 Jun  8 03:59 extsrc/pikchr.js
-rw-rw-r-- 1 stephan stephan 97578 Jun  8 03:59 extsrc/pikchr.wasm
</code></pre>

<div class="sidebar">If that fails with a message along the lines of







>
>
|







489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
      to give JS code access to the API exported by the WASM file.

When a new version of <tt>extsrc/pikchr.c</tt> is installed, the
files <tt>pikchr.{js,wasm}</tt> will need to be recompiled to account
for that. Running <tt>make wasm</tt> will, if the build is set up for
the emsdk, recompile those:

<pre><code>$ rm extsrc/pikchr.{js,wasm}
# ^^^^ that rm has proven necessary in order to ensure rebuilds
$ make wasm
./tools/emcc.sh -o extsrc/pikchr.js ...
$ ls -la extsrc/pikchr.{js,wasm}
-rw-rw-r-- 1 stephan stephan 17263 Jun  8 03:59 extsrc/pikchr.js
-rw-rw-r-- 1 stephan stephan 97578 Jun  8 03:59 extsrc/pikchr.wasm
</code></pre>

<div class="sidebar">If that fails with a message along the lines of
Changes to www/cgi.wiki.
71
72
73
74
75
76
77

78
79



80
81
82
83
84
85
86
If it is present, and if the [#directory:|<b>directory:</b>] option is used,
and if the PATH_INFO string is empty, then Fossil will show a list
of available Fossil repositories.

The "skin" of the reply is determined by the first
repository in the list that has a non-zero
[/help?cmd=repolist-skin|repolist-skin] setting.

If no repository has such a non-zero repolist-skin setting, then
the repository list is generic HTML without any decoration.




The repolist-generated page recurses into subdirectories and will list
all <tt>*.fossil</tt> files found, with the following exceptions:

   *  Filenames starting with a period are treated as "hidden" and skipped.

   *  Subdirectory names which match the base name of a fossil file in







>

|
>
>
>







71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
If it is present, and if the [#directory:|<b>directory:</b>] option is used,
and if the PATH_INFO string is empty, then Fossil will show a list
of available Fossil repositories.

The "skin" of the reply is determined by the first
repository in the list that has a non-zero
[/help?cmd=repolist-skin|repolist-skin] setting.

If no repository has such a non-zero repolist-skin setting, then
the repository list is generic HTML without any decoration, with
the page title taken from the <tt>FOSSIL_REPOLIST_TITLE</tt>
environment variable. The variable can be defined in the CGI
control file using the [#setenv|<tt>setenv:</tt>] statement.

The repolist-generated page recurses into subdirectories and will list
all <tt>*.fossil</tt> files found, with the following exceptions:

   *  Filenames starting with a period are treated as "hidden" and skipped.

   *  Subdirectory names which match the base name of a fossil file in
Changes to www/changes.wiki.
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
<title>Change Log</title>

<h2 id='v2_26'>Changes for version 2.26 (pending)</h2>

  *  Enhanced the --from option on "[/help?cmd=diff|fossil diff]" so that

     it optionally accepts a directory name as its argument, and uses files
     under that directory as the baseline for the diff.








  *  Added the [/help?cmd=/ckout|/ckout web page] to provide information
     about pending changes in a working check-out
  *  The [/help?cmd=ui|fossil ui] command defaults to using the

     [/help?cmd=/ckout|/ckout page] as its start page.  Or, if the
     "--from PATH" option is present, the default start page becomes
     "/ckout?exbase=PATH".








  *  Added the [/help?cmd=merge-info|fossil merge-info] command and especially
     the --tk option to that command, to provide analysis of the most recent
     merge or update operation.












  *  Added the ability to sign check-ins with SSH keys.
  *  Issue a warning if a user tries to commit on a check-in where the
     branch has been changed.
  *  When a merge conflict occurs, a new section is added to the conflict
     text that shows Fossil's suggested resolution to the conflict.
  *  Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
     diffs of multiple files.









  *  Enhancements to the [/help?cmd=/timeline|/timeline page]:
     <ol type="a">
     <li> Added the "ml=" ("Merge-in List") query parameter that works
          like "rl=" ("Related List") but adds "mionly" style related
          check-ins instead of the full "rel" style.
     <li> For "tl=", "rl=", and "ml=", the order of the branches in the
          graph now tries to match the order of the branches named in




|
>
|
|
>
>
>
>
>
>
>
>


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







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
<title>Change Log</title>

<h2 id='v2_26'>Changes for version 2.26 (pending)</h2>

  *  Enhancements to [/help?cmd=diff|fossil diff] and similar:
     <ol type="a">
     <li> The --from can optionally accepts a directory name as its argument,
          and uses files under that directory as the baseline for the diff.
     <li> For "gdiff", if no [/help?cmd=gdiff-command|gdiff-command setting]
          is defined, Fossil tries to do a --tk diff if "tclsh" and "wish"
          are available, or a --by diff if not.
     <li> The "Reload" button is added to --tk diffs, to bring the displayed
          diff up to date with the latest changes on disk.
     <li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
          diffs of multiple files.
     </ol>
  *  Added the [/help?cmd=/ckout|/ckout web page] to provide information
     about pending changes in a working check-out
  *  Enhancements to the [/help?cmd=ui|fossil ui] command:
     <ol type="a">
     <li> Defaults to using the new [/help?cmd=/ckout|/ckout page] as its
          start page.  Or, if the new "--from PATH" option is present, the
          default start page becomes "/ckout?exbase=PATH".
     <li> The new "--extpage FILENAME" option opens the named file as if it
          where in a [./serverext.wiki|CGI extension].  Example usage: the
          person editing this change log has 
          "fossil ui --extpage www/changes.wiki" running and hence can
          press "Reload" on the web browser to view edits.
     </ol>
  *  Enhancements to [/help?cmd=merge|fossil merge]:
     <ol type="a">
     <li> Added the [/help?cmd=merge-info|fossil merge-info] command and
          especially the --tk option to that command, to provide analysis
          of the most recent merge or update operation.
     <li> When a merge conflict occurs, a new section is added to the conflict
          text that shows Fossil's suggested resolution to the conflict.
     </ol>
  *  Enhancements to [/help?cmd=commit|fossil commit]:
     <ol type="a">
     <li> If Fossil sees potential formatting mistakes (ex: bad hyperlinks)
          in the check-in comment, it will alert the developer and give
          him or her the opportunity to edit the comment before continuing.
          This feature is controllable by the
          [/help?cmd=verify-comments|verify-comments setting].
     <li> The new "--if-changes" option causes the commit to become
          a quiet no-op if there are no pending changes.
     <li> Added the ability to sign check-ins with SSH keys.
     <li> Issue a warning if a user tries to commit on a check-in where the
          branch has been changed.




     <li> The interactive checkin comment prompt shows the formatting rules
          set for that repository.
     </ol>
  *  Deprecate the --comfmtflags and --comment-format global options and
     no longer list them in the built-in help, but keep them working for
     backwards compatibility.
     Alternative TTY comment formatting can still be specified using the
     [/help?cmd=comment-format|comment-format setting], if desired.  The
     default comment format is now called "canonical", not "legacy".
  *  Enhancements to the [/help?cmd=/timeline|/timeline page]:
     <ol type="a">
     <li> Added the "ml=" ("Merge-in List") query parameter that works
          like "rl=" ("Related List") but adds "mionly" style related
          check-ins instead of the full "rel" style.
     <li> For "tl=", "rl=", and "ml=", the order of the branches in the
          graph now tries to match the order of the branches named in
41
42
43
44
45
46
47












48
49
50

51
52
53


54


55
56
57























58
59
60
61
62
63
64
          GLOB characters, then the matching style ("ms=") is set to GLOB
          automatically and the "ms=" query parameter can be omitted.
     <li> Enhance the "ymd" query parameter so that when used like
          "ymd=YYYYMMDD-YYYYMMDD" it shows all events in the range of
          dates specified.
     <li> Accept the "Z" (Zulu-time) suffix on date arguments for the
          "ymd" and "yw" query parameters.












     </ol>
  *  Add the "--if-changes" option to the [/help?cmd=commit|fossil commit]
     command that causes the command to become a quiet no-op if there are

     no pending changes.
  *  Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
     and debugging


  *  Fix a bug in [/help?cmd=patch|fossil patch create] that causes


     [/help?cmd=revert|fossil revert] operations that happened on individual
     files after a [/help?cmd=merge|fossil merge] to be omitted from the
     patch.
























<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>

  *  The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
     that have non-ASCII filenames
  *  Add the [/help?cmd=tree|fossil tree] command.
  *  On case-insensitive filesystems, store files using the filesystem's







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

|
<
>
|


>
>
|
>
>
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
          GLOB characters, then the matching style ("ms=") is set to GLOB
          automatically and the "ms=" query parameter can be omitted.
     <li> Enhance the "ymd" query parameter so that when used like
          "ymd=YYYYMMDD-YYYYMMDD" it shows all events in the range of
          dates specified.
     <li> Accept the "Z" (Zulu-time) suffix on date arguments for the
          "ymd" and "yw" query parameters.
     <li> The new "min" query parameter, when added to a from=,to= query,
          collapses long runs of check-ins on the same branch into just
          end-points.
     <li> The p= and d= parameters an reference different check-ins, which
          case the timeline shows those check-ins that are both ancestors
          of p= and descendants of d=.
     <li> The saturation and intensity of user-specified checkin and branch
          colors are automatically adjusted to keep the colors
          compatible with the current skin, unless the
          [/help?cmd=raw-bgcolor|raw-bgcolor setting] is turned on.  The
          /test-bgcolor page was added to
          test and visualize how these adjustments.
     </ol>
  *  The [/help?cmd=/docfile|/docfile webpage] was added.  It works like

     /doc but keeps the title of markdown documents with the document rather
     that moving it up to the page title.
  *  Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
     and debugging
  *  Added the "artifact_to_json(NAME)" SQL function that returns a JSON
     decoding of the artifact described by NAME.
  *  Improvements to the [/help?cmd=patch|fossil patch] command:
     <ol type="a">
     <li> Fix a bug in "fossil patch create" that causes
          [/help?cmd=revert|fossil revert] operations that happened
          on individualfiles after a [/help?cmd=merge|fossil merge]
          to be omitted from the patch.
     <li>  Added the [/help?cmd=patch|patch alias] command for managing
           aliases for remote checkout names.
     </ol>
  *  Enhancements to on-line help and the [/help?cmd=help|fossil help] command:
     <ol type="a">
     <li> Add the ability to search the help text, either in the UI
          (on the [/help?cmd=/search|/search page]) or from the command-line
          (using the "[/help?cmd=search|fossil search -h PATTERN]" command.)
     <li> Accepts an optional SUBCOMMAND argument following the
          COMMAND argument and only shows results for the specified
          subcommand, not the entire command.
     <li> The -u (--usage) option shows only the command-line syntax
     <li> The -o (--options) option shows only the command-line options
     </ol>
  *  Added the ability to attach wiki pages to a ticket for extended
     descriptions.
  *  Added the "hash" query parameter to the
     [/help?cmd=/whatis|/whatis webpage].
  *  Add a "user elevation" [/doc/trunk/www/alerts.md|subscription]
     which alerts subscribers when an admin creates a new user or
     adds new permissions to one.
  *  Diverse minor fixes and additions.


<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>

  *  The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
     that have non-ASCII filenames
  *  Add the [/help?cmd=tree|fossil tree] command.
  *  On case-insensitive filesystems, store files using the filesystem's
Changes to www/env-opts.md.
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

`--comfmtflags NUMBER`: Specify flags that control how check-in comments
and certain other text outputs are formatted for display. The flags are
individual bits in `NUMBER`, which must be specified in base 10:

  * _0_ &mdash; Uses the revised algorithm with no special handling.

  * _1_ &mdash; Uses the legacy algorithm, other flags are ignored.

  * _2_ &mdash; Trims leading and trailing carriage-returns and line-feeds
        where they do not materially impact pre-existing formatting
        (i.e. at the start of the comment string _and_ right before
        line indentation).

  * _4_ &mdash; Trims leading and trailing spaces where they do not materially
        impact the pre-existing formatting (i.e. at the start of the
        comment string _and_ right before line indentation).

  * _8_ &mdash; Attempts to break lines on word boundaries while honoring the
        logical line length.

  * _16_ &mdash; Looks for the original comment text within the text being
         printed.  Upon matching, a new line will be emitted, thus
         preserving more of the pre-existing formatting.


`--comment-format NUMBER`: Alias for `--comfmtflags NUMBER`.







`--errorlog ERRLOG`: Name a file to which fossil will log panics,
errors, and warnings.


`--help`: If `--help` is found anywhere on the command line, translate
the command to `fossil help cmdname` where `cmdname` is the first







|




















>
>
>
>
>







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

`--comfmtflags NUMBER`: Specify flags that control how check-in comments
and certain other text outputs are formatted for display. The flags are
individual bits in `NUMBER`, which must be specified in base 10:

  * _0_ &mdash; Uses the revised algorithm with no special handling.

  * _1_ &mdash; Uses the canonical algorithm, other flags are ignored.

  * _2_ &mdash; Trims leading and trailing carriage-returns and line-feeds
        where they do not materially impact pre-existing formatting
        (i.e. at the start of the comment string _and_ right before
        line indentation).

  * _4_ &mdash; Trims leading and trailing spaces where they do not materially
        impact the pre-existing formatting (i.e. at the start of the
        comment string _and_ right before line indentation).

  * _8_ &mdash; Attempts to break lines on word boundaries while honoring the
        logical line length.

  * _16_ &mdash; Looks for the original comment text within the text being
         printed.  Upon matching, a new line will be emitted, thus
         preserving more of the pre-existing formatting.


`--comment-format NUMBER`: Alias for `--comfmtflags NUMBER`.


> NOTE: As of Fossil version 2.26, use of the `--comfmtflags` and
> `--comment-format` options is no longer recommended and they are
> no longer documented, but retained for backwards compatibility.


`--errorlog ERRLOG`: Name a file to which fossil will log panics,
errors, and warnings.


`--help`: If `--help` is found anywhere on the command line, translate
the command to `fossil help cmdname` where `cmdname` is the first
141
142
143
144
145
146
147





148
149
150
151
152
153
154
local (or remote) testing of the moderation subsystem and its impact
on the contents and status of wiki pages.


`FOSSIL_HOME`: Location of [configuration database][configdb].
See the [configuration database location][configloc] description
for additional information.






`FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for
SEE as text to be hashed into the actual encryption key.  This has no
effect if Fossil was not compiled with SEE support enabled.


`FOSSIL_USER`: Name of the default user account if the checkout, local







>
>
>
>
>







146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
local (or remote) testing of the moderation subsystem and its impact
on the contents and status of wiki pages.


`FOSSIL_HOME`: Location of [configuration database][configdb].
See the [configuration database location][configloc] description
for additional information.

`FOSSIL_REPOLIST_TITLE`: The page title of the "Repository List" page
loaded by the `fossil all ui` or `fossil ui /` commands. Only used if
none of the listed repositories has the `repolist_skin` property set.
Can be set from the [CGI control file][cgictlfile].

`FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for
SEE as text to be hashed into the actual encryption key.  This has no
effect if Fossil was not compiled with SEE support enabled.


`FOSSIL_USER`: Name of the default user account if the checkout, local
208
209
210
211
212
213
214




215
216
217
218
219
220
221
`FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows),
`HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is
used as the location of the `~/.fossil` file.

`LOGNAME`: Name of the logged in user on many Unix-like platforms.
Used as the fossil user name if `FOSSIL_USER` is not specified. See
the discussion of Fossil Username below for a lot more detail.





`PATH`: Used by most platforms to locate programs invoked without a
fully qualified name. Explicitly used by `fossil ui` on certain platforms
to choose the browser to launch.

`PATH_INFO`: If defined, included in error log messages.








>
>
>
>







218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
`FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows),
`HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is
used as the location of the `~/.fossil` file.

`LOGNAME`: Name of the logged in user on many Unix-like platforms.
Used as the fossil user name if `FOSSIL_USER` is not specified. See
the discussion of Fossil Username below for a lot more detail.

`NO_COLOR`: If defined and not set to a `false` value (i.e. "off", "no",
"false", "0"), the `fossil search` command skips colorization of console
output using ANSI escape codes (VT100).

`PATH`: Used by most platforms to locate programs invoked without a
fully qualified name. Explicitly used by `fossil ui` on certain platforms
to choose the browser to launch.

`PATH_INFO`: If defined, included in error log messages.

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

will happen on all platforms.


### Web browser

Occasionally, fossil wants to launch a web browser for the user, most
obviously as part of the `fossil ui` command. In that specific case,
the browser is launched pointing at the web server started by `fossil
ui` listening on a private TCP port.

On all platforms, if the local or global settings `web-browser` is
set, that is the command used to open a URL.

Otherwise, the specific actions vary by platform.

On Unix-like platforms other than Apple's, it looks for the first
program from the list `xdg-open`, `gnome-open`, `firefox`, and
`google-chrome` that it can find on the `PATH`.

On Apple platforms, it assumes that `open` is the command to open a
URL in the user's configured default browser.

On Windows platforms, it assumes that `start` is the command to open
a URL in the user's configured default browser.

[configdb]: ./tech_overview.wiki#configdb
[configloc]: ./tech_overview.wiki#configloc








|
|


















>
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
will happen on all platforms.


### Web browser

Occasionally, fossil wants to launch a web browser for the user, most
obviously as part of the `fossil ui` command. In that specific case,
the browser is launched pointing at the web server started by
`fossil ui` listening on a private TCP port.

On all platforms, if the local or global settings `web-browser` is
set, that is the command used to open a URL.

Otherwise, the specific actions vary by platform.

On Unix-like platforms other than Apple's, it looks for the first
program from the list `xdg-open`, `gnome-open`, `firefox`, and
`google-chrome` that it can find on the `PATH`.

On Apple platforms, it assumes that `open` is the command to open a
URL in the user's configured default browser.

On Windows platforms, it assumes that `start` is the command to open
a URL in the user's configured default browser.

[configdb]: ./tech_overview.wiki#configdb
[configloc]: ./tech_overview.wiki#configloc
[cgictlfile]: ./cgi.wiki
Changes to www/fileformat.wiki.
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
repository.
Fossil recognizes the following kinds of structural
artifacts:

<ul>
<li> [#manifest | Manifests] </li>
<li> [#cluster | Clusters] </li>
<li> [#ctrl | Control Artifacts] </li>
<li> [#wikichng | Wiki Pages] </li>
<li> [#tktchng | Ticket Changes] </li>
<li> [#attachment | Attachments] </li>
<li> [#event | TechNotes] </li>
<li> [#forum | Forum Posts] </li>
</ul>








|







59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
repository.
Fossil recognizes the following kinds of structural
artifacts:

<ul>
<li> [#manifest | Manifests] </li>
<li> [#cluster | Clusters] </li>
<li> [#ctrl | Control (a.k.a. Tag) Artifacts] </li>
<li> [#wikichng | Wiki Pages] </li>
<li> [#tktchng | Ticket Changes] </li>
<li> [#attachment | Attachments] </li>
<li> [#event | TechNotes] </li>
<li> [#forum | Forum Posts] </li>
</ul>

171
172
173
174
175
176
177
178



179
180
181
182
183
184
185
is optional.  The file format might be extended with new permission
letters in the future.  The optional 4th argument is the name of the
same file as it existed in the parent check-in.  If the name of the
file is unchanged from its parent, then the 4th argument is omitted.

A manifest has zero or one <b>N</b> cards.  The <b>N</b> card specifies the mimetype for the
text in the comment of the <b>C</b> card.  If the <b>N</b> card is omitted, a default mimetype
is used.




A manifest has zero or one <b>P</b> cards.  Most manifests have one <b>P</b> card.
The <b>P</b> card has a varying number of arguments that
define other manifests from which the current manifest
is derived.  Each argument is a lowercase
hexadecimal artifact hash of a predecessor manifest.  All arguments
to the <b>P</b> card must be unique within that card.







|
>
>
>







171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
is optional.  The file format might be extended with new permission
letters in the future.  The optional 4th argument is the name of the
same file as it existed in the parent check-in.  If the name of the
file is unchanged from its parent, then the 4th argument is omitted.

A manifest has zero or one <b>N</b> cards.  The <b>N</b> card specifies the mimetype for the
text in the comment of the <b>C</b> card.  If the <b>N</b> card is omitted, a default mimetype
is used. Note that the <b>N</b> card has never actually been used by
any Fossil implementation.  The implementation has always interpreted
check-in comments according to the [/wiki_rules|Fossil Wiki formatting rules].
There are no current plans to ever change that.

A manifest has zero or one <b>P</b> cards.  Most manifests have one <b>P</b> card.
The <b>P</b> card has a varying number of arguments that
define other manifests from which the current manifest
is derived.  Each argument is a lowercase
hexadecimal artifact hash of a predecessor manifest.  All arguments
to the <b>P</b> card must be unique within that card.
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
the <b>Z</b> card of a manifest.  The argument to the <b>Z</b> card is the
lower-case hexadecimal representation of the MD5 checksum of all
prior cards in the cluster.  The <b>Z</b> card is required.

An example cluster from Fossil can be seen
[/artifact/d03dbdd73a2a8 | here].

<h3 id="ctrl">2.3 Control Artifacts</h3>

Control artifacts are used to assign properties to other artifacts
within the repository.
Allowed cards in a control artifact are as follows:

<div class="indent">
<b>D</b> <i>time-and-date-stamp</i><br />
<b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <i>artifact-id</i> ?<i>value</i>?<br />
<b>U</b> <i>user-name</i><br />
<b>Z</b> <i>checksum</i><br />
</div>





A control artifact must have one <b>D</b> card, one <b>U</b> card, one <b>Z</b> card and
one or more <b>T</b> cards.  No other cards or other text is
allowed in a control artifact.  Control artifacts might be PGP
clearsigned.

The <b>D</b> card and the <b>Z</b> card of a control artifact are the same







|


|
|







>
>
>
>







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
the <b>Z</b> card of a manifest.  The argument to the <b>Z</b> card is the
lower-case hexadecimal representation of the MD5 checksum of all
prior cards in the cluster.  The <b>Z</b> card is required.

An example cluster from Fossil can be seen
[/artifact/d03dbdd73a2a8 | here].

<h3 id="ctrl">2.3 Control (a.k.a. Tag) Artifacts</h3>

Control artifacts are used to assign properties to other artifacts
within the repository.  Allowed cards in a control artifact are as
follows:

<div class="indent">
<b>D</b> <i>time-and-date-stamp</i><br />
<b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <i>artifact-id</i> ?<i>value</i>?<br />
<b>U</b> <i>user-name</i><br />
<b>Z</b> <i>checksum</i><br />
</div>

Control articles are also referred to as Tag artifacts, but tags can
also be applied via other artifact types, as described in
[#summary|the Card Summary table].

A control artifact must have one <b>D</b> card, one <b>U</b> card, one <b>Z</b> card and
one or more <b>T</b> cards.  No other cards or other text is
allowed in a control artifact.  Control artifacts might be PGP
clearsigned.

The <b>D</b> card and the <b>Z</b> card of a control artifact are the same
Changes to www/serverext.wiki.
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
an "Extension Root Directory" or "extroot" as part of the 
[./server/index.html|server setup].
If the Fossil server is itself run as 
[./server/any/cgi.md|CGI], then add a line to the 
[./cgi.wiki#extroot|CGI script file] that says:

<pre>
extroot: <i>DIRECTORY</i>
</pre>

Or, if the Fossil server is being run using the 
"[./server/any/none.md|fossil server]" or
"[./server/any/none.md|fossil ui]" or 
"[./server/any/inetd.md|fossil http]" commands, then add an extra 
"--extroot <i>DIRECTORY</i>" option to that command.

The <i>DIRECTORY</i> is the DOCUMENT_ROOT for the CGI.
Files in the DOCUMENT_ROOT are accessed via URLs like this:

<pre>
https://example-project.org/ext/<i>FILENAME</i>
</pre>

In other words, access files in DOCUMENT_ROOT by appending the filename
relative to DOCUMENT_ROOT to the [/help?cmd=/ext|/ext]
page of the Fossil server.

Files that are readable but not executable are returned as static


content.  Files that are executable are run as CGI.

<h3>2.1 Example #1</h3>

The source code repository for SQLite is a Fossil server that is run
as CGI.  The URL for the source code repository is [https://sqlite.org/src].
The CGI script looks like this:








|












|





>
|
>
>
|







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
an "Extension Root Directory" or "extroot" as part of the 
[./server/index.html|server setup].
If the Fossil server is itself run as 
[./server/any/cgi.md|CGI], then add a line to the 
[./cgi.wiki#extroot|CGI script file] that says:

<pre>
    extroot: <i>DIRECTORY</i>
</pre>

Or, if the Fossil server is being run using the 
"[./server/any/none.md|fossil server]" or
"[./server/any/none.md|fossil ui]" or 
"[./server/any/inetd.md|fossil http]" commands, then add an extra 
"--extroot <i>DIRECTORY</i>" option to that command.

The <i>DIRECTORY</i> is the DOCUMENT_ROOT for the CGI.
Files in the DOCUMENT_ROOT are accessed via URLs like this:

<pre>
    https://example-project.org/ext/<i>FILENAME</i>
</pre>

In other words, access files in DOCUMENT_ROOT by appending the filename
relative to DOCUMENT_ROOT to the [/help?cmd=/ext|/ext]
page of the Fossil server.

  *  Files that are readable but not executable are returned as static
     content.

  *  Files that are executable are run as CGI.

<h3>2.1 Example #1</h3>

The source code repository for SQLite is a Fossil server that is run
as CGI.  The URL for the source code repository is [https://sqlite.org/src].
The CGI script looks like this:

115
116
117
118
119
120
121

























122
123
124
125
126
127
128
129
130
131
132
133
134
script.  (The extension mechanism is not required to use Wapp.  You can use
any kind of program you like.  But the creator of SQLite and Fossil is fond
of [https://www.tcl.tk|Tcl/Tk] and so he tends to gravitate toward Tcl-based
technologies like Wapp.)  The fileup1 script is a demo program that lets
the user upload a file using a form, and then displays that file in the reply.
There is a link on the page that causes the fileup1 script to return a copy
of its own source-code, so you can see how it works.


























<h2 id="cgi-inputs">3.0 CGI Inputs</h2>

The /ext extension mechanism is an ordinary CGI interface.  Parameters
are passed to the CGI program using environment variables.  The following
standard CGI environment variables are supported:

  *  AUTH_TYPE
  *  AUTH_CONTENT
  *  CONTENT_LENGTH
  *  CONTENT_TYPE
  *  DOCUMENT_ROOT
  *  GATEWAY_INTERFACE







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





|







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
script.  (The extension mechanism is not required to use Wapp.  You can use
any kind of program you like.  But the creator of SQLite and Fossil is fond
of [https://www.tcl.tk|Tcl/Tk] and so he tends to gravitate toward Tcl-based
technologies like Wapp.)  The fileup1 script is a demo program that lets
the user upload a file using a form, and then displays that file in the reply.
There is a link on the page that causes the fileup1 script to return a copy
of its own source-code, so you can see how it works.

<h3>2.3 Example #3</h3>

For Fossil versions dated 2025-03-23 and later, the "--extpage FILENAME"
option to the [/help?cmd=ui|fossil ui] command is a short cut that treats
FILENAME as a CGI extension.  When the ui command starts up a new web browser
pages, it points that page to the FILENAME extension.  So if FILENAME is
a static content file (such as an HTML file or 
[/md_rules|Markdown] or [/wiki_rules|Wiki] document), then the
rendered content of the file is displayed.  Meanwhile, the user can be
editing the source text for that document in a separate window, and
periodically pressing "Reload" on the web browser to instantly view the
rendered results.

For example, the author of this documentation page is running
"<tt>fossil ui --extpage www/serverext.wiki</tt>" while editing this
very paragraph, and presses Reload from time to time to view his
edits.

A same idea applies when developing new CGI applications using a script
language (for example using [https://wapp.tcl.tk|Wapp]).  Run the
command "<tt>fossil ui --extpage SCRIPT</tt>" where SCRIPT is the name
of the application script, while editing that script in a separate
window, then press Reload periodically on the web browser to test the
script.

<h2 id="cgi-inputs">3.0 CGI Inputs</h2>

The /ext extension mechanism is an ordinary CGI interface.  Parameters
are passed to the CGI program using environment variables.  The following
standard CGI environment variables are supplied:

  *  AUTH_TYPE
  *  AUTH_CONTENT
  *  CONTENT_LENGTH
  *  CONTENT_TYPE
  *  DOCUMENT_ROOT
  *  GATEWAY_INTERFACE
Changes to www/settings.wiki.
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
<title>Fossil Settings</title>

<h2>Using Fossil Settings</h2>

Settings control the behaviour of fossil. They are set with the
<tt>fossil settings</tt> command, or through the web interface in
the Settings page in the Admin section.

For a list of all settings, view the Settings page, or type
<tt>fossil help settings</tt> from the command line.


<h3 id="repo">Repository settings</h3>

Settings are set on a per-repository basis. When you clone a repository,
a subset of settings are copied to your local repository.

If you make a change to a setting on your local repository, it is not
synced back to the server when you <tt>push</tt> or <tt>sync</tt>. If
you make a change on the server, you need to manually make the change on
all repositories which are cloned from this repository.

You can also set a setting globally on your local machine. The value
will be used for all repositories cloned to your machine, unless
overridden explicitly in a particular repository. Global settings can be
set by using the <tt>-global</tt> option on the <tt>fossil settings</tt>
command.

<h3 id="versionable">"Versionable" settings</h3>

Most of the settings control the behaviour of fossil on your local
machine, largely acting to reflect your preference on how you want to
use Fossil, how you communicate with the server, or options for hosting
a repository on the web.

However, for historical reasons, some settings affect how you work with


|









|















|







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
<title>Fossil Settings</title>

<h1>Using Fossil Settings</h1>

Settings control the behaviour of fossil. They are set with the
<tt>fossil settings</tt> command, or through the web interface in
the Settings page in the Admin section.

For a list of all settings, view the Settings page, or type
<tt>fossil help settings</tt> from the command line.


<h2 id="repo">1.0 Repository settings</h2>

Settings are set on a per-repository basis. When you clone a repository,
a subset of settings are copied to your local repository.

If you make a change to a setting on your local repository, it is not
synced back to the server when you <tt>push</tt> or <tt>sync</tt>. If
you make a change on the server, you need to manually make the change on
all repositories which are cloned from this repository.

You can also set a setting globally on your local machine. The value
will be used for all repositories cloned to your machine, unless
overridden explicitly in a particular repository. Global settings can be
set by using the <tt>-global</tt> option on the <tt>fossil settings</tt>
command.

<h2 id="versionable">2.0 "Versionable" settings</h2>

Most of the settings control the behaviour of fossil on your local
machine, largely acting to reflect your preference on how you want to
use Fossil, how you communicate with the server, or options for hosting
a repository on the web.

However, for historical reasons, some settings affect how you work with
Changes to www/th1.md.
225
226
227
228
229
230
231

232
233
234
235
236
237
238
  *  [trace](#trace)
  *  [unversioned content](#unversioned_content)
  *  [unversioned list](#unversioned_list)
  *  [utime](#utime)
  *  [verifyCsrf](#verifyCsrf)
  *  [verifyLogin](#verifyLogin)
  *  [wiki](#wiki)


Each of the commands above is documented by a block comment above their
implementation in the th\_main.c or th\_tcl.c source files.

All commands starting with "tcl", with the exception of "tclReady",
require the Tcl integration subsystem be included at compile-time.
Additionally, the "tcl" repository setting must be enabled at runtime







>







225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
  *  [trace](#trace)
  *  [unversioned content](#unversioned_content)
  *  [unversioned list](#unversioned_list)
  *  [utime](#utime)
  *  [verifyCsrf](#verifyCsrf)
  *  [verifyLogin](#verifyLogin)
  *  [wiki](#wiki)
  *  [wiki_assoc](#wiki_assoc)

Each of the commands above is documented by a block comment above their
implementation in the th\_main.c or th\_tcl.c source files.

All commands starting with "tcl", with the exception of "tclReady",
require the Tcl integration subsystem be included at compile-time.
Additionally, the "tcl" repository setting must be enabled at runtime
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


<a id="bireqjs"></a>TH1 builtin_request_js Command
--------------------------------------------------

  *  builtin_request_js NAME

NAME must be the name of one of the 
[built-in javascript source files](/dir?ci=trunk&type=flat&name=src&re=js$).
This command causes that javascript file to be appended to the delivered
document.



<a id="capexpr"></a>TH1 capexpr Command
-----------------------------------------------------

  *  capexpr CAPABILITY-EXPR

The capability expression is a list. Each term of the list is a
cluster of [capability letters](./caps/ref.html). 
The overall expression is true if any
one term is true. A single term is true if all letters within that
term are true. Or, if the term begins with "!", then the term is true
if none of the terms are true. Or, if the term begins with "@" then
the term is true if all of the capability letters in that term are
available to the "anonymous" user. Or, if the term is "*" then it is
always true.







|












|







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


<a id="bireqjs"></a>TH1 builtin_request_js Command
--------------------------------------------------

  *  builtin_request_js NAME

NAME must be the name of one of the
[built-in javascript source files](/dir?ci=trunk&type=flat&name=src&re=js$).
This command causes that javascript file to be appended to the delivered
document.



<a id="capexpr"></a>TH1 capexpr Command
-----------------------------------------------------

  *  capexpr CAPABILITY-EXPR

The capability expression is a list. Each term of the list is a
cluster of [capability letters](./caps/ref.html).
The overall expression is true if any
one term is true. A single term is true if all letters within that
term are true. Or, if the term begins with "!", then the term is true
if none of the terms are true. Or, if the term begins with "@" then
the term is true if all of the capability letters in that term are
available to the "anonymous" user. Or, if the term is "*" then it is
always true.
859
860
861
862
863
864
865









866
867
868
869
870
871
872

<a id="wiki"></a>TH1 wiki Command
-----------------------------------

  *  wiki STRING

Renders STRING as wiki content.










Tcl Integration Commands
------------------------

When the Tcl integration subsystem is enabled, several commands are added
to the Tcl interpreter.  They are used to allow Tcl scripts access to the
Fossil functionality provided via TH1.  The following is a summary of the







>
>
>
>
>
>
>
>
>







860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882

<a id="wiki"></a>TH1 wiki Command
-----------------------------------

  *  wiki STRING

Renders STRING as wiki content.

<a id="wiki_assoc"></a>TH1 wiki_assoc Command
-----------------------------------

  *  wiki_assoc STRING STRING

Renders the special wiki. The first string refers to the namespace
(checkin, branch, tag, ticket). The second string specifies the
concrete wiki page to be rendered.

Tcl Integration Commands
------------------------

When the Tcl integration subsystem is enabled, several commands are added
to the Tcl interpreter.  They are used to allow Tcl scripts access to the
Fossil functionality provided via TH1.  The following is a summary of the
Added www/title-test.md.
























>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
# Markdown Doc Title > & " ' < Test

Test of unusual characters in the title of Markdown formatted documents.
The title should read:

>  Markdown Doc Title > & " ' < Test

See also:

  *  [](/doc/trunk/www/title-test.wiki)
  *  [](/wiki?name=Test+Wiki+>+%26+%22+%27+%3c+Title&p)
  *  [](/forumpost/481ab1f9)
Added www/title-test.wiki.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<title>Wiki Doc Title > & " ' < Test</title>

Test of unusual characters in the title of Fossil-wiki formatted documents.
The title should read:

<big><b><verbatim>
   Wiki Doc Title > & " ' < Test
</verbatim></b></big>

See also:

  *  [/doc/trunk/www/title-test.md]
  *  [/wiki?name=Test+Wiki+>+%26+%22+%27+%3c+Title&p]
  *  [/forumpost/481ab1f9]