Fossil

Check-in [5cc88946]
Login

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

Overview
Comment:Implemented /json/wiki/list (first draft, may change). Pulled in latest cson_sqlite3 additions to simplify the impl.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | json
Files: files | file ages | folders
SHA1:5cc88946a740e2697b049c6b739c0d3aeed9ee2e
User & Date: stephan 2011-09-19 18:48:15
Context
2011-09-19
18:49
fixed a double-free() which could happen in one error case. check-in: 65e946c7 user: stephan tags: json
18:48
Implemented /json/wiki/list (first draft, may change). Pulled in latest cson_sqlite3 additions to simplify the impl. check-in: 5cc88946 user: stephan tags: json
17:11
minor cleanups in prep for the "larger" JSON APIs. check-in: 87e20659 user: stephan tags: json
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/cson_amalgamation.c.

5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
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
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
5460
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
5481
5482
5483
5484
5485
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
5514
5515
5516
5517
5518
....
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553

5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584
5585
5586
....
5606
5607
5608
5609
5610
5611
5612




































































5613
5614
5615
5616
5617
5618
5619
....
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
5659
5660
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
....
5704
5705
5706
5707
5708
5709
5710
5711
5712
5713
5714
5715
5716
5717
5718
5719
5720
5721
5722
5723
5724
5725
....
5738
5739
5740
5741
5742
5743
5744
5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
....
5795
5796
5797
5798
5799
5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
5812
5813
5814
5815
5816
5817
5818
5819
5820
5821
5822
5823
5824
5825
5826
5827
5828
5829
5830
5831
5832
5833
5834
5835
5836
5837
5838
5839
5840
5841
5842
5843
5844
5845
5846
5847
5848
5849
5850
5851
5852
5853
5854
5855
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
5870
5871
5872
5873
5874
5875
5876
5877
5878
5879
5880
5881
5882
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
5894
5895
5896
5897
5898
5899
5900
5901
5902
5903
5904
5905
5906
5907
5908
5909
5910
5911
5912
5913
5914
5915
5916
5917
5918
5919
5920
5921
5922
5923
5924
5925
5926
5927
5928
5929
5930
5931
5932
5933
5934
5935
5936
5937
5938
5939
5940
5941
5942
5943
5944
5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
5960
5961
5962
5963
5964
5965
5966
5967
5968
5969
5970
5971
5972
5973
5974
5975
5976
5977
5978
5979
5980
5981
5982
5983
5984
5985
5986
5987
5988
5989
5990
5991
5992
5993
5994
5995
5996
5997
5998
5999
6000
6001
6002
6003
6004
6005
6006
6007
6008
6009
6010
6011
6012
6013
6014
6015
6016
6017
6018
6019
6020
6021
6022
6023
6024
6025
6026
6027
6028
6029
6030
6031
6032
6033
6034
6035
6036
6037
6038
6039
6040
6041
6042
6043
6044
6045
6046
6047
6048
6049
6050
6051
6052
6053
6054
6055
6056
6057
6058
6059
6060
6061
6062
6063
6064
6065
6066
6067
6068
6069
6070
6071
6072
6073
6074
6075
6076
6077
6078
6079
6080
6081
6082
6083
6084
6085
6086
6087
6088
6089
6090
6091
6092
6093
6094
6095
6096
6097
6098
6099
6100
6101
6102
6103
6104
6105
6106
6107
6108
6109
6110
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
6123
6124
6125
6126
6127
6128
6129
6130
6131
6132
6133
6134
6135
6136
6137
6138
6139
6140
6141
6142
6143
6144
6145
6146
6147
6148
6149
6150
6151
6152
6153
6154
6155
6156
6157
6158
6159
6160
6161
6162
6163
6164
6165
6166
6167
6168
6169
6170
6171
6172
6173
6174
6175
6176
6177
6178
6179
6180
6181
6182
6183
6184
6185
6186
6187
6188
6189
6190
6191
6192
6193
6194
6195
6196
6197
6198
6199
6200
6201
6202
6203
6204
6205
6206
6207
6208
6209
6210
6211
6212
6213
6214
6215
6216
6217
6218
6219
6220
6221
6222
6223
6224
6225
6226
6227
6228
6229
6230
6231
6232
6233
6234
6235
6236
6237
6238
6239
6240
6241
6242
6243
6244
6245
6246
6247
6248
6249
6250
6251
6252
6253
6254
6255
6256
6257
6258
6259
6260
6261
6262
6263
6264
6265
6266
6267
6268
6269
6270
6271
6272
6273
6274
6275
6276
6277
6278
6279
6280
6281
6282
6283
6284
6285
6286
6287
6288
6289
6290
6291
6292
6293
6294
6295
6296
6297
6298
6299
6300
6301
6302
6303
6304
6305
6306
6307
6308
6309
6310
6311
6312
6313
6314
6315
6316
6317
6318
6319
6320
6321
6322
6323
6324
6325
6326
6327
6328
6329
6330
6331
6332
6333
6334
6335
6336
6337
6338
6339
6340
6341
6342
6343
6344
6345
6346
6347
6348
6349
6350
6351
6352
6353
6354
6355
6356
6357
6358
6359
6360
6361
6362
6363
6364
6365
6366
6367
6368
6369
6370
6371
6372
6373
6374
6375
6376
6377
6378
6379
6380
6381
6382
6383
6384
6385
6386
6387
6388
6389
6390
6391
6392
6393
6394
6395
6396
6397
6398
6399
6400
6401
6402
6403
6404
6405
6406
6407
6408
6409
6410
6411
6412
6413
6414
6415
6416
6417
6418
6419
6420
6421
6422
6423
6424
6425
6426
6427
6428
6429
6430
6431
6432
6433
6434
6435
6436
6437
6438
6439
6440
6441
6442
6443
6444
6445
6446
6447
6448
6449
6450
6451
6452
6453
6454
6455
6456
6457
6458
6459
6460
6461
6462
6463
6464
6465
6466
6467
6468
6469
6470
6471
6472
6473
6474
6475
6476
6477
6478
6479
6480
6481
6482
6483
6484
6485
6486
6487
6488
6489
6490
6491
6492
6493
6494
6495
6496
6497
6498
6499
6500
6501
6502
6503
6504
6505
6506
6507
6508
6509
6510
6511
6512
6513
6514
6515
6516
6517
6518
6519
6520
6521
6522
6523
6524
6525
6526
6527
6528
6529
6530
6531
6532
6533
6534
6535
6536
6537
6538
6539
6540
6541
6542
6543
6544
6545
6546
6547
6548
6549
6550
6551
6552
6553
6554
6555
6556
6557
6558
6559
6560
6561
6562
6563
6564
6565
6566
6567
6568
6569
6570
6571
6572
6573
6574
6575
6576
6577
6578
6579
6580
6581
6582
6583
6584
6585
6586
6587
6588
6589
6590
6591
6592
6593
6594
6595
6596
6597
6598
6599
6600
6601
6602
6603
6604
6605
6606
6607
6608
6609
6610
6611
6612
6613
6614
6615
6616
6617
6618
6619
6620
6621
6622
6623
6624
6625
6626
6627
6628
6629
6630
6631
6632
6633
6634
6635
6636
6637
6638
6639
6640
6641
6642
6643
6644
6645
6646
6647
6648
6649
6650
6651
6652
6653
6654
6655
6656
6657
6658
6659
6660
6661
6662
6663
6664
6665
6666
6667
6668
6669
6670
6671
6672
6673
6674
6675
6676
6677
6678
6679
6680
6681
6682
6683
6684
6685
6686
6687
6688
6689
6690
6691
6692
6693
6694
6695
6696
6697
6698
6699
6700
6701
6702
6703
6704
6705
6706
6707
6708
6709
6710
6711
6712
6713
6714
6715
6716
6717
6718
6719
6720
6721
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
6749
6750
6751
6752
6753
6754
6755
6756
6757
6758
6759
6760
6761
6762
6763
6764
6765
6766
6767
6768
6769
6770
6771
6772
6773
6774
6775
6776
6777
6778
6779
6780
6781
6782
6783
6784
6785
6786
6787
6788
6789
6790
6791
6792
6793
6794
6795
6796
6797
6798
6799
6800
6801
6802
6803
6804
6805
6806
6807
6808
6809
6810
6811
6812
6813
6814
6815
6816
6817
6818
6819
6820
6821
6822
6823
6824
6825
6826
6827
6828
6829
6830
6831
6832
6833
6834
6835
6836
6837
6838
6839
6840
6841
6842
6843
6844
6845
6846
6847
6848
6849
6850
6851
6852
6853
6854
6855
6856
6857
6858
6859
6860
6861
6862
6863
6864
6865
6866
6867
6868
6869
6870
6871
6872
6873
6874
6875
6876
6877
6878
6879
6880
6881
6882
6883
6884
6885
6886
6887
6888
6889
6890
6891
6892
6893
6894
6895
6896
6897
6898
6899
6900
6901
6902
6903
6904
6905
6906
6907
6908
6909
6910
6911
6912
6913
6914
6915
6916
6917
6918
6919
6920
6921
6922
6923
6924
6925
6926
6927
6928
6929
6930
6931
6932
6933
6934
6935
6936
6937
6938
6939
6940
6941
6942
6943
6944
6945
6946
6947
6948
6949
6950
6951
6952
6953
6954
6955
6956
6957
6958
6959
6960
6961
6962
6963
6964
6965
6966
6967
6968
6969
6970
6971
6972
6973
6974
6975
6976
6977
6978
6979
6980
6981
6982
6983
6984
6985
6986
6987
6988
6989
6990
6991
6992
6993
6994
6995
6996
6997
6998
6999
7000
7001
7002
7003
7004
7005
7006
7007
7008
7009
7010
7011
7012
7013
7014
7015
7016
7017
7018
7019
7020
7021
7022
7023
7024
7025
7026
7027
7028
7029
7030
7031
7032
7033
7034
7035
7036
7037
7038
7039
7040
7041
7042
7043
7044
7045
7046
7047
7048
7049
7050
7051
7052
7053
7054
7055
7056
7057
7058
7059
7060
7061
7062
7063
7064
7065
7066
7067
7068
7069
7070
7071
7072
7073
7074
7075
7076
7077
7078
7079
7080
7081
7082
7083
7084
7085
7086
7087
7088
7089
7090
7091
7092
7093
7094
7095
7096
7097
7098
7099
7100
7101
7102
7103
7104
7105
7106
7107
7108
7109
7110
7111
7112
7113
7114
7115
7116
7117
7118
7119
7120
7121
7122
7123
7124
7125
7126
7127
7128
7129
7130
7131
7132
7133
7134
7135
7136
7137
7138
7139
7140
7141
7142
7143
7144
7145
7146
7147
7148
7149
7150
7151
7152
7153
7154
7155
7156
7157
7158
7159
7160
7161
7162
7163
7164
7165
7166
7167
7168
7169
7170
7171
7172
7173
7174
7175
7176
7177
7178
7179
7180
7181
7182
7183
7184
7185
7186
7187
7188
7189
7190
7191
7192
7193
7194
7195
7196
7197
7198
7199
7200
7201
7202
7203
7204
7205
7206
7207
7208
7209
7210
7211
7212
7213
7214
7215
7216
7217
7218
7219
7220
7221
7222
7223
7224
7225
7226
7227
7228
7229
7230
7231
7232
7233
7234
7235
7236
7237
7238
7239
7240
7241
7242
7243
7244
7245
7246
7247
7248
7249
7250
7251
7252
7253
7254
7255
7256
7257
7258
7259
7260
7261
7262
7263
7264
7265
7266
7267
7268
7269
7270
7271
7272
7273
7274
7275
7276
7277
7278
7279
7280
7281
7282
7283
7284
7285
7286
7287
7288
7289
7290
7291
7292
7293
7294
7295
7296
7297
7298
7299
7300
7301
7302
7303
7304
7305
7306
7307
7308
7309
7310
7311
7312
7313
7314
7315
7316
7317
7318
7319
7320
7321
7322
7323
7324
7325
7326
7327
7328
7329
7330
7331
7332
7333
7334
7335
7336
7337
7338
7339
7340
7341
7342
7343
7344
7345
7346
7347
7348
7349
7350
7351
7352
7353
7354
7355
7356
7357
7358
7359
7360
7361
7362
7363
7364
7365
7366
7367
7368
7369
7370
7371
7372
7373
7374
7375
7376
7377
7378
7379
7380
7381
7382
7383
7384
7385
7386
7387
7388
7389
7390
7391
7392
7393
7394
7395
7396
7397
7398
7399
7400
7401
7402
7403
7404
7405
7406
7407
7408
7409
7410
7411
7412
7413
7414
7415
7416
7417
7418
7419
7420
7421
7422
7423
7424
7425
7426
7427
7428
7429
7430
7431
7432
7433
7434
7435
7436
7437
7438
7439
7440
7441
7442
7443
7444
7445
7446
7447
7448
7449
7450
7451
7452
7453
7454
7455
7456
7457
7458
7459
7460
7461
7462
7463
7464
7465
7466
7467
7468
7469
7470
7471
7472
7473
7474
7475
7476
7477
7478
7479
7480
7481
7482
7483
7484
7485
7486
7487
7488
7489
7490
7491
7492
7493
7494
7495
7496
7497
7498
7499
7500
7501
7502
7503
7504
7505
7506
7507
7508
7509
7510
7511
7512
7513
7514
7515
7516
7517
7518
7519
7520
7521
7522
7523
7524
7525
7526
7527
7528
7529
7530
7531
7532
7533
7534
7535
7536
7537
7538
7539
7540
7541
7542
7543
7544
7545
7546
7547
7548
7549
7550
7551
7552
7553
7554
7555
7556
7557
7558
7559
7560
7561
7562
7563
7564
7565
7566
7567
7568
7569
7570
7571
7572
7573
7574
7575
7576
7577
7578
7579
7580
7581
7582
7583
7584
7585
7586
7587
7588
7589
7590
7591
7592
7593
7594
7595
7596
7597
7598
7599
7600
7601
7602
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
7657
7658
7659
7660
7661
7662
7663
7664
7665
7666
7667
7668
7669
7670
7671
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
7726
7727
7728
7729
7730
7731
7732
7733
7734
7735
7736
7737
7738
7739
7740
7741
7742
7743
7744
7745
7746
7747
7748
7749
7750
7751
7752
7753
7754
7755
7756
7757
7758
7759
7760
7761
7762
7763
7764
7765
7766
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
7809
7810
7811
7812
7813
7814
7815
7816
7817
7818
7819
7820
7821
7822
7823
7824
7825
7826
7827
7828
7829
7830
7831
7832
7833
7834
7835
7836
7837
7838
7839
7840
7841
7842
7843
7844
7845
7846
7847
7848
7849
7850
7851
7852
7853
7854
7855
7856
7857
7858
7859
7860
7861
7862
7863
7864
7865
7866
7867
7868
7869
7870
7871
7872
7873
7874
7875
7876
7877
7878
7879
7880
7881
7882
7883
7884
7885
7886
7887
7888
7889
7890
7891
7892
7893
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
7919
7920
7921
7922
7923
7924
7925
7926
7927
7928
7929
7930
7931
7932
7933
7934
7935
7936
7937
7938
7939
7940
7941
7942
7943
7944
7945
7946
7947
7948
7949
7950
7951
7952
7953
7954
7955
7956
7957
7958
7959
7960
7961
7962
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
8003
8004
8005
8006
8007
8008
8009
8010
8011
8012
8013
8014
8015
8016
8017
8018
8019
8020
8021
8022
8023
8024
8025
8026
8027
8028
8029
8030
8031
8032
8033
8034
8035
8036
8037
8038
8039
8040
8041
8042
8043
8044
8045
8046
8047
8048
8049
8050
8051
8052
8053
8054
8055
8056
8057
8058
8059
8060
8061
8062
8063
8064
8065
8066
8067
8068
8069
8070
8071
8072
8073
8074
8075
8076
8077
8078
8079
8080
8081
8082
8083
8084
8085
8086
8087
8088
8089
8090
8091
8092
8093
8094
8095
8096
8097
8098
8099
8100
8101
8102
8103
8104
8105
8106
8107
8108
8109
8110
8111
8112
8113
8114
8115
8116
8117
8118
8119
8120
8121
8122
8123
8124
8125
8126
8127
8128
8129
8130
8131
8132
8133
8134
8135
8136
8137
8138
8139
8140
8141
8142
8143
8144
8145
8146
8147
8148
8149
8150
8151
8152
8153
8154
8155
8156
8157
8158
8159
8160
8161
8162
8163
8164
8165
8166
8167
8168
8169
8170
8171
8172
8173
8174
8175
8176
8177
8178
8179
8180
8181
8182
8183
8184
8185
8186
8187
8188
8189
8190
8191
8192
8193
8194
8195
8196
8197
8198
8199
8200
8201
8202
8203
8204
8205
8206
8207
8208
8209
8210
8211
8212
8213
8214
8215
8216
8217
8218
8219
8220
8221
8222
8223
8224
8225
8226
8227
8228
8229
8230
8231
8232
8233
8234
8235
8236
8237
8238
8239
8240
8241
8242
8243
8244
8245
8246
8247
8248
8249
8250
8251
8252
8253
8254
8255
8256
8257
8258
8259
8260
8261
8262
8263
8264
8265
8266
8267
8268
8269
8270
8271
8272
8273
8274
8275
8276
8277
8278
8279
8280
8281
8282
8283
8284
8285
8286
8287
8288
8289
8290
8291
8292
8293
8294
8295
8296
8297
8298
8299
8300
8301
8302
8303
8304
8305
8306
8307
8308
8309
8310
8311
8312
8313
8314
8315
8316
8317
8318
8319
8320
8321
8322
8323
8324
8325
8326
8327
8328
8329
8330
8331
8332
8333
8334
8335
8336
8337
8338
8339
8340
8341
8342
8343
8344
8345
8346
8347
8348
8349
8350
8351
8352
8353
8354
8355
8356
8357
8358
8359
8360
8361
8362
8363
8364
8365
8366
8367
8368
8369
8370
8371
8372
8373
8374
8375
8376
8377
8378
8379
8380
8381
8382
8383
8384
8385
8386
8387
8388
8389
8390
8391
8392
8393
8394
8395
8396
8397
8398
8399
8400
8401
8402
8403
8404
8405
8406
8407
8408
8409
8410
8411
8412
8413
8414
8415
8416
8417
8418
8419
8420
8421
8422
8423
8424
8425
8426
8427
8428
8429
8430
8431
8432
8433
8434
8435
8436
8437
8438
8439
8440
8441
8442
8443
8444
8445
8446
8447
8448
8449
8450
8451
8452
8453
8454
8455
8456
8457
8458
8459
8460
8461
8462
8463
8464
8465
8466
8467
8468
8469
8470
8471
8472
8473
8474
8475
8476
8477
8478
8479
8480
8481
8482
8483
8484
8485
8486
8487
8488
8489
8490
8491
8492
8493
8494
8495
8496
8497
8498
8499
8500
8501
8502
8503
8504
8505
8506
8507
8508
8509
8510
8511
8512
8513
8514
8515
8516
8517

            if(obj) cleaner(obj);
        }
    }
    cson_kvp_list_reserve(self,0);
}
/* end file ./cson_lists.h */
/* begin file ./cson_session.c */
#include <string.h> /* strlen() */
#include <stdlib.h> /* memcpy() */
#include <assert.h>


/**
   Maximum name length for cson_sessmgr_register(),
   including the trailing NUL.
*/
enum {
   CsonSessionNameLen = 32
};

typedef struct cson_sessmgr_reg cson_sessmgr_reg;
/**
   Holds name-to-factory mappings for cson_sessmgr implementations.
*/
struct cson_sessmgr_reg
{
    char name[CsonSessionNameLen];
    cson_sessmgr_factory_f factory;
};


#if !CSON_ENABLE_CPDO
int cson_sessmgr_cpdo( cson_sessmgr ** tgt, cson_object const * opt )
{
    return cson_rc.UnsupportedError;
}
#endif
#if !CSON_ENABLE_WHIO
int cson_sessmgr_whio_ht( cson_sessmgr ** tgt, cson_object const * opt )
{
    return cson_rc.UnsupportedError;
}
int cson_sessmgr_whio_epfs( cson_sessmgr ** tgt, cson_object const * opt )
{
    return cson_rc.UnsupportedError;
}
#endif

/**
   Holds the list of registered cson_sessmgr implementations. Used by
   cson_sessmgr_register(), cson_sessmgr_load(), and
   cson_sessmgr_names().

   Maintenance reminder: the API docs promise that at least 10 slots
   are initially available.
*/
static cson_sessmgr_reg CsonSessionReg[] = {
{{'f','i','l','e',0},cson_sessmgr_file},
#if CSON_ENABLE_CPDO
{{'c','p','d','o',0},cson_sessmgr_cpdo},
#endif
#if CSON_ENABLE_WHIO
{{'w','h','i','o','_','h','t',0},cson_sessmgr_whio_ht},
{{'w','h','i','o','_','e','p','f','s',0},cson_sessmgr_whio_epfs},
#endif
#define REG {{0},NULL}
REG,REG,REG,REG,REG,
REG,REG,REG,REG,REG
#undef REG
};
static const unsigned int CsonSessionRegLen = sizeof(CsonSessionReg)/sizeof(CsonSessionReg[0]);


int cson_sessmgr_register( char const * name, cson_sessmgr_factory_f f )
{
    if( ! name || !*name || !f ) return cson_rc.ArgError;
    else
    {
        cson_sessmgr_reg * r = CsonSessionReg;
        unsigned int nlen = strlen(name);
        unsigned int i = 0;
        if( nlen >= CsonSessionNameLen ) return cson_rc.RangeError;
        for( ; i < CsonSessionRegLen; ++i, ++r )
        {
            if( r->name[0] ) continue;
            memcpy( r->name, name, nlen );
            r->name[nlen] = 0;
            r->factory = f;
            return 0;
        }
        return cson_rc.RangeError;
    }
}


int cson_sessmgr_load( char const * name, cson_sessmgr ** tgt, cson_object const * opt )
{
    if( ! name || !*name || !tgt ) return cson_rc.ArgError;
    else
    {
        cson_sessmgr_reg const * r = CsonSessionReg;
        unsigned int i = 0;
        for( ; i < CsonSessionRegLen; ++i, ++r )
        {
            if( ! r->name[0] ) break /* end of list */;
            else if( 0 != strcmp( r->name, name ) ) continue;
            else
            {
                assert( NULL != r->factory );
                return r->factory( tgt, opt );
            }
            
        }
        return cson_rc.UnsupportedError;
    }
}

char const * const * cson_sessmgr_names()
{
    static char const * names[sizeof(CsonSessionReg)/sizeof(CsonSessionReg[0])+1];
    unsigned int i = 0;
    cson_sessmgr_reg const * r = CsonSessionReg;
    for( ; i < CsonSessionRegLen; ++i, ++r )
    {
        /*
          pedantic threading note: as long as this function is not
          used concurrently with cson_sessmgr_register(), the worst we
          will do here if this function is called, or its results
          used, concurrently is overwrite in-use values with the same
          values.
         */
        names[i] = r->name[0] ? r->name : NULL;
    }
    names[i] = NULL;
    return names;
}
/* end file ./cson_session.c */
/* begin file ./cson_session_file.c */
#if !defined(_WIN32) && !defined(_WIN64)
#  if !defined(_POSIX_VERSION)
#    define _POSIX_VERSION 200112L /* chmod(), unlink() */
#  endif
#  define ENABLE_POSIX_FILE_OPS 1
#else
#  define ENABLE_POSIX_FILE_OPS 0
#endif

#include <stdlib.h>
#include <string.h>
#include <assert.h>
#if ENABLE_POSIX_FILE_OPS
#  define UNLINK_FILE unlink
#  include <unistd.h> /* unlink() */
#  include <sys/stat.h> /* chmod() */
#else
/* http://msdn.microsoft.com/en-us/library/1c3tczd6(v=vs.80).aspx
  #  define UNLINK_FILE _unlink
  #  include <io.h>
*/
#  define UNLINK_FILE remove
#  include <stdio.h> /* remove(), _unlink() */
#endif

static int cson_session_file_load( cson_sessmgr * self, cson_value ** tgt, char const * id );
static int cson_session_file_save( cson_sessmgr * self, cson_value const * root, char const * id );
static int cson_session_file_remove( cson_sessmgr * self, char const * id );
static void cson_session_file_finalize( cson_sessmgr * self );

static const cson_sessmgr_api cson_sessmgr_api_file =
{
    cson_session_file_load,
    cson_session_file_save,
    cson_session_file_remove,
    cson_session_file_finalize
};

typedef struct cson_sessmgr_file_impl cson_sessmgr_file_impl;
struct cson_sessmgr_file_impl
{
    char * dir;
    char * prefix;
    char * suffix;
};

static const cson_sessmgr cson_sessmgr_file_empty =
{
    &cson_sessmgr_api_file,
    NULL
};

static char * cson_session_file_strdup( char const * src )
{
    size_t const n = src ? strlen(src) : 0;
    char * rc = src ? (char *)calloc(1, n+1) : NULL;
    if( ! rc ) return NULL;
    memcpy( rc, src, n );
    return rc;
}

/* Helper macro for varios cson_sessmgr_api member implementations. */
#define IMPL_DECL(RC) \
    cson_sessmgr_file_impl * impl = (self && (self->api == &cson_sessmgr_api_file)) \
    ? (cson_sessmgr_file_impl*)self->impl \
    : NULL;                                   \
    if( NULL == impl ) return RC

static int cson_session_file_name( cson_sessmgr_file_impl * impl,
                                   char const * id,
                                   char * buf, unsigned int bufLen )
{
    char const * dir = impl->dir ? impl->dir : ".";
    char const * pre = impl->prefix ? impl->prefix : "";
    char const * suf = impl->suffix ? impl->suffix : "";
    char * pos = NULL /* current write possition. */;
    unsigned int flen = 0 /* length of the next token. */;
    unsigned int olen = 0 /* total number of bytes written so far. */;
    if( ! id || !*id ) return cson_rc.ArgError;

#define CHECKLEN if(olen >= bufLen) return cson_rc.RangeError; assert( pos < (buf+bufLen) )
    pos = buf;

#define PUSH(FIELD) \
    flen = strlen(FIELD); \
    olen += flen; \
    CHECKLEN; \
    strncpy( pos, FIELD, flen ); \
    pos += flen

    PUSH(dir);

    ++olen;
    CHECKLEN;
#if defined(_WIN32)
    *(pos++) = '\\';
#else
    *(pos++) = '/';
#endif

    PUSH(pre);
    PUSH(id);
    PUSH(suf);
    if( pos >= (buf + bufLen) ) return cson_rc.RangeError;
    *pos = 0;
    return 0;
#undef PUSH
#undef CHECKLEN
}

static int cson_session_file_load( cson_sessmgr * self, cson_value ** root, char const * id )
{
    enum { BufSize = 1024 };
    char fname[BufSize];
    FILE * fh = NULL;
    int rc;
    IMPL_DECL(cson_rc.ArgError);
    if( !root || !id || !*id ) return cson_rc.ArgError;
    memset( fname, 0, BufSize );
    rc = cson_session_file_name( impl, id, fname, BufSize );
    if( 0 != rc ) return rc;
    fh = fopen( fname, "r" );
    if( ! fh ) return cson_rc.IOError;
    rc = cson_parse_FILE( root, fh, NULL, NULL );
    fclose( fh );
    return rc;
}

static int cson_session_file_save( cson_sessmgr * self, cson_value const * root, char const * id )
{
    enum { BufSize = 1024 };
    char fname[BufSize];
    FILE * fh = NULL;
    int rc;
    IMPL_DECL(cson_rc.ArgError);
    if( !root || !id || !*id ) return cson_rc.ArgError;
    memset( fname, 0, BufSize );

    rc = cson_session_file_name( impl, id, fname, BufSize );
    if( 0 != rc ) return rc;
    fh = fopen( fname, "w" );
    if( ! fh ) return cson_rc.IOError;
#if ENABLE_POSIX_FILE_OPS
    chmod( fname, 0600 );
#endif
    rc = cson_output_FILE( root, fh, NULL );
    fclose( fh );
    if( rc )
    {
        UNLINK_FILE( fname );
    }
    return rc;
}

void cson_session_file_finalize( cson_sessmgr * self )
{
    if( self && (self->api == &cson_sessmgr_api_file) )
    {
        cson_sessmgr_file_impl * impl = (cson_sessmgr_file_impl *)self->impl;
        free( impl->dir );
        free( impl->prefix );
        free( impl->suffix );
        free( impl );
        *self = cson_sessmgr_file_empty;
        free( self );
    }
}

static int cson_session_file_remove( cson_sessmgr * self, char const * id )
{
    enum { BufSize = 1024 };
    char fname[BufSize];
    int rc;
    IMPL_DECL(cson_rc.ArgError);
    if( !id || !*id ) return cson_rc.ArgError;
    memset( fname, 0, BufSize );
    rc = cson_session_file_name( impl, id, fname, BufSize );
    if( 0 != rc ) return rc;
    rc = UNLINK_FILE( fname );
    return (0==rc) ? 0 : cson_rc.IOError;
}


int cson_sessmgr_file( cson_sessmgr ** tgt, cson_object const * opt )
{
    int rc;
    cson_sessmgr * m = tgt ? (cson_sessmgr *)malloc(sizeof(cson_sessmgr)) : NULL;
    cson_sessmgr_file_impl * impl = m
        ? (cson_sessmgr_file_impl *)malloc(sizeof(cson_sessmgr_file_impl))
        : NULL;
    if( ! m ) return tgt ? cson_rc.AllocError : cson_rc.ArgError;
    else if( ! impl )
    {
        free(m);
        return cson_rc.AllocError;
    }
    *m = cson_sessmgr_file_empty;
    m->impl = impl;
    if( opt )
    {
        cson_string const * jstr;
#define CP(KEY) \
        jstr = cson_value_get_string( cson_object_get( opt, # KEY ) ); \
        if( jstr ) { \
            impl->KEY = cson_session_file_strdup( cson_string_cstr( jstr ) ); \
            if( ! impl->KEY ) { \
                rc = cson_rc.AllocError;        \
                goto error_clean;               \
            } \
        } (void)0
        
        CP(dir);
        CP(prefix);
        CP(suffix);
#undef CP
    }
#define CP(KEY,VAL) if( ! impl->KEY ) { \
            impl->KEY = cson_session_file_strdup(VAL); \
            if( ! impl->KEY ) { \
                rc = cson_rc.AllocError;        \
                goto error_clean;               \
            } \
        } (void)0
#if ENABLE_POSIX_FILE_OPS
    CP(dir,"/tmp");
#else
    CP(dir,".");
#endif
    CP(prefix,"cson-session-");
    CP(suffix,".json");
#undef CP
    *tgt = m;
    return 0;
    error_clean:
    m->api->finalize( m );
    return rc;
}

#undef IMPL_DECL
#undef ENABLE_POSIX_FILE_OPS
#undef UNLINK_FILE
/* end file ./cson_session_file.c */
/* begin file ./cson_sqlite3.c */
/** @file cson_sqlite3.c

This file contains the implementation code for the cson
sqlite3-to-JSON API.

License: the same as the cson core library.
................................................................................
#define MARKER if(0) printf
#endif

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

static cson_value * cson_sqlite3_stmt_to_value( sqlite3_stmt * st, int col )
{
    if( ! st ) return NULL;
    else
    {
#if 0
        sqlite3_value * val = sqlite3_column_type(st,col);
        int const vtype = val ? sqlite3_value_type(val) : -1;
        if( ! val ) return cson_value_null();
#else
        int const vtype = sqlite3_column_type(st,col);
#endif
        switch( vtype )
        {
            case SQLITE_NULL:
                return cson_value_null();
          case SQLITE_INTEGER: {

              return cson_value_new_integer( (cson_int_t) sqlite3_column_int64(st, col)  );
          }
            case SQLITE_FLOAT:
                return cson_value_new_double( sqlite3_column_double(st, col) );
            case SQLITE_BLOB: /* arguably fall through... */
            case SQLITE_TEXT: {
                char const * str = (char const *)sqlite3_column_text(st,col);
                return cson_value_new_string(str, str ? strlen(str) : 0);
            }
            default:
                return NULL;
        }
    }
}

/**
    st must be a valid prepared statement. This function creates
    a JSON array containing its columns, in order.
    
    Returns a new array value on success, which the caller owns.
    On error NULL is returned.
    
    st is not traversed or freed by this function - only the column
    count and names are read.
*/
static cson_value * cson_sqlite3_stmt_cols( sqlite3_stmt * st )
{
    cson_value * aryV = NULL;
    cson_array * ary = NULL;
    char const * colName = NULL;
    int i = 0;
    int rc = 0;
    int colCount = 0;
................................................................................
    else
    {
        cson_value_free(aryV);
        return NULL;
    }
}





































































/**
    Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
    parameter is non-0.
*/
static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt )
{
#define RETURN(RC) { if(rootV) cson_value_free(rootV); return RC; }
................................................................................
    {
        cson_value * rootV = NULL;
        cson_object * root = NULL;
        cson_value * colsV = NULL;
        cson_value * rowsV = NULL;
        cson_array * rows = NULL;
        cson_value * objV = NULL;
        cson_object * obj = NULL;
        cson_value * currentValue = NULL;
        char const * colName = NULL;
        int rc = 0;
        int i = 0;
        int const colCount = sqlite3_column_count(st);
        if( colCount <= 0 ) return cson_rc.ArgError;
        rootV = cson_value_new_object();
        if( ! rootV ) return cson_rc.AllocError;
        colsV = cson_sqlite3_stmt_cols(st);
        if( ! colsV )
        {
            cson_value_free( rootV );
            RETURN(cson_rc.AllocError);
        }
        root = cson_value_get_object(rootV);
        rc = cson_object_set( root, "columns", colsV );
        if( rc )
        {
            cson_value_free( colsV );
            RETURN(rc);
        }

        colsV = NULL;
        
        rowsV = cson_value_new_array();
        if( ! rowsV ) RETURN(cson_rc.AllocError);
        rc = cson_object_set( root, "rows", rowsV );
        if( rc )
        {
            cson_value_free( rowsV );
            RETURN(rc);
        }
        rows = cson_value_get_array(rowsV);
        assert(rows);
        while( SQLITE_ROW == sqlite3_step(st) )
        {
            objV = cson_value_new_object();
            if( ! objV ) RETURN(cson_rc.AllocError);
            rc = cson_array_append( rows, objV );
            if( rc )
            {
                cson_value_free( objV );
                RETURN(rc);
            }
            obj = cson_value_get_object(objV);
            for( i = 0; i < colCount; ++i )
            {
                colName = sqlite3_column_name( st, i );
                if( ! colName ) RETURN(cson_rc.AllocError);
                currentValue = cson_sqlite3_stmt_to_value(st,i);
                if( ! currentValue ) currentValue = cson_value_null();
                rc = cson_object_set( obj, colName, currentValue );
                if( 0 != rc )
                {
                    cson_value_free( currentValue );
                    RETURN(rc);
                }
            }
        }
        *tgt = rootV;
        return 0;
    }
#undef RETURN
}

................................................................................
    {
        cson_value * rootV = NULL;
        cson_object * root = NULL;
        cson_value * aryV = NULL;
        cson_array * ary = NULL;
        cson_value * rowsV = NULL;
        cson_array * rows = NULL;
        cson_value * colV = NULL;
        int rc = 0;
        int i = 0;
        int const colCount = sqlite3_column_count(st);
        if( colCount <= 0 ) return cson_rc.ArgError;
        rootV = cson_value_new_object();
        if( ! rootV ) return cson_rc.AllocError;
        aryV = cson_sqlite3_stmt_cols(st);
        if( ! aryV )
        {
            cson_value_free( rootV );
            RETURN(cson_rc.AllocError);
        }
        root = cson_value_get_object(rootV);
        rc = cson_object_set( root, "columns", aryV );
................................................................................
            cson_value_free( rowsV );
            RETURN(rc);
        }
        rows = cson_value_get_array(rowsV);
        assert(rows);
        while( SQLITE_ROW == sqlite3_step(st) )
        {
            aryV = cson_value_new_array();
            if( ! aryV ) RETURN(cson_rc.AllocError);
            rc = cson_array_append( rows, aryV );
            if( 0 != rc )
            {
                cson_value_free( aryV );
                RETURN(rc);
            }
            ary = cson_value_get_array(aryV);
            rc = cson_array_reserve(ary, (unsigned int) colCount );
            if( 0 != rc ) RETURN(rc);
            for( i = 0; i < colCount; ++i )
            {
                colV = cson_sqlite3_stmt_to_value(st,i);
                if( ! colV ) colV = cson_value_null();
                rc = cson_array_set( ary, i, colV );
                if( 0 != rc )
                {
                    cson_value_free( colV );
                    RETURN(rc);
                }
            }
        }
        *tgt = rootV;
        return 0;
    }
#undef RETURN
}

................................................................................

#if defined(__cplusplus)
} /*extern "C"*/
#endif
#undef MARKER
#endif /* CSON_ENABLE_SQLITE3 */
/* end file ./cson_sqlite3.c */
/* begin file cgi/whuuid.h */
#if !defined(WANDERGINHORSE_NET_WHUUID_H_INCLUDED)
#define WANDERGINHORSE_NET_WHUUID_H_INCLUDED 1
#include <stdio.h> /* only for decl of FILE. */
/************************************************************************
An experiment in creating random UUIDs (http://wikipedia.org/wiki/Uuid).


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

License: Public Domain


Features:

- Small API. Only two relevant classes and a handful of functions.

- Uses a client-specified RNG source. Two are provided with the
library. The RNG source may be arbitrarily stateful, and each may have
instance-specific data.

- State objects have a uniform cleanup interface, but each implementation
defines which cleanup behaviours need to be performed (e.g. closing
an input file).

- Fairly fast, assuming your RNG is. (My 2.6GHz PC can generate, and send
them to stdout, just over 1.3 million UUIDs per second.)


Misfeatures:

- Does not support a specific version of UUID, as detailed at
[http://wikipedia.org/wiki/Uuid]. Its UUIDs have random data in all
positions, as opposed to reserving certain positions for specific
values or using specified algorithms to generate the values. Thus the
UUIDs it generates are similar to Version 4 UUIDs except that no bytes
are reserved for specific values.

PS: i don't really consider that to be a mis-feature. IMHO UUIDs
should be completely random, with no reserved bytes.


------------------------------------------------------------------------
TIP: checking for duplicate UUIDs

The sqlite3 tool can be used for checking for duplicate UUIDs. Simply
print the UUIDs, one per line, and feed them into sqlite3 like so:

@code
sqlite3> create table ids (id,unide(id));
sqlite3> .import myUUIDListFile ids
@endcode

If sqlite3 does not complain, there were no duplicates.

You can also test by sorting the list, removing duplicates, and
checking the length of the list. e.g. assume we have a file named "1m"
containing 1 million UUIDs. From a Unix shell we could do:

@code
~> sort -u < 1m > 1ms
~> ls -la 1m 1ms
@endcode

If the files have the same size then there were no duplicates.

In my tests i have not encountered duplicates except when testing
a deterministic RNG with a specific seed.
************************************************************************/

/** @def WHUUID_CONFIG_KEEP_METRICS

    If WHUUID_CONFIG_KEEP_METRICS is a true value then the library keeps track
    of how many times a given hex digit value is generated by the
    whuuid_rng class. It has a minimal performance hit, but if
    the data will never be used then it can be turned off.
*/
#define WHUUID_CONFIG_KEEP_METRICS 1

/** @enum whuuid_constants

A list of constant values used by the whuuid API.

*/
enum whuuid_constants {
/**
   The length of a UUID canonical-form string, not including
   a trailing NULL bytes. e.g.:

   00000000-0000-0000-0000-000000000000
*/
whuuid_length_canonical = 36,
/**
   The length of a UUID in canonical form, including
   a trailing NULL byte.
*/
whuuid_length_cstring = whuuid_length_canonical + 1,
/**
   The number of bytes of data necessary to store
   a UUID in "raw" form.
*/
whuuid_length_bytes = 16
};

/**
   Represents a single UUID.
*/
struct whuuid_t
{
    unsigned char bytes[whuuid_length_bytes];
};
typedef struct whuuid_t whuuid_t;
/**
   A zero-initialized whuiid_t initialization object.
*/
extern const whuuid_t whuuid_t_empty;

/**
   A class holding RNG information. Each instance manages a single RNG
   source, which is used to populate any number of whuiid_t objects
   with random data. They may or may not need to dynamically allocate
   resources (e.g. open a file containing random data), depending
   on the implementation.   

   They should normally be initialized via factory functions, and
   those functions should:

   a) Allocate any private resources the object needs and store them in
   self->impl.

   b) Set the cleanup() member function to a function which knows how
   to clean up any resources stored in self->impl.

   c) Set the rand() member to a function which knows how to use
   the private state to generate random data.


   The most basic usage looks something like this:

   @code
   whuuid_rng st = whuuid_rng_lcrng; // a Linear Congruent RNG
   whuuid_t u = whuuid_t_empty;
   char buffer[whuuid_length_canonical+1]; // buffer for UUID string
   buffer[whuuid_length_canonical] = 0; // add trailing NULL
   for( int i =0; i < 100; ++i )
   {// generate 100 UUIDs to print them
       whuuid_fill_rand( &u, &st ); // generate UUID using st->rand()
       whuuid_to_string( &u, buffer );
       puts(buffer);
   }
   st.cleanup(&st); // see below.
   @endcode

   In that particular case the state object has no state which
   needs cleaning, but we could also set up a FILE as an input source,
   in which case we need to clean up the object:

   @code
   st = whuuid_rng_FILE;
   st.impl = fopen("/dev/urandom", "r");
   ... use st ...
   st.cleanup(&st); // will fclose() the file
   @endcode

   If a state object is dynamically allocated then it should be freed
   after calling its cleanup() member to free any
   implementation-specific resources.
*/
struct whuuid_rng
{
    /**
       Must set *tgt to sizeof(unsigned int) random bytes. Must return
       0 on success or non-zero if something goes wrong (e.g. the
       input source has failed or run out of numbers). How it uses (or
       ignores) the self argument is implementation-specific.
    */
    int (*rand)( struct whuuid_rng * self, unsigned int  * tgt );
    /**
       Must clean up self, but not free self itself. How it does this
       is implementation-specific. If it has no private state,
       this function may be NULL.

       whuuid_rng objects can be allocated on the stack or via
       arbitrary mechanisms, so the cleanup routine must not free the
       self object. How it is freed (after it is cleaned up) depends
       on how it was allocated.
    */
    void (*cleanup)( struct whuuid_rng * self );
    /**
       Implementations may store any private state here. This member is
       not for public use.
    */
    void * impl;
    /**
       Stores the distribution of values created by this state
       object. whuuid_fill_rand() updates these values.
    */
    unsigned long distribution[whuuid_length_bytes];
};


/** Convenience typedef. */
typedef struct whuuid_rng whuuid_rng;

/**
   A zero-initialized whuiid_state initialization object.
*/
extern const whuuid_rng whuuid_rng_empty;

/**
   An almost-empty whuiid_state initialization object with
   its rand() member set to whuuid_lc_rand.
*/
extern const whuuid_rng whuuid_rng_lcrng;

/**
   A whuuid_state initialization object with its rand() member set to
   whuuid_FILE_rand and its cleanup() member set to
   whuuid_FILE_cleanup.  Clients may copy this then set the impl
   member to point it to an opened FILE handle. The FILE handle will
   be closed when the cleanup() member is called. If the state object
   should not close the file when it cleans up, set the cleanup()
   member to NULL.
*/
extern const whuuid_rng whuuid_rng_FILE;

/**
   Implements the whuuid_rng::rand() interface.

   This implementaion uses/abuses st->impl to store a numeric state
   value for a linear congruent RNG. If st->impl is NULL then a seed
   value is generated using some external source (we malloc() a few
   bytes to get a random address, and we use that address as a
   seed). The state value is stored directly in st->impl and does not
   need to be cleaned up. (The memory malloc()ed to get the initial
   seed is free()d immediately after it is malloc()ed.)

   Returns 0 on success, non-zero on error. The only error conditions
   are !st or !tgt. A malloc() error on the initial seeding will not
   cause an error (but causes a determinate (but unspecified) seed
   value to be used).

   In my (informal/unscientific) tests, this RNG works very well for
   generating UUIDs, out-performing /dev/urandom in terms of even
   numeric distribution most of the time.
*/
int whuuid_lc_rand( whuuid_rng * st, unsigned int *tgt );

/**
   Implements the whuuid_rng::rand() interface.

   If st->impl is not NULL it is assumed to be-a (FILE*) and
   sizeof(unsigned int) bytes are read from it and returned via the
   tgt argument.

   Returns non-zero if !st or !st->impl, or if reading from the file
   fails.

   Results are undefined if st->impl is non-null but is-not-a FILE.

   Note that this implementation does nothing fancy like buffering
   some larger amount of random input. Each call reads sizeof(int)
   bytes. If performance is of a concern, create an implementation
   which stores a struct containing the FILE and the buffer somewhere
   in st->impl and reads the input in larger blocks. Also implement a
   cleanup function which can free the buffer.

   @see whuuid_FILE_cleanup()
   @see whuuid_rng_FILE
*/
int whuuid_FILE_rand( whuuid_rng * st, unsigned int * tgt );

/**
   Implements the whuuid_rng::cleanup() interface for state
   objects where obj->impl is-a FILE handle opened via
   fopen() (or equivalent).
   
   Assumes self->impl is-a (FILE*) and calls fclose() on it.
*/
void whuuid_FILE_cleanup( whuuid_rng * self );

/**
   Converts src->bytes to a canonical-form UUID string.  dest must be
   valid memory at least whuuid_length_canonical bytes long, and on
   success exactly whuuid_length_canonical bytes will be written to it.
   No terminating null is added.

   Returns 0 on success, non-zero on error. The only error conditions
   are (!src) or (!dest).
*/
int whuuid_to_string( whuuid_t const * src, char * dest );

/**
   Populates all of dest->bytes, using st->rand() to collect the
   random bytes. It calls st->rand() enough times to collect
   whuuid_length_bytes bytes.

   Returns 0 on success, non-0 on error. The error conditions are:

   - !st or !dest

   - st->rand() returns non-0, in which case that error code is passed
   back to the caller.

   st->distribution is modified by this function to record the number
   of times any given digit (hex 0..f) is generated via a call to
   rand() (but note that each call to st->rand() is used to generate
   (sizeof(unsigning int)*2) digits).

   This routine does not guaranty that the bytes returned by
   st->rand() are used in the exact same order as they are returned.
*/
int whuuid_fill_rand( whuuid_t * dest, whuuid_rng * st );

/**
   Copies whuuid_length_bytes bytes from src to dest->bytes.

   Returns 0 on success. The only error cases are !dest or !src.
*/
int whuuid_fill( whuuid_t * dest, unsigned char const * src );


/**
   Compares lhs->bytes and rhs->bytes and
   returns 0, less than 0, or greater than 0 depending on whether
   lhs equals, is less than, or is greater to rhs, respectively.
   i.e. it behaves like memcmp(3).

   A NULL value for lhs or rhs compares as less-than any other value
   except NULL, to which it compares equal.
*/
short whuuid_compare( whuuid_t const * lhs, whuuid_t const * rhs );

/**
   Debugging/testing function which dumps the RNG distribution counts
   of st to the given FILE handle. The stats are updated on each call
   to whuuid_fill_rand() IF the WHUUID_CONFIG_KEEP_METRICS macro is
   set to a true value when the library is built.

   If full is non-zero then a full list of metrics is dumped,
   otherwise just an overview.

   Returns 0 on success, non-zero on error (!dest, !st, or
   WHUUID_CONFIG_KEEP_METRICS is false).
*/
int whuuid_dump_distribution( whuuid_rng const * st, short full, FILE * dest );

#endif /* WANDERGINHORSE_NET_WHUUID_H_INCLUDED */
/* end file cgi/whuuid.h */
/* begin file cgi/whuuid.c */
#include <assert.h>
#include <string.h> /* memset() */

#include <stdlib.h> /* malloc(), free() */


#if WHUUID_CONFIG_KEEP_METRICS
#  include <stdio.h> /* fprintf(), FILE */
#endif

const whuuid_t whuuid_t_empty = {
{0,0,0,0,
 0,0,0,0,
 0,0,0,0,
 0,0,0,0}/*bytes*/
};


static void whuuid_noop_cleanup( whuuid_rng * self )
{
    /* does nothing */
}
/**
   An almost-empty-initialized whuuid_rng object which uses
   whuuid_rand_uuint() as its random data source. It has no resources
   associated with it.
*/
const whuuid_rng whuuid_rng_empty = {
NULL/*rand*/,
whuuid_noop_cleanup/*cleanup*/,
NULL/*impl*/,
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}/*distribution*/
};

const whuuid_rng whuuid_rng_lcrng = {
whuuid_lc_rand/*rand*/,
whuuid_noop_cleanup/*cleanup*/,
NULL/*impl*/,
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}/*distribution*/
};

const whuuid_rng whuuid_rng_FILE = {
whuuid_FILE_rand/*rand*/,
whuuid_FILE_cleanup/*cleanup*/,
NULL/*impl*/,
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}/*distribution*/
};

/** BITS2CHAR(X) expects (X<=15). Returns the hex-code char for it
    ('0'..'f'), or 0 if X is out of range. */
#define BITS2CHAR(X) ( ((X)<=0x09) ? ('0'+(X)) : (((X)<=0xF) ? ('a'+((X)-10)) : 0))


void whuuid_FILE_cleanup( whuuid_rng * self )
{
    if( self && self->impl )
    {
        fclose( (FILE*)self->impl );
        self->impl = 0;
    }
}

int whuuid_FILE_rand( whuuid_rng * st, unsigned int * tgt )
{
    if( st && st->impl )
    {
        unsigned int d = 0;
        if( 1 != fread( &d, sizeof(d), 1, (FILE*)st->impl ) )
        {
            return -1;
        }
        *tgt = d;
        return 0;
    }
    return -1;
}

#include <time.h>
int whuuid_lc_rand( whuuid_rng * st, unsigned int  * tgt )
{
    typedef unsigned long NumType;
    NumType num = (NumType)st->impl;
    if( ! st || ! tgt ) return -1;
#define RNG(SEED) (NumType)( (NumType)((NumType)(SEED) * (NumType)1103515245) + 12345)
    /* ^^^^^ This RNG Works very well for this use case (comparable
       with /dev/urandom on my box). Algo found in Angband sources. */
    if( ! num )
    {
        void * x;
        num = (NumType) st;
        /* Generate a unique seed. */
        x = malloc( (num % 13)+9 );
        free(x);
        num = (NumType)(RNG(x) ^ num) >> 6
            /*
              The bitshift part is to work around the problem that the
              left-most byte of generated UUIDs always have the same
              starting sequences.
             */
            ;
    }
    else
    {
        num = RNG(num);
    }
#undef RNG
    st->impl = (void *)num;
    *tgt = num;
    return 0;
}

int whuuid_to_string( whuuid_t const * src, char * dest )
{
    unsigned int i = 0;
    int part = 1;
    int span = 0;
    char byte = 0;
    char nibble = 0;
    if( ! src || ! dest ) return -1;
    for( i = 0; i < whuuid_length_bytes; )
    {
        int x;
        if( 1 == part ) span = 8;
        else if( (part>1) && (part<5) ) span = 4;
        else if( 5 == part ) span = 12;
        for( x = 0; x < (span/2); ++x )
        {
            byte = src->bytes[i++];
            nibble = (byte >> 4) & 0x0F;
            *(dest++) = BITS2CHAR(nibble);
            nibble = (byte & 0x0F);
            *(dest++) = BITS2CHAR(nibble);
        }
        if( part < 5 )
        {
            *(dest++) = '-';
            ++part;
        }
        else break;
    }
    return 0;
}

int whuuid_fill( whuuid_t * dest, unsigned char const * src )
{
    if( ! dest || ! src ) return -1;
    else
    {
        memcpy( dest, src, whuuid_length_bytes );
        return 0;
    }
}

int whuuid_fill_rand( whuuid_t * dest, whuuid_rng * st )
{
    unsigned int i = 0, x = 0;
    unsigned char * c = 0;
    unsigned int r;
    unsigned char nibble;
    int rc = 0;
    if( ! st || ! dest ) return -1;
    if( ! dest ) return -1;
    for( ; i < whuuid_length_bytes; )
    {
        rc = st->rand(st, &r);
        if( rc ) break;
        c = (unsigned char *)&r;
        for( x = sizeof(r); (x > 0) && (i < whuuid_length_bytes); --x, ++i, ++c )
        {
            dest->bytes[i] = *c;
#if WHUUID_CONFIG_KEEP_METRICS
            nibble = (*c >> 4) & 0x0F;
            ++st->distribution[nibble];
            nibble = (*c & 0x0F);
            ++st->distribution[nibble];
#endif
        }
    }
    return rc;
}

short whuuid_compare( whuuid_t const * lhs, whuuid_t const * rhs )
{
    if( ! lhs ) return rhs ? -1 : 0;
    else if( ! rhs ) return 1;
    else if( lhs == rhs ) return 0;
    else
    {
#if 0
        unsigned int i = 0;
        unsigned char const * l = lhs->bytes;
        unsigned char const * r = rhs->bytes;
        unsigned char bl = 0, br = 0; /* current byte of lhs/rhs*/
        unsigned char nl = 0, nr = 0;/* 4-bit part of bl/br*/
        for( ; i < whuuid_length_bytes; ++i )
        {
            bl = l[i];
            br = r[i];
            nl = (bl >> 4);
            nr = (br >> 4);
            if( nl < nr ) return -1;
            else if( nl > nr ) return 1;
            nl = (bl & 0x0F);
            nr = (br & 0x0F);
            if( nl < nr ) return -1;
            else if( nl > nr ) return 1;
        }
        return 0;
#else
        return memcmp( lhs->bytes, rhs->bytes, whuuid_length_bytes );
#endif
    }
}

int whuuid_dump_distribution( whuuid_rng const * st, short full, FILE * dest )
{
#if ! WHUUID_CONFIG_KEEP_METRICS
    fprintf("WHUUID_CONFIG_KEEP_METRICS is false, so whuuid_dump_distribution() cannot work!\n");
    return -1;
#else
    unsigned short i = 0;
    double total = 0;
    unsigned long int max = 0, min = st->distribution[0];
    unsigned long int x = 0;
    char minL = 0, maxL = 0;
    if( full )
    {
        fprintf(dest,"Random number distribution:\nDigit:\tCount:\n");
    }
    for( ; i < 16; ++i )
    {
        x = st->distribution[i];
        total += x;
        if( max < x )
        {
            max = x;
            maxL = BITS2CHAR(i);
        }
        else if( min >= x )
        {
            min = x;
            minL = BITS2CHAR(i);
        }
    }
    if( full )
    {
        for( i = 0; i < 16; ++i )
        {
            x = st->distribution[i];
            fprintf(dest,"%c\t%lu (%0.6f%%)\n",
                    BITS2CHAR(i),
                    x, (x/ total) *100 );
        }
    }
    fprintf(dest,"Least Hits: '%c' (%lu)\nMost Hits: '%c' (%lu)\n",
           minL, min, maxL, max );
    if( max == min )
    {
        fprintf(dest,"Least==Most == best possible random distribution!\n" );
    }
    else
    {
        fprintf(dest,"Max-Min diff = %lu (%0.4f%% of Max)\n", max - min, ((max - min)/(double)max)*100 );
    }
    fprintf(dest,"Total random 4-bit UUID digits: %0.0f\n\n",total);
    return 0;
#endif
}

#undef BITS2CHAR
/* end file cgi/whuuid.c */
/* begin file cgi/cson_cgi.c */
#include <assert.h>
#include <stdlib.h> /* environ, getenv(), atexit() */
#include <ctype.h> /* isspace() */
#include <string.h> /* strlen() */
#include <stdarg.h>
#include <time.h>
#include <locale.h> /* setlocale(), needed for JSON parser. */

#if CSON_ENABLE_UNIX
#  define CSON_CGI_USE_SIGNALS 1
#else
#  define CSON_CGI_USE_SIGNALS 0
#endif

/* If RNG_FILENAME evaluates to true then we use that file for getting
   random bytes for session IDs. FIXME: we effectively leak a file
   handle if this is enabled.
*/
#if 0
#  define RNG_FILENAME "/dev/urandom"
#else
#  define RNG_FILENAME NULL
#endif


#if 1
#define MARKER if(1) printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); if(1) printf
#else
static void noop_printf(char const * fmt, ...) {}
#define MARKER if(0) printf
#endif

#if CSON_CGI_USE_SIGNALS
#  include <signal.h> /* signal() */
#endif

const cson_cgi_init_opt cson_cgi_init_opt_empty = cson_cgi_init_opt_empty_m;

/**
   Some cson_cgi-internal value keys.
*/
static const struct {
    char const * ENV_GET;
    char const * ENV_POST;
    char const * ENV_COOKIE;
    char const * ENV_SYS;
    char const * ENV_APP;
    char const * ENV_ARGV;
    char const * ENV_CONFIG;
    char const * ENV_SESSION;
    char const * RESPONSE_HEADERS;
} cson_cgi_keys = {
"$GET",
"$POST",
"$COOKIE",
"$ENV",
"$APP",
"$ARGV",
"$CONFIG",
"$SESSION",
"response.headers"
};


/**
   Shared state used by the cson_cgi API.
*/
const cson_cgi_cx cson_cgi_cx_empty = cson_cgi_cx_empty_m;

static int cson_cgi_printf(cson_cgi_cx * cx, char const * fmt, ... )
{
    if( ! fmt ) return 0;
    else
    {
        int rc;
        va_list vargs;
        assert( NULL != cx->opt.outStream );
        va_start( vargs, fmt );
        rc = vfprintf( cx->opt.outStream, fmt, vargs );
        /*if( rc > 0 ) fflush( cx->opt.outStream );*/
        va_end( vargs );
        return rc;
    }
}

static int cson_cgi_puts(cson_cgi_cx * cx, char const * str)
{
    size_t const slen = str ? strlen(str) : 0;
    if( slen )
    {
        if( 1 != fwrite( str, slen, 1, cx->opt.outStream ) )
        {
            return -1;
        }
    }
    if( 1 != fwrite( "\n", 1, 1, cx->opt.outStream ) )
    {
        return -2;
    }
    return (int) (slen + 1);
}

static int cson_cgi_putchar(cson_cgi_cx * cx, char ch)
{
    return ( 1 == fwrite( &ch, 1, 1, cx->opt.outStream ) )
        ? 1
        : -1;
}


cson_value * cson_cgi_argv(cson_cgi_cx *cx)
{
    return cx ? cx->argv.jval : NULL;
}

cson_array * cson_cgi_argv_array(cson_cgi_cx * cx)
{
    return cx ? cson_value_get_array( cx->argv.jval ) : NULL;
}

int cson_cgi_gc_add( cson_cgi_cx * cx, char const * key, cson_value * v, char freeOnError )
{
    int const rc = cson_object_set( cx->gc.jobj, key, v );
    if( (0 != rc) && freeOnError )
    {
        cson_value_free( v );
    }
    return rc;
}

int cson_cgi_response_root_set( cson_cgi_cx * cx, cson_value * v )
{
    if( ! cx ) return cson_rc.ArgError;
    else if( v && !cson_value_is_object(v) && !cson_value_is_array(v) )
    {
        return cson_rc.TypeError;
    }
    else if( cx->response.root != v )
    {
        int rc = 0;
        rc = cson_cgi_gc_add(cx, "response.root", v, 0 )
            /** TODO: confirm that cson_object_set() does not
                clean up the original object if insertion fails.
                If it does, we've just hosed the root node.
            */
            ;
        if( 0 != rc )
        {
            return rc;
        }
        else
        {
            cx->response.root = v;
            return 0;
        }
    }
    else
    {
        return 0;
    }
    
}
cson_value * cson_cgi_response_root_get( cson_cgi_cx * cx, char createMode )
{
    if( ! cx ) return NULL;
    else if( cx->response.root ) return cx->response.root;
    else
    {
        if( 0 != createMode )
        {
            if( createMode > 0 )
            {
                cx->response.root = cson_value_new_object();
            }
            else if( createMode < 0 )
            {
                cx->response.root = cson_value_new_array();
            }
            if( cx->response.root &&
                (0 != cson_cgi_gc_add(cx, "response.root", cx->response.root, 1 )) )
            {
                cx->response.root = NULL /* was cleaned up by cson_cgi_gc_add() */;
            }
        }
        return cx->response.root;
    }
}


/** @internal

Tokenizes an input string on a given separator. Inputs are:

- (inp) = is a pointer to the pointer to the start of the input.

- (separator) = the separator character

- (end) = a pointer to NULL. i.e. (*end == NULL)

This function scans *inp for the given separator char or a NULL char.
Successive separators at the start of *inp are skipped. The effect is
that, when this function is called in a loop, all neighboring
separators are ignored. e.g. the string "aa.bb...cc" will tokenize to
the list (aa,bb,cc) if the separator is '.' and to (aa.,...cc) if the
separator is 'b'.

Returns 0 (false) if it finds no token, else non-0 (true).

Output:

- (*inp) will be set to the first character of the next token.

- (*end) will point to the one-past-the-end point of the token.

If (*inp == *end) then the end of the string has been reached
without finding a token.

Post-conditions:

- (*end == *inp) if no token is found.

- (*end > *inp) if a token is found.

It is intolerant of NULL values for (inp, end), and will assert() in
debug builds if passed NULL as either parameter.

When looping, one must be sure to re-set the inp and end
parameters. For example:

@code
char const * head = input;
char const * tail = NULL;
while( cson_cgi_next_token( &inp, '/', &tail ) ) {
  ...
  head = tail;
  tail = NULL;
}
@endcode

If the loop calls 'continue', it must be careful to
ensure that the parameters are re-set, to avoid an endless
loop. This can be simplified with a goto:

@code
while( cson_cgi_next_token( &inp, '/', &tail ) ) {
  if( some condition ) {
     ... do something ...
     goto next_iter;
  }
  else {
    ....
  }
  next_iter;
  head = tail;
  tail = NULL;
}
@endcode

*/
char cson_cgi_next_token( char const ** inp, char separator, char const ** end )
{
    char const * pos = NULL;
    assert( inp && end && *inp );
    if( ! inp || !end ) return 0;
    else if( *inp == *end ) return 0;
    pos = *inp;
    if( !*pos )
    {
        *end = pos;
        return 0;
    }
    for( ; *pos && (*pos == separator); ++pos) { /* skip preceeding splitters */ }
    *inp = pos;
    for( ; *pos && (*pos != separator); ++pos) { /* find next splitter */ }
    *end = pos;
    return (pos > *inp) ? 1 : 0;
}

/**
   If map->jval is NULL then map->jval is created using
   cson_value_new_object() and map->jobj is assigned to its object
   reference. The newly-created map->jval is appended to
   cx->gc to ensure that map->jval lives a full life (as
   opposed to potentially being prematurly GC'd if a client later adds
   map->jval to his own container).

   If map->jval is not NULL then this function is a no-op.

   This function will assert() if map is NULL.

   Returns 0 on success, else cson_rc.AllocError. On error map->jval
   will be NULL after this call.

   On success, ownership of map->jval is transfered to (or potentially
   shared with) cx->gc.
*/
static int cson_cgi_init_env_map( cson_cgi_cx * cx, char const * gckey, cson_cgi_env_map * map )
{
    int rc = 0;
    assert( NULL != map );
    if( NULL == map->jval )
    {
        assert( NULL == map->jobj );
        map->jval = cson_value_new_object();
        if( NULL == map->jval ) return cson_rc.AllocError;
        rc = cson_cgi_gc_add( cx, gckey, map->jval, 1 )
            /* We do this to avoid a corner case in cleanup logic
               if the client stores this object in another container.
            */;
        if( 0 != rc )
        {
            map->jval = NULL /* was cleaned up by cson_cgi_gc_add() */;
        }
        else
        {
            map->jobj = cson_value_get_object( map->jval );
            assert( NULL != map->jobj );
        }
    }
    return rc;
}

char const * cson_cgi_getenv_cstr( cson_cgi_cx * cx, char const * where, char const * key )
{
    return cson_string_cstr( cson_value_get_string( cson_cgi_getenv(cx, where, key) ) );
}

cson_value * cson_cgi_path_part( cson_cgi_cx * cx, unsigned short ndx )
{
    cson_value * piV = cson_cgi_getenv( cx, "e", "PATH_INFO_SPLIT" );
    if( ! piV ) return NULL;
    else
    {
        unsigned int alen;
        cson_array * ar = cson_value_get_array(piV);
        assert( NULL != ar );
        alen = cson_array_length_get( ar );
        return ( ndx >= alen )
            ? NULL
            : cson_array_get( ar, ndx );
    }
}

char const * cson_cgi_path_part_cstr( cson_cgi_cx * cx, unsigned short ndx )
{
    return cson_string_cstr( cson_value_get_string( cson_cgi_path_part( cx, ndx ) ) );
}

/**
   cson_cgi_hexchar_to_int():

   For 'a'-'f', 'A'-'F' and '0'-'9', returns the appropriate decimal
   number.  For any other character it returns -1.
*/
static int cson_cgi_hexchar_to_int( int ch )
{
    if( (ch>='a' && ch<='f') ) return ch-'a'+10;
    else if( (ch>='A' && ch<='F') ) return ch-'A'+10;
    else if( (ch>='0' && ch<='9') ) return ch-'0';
    return -1;
}

/**

   Replaces %XX patterns in str with their equivalent character and
   '+' characters with a single whitespace. %XX patterns which are
   not hexidecimal values are not translated.

   str must be NULL or a NUL-terminated string. If it is NULL or the
   first byte is NUL then 0 is returned and this function has no
   side-effects.
   
   BUGS(?): URL-decoding might have a few bugs/corner cases.
*/
static int cson_cgi_urldecode_inline( char * str )
{
    unsigned char ch = 0;
    unsigned char cx1 = 0;
    unsigned char cx2 = 0;
    int decoded;
    unsigned char * pos = (unsigned char *)str;
    unsigned char * out = pos;
    unsigned char const * end;
    size_t slen = (str && *str) ? strlen(str) : 0;
    if( !slen ) return 0;
    end = pos + slen;
    for( ; pos < end; ++pos )
    {
        ch = *pos;
        if( ch == '%' )
        {
            cx1 = *(pos+1);
            /* FIXME: with only minor refactoring we can remove the
               isxdigit() calls and use cson_cgi_hexchar_to_int()
               instead, checking for a negative return value. That
               would potentially save us 2 extra function calls here.
             */
            if( isxdigit(cx1) )
            {
                cx2 = *(pos+2);
                if( isxdigit(cx2) )
                {
                    decoded = (cson_cgi_hexchar_to_int( cx1 ) * 16)
                        + cson_cgi_hexchar_to_int( cx2 );
                    *(out++) = (char)decoded;
                    pos += 2;
                    continue;
                }
                /* else fall through... */
            }
            /* else fall through... */
        }
        else if( ch == '+' )
        {
            *(out++) = ' ';
            continue;
        }
        *(out++) = ch;
    }
    *out = 0;
    return 0;
}

/**
   If PATH_INFO is set, this function splits it on '/'
   characters and creates an array out of the elements.
   The array is stored as $ENV["PATH_INFO_SPLIT"].

   Returns non-0 on error. If PATH_INFO is not set,
   0 is returned. If it is set but has no entries,
   an empty array is created.

   A return value of cson_rc.RangeError probably means that a path
   element was longer than our internal buffer size, in which case
   processing ends and PATH_INFO_SPLIT is not set. That error can
   probably be ignored by the caller, but all others are probably
   serious (e.g. AllocError).
*/
static int cson_cgi_import_path_info(cson_cgi_cx *cx)
{
    char const * pi = cson_cgi_getenv_cstr(cx, "e","PATH_INFO");
    if( NULL == pi ) return 0;
    else
    {
        cson_value * arV = cson_value_new_array();
        cson_array * ar;
        char const * head = pi;
        char const * tail = NULL;
        if( ! arV ) return cson_rc.AllocError;
        else
        {
            enum { BufSize = 128 };
            char buf[BufSize];
            cson_value * partV;
            unsigned int slen;
            int rc = 0;
            ar = cson_value_get_array(arV);
            while( cson_cgi_next_token( &head, '/', &tail ) )
            {
                slen = (tail-head);
                if( slen >= BufSize )
                {
                    rc = cson_rc.RangeError;
                    goto end_clean;
                }
                memcpy( buf, head, slen );
                buf[slen] = 0;
                cson_cgi_urldecode_inline( buf );
                partV = cson_value_new_string( buf, strlen(buf) );
                if( ! partV )
                {
                    rc = cson_rc.AllocError;
                    goto end_clean;
                }
                rc = cson_array_append( ar, partV );
                if( rc )
                {
                    cson_value_free( partV );
                    goto end_clean;
                }
                partV = NULL;
                head = tail;
                tail = NULL;
            }
            assert( 0 == rc );
            rc = cson_object_set( cx->request.env.jobj,
                                  "PATH_INFO_SPLIT",
                                  arV );
            end_clean:
            if( rc )
            {
                cson_value_free( arV );
            }
            return rc;
        }
    }
}

/**
   Imports (extern char ** environ) into cx->request.env, initializing
   cx->request.env if needed. If called multiple times the environment
   is re-read each time, but old entries which are no longer in the
   new environment are not removed from cx->request.env.

   Returns 0 on success.
*/
static int cson_cgi_import_environ(cson_cgi_cx * cx)
{
    extern char ** environ;
    int i = 0;
    char const * e = environ[0];
    char const * v = NULL;
    enum { KeyBufSize = 512 };
    char keybuf[KeyBufSize];
    char * kpos = NULL;
    int rc = 0;
    cson_value * jv = NULL;
    rc = cson_cgi_init_env_map( cx, cson_cgi_keys.ENV_SYS, &cx->request.env );
    if( 0 != rc ) return rc;
    for( ; e && *e; e = environ[++i] )
    {
        v = NULL;
        memset( keybuf, 0, KeyBufSize );
        kpos = keybuf;
        for( ; *e && ('=' != *e); ++e )
        {
            *(kpos++) = *e;
            assert( kpos < (keybuf+KeyBufSize) );
            if( kpos >= (keybuf+KeyBufSize) )
            {
                return cson_rc.RangeError;
            }
        }
        if( '=' == *e )
        {
            v = e+1;
        }
        else
        {
            v = "";
        }
        jv = cson_value_new_string( v, strlen(v) );
        if( NULL == jv )
        {
            rc = cson_rc.AllocError;
            break;
        }
        rc = cson_object_set( cx->request.env.jobj, keybuf, jv );
        if( 0 != rc ) break;
    }
    if( 0 == rc )
    {
        rc = cson_cgi_import_path_info(cx);
    }
    return rc;
}

/**
   Tries to save the current session data, if any, using the
   configured session manager.

   Returns 0 on success. If the environment has no session,
   it is treated as success but nothing is actually saved.

   If no session manager has been configured then
   cson_rc.UnsupportedError is returned.
*/
static int cson_cgi_session_save(cson_cgi_cx * cx)
{
    if( ! cx->session.mgr )
    {
        return cson_rc.UnsupportedError;
    }
    else if( !cx->session.id || !cx->session.env.jval )
    {
        return 0;
    }
    else
    {
        return cx->session.mgr->api->save( cx->session.mgr,
                                           cx->session.env.jval,
                                           cx->session.id );
    }
}

cson_cgi_cx * cson_cgi_cx_alloc()
{
    cson_cgi_cx * rc = (cson_cgi_cx *)malloc(sizeof(cson_cgi_cx));
    if( rc )
    {
        *rc = cson_cgi_cx_empty;
        rc->misc.allocStamp = rc;
    }
    return rc;
}

char cson_cgi_cx_clean( cson_cgi_cx * cx )
{
    if( !cx ) return 0;
    else
    {
        void const * allocStamp = NULL;
        if( cx->session.mgr )
        {
            cson_cgi_session_save(cx) /* ignoring error code */;
            cx->session.mgr->api->finalize( cx->session.mgr );
            cx->session.mgr = NULL;
        }
        if(NULL != cx->gc.jval)
        {
            cson_value_free( cx->gc.jval );
            cx->gc.jval = NULL;
            cx->gc.jobj = NULL;
        }
        if( cx->session.id )
        {
            free( cx->session.id );
            cx->session.id = NULL;
        }
        cson_buffer_reserve( &cx->tmpBuf, 0 );
        allocStamp = cx->misc.allocStamp;
        if( cx->opt.inStream && (stdin != cx->opt.inStream) ) fclose(cx->opt.inStream);
        if( cx->opt.outStream && (stderr == cx->opt.outStream) && (stdout != cx->opt.outStream) ) fclose(cx->opt.outStream);
        if( cx->opt.errStream && (stderr == cx->opt.errStream) && (stdout != cx->opt.errStream) ) fclose(cx->opt.errStream);
        *cx = cson_cgi_cx_empty;
        return ( allocStamp == cx )
            ? (free( cx ), 1)
            : 0;
    }
}

cson_value * cson_cgi_env_get_val( cson_cgi_cx * cx, char which, char createIfNeeded )
{
    cson_cgi_env_map * map = NULL;
    cson_value * v = NULL;
    char const * gckey = NULL;
    switch( which )
    {
      case 'c':
      case 'C':
          map = &cx->request.cookie;
          gckey = cson_cgi_keys.ENV_COOKIE;
          break;
      case 'e':
      case 'E':
          gckey = cson_cgi_keys.ENV_SYS;
          map = &cx->request.env;
          break;
      case 'g':
      case 'G':
          gckey = cson_cgi_keys.ENV_GET;
          map = &cx->request.get;
          break;
      case 'f':
      case 'F':
          gckey = cson_cgi_keys.ENV_CONFIG;
          map = &cx->config;
          break;
      case 'p':
      case 'P':
          gckey = cson_cgi_keys.ENV_POST;
          map = &cx->request.post;
          break;
      case 'a':
      case 'A':
          gckey = cson_cgi_keys.ENV_APP;
          map = &cx->clientEnv;
          break;
      case 's':
      case 'S':
          gckey = cson_cgi_keys.ENV_SESSION;
          map = &cx->session.env;
          break;
      default:
          break;
    }
    if( map )
    {
        v = map->jval;
        if( !v && createIfNeeded )
        {
            assert( NULL != gckey );
            cson_cgi_init_env_map( cx, gckey, map );
            v = map->jval;
        }
    }
    return v;
}

cson_object * cson_cgi_env_get_obj( cson_cgi_cx * cx, char which, char createIfNeeded )
{
    return cson_value_get_object( cson_cgi_env_get_val( cx, which, createIfNeeded ) );
}

/**
   Sets a variable in one of the environment objects.

   env must be the conventional character representation
   (case-insensitive) for on of the following environment objects:

   - g = GET
   - p = POST
   - e = ENV
   - c = COOKIE
   - u = USER

   On success 0 is returned and ownership of v is transfered to (or
   shared with) the appropriate environment object. On error non-zero
   is returned and ownership of v is not modified.
*/
static int cson_cgi_setenv_x( cson_cgi_cx * cx, char env, char const * key, cson_value * v )
{
    if( ! key || !*key ) return cson_rc.ArgError;
    else
    {
        cson_object * jo = cson_cgi_env_get_obj( cx, env, 1 );
        return ( NULL == jo )
            ? cson_rc.RangeError /* FIXME: expand the above code so we
                                    can distinguish between invalid
                                    env and allocation error. (Except that
                                    there is no allocation on get_obj().*/
            : cson_object_set( jo, key, v );
    }
}

int cson_cgi_setenv( cson_cgi_cx * cx, char const * key, cson_value * v )
{
    return cson_cgi_setenv_x( cx, 'a', key, v );
}

int cson_cgi_cookie_set( cson_cgi_cx * cx, char const * key, cson_value * v )
{

    if( ! key || !*key ) return cson_rc.ArgError;
    else
    {
        cson_object * jo = cson_cgi_env_get_obj( cx, 'c', 1 );
        return (NULL == jo)
            ? cson_rc.AllocError
            : cson_object_set( jo, key, v ? v : cson_value_null() );
    }
}

int cson_cgi_cookie_set2( cson_cgi_cx * cx,
                          char const * key, cson_value * v,
                          char const * domain, char const * path,
                          unsigned int expires, char secure, char httponly )
{
    if( ! key || !*key ) return cson_rc.ArgError;
    else
    {
        int rc;
        cson_value * jv = cson_value_new_object();
        cson_object * jo = cson_value_get_object(jv);
        cson_value * x = NULL;
        if( ! jo ) return cson_rc.AllocError;
        if( ! v ) v = cson_value_null() /* reminder: does not allocate */;

#define SET(KEY) if( 0 != (rc = cson_object_set( jo, KEY, x) ) ) {      \
            cson_value_free(x); \
            cson_value_free( jv ); \
            return rc; \
        }

        if( NULL != domain )
        {
            x = cson_value_new_string( domain, strlen(domain) );
            SET("domain");
        }
        if( NULL != path )
        {
            x = cson_value_new_string( path, strlen(path) );
            SET("path");
        }

        if( cson_value_is_null(v) )
        {
            x = cson_value_new_integer( 1 );
            SET("expires");
        }
        else if( expires )
        {
            x = cson_value_new_integer( (cson_int_t) expires );
            SET("expires");
        }
        if( secure )
        {
            x = cson_value_new_bool(secure);
            SET("secure");
        }
        if( httponly )
        {
            x = cson_value_new_bool(httponly);
            SET("httponly");
        }
#undef SET
        rc = cson_cgi_cookie_set( cx, key, jv );
        if( 0 != rc )
        {
            cson_value_free( jv );
        }
        else
        { /* set "value" last so that we can avoid tricky
             ownership/lifetime problems in error cases.
          */
            if( 0 != (rc = cson_object_set( jo, "value", v) ) )
            { /* remove the cookie. Note that this particular case
                 does not remove it from the HTTP client. In order to do that
                 we have to keep the existing path/domain/etc info.
              */
                cson_object * cookies = cson_cgi_env_get_obj( cx, 'c', 0 );
                if( cookies )
                {
                    cson_object_set( cookies, key, cson_value_null() )
                        /* Ignore error code, since we have no fallback
                           and cson_value_null() does not allocate.
                           Worst-case is that removing it fails, but when we
                           emit the cookie headers that cookie will be skipped
                           because it has no "value" field.
                        */
                        ;
                }
            }
        }
        return rc;
    }
}

cson_value * cson_cgi_getenv( cson_cgi_cx * cx, char const * fromWhere, char const * key )
{
    cson_value * jv = NULL;
    cson_object * map = NULL;
    if( (NULL == fromWhere) || !*fromWhere ) fromWhere = CSON_CGI_GETENV_DEFAULT;
    if( !key || !*key ) return NULL;
    for( ; *fromWhere ; ++fromWhere )
    {
        map = cson_cgi_env_get_obj( cx, *fromWhere, 0 );
        if( (NULL == map) && (('r'==*fromWhere)||('R'==*fromWhere)) )
        {
            jv = cson_cgi_getenv( cx, "gpc", key );
        }
        if( NULL != jv ) /* only in 'R' case */ break;
        else if( NULL == map ) continue /* invalid character or NULL map */;
        jv = cson_object_get( map, key );
        if( NULL != jv ) break;
    }
    return jv;
}


int cson_cgi_response_header_add( cson_cgi_cx * cx, char const * key, cson_value * v )
{
    int rc = 0;
    if( !cx || ! key || !*key ) return cson_rc.ArgError;
    rc = cson_cgi_init_env_map( cx, cson_cgi_keys.RESPONSE_HEADERS, &cx->response.headers );
    if( 0 == rc )
    {
        assert( NULL != cx->response.headers.jobj );
        rc = cson_object_set( cx->response.headers.jobj, key, v );
    }
    return rc;
}


char cson_cgi_is_jsonp(cson_cgi_cx * cx)
{
    if( ! cx ) return 0;
    else if( cx->misc.isJSONP < 0 )
    { /* guess */
        cx->misc.isJSONP = (NULL == cson_cgi_getenv( cx, "agp", CSON_CGI_KEY_JSONP ))
            ? 0 : 1;
    }
    return cx->misc.isJSONP;
}

void cson_cgi_enable_jsonp( cson_cgi_cx * cx, char b )
{
    if( cx ) cx->misc.isJSONP = b ? 1 : 0;
}

char const * cson_cgi_guess_content_type(cson_cgi_cx * cx)
{
    char const * cset;
    char doUtf8;
    cset = getenv("HTTP_ACCEPT_CHARSET");
    doUtf8 = ((NULL == cset) || (NULL!=strstr("utf-8",cset)))
        ? 1 : 0;
    if( cson_cgi_is_jsonp(cx) )
    {
        return doUtf8
            ? "application/javascript; charset=utf-8"
            : "application/javascript";
    }
    else
    {
        /*
          Content-type

          If the browser does not sent an ACCEPT for application/json
          then we fall back to text/plain.
        */
        char const * cstr;
        cstr = getenv("HTTP_ACCEPT");
        if( NULL == cstr )
        {
            return doUtf8
                ? "application/json; charset=utf-8"
                : "application/json";
        }
        else
        {
            if( strstr( cstr, "application/json" )
                || strstr( cstr, "*/*" ) )
            {
                return doUtf8
                    ? "application/json; charset=utf-8"
                    : "application/json";
            }
            else
            {
                return "text/plain";
            }
        }
    }
}


/**
   URL-encodes src to dest and NUL-terminates it. dest must be at
   least *destLen bytes long. Upon a successful return, *destLen
   will be modified to hold the new string's length.

   Returns 0 on success. On error dest might be partially populated.

   Returns cson_rc.RangeError if dest is not long enough to hold
   the conversion and a terminating NUL.
*/
static int cson_cgi_urlencode( char const * src, char * dest_, size_t * destLen )
{
#define needs_escape \
    ( (ch >= 32 && ch <=47)                       \
      || ( ch>=58 && ch<=64)                      \
      || ( ch>=91 && ch<=96)                      \
      || ( ch>=123 && ch<=126)                    \
      || ( ch<32 || ch>=127)                      \
      )
    char const * pos = src;
    char ch;
    size_t dpos = 0;
    char * dest = dest_;
    static char const * hex = "0123456789ABCDEF";
    if( ! dest || !destLen ) return cson_rc.RangeError;
    for( ; pos && *pos; ++pos )
    {
        ch = *pos;
        if( ! needs_escape )
        {
            if( ++dpos >= *destLen ) return cson_rc.RangeError;
            *(dest++) = ch;
            continue;
        }
        else
        {
            if( (dpos+=3) >= *destLen ) return cson_rc.RangeError;
            *(dest++) = '%';
            *(dest++) = hex[((ch>>4)&0xf)];
            *(dest++) = hex[(ch&0xf)];
        }
    }
    if( ++dpos >= *destLen ) return cson_rc.RangeError;
    *dest = 0;
    *destLen = dest - dest_;
    return 0;
#undef needs_escape
}

/**
   If orig is one of the types (string,double,bool,undef,null) then
   a pointer to its string representation is returned, else NULL
   is returned.

   For non-string types, dest must be at least destLen bytes of memory, and
   if destLen is not long enough to hold the string form then NULL is returned.

   On success a pointer to a string is returned. It will be one of:

   - if orig is-a string then it's underlying string.

   - for (double,integer,bool,undef,null), dest will be returned. The encoded
   form is decimal for (double,integer), the number 0 or 1 for bool, and the
   number 0 for (undef,null).

   Ownership of dest is not modified by this call.

   The returned value is valid until either orig or dest are modified.

   On error dest is not modified. Dest is also not modified if orig
   is-a string, as its own string bytes are returned instead.
*/
static char const * cson_cgi_pod_to_string( cson_value const * orig,
                                            char * dest, unsigned int destLen )
{
    if( ! orig || !dest || !destLen ) return NULL;
    else
    {/* FIXME? use cson's output support for the numeric types. i
        _think_ those bits might not be in the public API, though.
        We could use it for serializing objects/arrays, in any case.
      */
        enum { NumBufSize = 80 };
        if( cson_value_is_string(orig) )
        {
            cson_string const * jstr = cson_value_get_string(orig);
            assert( NULL != jstr );
            return cson_string_cstr( jstr ); 
        }
        else if( cson_value_is_integer(orig) )
        {
            char tmp[NumBufSize] = {0};
            int const sc = sprintf( tmp, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig));
            if( sc <= 0 ) return NULL;
            else if( (unsigned int)sc >= destLen ) return NULL;
            else
            {
                strcpy( dest, tmp );
                return dest;
            }
        }
        else if( cson_value_is_double(orig) )
        {
            char tmp[NumBufSize] = {0};
            int const sc = sprintf( tmp, "%"CSON_DOUBLE_T_PFMT, cson_value_get_double(orig));
            if( sc <= 0 ) return NULL;
            else if( (unsigned int)sc >= destLen ) return NULL;
            else
            {
                strcpy( dest, tmp );
                if(1)
                { /* Strip trailing zeroes... */
                    unsigned int urc = strlen(dest);
                    char * pos = dest + urc - 1;
                    for( ; ('0' == *pos) && urc && (*(pos-1) != '.'); --pos, --urc )
                    {
                        *pos = 0;
                    }
                    assert(urc && *pos);
                }
                return dest;
            }
        }
        else if( cson_value_is_bool( orig ) )
        {
            char const bv = cson_value_get_bool(orig);
            if( destLen < 2 ) return NULL;
            *dest = bv ? '1' : '0';
            *(dest+1) = 0;
            return dest;
        }
        else if( cson_value_is_null( orig ) || cson_value_is_undef( orig ) )
        {
            if( destLen < 2 ) return NULL;
            *dest = '0';
            *(dest+1) = 0;
            return dest;
        }
        else
        {
            return NULL;
        }
    }
}


/**
   Writes an RFC822 timestamp string to dest, which must be at least destLen bytes long.
   On success returns dest, else NULL. destLen must be at least 31.
*/
static char * cson_cgi_rfc822_timedate( time_t now, char * dest, unsigned int destLen )
{
    static const char * dayNames[] = 
        {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
         0 };
    static const char * monthNames[] =
        {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
         0};

    struct tm * t = (dest && (destLen>30)) ? gmtime(&now) : NULL;
    if( ! t || (destLen<31) ) return NULL;
    else
    {
        int const rc = sprintf( dest,
                                "%s, %d %s %02d %02d:%02d:%02d GMT",
                                dayNames[t->tm_wday], t->tm_mday,
                                monthNames[t->tm_mon],
                                t->tm_year+1900, t->tm_hour,
                                t->tm_min, t->tm_sec
                                );
        assert( (rc>0) && ((unsigned int)rc) < destLen );
        return dest;
    }
}

/**
   Outputs the cookie-specific HTTP headers.

   Returns 0 on success.
*/
static int cson_cgi_response_output_cookies(cson_cgi_cx * cx)
{
    cson_kvp * kvp = NULL;
    cson_object * jo = NULL;
    cson_object_iterator iter = cson_object_iterator_empty;
    assert(cx);
    jo = cx->request.cookie.jobj;
    if( ! jo ) return 0;
    else
    {
        enum { CookieBufSize = 1024 * 8,
               ValBufSize = 1024 * 4,
               TSBufSize = 32
        };
        char cookieBuf[CookieBufSize] = {0} /* buffer for whole cookie string */;
        char valBuf[ValBufSize] = {0} /* buffer for value encoding */;
        char urlBuf[ValBufSize] = {0} /* buffer for urlencoding */;
        char tsBuf[TSBufSize] = {0} /* buffer for expiry timestamp */;
        int rc = cson_object_iter_init( jo, &iter );
        assert( CookieBufSize > ValBufSize );
        if( 0 != rc ) return rc;
        while( (kvp = cson_object_iter_next(&iter)) )
        {
            cson_string const * key = cson_kvp_key(kvp);
            cson_value const * val = cson_kvp_value(kvp);
            if( cson_value_is_null(val) )
            {
                cson_cgi_printf(cx,"Set-Cookie: %s=null; expires=Thu, 01-Jan-1970 00:00:01 GMT\r\n", cson_string_cstr(key));
                continue;
            }
            if( cson_value_is_object(val) )
            {
                /*
                  Accept in Object in the form:

                  {
                  value: VALUE,
                  domain: string,
                  path: string,
                  secure: bool,
                  httponly: bool,
                  expires: integer
                  }
                 */
                cson_object const * obj = cson_value_get_object( val );
                cson_value const * cv = cson_object_get( obj, "value" );
                char const * valstr = NULL;
                char const isNull = !cv || cson_value_is_null( cv );
                if( isNull )
                {
                    cson_cgi_printf(cx, "Set-Cookie: %s=0", cson_string_cstr(key));
                }
                else
                {
                    /* FIXME: streamify urlencode so we can get around fixed buffer size. */
                    valstr = cson_cgi_pod_to_string( cv, valBuf, ValBufSize );
                    if( ! valstr ) continue;
                    else
                    {
                        size_t bSize = ValBufSize;
                        memset( urlBuf, 0, ValBufSize );
                        if( 0 != cson_cgi_urlencode( valstr, urlBuf, &bSize ) )
                        {
                            /* buffer is too small. Skip it. */
                            continue;
                        }
                        assert( bSize <= ValBufSize );
                        cson_cgi_printf(cx, "Set-Cookie: %s=%s", cson_string_cstr(key), urlBuf);
                    }
                }

#define DOPART(KEY,KEY2) cv = cson_object_get( obj, KEY );  \
                if( cv ) { \
                    valstr = cson_cgi_pod_to_string( cv, valBuf, ValBufSize ); \
                    if( valstr ) { \
                        cson_cgi_printf( cx, "; "KEY2"=%s", valstr );  \
                    } } (void)0
                DOPART("domain","Domain");
                DOPART("path","Path");
#undef DOPART

                cv = cson_object_get( obj, "expires" );
                if( cv || isNull )
                {
                    cson_int_t const intVal = isNull ? 1 : cson_value_get_integer(cv);
                    if( intVal )
                    {
                        valstr = cson_cgi_rfc822_timedate( (time_t)intVal, tsBuf, TSBufSize );
                        if( valstr )
                        {
                            cson_cgi_printf( cx, "; Expires=%s", valstr );
                        }
                    }
#if 0
                    else if( cson_value_is_string(cv) )
                    {
                        /* TODO?: assume it's already propery formatted. */
                    }
                    else
                    {
                        /* skip it.*/
                    }
#endif
                }
                cv = cson_object_get( obj, "secure" );
                if( cson_value_get_bool(cv) )
                {
                    cson_cgi_printf( cx, "; Secure" );
                }
                
                cv = cson_object_get( obj, "httponly" );
                if( cson_value_get_bool(cv) )
                {
                    cson_cgi_printf( cx, "; HttpOnly" );
                }
                cson_cgi_puts(cx, "\r");
            }
            else
            {
                char const * valstr;
                memset( valBuf, 0, ValBufSize );
                valstr = cson_cgi_pod_to_string( val, valBuf, ValBufSize );
                if( ! valstr ) continue;
                else
                {
                    size_t bSize = CookieBufSize;
                    memset( cookieBuf, 0, CookieBufSize );
                    rc = cson_cgi_urlencode( valstr, cookieBuf, &bSize );
                    if( 0 != rc )
                    {
                        /* too beaucoup. skip it */
                        continue;
                    }
                    assert( bSize < CookieBufSize );
                    cson_cgi_printf(cx,"Set-Cookie: %s=%s\r\n", cson_string_cstr(key), cookieBuf);
                }
            }
        }
        return 0;
    }

}
int cson_cgi_response_output_headers(cson_cgi_cx * cx)
{
    enum { BufSize = 64 };
    cson_object * jo = NULL;
    int rc;
    rc = cson_cgi_printf(cx, "Content-type: %s\r\n", cson_cgi_guess_content_type(cx) );
    if( rc <= 0 ) return rc;
    rc = cson_cgi_puts(cx, "Status: 200 OK\r");
    if( rc <= 0 ) return rc;
    jo = cx->response.headers.jobj;
    if( jo )
    {
        char buf[BufSize] = {0};
        cson_object_iterator iter = cson_object_iterator_empty;
        cson_kvp * kvp;
        cson_string const * key;
        cson_value const * val;
        char const * valcstr;
        rc = cson_object_iter_init( jo, &iter );
        if( 0 != rc ) return rc;
        while( (kvp = cson_object_iter_next(&iter)) )
        {
            key = cson_kvp_key(kvp);
            val = cson_kvp_value(kvp);
            valcstr = cson_cgi_pod_to_string( val, buf, BufSize );
            if( ! valcstr ) continue;
            assert( NULL != key );
            assert( NULL != val );
            cson_cgi_printf(cx, "%s: %s\r\n",
                            cson_string_cstr(key),
                            valcstr ? valcstr : "");
        }
    }
    rc = cson_cgi_response_output_cookies(cx);
    return rc;
}

int cson_cgi_response_output_root(cson_cgi_cx * cx)
{
    return ( !cx || !cx->response.root )
        ? cson_rc.ArgError
        : cson_output_FILE( cx->response.root, cx->opt.outStream, &cx->opt.outOpt );
}

int cson_cgi_response_output_all(cson_cgi_cx * cx)
{
    int rc = 0;
    char isJP = 0;
    char doHeaders = cx->opt.httpHeadersMode;
    if( NULL == cx->response.root )
    {
        return cson_rc.ArgError;
    }
    isJP = cson_cgi_is_jsonp(cx);
    if( doHeaders < 0 )
    {
        if( NULL!=getenv("GATEWAY_INTERFACE") )
        {
            doHeaders = 1;
        }
    }
    if( doHeaders > 0 )
    {
        rc = cson_cgi_response_output_headers(cx);
        if( 0 == rc )
        {
            cson_cgi_puts(cx,"\r")/*yes, putS, not putCHAR!*/;
        }
        else return rc;
    }
    if( isJP )
    {
        cson_cgi_printf(cx,"%s(", "FIXME_JSONP_CALLBACK_NAME" );
    }
    rc = cson_cgi_response_output_root(cx);
    if( 0 == rc )
    {
        if( isJP )
        {
            cson_cgi_putchar(cx,')');
        }
        cson_cgi_putchar(cx,'\n');
        fflush( cx->opt.outStream );
    }
    return rc;
}

/**
   Parses inp as a delimited list, separated by the given
   separator character. Each item in the list is treated
   as a key/value pair in the form KEY=VALUE, and inserted
   into the target cson_object (which must not be NULL).

   This is intended for parsing HTTP GET-style parameter lists.

   If doUrlDecode is true (non-zero) then the VALUE part of the
   key/value pair gets url-decoded before insertion. (FIXME? Also
   decode the keys?)

   If firstOneWins is non-0 then if a given key in the parameters is
   duplicated, entries after the first are ignored. If it is 0 then
   the "last one wins." This is basically a workaround for when we
   have multiple session ID cookies hanging around :/.
   
   On success it returns 0.

   If a given key contains the string "[]", that part is stripped and
   the entry is treated like an array element. e.g. a query string of
   "a[]=3&a[]=7" would result in an array property named "a" with the
   (string) entries ("3", "7").
   
*/
static int cson_cgi_parse_param_list( cson_cgi_cx * cx,
                                      cson_object * tgt,
                                      char const * inp,
                                      char separator,
                                      char doUrlDecode,
                                      char firstOneWins)
{
    if( ! tgt || !separator ) return cson_rc.ArgError;
    else if( !inp || !*inp ) return 0;
    else
    {
        char const * head = inp;
        char const * tail = NULL;
        char * out = NULL;
        unsigned int inLen = strlen( inp );
        unsigned int valLen;
        cson_value * jval = NULL;
        cson_value * listV = NULL;
        cson_array * list = NULL;
        int rc = cson_buffer_reserve( &cx->tmpBuf, inLen+1 );
        if( 0 != rc ) return rc;
        while( cson_cgi_next_token( &head, separator, &tail ) )
        {
            char const * key = head;
            char * value = NULL;
            rc = 0;
            if( head == tail ) break;
            out = (char *)cx->tmpBuf.mem;
            memset( cx->tmpBuf.mem, 0, cx->tmpBuf.capacity );
            for( ; (key<tail) && *key && isspace(*key); ++key )
            {
                /* strip leading spaces in the key name
                   (happens in cookie values). */
            }
            if( key==tail ) break;
            else if( '='==*key )
            {
                /* all-space key. Just skip it. */
                goto next_iter;
            }
            /* Write the key part to the buffer... */
            for( ; (key<tail) && *key && ('='!=*key); ++key ) {
                *(out++) = *key;
            }
            *(out++) = 0;
            if( '=' == *key )
            {
                ++key;
            }
            value = out;
            valLen = 0;
            /* Write the value part to the buffer... */
            for( ; (key<tail) && *key; ++key, ++valLen ) {
                *(out++) = *key;
            }
            key = (char const *)cx->tmpBuf.mem;
            if( firstOneWins && (NULL != cson_object_get( tgt, key )) )
            {
                goto next_iter;
            }
            if( doUrlDecode && valLen )
            {
                cson_cgi_urldecode_inline( value );
            }
            /*MARKER("key=[%s], valLen=%u, value=[%s]\n", key, valLen, value );*/
            jval = cson_value_new_string( value, valLen );
            if( NULL == jval )
            {
                rc = cson_rc.AllocError;
                goto the_end;
            }
            if( NULL != (out = strstr(key,"[]")) )
            { /* Treat key as an array entry, like PHP does... */
                cson_value * freeThisOnErr = NULL;
                *out = 0;
                list = NULL;
                listV = cson_object_get( tgt, key );
                if( listV )
                {
                    if( ! cson_value_is_array( listV ) )
                    {
                        /* skip it to avoid hosing a different entry. */
                        cson_value_free( jval );
                        jval = NULL;
                        goto next_iter;
                    }
                }
                else
                { /* create a new array to hold the value */
                    listV = cson_value_new_array();
                    if( ! listV )
                    {
                        cson_value_free( jval );
                        rc = cson_rc.AllocError;
                        goto the_end;
                    }
                    rc = cson_object_set( tgt, key, listV );
                    if( 0 != rc )
                    {
                        cson_value_free( listV );
                        cson_value_free( jval );
                        goto the_end;
                    }
                    freeThisOnErr = listV;
                }
                list = cson_value_get_array( listV );
                assert( NULL != list );
                rc = cson_array_append( list, jval );
                if( 0 != rc )
                {
                    cson_value_free( jval );
                    cson_value_free( freeThisOnErr );
                    goto the_end;
                }
            }
            else
            {
                rc = cson_object_set( tgt, key, jval );
                if( 0 != rc )
                {
                    cson_value_free( jval );
                    goto the_end;
                }
            }
            next_iter:
            head = tail;
            tail = NULL;
        }
        the_end:
        cson_buffer_reserve( &cx->tmpBuf, 0 );
        return rc;
    }
}
                             

/**
   Parses key/value pairs from a QUERY_STRING-formatted
   string.

   Returns 0 on success. The "most likely" error condition, in terms
   of potential code paths, is is an allocation error.
   
   TODO: if the key part of any entry ends with "[]", treat it as an
   array entry, like PHP does.
*/
static int cson_cgi_parse_query_string( cson_cgi_cx * cx, char const * qstr )
{
    cson_object * env = NULL;
    if( !qstr || !*qstr ) return 0;
    assert(cx);
    env = cson_cgi_env_get_obj( cx, 'g', 1 );
    if( NULL == env ) return cson_rc.AllocError /* guess! */;
    return cson_cgi_parse_param_list( cx, env, qstr, '&', 1, 0 );
}

#if CSON_CGI_ENABLE_POST_FORM_URLENCODED
static int cson_cgi_parse_post_urlencoded( cson_cgi_cx * cx, char const * qstr )
{
    cson_object * env = NULL;
    if( !qstr || !*qstr ) return 0;
    assert(cx);
    env = cson_cgi_env_get_obj( cx, 'p', 1 );
    if( NULL == env ) return cson_rc.AllocError /* guess! */;
    return cson_cgi_parse_param_list( cx, env, qstr, '&', 1, 0 );
}
#endif

/**
   Like cson_cgi_parse_query_string(), but expects qstr to be in COOKIE
   format.
*/
static int cson_cgi_parse_cookies( cson_cgi_cx * cx, char const * qstr )
{
    cson_object * env = NULL;
    if( !qstr || !*qstr ) return 0;
    assert(cx);
    env = cson_cgi_env_get_obj(cx, 'c', 1 );
    if( NULL == env ) return cson_rc.AllocError /* guess! */;
    return cson_cgi_parse_param_list( cx, env, qstr, ';', 1, 1 );
}


/**
   Initializes cx->argv.jval and cx->argv.jarr, adds them to the
   garbage collector, then copies argv to cx->argv.jarr as an
   array of JSON strings.

   Returns 0 on success.

   Results are undefined if argv is not a properly initialized array
   of NUL-terminated strings with at least argc entries.

   If argc is 0 or less then cx->argv is still initialized but has
   a length of 0.

   After the first call, further arguments are appended to the current
   list.
*/
static int cson_cgi_init_argv( cson_cgi_cx * cx, int argc, char const * const * argv )
{
    int rc = 0;
    int i;
    assert( NULL != cx->gc.jobj );
    if( cx->argv.jval == NULL )
    {
        cson_value * v = cson_value_new_array();
        if( NULL == v ) return cson_rc.AllocError;
        rc = cson_cgi_gc_add( cx, cson_cgi_keys.ENV_ARGV, v, 1 );
        if( 0 != rc )
        {
            /* reminder: v was freed by cson_cgi_gc_add() */
            return rc;
        }
        cx->argv.jval = v;
        cx->argv.jarr = cson_value_get_array( v );
        assert( NULL != cx->argv.jarr );
    }
    for( i = 0; i < argc; ++i )
    {
        char const * arg = argv[i];
        cson_value * vstr = cson_value_new_string( arg ? arg : "",
                                                   arg ? strlen(arg) : 0 );
        if( NULL == vstr ) return cson_rc.AllocError;
        rc = cson_array_append( cx->argv.jarr, vstr );
        if( 0 != rc )
        {
            cson_value_free( vstr );
            break;
        }
    }
    return rc;
}

typedef struct CgiPostReadState_ {
    FILE * fh;
    unsigned int len;
    unsigned int pos;
} CgiPostReadState;

static int cson_data_source_FILE_n( void * state, void * dest, unsigned int * n )
{
    if( ! state || !dest || !n ) return cson_rc.ArgError;
    else
    {
        CgiPostReadState * st = (CgiPostReadState *)state;
        if( st->pos >= st->len )
        {
            *n = 0;
            return 0;
        }
        else if( !*n || ((st->pos + *n) > st->len) ) return cson_rc.RangeError;
        else
        {
            unsigned int rsz = (unsigned int)fread( dest, 1, *n, st->fh );
            if( ! rsz )
            {
                *n = rsz;
                return feof(st->fh) ? 0 : cson_rc.IOError;
            }
            else
            {
                *n = rsz;
                st->pos += *n;
                return 0;
            }
        }
    }
}


static int cson_cgi_parse_POST_JSON(cson_cgi_cx * cx, FILE * src, unsigned int contentLen)
{
    cson_value * jv = NULL;
    int rc = 0;
    CgiPostReadState state;
    cson_parse_info pinfo = cson_parse_info_empty;
    assert( 0 != contentLen );
    assert( NULL == cx->request.post.jval );
    state.fh = src;
    state.len = contentLen;
    state.pos = 0;
    rc = cson_parse( &jv, cson_data_source_FILE_n, &state, NULL, &pinfo );
    if( rc )
    {
#if 0
        fprintf(stderr, "%s: Parsing POST as JSON failed: code=%d (%s) line=%u, col=%u\n",
                __FILE__, rc, cson_rc_string(rc), pinfo.line, pinfo.col );
#endif
        return rc;
    }
    rc = cson_cgi_gc_add( cx, cson_cgi_keys.ENV_POST, jv, 1 );
    if( 0 == rc )
    {
        cx->request.post.jval = jv;
        cx->request.post.jobj = cson_value_get_object( jv );
        assert( cx->request.post.jobj && "FIXME: also support an Array as POST data node." ); 
    }
    return rc;
}

static int cson_cgi_init_POST(cson_cgi_cx * cx)
{
    if( ! cx || !cx->opt.inStream ) return cson_rc.ArgError;
    else
    {
        FILE * src = cx->opt.inStream;
        char const * ctype = cson_string_cstr( cson_value_get_string( cson_cgi_getenv( cx, "e", "CONTENT_TYPE" ) ) );
        if( NULL == ctype ) return 0;
        else
        {
            char const * clen = cson_string_cstr( cson_value_get_string( cson_cgi_getenv( cx, "e", "CONTENT_LENGTH" ) ) );
            if( NULL == clen ) return cson_rc.ArgError;
            else
            {
                char * endpt = NULL;
                long len = strtol( clen, &endpt, 10 );
                if( (endpt && *endpt) || (len<=0) ) return cson_rc.RangeError;
#if CSON_CGI_ENABLE_POST_FORM_URLENCODED
                else if( 0 == strncmp(ctype,"application/x-www-form-urlencoded",33) )
                {
                    cson_buffer buf = cson_buffer_empty;
                    int rc = cson_buffer_fill_from( &buf, cson_data_source_FILE, src );
                    if( rc )
                    {
                        goto end_clean;
                        return rc;
                    }
                    if( buf.mem && buf.used )
                    {
#if 1
                        if( strlen((char const *)buf.mem)
                            != buf.used )
                        {
                            /* assume bad/malicious input. */
                            rc = cson_rc.RangeError;
                            goto end_clean;
                        }
#endif
                        rc = cson_cgi_parse_post_urlencoded( cx, (char const *)buf.mem );
                    }
                    end_clean:
                    cson_buffer_reserve( &buf, 0 );
                    return rc;
                }
#endif
                else if( (0 == strncmp(ctype,"application/json",16))
                         || (0 == strncmp(ctype,"text/plain",10))
                         || (0 == strncmp(ctype,"application/javascript",22))
                         )
                {
                    return cson_cgi_parse_POST_JSON(cx, src, len);
                }
                else
                {
                    return cson_rc.TypeError;
                }
            }
        }
    }
}

static int cson_cgi_init_config( cson_cgi_cx * cx, char const * fname )
{
    int rc;
    cson_value * root = NULL;
    rc = cson_parse_filename( &root, fname, NULL, NULL );
    if( 0 == rc )
    {
        assert( NULL != root );
        if( ! cson_value_is_object(root) )
        {
            cson_value_free( root );
            rc = cson_rc.TypeError;
        }
        else
        {
            rc = cson_cgi_gc_add( cx,cson_cgi_keys.ENV_CONFIG, root, 1 );
            if( 0 == rc )
            {
                cx->config.jval = root;
                cx->config.jobj = cson_value_get_object( root );
                assert( NULL != cx->config.jobj );
            }
        }
    }
    return rc;
}

static char * cson_cgi_strdup( char const * src )
{
    size_t const n = src ? strlen(src) : 0;
    char * rc = src ? (char *)malloc(n+1) : NULL;
    if( ! rc ) return NULL;
    memcpy( rc, src, n );
    rc[n] = 0;
    return rc;
}

/**
   Writes a 36-byte (plus one NUL byte) UUID value to dest. dest
   must be at least 37 bytes long. If dest is NULL this function
   has no side effects.

   Not thread-safe.
*/
void cson_cgi_generate_uuid( cson_cgi_cx * cx, char * dest )
{
    static whuuid_rng rng = {
    NULL/*rand*/,
    NULL/*cleanup*/,
    NULL/*impl*/
#if WHUUID_CONFIG_KEEP_METRICS
    ,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}/*distribution*/
#endif
    };
    whuuid_t u = whuuid_t_empty;
    if( NULL == dest ) return;
    else if( (NULL==rng.rand) && (NULL != RNG_FILENAME) )
    { /* try to open rng file... */
        /* FIXME: we're missing a cleanup handler for the RNG_FILENAME case. */
        FILE * f = fopen(RNG_FILENAME, "rb");
        if( NULL != f )
        {
            rng = whuuid_rng_FILE;
            rng.impl = f;
        }
    }
    if( NULL == rng.rand )
    { /* fall back to LC rng */
        extern char ** environ;
        void * addr;
        unsigned long seed;
        rng = whuuid_rng_lcrng;
        addr = malloc(
                      (((unsigned long)environ) % 13) + 9
                      );
        free(addr) /* but keep the address as a seed value */;
        seed = (unsigned long)addr * (unsigned long)time(NULL);
        rng.impl = (void *)seed;
    }
    whuuid_fill_rand( &u, &rng );
    whuuid_to_string( &u, dest );
}

char const * cson_cgi_session_id(cson_cgi_cx * cx)
{
    return cx ? cx->session.id : NULL;
}


static int cson_cgi_init_session_mgr(cson_cgi_cx * cx)
{
    /*
       Check for this config structure:

       {
       manager:"mgrID",
       managers:{
           mgrID:{
               sessionDriver: "back-end-name" (e.g. "cpdo" or "file"),
               ... back-end-specific options ...
           },
           otherManager: { ... }
       }
    */
    cson_object const * conf = cson_cgi_env_get_obj(cx,  'f', 0 );
    cson_string const * aString;
    cson_value const * optV = NULL;
    cson_object const * optObj = NULL;
    if( NULL == conf ) return 0;
    assert( cx && !cx->session.mgr );

    /* get "manager" part... */
    aString = cson_value_get_string( cson_object_get_sub( conf, "session.manager", '.' ) );
    if( NULL == aString ) return 0;

    /* Fetch that manager config ... */
    optV = cson_object_get_sub( conf, "session.managers", '.' );
    if( optV )
    {
        optV = cson_object_get( cson_value_get_object( optV ), cson_string_cstr( aString ) );
    }
    optObj = cson_value_get_object( optV );
    if( ! optObj ) return 0;

    /* Get the "sessionDriver" part ... */
    aString = cson_value_get_string( cson_object_get( optObj, "sessionDriver" ) );
    if( NULL == aString ) return 0;

    return cson_sessmgr_load( cson_string_cstr(aString), &cx->session.mgr, optObj );
}


static char const * cson_cgi_get_session_key(cson_cgi_cx * cx)
{
    cson_object const * conf = cson_cgi_env_get_obj( cx, 'f', 0 );
    char const * sessKey = CSON_CGI_KEY_SESSION;
    assert( NULL != cx );
    if( conf )
    {
        cson_string const * k = cson_value_get_string( cson_object_get_sub( conf, "session.cookieName", '.' ) );
        char const * ck = k ? cson_string_cstr(k) : NULL;
        if( ck ) sessKey = ck;
    }
    return sessKey;
}

static int cson_cgi_gen_session_id(cson_cgi_cx * cx)
{
    char buf[37] = {0};
    if( cx->session.id )
    {
        free( cx->session.id );
        cx->session.id = NULL;
    }
    cson_cgi_generate_uuid( cx, buf );
    cx->session.id = cson_cgi_strdup( buf );
    return ( NULL == cx->session.id )
        ? cson_rc.AllocError
        : 0;
}

static int cson_cgi_init_session( cson_cgi_cx * cx, char const * forceID )
{
    char const * idstr;
    char const * sessKey;
    int rc = cson_cgi_init_session_mgr(cx);
    if( 0 != rc ) return rc;
    else if( NULL == cx->session.mgr ) return 0
        /* treat as non-fatal error */;
    sessKey = cson_cgi_get_session_key(cx);
    assert( sessKey && *sessKey );
    /* Try to get the session ID ... */
    idstr = (forceID && *forceID)
        ? forceID
        : cson_string_cstr( cson_value_get_string( cson_cgi_getenv( cx, "cegp", sessKey ) ) );
    if( NULL == idstr )
    { /* Generate a session ID but defer creation of the session
         object until the client does it. If they never use it,
         we won't bother saving the session.
      */
        rc = cson_cgi_gen_session_id(cx);
        if( 0 != rc ) return rc;
    }
    else
    { /* try to load the session */
        cson_value * sessV = NULL;
        free( cx->session.id );
        cx->session.id = cson_cgi_strdup( idstr );
        if( ! cx->session.id ) return cson_rc.AllocError;
        rc = cx->session.mgr->api->load( cx->session.mgr, &sessV,
                                         cx->session.id );
        if( (0 == rc) && sessV )
        {
            rc = cson_cgi_gc_add( cx, cson_cgi_keys.ENV_SESSION, sessV, 1 );
            if( 0 != rc )
            { /* almost certainly an alloc error */
                return rc;
            }
            cx->session.env.jval = sessV;
            cx->session.env.jobj = cson_value_get_object( sessV );
        }
        else
        {
            if( !forceID || !*forceID )
            {
                /* On load error, assume the session ID is
                   stale. Re-generate it to avoid potential future
                   collisions. This heuristic will cause us intermittent
                   grief when loading does not work for a second or three
                   due to network-related problems. Each time that
                   happens, the caller will lose his session.
                */
                rc = cson_cgi_gen_session_id(cx);
                if( 0 != rc ) return rc;
            }
        }
    }
    assert( NULL != cx->session.id );
    { /* make sure the session ID is set in the cookies and has an updated
         expiry time... */
        unsigned int expiry = 0;
        cson_object const * conf;
        cson_value * jstr = cson_value_new_string( cx->session.id,
                                                   strlen(cx->session.id) );
        if( ! jstr ) return cson_rc.AllocError;
        conf = cson_cgi_env_get_obj( cx, 'f', 0 );
        if( conf )
        {
            expiry = cson_value_get_integer( cson_object_get_sub( conf, "session.cookieLifetimeMinutes", '.' ) );
            if( expiry ) expiry *= 60 /* convert to seconds */;
        }
        if( ! expiry )
        {
            expiry = (60*60*24);
        }
        expiry += (unsigned int)time(NULL);
        
        rc = cson_cgi_cookie_set2( cx, sessKey, jstr,
                                   NULL, NULL,
                                   expiry,
                                   0/*FIXME: set 'secure' option in HTTPS mode.*/,
                                   0/*FIXME: make the httponly flag configurable*/ );
        if( 0 != rc )
        {
            cson_value_free( jstr );
            if( cson_rc.AllocError == rc ) return rc;
            rc = 0 /* else treat as non-fatal */;
        }
    }
    return 0;
}



int cson_cgi_init(cson_cgi_cx * cx, int argc, char const * const * argv, cson_cgi_init_opt * opt )
{
    int rc = 0;
    static int hasInited = 0;
    if( NULL == cx ) return cson_rc.ArgError;
    else if( NULL != cx->gc.jval )
    { /* we've already done this or object was mal-initialized... */
        return cson_rc.ArgError;
    }

    assert( NULL != CSON_CGI_GETENV_DEFAULT );

#if CSON_CGI_USE_SIGNALS
    {
        /* FIXME: use sigaction() instead of signal() */
        typedef void (*sighnd)(int);
        sighnd oldSigPipe;
        oldSigPipe = signal(SIGPIPE, SIG_IGN) /* to try avoid unclean termination if client disconnects. */;
        if( SIG_ERR == oldSigPipe )
        {
            return cson_rc.UnknownError;
        }
    }
#endif

    if( ! hasInited )
    {
        hasInited = 1;
        setlocale( LC_ALL, "C" )
            /* supposedly important for underlying JSON parser.
               FIXME: only do this init once!
            */;
    }

    cx->gc.jval = cson_value_new_object();
    if( NULL == cx->gc.jval )
    {
        return cson_rc.AllocError;
    }
    cx->gc.jobj = cson_value_get_object( cx->gc.jval );
    assert( NULL != cx->gc.jobj );

    if( opt )
    {
        cx->opt = *opt;
    }
    if( NULL == cx->opt.inStream ) cx->opt.inStream = stdin;
    if( NULL == cx->opt.outStream ) cx->opt.outStream = stdout;
    if( NULL == cx->opt.errStream ) cx->opt.errStream = stderr;

#define CHECKRC if(rc) goto end
    rc = cson_cgi_import_environ(cx);
    CHECKRC;
    rc = cson_cgi_init_argv( cx, argc, argv );
    CHECKRC;
    { /* read config file */
        char const * conffile = cx->opt.configFile;
        if( ! conffile )
        {
            cson_value const * v = cson_cgi_getenv( cx, "e", "CSON_CGI_CONFIG" );
            if( v && cson_value_is_string(v) )
            {
                conffile = cson_string_cstr( cson_value_get_string( v ) );
            }
        }
        if( conffile )
        {
            cson_cgi_init_config( cx, conffile )
                /* Ignore error code.

                TODO:

                - use argv[0]+".json" as the default config file.
                */
                ;
        }
    }

    rc = cson_cgi_parse_query_string( cx, getenv("QUERY_STRING") );
    CHECKRC;
    rc = cson_cgi_parse_cookies( cx, getenv("HTTP_COOKIE") );
    CHECKRC;
    rc = cson_cgi_init_POST(cx);
    if( cson_rc.AllocError == rc ) goto end;
    else rc = 0
        /* this can fail for several reasons which are non-fatal. */
        ;

    if( (NULL == opt) )
    {
        /* TODO: read these values from cx->config, if available. */
        cx->opt.outOpt.indentation = 1;
        cx->opt.outOpt.addNewline = 1;
        cx->opt.outOpt.addSpaceAfterColon = 1;
        cx->opt.outOpt.indentSingleMemberValues = 1;
    }

    rc = cson_cgi_init_session( cx, opt ? opt->sessionID : NULL )
        /* ignore non-OOM error codes. Not fatal. */;
    if( cson_rc.AllocError == rc ) goto end;
    else rc = 0;

    /*
      TODOs:

      - Read form-urlencoded POST data. (Do this BEFORE
      restoring the session, so that we can get the session
      ID from there if needed.)
    */
    end:
    return rc;
#undef CHECKRC
}

#undef cson_cgi_env_map_empty_m
#undef CSON_CGI_USE_SIGNALS
#undef RNG_FILENAME
/* end file cgi/cson_cgi.c */







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







 







|













|
|
|
>

<
|
|
|
|
|
|
|
|
|




<
<
<
<
<
<
<
<
<
<
|







 







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







 







<
<
<

<




|












<

<












|
|






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







 







<

<




|







 







|
|






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







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
5131
5132
5133
5134
5135
5136
5137






















































































































































































































































































































































































5138
5139
5140
5141
5142
5143
5144
....
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181

5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194










5195
5196
5197
5198
5199
5200
5201
5202
....
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
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
5299
5300
5301
5302
5303
....
5306
5307
5308
5309
5310
5311
5312



5313

5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330

5331

5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351














5352
5353
5354
5355
5356
5357
5358
....
5368
5369
5370
5371
5372
5373
5374

5375

5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
....
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414














5415
5416
5417
5418
5419
5420
5421
....
5443
5444
5445
5446
5447
5448
5449





























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































            if(obj) cleaner(obj);
        }
    }
    cson_kvp_list_reserve(self,0);
}
/* end file ./cson_lists.h */






















































































































































































































































































































































































/* begin file ./cson_sqlite3.c */
/** @file cson_sqlite3.c

This file contains the implementation code for the cson
sqlite3-to-JSON API.

License: the same as the cson core library.
................................................................................
#define MARKER if(0) printf
#endif

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

cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col )
{
    if( ! st ) return NULL;
    else
    {
#if 0
        sqlite3_value * val = sqlite3_column_type(st,col);
        int const vtype = val ? sqlite3_value_type(val) : -1;
        if( ! val ) return cson_value_null();
#else
        int const vtype = sqlite3_column_type(st,col);
#endif
        switch( vtype )
        {
          case SQLITE_NULL:
              return cson_value_null();
          case SQLITE_INTEGER:
              /* FIXME: for large integers fall back to Double instead. */
              return cson_value_new_integer( (cson_int_t) sqlite3_column_int64(st, col)  );

          case SQLITE_FLOAT:
              return cson_value_new_double( sqlite3_column_double(st, col) );
          case SQLITE_BLOB: /* arguably fall through... */
          case SQLITE_TEXT: {
              char const * str = (char const *)sqlite3_column_text(st,col);
              return cson_value_new_string(str, str ? strlen(str) : 0);
          }
          default:
              return NULL;
        }
    }
}











cson_value * cson_sqlite3_column_names( sqlite3_stmt * st )
{
    cson_value * aryV = NULL;
    cson_array * ary = NULL;
    char const * colName = NULL;
    int i = 0;
    int rc = 0;
    int colCount = 0;
................................................................................
    else
    {
        cson_value_free(aryV);
        return NULL;
    }
}


cson_value * cson_sqlite3_row_to_object( sqlite3_stmt * st )
{
    cson_value * rootV = NULL;
    cson_object * root = NULL;
    char const * colName = NULL;
    int i = 0;
    int rc = 0;
    cson_value * currentValue = NULL;
    int const colCount = sqlite3_column_count(st);
    if( !colCount ) return NULL;
    rootV = cson_value_new_object();
    if(!rootV) return NULL;
    root = cson_value_get_object(rootV);
    for( i = 0; i < colCount; ++i )
    {
        colName = sqlite3_column_name( st, i );
        if( ! colName ) goto error;
        currentValue = cson_sqlite3_column_to_value(st,i);
        if( ! currentValue ) currentValue = cson_value_null();
        rc = cson_object_set( root, colName, currentValue );
        if( 0 != rc )
        {
            cson_value_free( currentValue );
            goto error;
        }
    }
    goto end;
    error:
    cson_value_free( rootV );
    rootV = NULL;
    end:
    return rootV;
}

cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st )
{
    cson_value * aryV = NULL;
    cson_array * ary = NULL;
    int i = 0;
    int rc = 0;
    int const colCount = sqlite3_column_count(st);
    if( ! colCount ) return NULL;
    aryV = cson_value_new_array();
    if( ! aryV ) return NULL;
    ary = cson_value_get_array(aryV);
    rc = cson_array_reserve(ary, (unsigned int) colCount );
    if( 0 != rc ) goto error;

    for( i = 0; i < colCount; ++i ){
        cson_value * elem = cson_sqlite3_column_to_value(st,i);
        if( ! elem ) goto error;
        rc = cson_array_append(ary,elem);
        if(0!=rc)
        {
            cson_value_free( elem );
            goto end;
        }
    }
    goto end;
    error:
    cson_value_free(aryV);
    aryV = NULL;
    end:
    return aryV;
}

    
/**
    Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
    parameter is non-0.
*/
static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt )
{
#define RETURN(RC) { if(rootV) cson_value_free(rootV); return RC; }
................................................................................
    {
        cson_value * rootV = NULL;
        cson_object * root = NULL;
        cson_value * colsV = NULL;
        cson_value * rowsV = NULL;
        cson_array * rows = NULL;
        cson_value * objV = NULL;



        int rc = 0;

        int const colCount = sqlite3_column_count(st);
        if( colCount <= 0 ) return cson_rc.ArgError;
        rootV = cson_value_new_object();
        if( ! rootV ) return cson_rc.AllocError;
        colsV = cson_sqlite3_column_names(st);
        if( ! colsV )
        {
            cson_value_free( rootV );
            RETURN(cson_rc.AllocError);
        }
        root = cson_value_get_object(rootV);
        rc = cson_object_set( root, "columns", colsV );
        if( rc )
        {
            cson_value_free( colsV );
            RETURN(rc);
        }

        colsV = NULL;

        rowsV = cson_value_new_array();
        if( ! rowsV ) RETURN(cson_rc.AllocError);
        rc = cson_object_set( root, "rows", rowsV );
        if( rc )
        {
            cson_value_free( rowsV );
            RETURN(rc);
        }
        rows = cson_value_get_array(rowsV);
        assert(rows);
        while( SQLITE_ROW == sqlite3_step(st) )
        {
            objV = cson_sqlite3_row_to_object(st);
            if( ! objV ) RETURN(cson_rc.UnknownError);
            rc = cson_array_append( rows, objV );
            if( rc )
            {
                cson_value_free( objV );
                RETURN(rc);
            }














        }
        *tgt = rootV;
        return 0;
    }
#undef RETURN
}

................................................................................
    {
        cson_value * rootV = NULL;
        cson_object * root = NULL;
        cson_value * aryV = NULL;
        cson_array * ary = NULL;
        cson_value * rowsV = NULL;
        cson_array * rows = NULL;

        int rc = 0;

        int const colCount = sqlite3_column_count(st);
        if( colCount <= 0 ) return cson_rc.ArgError;
        rootV = cson_value_new_object();
        if( ! rootV ) return cson_rc.AllocError;
        aryV = cson_sqlite3_column_names(st);
        if( ! aryV )
        {
            cson_value_free( rootV );
            RETURN(cson_rc.AllocError);
        }
        root = cson_value_get_object(rootV);
        rc = cson_object_set( root, "columns", aryV );
................................................................................
            cson_value_free( rowsV );
            RETURN(rc);
        }
        rows = cson_value_get_array(rowsV);
        assert(rows);
        while( SQLITE_ROW == sqlite3_step(st) )
        {
            aryV = cson_sqlite3_row_to_array(st);
            if( ! aryV ) RETURN(cson_rc.UnknownError);
            rc = cson_array_append( rows, aryV );
            if( 0 != rc )
            {
                cson_value_free( aryV );
                RETURN(rc);
            }














        }
        *tgt = rootV;
        return 0;
    }
#undef RETURN
}

................................................................................

#if defined(__cplusplus)
} /*extern "C"*/
#endif
#undef MARKER
#endif /* CSON_ENABLE_SQLITE3 */
/* end file ./cson_sqlite3.c */




























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Changes to src/cson_amalgamation.h.

2042
2043
2044
2045
2046
2047
2048

















































2049
2050
2051
2052
2053
2054
2055
....
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
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
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
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
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
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
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
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
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
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
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
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
#if CSON_ENABLE_SQLITE3 /* we do this here for the sake of the amalgamation build */
#include <sqlite3.h>

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


















































/**
    Converts the results of an sqlite3 SELECT statement to JSON,
    in the form of a cson_value object tree.
    
    st must be a prepared, but not yet traversed, SELECT query.
    tgt must be a pointer to NULL (see the example below). If
    either of those arguments are NULL, cson_rc.ArgError is returned.
................................................................................
#if defined(__cplusplus)
} /*extern "C"*/
#endif
    
#endif /* CSON_ENABLE_SQLITE3 */
#endif /* WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED */
/* end file include/wh/cson/cson_sqlite3.h */
/* begin file include/wh/cson/cson_session.h */
#if !defined(WANDERINGHORSE_NET_CSON_SESSION_H_INCLUDED)
#define WANDERINGHORSE_NET_CSON_SESSION_H_INCLUDED 1

/** @page page_cson_session cson Session API

The cson_session API provides a small interface,
called cson_sessmgr, which defines the basic operations
needed for implementent persistent application state,
across application sessions, by storing the state as
JSON data in "some back-end storage." The exact underlying
storage is not specified by the interface, but two
implementations are provided by the library:

- File-based sessions.

- Database-based sessions, using libcpdo for connection
abstraction.

libcpdo is included, in full, in the cson source tree,
but can also be found on its web page:

    http://fossil.wanderinghorse.net/repos/cpdo/

@see cson_sessmgr_register()
@see cson_sessmgr_load()
@see cson_sessmgr_names()
@see cson_sessmgr
@see cson_sessmgr_api
*/


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

    typedef struct cson_sessmgr cson_sessmgr;
    typedef struct cson_sessmgr_api cson_sessmgr_api;

    /** @struct cson_sessmgr_api
        
       Defines operations required by "session managers." Session managers
       are responsible for loading and saving cson session information
       in the form of JSON data.

       @see cson_sessmgr
    */
    struct cson_sessmgr_api
    {
        /**
           Loads/creates a session object (JSON data). The
           implementation must use the given identifier for loading an
           existing session, creating the session if createIfNeeded is
           true and the session data is not found. If createIfNeeded
           is true then the implementation must create an Object for
           the session root, as opposed to an Array or other JSON
           value. Clients are allowed to use non-Objects as their
           sessions but doing so would seem to have no benefit, and is
           not recommended.

           If the given id cannot be found then cson_rc.NotFoundError
           must be returned. On success it must assign the root node
           of the session tree to *tgt and return 0.  On error *tgt
           must not be modified and non-zero must be returned.

           On success ownership of *tgt is transfered to the caller.

           Error conditions include:

           - self, tgt, or id are NULL: cson_rc.ArgError

           - id is "not valid" (the meaning of "valid" is
           implementation-dependent): cson_rc.ArgError

           The identifier string must be NUL-terminated. Its maximum
           length, if any, is implementation-dependent.
        */
        int (*load)( cson_sessmgr * self, cson_value ** tgt, char const * id );

        /**
           Must save the given JSON object tree to the underlying storage, using the given identifier
           as its unique key. It must overwrite any existing session with that same identifier.
        */
        int (*save)( cson_sessmgr * self, cson_value const * root, char const * id );

        /**
           Must remove all session data associated with the given id.

           Must return 0 on success, non-0 on error.
        */
        int (*remove)( cson_sessmgr * self, char const * id );
        /**
           Must free up any resources used by the self object and then
           free self. After calling this, further use of the self
           object invokes undefined behaviour.
        */
        void (*finalize)( cson_sessmgr * self );
    };

    /**
       cson_sessmgr is base interface type for concrete
       cson_sessmgr_api implementations.  Each holds a pointer to its
       underlying implementation and to implementation-private
       data.
    
       @see cson_sessmgr_register()
       @see cson_sessmgr_load()
       @see cson_sessmgr_names()
       @see cson_sessmgr_api
    */
    struct cson_sessmgr
    {
        /**
           The concrete implementation functions for this
           session manager instance.
        */
        const cson_sessmgr_api * api;
        /**
           Private implementation date for this session manager
           instance. It is owned by this object and will be freed when
           thisObject->api->finalize(thisObject) is called. Client
           code must never use nor rely on the type/contents of the
           memory stored here.
        */
        void * impl;
    };

    /**
       A typedef for factory functions which instantiate cson_sessmgr
       instances.

       The semantics are:

       - tgt must be a non-NULL pointer where the result object can be
       stored. If it is NULL, cson_rc.ArgError must be returned.

       - opt (configuration options) may or may not be required,
       depending on the manager. If it is required and not passed in,
       cson_rc.ArgError must be returned. If the config options are
       required but the passed-in object is missing certain values, or
       has incorrect values, the implementation may substitute
       sensible defaults (if possible) or return cson_rc.ArgError.

       - On error non-0 (one of the cson_rc values) must be returned
       and tgt must not be modified.

       - On success *tgt must be pointed to the new manager object,
       zero must be returned, and the caller takes over ownership of
       the *tgt value (and must eventually free it with
       obj->api->finalize(obj)).
    */
    typedef int (*cson_sessmgr_factory_f)( cson_sessmgr ** tgt, cson_object const * config );
    
#define cson_sessmgr_empty_m { NULL/*api*/, NULL/*impl*/ }

    /**
       Registers a session manager by name. The given name must be a
       NUL-terminaed string shorter than some internal limit
       (currently 32 bytes, including the trailing NUL). f must be a
       function conforming to the cson_sessmgr_factory_f() interface.

       On success returns 0.

       On error either one of the arguments was invalid, an entry with
       the given name was already found, or no space is left in the
       internal registration list. The API guarantees that at least 10
       slots are initially available, and it is not anticipated that
       more than a small handful of them will ever be used.

       This function is not threadsafe - do not register factories
       concurrently from multiple threads.

       By default the following registrations are (possibly)
       pre-installed:

       - "file" = cson_sessmgr_file()

       - "cpdo" = cson_sessmgr_cpdo() IF this library is compiled with
       the macro CSON_ENABLE_CPDO set to a true value. Exactly which
       databases are supported by that back-end (if any) are
       determined by how the cpdo library code is compiled.

       - "whio_ht" = cson_sessmgr_whio_ht() IF this library is compiled
       with whio support.

       - "whio_epfs" = cson_sessmgr_whio_epfs() IF this library is
       compiled with whio support.
    */
    int cson_sessmgr_register( char const * name, cson_sessmgr_factory_f f );

    /**
       A front-end to loading cson_sessmgr intances by their
       cson_session-conventional name. The first arguments must be a
       NUL-terminated string holding the name of the session manager
       driver. The other two arguments have the same semantics as for
       cson_sessmgr_factory_f(), so see that typedef's documentation
       regarding, e.g., ownership of the *tgt value.

       This function is thread-safe with regards to itself but not
       with regards to cson_sessmgr_register(). That is, it is legal
       to call this function concurrently from multiple threads,
       provided the arguments themselves are not being used
       concurrently. However, it is not safe to call this function
       when cson_sessmgr_register() is being called from another
       thread, as that function modifies the lookup table used by this
       function.

       On success 0 is returned and the ownership of *tgt is as
       documented for cson_sessmgr_factory_f(). On error non-0 is
       returned and tgt is not modified.
    */
    int cson_sessmgr_load( char const * name, cson_sessmgr ** tgt, cson_object const * opt );

    /**
       Returns the list of session managers registered via
       cson_sessmgr_register(). This function is not thread-safe in
       conjunction with cson_sessmgr_register(), and results are
       undefined if that function is called while this function is
       called or the results of this function call are being used.

       The returned array is never NULL but has a NULL as its final
       entry.

       Example usage:

       @code
       char const * const * mgr = cson_sessmgr_names();
       for( ; *mgr; ++mgr ) puts( *mgr );
       @endcode
    */
    char const * const * cson_sessmgr_names();
    
    /**
       A cson_sessmgr_factory_f() implementation which returns a new
       session manager which uses local files for storage.

       tgt must be a non-NULL pointer where the result can be stored.
       
       The opt object may be NULL or may be a configuration object
       with the following structure:

       @code
       {
       dir: string (directory to store session files in),
       prefix: string (prefix part of filename),
       suffix: string (file extension, including leading '.')
       }
       @endcode

       Any missing options will assume (unspecified) default values.
       This routine does not ensure the validity of the option values,
       other than to make sure they are strings.

       The returned object is owned by the caller, who must eventually
       free it using obj->api->finalize(obj). If it returns NULL,
       the error was an out-of-memory condition or tgt was NULL.

       On error non-0 is returned, but the only error conditions are
       allocation errors and (NULL==tgt), which will return
       cson_rc.AllocError resp. cson_rc.ArgError.

       Threading notes:

       - As long as no two operations on these manager instances use
       the same JSON object and/or session ID at the same time,
       multi-threaded usage should be okay. All save()/load()/remove()
       data is local to those operations, with the exception of the
       input arguments (which must not be used concurrently to those
       calls).

       Storage locking:

       - No locking of input/output files is done, under the
       assumption that only one thread/process will be using a given
       session ID (which should, after all, be unique world-wide).  If
       sessions will only be read, not written, there is little danger
       of something going wrong vis-a-vis locking (assuming the
       session files exists and can be read).

       TODO:

       - Add a config option to enable storage locking. If we'll
       re-implement this to use the whio API under the hood then we
       could use the (slightly simpler) whio_lock API for this.
    */
    int cson_sessmgr_file( cson_sessmgr ** tgt, cson_object const * opt );

    /**
       This is only available if cson is compiled with cpdo support.

       Implements the cson_sessmgr_factory_f() interface.
       
       This function tries to create a database connection using the options
       supplied in the opt object. The opt object must structurarly look like:

       @code
       {
       "dsn": "cpdo dsn string",
       "user": "string",
       "password": "string",
       "table": "table_name_where_sessions_are_stored",
       "fieldId": "field_name_for_session_id (VARCHAR/STRING)",
       "fieldTimestamp": "field_name_for_last_saved_timestamp (INTEGER)",
       "fieldSession": "field_name_for_session_data (TEXT)"
       }
       @endcode

       On success it returns 0 and sets *tgt to the new session manager,
       which is owned by the caller and must eventually be freed by calling
       obj->api->finalize(obj).

       This function can fail for any number of reasons:

       - Any parameters are NULL (cson_rc.ArgError).

       - cpdo cannot connect to the given DSN with the given
       username/password. Any error in establishing a connection causes
       cson_rc.IOError to be returned, as opposed to the underlying
       cpdo error code.

       - Any of the "table" or "fieldXXX" properties are NULL. It
       needs these data in order to know where to load/save sessions.


       If any required options are missing, cson_rc.ArgError is
       returned.

       TODO: add option "preferBlob", which can be used to set the db
       field type preference for the fieldSession field to
       blob. Currently it prefers string but will try blob operations
       if string ops fail.  Blobs have the disadvantage of much larger
       encoded sizes but the advantage that the JSON data is encoded
       (at least by sqlite3) as a hex number stream, making it
       unreadable to casual observers.       

       @endcode
    */
    int cson_sessmgr_cpdo( cson_sessmgr ** tgt, cson_object const * opt );

    /**
       This cson_sessmgr_factory_f() implementation might or might not
       be compiled in, depending on the mood of the cson
       maintainer. It is very niche-market, and primarily exists just
       to test (and show off) the whio_ht code.

       It uses libwhio's on-storage hashtable (called whio_ht)
       as the underlying storage:
       
       http://fossil.wanderinghorse.net/repos/whio/index.cgi/wiki/whio_ht

       The opt object must not be NULL and must contain a single
       string property named "file" which contains the path to the
       whio_ht file to use for sessions. That file must have been
       previously created, either programatically using the whio_ht
       API or using whio-ht-tool:

       http://fossil.wanderinghorse.net/repos/whio/index.cgi/wiki/whio_ht_tool

       See cson_sessmgr_factory_f() for the semantics of the tgt
       argument and the return value.

       Threading notes:

       While the underlying hashtable supports a client-defined mutex,
       this usage of it does not set one (because we have no default
       one to use). What this means for clients is that they must not
       use this session manager from multiple threads, nor may they
       use multiple instances in the same process which use the same
       underlying hashtable file from multiple threads. How best to
       remedy this (allowing the client to tell this API what mutex to
       use) is not yet clear. Maybe a global whio_mutex object which
       the client must initialize before instantiating these session
       managers.
       
       Storage Locking:

       If the underlying filesystem reports that it supports file
       locking (via the whio_lock API, basically meaning POSIX
       fcntl()-style locking) the the session manager will use it. For
       the load() operation a read lock is acquired and for
       save()/remove() a write lock. The operations will fail if
       locking fails, the exception being if the device reports that
       it doesn't support locking, in which case we optimistically
       save/load/remove without locking.

       Remember that in POSIX-style locking, a single process does not
       see its own locks and can overwrite locks set via other
       threads. This means that multi-threaded use of a given
       instance, or multiple instances in the same process using the
       same underlying hashtable file, will likely eventually corrupt
       the hashtable.

       TODO:

       - Add a config option to disable storage locking, for clients
       who really don't want to use it.
    */
    int cson_sessmgr_whio_ht( cson_sessmgr ** tgt, cson_object const * opt );

    /**
       This cson_sessmgr_factory_f() implementation might or might not
       be compiled in, depending on the mood of the cson
       maintainer. It is very niche-market, and primarily exists just
       to test (and show off) the whio_epfs code.

       It uses libwhio's embedded filesystem (called whio_epfs) as the
       underlying storage:
       
       http://fossil.wanderinghorse.net/repos/whio/index.cgi/wiki/whio_epfs

       The opt object must not be NULL and must contain a single
       string property named "file" which contains the path to the
       whio_epfs "container file" to use for storing sessions. That
       file must have been previously created, either programatically
       using the whio_epfs API or using whio-epfs-mkfs:

       http://fossil.wanderinghorse.net/repos/whio/index.cgi/wiki/whio_epfs_mkfs

       The EPFS container file MUST be created with a "namer"
       installed. See the above page for full details and examples.
       
       See cson_sessmgr_factory_f() for the semantics of the tgt
       argument and the return value.

       Threading notes:

       - It is not legal to use this session manager from multiple threads.
       Doing so will eventually corrupt the underlying EFS if multiple writers
       work concurrently, and will also eventually _appear_ corrupt to multiple
       readers.

       Storage locking:

       The underlying storage (EFS container file) is locked (with a
       write lock) for the lifetime the the returned session manager
       IF the storage reports that it supports locking. Unlocked
       write access from an outside application will corrupt the EFS.

       TODOs:

       - Add config option to explicitly disable locking support.
    */
    int cson_sessmgr_whio_epfs( cson_sessmgr ** tgt, cson_object const * opt );
    
#if 0
    /** TODO? dummy manager which has no i/o support. */
    int cson_sessmgr_transient( cson_sessmgr ** tgt );
#endif

/* LICENSE

This software's source code, including accompanying documentation and
demonstration applications, are licensed under the following
conditions...

Certain files are imported from external projects and have their own
licensing terms. Namely, the JSON_parser.* files. See their files for
their official licenses, but the summary is "do what you want [with
them] but leave the license text and copyright in place."

The author (Stephan G. Beal [http://wanderinghorse.net/home/stephan/])
explicitly disclaims copyright in all jurisdictions which recognize
such a disclaimer. In such jurisdictions, this software is released
into the Public Domain.

In jurisdictions which do not recognize Public Domain property
(e.g. Germany as of 2011), this software is Copyright (c) 2011 by
Stephan G. Beal, and is released under the terms of the MIT License
(see below).

In jurisdictions which recognize Public Domain property, the user of
this software may choose to accept it either as 1) Public Domain, 2)
under the conditions of the MIT License (see below), or 3) under the
terms of dual Public Domain/MIT License conditions described here, as
they choose.

The MIT License is about as close to Public Domain as a license can
get, and is described in clear, concise terms at:

    http://en.wikipedia.org/wiki/MIT_License

The full text of the MIT License follows:

--
Copyright (c) 2011 Stephan G. Beal (http://wanderinghorse.net/home/stephan/)

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

--END OF MIT LICENSE--

For purposes of the above license, the term "Software" includes
documentation and demonstration source code which accompanies
this software. ("Accompanies" = is contained in the Software's
primary public source code repository.)

*/

#if defined(__cplusplus)
} /*extern "C"*/
#endif

#endif /* WANDERINGHORSE_NET_CSON_SESSION_H_INCLUDED */
/* end file include/wh/cson/cson_session.h */
/* begin file include/wh/cson/cson_cgi.h */
#if !defined(WANDERINGHORSE_NET_CSON_CGI_H_INCLUDED)
#define WANDERINGHORSE_NET_CSON_CGI_H_INCLUDED 1

/** @page page_cson_cgi cson CGI API

cson_cgi is a small framework encapsulating features usefor for
writing JSON-only applications (primarily CGI apps) in C. It is based
off of the cson JSON library:

    http://fossil.wanderinghorse.net/repos/cson/

In essence, it takes care of the basic CGI-related app setup, making
the data somewhat more accessible for client purposes.  Clients
create, as output, a single JSON object. The framework takes care of
outputing it, along with any necessary HTTP headers.


Primary features:

- Uses cson for its JSON handling, so it's pretty simple to use.

- Provides a simple-to-use mini-framework for writing CGI applications
which generate only JSON output.

- Various sources of system data are converted by the framework to
JSON for use by the client. This includes HTTP GET, OS environment,
command-line arguments, HTTP cookies, and (with some limitations) HTTP
POST.

- Can read unencoded JSON POST data (TODO: read in form-urlencoded as
a JSON object).

- Supports an optional JSON config file.

- Optional persistent sessions using files, sqlite3, or MySQL5
for the storage.


Primary misfeatures:

- Very young and not yet complete.

- Intended for writing apps which ONLY generate JSON data, not HTML.

- JSONP output support is currently incomplete.

- TODO: support reading of POSTed Array data (currently only Objects
work).

- We're missing a good number of convenience functions.

- Add client API for setting cookies. Currently they can be fetched
or removed but not explicitly set.


Other potential TODOs:

- Session support using session cookies for the IDs. For this to work
we also need to add a storage back-end or two (files, db (e.g. using
cpdo), etc.) and have a fast, good source of random numbers for
generating UUIDs. It _seems_ that using the address of (extern char **
environ) as a seed might be fairly random, but there are probably
environments where that won't suffice. i want to avoid
platform-specific bits, like /dev/urandom, if possible.

- Add client-definable i/o routines. We have this code in the whprintf
tree, but i was hoping to avoid having to import that here. This would
allow a client to add, e.g., gzip support.

*/

/** @page page_cson_cgi_session cson CGI Sessions

cson_cgi_init() will initialize a persistent session if it can figure
out everything it needs in order to do so. If it cannot it will simply
skip session initialization. A client can tell if a session was
established or not by calling cson_cgi_get_env_val(cx,'s',0).  If that
returns NULL then no session was created during initialization.
The API currently provides no way to initialize one after the fact.
That is, client code can use cson_cgi_get_env_val(cx,'s',1) to create a
session object, but it won't automatically be persistent across
application sessions.

Session management is really just the following:

- Load a JSON object from some persistent storage.
- Save that object at shutdown.

"Persistent storage" is the important phrase here. How the sessions
are saved is really unimportant. The library uses a generic interface
(cson_sessmgr) to handle the i/o, and can use any implementation we
care to provide. As of this writing (20110413) files, sqlite3, and
MySQL5 are supported.

The session object is a JSON object available via cson_cgi_getenv(),
using "s" as the environment name (the second parameter to
cson_cgi_getenv()).

At library shutdown (typically when main() exits, but also via
cson_cgi_cx_clean()), the session is saved using the current
session manager. If the session is not loaded during initialization,
but is created later, the library will assign a new session ID to it
before saving.

See cson_cgi_config_file() for examples of configuring the session
management.
*/


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

    /** @def CSON_CGI_ENABLE_POST_FORM_URLENCODED

    If CSON_CGI_ENABLE_POST_FORM_URLENCODED is set to a true value
    then the API will try to process form-urlencoded POST data,
    otherwise it will not.

    Reminder to self: this is basically a quick hack for fossil
    integration. We disable form encoding in that build because fossil
    handles that itself and we must not interfere with it.
    */
#if !defined(CSON_CGI_ENABLE_POST_FORM_URLENCODED)
#define CSON_CGI_ENABLE_POST_FORM_URLENCODED 0
#endif    
    /** @def CSON_CGI_GETENV_DEFAULT

        The default environment name(s) to use for cson_cgi_getenv().
    */
#define CSON_CGI_GETENV_DEFAULT "gpce"
    /** @def CSON_CGI_KEY_JSONP

        TODO?: get rid of this and move cson_cgi_keys into the public
        API?

        The default environment key name to use for checking whether a
        response should used JSONP or not.
    */
#define CSON_CGI_KEY_JSONP "jspon"

#define CSON_CGI_KEY_SESSION "CSONSESSID"


    typedef struct cson_cgi_init_opt cson_cgi_init_opt;
    /** @struct cson_cgi_init_opt

        A type to hold core runtime configuration information for
        cson_cgi.
    */
    struct cson_cgi_init_opt
    {
        /**
           stdin stream. If NULL, stdin is used.
        */
        FILE * inStream;
        /**
           stdout stream. If NULL, stdout is used.
        */
        FILE * outStream;
        /**
           stderr stream. If NULL, stderr is used.
         */
        FILE * errStream;
        /**
           Path to a JSON config file.
         */
        char const * configFile;

        /**
           If set then the session will be forced to use this
           ID, otherwise one will be generated.
        */
        char const * sessionID;

        /**
           Values are interpretted as:

           0 = do not output headers when cson_cgi_response_output_all()
           is called.

           (>0) = always output headers when cson_cgi_response_output_all()
           is called.

           (<0) = Try to determine, based on environment variables, if
           we are running in CGI mode. If so, output the headers when
           cson_cgi_response_output_all() is called, otherwise omit
           them.

           If the headers are omitted then so is the empty line
           which normally separates them from the response body!

           The intention of this flag is to allow non-CGI apps
           to disable output of the HTTP headers.
        */
        char httpHeadersMode;
        
        /**
           JSON output options.
        */
        cson_output_opt outOpt;

    };

    /** Empty-initialized cson_cgi_init_opt object. */
#define cson_cgi_init_opt_empty_m {                             \
        NULL/*inStream*/, NULL/*outStream*/, NULL/*errStream*/, \
            NULL /*configFile*/, NULL /*sessionID*/,            \
            -1/*httpHeadersMode*/,                              \
            cson_output_opt_empty_m /*outOpt*/                  \
    }

    /** Empty-initialized cson_cgi_init_opt object. */
    extern const cson_cgi_init_opt cson_cgi_init_opt_empty;


    /** @struct cson_cgi_env_map
       Holds a cson_object and its cson_value parent
       reference.
    */
    struct cson_cgi_env_map
    {
        /** Parent reference of jobj. */
        cson_value * jval;
        /** Object reference of jval. */
        cson_object * jobj;
    };
    typedef struct cson_cgi_env_map cson_cgi_env_map;

    /** Empty cson_cgi_env_map object. */
#define cson_cgi_env_map_empty_m { NULL, NULL }

    /** @struct cson_cgi_cx

       Internal state used by cson_cgi.

       Clients must not rely on its internal structure. It is in the
       public API so that it can be stack- or custom-allocated.  To
       properly initialize such an object, use cson_cgi_cx_empty_m
       cson_cgi_cx_empty, depending on the context.
    */
    struct cson_cgi_cx
    {
        /**
           Various key/value stores used by the framework. Each
           corresponds to some convention source of key/value
           pairs, e.g. QUERY_STRING or POST parameters.
        */
        struct {
            /**
               The system's environment variables.
            */
            cson_cgi_env_map env;
            /**
               Holds QUERY_STRING key/value pairs.
            */
            cson_cgi_env_map get;
            /**
               Holds POST form/JSON data key/value pairs.
            */
            cson_cgi_env_map post;
            /**
               Holds cookie key/value pairs.
            */
            cson_cgi_env_map cookie;
            /**
               Holds request headers.
            */
            cson_cgi_env_map headers;
        } request;
        /**
           Holds data related to the response JSON.
        */
        struct {
            /**
               HTTP error code to report.
            */
            int httpCode;

            /**
               Root JSON object. Must be an Object or Array (per the JSON
               spec).
            */
            cson_value * root;
            /**
               Holds HTTP response headers as an array of key/value
               pairs.
            */
            cson_cgi_env_map headers;
        } response;
        /**
           A place to store cson_value references
           for cleanup purposes.
        */
        cson_cgi_env_map gc;
        /**
           Holds client-defined key/value pairs.
        */
        cson_cgi_env_map clientEnv;
        cson_cgi_env_map config;
        struct {
            cson_cgi_env_map env;
            cson_sessmgr * mgr;
            char * id;
        } session;
        struct {
            cson_array * jarr;
            cson_value * jval;
        } argv;

        cson_cgi_init_opt opt;
        cson_buffer tmpBuf;
        struct {
            char isJSONP;
            void const * allocStamp;
        } misc;
    };
    typedef struct cson_cgi_cx cson_cgi_cx;
    /**
       Empty-initialized cson_cgi_cx object.
     */
    extern const cson_cgi_cx cson_cgi_cx_empty;
    /**
       Empty-initialized cson_cgi_cx object.
     */
#define cson_cgi_cx_empty_m \
    { \
    { /*maps*/ \
        cson_cgi_env_map_empty_m /*env*/, \
        cson_cgi_env_map_empty_m /*get*/, \
        cson_cgi_env_map_empty_m /*post*/, \
        cson_cgi_env_map_empty_m /*cookie*/, \
        cson_cgi_env_map_empty_m /*headers*/ \
    }, \
    {/*response*/ \
        0 /*httpCode*/, \
        NULL /*root*/, \
        cson_cgi_env_map_empty_m /*headers*/ \
    }, \
    cson_cgi_env_map_empty_m /*gc*/, \
    cson_cgi_env_map_empty_m /*clientEnv*/, \
    cson_cgi_env_map_empty_m /*config*/, \
    {/*session*/ \
        cson_cgi_env_map_empty_m /*env*/, \
        NULL /*mgr*/, \
        NULL /*id*/ \
    }, \
    {/*argv*/ \
        NULL /*jarr*/, \
        NULL /*jval*/ \
    }, \
    cson_cgi_init_opt_empty_m /*opt*/, \
    cson_buffer_empty_m /* tmpBuf */, \
    {/*misc*/ \
        -1 /*isJSONP*/,                         \
            NULL/*allocStamp*/ \
    } \
    }

    cson_cgi_cx * cson_cgi_cx_alloc();
    /**
       Cleans up all internal state of cx. IFF cx was allocated by
       cson_cgi_cx_alloc() then cx is also free()d, else it is assumed
       to have been allocated by the caller (possibly on the stack).

       Returns 1 if cx is not NULL and this function actually frees
       it.  If it returns 0 then either cx is NULL or this function
       cleaned up its internals but did not free(cx) (cx is assumed to
       have been allocated by the client).
    */
    char cson_cgi_cx_clean(cson_cgi_cx * cx);
    
    /**
       Initializes the internal cson_cgi environment and must be
       called one time, from main(), at application startup.

       cx must be either:

       - Created via cson_cgi_cx_alloc().

       - Alternately allocated (e.g. on the stack) and initialized
       by copying cson_cgi_cx_empty over it.

       Any other initialization leads to undefined behaviour.
       
       Returns 0 on success. On error the problem is almost certainly
       an allocation error. If it returns non-0 then the rest of the
       API will not work (and using the rest of the API invokes
       undefined behaviour unless documented otherwise for a specific
       function), so the application should exit immediately with an
       error code. The error codes returned by this function all come
       from the cson_rc object.

       The returned object must eventually be passed to
       cson_cgi_cx_clean(), regardless of success or failure, to clean
       up any resources allocated for the object.

       On success:

       - 0 is returned.

       - The cx object takes over ownership of any streams set in the
       opt object UNLESS they are the stdin/stdout/stderr streams (in
       which case ownership does not change).

       On error non-0 is returned and ownership of the opt.xxStream
       STILL transfers over to cx as described above (because this
       simpifies client-side error handling ).
       
       The 'opt' parameter can be used to tweak certain properties
       of the framework. It may be NULL, in which case defaults are
       used.
       
       This function currently performs the following initialization:

       - Parses QUERY_STRING environment variable into a JSON object.

       - Parses the HTTP_COOKIE environment variable into a JSON object.
       
       - Transforms the system environment to JSON.

       - Copies the list of arguments (argv, in the form conventional
       for main()) to JSON array form for downstream use by the client
       application. It does not interpret these arguments in any
       way. Clients may use cson_cgi_argv() and
       cson_cgi_argv_array() to fetch the list later on in the
       application's lifetime (presumably outside of main()). It is
       legal to pass (argv==NULL) only if argc is 0 or less.

       - If the CONTENT_TYPE env var is one of (application/json,
       application/javascript, or text/plain) and CONTENT_LENGTH is
       set then stdin is assumed to be JSON data coming in via POST.
       An error during that parsing is ignored for initialization purposes
       unless it is an allocation error, in which case it is propagated
       back to the caller of this function.

       - If the CSON_CGI_CONFIG env var is set then that file is read.
       Errors in loading the config are silently ignored.

       - If session management is properly configured in the
       configuration file and if a variable named CSON_CGI_KEY_SESSION
       is found in the environment (cookies, GET, POST, or system env)
       then the previous session is loaded. If it cannot be loaded,
       the error is ignored. (Note that the cookie name can be
       changed via the configuration file.)

       TODOs:

       - Add config file option to the opt object.
       
       - Only read POST data when REQUEST_METHOD==POST?
       
       - Convert form-urlencoded POST data to a JSON object.

       - Potentially add an option to do automatic data type detection
       for numeric GET/POST/ENV/COOKIE data, such that fetching the
       cson_value for such a key would return a numeric value object
       as opposed to a string. Or we could add that option in a
       separate function which walks a JSON Object and performs that
       check/transformation on all of its entries. That currently
       can't be done properly with the cson_object_iterator API
       because changes to the object while looping invalidate the
       iterator. This option would also open up problems when clients
       pass huge strings which just happen to look like numbers.


       @see cson_cgi_config_file()
    */
    int cson_cgi_init( cson_cgi_cx * cx, int argc, char const * const * argv, cson_cgi_init_opt * options );

    /**
       Searches for a value from the CGI environment. The fromWhere
       parameter is a NUL-terminated string which specifies which
       environment(s) to check, and may be made up of any of the
       letters [gprecl], case-insensitive. If fromWhere is NULL or its
       first byte is NUL (i.e. it is empty) then the default value
       defined in CSON_CGI_GETENV_DEFAULT is used.

       The environments are searched in the order specified in
       fromWhere. The various letters mean:

       - g = GET: key/value pairs parsed from the QUERY_STRING
       environment variable.

       - p = POST: form-encoded key/value pairs parsed from stdin.

       - r = REQUEST, equivalent to "gpc", a superset of GET/POST/COOKIE.

       - e = ENV, e.g. via getenv(), but see cson_cgi_env_get_val()
       for more details.

       - c = COOKIE: request cookies (not response cookies) parsed
       from the HTTP_COOKIE environment variable.

       - a = APP: an environment namespace reserved for client app use.

       - f = CONFIG FILE.

       - Use key 's' for the SESSION.
       
       Invalid characters are ignored.

       The returned value is owned by the cson_cgi environment and
       must not be destroyed by the caller. NULL is returned if none
       of the requested environments contain the given key.

       Results are undefined if fromWhere is not NULL and is not
       NUL-terminated.

       TODOs:

       - Replace CSON_CGI_GETENV_DEFAULT with a runtime-configurable
       value (via a config file).

    */
    cson_value * cson_cgi_getenv( cson_cgi_cx * cx, char const * fromWhere, char const * key );

    /**
       A convenience form of cson_cgi_getenv() which returns the given
       key as a string. This will return NULL if the requested key
       is-not-a string value. It does not convert non-string values to
       strings.

       On success the string value is returned. Its bytes are owned by
       this API and are valid until the given key is removed/replaced
       from/in the environment object it was found in or that
       environment object is cleaned up.
    */
    char const * cson_cgi_getenv_cstr( cson_cgi_cx * cx, char const * where, char const * key );

    /**
       During initialization, if the PATH_INFO environment variable is set,
       it is split on '/' characters into array. That array is stored in the
       environment with the name PATH_INFO_SPLIT. This function returns the
       element of the PATH_INFO at the given index, or NULL if ndx is out
       of bounds or if no PATH_INFO is available.

       e.g. if PATH_INFO=/a/b/c, passing 0 to this function would return
       "a", passing 2 would return "c", and passing anything greater than 2
       would return NULL.
    */
    char const * cson_cgi_path_part_cstr( cson_cgi_cx * cx, unsigned short ndx );

    /**
       Functionally equivalent to cson_cgi_path_part_cstr(), but
       returns the underlying value as a cson value handle. That handle
       is owned by the underlying PATH_INFO_SPLIT array (which is
       owned by the "e" environment object).

       Unless the client has mucked with the PATH_INFO_SPLIT data, the
       returned value will (if it is not NULL) have a logical type of
       String.
    */
    cson_value * cson_cgi_path_part( cson_cgi_cx * cx, unsigned short ndx );
    
    /**
       Sets or unsets a key in the "user" environment/namespace. If v is NULL
       then the value is removed, otherwise it is set/replaced.

       Returns 0 on success. If key is NULL or has a length of 0 then
       cson_rc.ArgError is returned.

       The user namespace object can be fetched via
       cson_cgi_env_get_val('a',...).

       On success ownership of v is transfered to (or shared with) the
       cson_cgi API. On error ownership of v is not modified. Aside from
    */
    int cson_cgi_setenv( cson_cgi_cx * cx, char const * key, cson_value * v );

    /**
       This function is not implemented, but exists as a convenient
       place to document the cson_cgi config file format.
       
       cson_cgi_init() accepts the name of a configuration file
       (assumed to be in JSON format) to read during
       initialization. The library optionally uses the configuration
       to change certain aspects of its behaviour.

       The following commented JSON demonstrates the configuration
       file options:

       @code
       {
       "formatting": { // NOT YET HONORED. Will mimic cson_output_opt.
           "indentation": 1,
           "addNewline": true,
           "addSpaceAfterColon": true,
           "indentSingleMemberValues": true
       },
       "session": { // Options for session handling
           "manager": "file", // name of session manager impl. Should
                              // have a matching entry in "managers" (below)
           "cookieLifetimeMinutes": 10080, // cookie lifetime in minutes
           "cookieName": "cson_session_id", // cookie name for session ID
           "managers": {
               "file": {
                   "sessionDriver": "file", -- cson_cgi-internal session manager name
                   "dir": "./SESSIONS",
                   "prefix": "cson-session-",
                   "suffix": ".json"
               },
               "mysql5": {
                   "sessionDriver": "cpdo", -- cson_cgi-internal session manager name
                   "dsn": "mysql5:dbname=cpdo;host=localhost",
                   "user": "cpdo",
                   "password": "cpdo",
                   "table": "cson_session",
                   "fieldId": "id",
                   "fieldTimestamp": "last_saved",
                   "fieldSession": "json"
               },
               "sqlite3": {
                   "sessionDriver": "cpdo", -- cson_cgi-internal session manager name
                   "dsn": "sqlite3:sessions.sqlite3",
                   "user": null,
                   "password": null,
                   "table": "cson_session",
                   "fieldId": "id",
                   "fieldTimestamp": "last_saved",
                   "fieldSession": "json"
               }
           }
       }
       }
       @endcode

       TODO: allow initialization to take a JSON object, as opposed to
       a filename, so that we can embed the configuration inside client-side
       config data.
    */
    void cson_cgi_config_file();

    /**
       Sets or (if v is NULL) unsets a cookie value.

       v must either be of one of the types (string, integer, double,
       bool, null, NULL) or must be an object with the following
       structure:

       @code
       {
           value: (string, integer, double, bool, or null),
           OPTIONAL path: string,
           OPTIONAL domain: string,
           OPTIONAL expires: integer (Unix epoch timestamp),
           OPTIONAL secure: bool,
           OPTIONAL httponly: bool
       }
       @endcode

       For the object form, if the "value" property is missing or not of
       the correct type then the cookie will not be emitted in the
       HTTP response headers. The other properties are optional. A value
       of NULL or cson_value_null() will cause the expiry field (if set)
       to be ignored. Note, however, that removal will only work
       on the client side if all other cookie parameters match
       (e.g. domain and path).

       Returns 0 on success, non-0 on error.

       A duplicate cookie replaces any previous cookie with the same
       key.
       
       On success ownership of v is shared with the cson_cgi API (via
       reference counting). On error ownership of v is not modified.
    */
    int cson_cgi_cookie_set( cson_cgi_cx * cx, char const * key, cson_value * v );

    /**
       Sets or (if v is NULL) unsets an HTTP cookie value. key may not
       be NULL nor have a length of 0. v must be one of the types
       (string, integer, double, bool, null, NULL). Any other pointer
       arguments may be NULL, in which case they are not used.
       If v is NULL then the JSON null value is used as a placeholder
       value so that when the HTTP headers are generated, the cookie
       can be unset on the client side.

       This function creates an object with the structure documented
       in cson_cgi_cookie_set() and then passes that object to
       cson_cgi_cookie_set(). Any parameters which have NULL/0 values
       are not emitted in the generated object, with the exception of
       (v==NULL), which causes the expiry property to be ignored and a
       value from a time far in the past to be used (so that the
       client will expire it)..

       Returns 0 on success, non-0 on error.

       On success ownership of v is shared with the cson_cgi API (via
       reference counting). On error ownership of v is not modified.
    */
    int cson_cgi_cookie_set2( cson_cgi_cx * cx, char const * key, cson_value * v,
                              char const * domain, char const * path,
                              unsigned int expires, char secure, char httponly );
    
    /**
        Returns the internal "environment" JSON object corresponding
        to the given 'which' letter, which must be one of
        (case-insensitive):

        - g = GET
        - p = POST
        - c = COOKIE
        - e = ENV (i.e. system environment)
        - s = SESSION
        - a = APP (application-specific)

        TODO: s = SESSION

        See cson_cgi_getenv() for more details about each of those.

        Returns NULL if 'which' is not one of the above.

        Note that in the 'e' (system environment) case, making
        modifications to the returned object will NOT also modify the
        system environment.  Likewise, future updates to the system
        environment will not be automatically reflected in the
        returned object.

        The returned object is owned by the cson_cgi environment and
        must not be destroyed by the caller.

        If createIfNeeded is non-0 (true) then the requested
        environment object is created if it was formerly empty. In that
        case, a return value of NULL can indicate an invalid 'which'
        parameter or an allocation error.       

        To get the Object reference to this environment use
        cson_cgi_env_get_obj() or pass the result of this function
        to cson_value_get_object().
 
        The returned value is owned by the cson_cgi API.

        The public API does not provide a way for clients to modify
        several of the internal environment stores, e.g. HTTP GET
        parameters are set only by this framework. However, clients
        can (if needed) get around this by fetching the associated
        "environment object" via this function or
        cson_cgi_env_get_obj(), and modifying it directly. Clients are
        encouraged to use the other public APIs for dealing with the
        environment, however, and are encouraged to not directly modify
        "special" namespaces like the cookie/GET/POST data.        
    */
    cson_value * cson_cgi_env_get_val( cson_cgi_cx * cx, char which, char createIfNeeded );

    /**
       Equivalent to:

       @code
       cson_value_get_object( cson_cgi_env_get_val( which, createIfNeeded ) );
       @endcode

       Note, however, that it is at least theoretically possible that
       cson_cgi_env_get_val() return non-NULL but this function
       returns NULL. If that happens it means that the value returned
       by cson_cgi_env_get_val() is-not-a Object instance, but is
       something else (maybe an array?).
    */
    cson_object * cson_cgi_env_get_obj( cson_cgi_cx * cx, char which, char createIfNeeded );

    /**
       Adds the given key/value to the list of HTTP headers (replacing
       any existing entry with the same name).  If v is NULL then any
       header with the given key is removed from the pending response.

       Returns 0 on success. On success ownership of v is transfered
       to (or shared with) the internal header list. On error,
       ownership of v is not modified.

       If v is not of one of the types (string, integer, double, bool,
       undef, null) then the header will not be output when when
       cson_cgi_response_output_headers() is called. If it is one of
       those types then its stringified value will be its "natural"
       form (for strings and numbers), the integer 0 or 1 for
       booleans, and the number 0 for null.  Note that a literal
       (v==NULL) is treated differently from a JSON null - it UNSETS
       the given header.

       This function should not be used for setting cookies, as they
       require extra url-encoding and possibly additional
       parameters. Use cson_cgi_cookie_set() and
       cson_cgi_cookie_set2() to set cookie headers.
    */
    int cson_cgi_response_header_add( cson_cgi_cx * cx, char const * key, cson_value * v );

    /**
       Returns a cson array value containing the arguments passed
       to cson_cgi_init(). The returned value is owned by the cson_cgi
       API and must not be destroyed by the caller.

       Only returns NULL if initialization of cson_cgi_init() fails
       early on, and is almost certainly indicative of an allocation
       error. If cson_cgi_init() is given a 0-length argument list
       then this function will return an empty array (except in the
       NULL case mentioned above).
    */
    cson_value * cson_cgi_argv(cson_cgi_cx * cx);
    
    /**
       Equivalent to:

       @code
       cson_value_get_array( cson_cgi_argv() );
       @endcode
    */
    cson_array * cson_cgi_argv_array(cson_cgi_cx * cx);

    /**
       Flushes all response headers set via cson_cgi_response_header_add()
       to stdout. The client must output an empty line before the body
       part (if any), and may output custom headers before doing so.

       Do not call this more than once.
    */
    int cson_cgi_response_output_headers(cson_cgi_cx * cx);

    /**
       Outputs the response root object to stdout. If none has been
       set, non-0 is returned.

       Returns 0 on success. On error, partial output might be
       generated.

       Do not call this more than once.
    */
    int cson_cgi_response_output_root(cson_cgi_cx * cx);

    /**
       Outputs the whole response, including headers and the root JSON
       value.

       Returns 0 on success. Fails without side effects if
       no root is set.

       Do not call this more than once.
    */
    int cson_cgi_response_output_all(cson_cgi_cx * cx);
    
    /**
       Don't use this - i need to re-think the JSONP bits.
    
       Returns non-0 (true) if the GET/POST environment contains a key
       named CSON_CGI_KEY_JSONP. If this is the case, then when
       cson_cgi_response_output_headers() is called the Content-type
       is set to "application/javascript".

       If cson_cgi_enable_jsonp() is ever called to set this option
       explicitly, this function does not guess, but uses that value
       instead.

       When JSONP is desired, the generated page output must be
       wrapped in the appropriate JS code.
    */
    char cson_cgi_is_jsonp(cson_cgi_cx * cx);

    /**
       Don't use this - i need to re-think the JSONP bits.

       Sets or unsets JSONP mode. If b is 0 then JSONP guessing is
       explicitly disabled and output is assumed to be JSON. If it is
       non-0 then cson_cgi_guess_content_type() will never return
       "application/javascript".

       When JSONP is desired, the generated page output must be
       wrapped in the appropriate JS code.
    */
    void cson_cgi_enable_jsonp( cson_cgi_cx * cx, char b );

    /**
       Tries to guess the "best" Content-type header value for
       the current session, based on several factors:

       - If the GET/POST data contains a variable named
       CSON_CGI_KEY_JSONP then "application/javascript" is returned.

       - If the HTTP_ACCEPT environment variable is NOT set or
       contains "application/json" then "application/json [possibly
       charset info]" is returned.

       - If the HTTP_ACCEPT environment variable is set but does not
       contain "application/json" then "text/javascript" is returned.

       - If cson_cgi_enable_jsonp() is called and passed a true value,
       "application/javascript" is returned.

       
       If HTTP_ACCEPT_CHARSET is NOT set or contains "utf-8" then
       ";charset=utf-8" is included in the the returned string.
       
       The returned string is static and immutable and suitable for
       use as a Content-type header value. The string is guaranteed to
       be valid until the application exits. Multiple calls to this
       function, with different underlying environment data, can cause
       different results to be returned.

       Returns NULL if it absolutely cannot figure out what to do, but
       currently it has no such logic paths.
    */
    char const * cson_cgi_guess_content_type(cson_cgi_cx * cx);

    /**
       Sets the response content root, replacing any
       existing one (and possibly cleaning it up).

       Returns 0 on success. On success, ownership of v
       is transfered to (or shared with) the cson_cgi
       API. It will be cleaned up at app shutdown time
       or if it is subsequently replaced and has no
       other open references to it.

       On error ownership of v is not modified and any previous
       root is not removed.

       If v is-not-a Object or Array, nor NULL, then cson_rc.TypeError
       is returned. JSON requires either an object or array for the
       root node. Passing NULL will possibly free up any current root
       (depending on its reference count).
    */
    int cson_cgi_response_root_set( cson_cgi_cx * cx, cson_value * v );

    /**
       Fetches the current content root JSON value, as set via
       cson_cgi_response_root_set() or (if no root has been set), as
       defined by createMode, as described below.

       If a content root has been set (or previously initialized)
       then the value of createMode is ignored. If no root has been
       set then this function might try to create one, as described
       here:

       (createMode==0) means not to create a root element if none
       exists already.

       (createMode<0) means to create the root as an Array value.

       (createMode>0) means to create the root as an Object value.

       Returns NULL on allocation error or if no root has been set and
       (createMode==0). On success the returned value is guaranteed to
       be either an Array or Object (see cson_value_get_object() and
       cson_value_get_array()) and it is owned by the cson_cgi API (so
       it must not be destroyed by the caller). If the client needs to
       destroy it, pass NULL to cson_cgi_response_root_set().
    */
    cson_value * cson_cgi_response_root_get( cson_cgi_cx * cx, char createMode );
    
    /**
       Returns the current session ID. If session management is not
       enabled then NULL is returned.

       The returned bytes are owned by the cson_cgi API and are valid
       until the library is cleaned up (via cson_cgi_cleanup_lib() or
       via the normal shutdown process) or the session ID is
       re-generated for some reason. It is best not to hold a
       reference to this, but to copy it if it will be needed later.

       If the return value is not NULL, it is guaranteed to be
       NUL-terminated.
    */
    char const * cson_cgi_session_id(cson_cgi_cx *);

    /**
       Writes a 36-byte (plus one NUL byte) random UUID value to
       dest. dest must be at least 37 bytes long. If dest is NULL this
       function has no side effects.

       This function uses internal RNG state and is not thread-safe.
    */
    void cson_cgi_generate_uuid( cson_cgi_cx * cx, char * dest );

    /**
       Adds v to the API-internal cleanup mechanism. key must be a
       unique key for the given element. Adding another item with that
       key may free the previous one. If freeOnError is true then v is
       passed to cson_value_free() if the key cannot be inserted,
       otherweise ownership of v is not changed on error.

       Returns 0 on success.

       On success, ownership of v is transfered to (or shared with)
       cx, and v will be valid until cx is cleaned up or its key is
       replaced via another call to this function.
    */
    int cson_cgi_gc_add( cson_cgi_cx * cx, char const * key, cson_value * v, char freeOnError );
    
    /* LICENSE

This software's source code, including accompanying documentation and
demonstration applications, are licensed under the following
conditions...

Certain files are imported from external projects and have their own
licensing terms. Namely, the JSON_parser.* files. See their files for
their official licenses, but the summary is "do what you want [with
them] but leave the license text and copyright in place."

The author (Stephan G. Beal [http://wanderinghorse.net/home/stephan/])
explicitly disclaims copyright in all jurisdictions which recognize
such a disclaimer. In such jurisdictions, this software is released
into the Public Domain.

In jurisdictions which do not recognize Public Domain property
(e.g. Germany as of 2011), this software is Copyright (c) 2011 by
Stephan G. Beal, and is released under the terms of the MIT License
(see below).

In jurisdictions which recognize Public Domain property, the user of
this software may choose to accept it either as 1) Public Domain, 2)
under the conditions of the MIT License (see below), or 3) under the
terms of dual Public Domain/MIT License conditions described here, as
they choose.

The MIT License is about as close to Public Domain as a license can
get, and is described in clear, concise terms at:

    http://en.wikipedia.org/wiki/MIT_License

The full text of the MIT License follows:

--
Copyright (c) 2011 Stephan G. Beal (http://wanderinghorse.net/home/stephan/)

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

--END OF MIT LICENSE--

For purposes of the above license, the term "Software" includes
documentation and demonstration source code which accompanies
this software. ("Accompanies" = is contained in the Software's
primary public source code repository.)

*/

#if defined(__cplusplus)
} /*extern "C"*/
#endif

#endif /* WANDERINGHORSE_NET_CSON_CGI_H_INCLUDED */
/* end file include/wh/cson/cson_cgi.h */







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







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
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
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
....
2192
2193
2194
2195
2196
2197
2198













































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































#if CSON_ENABLE_SQLITE3 /* we do this here for the sake of the amalgamation build */
#include <sqlite3.h>

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

/**
   Converts a single value from a single 0-based column index to its JSON
   equivalent.

   On success it returns a new JSON value, which will have a different concrete
   type depending on the field type reported by sqlite3_column_type(st,col):

   Integer, double, null, or string (TEXT and BLOB data, though not
   all blob data is legal for a JSON string).

   st must be a sqlite3_step()'d row and col must be a 0-based column
   index within that result row.
 */       
cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col );

/**
   Creates a JSON Array object containing the names of all columns
   of the given prepared statement handle. 
    
   Returns a new array value on success, which the caller owns. Its elements
   are in the same order as in the underlying query.

   On error NULL is returned.
    
   st is not traversed or freed by this function - only the column
   count and names are read.
*/
cson_value * cson_sqlite3_column_names( sqlite3_stmt * st );

/**
   Creates a JSON Object containing key/value pairs corresponding
   to the result columns in the current row of the given statement
   handle. st must be a sqlite3_step()'d row result.

   On success a new Object is returned which is owned by the
   caller. On error NULL is returned.

   cson_sqlite3_column_to_value() is used to convert each column to a
   JSON value, and the column names are taken from
   sqlite3_column_name().
*/
cson_value * cson_sqlite3_row_to_object( sqlite3_stmt * st );

/**
   Similar to cson_sqlite3_row_to_object(), but creates an Array
   value which contains the JSON-form values of the given result
   set row.
*/
cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st );
/**
    Converts the results of an sqlite3 SELECT statement to JSON,
    in the form of a cson_value object tree.
    
    st must be a prepared, but not yet traversed, SELECT query.
    tgt must be a pointer to NULL (see the example below). If
    either of those arguments are NULL, cson_rc.ArgError is returned.
................................................................................
#if defined(__cplusplus)
} /*extern "C"*/
#endif
    
#endif /* CSON_ENABLE_SQLITE3 */
#endif /* WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED */
/* end file include/wh/cson/cson_sqlite3.h */













































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Changes to src/json.c.

61
62
63
64
65
66
67









68
69
70
71
72
73
74
..
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
...
113
114
115
116
117
118
119

120
121
122
123
124
125
126
...
187
188
189
190
191
192
193
194
195
196






197
198
199
200
201
202
203
204
205
206
207
208
209
...
250
251
252
253
254
255
256
257
258
259
260

261
262
263
264
265
266
267
...
323
324
325
326
327
328
329
330
331
332


333
334
335
336
337
338
339
...
383
384
385
386
387
388
389





390
391
392
393







394
395
396
397
398
399
400
...
495
496
497
498
499
500
501
502

503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
...
522
523
524
525
526
527
528
529
530



531
532
533
534
535
536
537
...
931
932
933
934
935
936
937
938



939
940
941
942
943
944
945
....
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
....
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
** the payload.
**
** It is imperitive that NO callback functions EVER output ANYTHING to
** stdout, as that will effectively corrupt any HTTP output.
*/
typedef cson_value * (*fossil_json_f)();











/*
** Holds keys used for various JSON API properties.
*/
static const struct FossilJsonKeys_{
  char const * authToken;
  char const * commandPath;
................................................................................
    C(INVALID_REQUEST,"Invalid request");
    C(UNKNOWN_COMMAND,"Unknown Command");
    C(UNKNOWN,"Unknown error");
    C(RESOURCE_NOT_FOUND,"Resource not found");
    C(TIMEOUT,"Timeout reached");
    C(ASSERT,"Assertion failed");
    C(ALLOC,"Resource allocation failed");
    C(NYI,"Not yet implemented.");
    C(AUTH,"Authentication error");
    C(LOGIN_FAILED,"Login failed");
    C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed.");
    C(LOGIN_FAILED_NONAME,"Login failed - name not supplied");
    C(LOGIN_FAILED_NOPW,"Login failed - password not supplied");
    C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found");
    C(MISSING_AUTH,"Authentication info missing from request");
    C(DENIED,"Access denied");
    C(WRONG_MODE,"Request not allowed (wrong operation mode)");

................................................................................
    C(MISSING_ARGS,"Missing arguments");

    C(DB,"Database error");
    C(STMT_PREP,"Statement preparation failed");
    C(STMT_BIND,"Statement parameter binding failed");
    C(STMT_EXEC,"Statement execution/stepping failed");
    C(DB_LOCKED,"Database is locked");

#undef C
    default:
      return "Unknown Error";
  }
}
/*
** Implements the cson_data_dest_f() interface and outputs the data to
................................................................................
  sprintf(buf+7,"%04d", code);
  return buf;
}

/*
** Adds v to the API-internal cleanup mechanism. key must be a unique
** key for the given element. Adding another item with that key may
** free the previous one. If freeOnError is true then v is passed to
** cson_value_free() if the key cannot be inserted, otherweise
** ownership of v is not changed on error.






**
** Returns 0 on success.
**
*** On success, ownership of v is transfered to (or shared with)
*** g.json.gc, and v will be valid until that object is cleaned up or
*** its key is replaced via another call to this function.
*/
int json_gc_add( char const * key, cson_value * v, char freeOnError ){
  int const rc = cson_object_set( g.json.gc.o, key, v );
  assert( NULL != g.json.gc.o );
  if( (0 != rc) && freeOnError ){
    cson_value_free( v );
  }
................................................................................
    }
  }
  return NULL;
}


/*
** Returns the string form of a json_getenv() value, but ONLY
** If that value is-a String. Non-strings are not converted
** to strings for this purpose. Returned memory is owned by
** g.json or fossil..

*/
static char const * json_getenv_cstr( char const * zKey ){
  return cson_value_get_cstr( json_getenv(zKey) );
}


/*
................................................................................
}

/*
** Returns the current request's JSON authentication token, or NULL if
** none is found. The token's memory is owned by (or shared with)
** g.json.
**
** If an auth token is found in the GET/POST JSON request data then
** fossil is given that data for use in authentication for this
** session.


**
** Must be called once before login_check_credentials() is called or
** we will not be able to replace fossil's internal idea of the auth
** info in time (and future changes to that state may cause unexpected
** results).
**
** The result of this call are cached for future calls.
................................................................................
** json_cmd_top() (early on in those functions).
**
** Initializes g.json.gc and g.json.param.
*/
void json_main_bootstrap(){
  cson_value * v;
  assert( (NULL == g.json.gc.v) && "cgi_json_bootstrap() was called twice!" );





  v = cson_value_new_object();
  g.json.gc.v = v;
  g.json.gc.o = cson_value_get_object(v);








  v = cson_value_new_object();
  g.json.param.v = v;
  g.json.param.o = cson_value_get_object(v);
  json_gc_add("$PARAMS", v, 1);
}


................................................................................
      part = cson_value_new_string(arg,strlen(arg));
      cson_array_append(g.json.cmd.a, part);
    }
  }
  
  /* g.json.reqPayload exists only to simplify some of our access to
     the request payload. We currently only use this in the context of
     Object payloads, not Arrays, strings, etc. */

  g.json.reqPayload.v = cson_object_get( g.json.post.o, "payload" );
  if( g.json.reqPayload.v ){
    g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
        /* g.json.reqPayload.o may legally be NULL, which means only that
           g.json.reqPayload.v is-not-a Object.
        */;
  }

  do{/* set up JSON out formatting options. */
    unsigned char indent = g.isCGI ? 0 : 1;
    cson_value const * indentV = json_getenv("indent");
    if(indentV){
      if(cson_value_is_string(indentV)){
        int const n = atoi(cson_string_cstr(cson_value_get_string(indentV)));
        indent = (n>0)
          ? (unsigned char)n
................................................................................
        indent = (n>0) ? (unsigned char)n : 0;
      }
    }
    g.json.outOpt.indentation = indent;
    g.json.outOpt.addNewline = g.isCGI ? 0 : 1;
  }while(0);

  json_auth_token()/* will copy our auth token, if any, to fossil's core. */;
  if( g.isCGI ){



    login_check_credentials()/* populates g.perm */;
  }
  else{
    db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
  }
}

................................................................................
}

/*
** Implementation of the /json/login page.
**
*/
cson_value * json_page_login(void){
  static char preciseErrors =



#if 0
    g.json.errorDetailParanoia ? 0 : 1
#else
    0
#endif
    ;
  /*
................................................................................
  SETBUF(jo2, "encoding");
  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA %s.journal_mode", zDb));
  cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
  return jv;
#undef SETBUF
}




/*











** Implements the /json/wiki family of pages/commands. Far from
** complete.
**
*/
cson_value * json_page_wiki(void){
  cson_value * jlist = NULL;
  cson_value * rows = NULL;
  Stmt q;
  wiki_prepare_page_list(&q);
  cson_sqlite3_stmt_to_json( q.pStmt, &jlist, 1 );
  db_finalize(&q);
  assert( NULL != jlist );
  rows = cson_object_take( cson_value_get_object(jlist), "rows" );
  assert( NULL != rows );
  cson_value_free( jlist );




  return rows;
}










/*
** Placeholder /json/XXX page impl for NYI (Not Yet Implemented)
** (but planned) pages/commands.

*/
static cson_value * json_page_nyi(void){























  g.json.resultCode = FSL_JSON_E_NYI;



  return NULL;
}


/*
** Mapping of names to JSON pages/commands.  Each name is a subpath of
** /json (in CGI mode) or a subcommand of the json command in CLI mode
*/
static const JsonPageDef JsonPageDefs[] = {
/* please keep alphabetically sorted (case-insensitive) for maintenance reasons. */
{"anonymousPassword",json_page_anon_password, 1},
{"branch", json_page_nyi,0},
{"cap", json_page_cap, 0},

{"HAI",json_page_version,0},
{"login",json_page_login,1},
{"logout",json_page_logout,1},
{"stat",json_page_stat,0},
{"tag", json_page_nyi,0},
{"ticket", json_page_nyi,0},
{"timeline", json_page_nyi,0},
................................................................................
{"user", json_page_nyi,0},
{"version",json_page_version,0},
{"wiki",json_page_wiki,0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*
** Mapping of /json/wiki/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Wiki[] = {
{"get", json_page_nyi, 0},
{"list", json_page_nyi, 0},
{"save", json_page_nyi, 1},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*
** Mapping of /json/ticket/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Ticket[] = {
{"get", json_page_nyi, 0},
{"list", json_page_nyi, 0},
................................................................................
  if( ! pageDef ){
    json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 1 );
    return;
  }else if( pageDef->runMode > 0 /*HTTP only*/){
    rc = FSL_JSON_E_WRONG_MODE;
  }else{
    rc = 0;
    payload = (pageDef->func)();
  }
  if( g.json.resultCode ){
    json_err(g.json.resultCode, NULL, 1);
  }else{
    payload = json_create_response(rc, payload, NULL);
    cson_output_FILE( payload, stdout, &g.json.outOpt );
    cson_value_free( payload );







>
>
>
>
>
>
>
>
>







 







|


|







 







>







 







|
|
|
>
>
>
>
>
>



|
|
|







 







|
|
|
|
>







 







|
|
|
>
>







 







>
>
>
>
>




>
>
>
>
>
>
>







 







|
>








|







 







<

>
>
>







 







|
>
>
>







 







>
>
>

>
>
>
>
>
>
>
>
>
>
>




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

<
<
>

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

<










>







 







<
<
<
<
<
<
<
<
<
<







 







|







61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
...
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
...
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
...
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
...
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
...
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
...
554
555
556
557
558
559
560

561
562
563
564
565
566
567
568
569
570
571
...
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
....
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
....
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
** the payload.
**
** It is imperitive that NO callback functions EVER output ANYTHING to
** stdout, as that will effectively corrupt any HTTP output.
*/
typedef cson_value * (*fossil_json_f)();


/*
** Placeholder /json/XXX page impl for NYI (Not Yet Implemented)
** (but planned) pages/commands.
*/
static cson_value * json_page_nyi(void){
  g.json.resultCode = FSL_JSON_E_NYI;
  return NULL;
}

/*
** Holds keys used for various JSON API properties.
*/
static const struct FossilJsonKeys_{
  char const * authToken;
  char const * commandPath;
................................................................................
    C(INVALID_REQUEST,"Invalid request");
    C(UNKNOWN_COMMAND,"Unknown Command");
    C(UNKNOWN,"Unknown error");
    C(RESOURCE_NOT_FOUND,"Resource not found");
    C(TIMEOUT,"Timeout reached");
    C(ASSERT,"Assertion failed");
    C(ALLOC,"Resource allocation failed");
    C(NYI,"Not yet implemented");
    C(AUTH,"Authentication error");
    C(LOGIN_FAILED,"Login failed");
    C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed");
    C(LOGIN_FAILED_NONAME,"Login failed - name not supplied");
    C(LOGIN_FAILED_NOPW,"Login failed - password not supplied");
    C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found");
    C(MISSING_AUTH,"Authentication info missing from request");
    C(DENIED,"Access denied");
    C(WRONG_MODE,"Request not allowed (wrong operation mode)");

................................................................................
    C(MISSING_ARGS,"Missing arguments");

    C(DB,"Database error");
    C(STMT_PREP,"Statement preparation failed");
    C(STMT_BIND,"Statement parameter binding failed");
    C(STMT_EXEC,"Statement execution/stepping failed");
    C(DB_LOCKED,"Database is locked");
    C(DB_NEEDS_REBUILD,"Fossil repository needs to be rebuilt");
#undef C
    default:
      return "Unknown Error";
  }
}
/*
** Implements the cson_data_dest_f() interface and outputs the data to
................................................................................
  sprintf(buf+7,"%04d", code);
  return buf;
}

/*
** Adds v to the API-internal cleanup mechanism. key must be a unique
** key for the given element. Adding another item with that key may
** free the previous one (depending on its reference count). If
** freeOnError is true then v is passed to cson_value_free() if the
** key cannot be inserted, otherweise ownership of v is not changed on
** error. Failure to insert a key may be caused by any of the
** following:
**
** - Allocation error.
** - g.json.gc.o is NULL
** - key is NULL or empty.
**
** Returns 0 on success.
**
** On success, ownership of v is transfered to (or shared with)
** g.json.gc, and v will be valid until that object is cleaned up or
** its key is replaced via another call to this function.
*/
int json_gc_add( char const * key, cson_value * v, char freeOnError ){
  int const rc = cson_object_set( g.json.gc.o, key, v );
  assert( NULL != g.json.gc.o );
  if( (0 != rc) && freeOnError ){
    cson_value_free( v );
  }
................................................................................
    }
  }
  return NULL;
}


/*
** Returns the string form of a json_getenv() value, but ONLY If that
** value is-a String. Non-strings are not converted to strings for
** this purpose. Returned memory is owned by g.json or fossil and is
** valid until end-of-app or the given key is replaced in fossil's
** internals via cgi_replace_parameter() and friends or json_setenv().
*/
static char const * json_getenv_cstr( char const * zKey ){
  return cson_value_get_cstr( json_getenv(zKey) );
}


/*
................................................................................
}

/*
** Returns the current request's JSON authentication token, or NULL if
** none is found. The token's memory is owned by (or shared with)
** g.json.
**
** If an auth token is found in the GET/POST request data then fossil
** is given that data for use in authentication for this
** session. i.e. the GET/POST data overrides fossil's authentication
** cookie value (if any) and also works with clients which do not
** support cookies.
**
** Must be called once before login_check_credentials() is called or
** we will not be able to replace fossil's internal idea of the auth
** info in time (and future changes to that state may cause unexpected
** results).
**
** The result of this call are cached for future calls.
................................................................................
** json_cmd_top() (early on in those functions).
**
** Initializes g.json.gc and g.json.param.
*/
void json_main_bootstrap(){
  cson_value * v;
  assert( (NULL == g.json.gc.v) && "cgi_json_bootstrap() was called twice!" );

  /* g.json.gc is our "garbage collector" - where we put JSON values
     which need a long lifetime but don't have a logical parent to put
     them in.
  */
  v = cson_value_new_object();
  g.json.gc.v = v;
  g.json.gc.o = cson_value_get_object(v);

  /*
    g.json.param holds the JSONized counterpart of fossil's
    cgi_parameter_xxx() family of data. We store them as JSON, as
    opposed to using fossil's data directly, because we can retain
    full type information for data this way (as opposed to it always
    being of type string).
  */
  v = cson_value_new_object();
  g.json.param.v = v;
  g.json.param.o = cson_value_get_object(v);
  json_gc_add("$PARAMS", v, 1);
}


................................................................................
      part = cson_value_new_string(arg,strlen(arg));
      cson_array_append(g.json.cmd.a, part);
    }
  }
  
  /* g.json.reqPayload exists only to simplify some of our access to
     the request payload. We currently only use this in the context of
     Object payloads, not Arrays, strings, etc.
  */
  g.json.reqPayload.v = cson_object_get( g.json.post.o, "payload" );
  if( g.json.reqPayload.v ){
    g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
        /* g.json.reqPayload.o may legally be NULL, which means only that
           g.json.reqPayload.v is-not-a Object.
        */;
  }

  do{/* set up JSON output formatting options. */
    unsigned char indent = g.isCGI ? 0 : 1;
    cson_value const * indentV = json_getenv("indent");
    if(indentV){
      if(cson_value_is_string(indentV)){
        int const n = atoi(cson_string_cstr(cson_value_get_string(indentV)));
        indent = (n>0)
          ? (unsigned char)n
................................................................................
        indent = (n>0) ? (unsigned char)n : 0;
      }
    }
    g.json.outOpt.indentation = indent;
    g.json.outOpt.addNewline = g.isCGI ? 0 : 1;
  }while(0);


  if( g.isCGI ){
    json_auth_token()/* will copy our auth token, if any, to fossil's
                        core, which we need before we call
                        login_check_credentials(). */;
    login_check_credentials()/* populates g.perm */;
  }
  else{
    db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
  }
}

................................................................................
}

/*
** Implementation of the /json/login page.
**
*/
cson_value * json_page_login(void){
  static char preciseErrors = /* if true, "complete" JSON error codes are used,
                                 else they are "dumbed down" to a generic login
                                 error code.
                              */
#if 0
    g.json.errorDetailParanoia ? 0 : 1
#else
    0
#endif
    ;
  /*
................................................................................
  SETBUF(jo2, "encoding");
  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA %s.journal_mode", zDb));
  cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
  return jv;
#undef SETBUF
}


static cson_value * json_wiki_list(void);

/*
** Mapping of /json/wiki/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Wiki[] = {
{"get", json_page_nyi, 0},
{"list", json_wiki_list, 0},
{"save", json_page_nyi, 1},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*
** Implements the /json/wiki family of pages/commands. Far from
** complete.
**
*/
static cson_value * json_page_wiki(void){










  JsonPageDef const * def;
  char const * cmd = json_command_arg(2);
  if( ! cmd ){
    g.json.resultCode = FSL_JSON_E_MISSING_AUTH;
    return NULL;
  }
  def = json_handler_for_name( cmd, &JsonPageDefs_Wiki[0] );
  if(!def){
    g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND;
    return NULL;
  }
  else{
    return (*def->func)();
  }
}

/*


** INTERIM implementation of /json/wiki/list
*/
static cson_value * json_wiki_list(void){
  cson_value * listV = NULL;
  cson_array * list = NULL;
  Stmt q;
  db_prepare(&q,"SELECT"
             " substr(tagname,6) as name"
             " FROM tag WHERE tagname GLOB 'wiki-*'"
             " ORDER BY lower(name)");
  listV = cson_value_new_array();
  list = cson_value_get_array(listV);
  while( SQLITE_ROW == db_step(&q) ){
    cson_value * v = cson_sqlite3_column_to_value(q.pStmt,0);
    if(!v){
      cson_value_free(listV);
      goto error;
    }
    if( 0 != cson_array_append( list, v ) ){
      cson_value_free(v);
      goto error;
    }
  }
  db_finalize(&q);
  goto end;
  error:
  g.json.resultCode = FSL_JSON_E_UNKNOWN;
  cson_value_free(listV);
  listV = NULL;
  end:
  return listV;
}


/*
** Mapping of names to JSON pages/commands.  Each name is a subpath of
** /json (in CGI mode) or a subcommand of the json command in CLI mode
*/
static const JsonPageDef JsonPageDefs[] = {
/* please keep alphabetically sorted (case-insensitive) for maintenance reasons. */
{"anonymousPassword",json_page_anon_password, 1},
{"branch", json_page_nyi,0},
{"cap", json_page_cap, 0},
{"dir", json_page_nyi, 0},
{"HAI",json_page_version,0},
{"login",json_page_login,1},
{"logout",json_page_logout,1},
{"stat",json_page_stat,0},
{"tag", json_page_nyi,0},
{"ticket", json_page_nyi,0},
{"timeline", json_page_nyi,0},
................................................................................
{"user", json_page_nyi,0},
{"version",json_page_version,0},
{"wiki",json_page_wiki,0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};












/*
** Mapping of /json/ticket/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Ticket[] = {
{"get", json_page_nyi, 0},
{"list", json_page_nyi, 0},
................................................................................
  if( ! pageDef ){
    json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 1 );
    return;
  }else if( pageDef->runMode > 0 /*HTTP only*/){
    rc = FSL_JSON_E_WRONG_MODE;
  }else{
    rc = 0;
    payload = (*pageDef->func)();
  }
  if( g.json.resultCode ){
    json_err(g.json.resultCode, NULL, 1);
  }else{
    payload = json_create_response(rc, payload, NULL);
    cson_output_FILE( payload, stdout, &g.json.outOpt );
    cson_value_free( payload );