Tcl Library Source Code

Changes On Branch ldap-60160205fe-tls
Login

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

Changes In Branch ldap-60160205fe-tls Excluding Merge-Ins

This is equivalent to a diff from 7538200f88 to 2d8a920b69

2018-12-08
15:53
Re-integrating kbk's disjoint-set work check-in: c261a6a0ea user: hypnotoad tags: trunk
2018-09-24
18:37
ldap/ldapx <EF,T> Tkt [60160205fe] completed & merged check-in: 5e2cff31b4 user: andreask tags: pooryorick
18:36
ldap/ldapx <T> Tkt [60160205fe] Added test Closed-Leaf check-in: 2d8a920b69 user: andreask tags: ldap-60160205fe-tls
18:13
Update the ldap branch to latest. check-in: db19a51e4c user: andreask tags: ldap-60160205fe-tls
2018-08-14
19:16
Fix issue [a16b1095974e071d], error in mime.tcl check-in: 074ec6a961 user: pooryorick tags: pooryorick
2018-07-22
11:38
Add a package for trigonometric functions that use angles in degrees and additional trigonometric and hyperbolic functions, including their inverses. check-in: 7538200f88 user: arjenmarkus tags: trunk
2018-07-19
19:31
Change the criterium for detecting if two circles touch - use the mean of the two radii check-in: 6124e077a8 user: arjenmarkus tags: trunk

Changes to embedded/www/index.html.

2341
2342
2343
2344
2345
2346
2347
2348

2349
2350
2351
2352
2353
2354
2355
2341
2342
2343
2344
2345
2346
2347

2348
2349
2350
2351
2352
2353
2354
2355







-
+







<td class="#doctools_idxleft" width="35%"><a name="matching"> matching </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/grammar_me/me_intro.html"> grammar::me_intro </a> &#183; <a href="tcllib/files/modules/grammar_peg/peg_interp.html"> grammar::peg::interp </a> &#183; <a href="tcllib/files/apps/pt.html"> pt </a> &#183; <a href="tcllib/files/modules/pt/pt_astree.html"> pt::ast </a> &#183; <a href="tcllib/files/modules/pt/pt_cparam_config_critcl.html"> pt::cparam::configuration::critcl </a> &#183; <a href="tcllib/files/modules/pt/pt_cparam_config_tea.html"> pt::cparam::configuration::tea </a> &#183; <a href="tcllib/files/modules/pt/pt_json_language.html"> pt::json_language </a> &#183; <a href="tcllib/files/modules/pt/pt_param.html"> pt::param </a> &#183; <a href="tcllib/files/modules/pt/pt_pexpression.html"> pt::pe </a> &#183; <a href="tcllib/files/modules/pt/pt_pexpr_op.html"> pt::pe::op </a> &#183; <a href="tcllib/files/modules/pt/pt_pegrammar.html"> pt::peg </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_container.html"> pt::peg::container </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_container_peg.html"> pt::peg::container::peg </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_export.html"> pt::peg::export </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_export_container.html"> pt::peg::export::container </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_export_json.html"> pt::peg::export::json </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_export_peg.html"> pt::peg::export::peg </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_from_container.html"> pt::peg::from::container </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_from_json.html"> pt::peg::from::json </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_from_peg.html"> pt::peg::from::peg </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_import.html"> pt::peg::import </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_import_container.html"> pt::peg::import::container </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_import_json.html"> pt::peg::import::json </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_import_peg.html"> pt::peg::import::peg </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_interp.html"> pt::peg::interp </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_to_container.html"> pt::peg::to::container </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_to_cparam.html"> pt::peg::to::cparam </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_to_json.html"> pt::peg::to::json </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_to_param.html"> pt::peg::to::param </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_to_peg.html"> pt::peg::to::peg </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_to_tclparam.html"> pt::peg::to::tclparam </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_language.html"> pt::peg_language </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_introduction.html"> pt::pegrammar </a> &#183; <a href="tcllib/files/modules/pt/pt_pgen.html"> pt::pgen </a> &#183; <a href="tcllib/files/modules/pt/pt_rdengine.html"> pt::rde </a> &#183; <a href="tcllib/files/modules/pt/pt_tclparam_config_nx.html"> pt::tclparam::configuration::nx </a> &#183; <a href="tcllib/files/modules/pt/pt_tclparam_config_snit.html"> pt::tclparam::configuration::snit </a> &#183; <a href="tcllib/files/modules/pt/pt_tclparam_config_tcloo.html"> pt::tclparam::configuration::tcloo </a> &#183; <a href="tcllib/files/modules/pt/pt_util.html"> pt::util </a> &#183; <a href="tcllib/files/modules/pt/pt_to_api.html"> pt_export_api </a> &#183; <a href="tcllib/files/modules/pt/pt_from_api.html"> pt_import_api </a> &#183; <a href="tcllib/files/modules/pt/pt_introduction.html"> pt_introduction </a> &#183; <a href="tcllib/files/modules/pt/pt_parse_peg.html"> pt_parse_peg </a> &#183; <a href="tcllib/files/modules/pt/pt_parser_api.html"> pt_parser_api </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_op.html"> pt_peg_op </a> &#183; <a href="tcllib/files/modules/struct/graphops.html"> struct::graph::op </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="math"> math </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/math/math.html"> math </a> &#183; <a href="tcllib/files/modules/math/bigfloat.html"> math::bigfloat </a> &#183; <a href="tcllib/files/modules/math/bignum.html"> math::bignum </a> &#183; <a href="tcllib/files/modules/math/calculus.html"> math::calculus </a> &#183; <a href="tcllib/files/modules/math/qcomplex.html"> math::complexnumbers </a> &#183; <a href="tcllib/files/modules/math/constants.html"> math::constants </a> &#183; <a href="tcllib/files/modules/math/decimal.html"> math::decimal </a> &#183; <a href="tcllib/files/modules/math/fuzzy.html"> math::fuzzy </a> &#183; <a href="tcllib/files/modules/math/math_geometry.html"> math::geometry </a> &#183; <a href="tcllib/files/modules/math/interpolate.html"> math::interpolate </a> &#183; <a href="tcllib/files/modules/math/linalg.html"> math::linearalgebra </a> &#183; <a href="tcllib/files/modules/math/optimize.html"> math::optimize </a> &#183; <a href="tcllib/files/modules/math/pca.html"> math::PCA </a> &#183; <a href="tcllib/files/modules/math/polynomials.html"> math::polynomials </a> &#183; <a href="tcllib/files/modules/math/rational_funcs.html"> math::rationalfunctions </a> &#183; <a href="tcllib/files/modules/math/special.html"> math::special </a> &#183; <a href="tcllib/files/modules/simulation/annealing.html"> simulation::annealing </a> &#183; <a href="tcllib/files/modules/simulation/montecarlo.html"> simulation::montecarlo </a> &#183; <a href="tcllib/files/modules/simulation/simulation_random.html"> simulation::random </a>
<a href="tcllib/files/modules/math/math.html"> math </a> &#183; <a href="tcllib/files/modules/math/bigfloat.html"> math::bigfloat </a> &#183; <a href="tcllib/files/modules/math/bignum.html"> math::bignum </a> &#183; <a href="tcllib/files/modules/math/calculus.html"> math::calculus </a> &#183; <a href="tcllib/files/modules/math/qcomplex.html"> math::complexnumbers </a> &#183; <a href="tcllib/files/modules/math/constants.html"> math::constants </a> &#183; <a href="tcllib/files/modules/math/decimal.html"> math::decimal </a> &#183; <a href="tcllib/files/modules/math/fuzzy.html"> math::fuzzy </a> &#183; <a href="tcllib/files/modules/math/math_geometry.html"> math::geometry </a> &#183; <a href="tcllib/files/modules/math/interpolate.html"> math::interpolate </a> &#183; <a href="tcllib/files/modules/math/linalg.html"> math::linearalgebra </a> &#183; <a href="tcllib/files/modules/math/optimize.html"> math::optimize </a> &#183; <a href="tcllib/files/modules/math/pca.html"> math::PCA </a> &#183; <a href="tcllib/files/modules/math/polynomials.html"> math::polynomials </a> &#183; <a href="tcllib/files/modules/math/rational_funcs.html"> math::rationalfunctions </a> &#183; <a href="tcllib/files/modules/math/special.html"> math::special </a> &#183; <a href="tcllib/files/modules/math/trig.html"> math::trig </a> &#183; <a href="tcllib/files/modules/simulation/annealing.html"> simulation::annealing </a> &#183; <a href="tcllib/files/modules/simulation/montecarlo.html"> simulation::montecarlo </a> &#183; <a href="tcllib/files/modules/simulation/simulation_random.html"> simulation::random </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="mathematics"> mathematics </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/math/fourier.html"> math::fourier </a> &#183; <a href="tcllib/files/modules/math/statistics.html"> math::statistics </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
4140
4141
4142
4143
4144
4145
4146





4147
4148
4149
4150
4151

4152
4153
4154
4155
4156

4157
4158
4159
4160
4161

4162
4163
4164
4165
4166
4167
4168
4169

4170
4171
4172
4173
4174

4175
4176
4177
4178
4179

4180
4181
4182
4183
4184

4185
4186
4187
4188
4189

4190
4191
4192
4193
4194

4195
4196
4197
4198
4199

4200
4201
4202
4203
4204

4205
4206
4207
4208
4209

4210
4211
4212
4213
4214

4215
4216
4217
4218
4219

4220
4221
4222
4223
4224

4225
4226
4227
4228
4229

4230
4231
4232
4233
4234

4235
4236
4237
4238
4239

4240
4241
4242
4243
4244

4245
4246
4247
4248
4249

4250
4251
4252
4253
4254
4255
4256
4257

4258
4259
4260
4261
4262

4263
4264
4265
4266
4267

4268
4269
4270
4271
4272

4273
4274
4275
4276
4277

4278
4279
4280
4281
4282

4283
4284
4285
4286
4287

4288
4289
4290
4291
4292

4293
4294
4295
4296
4297

4298
4299
4300
4301
4302

4303
4304
4305
4306
4307
4308
4309
4310

4311
4312
4313
4314
4315

4316
4317
4318
4319
4320

4321
4322
4323
4324
4325

4326
4327
4328
4329
4330

4331
4332
4333
4334
4335

4336
4337
4338
4339
4340

4341
4342
4343
4344
4345
4346
4347
4348

4349
4350
4351
4352
4353

4354
4355
4356
4357
4358

4359
4360
4361
4362
4363

4364
4365
4366
4367
4368

4369
4370
4371
4372
4373

4374
4375
4376
4377
4378

4379
4380
4381
4382
4383

4384
4385
4386
4387
4388
4389
4390
4391

4392
4393
4394
4395
4396

4397
4398
4399
4400
4401

4402
4403
4404
4405
4406

4407
4408
4409
4410
4411
4412
4413
4414

4415
4416
4417
4418
4419

4420
4421
4422
4423
4424

4425
4426
4427
4428
4429

4430
4431
4432
4433
4434
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155

4156
4157
4158
4159
4160

4161
4162
4163
4164
4165

4166
4167
4168
4169
4170
4171
4172
4173

4174
4175
4176
4177
4178

4179
4180
4181
4182
4183

4184
4185
4186
4187
4188

4189
4190
4191
4192
4193

4194
4195
4196
4197
4198

4199
4200
4201
4202
4203

4204
4205
4206
4207
4208

4209
4210
4211
4212
4213

4214
4215
4216
4217
4218

4219
4220
4221
4222
4223

4224
4225
4226
4227
4228

4229
4230
4231
4232
4233

4234
4235
4236
4237
4238

4239
4240
4241
4242
4243

4244
4245
4246
4247
4248

4249
4250
4251
4252
4253

4254
4255
4256
4257
4258
4259
4260
4261

4262
4263
4264
4265
4266

4267
4268
4269
4270
4271

4272
4273
4274
4275
4276

4277
4278
4279
4280
4281

4282
4283
4284
4285
4286

4287
4288
4289
4290
4291

4292
4293
4294
4295
4296

4297
4298
4299
4300
4301

4302
4303
4304
4305
4306

4307
4308
4309
4310
4311
4312
4313
4314

4315
4316
4317
4318
4319

4320
4321
4322
4323
4324

4325
4326
4327
4328
4329

4330
4331
4332
4333
4334

4335
4336
4337
4338
4339

4340
4341
4342
4343
4344

4345
4346
4347
4348
4349
4350
4351
4352

4353
4354
4355
4356
4357

4358
4359
4360
4361
4362

4363
4364
4365
4366
4367

4368
4369
4370
4371
4372

4373
4374
4375
4376
4377

4378
4379
4380
4381
4382

4383
4384
4385
4386
4387

4388
4389
4390
4391
4392
4393
4394
4395

4396
4397
4398
4399
4400

4401
4402
4403
4404
4405

4406
4407
4408
4409
4410

4411
4412
4413
4414
4415
4416
4417
4418

4419
4420
4421
4422
4423

4424
4425
4426
4427
4428

4429
4430
4431
4432
4433

4434
4435
4436
4437
4438
4439







+
+
+
+
+




-
+




-
+




-
+







-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+







-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+







-
+




-
+




-
+




-
+




-
+




-
+




-
+







-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+







-
+




-
+




-
+




-
+







-
+




-
+




-
+




-
+





</td></tr>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="treeql"> TreeQL </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/treeql/treeql.html"> treeql </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="trigonometry"> trigonometry </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/math/trig.html"> math::trig </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="trimming"> trimming </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/textutil/textutil.html"> textutil </a> &#183; <a href="tcllib/files/modules/textutil/trim.html"> textutil::trim </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="twitter"> twitter </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/oauth/oauth.html"> oauth </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="type"> type </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/fileutil/fileutil.html"> fileutil </a> &#183; <a href="tcllib/files/modules/fumagic/cfront.html"> fileutil::magic::cfront </a> &#183; <a href="tcllib/files/modules/fumagic/cgen.html"> fileutil::magic::cgen </a> &#183; <a href="tcllib/files/modules/fumagic/filetypes.html"> fileutil::magic::filetype </a> &#183; <a href="tcllib/files/modules/fumagic/rtcore.html"> fileutil::magic::rt </a> &#183; <a href="tcllib/files/modules/snit/snit.html"> snit </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="type_checking"> Type checking </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/valtype/valtype_common.html"> valtype::common </a> &#183; <a href="tcllib/files/modules/valtype/cc_amex.html"> valtype::creditcard::amex </a> &#183; <a href="tcllib/files/modules/valtype/cc_discover.html"> valtype::creditcard::discover </a> &#183; <a href="tcllib/files/modules/valtype/cc_mastercard.html"> valtype::creditcard::mastercard </a> &#183; <a href="tcllib/files/modules/valtype/cc_visa.html"> valtype::creditcard::visa </a> &#183; <a href="tcllib/files/modules/valtype/ean13.html"> valtype::gs1::ean13 </a> &#183; <a href="tcllib/files/modules/valtype/iban.html"> valtype::iban </a> &#183; <a href="tcllib/files/modules/valtype/imei.html"> valtype::imei </a> &#183; <a href="tcllib/files/modules/valtype/isbn.html"> valtype::isbn </a> &#183; <a href="tcllib/files/modules/valtype/luhn.html"> valtype::luhn </a> &#183; <a href="tcllib/files/modules/valtype/luhn5.html"> valtype::luhn5 </a> &#183; <a href="tcllib/files/modules/valtype/usnpi.html"> valtype::usnpi </a> &#183; <a href="tcllib/files/modules/valtype/verhoeff.html"> valtype::verhoeff </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cU">Keywords: U</a>
</th></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="uevent"> uevent </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/hook/hook.html"> hook </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="unbind"> unbind </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/uev/uevent.html"> uevent </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="uncapitalize"> uncapitalize </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/textutil/textutil_string.html"> textutil::string </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="undenting"> undenting </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/textutil/adjust.html"> textutil::adjust </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="unicode"> unicode </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/stringprep/stringprep.html"> stringprep </a> &#183; <a href="tcllib/files/modules/stringprep/stringprep_data.html"> stringprep::data </a> &#183; <a href="tcllib/files/modules/stringprep/unicode.html"> unicode </a> &#183; <a href="tcllib/files/modules/stringprep/unicode_data.html"> unicode::data </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="union"> union </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/struct/disjointset.html"> struct::disjointset </a> &#183; <a href="tcllib/files/modules/struct/struct_set.html"> struct::set </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="unit"> unit </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/units/units.html"> units </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="unknown_hooking"> unknown hooking </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/namespacex/namespacex.html"> namespacex </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="untie"> untie </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/tie/tie_std.html"> tie </a> &#183; <a href="tcllib/files/modules/tie/tie.html"> tie </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="update"> update </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/coroutine/tcllib_coroutine.html"> coroutine </a> &#183; <a href="tcllib/files/modules/coroutine/coro_auto.html"> coroutine::auto </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="uri"> uri </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/uri/uri.html"> uri </a> &#183; <a href="tcllib/files/modules/uri/urn-scheme.html"> uri_urn </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="url"> url </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/doctools2idx/idx_container.html"> doctools::idx </a> &#183; <a href="tcllib/files/modules/doctools2idx/idx_export.html"> doctools::idx::export </a> &#183; <a href="tcllib/files/modules/doctools2idx/idx_import.html"> doctools::idx::import </a> &#183; <a href="tcllib/files/modules/doctools2toc/toc_export.html"> doctools::toc::export </a> &#183; <a href="tcllib/files/modules/doctools2toc/toc_import.html"> doctools::toc::import </a> &#183; <a href="tcllib/files/modules/map/map_geocode_nominatim.html"> map::geocode::nominatim </a> &#183; <a href="tcllib/files/modules/map/map_slippy_fetcher.html"> map::slippy::fetcher </a> &#183; <a href="tcllib/files/modules/uri/uri.html"> uri </a> &#183; <a href="tcllib/files/modules/uri/urn-scheme.html"> uri_urn </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="urn"> urn </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/uri/urn-scheme.html"> uri_urn </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="us_npi"> US-NPI </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/valtype/usnpi.html"> valtype::usnpi </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="utilities"> utilities </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/namespacex/namespacex.html"> namespacex </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="uuencode"> uuencode </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/base64/uuencode.html"> uuencode </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="uuid"> UUID </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/uuid/uuid.html"> uuid </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cV">Keywords: V</a>
</th></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="validation"> Validation </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/valtype/valtype_common.html"> valtype::common </a> &#183; <a href="tcllib/files/modules/valtype/cc_amex.html"> valtype::creditcard::amex </a> &#183; <a href="tcllib/files/modules/valtype/cc_discover.html"> valtype::creditcard::discover </a> &#183; <a href="tcllib/files/modules/valtype/cc_mastercard.html"> valtype::creditcard::mastercard </a> &#183; <a href="tcllib/files/modules/valtype/cc_visa.html"> valtype::creditcard::visa </a> &#183; <a href="tcllib/files/modules/valtype/ean13.html"> valtype::gs1::ean13 </a> &#183; <a href="tcllib/files/modules/valtype/iban.html"> valtype::iban </a> &#183; <a href="tcllib/files/modules/valtype/imei.html"> valtype::imei </a> &#183; <a href="tcllib/files/modules/valtype/isbn.html"> valtype::isbn </a> &#183; <a href="tcllib/files/modules/valtype/luhn.html"> valtype::luhn </a> &#183; <a href="tcllib/files/modules/valtype/luhn5.html"> valtype::luhn5 </a> &#183; <a href="tcllib/files/modules/valtype/usnpi.html"> valtype::usnpi </a> &#183; <a href="tcllib/files/modules/valtype/verhoeff.html"> valtype::verhoeff </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="value_checking"> Value checking </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/valtype/valtype_common.html"> valtype::common </a> &#183; <a href="tcllib/files/modules/valtype/cc_amex.html"> valtype::creditcard::amex </a> &#183; <a href="tcllib/files/modules/valtype/cc_discover.html"> valtype::creditcard::discover </a> &#183; <a href="tcllib/files/modules/valtype/cc_mastercard.html"> valtype::creditcard::mastercard </a> &#183; <a href="tcllib/files/modules/valtype/cc_visa.html"> valtype::creditcard::visa </a> &#183; <a href="tcllib/files/modules/valtype/ean13.html"> valtype::gs1::ean13 </a> &#183; <a href="tcllib/files/modules/valtype/iban.html"> valtype::iban </a> &#183; <a href="tcllib/files/modules/valtype/imei.html"> valtype::imei </a> &#183; <a href="tcllib/files/modules/valtype/isbn.html"> valtype::isbn </a> &#183; <a href="tcllib/files/modules/valtype/luhn.html"> valtype::luhn </a> &#183; <a href="tcllib/files/modules/valtype/luhn5.html"> valtype::luhn5 </a> &#183; <a href="tcllib/files/modules/valtype/usnpi.html"> valtype::usnpi </a> &#183; <a href="tcllib/files/modules/valtype/verhoeff.html"> valtype::verhoeff </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="vectors"> vectors </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/math/linalg.html"> math::linearalgebra </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="verhoeff"> verhoeff </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/valtype/verhoeff.html"> valtype::verhoeff </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="vertex"> vertex </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/struct/graph.html"> struct::graph </a> &#183; <a href="tcllib/files/modules/struct/graphops.html"> struct::graph::op </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="vertex_cover"> vertex cover </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/struct/graphops.html"> struct::graph::op </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="virtual_channel"> virtual channel </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/virtchannel_base/cat.html"> tcl::chan::cat </a> &#183; <a href="tcllib/files/modules/virtchannel_core/core.html"> tcl::chan::core </a> &#183; <a href="tcllib/files/modules/virtchannel_core/events.html"> tcl::chan::events </a> &#183; <a href="tcllib/files/modules/virtchannel_base/facade.html"> tcl::chan::facade </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_fifo.html"> tcl::chan::fifo </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_fifo2.html"> tcl::chan::fifo2 </a> &#183; <a href="tcllib/files/modules/virtchannel_base/halfpipe.html"> tcl::chan::halfpipe </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_memchan.html"> tcl::chan::memchan </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_null.html"> tcl::chan::null </a> &#183; <a href="tcllib/files/modules/virtchannel_base/nullzero.html"> tcl::chan::nullzero </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_random.html"> tcl::chan::random </a> &#183; <a href="tcllib/files/modules/virtchannel_base/std.html"> tcl::chan::std </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_string.html"> tcl::chan::string </a> &#183; <a href="tcllib/files/modules/virtchannel_base/textwindow.html"> tcl::chan::textwindow </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_variable.html"> tcl::chan::variable </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_zero.html"> tcl::chan::zero </a> &#183; <a href="tcllib/files/modules/virtchannel_base/randseed.html"> tcl::randomseed </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/adler32.html"> tcl::transform::adler32 </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/vt_base64.html"> tcl::transform::base64 </a> &#183; <a href="tcllib/files/modules/virtchannel_core/transformcore.html"> tcl::transform::core </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/vt_counter.html"> tcl::transform::counter </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/vt_crc32.html"> tcl::transform::crc32 </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/hex.html"> tcl::transform::hex </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/identity.html"> tcl::transform::identity </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/limitsize.html"> tcl::transform::limitsize </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/observe.html"> tcl::transform::observe </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/vt_otp.html"> tcl::transform::otp </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/rot.html"> tcl::transform::rot </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/spacer.html"> tcl::transform::spacer </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/tcllib_zlib.html"> tcl::transform::zlib </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="virtual_machine"> virtual machine </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/grammar_me/me_cpu.html"> grammar::me::cpu </a> &#183; <a href="tcllib/files/modules/grammar_me/me_cpucore.html"> grammar::me::cpu::core </a> &#183; <a href="tcllib/files/modules/grammar_me/gasm.html"> grammar::me::cpu::gasm </a> &#183; <a href="tcllib/files/modules/grammar_me/me_tcl.html"> grammar::me::tcl </a> &#183; <a href="tcllib/files/modules/grammar_me/me_intro.html"> grammar::me_intro </a> &#183; <a href="tcllib/files/modules/grammar_me/me_vm.html"> grammar::me_vm </a> &#183; <a href="tcllib/files/modules/grammar_peg/peg_interp.html"> grammar::peg::interp </a> &#183; <a href="tcllib/files/modules/pt/pt_param.html"> pt::param </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="visa"> VISA </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/valtype/cc_visa.html"> valtype::creditcard::visa </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="vwait"> vwait </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/coroutine/tcllib_coroutine.html"> coroutine </a> &#183; <a href="tcllib/files/modules/coroutine/coro_auto.html"> coroutine::auto </a> &#183; <a href="tcllib/files/modules/smtpd/smtpd.html"> smtpd </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cW">Keywords: W</a>
</th></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="wais"> wais </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/uri/uri.html"> uri </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="widget"> widget </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/snit/snit.html"> snit </a> &#183; <a href="tcllib/files/modules/snit/snitfaq.html"> snitfaq </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="widget_adaptors"> widget adaptors </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/snit/snit.html"> snit </a> &#183; <a href="tcllib/files/modules/snit/snitfaq.html"> snitfaq </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="wiki"> wiki </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/doctools/docidx.html"> doctools::idx </a> &#183; <a href="tcllib/files/modules/doctools2idx/idx_container.html"> doctools::idx </a> &#183; <a href="tcllib/files/modules/doctools2idx/idx_export.html"> doctools::idx::export </a> &#183; <a href="tcllib/files/modules/doctools2idx/idx_export_wiki.html"> doctools::idx::export::wiki </a> &#183; <a href="tcllib/files/modules/doctools2toc/toc_container.html"> doctools::toc </a> &#183; <a href="tcllib/files/modules/doctools/doctoc.html"> doctools::toc </a> &#183; <a href="tcllib/files/modules/doctools2toc/toc_export.html"> doctools::toc::export </a> &#183; <a href="tcllib/files/modules/doctools2toc/toc_export_wiki.html"> doctools::toc::export::wiki </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="word"> word </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/doctools2base/tcl_parse.html"> doctools::tcl::parse </a> &#183; <a href="tcllib/files/modules/wip/wip.html"> wip </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="www"> WWW </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/httpd/httpd.html"> tool </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="www"> www </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/uri/uri.html"> uri </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cX">Keywords: X</a>
</th></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="x_208"> x.208 </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/asn/asn.html"> asn </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="x_209"> x.209 </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/asn/asn.html"> asn </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="x_500"> x.500 </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/ldap/ldap.html"> ldap </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="xgoogletoken"> XGoogleToken </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/sasl/gtoken.html"> SASL::XGoogleToken </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="xml"> xml </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/amazon-s3/xsxp.html"> xsxp </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="xor"> xor </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/virtchannel_transform/vt_otp.html"> tcl::transform::otp </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="xpath"> XPath </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/treeql/treeql.html"> treeql </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="xslt"> XSLT </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/treeql/treeql.html"> treeql </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cY">Keywords: Y</a>
</th></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="yaml"> yaml </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/yaml/huddle.html"> huddle </a> &#183; <a href="tcllib/files/modules/yaml/yaml.html"> yaml </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="ydecode"> ydecode </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/base64/yencode.html"> yencode </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="yenc"> yEnc </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/base64/yencode.html"> yencode </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="yencode"> yencode </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/base64/yencode.html"> yencode </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cZ">Keywords: Z</a>
</th></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="zero"> zero </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/virtchannel_base/nullzero.html"> tcl::chan::nullzero </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_zero.html"> tcl::chan::zero </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="zip"> zip </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/zip/decode.html"> zipfile::decode </a> &#183; <a href="tcllib/files/modules/zip/encode.html"> zipfile::encode </a> &#183; <a href="tcllib/files/modules/zip/mkzip.html"> zipfile::mkzip </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="zlib"> zlib </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/virtchannel_transform/tcllib_zlib.html"> tcl::transform::zlib </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="zoom"> zoom </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/map/map_slippy.html"> map::slippy </a> &#183; <a href="tcllib/files/modules/map/map_slippy_cache.html"> map::slippy::cache </a> &#183; <a href="tcllib/files/modules/map/map_slippy_fetcher.html"> map::slippy::fetcher </a>
</td></tr>
</table>

Added embedded/www/tcllib/files/modules/clay/clay.html.









































































































































































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

<!DOCTYPE html><html><head>
<title>clay - Clay Framework</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
	background: 	#FFFFFF;
	color:	 	black;
    }
    DIV.doctools {
	margin-left:	10%;
	margin-right:	10%;
    }
    DIV.doctools H1,DIV.doctools H2 {
	margin-left:	-5%;
    }
    H1, H2, H3, H4 {
	margin-top: 	1em;
	font-family:	sans-serif;
	font-size:	large;
	color:		#005A9C;
	background: 	transparent;
	text-align:		left;
    }
    H1.doctools_title {
	text-align: center;
    }
    UL,OL {
	margin-right: 0em;
	margin-top: 3pt;
	margin-bottom: 3pt;
    }
    UL LI {
	list-style: disc;
    }
    OL LI {
	list-style: decimal;
    }
    DT {
	padding-top: 	1ex;
    }
    UL.doctools_toc,UL.doctools_toc UL, UL.doctools_toc UL UL {
	font:		normal 12pt/14pt sans-serif;
	list-style:	none;
    }
    LI.doctools_section, LI.doctools_subsection {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding: 	0em;
    }
    PRE {
	display: 	block;
	font-family:	monospace;
	white-space:	pre;
	margin:		0%;
	padding-top:	0.5ex;
	padding-bottom:	0.5ex;
	padding-left:	1ex;
	padding-right:	1ex;
	width:		100%;
    }
    PRE.doctools_example {
	color: 		black;
	background: 	#f5dcb3;
	border:		1px solid black;
    }
    UL.doctools_requirements LI, UL.doctools_syntax LI {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding:	0em;
    }
    DIV.doctools_synopsis {
	color: 		black;
	background: 	#80ffff;
	border:		1px solid black;
	font-family:	serif;
	margin-top: 	1em;
	margin-bottom: 	1em;
    }
    UL.doctools_syntax {
	margin-top: 	1em;
	border-top:	1px solid black;
    }
    UL.doctools_requirements {
	margin-bottom: 	1em;
	border-bottom:	1px solid black;
    }
--></style>
</head>
<!-- Generated from file 'clay.man' by tcllib/doctools with format 'html'
   -->
<!-- Copyright &amp;copy; 2018 Sean Woods &amp;lt;[email protected]&amp;gt;
   -->
<!-- clay.n
   -->
<body><div class="doctools">
<h1 class="doctools_title">clay(n) 0.3 clay &quot;Clay Framework&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>clay - A minimalist framework for large scale OO Projects</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a>
<ul>
<li class="doctools_subsection"><a href="#subsection1">Structured Data</a></li>
<li class="doctools_subsection"><a href="#subsection2">Clay Dialect</a></li>
<li class="doctools_subsection"><a href="#subsection3">Method Delegation</a></li>
</ul>
</li>
<li class="doctools_section"><a href="#section2">Commands</a></li>
<li class="doctools_section"><a href="#section3">Classes</a>
<ul>
<li class="doctools_subsection"><a href="#subsection4">Class  oo::class</a></li>
<li class="doctools_subsection"><a href="#subsection5">Class  oo::object</a></li>
<li class="doctools_subsection"><a href="#subsection6">Class  clay::object</a></li>
<li class="doctools_subsection"><a href="#subsection7">Class  clay::doctool</a></li>
</ul>
</li>
<li class="doctools_section"><a href="#section4">AUTHORS</a></li>
<li class="doctools_section"><a href="#section5">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
<li class="doctools_section"><a href="#category">Category</a></li>
<li class="doctools_section"><a href="#copyright">Copyright</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl 8.6</b></li>
<li>package require <b class="pkgname">uuid</b></li>
<li>package require <b class="pkgname">oo::dialect</b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1">proc <b class="cmd">putb</b> <span class="opt">?<i class="arg">map</i>?</span> <i class="arg">text</i></a></li>
<li><a href="#2">proc <b class="cmd">clay::ancestors</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#3">proc <b class="cmd">clay::args_to_dict</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#4">proc <b class="cmd">clay::args_to_options</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#5">proc <b class="cmd">clay::dictmerge</b> <i class="arg">varname</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#6">proc <b class="cmd">clay::_dictmerge</b> <i class="arg">a</i> <i class="arg">b</i></a></li>
<li><a href="#7">proc <b class="cmd">clay::dictputb</b> <i class="arg">dict</i></a></li>
<li><a href="#8">proc <b class="cmd">clay::_dictputb</b> <i class="arg">leaf</i> <i class="arg">level</i> <i class="arg">varname</i> <i class="arg">dict</i></a></li>
<li><a href="#9">proc <b class="cmd">clay::dynamic_arguments</b> <i class="arg">ensemble</i> <i class="arg">method</i> <i class="arg">arglist</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#10">proc <b class="cmd">clay::dynamic_wrongargs_message</b> <i class="arg">arglist</i></a></li>
<li><a href="#11">proc <b class="cmd">clay::is_dict</b> <i class="arg">d</i></a></li>
<li><a href="#12">proc <b class="cmd">clay::is_null</b> <i class="arg">value</i></a></li>
<li><a href="#13">proc <b class="cmd">clay::leaf</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#14">proc <b class="cmd">clay::path</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#15">proc <b class="cmd">clay::script_path</b></a></li>
<li><a href="#16">proc <b class="cmd">clay::NSNormalize</b> <i class="arg">qualname</i></a></li>
<li><a href="#17">proc <b class="cmd">clay::uuid_generate</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#18">proc <b class="cmd">clay::dynamic_methods</b> <i class="arg">class</i></a></li>
<li><a href="#19">proc <b class="cmd">clay::dynamic_methods_class</b> <i class="arg">thisclass</i></a></li>
<li><a href="#20">proc <b class="cmd">clay::define::Array</b> <i class="arg">name</i> <span class="opt">?<i class="arg">values</i> <b class="const"></b>?</span></a></li>
<li><a href="#21">proc <b class="cmd">clay::define::component</b> <i class="arg">name</i> <i class="arg">info</i></a></li>
<li><a href="#22">proc <b class="cmd">clay::define::constructor</b> <i class="arg">arglist</i> <i class="arg">rawbody</i></a></li>
<li><a href="#23">proc <b class="cmd">clay::define::class_method</b> <i class="arg">name</i> <i class="arg">arglist</i> <i class="arg">body</i></a></li>
<li><a href="#24">proc <b class="cmd">clay::define::clay</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#25">proc <b class="cmd">clay::define::destructor</b> <i class="arg">rawbody</i></a></li>
<li><a href="#26">proc <b class="cmd">clay::define::Dict</b> <i class="arg">name</i> <span class="opt">?<i class="arg">values</i> <b class="const"></b>?</span></a></li>
<li><a href="#27">proc <b class="cmd">clay::define::Variable</b> <i class="arg">name</i> <span class="opt">?<i class="arg">default</i> <b class="const"></b>?</span></a></li>
<li><a href="#28">proc <b class="cmd">clay::object_create</b> <i class="arg">objname</i> <span class="opt">?<i class="arg">class</i> <b class="const"></b>?</span></a></li>
<li><a href="#29">proc <b class="cmd">clay::object_rename</b> <i class="arg">object</i> <i class="arg">newname</i></a></li>
<li><a href="#30">proc <b class="cmd">clay::object_destroy</b> <i class="arg">objname</i></a></li>
<li><a href="#31">proc <b class="cmd">clay::ensemble_methodbody</b> <i class="arg">ensemble</i> <i class="arg">einfo</i></a></li>
<li><a href="#32">proc <b class="cmd">clay::define::Ensemble</b> <i class="arg">rawmethod</i> <i class="arg">arglist</i> <i class="arg">body</i></a></li>
<li><a href="#33">proc <b class="cmd">clay::cat</b> <i class="arg">fname</i></a></li>
<li><a href="#34">proc <b class="cmd">clay::docstrip</b> <i class="arg">text</i></a></li>
<li><a href="#35">method <b class="cmd">clay ancestors</b></a></li>
<li><a href="#36">method <b class="cmd">clay dump</b></a></li>
<li><a href="#37">method <b class="cmd">clay get</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></li>
<li><a href="#38">method <b class="cmd">clay merge</b> <i class="arg">dict</i> <span class="opt">?<b class="option">dict...</b>?</span></a></li>
<li><a href="#39">method <b class="cmd">clay replace</b> <i class="arg">dictionary</i></a></li>
<li><a href="#40">method <b class="cmd">clay search</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></li>
<li><a href="#41">method <b class="cmd">clay set</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span> <i class="arg">value</i></a></li>
<li><a href="#42">method <b class="cmd">clay ancestors</b></a></li>
<li><a href="#43">method <b class="cmd">clay cget</b> <i class="arg">field</i></a></li>
<li><a href="#44">method <b class="cmd">clay delegate</b> <span class="opt">?<i class="arg">stub</i>?</span> <span class="opt">?<i class="arg">object</i>?</span></a></li>
<li><a href="#45">method <b class="cmd">clay dump</b></a></li>
<li><a href="#46">method <b class="cmd">clay ensemble_map</b></a></li>
<li><a href="#47">method <b class="cmd">clay eval</b> <i class="arg">script</i></a></li>
<li><a href="#48">method <b class="cmd">clay evolve</b></a></li>
<li><a href="#49">method <b class="cmd">clay exists</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></li>
<li><a href="#50">method <b class="cmd">clay flush</b></a></li>
<li><a href="#51">method <b class="cmd">clay forward</b> <i class="arg">method</i> <i class="arg">object</i></a></li>
<li><a href="#52">method <b class="cmd">clay get</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></li>
<li><a href="#53">method <b class="cmd">clay leaf</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></li>
<li><a href="#54">method <b class="cmd">clay merge</b> <i class="arg">dict</i> <span class="opt">?<b class="option">dict...</b>?</span></a></li>
<li><a href="#55">method <b class="cmd">clay mixin</b> <i class="arg">class</i> <span class="opt">?<b class="option">class...</b>?</span></a></li>
<li><a href="#56">method <b class="cmd">clay mixinmap</b> <span class="opt">?<i class="arg">stub</i>?</span> <span class="opt">?<i class="arg">classes</i>?</span></a></li>
<li><a href="#57">method <b class="cmd">clay provenance</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></li>
<li><a href="#58">method <b class="cmd">clay replace</b> <i class="arg">dictionary</i></a></li>
<li><a href="#59">method <b class="cmd">clay source</b> <i class="arg">filename</i></a></li>
<li><a href="#60">method <b class="cmd">clay set</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span> <i class="arg">value</i></a></li>
<li><a href="#61">method <b class="cmd">InitializePublic</b></a></li>
<li><a href="#62">method <b class="cmd">InitializePublic</b></a></li>
<li><a href="#63">method <b class="cmd">constructor</b></a></li>
<li><a href="#64">method <b class="cmd">arglist</b> <i class="arg">arglist</i></a></li>
<li><a href="#65">method <b class="cmd">comment</b> <i class="arg">block</i></a></li>
<li><a href="#66">method <b class="cmd">keyword.Class</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <i class="arg">body</i></a></li>
<li><a href="#67">method <b class="cmd">keyword.class</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <i class="arg">body</i></a></li>
<li><a href="#68">method <b class="cmd">keyword.class_method</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#69">method <b class="cmd">keyword.method</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#70">method <b class="cmd">keyword.proc</b> <i class="arg">commentblock</i> <i class="arg">name</i> <i class="arg">arglist</i> <i class="arg">body</i></a></li>
<li><a href="#71">method <b class="cmd">reset</b></a></li>
<li><a href="#72">method <b class="cmd">Main</b></a></li>
<li><a href="#73">method <b class="cmd">section.method</b> <i class="arg">keyword</i> <i class="arg">method</i> <i class="arg">minfo</i></a></li>
<li><a href="#74">method <b class="cmd">section.class</b> <i class="arg">class_name</i> <i class="arg">class_info</i></a></li>
<li><a href="#75">method <b class="cmd">section.command</b> <i class="arg">procinfo</i></a></li>
<li><a href="#76">method <b class="cmd">manpage</b> <span class="opt">?<b class="option">header <em>value</em></b>?</span> <span class="opt">?<b class="option">footer <em>value</em></b>?</span> <span class="opt">?<b class="option">authors <em>list</em></b>?</span></a></li>
<li><a href="#77">method <b class="cmd">scan_text</b> <i class="arg">text</i></a></li>
<li><a href="#78">method <b class="cmd">scan_file</b> <i class="arg">filename</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>Clay introduces a method ensemble to both <b class="class">oo::class</b> and <b class="class">oo::object</b> called
clay. This ensemble handles all of the high level interactions within the framework.
Clay stores structured data. Clan manages method delegation. Clay has facilities to
manage the complex interactions that come about with mixins.</p>
<p>The central concept is that inside of every object and class
(which are actually objects too) is a dict called clay. What is stored in that dict is
left to the imagination. But because this dict is exposed via a public method, we can
share structured data between object, classes, and mixins.</p>
<div id="subsection1" class="doctools_subsection"><h3><a name="subsection1">Structured Data</a></h3>
<p>Clay uses a standardized set of method interactions and introspection that TclOO already provides to perform on-the-fly searches. On-the-fly searches mean that the data is never stale, and we avoid many of the sorts of collisions that would arise when objects start mixing in other classes during operation.</p>
<p>The <b class="method">clay</b> methods for both classes and objects have a get and a set method. For objects, get will search through the local clay dict. If the requested leaf is not found, or the query is for a branch, the system will then begin to poll the clay methods of all of the class that implements the object, all of that classes’ ancestors, as well as all of the classes that have been mixed into this object, and all of their ancestors.</p>
<p>Intended branches on a tree end with a directory slash (/). Intended leaves are left unadorned. This is a guide for the tool that builds the search
results to know what parts of a dict are intended to be branches and which are intended to be leaves.
For simple cases, branch marking can be ignored:</p>
<pre class="doctools_example">
::oo::class create ::foo { }
::foo clay set property/ color blue
::foo clay set property/ shape round
set A [::foo new]
$A clay get property/
{color blue shape round}
$A clay set property/ shape square
$A clay get property/
{color blue shape square}
</pre>
<p>But when you start storing blocks of text, guessing what field is a dict and what isn’t gets messy:</p>
<pre class="doctools_example">
::foo clay set description {A generic thing of designated color and shape}
$A clay get description
{A generic thing of designated color and shape}
Without a convention for discerning branches for leaves what should have been a value can be accidentally parsed as a dictionary, and merged with all of the other values that were never intended to be merge. Here is an example of it all going wrong:
::oo::class create ::foo { }
# Add description as a leaf
::foo clay set description  {A generic thing of designated color and shape}
# Add description as a branch
::foo clay set description/  {A generic thing of designated color and shape}
::oo::class create ::bar {
  superclass foo
}
# Add description as a leaf
::bar clay set description  {A drinking establishment of designated color and shape and size}
# Add description as a branch
::bar clay set description/  {A drinking establishment of designated color and shape and size}
set B [::bar new]
# As a leaf we get the value verbatim from he nearest ancestor
$B clay get description
  {A drinking establishment of designated color and shape and size}
# As a branch we get a recursive merge
$B clay get description/
{A drinking establishment of designated color and size thing of}
</pre>
</div>
<div id="subsection2" class="doctools_subsection"><h3><a name="subsection2">Clay Dialect</a></h3>
<p>Clay is built using the oo::dialect module from Tcllib. oo::dialect allows you to either add keywords directly to clay, or to create your own
metaclass and keyword set using Clay as a foundation. For details on the keywords and what they do, consult the functions in the ::clay::define namespace.</p>
</div>
<div id="subsection3" class="doctools_subsection"><h3><a name="subsection3">Method Delegation</a></h3>
<p>Method Delegation
It is sometimes useful to have an external object that can be invoked as if it were a method of the object. Clay provides a delegate ensemble method to perform that delegation, as well as introspect which methods are delegated in that manner. All delegated methods are marked with html-like tag markings (&lt; &gt;) around them.</p>
<pre class="doctools_example">
::clay::define counter {
  Variable counter 0
  method incr {{howmuch 1}} {
    my variable counter
    incr counter $howmuch
  }
  method value {} {
    my variable counter
    return $counter
  }
  method reset {} {
    my variable counter
    set counter 0
  }
}
::clay::define example {
  variable buffer
  constructor {} {
    # Build a counter object
    set obj [namespace current]::counter
    ::counter create $obj
    # Delegate the counter
    my delegate &lt;counter&gt; $obj
  }
  method line {text} {
    my &lt;counter&gt; incr
    append buffer $text
  }
}
set A [example new]
$A line {Who’s line is it anyway?}
$A &lt;counter&gt; value
1
</pre>
</div>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">Commands</a></h2>
<dl class="doctools_definitions">
<dt><a name="1">proc <b class="cmd">putb</b> <span class="opt">?<i class="arg">map</i>?</span> <i class="arg">text</i></a></dt>
<dd><p>Append a line of text to a variable. Optionally apply a string mapping.</p></dd>
<dt><a name="2">proc <b class="cmd">clay::ancestors</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="3">proc <b class="cmd">clay::args_to_dict</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="4">proc <b class="cmd">clay::args_to_options</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="5">proc <b class="cmd">clay::dictmerge</b> <i class="arg">varname</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="6">proc <b class="cmd">clay::_dictmerge</b> <i class="arg">a</i> <i class="arg">b</i></a></dt>
<dd></dd>
<dt><a name="7">proc <b class="cmd">clay::dictputb</b> <i class="arg">dict</i></a></dt>
<dd></dd>
<dt><a name="8">proc <b class="cmd">clay::_dictputb</b> <i class="arg">leaf</i> <i class="arg">level</i> <i class="arg">varname</i> <i class="arg">dict</i></a></dt>
<dd></dd>
<dt><a name="9">proc <b class="cmd">clay::dynamic_arguments</b> <i class="arg">ensemble</i> <i class="arg">method</i> <i class="arg">arglist</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="10">proc <b class="cmd">clay::dynamic_wrongargs_message</b> <i class="arg">arglist</i></a></dt>
<dd></dd>
<dt><a name="11">proc <b class="cmd">clay::is_dict</b> <i class="arg">d</i></a></dt>
<dd></dd>
<dt><a name="12">proc <b class="cmd">clay::is_null</b> <i class="arg">value</i></a></dt>
<dd></dd>
<dt><a name="13">proc <b class="cmd">clay::leaf</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="14">proc <b class="cmd">clay::path</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="15">proc <b class="cmd">clay::script_path</b></a></dt>
<dd></dd>
<dt><a name="16">proc <b class="cmd">clay::NSNormalize</b> <i class="arg">qualname</i></a></dt>
<dd></dd>
<dt><a name="17">proc <b class="cmd">clay::uuid_generate</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="18">proc <b class="cmd">clay::dynamic_methods</b> <i class="arg">class</i></a></dt>
<dd></dd>
<dt><a name="19">proc <b class="cmd">clay::dynamic_methods_class</b> <i class="arg">thisclass</i></a></dt>
<dd></dd>
<dt><a name="20">proc <b class="cmd">clay::define::Array</b> <i class="arg">name</i> <span class="opt">?<i class="arg">values</i> <b class="const"></b>?</span></a></dt>
<dd><p>New OO Keywords for clay</p></dd>
<dt><a name="21">proc <b class="cmd">clay::define::component</b> <i class="arg">name</i> <i class="arg">info</i></a></dt>
<dd></dd>
<dt><a name="22">proc <b class="cmd">clay::define::constructor</b> <i class="arg">arglist</i> <i class="arg">rawbody</i></a></dt>
<dd></dd>
<dt><a name="23">proc <b class="cmd">clay::define::class_method</b> <i class="arg">name</i> <i class="arg">arglist</i> <i class="arg">body</i></a></dt>
<dd></dd>
<dt><a name="24">proc <b class="cmd">clay::define::clay</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="25">proc <b class="cmd">clay::define::destructor</b> <i class="arg">rawbody</i></a></dt>
<dd></dd>
<dt><a name="26">proc <b class="cmd">clay::define::Dict</b> <i class="arg">name</i> <span class="opt">?<i class="arg">values</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="27">proc <b class="cmd">clay::define::Variable</b> <i class="arg">name</i> <span class="opt">?<i class="arg">default</i> <b class="const"></b>?</span></a></dt>
<dd><p>This keyword can also be expressed:</p>
<pre class="doctools_example">property variable NAME {default DEFAULT}</pre>
<p>Variables registered in the variable property are also initialized
    (if missing) when the object changes class via the <em>morph</em> method.</p></dd>
<dt><a name="28">proc <b class="cmd">clay::object_create</b> <i class="arg">objname</i> <span class="opt">?<i class="arg">class</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="29">proc <b class="cmd">clay::object_rename</b> <i class="arg">object</i> <i class="arg">newname</i></a></dt>
<dd></dd>
<dt><a name="30">proc <b class="cmd">clay::object_destroy</b> <i class="arg">objname</i></a></dt>
<dd></dd>
<dt><a name="31">proc <b class="cmd">clay::ensemble_methodbody</b> <i class="arg">ensemble</i> <i class="arg">einfo</i></a></dt>
<dd></dd>
<dt><a name="32">proc <b class="cmd">clay::define::Ensemble</b> <i class="arg">rawmethod</i> <i class="arg">arglist</i> <i class="arg">body</i></a></dt>
<dd></dd>
<dt><a name="33">proc <b class="cmd">clay::cat</b> <i class="arg">fname</i></a></dt>
<dd><p>Concatenate a file</p></dd>
<dt><a name="34">proc <b class="cmd">clay::docstrip</b> <i class="arg">text</i></a></dt>
<dd><p>Strip the global comments from tcl code. Used to
 prevent the documentation markup comments from clogging
 up files intended for distribution in machine readable format.</p></dd>
</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">Classes</a></h2>
<div id="subsection4" class="doctools_subsection"><h3><a name="subsection4">Class  oo::class</a></h3>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="35">method <b class="cmd">clay ancestors</b></a></dt>
<dd><p>Return this class and all ancestors in search order.</p></dd>
<dt><a name="36">method <b class="cmd">clay dump</b></a></dt>
<dd><p>Return a complete dump of this object's clay data, but only this object's clay data.</p></dd>
<dt><a name="37">method <b class="cmd">clay get</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></dt>
<dd><p>Pull a chunk of data from the clay system. If the last element of <em>path</em> is a branch (ends in a slash /),
     returns a recursive merge of all data from this object and it's constituent classes of the data in that branch.
     If the last element is a leaf, search this object for a matching leaf, or search all  constituent classes for a matching
     leaf and return the first value found.
     If no value is found, returns an empty string.</p></dd>
<dt><a name="38">method <b class="cmd">clay merge</b> <i class="arg">dict</i> <span class="opt">?<b class="option">dict...</b>?</span></a></dt>
<dd><p>Recursively merge the dictionaries given into the object's local clay storage.</p></dd>
<dt><a name="39">method <b class="cmd">clay replace</b> <i class="arg">dictionary</i></a></dt>
<dd><p>Replace the contents of the internal clay storage with the dictionary given.</p></dd>
<dt><a name="40">method <b class="cmd">clay search</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></dt>
<dd><p>Return the first matching value for the path in either this class's clay data or one of its ancestors</p></dd>
<dt><a name="41">method <b class="cmd">clay set</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span> <i class="arg">value</i></a></dt>
<dd><p>Merge the conents of <b class="const">value</b> with the object's clay storage at <b class="const">path</b>.</p></dd>
</dl>
</div>
<div id="subsection5" class="doctools_subsection"><h3><a name="subsection5">Class  oo::object</a></h3>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="42">method <b class="cmd">clay ancestors</b></a></dt>
<dd><p>Return the class this object belongs to, all classes mixed into this object, and all ancestors of those classes in search order.</p></dd>
<dt><a name="43">method <b class="cmd">clay cget</b> <i class="arg">field</i></a></dt>
<dd><p>Pull a value from either the object's clay structure or one of its constituent classes that matches the field name.
 The order of search us:</p>
<p>1. The as a value in local dict variable config</p>
<p>2. The as a value in local dict variable clay</p>
<p>3. As a leaf in any ancestor as a root of the clay tree</p>
<p>4. As a leaf in any ancestor under the const/ branch of the clay tree</p></dd>
<dt><a name="44">method <b class="cmd">clay delegate</b> <span class="opt">?<i class="arg">stub</i>?</span> <span class="opt">?<i class="arg">object</i>?</span></a></dt>
<dd><p>Introspect or control method delegation. With no arguments, the method will return a
 key/value list of stubs and objects. With just the <i class="arg">stub</i> argument, the method will
 return the object (if any) attached to the stub. With a <i class="arg">stub</i> and an <i class="arg">object</i>
 this command will forward all calls to the method <i class="arg">stub</i> to the <i class="arg">object</i>.</p></dd>
<dt><a name="45">method <b class="cmd">clay dump</b></a></dt>
<dd><p>Return a complete dump of this object's clay data, as well as the data from all constituent classes recursively blended in.</p></dd>
<dt><a name="46">method <b class="cmd">clay ensemble_map</b></a></dt>
<dd><p>Return a dictionary describing the method ensembles to be assembled for this object</p></dd>
<dt><a name="47">method <b class="cmd">clay eval</b> <i class="arg">script</i></a></dt>
<dd><p>Evaluated a script in the namespace of this object</p></dd>
<dt><a name="48">method <b class="cmd">clay evolve</b></a></dt>
<dd><p>Trigger the <b class="method">InitializePublic</b> private method</p></dd>
<dt><a name="49">method <b class="cmd">clay exists</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></dt>
<dd><p>Returns 1 if <em>path</em> exists in either the object's clay data. Values greater than one indicate the element exists in one of the object's constituent classes. A value of zero indicates the path could not be found.</p></dd>
<dt><a name="50">method <b class="cmd">clay flush</b></a></dt>
<dd><p>Wipe any caches built by the clay implementation</p></dd>
<dt><a name="51">method <b class="cmd">clay forward</b> <i class="arg">method</i> <i class="arg">object</i></a></dt>
<dd><p>A convenience wrapper for</p>
<pre class="doctools_example">oo::objdefine [self] forward {*}$args</pre>
</dd>
<dt><a name="52">method <b class="cmd">clay get</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></dt>
<dd><p>Pull a chunk of data from the clay system. If the last element of <em>path</em> is a branch (ends in a slash /),
   returns a recursive merge of all data from this object and it's constituent classes of the data in that branch.
   If the last element is a leaf, search this object for a matching leaf, or search all  constituent classes for a matching
   leaf and return the first value found.
   If no value is found, returns an empty string.</p></dd>
<dt><a name="53">method <b class="cmd">clay leaf</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></dt>
<dd><p>A modified get which is tailored to pull only leaf elements</p></dd>
<dt><a name="54">method <b class="cmd">clay merge</b> <i class="arg">dict</i> <span class="opt">?<b class="option">dict...</b>?</span></a></dt>
<dd><p>Recursively merge the dictionaries given into the object's local clay storage.</p></dd>
<dt><a name="55">method <b class="cmd">clay mixin</b> <i class="arg">class</i> <span class="opt">?<b class="option">class...</b>?</span></a></dt>
<dd><p>Perform [oo::objdefine [self] mixin] on this object, with a few additional rules:
   Prior to the call, for any class was previously mixed in, but not in the new result, execute the script registered to mixin/ unmap-script (if given.)
   For all new classes, that were not present prior to this call, after the native TclOO mixin is invoked, execute the script registered to mixin/ map-script (if given.)
   Fall all classes that are now present and “mixed in”, execute the script registered to mixin/ react-script (if given.)</p></dd>
<dt><a name="56">method <b class="cmd">clay mixinmap</b> <span class="opt">?<i class="arg">stub</i>?</span> <span class="opt">?<i class="arg">classes</i>?</span></a></dt>
<dd><p>With no arguments returns the map of stubs and classes mixed into the current object. When only stub is given,
  returns the classes mixed in on that stub. When stub and classlist given, replace the classes currently on that stub with the given
  classes and invoke clay mixin on the new matrix of mixed in classes.</p></dd>
<dt><a name="57">method <b class="cmd">clay provenance</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></dt>
<dd><p>Return either <b class="const">self</b> if that path exists in the current object, or return the first class (if any) along the clay search path which contains that element.</p></dd>
<dt><a name="58">method <b class="cmd">clay replace</b> <i class="arg">dictionary</i></a></dt>
<dd><p>Replace the contents of the internal clay storage with the dictionary given.</p></dd>
<dt><a name="59">method <b class="cmd">clay source</b> <i class="arg">filename</i></a></dt>
<dd><p>Source the given filename within the object's namespace</p></dd>
<dt><a name="60">method <b class="cmd">clay set</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span> <i class="arg">value</i></a></dt>
<dd><p>Merge the conents of <b class="const">value</b> with the object's clay storage at <b class="const">path</b>.</p></dd>
<dt><a name="61">method <b class="cmd">InitializePublic</b></a></dt>
<dd><p>Instantiate variables. Called on object creation and during clay mixin.</p></dd>
</dl>
</div>
<div id="subsection6" class="doctools_subsection"><h3><a name="subsection6">Class  clay::object</a></h3>
<p>clay::object
 This class is inherited by all classes that have options.</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="62">method <b class="cmd">InitializePublic</b></a></dt>
<dd><p>Instantiate variables and build ensemble methods.</p></dd>
</dl>
</div>
<div id="subsection7" class="doctools_subsection"><h3><a name="subsection7">Class  clay::doctool</a></h3>
<pre class="doctools_example">{ set authors {
   {John Doe} {[email protected]}
   {Tom RichardHarry} {[email protected]}
 }
 # Create the object
 ::clay::doctool create AutoDoc
 set fout [open [file join $moddir module.tcl] w]
 foreach file [glob [file join $srcdir *.tcl]] {
   set content [::clay::cat [file join $srcdir $file]]
    # Scan the file
    AutoDoc scan_text $content
    # Strip the comments from the distribution
    puts $fout [::clay::docstrip $content]
 }
 # Write out the manual page
 set manout [open [file join $moddir module.man] w]
 dict set arglist header [string map $modmap [::clay::cat [file join $srcdir manual.txt]]]
 dict set arglist footer [string map $modmap [::clay::cat [file join $srcdir footer.txt]]]
 dict set arglist authors $authors
 puts $manout [AutoDoc manpage {*}$arglist]
 close $manout
}</pre>
<p>Tool for build scripts to dynamically generate manual files from comments
 in source code files</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="63">method <b class="cmd">constructor</b></a></dt>
<dd></dd>
<dt><a name="64">method <b class="cmd">arglist</b> <i class="arg">arglist</i></a></dt>
<dd><p>Process an argument list into an informational dict.
 This method also understands non-positional
 arguments expressed in the notation of Tip 471
 <a href="https://core.tcl-lang.org/tips/doc/trunk/tip/479.md">https://core.tcl-lang.org/tips/doc/trunk/tip/479.md</a>.</p>
<p>The output will be a dictionary of all of the fields and whether the fields
 are <b class="const">positional</b>, <b class="const">mandatory</b>, and whether they have a
 <b class="const">default</b> value.</p>
<p>Example:</p>
<pre class="doctools_example">   my arglist {a b {c 10}}
   &gt; a {positional 1 mandatory 1} b {positional 1 mandatory 1} c {positional 1 mandatory 0 default 10}
</pre>
</dd>
<dt><a name="65">method <b class="cmd">comment</b> <i class="arg">block</i></a></dt>
<dd><p>Convert a block of comments into an informational dictionary.
 If lines in the comment start with a single word ending in a colon,
 all subsequent lines are appended to a dictionary field of that name.
 If no fields are given, all of the text is appended to the <b class="const">description</b>
 field.</p>
<p>Example:</p>
<pre class="doctools_example"> my comment {Does something cool}
 &gt; description {Does something cool}
 my comment {
 title : Something really cool
 author : Sean Woods
 author : John Doe
 description :
 This does something really cool!
 }
 &gt; description {This does something really cool!}
   title {Something really cool}
   author {Sean Woods
   John Doe}
</pre>
</dd>
<dt><a name="66">method <b class="cmd">keyword.Class</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <i class="arg">body</i></a></dt>
<dd><p>Process an oo::objdefine call that modifies the class object
 itself</p></dd>
<dt><a name="67">method <b class="cmd">keyword.class</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <i class="arg">body</i></a></dt>
<dd><p>Process an oo::define, clay::define, etc statement.</p></dd>
<dt><a name="68">method <b class="cmd">keyword.class_method</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Process a statement for a clay style class method</p></dd>
<dt><a name="69">method <b class="cmd">keyword.method</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Process a statement for a tcloo style object method</p></dd>
<dt><a name="70">method <b class="cmd">keyword.proc</b> <i class="arg">commentblock</i> <i class="arg">name</i> <i class="arg">arglist</i> <i class="arg">body</i></a></dt>
<dd><p>Process a proc statement</p></dd>
<dt><a name="71">method <b class="cmd">reset</b></a></dt>
<dd><p>Reset the state of the object and its embedded coroutine</p></dd>
<dt><a name="72">method <b class="cmd">Main</b></a></dt>
<dd><p>Main body of the embedded coroutine for the object</p></dd>
<dt><a name="73">method <b class="cmd">section.method</b> <i class="arg">keyword</i> <i class="arg">method</i> <i class="arg">minfo</i></a></dt>
<dd><p>Generate the manual page text for a method or proc</p></dd>
<dt><a name="74">method <b class="cmd">section.class</b> <i class="arg">class_name</i> <i class="arg">class_info</i></a></dt>
<dd><p>Generate the manual page text for a class</p></dd>
<dt><a name="75">method <b class="cmd">section.command</b> <i class="arg">procinfo</i></a></dt>
<dd><p>Generate the manual page text for the commands section</p></dd>
<dt><a name="76">method <b class="cmd">manpage</b> <span class="opt">?<b class="option">header <em>value</em></b>?</span> <span class="opt">?<b class="option">footer <em>value</em></b>?</span> <span class="opt">?<b class="option">authors <em>list</em></b>?</span></a></dt>
<dd><p>Generate the manual page. Returns the completed text suitable for saving in .man file.
 The header argument is a block of doctools text to go in before the machine generated
 section. footer is a block of doctools text to go in after the machine generated
 section. authors is a list of individual authors and emails in the form of AUTHOR EMAIL ?AUTHOR EMAIL?...</p></dd>
<dt><a name="77">method <b class="cmd">scan_text</b> <i class="arg">text</i></a></dt>
<dd><p>Scan a block of text</p></dd>
<dt><a name="78">method <b class="cmd">scan_file</b> <i class="arg">filename</i></a></dt>
<dd><p>Scan a file of text</p></dd>
</dl>
</div>
</div>
<div id="section4" class="doctools_section"><h2><a name="section4">AUTHORS</a></h2>
<p>Sean Woods <a href="mailto:<[email protected]>">mailto:&lt;[email protected]&gt;</a></p>
</div>
<div id="section5" class="doctools_section"><h2><a name="section5">Bugs, Ideas, Feedback</a></h2>
<p>This document, and the package it describes, will undoubtedly contain
bugs and other problems.
Please report such in the category <em>oo</em> of the
<a href="http://core.tcl.tk/tcllib/reportlist">Tcllib Trackers</a>.
Please also report any ideas for enhancements you may have for either
package and/or documentation.</p>
<p>When proposing code changes, please provide <em>unified diffs</em>,
i.e the output of <b class="const">diff -u</b>.</p>
<p>Note further that <em>attachments</em> are strongly preferred over
inlined patches. Attachments can be made by going to the <b class="const">Edit</b>
form of the ticket immediately after its creation, and then using the
left-most button in the secondary navigation bar.</p>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>
<p>TclOO, oo</p>
</div>
<div id="category" class="doctools_section"><h2><a name="category">Category</a></h2>
<p>Programming tools</p>
</div>
<div id="copyright" class="doctools_section"><h2><a name="copyright">Copyright</a></h2>
<p>Copyright &copy; 2018 Sean Woods &lt;[email protected]&gt;</p>
</div>
</div></body></html>

Changes to embedded/www/tcllib/files/modules/cron/cron.html.

129
130
131
132
133
134
135
136
137


138
139
140
141
142
143
144
129
130
131
132
133
134
135


136
137
138
139
140
141
142
143
144







-
-
+
+







<li><a href="#5"><b class="cmd">::cron::object_coroutine</b> <i class="arg">object</i> <i class="arg">coroutine</i> <i class="arg">?info?</i></a></li>
<li><a href="#6"><b class="cmd">::cron::sleep</b> <i class="arg">milliseconds</i></a></li>
<li><a href="#7"><b class="cmd">::cron::task delete</b> <i class="arg">process</i></a></li>
<li><a href="#8"><b class="cmd">::cron::task exists</b> <i class="arg">process</i></a></li>
<li><a href="#9"><b class="cmd">::cron::task info</b> <i class="arg">process</i></a></li>
<li><a href="#10"><b class="cmd">::cron::task set</b> <i class="arg">process</i> <i class="arg">field</i> <i class="arg">value</i> <i class="arg">?field...?</i> <i class="arg">?value...?</i></a></li>
<li><a href="#11"><b class="cmd">::cron::wake</b> <i class="arg">?who?</i></a></li>
<li><a href="#12"><b class="cmd">::cron::clock_step</b> <i class="arg">milleseconds</i></a></li>
<li><a href="#13"><b class="cmd">::cron::clock_delay</b> <i class="arg">milleseconds</i></a></li>
<li><a href="#12"><b class="cmd">::cron::clock_step</b> <i class="arg">milliseconds</i></a></li>
<li><a href="#13"><b class="cmd">::cron::clock_delay</b> <i class="arg">milliseconds</i></a></li>
<li><a href="#14"><b class="cmd">::cron::clock_sleep</b> <i class="arg">seconds</i> <i class="arg">?offset?</i></a></li>
<li><a href="#15"><b class="cmd">::cron::clock_set</b> <i class="arg">newtime</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>The <b class="package">cron</b> package provides a Pure-tcl set of tools to allow
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247

















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

260
261
262


263
264

265
266
267
268
269
270
271
272

273
274
275
276
277
278
279
224
225
226
227
228
229
230

















231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258

259
260


261
262
263

264
265
266
267
268
269
270
271

272
273
274
275
276
277
278
279







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
+

-
-
+
+

-
+







-
+







<dt><a name="8"><b class="cmd">::cron::task exists</b> <i class="arg">process</i></a></dt>
<dd><p>Returns true if <i class="arg">process</i> is registered with cron.</p></dd>
<dt><a name="9"><b class="cmd">::cron::task info</b> <i class="arg">process</i></a></dt>
<dd><p>Returns a dict describing <i class="arg">process</i>. See <b class="cmd">::cron::task set</b> for a description of the options.</p></dd>
<dt><a name="10"><b class="cmd">::cron::task set</b> <i class="arg">process</i> <i class="arg">field</i> <i class="arg">value</i> <i class="arg">?field...?</i> <i class="arg">?value...?</i></a></dt>
<dd><p>If <i class="arg">process</i> does not exist, it is created. Options Include:</p>
<dl class="doctools_definitions">
<b class="cmd"><a href="../../../../index.html#command">command</a></b>
If <b class="cmd"><a href="../coroutine/tcllib_coroutine.html">coroutine</a></b> is black, a global command which implements this process. If <b class="cmd"><a href="../coroutine/tcllib_coroutine.html">coroutine</a></b> is not
black, the command to invoke to create or recreate the coroutine.
<b class="cmd"><a href="../coroutine/tcllib_coroutine.html">coroutine</a></b>
The name of the coroutine (if any) which implements this process.
<b class="cmd">frequency</b>
If -1, this process is terminated after the next event. If 0 this process should be called during every
idle event. If positive, this process should generate events periodically. The frequency is an interger number
of milleseconds between events.
<b class="cmd"><a href="../../../../index.html#object">object</a></b>
The object associated with this process or coroutine.
<b class="cmd">scheduled</b>
If non-zero, the absolute time from the epoch (in milleseconds) that this process will trigger an event.
If zero, and the <b class="cmd">frequency</b> is also zero, this process is called every idle loop.
<b class="cmd"><a href="../../../../index.html#running">running</a></b>
A boolean flag. If true it indicates the process never returned or yielded during the event loop,
and will not be called again until it does so.
<dt><b class="cmd"><a href="../../../../index.html#command">command</a></b></dt>
<dd><p>If <b class="cmd"><a href="../coroutine/tcllib_coroutine.html">coroutine</a></b> is black, a global command which implements this process. If <b class="cmd"><a href="../coroutine/tcllib_coroutine.html">coroutine</a></b> is not
black, the command to invoke to create or recreate the coroutine.</p></dd>
<dt><b class="cmd"><a href="../coroutine/tcllib_coroutine.html">coroutine</a></b></dt>
<dd><p>The name of the coroutine (if any) which implements this process.</p></dd>
<dt><b class="cmd">frequency</b></dt>
<dd><p>If -1, this process is terminated after the next event. If 0 this process should be called during every
idle event. If positive, this process should generate events periodically. The frequency is an integer number
of milliseconds between events.</p></dd>
<dt><b class="cmd"><a href="../../../../index.html#object">object</a></b></dt>
<dd><p>The object associated with this process or coroutine.</p></dd>
<dt><b class="cmd">scheduled</b></dt>
<dd><p>If non-zero, the absolute time from the epoch (in milliseconds) that this process will trigger an event.
If zero, and the <b class="cmd">frequency</b> is also zero, this process is called every idle loop.</p></dd>
<dt><b class="cmd"><a href="../../../../index.html#running">running</a></b></dt>
<dd><p>A boolean flag. If true it indicates the process never returned or yielded during the event loop,
and will not be called again until it does so.</p></dd>
</dl></dd>
<dt><a name="11"><b class="cmd">::cron::wake</b> <i class="arg">?who?</i></a></dt>
<dd><p>Wake up cron, and arrange for its event loop to be run during the next Idle cycle.</p>
<pre class="doctools_example">
::cron::wake {I just did something important}
</pre>
</dd>
</dl>
<p>Several utility commands are provided that are used internally within cron and for
testing cron, but may or may not be useful in the general cases.</p>
<dl class="doctools_definitions">
<dt><a name="12"><b class="cmd">::cron::clock_step</b> <i class="arg">milleseconds</i></a></dt>
<dt><a name="12"><b class="cmd">::cron::clock_step</b> <i class="arg">milliseconds</i></a></dt>
<dd><p>Return a clock time absolute to the epoch which falls on the next
border between one second and the next for the value of <i class="arg">milleseconds</i></p></dd>
<dt><a name="13"><b class="cmd">::cron::clock_delay</b> <i class="arg">milleseconds</i></a></dt>
border between one second and the next for the value of <i class="arg">milliseconds</i></p></dd>
<dt><a name="13"><b class="cmd">::cron::clock_delay</b> <i class="arg">milliseconds</i></a></dt>
<dd><p>Return a clock time absolute to the epoch which falls on the next
border between one second and the next <i class="arg">milleseconds</i> in the future.</p></dd>
border between one second and the next <i class="arg">milliseconds</i> in the future.</p></dd>
<dt><a name="14"><b class="cmd">::cron::clock_sleep</b> <i class="arg">seconds</i> <i class="arg">?offset?</i></a></dt>
<dd><p>Return a clock time absolute to the epoch which falls exactly <i class="arg">seconds</i> in
the future. If offset is given it may be positive or negative, and will shift
the final time to before or after the second would flip.</p></dd>
<dt><a name="15"><b class="cmd">::cron::clock_set</b> <i class="arg">newtime</i></a></dt>
<dd><p>Sets the internal clock for cron. This command will advance the time in 100ms
increment, triggering events, until the internal time catches up with <i class="arg">newtime</i>.</p>
<p><i class="arg">newtime</i> is expressed in absolute milleseconds since the beginning of the epoch.</p></dd>
<p><i class="arg">newtime</i> is expressed in absolute milliseconds since the beginning of the epoch.</p></dd>
</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">Bugs, Ideas, Feedback</a></h2>
<p>This document, and the package it describes, will undoubtedly contain
bugs and other problems.
Please report such in the category <em>odie</em> of the
<a href="http://core.tcl.tk/tcllib/reportlist">Tcllib Trackers</a>.

Changes to embedded/www/tcllib/files/modules/doctools/cvs.html.

166
167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
166
167
168
169
170
171
172


173
174
175
176
177
178
179
180







-
-
+







<dt>varname <i class="arg">fvar</i> (in)</dt>
<dd><p>Has to refer to an array variable. Keys are strings containing
date, author of a log entry, and a comment for that entry, in this
order, separated by commas.</p>
<p>The values are lists of the files the entry is touching.</p></dd>
</dl></dd>
<dt><a name="2"><b class="cmd">::doctools::cvs::toChangeLog</b> <i class="arg">evar</i> <i class="arg">cvar</i> <i class="arg">fvar</i></a></dt>
<dd><p>]
The three arguments for this command are the same as the last three
<dd><p>The three arguments for this command are the same as the last three
arguments of the command <b class="cmd">::doctools::cvs::scanLog</b>. This command
however expects them to be filled with information about one or more
logs. It takes this information and converts it into a text in the
format of a ChangeLog as accepted and generated by <b class="syscmd"><a href="../../../../index.html#emacs">emacs</a></b>. The
constructed text is returned as the result of the command.</p></dd>
</dl>
</div>

Changes to embedded/www/tcllib/files/modules/doctools/doctools_lang_intro.html.

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
216
217
218
219
220
221
222










223
224
225
226
227
228
229







-
-
-
-
-
-
-
-
-
-







[<b class="cmd">require   PACKAGE</b>]
[description]
[manpage_end]
</pre>
<p>Remember that the whitespace is optional. The document</p>
<pre class="doctools_example">
    [manpage_begin NAME SECTION VERSION]
[see_also doctools_intro]
[see_also doctools_lang_cmdref]
[see_also doctools_lang_faq]
[see_also doctools_lang_syntax]
[keywords {doctools commands}]
[keywords {doctools language}]
[keywords {doctools markup}]
[keywords {doctools syntax}]
[keywords markup]
[keywords {semantic markup}]
    [copyright {YEAR AUTHOR}][titledesc TITLE][moddesc MODULE_TITLE]
    [require PACKAGE VERSION][require PACKAGE][description]
    [vset CATEGORY doctools]
[include ../doctools2base/include/feedback.inc]
[manpage_end]
</pre>
<p>has the same meaning as the example before.</p>
420
421
422
423
424
425
426
427

428
429
430
431
432
433
434
410
411
412
413
414
415
416

417
418
419
420
421
422
423
424







-
+







<p>The example demonstrating the use of text markup is an excerpt from
the <i class="term"><a href="doctools_lang_cmdref.html">doctools language command reference</a></i>, with some
highlighting added.
It shows their use within a block of text, as the arguments of a list
item command (<b class="cmd">call</b>), and our ability to nest them.</p>
<pre class="doctools_example">
  ...
  [call [<b class="cmd">cmd arg_def</b>] [<b class="cmd">arg type</b>] [<b class="cmd">arg name</b>]] [<b class="cmd">opt</b> [<b class="cmd">arg mode</b>]]]
  [call [<b class="cmd">cmd arg_def</b>] [<b class="cmd">arg type</b>] [<b class="cmd">arg name</b>] [<b class="cmd">opt</b> [<b class="cmd">arg mode</b>]]]
  Text structure. List element. Argument list. Automatically closes the
  previous list element. Specifies the data-[<b class="cmd">arg type</b>] of the described
  argument of a command, its [<b class="cmd">arg name</b>] and its i/o-[<b class="cmd">arg mode</b>]. The
  latter is optional.
  ...
</pre>
</div>

Changes to embedded/www/tcllib/files/modules/fumagic/cfront.html.

137
138
139
140
141
142
143
144
145
146





147
148
149
150
151
152
153
137
138
139
140
141
142
143



144
145
146
147
148
149
150
151
152
153
154
155







-
-
-
+
+
+
+
+







into recognizers based on the <b class="package"><a href="rtcore.html">fileutil::magic::rt</a></b> recognizer
runtime package. For the generator backed used by this compiler see
the package <b class="package"><a href="cgen.html">fileutil::magic::cgen</a></b>.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">COMMANDS</a></h2>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">::fileutil::magic::cfront::compile</b> <i class="arg">path</i>...</a></dt>
<dd><p>This command takes the paths of one or more files and directories and
compiles all the files, and the files in all the directories into a
single recognizer for all the file types specified in these files.</p>
<dd><p>This command takes the paths of one or more files and directories and compiles
all the files, and the files in all the directories into a single analyzer for
all the file types specified in these files.  It returns a list whose first
item is a list per-file dictionaries of analyzer scripts and whose second item
is a list of analyzer commands.</p>
<p>All the files have to be in the format specified by magic(5).</p>
<p>The result of the command is a Tcl script containing the generated
recognizer.</p></dd>
<dt><a name="2"><b class="cmd">::fileutil::magic::cfront::procdef</b> <i class="arg">procname</i> <i class="arg">path</i>...</a></dt>
<dd><p>This command behaves like <b class="cmd">::fileutil::magic::cfront::compile</b>
with regard to the specified path arguments, then wraps the resulting
recognizer script into a procedure named <i class="arg">procname</i>, puts code

Changes to embedded/www/tcllib/files/modules/fumagic/rtcore.html.

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







135
136

137
138
139
140
141
142
143
144




145
146
147
148
149
150
151
152

153
154
155
156
157
158
159
160

161
162
163
164
165
166
167
168







169
170
171
172



173
174
175
176
177
178

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
201
202
203
204
205
206



207
208
209
210



211
212

213
214
215

216
217
218
219
220
221
222
223


224
225
226



227
228
229


230
231

232
233
234
235
236
237
238
239
240
241
242


243
244
245
246
247
248
249
250

251
252
253
254
255
256
257


258
259

260
261
262
263
264
265
266
267




268
269
270
271
272
273



274
275
276
277
278
279
280
121
122
123
124
125
126
127







128
129
130
131
132
133
134


135








136
137
138
139
140
141
142
143
144
145


146

147
148
149
150
151
152

153
154







155
156
157
158
159
160
161




162
163
164






165
166
167













168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185



186
187
188


189



190








191
192



193
194
195



196
197
198

199
200
201
202
203







204
205








206







207
208
209

210








211
212
213
214






215
216
217
218
219
220
221
222
223
224







-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
-
-
-
+
+
+
+






-
-
+
-






-
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
+
+
+
-
-
-
-
-
-
+


-
-
-
-
-
-
-
-
-
-
-
-
-
+













+
+
+

-
-
-
+
+
+
-
-
+
-
-
-
+
-
-
-
-
-
-
-
-
+
+
-
-
-
+
+
+
-
-
-
+
+

-
+




-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
+

-
+
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+







<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl 8.5</b></li>
<li>package require <b class="pkgname">fileutil::magic::rt <span class="opt">?2.0?</span></b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><b class="cmd">::fileutil::magic::rt::&gt;</b></a></li>
<li><a href="#2"><b class="cmd">::fileutil::magic::rt::&lt;</b></a></li>
<li><a href="#3"><b class="cmd">::fileutil::magic::rt::open</b> <i class="arg">filename</i></a></li>
<li><a href="#4"><b class="cmd">::fileutil::magic::rt::close</b></a></li>
<li><a href="#5"><b class="cmd">::fileutil::magic::rt::file_start</b> <i class="arg">name</i></a></li>
<li><a href="#6"><b class="cmd">::fileutil::magic::rt::result</b> <span class="opt">?<i class="arg">msg</i>?</span></a></li>
<li><a href="#7"><b class="cmd">::fileutil::magic::rt::resultv</b> <span class="opt">?<i class="arg">msg</i>?</span></a></li>
<li><a href="#8"><b class="cmd">::fileutil::magic::rt::emit</b> <i class="arg">msg</i></a></li>
<li><a href="#9"><b class="cmd">::fileutil::magic::rt::offset</b> <i class="arg">where</i></a></li>
<li><a href="#3"><b class="cmd">::fileutil::magic::rt::new</b> <i class="arg">chan</i> <i class="arg">named</i> <i class="arg">analyze</i></a></li>
<li><a href="#4"><b class="cmd">::fileutil::magic::rt::file_start</b> <i class="arg">name</i></a></li>
<li><a href="#5"><b class="cmd">::fileutil::magic::rt::emit</b> <i class="arg">msg</i></a></li>
<li><a href="#6"><b class="cmd">::fileutil::magic::rt::O</b> <i class="arg">where</i></a></li>
<li><a href="#7"><b class="cmd">::fileutil::magic::rt::R</b> <i class="arg">where</i></a></li>
<li><a href="#8"><b class="cmd">::fileutil::magic::rt::Nv</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">compinvert</i> <i class="arg">comp</i> <i class="arg">expected</i></a></li>
<li><a href="#9"><b class="cmd">::fileutil::magic::rt::N</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">testinvert</i> <i class="arg">compinvert</i> <i class="arg">mod</i> <i class="arg">mand</i> <i class="arg">comp</i> <i class="arg">expected</i></a></li>
<li><a href="#10"><b class="cmd">::fileutil::magic::rt::Nv</b> <i class="arg">type</i> <i class="arg">offset</i> <span class="opt">?<i class="arg">qual</i>?</span></a></li>
<li><a href="#11"><b class="cmd">::fileutil::magic::rt::N</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></li>
<li><a href="#10"><b class="cmd">::fileutil::magic::rt::S</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">testinvert</i> <i class="arg">mod</i> <i class="arg">mand</i> <i class="arg">comp</i> <i class="arg">val</i></a></li>
<li><a href="#12"><b class="cmd">::fileutil::magic::rt::Nvx</b> <i class="arg">type</i> <i class="arg">offset</i> <span class="opt">?<i class="arg">qual</i>?</span></a></li>
<li><a href="#13"><b class="cmd">::fileutil::magic::rt::Nx</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></li>
<li><a href="#14"><b class="cmd">::fileutil::magic::rt::S</b> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></li>
<li><a href="#15"><b class="cmd">::fileutil::magic::rt::Sx</b> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></li>
<li><a href="#16"><b class="cmd">::fileutil::magic::rt::L</b> <i class="arg">newlevel</i></a></li>
<li><a href="#17"><b class="cmd">::fileutil::magic::rt::I</b> <i class="arg">base</i> <i class="arg">type</i> <i class="arg">delta</i></a></li>
<li><a href="#18"><b class="cmd">::fileutil::magic::rt::R</b> <i class="arg">offset</i></a></li>
<li><a href="#19"><b class="cmd">::fileutil::magic::rt::U</b> <i class="arg">fileindex</i> <i class="arg">name</i></a></li>
<li><a href="#11"><b class="cmd">::fileutil::magic::rt::L</b> <i class="arg">newlevel</i></a></li>
<li><a href="#12"><b class="cmd">::fileutil::magic::rt::I</b> <i class="arg">offset</i> <i class="arg">it</i> <i class="arg">ioi</i> <i class="arg">ioo</i> <i class="arg">iir</i> <i class="arg">io</i></a></li>
<li><a href="#13"><b class="cmd">::fileutil::magic::rt::R</b> <i class="arg">offset</i></a></li>
<li><a href="#14"><b class="cmd">::fileutil::magic::rt::U</b> <i class="arg">fileindex</i> <i class="arg">name</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>This package provides the runtime core for file type recognition
engines written in pure Tcl and is thus used by all other packages in
this module, i.e. the two frontend packages
<b class="package">fileutil::magic::mimetypes</b> and
this module such as <b class="package"><a href="filetypes.html">fileutil::magic::filetype</a></b> and the two compiler
<b class="package">fileutil::magic::filetypes</b>, and the two engine compiler
packages <b class="package"><a href="cgen.html">fileutil::magic::cgen</a></b> and
<b class="package"><a href="cfront.html">fileutil::magic::cfront</a></b>.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">COMMANDS</a></h2>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">::fileutil::magic::rt::&gt;</b></a></dt>
<dd><p>Shorthand for <b class="cmd">incr level</b>.</p></dd>
<dd><p>Increment the level and perform related housekeeping</p></dd>
<dt><a name="2"><b class="cmd">::fileutil::magic::rt::&lt;</b></a></dt>
<dd><p>Shorthand for <b class="cmd">incr level -1</b>.</p></dd>
<dt><a name="3"><b class="cmd">::fileutil::magic::rt::open</b> <i class="arg">filename</i></a></dt>
<dd><p>This command initializes the runtime and prepares the file
<i class="arg">filename</i> for use by the system.
This command has to be invoked first, before any other command of this
package.</p>
<p>The command returns the channel handle of the opened file as its
<dd><p>Decrement the level and perform related housekeeping</p></dd>
<dt><a name="3"><b class="cmd">::fileutil::magic::rt::new</b> <i class="arg">chan</i> <i class="arg">named</i> <i class="arg">analyze</i></a></dt>
<dd><p>Create a new command which returns one description of the file each time it is
called, and a code of <i class="arg">break</i> when there are no more descriptions.
<i class="arg">chan</i> is the channel containing the data to describe.  The channel
configuration is then managed as needed.
<i class="arg">named</i> is a dictionary of named tests, as generated by
result.</p></dd>
<dt><a name="4"><b class="cmd">::fileutil::magic::rt::close</b></a></dt>
<dd><p>This command closes the last file opened via
<b class="cmd">::fileutil::magic::rt::open</b> and shuts the runtime down.
<b class="cmd">fileutil::magic::cfront::compile</b>.
<i class="arg">test</i> is a command prefix for a routine composed of the list of commands
as returned by <b class="cmd">fileutil::magic::cfront::compile</b>.</p></dd>
This command has to be invoked last, after the file has been dealt
with completely.
Afterward another invokation of <b class="cmd">::fileutil::magic::rt::open</b>  is
required to process another file.</p>
<p>This command returns the empty string as its result.</p></dd>
<dt><a name="5"><b class="cmd">::fileutil::magic::rt::file_start</b> <i class="arg">name</i></a></dt>
<dt><a name="4"><b class="cmd">::fileutil::magic::rt::file_start</b> <i class="arg">name</i></a></dt>
<dd><p>This command marks the start of a magic file when debugging. It
returns the empty string as its result.</p></dd>
<dt><a name="6"><b class="cmd">::fileutil::magic::rt::result</b> <span class="opt">?<i class="arg">msg</i>?</span></a></dt>
<dd><p>This command returns the current result and stops processing.</p>
<p>If <i class="arg">msg</i> is specified its text is added to the result before it is
returned. See <b class="cmd">::fileutil::magic::rt::emit</b> for the allowed
special character sequences.</p></dd>
<dt><a name="7"><b class="cmd">::fileutil::magic::rt::resultv</b> <span class="opt">?<i class="arg">msg</i>?</span></a></dt>
<dd><p>This command returns the current result.
In contrast to <b class="cmd">::fileutil::magic::rt::result</b> processing
continues.</p>
<p>If <i class="arg">msg</i> is specified its text is added to the result before it is
returned. See <b class="cmd">::fileutil::magic::rt::emit</b> for the allowed
special character sequences.</p></dd>
<dt><a name="8"><b class="cmd">::fileutil::magic::rt::emit</b> <i class="arg">msg</i></a></dt>
<dt><a name="5"><b class="cmd">::fileutil::magic::rt::emit</b> <i class="arg">msg</i></a></dt>
<dd><p>This command adds the text <i class="arg">msg</i> to the result buffer. The
message may contain the following special character sequences. They
will be replaced with buffered values before the message is added to
the result. The command returns the empty string as its result.</p>
<dl class="doctools_definitions">
<dt><b class="const">\b</b></dt>
<dd><p>This sequence is removed</p></dd>
<dt><b class="const">%s</b></dt>
<dd><p>Replaced with the last buffered string value.</p></dd>
<dt><b class="const">%ld</b></dt>
<dd><p>Replaced with the last buffered numeric value.</p></dd>
<dt><b class="const">%d</b></dt>
<dd><p>See above.</p></dd>
<dt><b class="const">${x:...?...}</b></dt>
<dd><p>Substitute one string if the file is executable, and
another string otherwise.</p></dd>
</dl></dd>
<dt><a name="10"><b class="cmd">::fileutil::magic::rt::Nv</b> <i class="arg">type</i> <i class="arg">offset</i> <span class="opt">?<i class="arg">qual</i>?</span></a></dt>
<dd><p>This command fetches the numeric value with <i class="arg">type</i> from the
absolute location <i class="arg">offset</i> and returns it as its result. The
<dt><a name="6"><b class="cmd">::fileutil::magic::rt::O</b> <i class="arg">where</i></a></dt>
<dd><p>Produce an offset from <i class="arg">where</i>, relative to the cursor one level up.
Produce an offset from <i class="arg">where</i>, relative to the offset one level up.</p></dd>
fetched value is further stored in the numeric buffer.</p>
<p>If <i class="arg">qual</i> is specified it is considered to be a mask and applied
<dt><a name="8"><b class="cmd">::fileutil::magic::rt::Nv</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">compinvert</i> <i class="arg">comp</i> <i class="arg">expected</i></a></dt>
to the fetched value before it is stored and returned. It has to have
the form of a partial Tcl bit-wise expression, i.e.</p>
<pre class="doctools_example">
<dd><p>A limited form of <b class="cmd">::fileutile::magic::rt::N</b> that only checks for
	&amp; number
</pre>
<p>For example:</p>
<pre class="doctools_example">
	Nv lelong 0 &amp;0x8080ffff
</pre>
<p>For the possible types see section <span class="sectref"><a href="#section3">NUMERIC TYPES</a></span>.</p></dd>
<dt><a name="11"><b class="cmd">::fileutil::magic::rt::N</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></dt>
equality and can't be told to invert the test.</p></dd>
<dt><a name="9"><b class="cmd">::fileutil::magic::rt::N</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">testinvert</i> <i class="arg">compinvert</i> <i class="arg">mod</i> <i class="arg">mand</i> <i class="arg">comp</i> <i class="arg">expected</i></a></dt>
<dd><p>This command behaves mostly like <b class="cmd">::fileutil::magic::rt::Nv</b>,
except that it compares the fetched and masked value against <i class="arg">val</i>
as specified with <i class="arg">comp</i> and returns the result of that
<dd><p>Fetch the numeric value with <i class="arg">type</i> from the absolute location
<i class="arg">offset</i>, compare it with <i class="arg">expected</i> using <i class="arg">comp</i> as the comparision
operator,  and returns the result.</p>
comparison.</p>
<p>The argument <i class="arg">comp</i> has to contain one of Tcl's comparison
operators, and the comparison made will be</p>
<p>The argument <i class="arg">comp</i> must be one of Tcl's comparison
operators.</p>
<pre class="doctools_example">
	&lt;val&gt; &lt;comp&gt; &lt;fetched-and-masked-value&gt;
	&lt;comp&gt; &lt;fetched-and-masked-value&gt; &lt;comp&gt; &lt;expected&gt;
</pre>
<p>The special comparison operator <b class="const">x</b> signals that no comparison
should be done, or, in other words, that the fetched value will always
match <i class="arg">val</i>.</p></dd>
<dt><a name="12"><b class="cmd">::fileutil::magic::rt::Nvx</b> <i class="arg">type</i> <i class="arg">offset</i> <span class="opt">?<i class="arg">qual</i>?</span></a></dt>
<dd><p>This command behaves like <b class="cmd">::fileutil::magic::rt::Nv</b>, except that
it additionally remembers the location in the file after the fetch in
the calling context, for the current level, for later use by
<b class="cmd">::fileutil::magic::rt::R</b>.</p></dd>
<dt><a name="13"><b class="cmd">::fileutil::magic::rt::Nx</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></dt>
<dd><p>This command behaves like <b class="cmd">::fileutil::magic::rt::N</b>, except that
<dt><a name="10"><b class="cmd">::fileutil::magic::rt::S</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">testinvert</i> <i class="arg">mod</i> <i class="arg">mand</i> <i class="arg">comp</i> <i class="arg">val</i></a></dt>
<dd><p>Like <b class="cmd">::fileutil::magic::rt::N</b> except that it fetches and compares string
it additionally remembers the location in the file after the fetch in
the calling context, for the current, for later use by
<b class="cmd">::fileutil::magic::rt::R</b>.</p></dd>
<dt><a name="14"><b class="cmd">::fileutil::magic::rt::S</b> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></dt>
<dd><p>This command behaves like <b class="cmd">::fileutil::magic::rt::N</b>, except that
it fetches and compares strings, not numeric data. The fetched value
is also stored in the internal string buffer instead of the numeric
buffer.</p></dd>
types , not numeric data.</p></dd>
<dt><a name="15"><b class="cmd">::fileutil::magic::rt::Sx</b> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></dt>
<dd><p>This command behaves like <b class="cmd">::fileutil::magic::rt::S</b>, except that
it additionally remembers the location in the file after the fetch in
the calling context, for the current level, for later use by
<b class="cmd">::fileutil::magic::rt::R</b>.</p></dd>
<dt><a name="16"><b class="cmd">::fileutil::magic::rt::L</b> <i class="arg">newlevel</i></a></dt>
<dd><p>This command sets the current level in the calling context to
<dt><a name="11"><b class="cmd">::fileutil::magic::rt::L</b> <i class="arg">newlevel</i></a></dt>
<dd><p>Sets the current level in the calling context to
<i class="arg">newlevel</i>. The command returns the empty string as its result.</p></dd>
<dt><a name="17"><b class="cmd">::fileutil::magic::rt::I</b> <i class="arg">base</i> <i class="arg">type</i> <i class="arg">delta</i></a></dt>
<dt><a name="12"><b class="cmd">::fileutil::magic::rt::I</b> <i class="arg">offset</i> <i class="arg">it</i> <i class="arg">ioi</i> <i class="arg">ioo</i> <i class="arg">iir</i> <i class="arg">io</i></a></dt>
<dd><p>This command handles base locations specified indirectly through the
contents of the inspected file. It returns the sum of <i class="arg">delta</i> and
the value of numeric <i class="arg">type</i> fetched from the absolute location
<i class="arg">base</i>.</p>
<p>For the possible types see section <span class="sectref"><a href="#section3">NUMERIC TYPES</a></span>.</p></dd>
<dt><a name="18"><b class="cmd">::fileutil::magic::rt::R</b> <i class="arg">offset</i></a></dt>
<dd><p>This command handles base locations specified relative to the end of
the last field one level above.</p>
<dd><p>Calculates an offset based on an initial offset and the provided modifiers.</p></dd>
<dt><a name="13"><b class="cmd">::fileutil::magic::rt::R</b> <i class="arg">offset</i></a></dt>
<dd><p>Given an initial offset, calculates an offset relative to the cursor at the
next level up. The cursor is the position in the data one character after the
<p>In other words, the command computes an absolute location in the file
based on the relative <i class="arg">offset</i> and returns it as its result. The
base the offset is added to is the last location remembered for the
level in the calling context.</p></dd>
<dt><a name="19"><b class="cmd">::fileutil::magic::rt::U</b> <i class="arg">fileindex</i> <i class="arg">name</i></a></dt>
<dd><p>Use a named test script at the current level.</p></dd>
data extracted from the file one level up.</p></dd>
<dt><a name="14"><b class="cmd">::fileutil::magic::rt::U</b> <i class="arg">fileindex</i> <i class="arg">name</i></a></dt>
<dd><p>Add a level and use a named test script.</p></dd>
</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">NUMERIC TYPES</a></h2>
<dl class="doctools_definitions">
<dt><b class="const">byte</b></dt>
<dd><p>8-bit integer</p></dd>
<dt><b class="const">short</b></dt>

Changes to embedded/www/tcllib/files/modules/httpd/httpd.html.

1

2
3


4
5
6
7
8
9
10
1
2


3
4
5
6
7
8
9
10
11

+
-
-
+
+








<!DOCTYPE html><html><head>
<div class='fossil-doc' data-title='tool - Tcl Web Server'>
<style>
<title>httpd - Tcl Web Server</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
	background: 	#FFFFFF;
	color:	 	black;
85
86
87
88
89
90
91
92
93
94



95
96


97
98
99
100
101
102





103
104

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

























130
131
132
133
134
135
136
137
138
139
140
141



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201




































































































202
203
204
205
206
207
208
209
210
211
212
213
214
215

216
217
218
219



220
221
222
223



224














225
226
227



228
229
230
231
232



233
234
235
236
237
238



239
240

241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257






258
259
260
261
262
263
264
265








266
267
268
269


270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285

286
287
288


289
290
291
292
293
294
295
296







297

298
299
300
301

302
303
304


305
306
307
308
309
310
311






312
313
314
315
316
317















318
319
320
321



322
323
324
325
326








327
328
329
330




331
332
333
334
335
336
337
338








339
340
341




342












343
344
345
346


347
348

349
350




351
352
353
354
355
356
357
358
359
360
361
362
363
















































364
365
366
367


368
369
370
371

372
373
374
375
376
377
378

379
380
381
382

383
384
385
386
387
388
389
390
391
392
393
394
395

396
397
398




399
400
401


402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424



425
426

427
428

429
430
431

432
433
434

435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454

455
456
457








458
459
460


461
462
463
464
465




466
467
468



469
470


471
472

473
474

















475
476
477
478
479
480
481
482
483






484
485
486






487
488
489
490
491





492
493
494
495
496



















497
498
499
500
501
502
503
504











































505
506
507

508





509
510
511
512
513




514
515


516

517
518
















519
520
521


522
523
524
525















526

















527

528

529
530
531

532
533
534
535
536
537
538


















539














540











541

542
543

544
545
546










547
548





549



550
551
552








553


554
555
556









557

558

559
560
561

562
563
564
565
566
567
568
569
570
571
572
573
574
575
576

577
578
579
580
581
582
583
584

86
87
88
89
90
91
92



93
94
95


96
97






98
99
100
101
102
103

104
105
106
107
108
109
110
111


















112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145



146
147
148



149
150
151
152
153
154
155
156
157
158















































159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271

272
273



274
275
276
277
278


279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297


298
299
300

301



302
303
304






305
306
307


308

















309
310
311
312
313
314








315
316
317
318
319
320
321
322




323
324
















325
326
327

328
329
330







331
332
333
334
335
336
337
338
339
340
341
342

343
344


345
346
347
348





349
350
351
352
353
354






355
356
357
358
359
360
361
362
363
364
365
366
367
368
369




370
371
372





373
374
375
376
377
378
379
380




381
382
383
384








385
386
387
388
389
390
391
392



393
394
395
396

397
398
399
400
401
402
403
404
405
406
407
408




409
410


411

412
413
414
415
416













417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464




465
466




467







468
469



470













471



472
473
474
475



476
477























478
479
480


481


482



483



484
485



















486
487


488
489
490
491
492
493
494
495
496


497
498
499




500
501
502
503
504
505
506
507
508
509


510
511


512


513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529









530
531
532
533
534
535



536
537
538
539
540
541





542
543
544
545
546





547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565








566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610

611

612
613
614
615
616
617




618
619
620
621
622
623
624
625

626


627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643


644
645




646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680

681



682
683






684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728

729


730
731


732
733
734
735
736
737
738
739
740
741


742
743
744
745
746
747
748
749
750



751
752
753
754
755
756
757
758
759
760
761



762
763
764
765
766
767
768
769
770
771
772

773
774
775

776
777
778
779
780
781
782
783
784
785
786
787
788
789
790

791
792
793
794
795
796
797
798

799







-
-
-
+
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+

-
+







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
-
-
+
+
+
-
-
-










-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+













-
+

-
-
-
+
+
+


-
-
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+
-

-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+


-
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+

+



-
+

-
-
+
+


-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
-
-
+
-

+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
-
-
-
-
+
-
-
-
-
-
-
-
+

-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
+
-
-
+
-
-
-
+
-
-
-
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+

-
-
+
+
+
+
+
+
+
+

-
-
+
+

-
-
-
-
+
+
+
+



+
+
+
-
-
+
+
-
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+
-
+
+
+
+
+

-
-
-
-
+
+
+
+


+
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
+
-
-
-
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
+

-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+

+
+
+
-
-
-
+
+
+
+
+
+
+
+

+
+
-
-
-
+
+
+
+
+
+
+
+
+

+
-
+


-
+














-
+







-
+
	margin-top: 	1em;
	border-top:	1px solid black;
    }
    UL.doctools_requirements {
	margin-bottom: 	1em;
	border-bottom:	1px solid black;
    }
</style>
 <hr> [
   <a href="../../../../toc.html">Main Table Of Contents</a>
--></style>
</head>
<!-- Generated from file 'httpd.man' by tcllib/doctools with format 'html'
| <a href="../../../toc.html">Table Of Contents</a>
| <a href="../../../../index.html">Keyword Index</a>
   -->
<!-- Copyright &amp;copy; 2018 Sean Woods &amp;lt;yoda@etoyoc.com&amp;gt;
| <a href="../../../../toc0.html">Categories</a>
| <a href="../../../../toc1.html">Modules</a>
| <a href="../../../../toc2.html">Applications</a>
 ] <hr>
<div class="doctools">
<h1 class="doctools_title">tool(n) 4.1.1 tcllib &quot;Tcl Web Server&quot;</h1>
   -->
<!-- httpd.n
   -->
<body><div class="doctools">
<h1 class="doctools_title">httpd(n) 4.3 httpd &quot;Tcl Web Server&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>tool - A TclOO and coroutine based web server</p>
<p>httpd - A TclOO and coroutine based web server</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a></li>
<li class="doctools_section"><a href="#section2">Minimal Example</a></li>
<li class="doctools_section"><a href="#section3">Class ::httpd::server</a></li>
<li class="doctools_section"><a href="#section4">Class ::httpd::reply</a></li>
<li class="doctools_section"><a href="#section5">Reply Method Ensembles</a></li>
<li class="doctools_section"><a href="#section6">Reply Method Ensemble: http_info</a></li>
<li class="doctools_section"><a href="#section7">Reply Method Ensemble: request</a></li>
<li class="doctools_section"><a href="#section8">Reply Method Ensemble: reply</a></li>
<li class="doctools_section"><a href="#section9">Reply Methods</a></li>
<li class="doctools_section"><a href="#section10">Class ::httpd::content</a></li>
<li class="doctools_section"><a href="#section11">Class ::httpd::content.cgi</a></li>
<li class="doctools_section"><a href="#section12">Class ::httpd::content.file</a></li>
<li class="doctools_section"><a href="#section13">Class ::httpd::content.proxy</a></li>
<li class="doctools_section"><a href="#section14">Class ::httpd::content.scgi</a></li>
<li class="doctools_section"><a href="#section15">Class ::httpd::content.websocket</a></li>
<li class="doctools_section"><a href="#section16">SCGI Server Functions</a></li>
<li class="doctools_section"><a href="#section17">Class ::httpd::reply.scgi</a></li>
<li class="doctools_section"><a href="#section18">Class ::httpd::server.scgi</a></li>
<li class="doctools_section"><a href="#section19">AUTHORS</a></li>
<li class="doctools_section"><a href="#section20">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#section3">Classes</a>
<ul>
<li class="doctools_subsection"><a href="#subsection1">Class  httpd::mime</a></li>
<li class="doctools_subsection"><a href="#subsection2">Class  httpd::reply</a></li>
<li class="doctools_subsection"><a href="#subsection3">Class  httpd::server</a></li>
<li class="doctools_subsection"><a href="#subsection4">Class  httpd::server::dispatch</a></li>
<li class="doctools_subsection"><a href="#subsection5">Class  httpd::content.redirect</a></li>
<li class="doctools_subsection"><a href="#subsection6">Class  httpd::content.cache</a></li>
<li class="doctools_subsection"><a href="#subsection7">Class  httpd::content.template</a></li>
<li class="doctools_subsection"><a href="#subsection8">Class  httpd::content.file</a></li>
<li class="doctools_subsection"><a href="#subsection9">Class  httpd::content.exec</a></li>
<li class="doctools_subsection"><a href="#subsection10">Class  httpd::content.proxy</a></li>
<li class="doctools_subsection"><a href="#subsection11">Class  httpd::content.cgi</a></li>
<li class="doctools_subsection"><a href="#subsection12">Class  httpd::protocol.scgi</a></li>
<li class="doctools_subsection"><a href="#subsection13">Class  httpd::content.scgi</a></li>
<li class="doctools_subsection"><a href="#subsection14">Class  httpd::server.scgi</a></li>
<li class="doctools_subsection"><a href="#subsection15">Class  httpd::content.websocket</a></li>
<li class="doctools_subsection"><a href="#subsection16">Class  httpd::plugin</a></li>
<li class="doctools_subsection"><a href="#subsection17">Class  httpd::plugin.dict_dispatch</a></li>
<li class="doctools_subsection"><a href="#subsection18">Class  httpd::reply.memchan</a></li>
<li class="doctools_subsection"><a href="#subsection19">Class  httpd::plugin.local_memchan</a></li>
</ul>
</li>
<li class="doctools_section"><a href="#section4">AUTHORS</a></li>
<li class="doctools_section"><a href="#section5">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
<li class="doctools_section"><a href="#category">Category</a></li>
<li class="doctools_section"><a href="#copyright">Copyright</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl 8.6</b></li>
<li>package require <b class="pkgname">httpd <span class="opt">?4.1.1?</span></b></li>
<li>package require <b class="pkgname">sha1</b></li>
<li>package require <b class="pkgname">dicttool</b></li>
<li>package require <b class="pkgname">httpd <span class="opt">?4.3?</span></b></li>
<li>package require <b class="pkgname">uuid</b></li>
<li>package require <b class="pkgname">clay</b></li>
<li>package require <b class="pkgname">oo::meta</b></li>
<li>package require <b class="pkgname">oo::dialect</b></li>
<li>package require <b class="pkgname">tool</b></li>
<li>package require <b class="pkgname">coroutine</b></li>
<li>package require <b class="pkgname">fileutil</b></li>
<li>package require <b class="pkgname">fileutil::magic::filetype</b></li>
<li>package require <b class="pkgname">websocket</b></li>
<li>package require <b class="pkgname">mime</b></li>
<li>package require <b class="pkgname">cron</b></li>
<li>package require <b class="pkgname">uri</b></li>
<li>package require <b class="pkgname">Markdown</b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1">constructor ?port <span class="opt">?port?</span>? ?myaddr <span class="opt">?ipaddr?</span>|all? ?server_string <span class="opt">?string?</span>? ?server_name <span class="opt">?string?</span>?</a></li>
<li><a href="#2">method <b class="cmd">add_uri</b> <i class="arg">pattern</i> <i class="arg">dict</i></a></li>
<li><a href="#3">method <b class="cmd">connect</b> <i class="arg">sock</i> <i class="arg">ip</i> <i class="arg">port</i></a></li>
<li><a href="#4">method <b class="cmd">Connect</b> <i class="arg">uuid</i> <i class="arg">sock</i> <i class="arg">ip</i></a></li>
<li><a href="#5">method <b class="cmd"><a href="../counter/counter.html">counter</a></b> <i class="arg">which</i></a></li>
<li><a href="#6">method <b class="cmd">CheckTimeout</b></a></li>
<li><a href="#7">method <b class="cmd">dispatch</b> <i class="arg">header_dict</i></a></li>
<li><a href="#8">method <b class="cmd"><a href="../log/log.html">log</a></b> <i class="arg">args</i></a></li>
<li><a href="#9">method <b class="cmd">port_listening</b></a></li>
<li><a href="#10">method <b class="cmd">PrefixNormalize</b> <i class="arg">prefix</i></a></li>
<li><a href="#11">method <b class="cmd">start</b></a></li>
<li><a href="#12">method <b class="cmd">stop</b></a></li>
<li><a href="#13">method <b class="cmd">template</b> <i class="arg">page</i></a></li>
<li><a href="#14">method <b class="cmd">TemplateSearch</b> <i class="arg">page</i></a></li>
<li><a href="#15">method <b class="cmd">Validate_Connection</b> <i class="arg">sock</i> <i class="arg">ip</i></a></li>
<li><a href="#16">method <b class="cmd">ENSEMBLE::add</b> <i class="arg">field</i> <i class="arg">element</i></a></li>
<li><a href="#17">method <b class="cmd">ENSEMBLE::dump</b></a></li>
<li><a href="#18">method <b class="cmd">ENSEMBLE::get</b> <i class="arg">field</i></a></li>
<li><a href="#19">method <b class="cmd">ENSEMBLE::reset</b></a></li>
<li><a href="#20">method <b class="cmd">ENSEMBLE::remove</b> <i class="arg">field</i> <i class="arg">element</i></a></li>
<li><a href="#21">method <b class="cmd">ENSEMBLE::replace</b> <i class="arg">keyvaluelist</i></a></li>
<li><a href="#22">method <b class="cmd">ENSEMBLE::reset</b></a></li>
<li><a href="#23">method <b class="cmd">ENSEMBLE::set</b> <i class="arg">field</i> <i class="arg">value</i></a></li>
<li><a href="#24">method <b class="cmd">http_info::netstring</b></a></li>
<li><a href="#25">method <b class="cmd">request::parse</b> <i class="arg">string</i></a></li>
<li><a href="#26">method <b class="cmd">reply::output</b></a></li>
<li><a href="#27">method <b class="cmd">close</b></a></li>
<li><a href="#28">method <b class="cmd">HttpHeaders</b> <i class="arg">sock</i> <i class="arg">?debug?</i></a></li>
<li><a href="#29">method <b class="cmd">dispatch</b> <i class="arg">newsock</i> <i class="arg">datastate</i></a></li>
<li><a href="#30">method <b class="cmd"><a href="../../../../index.html#error">error</a></b> <i class="arg">code</i> <i class="arg">?message?</i> <i class="arg">?errorInfo?</i></a></li>
<li><a href="#31">method <b class="cmd">content</b></a></li>
<li><a href="#32">method <b class="cmd">EncodeStatus</b> <i class="arg">status</i></a></li>
<li><a href="#33">method FormData</a></li>
<li><a href="#34">method MimeParse <i class="arg">mimetext</i></a></li>
<li><a href="#35">method <b class="cmd">DoOutput</b></a></li>
<li><a href="#36">method PostData <i class="arg">length</i></a></li>
<li><a href="#37">method <b class="cmd">puts</b> <i class="arg">string</i></a></li>
<li><a href="#38">method <b class="cmd">reset</b></a></li>
<li><a href="#39">method <b class="cmd">timeOutCheck</b></a></li>
<li><a href="#40">method <b class="cmd"><a href="../../../../index.html#timestamp">timestamp</a></b></a></li>
<li><a href="#41">method <b class="cmd">TransferComplete</b> <i class="arg">args</i></a></li>
<li><a href="#42">method <b class="cmd">Url_Decode</b> <i class="arg">string</i></a></li>
<li><a href="#43">method cgi_info</a></li>
<li><a href="#44">option <b class="cmd">path</b></a></li>
<li><a href="#45">option <b class="cmd"><a href="../../../../index.html#prefix">prefix</a></b></a></li>
<li><a href="#46">method proxy_info</a></li>
<li><a href="#47">method scgi_info</a></li>
<li><a href="#1">method <b class="cmd">ChannelCopy</b> <i class="arg">in</i> <i class="arg">out</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#2">method <b class="cmd">html_header</b> <span class="opt">?<i class="arg">title</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#3">method <b class="cmd">html_footer</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#4">method <b class="cmd">http_code_string</b> <i class="arg">code</i></a></li>
<li><a href="#5">method <b class="cmd">HttpHeaders</b> <i class="arg">sock</i> <span class="opt">?<i class="arg">debug</i> <b class="const"></b>?</span></a></li>
<li><a href="#6">method <b class="cmd">HttpHeaders_Default</b></a></li>
<li><a href="#7">method <b class="cmd">HttpServerHeaders</b></a></li>
<li><a href="#8">method <b class="cmd">MimeParse</b> <i class="arg">mimetext</i></a></li>
<li><a href="#9">method <b class="cmd">Url_Decode</b> <i class="arg">data</i></a></li>
<li><a href="#10">method <b class="cmd">Url_PathCheck</b> <i class="arg">urlsuffix</i></a></li>
<li><a href="#11">method <b class="cmd">wait</b> <i class="arg">mode</i> <i class="arg">sock</i></a></li>
<li><a href="#12">method <b class="cmd">constructor</b> <i class="arg">ServerObj</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#13">method <b class="cmd">destructor</b> <span class="opt">?<i class="arg">dictargs</i>?</span></a></li>
<li><a href="#14">method <b class="cmd">close</b></a></li>
<li><a href="#15">method <b class="cmd">Log_Dispatched</b></a></li>
<li><a href="#16">method <b class="cmd">dispatch</b> <i class="arg">newsock</i> <i class="arg">datastate</i></a></li>
<li><a href="#17">method <b class="cmd">Dispatch</b></a></li>
<li><a href="#18">method <b class="cmd">html_css</b></a></li>
<li><a href="#19">method <b class="cmd">html_header</b> <i class="arg">title</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#20">method <b class="cmd">html_footer</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#21">method <b class="cmd">error</b> <i class="arg">code</i> <span class="opt">?<i class="arg">msg</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">errorInfo</i> <b class="const"></b>?</span></a></li>
<li><a href="#22">method <b class="cmd">content</b></a></li>
<li><a href="#23">method <b class="cmd">EncodeStatus</b> <i class="arg">status</i></a></li>
<li><a href="#24">method <b class="cmd">log</b> <i class="arg">type</i> <span class="opt">?<i class="arg">info</i> <b class="const"></b>?</span></a></li>
<li><a href="#25">method <b class="cmd">CoroName</b></a></li>
<li><a href="#26">method <b class="cmd">DoOutput</b></a></li>
<li><a href="#27">method <b class="cmd">FormData</b></a></li>
<li><a href="#28">method <b class="cmd">PostData</b> <i class="arg">length</i></a></li>
<li><a href="#29">method <b class="cmd">Session_Load</b></a></li>
<li><a href="#30">method <b class="cmd">TransferComplete</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#31">method <b class="cmd">puts</b> <i class="arg">line</i></a></li>
<li><a href="#32">method <b class="cmd">RequestFind</b> <i class="arg">field</i></a></li>
<li><a href="#33">method <b class="cmd">request</b> <i class="arg">subcommand</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#34">method <b class="cmd">reply</b> <i class="arg">subcommand</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#35">method <b class="cmd">reset</b></a></li>
<li><a href="#36">method <b class="cmd">timeOutCheck</b></a></li>
<li><a href="#37">method <b class="cmd">timestamp</b></a></li>
<li><a href="#38">method <b class="cmd">constructor</b> <i class="arg">args</i> <span class="opt">?<i class="arg">port</i> <b class="const">auto</b>?</span> <span class="opt">?<i class="arg">myaddr</i> <b class="const">127.0.0.1</b>?</span> <span class="opt">?<i class="arg">string</i> <b class="const">auto</b>?</span> <span class="opt">?<i class="arg">name</i> <b class="const">auto</b>?</span> <span class="opt">?<i class="arg">doc_root</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">reverse_dns</i> <b class="const">0</b>?</span> <span class="opt">?<i class="arg">configuration_file</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">protocol</i> <b class="const">HTTP/1.1</b>?</span></a></li>
<li><a href="#39">method <b class="cmd">destructor</b> <span class="opt">?<i class="arg">dictargs</i>?</span></a></li>
<li><a href="#40">method <b class="cmd">connect</b> <i class="arg">sock</i> <i class="arg">ip</i> <i class="arg">port</i></a></li>
<li><a href="#41">method <b class="cmd">ServerHeaders</b> <i class="arg">ip</i> <i class="arg">http_request</i> <i class="arg">mimetxt</i></a></li>
<li><a href="#42">method <b class="cmd">Connect</b> <i class="arg">uuid</i> <i class="arg">sock</i> <i class="arg">ip</i></a></li>
<li><a href="#43">method <b class="cmd">counter</b> <i class="arg">which</i></a></li>
<li><a href="#44">method <b class="cmd">CheckTimeout</b></a></li>
<li><a href="#45">method <b class="cmd">debug</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#46">method <b class="cmd">dispatch</b> <i class="arg">data</i></a></li>
<li><a href="#47">method <b class="cmd">Dispatch_Default</b> <i class="arg">reply</i></a></li>
<li><a href="#48">method <b class="cmd">Dispatch_Local</b> <i class="arg">data</i></a></li>
<li><a href="#49">method <b class="cmd">Headers_Local</b> <i class="arg">varname</i></a></li>
<li><a href="#50">method <b class="cmd">Headers_Process</b> <i class="arg">varname</i></a></li>
<li><a href="#51">method <b class="cmd">HostName</b> <i class="arg">ipaddr</i></a></li>
<li><a href="#52">method <b class="cmd">log</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#53">method <b class="cmd">plugin</b> <i class="arg">slot</i> <span class="opt">?<i class="arg">class</i> <b class="const"></b>?</span></a></li>
<li><a href="#54">method <b class="cmd">port_listening</b></a></li>
<li><a href="#55">method <b class="cmd">PrefixNormalize</b> <i class="arg">prefix</i></a></li>
<li><a href="#56">method <b class="cmd">source</b> <i class="arg">filename</i></a></li>
<li><a href="#57">method <b class="cmd">start</b></a></li>
<li><a href="#58">method <b class="cmd">stop</b></a></li>
<li><a href="#59">method <b class="cmd">SubObject {} db</b></a></li>
<li><a href="#60">method <b class="cmd">SubObject {} default</b></a></li>
<li><a href="#61">method <b class="cmd">template</b> <i class="arg">page</i></a></li>
<li><a href="#62">method <b class="cmd">TemplateSearch</b> <i class="arg">page</i></a></li>
<li><a href="#63">method <b class="cmd">Thread_start</b></a></li>
<li><a href="#64">method <b class="cmd">Uuid_Generate</b></a></li>
<li><a href="#65">method <b class="cmd">Validate_Connection</b> <i class="arg">sock</i> <i class="arg">ip</i></a></li>
<li><a href="#66">method <b class="cmd">reset</b></a></li>
<li><a href="#67">method <b class="cmd">content</b></a></li>
<li><a href="#68">method <b class="cmd">Dispatch</b></a></li>
<li><a href="#69">method <b class="cmd">content</b></a></li>
<li><a href="#70">method <b class="cmd">FileName</b></a></li>
<li><a href="#71">method <b class="cmd">DirectoryListing</b> <i class="arg">local_file</i></a></li>
<li><a href="#72">method <b class="cmd">content</b></a></li>
<li><a href="#73">method <b class="cmd">Dispatch</b></a></li>
<li><a href="#74">method <b class="cmd">CgiExec</b> <i class="arg">execname</i> <i class="arg">script</i> <i class="arg">arglist</i></a></li>
<li><a href="#75">method <b class="cmd">Cgi_Executable</b> <i class="arg">script</i></a></li>
<li><a href="#76">method <b class="cmd">proxy_channel</b></a></li>
<li><a href="#77">method <b class="cmd">proxy_path</b></a></li>
<li><a href="#78">method <b class="cmd">ProxyRequest</b> <i class="arg">chana</i> <i class="arg">chanb</i></a></li>
<li><a href="#79">method <b class="cmd">ProxyReply</b> <i class="arg">chana</i> <i class="arg">chanb</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#80">method <b class="cmd">Dispatch</b></a></li>
<li><a href="#81">method <b class="cmd">FileName</b></a></li>
<li><a href="#82">method <b class="cmd">proxy_channel</b></a></li>
<li><a href="#83">method <b class="cmd">ProxyRequest</b> <i class="arg">chana</i> <i class="arg">chanb</i></a></li>
<li><a href="#84">method <b class="cmd">ProxyReply</b> <i class="arg">chana</i> <i class="arg">chanb</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#85">method <b class="cmd">DirectoryListing</b> <i class="arg">local_file</i></a></li>
<li><a href="#86">method <b class="cmd">EncodeStatus</b> <i class="arg">status</i></a></li>
<li><a href="#87">method <b class="cmd">scgi_info</b></a></li>
<li><a href="#88">method <b class="cmd">proxy_channel</b></a></li>
<li><a href="#89">method <b class="cmd">ProxyRequest</b> <i class="arg">chana</i> <i class="arg">chanb</i></a></li>
<li><a href="#90">method <b class="cmd">ProxyReply</b> <i class="arg">chana</i> <i class="arg">chanb</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#91">method <b class="cmd">debug</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#92">method <b class="cmd">Connect</b> <i class="arg">uuid</i> <i class="arg">sock</i> <i class="arg">ip</i></a></li>
<li><a href="#93">method <b class="cmd">Dispatch_Dict</b> <i class="arg">data</i></a></li>
<li><a href="#94">method <b class="cmd">uri {} add</b> <i class="arg">vhosts</i> <i class="arg">patterns</i> <i class="arg">info</i></a></li>
<li><a href="#95">method <b class="cmd">uri {} direct</b> <i class="arg">vhosts</i> <i class="arg">patterns</i> <i class="arg">info</i> <i class="arg">body</i></a></li>
<li><a href="#96">method <b class="cmd">output</b></a></li>
<li><a href="#97">method <b class="cmd">DoOutput</b></a></li>
<li><a href="#98">method <b class="cmd">close</b></a></li>
<li><a href="#99">method <b class="cmd">local_memchan</b> <i class="arg">command</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#100">method <b class="cmd">Connect_Local</b> <i class="arg">uuid</i> <i class="arg">sock</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>This module implements a web server, suitable for embedding in an
application. The server is object oriented, and contains all of the
fundamentals needed for a full service website.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">Minimal Example</a></h2>
<p>Starting a web service requires starting a class of type
<b class="cmd">httpd::server</b>, and providing that server with one or more URIs
to service, and <b class="cmd">httpd::reply</b> derived classes to generate them.</p>
<pre class="doctools_example">
tool::define ::reply.hello {
oo::class create ::reply.hello {
  method content {} {
    my puts &quot;&lt;HTML&gt;&lt;HEAD&gt;&lt;TITLE&gt;IRM Dispatch Server&lt;/TITLE&gt;&lt;/HEAD&gt;&lt;BODY&gt;&quot;
    my puts &quot;&lt;h1&gt;Hello World!&lt;/h1&gt;&quot;
    my puts &lt;/BODY&gt;&lt;/HTML&gt;
my puts &quot;&lt;HTML&gt;&lt;HEAD&gt;&lt;TITLE&gt;IRM Dispatch Server&lt;/TITLE&gt;&lt;/HEAD&gt;&lt;BODY&gt;&quot;
my puts &quot;&lt;h1&gt;Hello World!&lt;/h1&gt;&quot;
my puts &lt;/BODY&gt;&lt;/HTML&gt;
  }
}
::docserver::server create HTTPD port 8015 myaddr 127.0.0.1
HTTPD add_uri /* [list mixin reply.hello]
::httpd::server create HTTPD port 8015 myaddr 127.0.0.1 doc_root ~/htdocs
HTTPD plugin dispatch httpd::server::dispatch
HTTPD uri add * /hello [list mixin reply.hello]
</pre>
<p>The bare module does have facilities to hose a files from a file system. Files that end in a .tml will be substituted in the style of Tclhttpd:</p>
<pre class="doctools_example">
&lt;!-- hello.tml --&gt;
[my html_header {Hello World!}]
Your Server is running.
&lt;p&gt;
The time is now [clock format [clock seconds]]
[my html_footer]
</pre>
<p>A complete example of an httpd server is in the /examples directory of Tcllib. It also show how to dispatch URIs to other processes via SCGI and HTTP proxies.</p>
<pre class="doctools_example">
cd ~/tcl/sandbox/tcllib
tclsh examples/httpd.tcl
</pre>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">Class ::httpd::server</a></h2>
<p>This class is the root object of the webserver. It is responsible
<div id="section3" class="doctools_section"><h2><a name="section3">Classes</a></h2>
<div id="subsection1" class="doctools_subsection"><h3><a name="subsection1">Class  httpd::mime</a></h3>
<p><b class="class">Methods</b></p>
for opening the socket and providing the initial connection negotiation.</p>
<dl class="doctools_definitions">
<dt><a name="1">constructor ?port <span class="opt">?port?</span>? ?myaddr <span class="opt">?ipaddr?</span>|all? ?server_string <span class="opt">?string?</span>? ?server_name <span class="opt">?string?</span>?</a></dt>
<dd><p>Build a new server object. <span class="opt">?port?</span> is the port to listen on</p></dd>
<dt><a name="2">method <b class="cmd">add_uri</b> <i class="arg">pattern</i> <i class="arg">dict</i></a></dt>
<dt><a name="1">method <b class="cmd">ChannelCopy</b> <i class="arg">in</i> <i class="arg">out</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="2">method <b class="cmd">html_header</b> <span class="opt">?<i class="arg">title</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Set the hander for a URI pattern. Information given in the <i class="arg">dict</i> is stored
in the data structure the <b class="cmd">dispatch</b> method uses. If a field called
<i class="arg">mixin</i> is given, that class will be mixed into the reply object immediately
after construction.</p></dd>
<dt><a name="3">method <b class="cmd">connect</b> <i class="arg">sock</i> <i class="arg">ip</i> <i class="arg">port</i></a></dt>
<dd><p>Reply to an open socket. This method builds a coroutine to manage the remainder
<dd></dd>
<dt><a name="3">method <b class="cmd">html_footer</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
of the connection. The coroutine's operations are driven by the <b class="cmd">Connect</b> method.</p></dd>
<dt><a name="4">method <b class="cmd">Connect</b> <i class="arg">uuid</i> <i class="arg">sock</i> <i class="arg">ip</i></a></dt>
<dt><a name="4">method <b class="cmd">http_code_string</b> <i class="arg">code</i></a></dt>
<dd><p>This method reads HTTP headers, and then consults the <b class="cmd">dispatch</b> method to
determine if the request is valid, and/or what kind of reply to generate. Under
normal cases, an object of class <b class="cmd">::http::reply</b> is created.
Fields the server are looking for in particular are:
class: A class to use instead of the server's own <i class="arg">reply_class</i>
mixin: A class to be mixed into the new object after construction.
All other fields are passed along to the <b class="cmd">http_info</b> structure of the
reply object.
After the class is created and the mixin is mixed in, the server invokes the
reply objects <b class="cmd">dispatch</b> method. This action passes control of the socket to
the reply object. The reply object manages the rest of the transaction, including
closing the socket.</p></dd>
<dt><a name="5">method <b class="cmd"><a href="../counter/counter.html">counter</a></b> <i class="arg">which</i></a></dt>
<dd><p>Increment an internal counter.</p></dd>
<dt><a name="6">method <b class="cmd">CheckTimeout</b></a></dt>
<dd><p>Check open connections for a time out event.</p></dd>
<dt><a name="7">method <b class="cmd">dispatch</b> <i class="arg">header_dict</i></a></dt>
<dd></dd>
<dt><a name="5">method <b class="cmd">HttpHeaders</b> <i class="arg">sock</i> <span class="opt">?<i class="arg">debug</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="6">method <b class="cmd">HttpHeaders_Default</b></a></dt>
<dd></dd>
<dt><a name="7">method <b class="cmd">HttpServerHeaders</b></a></dt>
<dd><p>Given a key/value list of information, return a data structure describing how
the server should reply.</p></dd>
<dt><a name="8">method <b class="cmd"><a href="../log/log.html">log</a></b> <i class="arg">args</i></a></dt>
<dd><p>Log an event. The input for args is free form. This method is intended
to be replaced by the user, and is a noop for a stock http::server object.</p></dd>
<dt><a name="9">method <b class="cmd">port_listening</b></a></dt>
<dd><p>Return the actual port that httpd is listening on.</p></dd>
<dt><a name="10">method <b class="cmd">PrefixNormalize</b> <i class="arg">prefix</i></a></dt>
<dd></dd>
<dt><a name="8">method <b class="cmd">MimeParse</b> <i class="arg">mimetext</i></a></dt>
<dd><p>Converts a block of mime encoded text to a key/value list. If an exception is encountered,
 the method will generate its own call to the <b class="cmd">error</b> method, and immediately invoke
 the <b class="cmd">output</b> method to produce an error code and close the connection.</p></dd>
<dt><a name="9">method <b class="cmd">Url_Decode</b> <i class="arg">data</i></a></dt>
<dd><p>De-httpizes a string.</p></dd>
<dt><a name="10">method <b class="cmd">Url_PathCheck</b> <i class="arg">urlsuffix</i></a></dt>
<dd><p>For the stock version, trim trailing /'s and *'s from a prefix. This
method can be replaced by the end user to perform any other transformations
needed for the application.</p></dd>
<dt><a name="11">method <b class="cmd">start</b></a></dt>
<dd></dd>
<dt><a name="11">method <b class="cmd">wait</b> <i class="arg">mode</i> <i class="arg">sock</i></a></dt>
<dd><p>Open the socket listener.</p></dd>
<dt><a name="12">method <b class="cmd">stop</b></a></dt>
<dd><p>Shut off the socket listener, and destroy any pending replies.</p></dd>
<dt><a name="13">method <b class="cmd">template</b> <i class="arg">page</i></a></dt>
<dd><p>Return a template for the string <i class="arg">page</i></p></dd>
<dt><a name="14">method <b class="cmd">TemplateSearch</b> <i class="arg">page</i></a></dt>
<dd><p>Perform a search for the template that best matches <i class="arg">page</i>. This
can include local file searches, in-memory structures, or even
database lookups. The stock implementation simply looks for files
with a .tml or .html extension in the <span class="opt">?doc_root?</span> directory.</p></dd>
<dt><a name="15">method <b class="cmd">Validate_Connection</b> <i class="arg">sock</i> <i class="arg">ip</i></a></dt>
<dd><p>Given a socket and an ip address, return true if this connection should
be terminated, or false if it should be allowed to continue. The stock
implementation always returns 0. This is intended for applications to
be able to implement black lists and/or provide security based on IP
address.</p></dd>
<dd></dd>
</dl>
</div>
<div id="section4" class="doctools_section"><h2><a name="section4">Class ::httpd::reply</a></h2>
<div id="subsection2" class="doctools_subsection"><h3><a name="subsection2">Class  httpd::reply</a></h3>
<p><em>ancestors</em>: <b class="class">httpd::mime</b></p>
<p>A class which shephards a request through the process of generating a
reply.
The socket associated with the reply is available at all times as the <i class="arg">chan</i>
variable.
The process of generating a reply begins with an <b class="cmd">httpd::server</b> generating a
<b class="cmd">http::class</b> object, mixing in a set of behaviors and then invoking the reply
object's <b class="cmd">dispatch</b> method.
In normal operations the <b class="cmd">dispatch</b> method:</p>
 reply.
 The socket associated with the reply is available at all times as the <i class="arg">chan</i>
 variable.
 The process of generating a reply begins with an <b class="cmd">httpd::server</b> generating a
 <b class="cmd">http::class</b> object, mixing in a set of behaviors and then invoking the reply
 object's <b class="cmd">dispatch</b> method.
 In normal operations the <b class="cmd">dispatch</b> method:</p>
<ol class="doctools_enumerated">
 
<li><p>Invokes the <b class="cmd">reset</b> method for the object to populate default headers.</p></li>
<li><p>Invokes the <b class="cmd">HttpHeaders</b> method to stream the MIME headers out of the socket</p></li>
<li><p>Invokes the <b class="cmd">request parse</b> method to convert the stream of MIME headers into a
dict that can be read via the <b class="cmd">request</b> method.</p></li>
 dict that can be read via the <b class="cmd">request</b> method.</p></li>
<li><p>Stores the raw stream of MIME headers in the <i class="arg">rawrequest</i> variable of the object.</p></li>
<li><p>Invokes the <b class="cmd">content</b> method for the object, generating an call to the <b class="cmd"><a href="../../../../index.html#error">error</a></b>
method if an exception is raised.</p></li>
<li><p>Invokes the <b class="cmd">content</b> method for the object, generating an call to the <b class="cmd">error</b>
 method if an exception is raised.</p></li>
<li><p>Invokes the <b class="cmd">output</b> method for the object</p></li>
</ol>
</div>
<div id="section5" class="doctools_section"><h2><a name="section5">Reply Method Ensembles</a></h2>
<p>The <b class="cmd">http::reply</b> class and its derivatives maintain several variables as dictionaries
internally. Access to these dictionaries is managed through a dedicated ensemble. The
ensemble implements most of the same behaviors as the <b class="cmd"><a href="../../../../index.html#dict">dict</a></b> command.
<p>Developers have the option of streaming output to a buffer via the <b class="cmd">puts</b> method of the
 reply, or simply populating the <i class="arg">reply_body</i> variable of the object.
 The information returned by the <b class="cmd">content</b> method is not interpreted in any way.
 If an exception is thrown (via the <b class="cmd">error</b> command in Tcl, for example) the caller will
 auto-generate a 500 {Internal Error} message.
 A typical implementation of <b class="cmd">content</b> look like:</p>
Each ensemble implements the following methods above, beyond, or modifying standard dicts:</p>
<dl class="doctools_definitions">
<dt><a name="16">method <b class="cmd">ENSEMBLE::add</b> <i class="arg">field</i> <i class="arg">element</i></a></dt>
<dd><p>Add <i class="arg">element</i> to a list stored in <i class="arg">field</i>, but only if it is not already present om the list.</p></dd>
<dt><a name="17">method <b class="cmd">ENSEMBLE::dump</b></a></dt>
<dd><p>Return the current contents of the data structure as a key/value list.</p></dd>
<pre class="doctools_example">
 clay::define ::test::content.file {
 	superclass ::httpd::content.file
 	# Return a file
 	# Note: this is using the content.file mixin which looks for the reply_file variable
 	# and will auto-compute the Content-Type
 	method content {} {
 	  my reset
     set doc_root [my request get DOCUMENT_ROOT]
     my variable reply_file
     set reply_file [file join $doc_root index.html]
 	}
 }
 clay::define ::test::content.time {
   # return the current system time
<dt><a name="18">method <b class="cmd">ENSEMBLE::get</b> <i class="arg">field</i></a></dt>
<dd><p>Return the value of the field <i class="arg">field</i>, or an empty string if it does not exist.</p></dd>
<dt><a name="19">method <b class="cmd">ENSEMBLE::reset</b></a></dt>
<dd><p>Return a key/value list of the default contents for this data structure.</p></dd>
 	method content {} {
 		my variable reply_body
     my reply set Content-Type text/plain
<dt><a name="20">method <b class="cmd">ENSEMBLE::remove</b> <i class="arg">field</i> <i class="arg">element</i></a></dt>
<dd><p>Remove all instances of <i class="arg">element</i> from the list stored in <i class="arg">field</i>.</p></dd>
<dt><a name="21">method <b class="cmd">ENSEMBLE::replace</b> <i class="arg">keyvaluelist</i></a></dt>
<dd><p>Replace the internal dict with the contents of <i class="arg">keyvaluelist</i></p></dd>
<dt><a name="22">method <b class="cmd">ENSEMBLE::reset</b></a></dt>
 		set reply_body [clock seconds]
 	}
 }
 clay::define ::test::content.echo {
 	method content {} {
 		my variable reply_body
     my reply set Content-Type [my request get CONTENT_TYPE]
 		set reply_body [my PostData [my request get CONTENT_LENGTH]]
<dd><p>Replace the internal dict with the default state.</p></dd>
<dt><a name="23">method <b class="cmd">ENSEMBLE::set</b> <i class="arg">field</i> <i class="arg">value</i></a></dt>
<dd><p>Set the value of <i class="arg">field</i> to <i class="arg">value</i>.</p></dd>
</dl>
 	}
 }
 clay::define ::test::content.form_handler {
 	method content {} {
</div>
<div id="section6" class="doctools_section"><h2><a name="section6">Reply Method Ensemble: http_info</a></h2>
<p>Manages HTTP headers passed in by the server.
Ensemble Methods:</p>
<dl class="doctools_definitions">
<dt><a name="24">method <b class="cmd">http_info::netstring</b></a></dt>
<dd><p>Return the contents of this data structure as a netstring encoded block.</p></dd>
</dl>
 	  set form [my FormData]
 	  my reply set Content-Type {text/html; charset=UTF-8}
     my puts [my html_header {My Dynamic Page}]
     my puts &quot;&lt;BODY&gt;&quot;
     my puts &quot;You Sent&lt;p&gt;&quot;
     my puts &quot;&lt;TABLE&gt;&quot;
     foreach {f v} $form {
       my puts &quot;&lt;TR&gt;&lt;TH&gt;$f&lt;/TH&gt;&lt;TD&gt;&lt;verbatim&gt;$v&lt;/verbatim&gt;&lt;/TD&gt;&quot;
</div>
<div id="section7" class="doctools_section"><h2><a name="section7">Reply Method Ensemble: request</a></h2>
<p>Managed data from MIME headers of the request.</p>
     }
     my puts &quot;&lt;/TABLE&gt;&lt;p&gt;&quot;
     my puts &quot;Send some info:&lt;p&gt;&quot;
     my puts &quot;&lt;FORM action=/[my request get REQUEST_PATH] method POST&gt;&quot;
<dl class="doctools_definitions">
     my puts &quot;&lt;TABLE&gt;&quot;
     foreach field {name rank serial_number} {
       set line &quot;&lt;TR&gt;&lt;TH&gt;$field&lt;/TH&gt;&lt;TD&gt;&lt;input name=\&quot;$field\&quot; &quot;
       if {[dict exists $form $field]} {
         append line &quot; value=\&quot;[dict get $form $field]\&quot;&quot;&quot;
       }
       append line &quot; /&gt;&lt;/TD&gt;&lt;/TR&gt;&quot;
       my puts $line
     }
     my puts &quot;&lt;/TABLE&gt;&quot;
     my puts [my html footer]
 	}
<dt><a name="25">method <b class="cmd">request::parse</b> <i class="arg">string</i></a></dt>
<dd><p>Replace the contents of the data structure with information encoded in a MIME
formatted block of text (<i class="arg">string</i>).</p></dd>
</dl>
 }
 </pre>
</div>
<div id="section8" class="doctools_section"><h2><a name="section8">Reply Method Ensemble: reply</a></h2>
<p><b class="class">Methods</b></p>
<p>Manage the headers sent in the reply.</p>
<dl class="doctools_definitions">
<dt><a name="12">method <b class="cmd">constructor</b> <i class="arg">ServerObj</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="13">method <b class="cmd">destructor</b> <span class="opt">?<i class="arg">dictargs</i>?</span></a></dt>
<dd><p>clean up on exit</p></dd>
<dt><a name="26">method <b class="cmd">reply::output</b></a></dt>
<dd><p>Return the contents of this data structure as a MIME encoded block appropriate
for an HTTP response.</p></dd>
</dl>
</div>
<div id="section9" class="doctools_section"><h2><a name="section9">Reply Methods</a></h2>
<dl class="doctools_definitions">
<dt><a name="27">method <b class="cmd">close</b></a></dt>
<dd><p>Terminate the transaction, and close the socket.</p></dd>
<dt><a name="28">method <b class="cmd">HttpHeaders</b> <i class="arg">sock</i> <i class="arg">?debug?</i></a></dt>
<dd><p>Stream MIME headers from the socket <i class="arg">sock</i>, stopping at an empty line. Returns
the stream as a block of text.</p></dd>
<dt><a name="29">method <b class="cmd">dispatch</b> <i class="arg">newsock</i> <i class="arg">datastate</i></a></dt>
<dt><a name="14">method <b class="cmd">close</b></a></dt>
<dd><p>Close channels opened by this object</p></dd>
<dt><a name="15">method <b class="cmd">Log_Dispatched</b></a></dt>
<dd><p>Record a dispatch event</p></dd>
<dt><a name="16">method <b class="cmd">dispatch</b> <i class="arg">newsock</i> <i class="arg">datastate</i></a></dt>
<dd><p>Accept the handoff from the server object of the socket
 <em>newsock</em> and feed it the state <em>datastate</em>.
 Fields the <em>datastate</em> are looking for in particular are:</p>
<p>* <b class="const">mixin</b> - A key/value list of slots and classes to be mixed into the
 object prior to invoking <b class="cmd">Dispatch</b>.</p>
<p>* <b class="const">http</b> - A key/value list of values to populate the object's <em>request</em>
 ensemble</p>
<p>All other fields are passed along to the <b class="method">clay</b> structure of the object.</p></dd>
<dt><a name="17">method <b class="cmd">Dispatch</b></a></dt>
<dd></dd>
<dt><a name="18">method <b class="cmd">html_css</b></a></dt>
<dd></dd>
<dt><a name="19">method <b class="cmd">html_header</b> <i class="arg">title</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="20">method <b class="cmd">html_footer</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="21">method <b class="cmd">error</b> <i class="arg">code</i> <span class="opt">?<i class="arg">msg</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">errorInfo</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="22">method <b class="cmd">content</b></a></dt>
<dd><p>REPLACE ME:
 This method is the &quot;meat&quot; of your application.
 It writes to the result buffer via the &quot;puts&quot; method
 and can tweak the headers via &quot;clay put header_reply&quot;</p></dd>
<dt><a name="23">method <b class="cmd">EncodeStatus</b> <i class="arg">status</i></a></dt>
<dd><p>Formulate a standard HTTP status header from he string provided.</p></dd>
<dt><a name="24">method <b class="cmd">log</b> <i class="arg">type</i> <span class="opt">?<i class="arg">info</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="25">method <b class="cmd">CoroName</b></a></dt>
<dd></dd>
<dt><a name="26">method <b class="cmd">DoOutput</b></a></dt>
<dd><p>Generates the the HTTP reply, streams that reply back across <i class="arg">chan</i>,
 and destroys the object.</p></dd>
<dt><a name="27">method <b class="cmd">FormData</b></a></dt>
<dd><p>For GET requests, converts the QUERY_DATA header into a key/value list.
 For POST requests, reads the Post data and converts that information to
 a key/value list for application/x-www-form-urlencoded posts. For multipart
 posts, it composites all of the MIME headers of the post to a singular key/value
 list, and provides MIME_* information as computed by the <b class="cmd">mime</b> package, including
 the MIME_TOKEN, which can be fed back into the mime package to read out the contents.</p></dd>
<dt><a name="28">method <b class="cmd">PostData</b> <i class="arg">length</i></a></dt>
<dd><p>Stream <i class="arg">length</i> bytes from the <i class="arg">chan</i> socket, but only of the request is a
 POST or PUSH. Returns an empty string otherwise.</p></dd>
<dt><a name="29">method <b class="cmd">Session_Load</b></a></dt>
<dd><p>Take over control of the socket <i class="arg">newsock</i>, and store that as the <i class="arg">chan</i> variable
for the object. This method runs through all of the steps of reading HTTP headers, generating
content, and closing the connection. (See class writetup).</p></dd>
<dt><a name="30">method <b class="cmd"><a href="../../../../index.html#error">error</a></b> <i class="arg">code</i> <i class="arg">?message?</i> <i class="arg">?errorInfo?</i></a></dt>
<dd><p>Manage session data</p></dd>
<dt><a name="30">method <b class="cmd">TransferComplete</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Generate an error message of the specified <i class="arg">code</i>, and display the <i class="arg">message</i> as the
reason for the exception. <i class="arg">errorInfo</i> is passed in from calls, but how or if it should be
displayed is a prerogative of the developer.</p></dd>
<dt><a name="31">method <b class="cmd">content</b></a></dt>
<dd><p>Intended to be invoked from <b class="cmd">chan copy</b> as a callback. This closes every channel
<dd><p>Generate the content for the reply. This method is intended to be replaced by the mixin.
Developers have the option of streaming output to a buffer via the <b class="cmd">puts</b> method of the
reply, or simply populating the <i class="arg">reply_body</i> variable of the object.
The information returned by the <b class="cmd">content</b> method is not interpreted in any way.
If an exception is thrown (via the <b class="cmd"><a href="../../../../index.html#error">error</a></b> command in Tcl, for example) the caller will
auto-generate a 500 {Internal Error} message.
A typical implementation of <b class="cmd">content</b> look like:</p>
 fed to it on the command line, and then destroys the object.</p>
<pre class="doctools_example">
tool::define ::test::content.file {
	superclass ::httpd::content.file
	# Return a file
     ###
	# Note: this is using the content.file mixin which looks for the reply_file variable
	# and will auto-compute the Content-Type
	method content {} {
	  my reset
    set doc_root [my http_info get doc_root]
    my variable reply_file
    set reply_file [file join $doc_root index.html]
	}
}
tool::define ::test::content.time {
  # return the current system time
	method content {} {
		my variable reply_body
     # Output the body
    my reply set Content-Type text/plain
		set reply_body [clock seconds]
	}
     ###
     chan configure $sock -translation binary -blocking 0 -buffering full -buffersize 4096
     chan configure $chan -translation binary -blocking 0 -buffering full -buffersize 4096
     if {$length} {
}
tool::define ::test::content.echo {
	method content {} {
       ###
       # Send any POST/PUT/etc content
		my variable reply_body
    my reply set Content-Type [my request get CONTENT_TYPE]
		set reply_body [my PostData [my request get CONTENT_LENGTH]]
	}
}
tool::define ::test::content.form_handler {
	method content {} {
	  set form [my FormData]
	  my reply set Content-Type {text/html; charset=UTF-8}
    my puts [my html header {My Dynamic Page}]
    my puts &quot;&lt;BODY&gt;&quot;
    my puts &quot;You Sent&lt;p&gt;&quot;
    my puts &quot;&lt;TABLE&gt;&quot;
    foreach {f v} $form {
      my puts &quot;&lt;TR&gt;&lt;TH&gt;$f&lt;/TH&gt;&lt;TD&gt;&lt;verbatim&gt;$v&lt;/verbatim&gt;&lt;/TD&gt;&quot;
    }
    my puts &quot;&lt;/TABLE&gt;&lt;p&gt;&quot;
    my puts &quot;Send some info:&lt;p&gt;&quot;
    my puts &quot;&lt;FORM action=/[my http_info get REQUEST_PATH] method POST&gt;&quot;
    my puts &quot;&lt;TABLE&gt;&quot;
    foreach field {name rank serial_number} {
      set line &quot;&lt;TR&gt;&lt;TH&gt;$field&lt;/TH&gt;&lt;TD&gt;&lt;input name=\&quot;$field\&quot; &quot;
      if {[dict exists $form $field]} {
       ###
       chan copy $sock $chan -size $SIZE -command [info coroutine]
       yield
        append line &quot; value=\&quot;[dict get $form $field]\&quot;&quot;&quot;
      }
     }
      append line &quot; /&gt;&lt;/TD&gt;&lt;/TR&gt;&quot;
      my puts $line
     catch {close $sock}
    }
    my puts &quot;&lt;/TABLE&gt;&quot;
    my puts [my html footer]
     chan flush $chan
	}
}
</pre>
 </pre>
</dd>
<dt><a name="32">method <b class="cmd">EncodeStatus</b> <i class="arg">status</i></a></dt>
<dd><p>Formulate a standard HTTP status header from he string provided.</p></dd>
<dt><a name="33">method FormData</a></dt>
<dd><p>For GET requests, converts the QUERY_DATA header into a key/value list.
For POST requests, reads the Post data and converts that information to
a key/value list for application/x-www-form-urlencoded posts. For multipart
posts, it composites all of the MIME headers of the post to a singular key/value
list, and provides MIME_* information as computed by the <b class="cmd"><a href="../mime/mime.html">mime</a></b> package, including
the MIME_TOKEN, which can be fed back into the mime package to read out the contents.</p></dd>
<dt><a name="34">method MimeParse <i class="arg">mimetext</i></a></dt>
<dd><p>Converts a block of mime encoded text to a key/value list. If an exception is encountered,
the method will generate its own call to the <b class="cmd"><a href="../../../../index.html#error">error</a></b> method, and immediately invoke
the <b class="cmd">output</b> method to produce an error code and close the connection.</p></dd>
<dt><a name="35">method <b class="cmd">DoOutput</b></a></dt>
<dd><p>Generates the the HTTP reply, and streams that reply back across <i class="arg">chan</i>.</p></dd>
<dt><a name="36">method PostData <i class="arg">length</i></a></dt>
<dd><p>Stream <i class="arg">length</i> bytes from the <i class="arg">chan</i> socket, but only of the request is a
POST or PUSH. Returns an empty string otherwise.</p></dd>
<dt><a name="37">method <b class="cmd">puts</b> <i class="arg">string</i></a></dt>
<dt><a name="31">method <b class="cmd">puts</b> <i class="arg">line</i></a></dt>
<dd><p>Appends the value of <i class="arg">string</i> to the end of <i class="arg">reply_body</i>, as well as a trailing newline
character.</p></dd>
<dt><a name="38">method <b class="cmd">reset</b></a></dt>
 character.</p></dd>
<dt><a name="32">method <b class="cmd">RequestFind</b> <i class="arg">field</i></a></dt>
<dd></dd>
<dt><a name="33">method <b class="cmd">request</b> <i class="arg">subcommand</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="34">method <b class="cmd">reply</b> <i class="arg">subcommand</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="35">method <b class="cmd">reset</b></a></dt>
<dd><p>Clear the contents of the <i class="arg">reply_body</i> variable, and reset all headers in the <b class="cmd">reply</b>
structure back to the defaults for this object.</p></dd>
<dt><a name="39">method <b class="cmd">timeOutCheck</b></a></dt>
 structure back to the defaults for this object.</p></dd>
<dt><a name="36">method <b class="cmd">timeOutCheck</b></a></dt>
<dd><p>Called from the <b class="cmd">http::server</b> object which spawned this reply. Checks to see
if too much time has elapsed while waiting for data or generating a reply, and issues
a timeout error to the request if it has, as well as destroy the object and close the
<i class="arg">chan</i> socket.</p></dd>
<dt><a name="40">method <b class="cmd"><a href="../../../../index.html#timestamp">timestamp</a></b></a></dt>
 if too much time has elapsed while waiting for data or generating a reply, and issues
 a timeout error to the request if it has, as well as destroy the object and close the
 <i class="arg">chan</i> socket.</p></dd>
<dt><a name="37">method <b class="cmd">timestamp</b></a></dt>
<dd><p>Return the current system time in the format:</p>
<pre class="doctools_example">%a, %d %b %Y %T %Z</pre>
</dd>
</dl>
</div>
<div id="subsection3" class="doctools_subsection"><h3><a name="subsection3">Class  httpd::server</a></h3>
<dt><a name="41">method <b class="cmd">TransferComplete</b> <i class="arg">args</i></a></dt>
<dd><p>Intended to be invoked from <b class="cmd">chan copy</b> as a callback. This closes every channel
<p><em>ancestors</em>: <b class="class">httpd::mime</b></p>
<p><b class="class">Methods</b></p>
fed to it on the command line, and then destroys the object.</p>
<pre class="doctools_example">
<dl class="doctools_definitions">
    ###
    # Output the body
<dt><a name="38">method <b class="cmd">constructor</b> <i class="arg">args</i> <span class="opt">?<i class="arg">port</i> <b class="const">auto</b>?</span> <span class="opt">?<i class="arg">myaddr</i> <b class="const">127.0.0.1</b>?</span> <span class="opt">?<i class="arg">string</i> <b class="const">auto</b>?</span> <span class="opt">?<i class="arg">name</i> <b class="const">auto</b>?</span> <span class="opt">?<i class="arg">doc_root</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">reverse_dns</i> <b class="const">0</b>?</span> <span class="opt">?<i class="arg">configuration_file</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">protocol</i> <b class="const">HTTP/1.1</b>?</span></a></dt>
<dd></dd>
<dt><a name="39">method <b class="cmd">destructor</b> <span class="opt">?<i class="arg">dictargs</i>?</span></a></dt>
<dd></dd>
<dt><a name="40">method <b class="cmd">connect</b> <i class="arg">sock</i> <i class="arg">ip</i> <i class="arg">port</i></a></dt>
<dd><p>Reply to an open socket. This method builds a coroutine to manage the remainder
 of the connection. The coroutine's operations are driven by the <b class="cmd">Connect</b> method.</p></dd>
<dt><a name="41">method <b class="cmd">ServerHeaders</b> <i class="arg">ip</i> <i class="arg">http_request</i> <i class="arg">mimetxt</i></a></dt>
<dd></dd>
<dt><a name="42">method <b class="cmd">Connect</b> <i class="arg">uuid</i> <i class="arg">sock</i> <i class="arg">ip</i></a></dt>
<dd><p>This method reads HTTP headers, and then consults the <b class="cmd">dispatch</b> method to
 determine if the request is valid, and/or what kind of reply to generate. Under
 normal cases, an object of class <b class="cmd">::http::reply</b> is created, and that class's
 <b class="cmd">dispatch</b> method.
 This action passes control of the socket to
 the reply object. The reply object manages the rest of the transaction, including
 closing the socket.</p></dd>
    ###
    chan configure $sock -translation binary -blocking 0 -buffering full -buffersize 4096
    chan configure $chan -translation binary -blocking 0 -buffering full -buffersize 4096
    if {$length} {
      ###
      # Send any POST/PUT/etc content
      ###
      chan copy $sock $chan -size $SIZE -command [info coroutine]
      yield
<dt><a name="43">method <b class="cmd">counter</b> <i class="arg">which</i></a></dt>
<dd><p>Increment an internal counter.</p></dd>
<dt><a name="44">method <b class="cmd">CheckTimeout</b></a></dt>
<dd><p>Check open connections for a time out event.</p></dd>
<dt><a name="45">method <b class="cmd">debug</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
    }
    catch {close $sock}
    chan flush $chan
<dt><a name="46">method <b class="cmd">dispatch</b> <i class="arg">data</i></a></dt>
<dd><p>Given a key/value list of information, return a data structure describing how
 the server should reply.</p></dd>
<dt><a name="47">method <b class="cmd">Dispatch_Default</b> <i class="arg">reply</i></a></dt>
<dd><p>Method dispatch method of last resort before returning a 404 NOT FOUND error.
 The default behavior is to look for a file in <em>DOCUMENT_ROOT</em> which
</pre>
</dd>
<dt><a name="42">method <b class="cmd">Url_Decode</b> <i class="arg">string</i></a></dt>
<dd><p>De-httpizes a string.</p></dd>
</dl>
 matches the query.</p></dd>
<dt><a name="48">method <b class="cmd">Dispatch_Local</b> <i class="arg">data</i></a></dt>
<dd><p>Method dispatch method invoked prior to invoking methods implemented by plugins.
 If this method returns a non-empty dictionary, that structure will be passed to
 the reply. The default is an empty implementation.</p></dd>
</div>
<div id="section10" class="doctools_section"><h2><a name="section10">Class ::httpd::content</a></h2>
<p>The httpd module includes several ready to use implementations of content mixins
for common use cases. Options are passed in to the <b class="cmd">add_uri</b> method of the server.</p>
</div>
<dt><a name="49">method <b class="cmd">Headers_Local</b> <i class="arg">varname</i></a></dt>
<dd><p>Introspect and possibly modify a data structure destined for a reply. This
 method is invoked before invoking Header methods implemented by plugins.
 The default implementation is empty.</p></dd>
<dt><a name="50">method <b class="cmd">Headers_Process</b> <i class="arg">varname</i></a></dt>
<dd><p>Introspect and possibly modify a data structure destined for a reply. This
 method is built dynamically by the <b class="cmd">plugin</b> method.</p></dd>
<dt><a name="51">method <b class="cmd">HostName</b> <i class="arg">ipaddr</i></a></dt>
<dd><p>Convert an ip address to a host name. If the server/ reverse_dns flag
 is false, this method simply returns the IP address back.
 Internally, this method uses the <em>dns</em> module from tcllib.</p></dd>
<dt><a name="52">method <b class="cmd">log</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Log an event. The input for args is free form. This method is intended
 to be replaced by the user, and is a noop for a stock http::server object.</p></dd>
<dt><a name="53">method <b class="cmd">plugin</b> <i class="arg">slot</i> <span class="opt">?<i class="arg">class</i> <b class="const"></b>?</span></a></dt>
<dd><p>Incorporate behaviors from a plugin.
 This method dynamically rebuilds the <b class="cmd">Dispatch</b> and <b class="cmd">Headers</b>
 method. For every plugin, the server looks for the following entries in
 <em>clay plugin/</em>:</p>
<div id="section11" class="doctools_section"><h2><a name="section11">Class ::httpd::content.cgi</a></h2>
<p>An implementation to relay requests to process which will accept post data
streamed in vie stdin, and sent a reply streamed to stdout.</p>
<dl class="doctools_definitions">
<dt><a name="43">method cgi_info</a></dt>
<dd><p>Mandatory method to be replaced by the end user. If needed, activates the
process to proxy, and then returns a list of three values:
<i class="arg">exec</i> - The arguments to send to exec to fire off the responding process, minus the stdin/stdout redirection.</p></dd>
<p><em>load</em> - A script to invoke in the server's namespace during the <b class="cmd">plugin</b> method invokation.</p>
<p><em>dispatch</em> - A script to stitch into the server's <b class="cmd">Dispatch</b> method.</p>
<p><em>headers</em> - A script to stitch into the server's <b class="cmd">Headers</b> method.</p>
<p><em>thread</em> - A script to stitch into the server's <b class="cmd">Thread_start</b> method.</p></dd>
<dt><a name="54">method <b class="cmd">port_listening</b></a></dt>
<dd><p>Return the actual port that httpd is listening on.</p></dd>
<dt><a name="55">method <b class="cmd">PrefixNormalize</b> <i class="arg">prefix</i></a></dt>
<dd><p>For the stock version, trim trailing /'s and *'s from a prefix. This
 method can be replaced by the end user to perform any other transformations
 needed for the application.</p></dd>
<dt><a name="56">method <b class="cmd">source</b> <i class="arg">filename</i></a></dt>
<dd></dd>
<dt><a name="57">method <b class="cmd">start</b></a></dt>
<dd><p>Open the socket listener.</p></dd>
<dt><a name="58">method <b class="cmd">stop</b></a></dt>
<dd><p>Shut off the socket listener, and destroy any pending replies.</p></dd>
<dt><a name="59">method <b class="cmd">SubObject {} db</b></a></dt>
<dd></dd>
<dt><a name="60">method <b class="cmd">SubObject {} default</b></a></dt>
<dd></dd>
<dt><a name="61">method <b class="cmd">template</b> <i class="arg">page</i></a></dt>
<dd><p>Return a template for the string <i class="arg">page</i></p></dd>
<dt><a name="62">method <b class="cmd">TemplateSearch</b> <i class="arg">page</i></a></dt>
<dd><p>Perform a search for the template that best matches <i class="arg">page</i>. This
 can include local file searches, in-memory structures, or even
 database lookups. The stock implementation simply looks for files
 with a .tml or .html extension in the <span class="opt">?doc_root?</span> directory.</p></dd>
<dt><a name="63">method <b class="cmd">Thread_start</b></a></dt>
<dd><p>Built by the <b class="cmd">plugin</b> method. Called by the <b class="cmd">start</b> method. Intended
 to allow plugins to spawn worker threads.</p></dd>
<dt><a name="64">method <b class="cmd">Uuid_Generate</b></a></dt>
<dd><p>Generate a GUUID. Used to ensure every request has a unique ID.
 The default implementation is:</p>
<pre class="doctools_example">
   return [::uuid::uuid generate]
 </pre>
</dd>
<dt><a name="65">method <b class="cmd">Validate_Connection</b> <i class="arg">sock</i> <i class="arg">ip</i></a></dt>
<dd><p>Given a socket and an ip address, return true if this connection should
 be terminated, or false if it should be allowed to continue. The stock
 implementation always returns 0. This is intended for applications to
 be able to implement black lists and/or provide security based on IP
 address.</p></dd>
</dl>
</div>
<div id="section12" class="doctools_section"><h2><a name="section12">Class ::httpd::content.file</a></h2>
<div id="subsection4" class="doctools_subsection"><h3><a name="subsection4">Class  httpd::server::dispatch</a></h3>
<p>An implementation to deliver files from the local file system.</p>
<p><em>ancestors</em>: <b class="class">httpd::server</b></p>
<p>Provide a backward compadible alias</p>
</div>
<div id="subsection5" class="doctools_subsection"><h3><a name="subsection5">Class  httpd::content.redirect</a></h3>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="44">option <b class="cmd">path</b></a></dt>
<dd><p>The root directory on the local file system to be exposed via http.</p></dd>
<dt><a name="45">option <b class="cmd"><a href="../../../../index.html#prefix">prefix</a></b></a></dt>
<dd><p>The prefix of the URI portion to ignore when calculating relative file paths.</p></dd>
<dt><a name="66">method <b class="cmd">reset</b></a></dt>
<dd></dd>
<dt><a name="67">method <b class="cmd">content</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection6" class="doctools_subsection"><h3><a name="subsection6">Class  httpd::content.cache</a></h3>
<p><b class="class">Methods</b></p>
<div id="section13" class="doctools_section"><h2><a name="section13">Class ::httpd::content.proxy</a></h2>
<dl class="doctools_definitions">
<p>An implementation to relay requests to another HTTP server, and relay
the results back across the request channel.</p>
<dt><a name="68">method <b class="cmd">Dispatch</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection7" class="doctools_subsection"><h3><a name="subsection7">Class  httpd::content.template</a></h3>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="69">method <b class="cmd">content</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection8" class="doctools_subsection"><h3><a name="subsection8">Class  httpd::content.file</a></h3>
<p>Class to deliver Static content
 When utilized, this class is fed a local filename
 by the dispatcher</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="46">method proxy_info</a></dt>
<dd><p>Mandatory method to be replaced by the end user. If needed, activates the
<dt><a name="70">method <b class="cmd">FileName</b></a></dt>
<dd></dd>
process to proxy, and then returns a list of three values:
<i class="arg">proxyhost</i> - The hostname where the proxy is located
<i class="arg">proxyport</i> - The port to connect to
<i class="arg">proxyscript</i> - A pre-amble block of text to send prior to the mirrored request</p></dd>
<dt><a name="71">method <b class="cmd">DirectoryListing</b> <i class="arg">local_file</i></a></dt>
<dd></dd>
<dt><a name="72">method <b class="cmd">content</b></a></dt>
<dd></dd>
<dt><a name="73">method <b class="cmd">Dispatch</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection9" class="doctools_subsection"><h3><a name="subsection9">Class  httpd::content.exec</a></h3>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="74">method <b class="cmd">CgiExec</b> <i class="arg">execname</i> <i class="arg">script</i> <i class="arg">arglist</i></a></dt>
<dd></dd>
<dt><a name="75">method <b class="cmd">Cgi_Executable</b> <i class="arg">script</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection10" class="doctools_subsection"><h3><a name="subsection10">Class  httpd::content.proxy</a></h3>
<p><em>ancestors</em>: <b class="class">httpd::content.exec</b></p>
<p>Return data from an proxy process</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="76">method <b class="cmd">proxy_channel</b></a></dt>
<dd></dd>
<dt><a name="77">method <b class="cmd">proxy_path</b></a></dt>
<dd></dd>
<dt><a name="78">method <b class="cmd">ProxyRequest</b> <i class="arg">chana</i> <i class="arg">chanb</i></a></dt>
<dd></dd>
<dt><a name="79">method <b class="cmd">ProxyReply</b> <i class="arg">chana</i> <i class="arg">chanb</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="80">method <b class="cmd">Dispatch</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection11" class="doctools_subsection"><h3><a name="subsection11">Class  httpd::content.cgi</a></h3>
<div id="section14" class="doctools_section"><h2><a name="section14">Class ::httpd::content.scgi</a></h2>
<p><em>ancestors</em>: <b class="class">httpd::content.proxy</b></p>
<p>An implementation to relay requests to a server listening on a socket
expecting SCGI encoded requests, and relay
the results back across the request channel.</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="47">method scgi_info</a></dt>
<dd><p>Mandatory method to be replaced by the end user. If needed, activates the
process to proxy, and then returns a list of three values:
<i class="arg">scgihost</i> - The hostname where the scgi listener is located
<i class="arg">scgiport</i> - The port to connect to
<i class="arg">scgiscript</i> - The contents of the <i class="arg">SCRIPT_NAME</i> header to be sent</p></dd>
<dt><a name="81">method <b class="cmd">FileName</b></a></dt>
<dd></dd>
<dt><a name="82">method <b class="cmd">proxy_channel</b></a></dt>
<dd></dd>
<dt><a name="83">method <b class="cmd">ProxyRequest</b> <i class="arg">chana</i> <i class="arg">chanb</i></a></dt>
<dd></dd>
<dt><a name="84">method <b class="cmd">ProxyReply</b> <i class="arg">chana</i> <i class="arg">chanb</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="85">method <b class="cmd">DirectoryListing</b> <i class="arg">local_file</i></a></dt>
<dd><p>For most CGI applications a directory list is vorboten</p></dd>
</dl>
</div>
<div id="subsection12" class="doctools_subsection"><h3><a name="subsection12">Class  httpd::protocol.scgi</a></h3>
<p>Return data from an SCGI process</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="86">method <b class="cmd">EncodeStatus</b> <i class="arg">status</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection13" class="doctools_subsection"><h3><a name="subsection13">Class  httpd::content.scgi</a></h3>
<p><em>ancestors</em>: <b class="class">httpd::content.proxy</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="87">method <b class="cmd">scgi_info</b></a></dt>
<dd></dd>
<dt><a name="88">method <b class="cmd">proxy_channel</b></a></dt>
<dd></dd>
<dt><a name="89">method <b class="cmd">ProxyRequest</b> <i class="arg">chana</i> <i class="arg">chanb</i></a></dt>
<dd></dd>
<dt><a name="90">method <b class="cmd">ProxyReply</b> <i class="arg">chana</i> <i class="arg">chanb</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection14" class="doctools_subsection"><h3><a name="subsection14">Class  httpd::server.scgi</a></h3>
<p><em>ancestors</em>: <b class="class">httpd::server</b></p>
<p>Act as an  SCGI Server</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="91">method <b class="cmd">debug</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="92">method <b class="cmd">Connect</b> <i class="arg">uuid</i> <i class="arg">sock</i> <i class="arg">ip</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="section15" class="doctools_section"><h2><a name="section15">Class ::httpd::content.websocket</a></h2>
<div id="subsection15" class="doctools_subsection"><h3><a name="subsection15">Class  httpd::content.websocket</a></h3>
<p>A placeholder for a future implementation to manage requests that can expect to be
promoted to a Websocket. Currently it is an empty class.</p>
<p>Upgrade a connection to a websocket</p>
</div>
<div id="section16" class="doctools_section"><h2><a name="section16">SCGI Server Functions</a></h2>
<p>The HTTP module also provides an SCGI server implementation, as well as an HTTP
<div id="subsection16" class="doctools_subsection"><h3><a name="subsection16">Class  httpd::plugin</a></h3>
<p>httpd plugin template</p>
</div>
<div id="subsection17" class="doctools_subsection"><h3><a name="subsection17">Class  httpd::plugin.dict_dispatch</a></h3>
<p>A rudimentary plugin that dispatches URLs from a dict
 data structure</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="93">method <b class="cmd">Dispatch_Dict</b> <i class="arg">data</i></a></dt>
<dd><p>Implementation of the dispatcher</p></dd>
implementation. To use the SCGI functions, create an object of the <b class="cmd">http::server.scgi</b>
class instead of the <b class="cmd">http::server</b> class.</p>
<dt><a name="94">method <b class="cmd">uri {} add</b> <i class="arg">vhosts</i> <i class="arg">patterns</i> <i class="arg">info</i></a></dt>
<dd></dd>
<dt><a name="95">method <b class="cmd">uri {} direct</b> <i class="arg">vhosts</i> <i class="arg">patterns</i> <i class="arg">info</i> <i class="arg">body</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection18" class="doctools_subsection"><h3><a name="subsection18">Class  httpd::reply.memchan</a></h3>
<p><em>ancestors</em>: <b class="class">httpd::reply</b></p>
<p><b class="class">Methods</b></p>
<div id="section17" class="doctools_section"><h2><a name="section17">Class ::httpd::reply.scgi</a></h2>
<p>An modified <b class="cmd">http::reply</b> implementation that understands how to deal with
netstring encoded headers.</p>
<dl class="doctools_definitions">
<dt><a name="96">method <b class="cmd">output</b></a></dt>
<dd></dd>
<dt><a name="97">method <b class="cmd">DoOutput</b></a></dt>
<dd></dd>
<dt><a name="98">method <b class="cmd">close</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection19" class="doctools_subsection"><h3><a name="subsection19">Class  httpd::plugin.local_memchan</a></h3>
<p><b class="class">Methods</b></p>
<div id="section18" class="doctools_section"><h2><a name="section18">Class ::httpd::server.scgi</a></h2>
<p>A modified <b class="cmd">http::server</b> which is tailored to replying to request according to
the SCGI standard instead of the HTTP standard.</p>
<dl class="doctools_definitions">
<dt><a name="99">method <b class="cmd">local_memchan</b> <i class="arg">command</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="100">method <b class="cmd">Connect_Local</b> <i class="arg">uuid</i> <i class="arg">sock</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>A modified connection method that passes simple GET request to an object
 and pulls data directly from the reply_body data variable in the object
 Needed because memchan is bidirectional, and we can't seem to communicate that
 the server is one side of the link and the reply is another</p></dd>
</dl>
</div>
</div>
<div id="section19" class="doctools_section"><h2><a name="section19">AUTHORS</a></h2>
<div id="section4" class="doctools_section"><h2><a name="section4">AUTHORS</a></h2>
<p>Sean Woods</p>
</div>
<div id="section20" class="doctools_section"><h2><a name="section20">Bugs, Ideas, Feedback</a></h2>
<div id="section5" class="doctools_section"><h2><a name="section5">Bugs, Ideas, Feedback</a></h2>
<p>This document, and the package it describes, will undoubtedly contain
bugs and other problems.
Please report such in the category <em>network</em> of the
<a href="http://core.tcl.tk/tcllib/reportlist">Tcllib Trackers</a>.
Please also report any ideas for enhancements you may have for either
package and/or documentation.</p>
<p>When proposing code changes, please provide <em>unified diffs</em>,
i.e the output of <b class="const">diff -u</b>.</p>
<p>Note further that <em>attachments</em> are strongly preferred over
inlined patches. Attachments can be made by going to the <b class="const">Edit</b>
form of the ticket immediately after its creation, and then using the
left-most button in the secondary navigation bar.</p>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>
<p><a href="../../../../index.html#tcloo">TclOO</a>, <a href="../../../../index.html#www">WWW</a>, <a href="../../../../index.html#http">http</a>, <a href="../../../../index.html#httpd">httpd</a>, <a href="../../../../index.html#httpserver">httpserver</a>, <a href="../../../../index.html#services">services</a></p>
<p>TclOO, WWW, http, httpd, httpserver, services</p>
</div>
<div id="category" class="doctools_section"><h2><a name="category">Category</a></h2>
<p>Networking</p>
</div>
<div id="copyright" class="doctools_section"><h2><a name="copyright">Copyright</a></h2>
<p>Copyright &copy; 2018 Sean Woods &lt;[email protected]&gt;</p>
</div>
</div>
</div></body></html>

Changes to embedded/www/tcllib/files/modules/log/log.html.

224
225
226
227
228
229
230
231
232

233
234
235
236

237
238
239
240
241
242
243
224
225
226
227
228
229
230


231
232
233


234
235
236
237
238
239
240
241







-
-
+


-
-
+







<dd><p>Compares two levels (including unique abbreviations) with respect to
their priority. This command can be used by the -command option of
lsort. The result is one of -1, 0 or 1 or an error. A result of -1
signals that level1 is of less priority than level2. 0 signals that
both levels have the same priority. 1 signals that level1 has higher
priority than level2.</p></dd>
<dt><a name="8"><b class="cmd">::log::lvSuppress</b> <i class="arg">level</i> {<i class="arg">suppress</i> 1}</a></dt>
<dd><p>]
(Un)suppresses the output of messages having the specified
<dd><p>(Un)suppresses the output of messages having the specified
level. Unique abbreviations for the level are allowed here too.</p></dd>
<dt><a name="9"><b class="cmd">::log::lvSuppressLE</b> <i class="arg">level</i> {<i class="arg">suppress</i> 1}</a></dt>
<dd><p>]
(Un)suppresses the output of messages having the specified level or
<dd><p>(Un)suppresses the output of messages having the specified level or
one of lesser priority. Unique abbreviations for the level are allowed
here too.</p></dd>
<dt><a name="10"><b class="cmd">::log::lvIsSuppressed</b> <i class="arg">level</i></a></dt>
<dd><p>Asks the package whether the specified level is currently
suppressed. Unique abbreviations of level names are allowed.</p></dd>
<dt><a name="11"><b class="cmd">::log::lvCmd</b> <i class="arg">level</i> <i class="arg">cmd</i></a></dt>
<dd><p>Defines for the specified level with which command to write the

Changes to embedded/www/tcllib/files/modules/math/math_geometry.html.

198
199
200
201
202
203
204
205

206
207
208
209
210
211
212
198
199
200
201
202
203
204

205
206
207
208
209
210
211
212







-
+







<li><p><em>polyline</em> - a list of an even number of coordinates,
interpreted as the x- and y-coordinates of an ordered set of points.</p></li>
<li><p><em>polygon</em> - like a polyline, but the implicit assumption is that
the polyline is closed (if the first and last points do not coincide,
the missing segment is automatically added).</p></li>
<li><p><em>point set</em> - again a list of an even number of coordinates, but
the points are regarded without any ordering.</p></li>
<li><p><em>circle</em> - a list of thtee numbers, the first two are the coordinates of the
<li><p><em>circle</em> - a list of three numbers, the first two are the coordinates of the
centre and the third is the radius.</p></li>
</ul>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">PROCEDURES</a></h2>
<p>The package defines the following public procedures:</p>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">::math::geometry::+</b> <i class="arg">point1</i> <i class="arg">point2</i></a></dt>
582
583
584
585
586
587
588
589
590


591
592
593
594
595
596
597
582
583
584
585
586
587
588


589
590
591
592
593
594
595
596
597







-
-
+
+







<dd><p>Line to be checked</p></dd>
<dt>list <i class="arg">circle</i></dt>
<dd><p>Circle that may or may not be intersected</p></dd>
</dl></dd>
<dt><a name="47"><b class="cmd">::math::geometry::intersectionCircleWithCircle</b> <i class="arg">circle1</i> <i class="arg">circle2</i></a></dt>
<dd><p>Determine the points at which the given two circles intersect. There can
be zero, one or two points. (If the two circles touch the circle or are very close,
then one point is returned. An arbitrary margin of 1.0e-10 times the radius of
the first circle is used to determine this situation.)</p>
then one point is returned. An arbitrary margin of 1.0e-10 times the mean of the radii of
the two circles is used to determine this situation.)</p>
<dl class="doctools_arguments">
<dt>list <i class="arg">circle1</i></dt>
<dd><p>First circle</p></dd>
<dt>list <i class="arg">circle2</i></dt>
<dd><p>Second circle</p></dd>
</dl></dd>
<dt><a name="48"><b class="cmd">::math::geometry::tangentLinesToCircle</b> <i class="arg">point</i> <i class="arg">circle</i></a></dt>

Changes to embedded/www/tcllib/files/modules/math/numtheory.html.


1
2
3
4
5
6
7
1
2
3
4
5
6
7
8
+








<div class='fossil-doc' data-title='math::numtheory - Tcl Math Library'>
<style>
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
94
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

121
122
123
124
125
126


127
128
129
130
131
132
133
134
135
136
137











138
139
140
141
142
143
144
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

121
122
123
124
125
126
127
128
129











130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147







-
+


















-
+






+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+







| <a href="../../../toc.html">Table Of Contents</a>
| <a href="../../../../index.html">Keyword Index</a>
| <a href="../../../../toc0.html">Categories</a>
| <a href="../../../../toc1.html">Modules</a>
| <a href="../../../../toc2.html">Applications</a>
 ] <hr>
<div class="doctools">
<h1 class="doctools_title">math::numtheory(n) 1.0 tcllib &quot;Tcl Math Library&quot;</h1>
<h1 class="doctools_title">math::numtheory(n) 1.1.1 tcllib &quot;Tcl Math Library&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>math::numtheory - Number Theory</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a></li>
<li class="doctools_section"><a href="#section2">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
<li class="doctools_section"><a href="#category">Category</a></li>
<li class="doctools_section"><a href="#copyright">Copyright</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl <span class="opt">?8.5?</span></b></li>
<li>package require <b class="pkgname">math::numtheory <span class="opt">?1.0?</span></b></li>
<li>package require <b class="pkgname">math::numtheory <span class="opt">?1.1.1?</span></b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><b class="cmd">math::numtheory::isprime</b> <i class="arg">N</i> <span class="opt">?<i class="arg">option</i> <i class="arg">value</i> ...?</span></a></li>
<li><a href="#2"><b class="cmd">math::numtheory::firstNprimes</b> <i class="arg">N</i></a></li>
<li><a href="#3"><b class="cmd">math::numtheory::primesLowerThan</b> <i class="arg">N</i></a></li>
<li><a href="#4"><b class="cmd">math::numtheory::primeFactors</b> <i class="arg">N</i></a></li>
<li><a href="#5"><b class="cmd">math::numtheory::primesLowerThan</b> <i class="arg">N</i></a></li>
<li><a href="#6"><b class="cmd">math::numtheory::primeFactors</b> <i class="arg">N</i></a></li>
<li><a href="#5"><b class="cmd">math::numtheory::uniquePrimeFactors</b> <i class="arg">N</i></a></li>
<li><a href="#6"><b class="cmd">math::numtheory::factors</b> <i class="arg">N</i></a></li>
<li><a href="#7"><b class="cmd">math::numtheory::totient</b> <i class="arg">N</i></a></li>
<li><a href="#8"><b class="cmd">math::numtheory::moebius</b> <i class="arg">N</i></a></li>
<li><a href="#9"><b class="cmd">math::numtheory::legendre</b> <i class="arg">a</i> <i class="arg">p</i></a></li>
<li><a href="#10"><b class="cmd">math::numtheory::jacobi</b> <i class="arg">a</i> <i class="arg">b</i></a></li>
<li><a href="#11"><b class="cmd">math::numtheory::gcd</b> <i class="arg">m</i> <i class="arg">n</i></a></li>
<li><a href="#12"><b class="cmd">math::numtheory::lcm</b> <i class="arg">m</i> <i class="arg">n</i></a></li>
<li><a href="#13"><b class="cmd">math::numtheory::numberPrimesGauss</b> <i class="arg">N</i></a></li>
<li><a href="#14"><b class="cmd">math::numtheory::numberPrimesLegendre</b> <i class="arg">N</i></a></li>
<li><a href="#15"><b class="cmd">math::numtheory::numberPrimesLegendreModified</b> <i class="arg">N</i></a></li>
<li><a href="#7"><b class="cmd">math::numtheory::uniquePrimeFactors</b> <i class="arg">N</i></a></li>
<li><a href="#8"><b class="cmd">math::numtheory::factors</b> <i class="arg">N</i></a></li>
<li><a href="#9"><b class="cmd">math::numtheory::totient</b> <i class="arg">N</i></a></li>
<li><a href="#10"><b class="cmd">math::numtheory::moebius</b> <i class="arg">N</i></a></li>
<li><a href="#11"><b class="cmd">math::numtheory::legendre</b> <i class="arg">a</i> <i class="arg">p</i></a></li>
<li><a href="#12"><b class="cmd">math::numtheory::jacobi</b> <i class="arg">a</i> <i class="arg">b</i></a></li>
<li><a href="#13"><b class="cmd">math::numtheory::gcd</b> <i class="arg">m</i> <i class="arg">n</i></a></li>
<li><a href="#14"><b class="cmd">math::numtheory::lcm</b> <i class="arg">m</i> <i class="arg">n</i></a></li>
<li><a href="#15"><b class="cmd">math::numtheory::numberPrimesGauss</b> <i class="arg">N</i></a></li>
<li><a href="#16"><b class="cmd">math::numtheory::numberPrimesLegendre</b> <i class="arg">N</i></a></li>
<li><a href="#17"><b class="cmd">math::numtheory::numberPrimesLegendreModified</b> <i class="arg">N</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>This package is for collecting various number-theoretic operations, with
a slight bias to prime numbers.</p>
<dl class="doctools_definitions">
183
184
185
186
187
188
189












190

191
192
193
194
195
196

197
198
199
200
201
202

203
204
205
206
207
208
209

210
211
212
213
214
215

216
217
218
219
220
221
222
223

224
225
226
227
228
229
230
231

232
233
234
235
236
237
238
239

240
241
242
243
244
245
246
247

248
249
250
251
252
253

254
255
256
257
258
259

260
261
262
263
264
265
266
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204

205
206
207
208
209
210

211
212
213
214
215
216

217
218
219
220
221
222
223

224
225
226
227
228
229

230
231
232
233
234
235
236
237

238
239
240
241
242
243
244
245

246
247
248
249
250
251
252
253

254
255
256
257
258
259
260
261

262
263
264
265
266
267

268
269
270
271
272
273

274
275
276
277
278
279
280
281







+
+
+
+
+
+
+
+
+
+
+
+
-
+





-
+





-
+






-
+





-
+







-
+







-
+







-
+







-
+





-
+





-
+







</dl></dd>
<dt><a name="4"><b class="cmd">math::numtheory::primeFactors</b> <i class="arg">N</i></a></dt>
<dd><p>Return a list of the prime numbers in the number N</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number to be factorised</p></dd>
</dl></dd>
<dt><a name="5"><b class="cmd">math::numtheory::primesLowerThan</b> <i class="arg">N</i></a></dt>
<dd><p>Return the prime numbers lower/equal to N</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Maximum number to consider</p></dd>
</dl></dd>
<dt><a name="6"><b class="cmd">math::numtheory::primeFactors</b> <i class="arg">N</i></a></dt>
<dd><p>Return a list of the prime numbers in the number N</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number to be factorised</p></dd>
</dl></dd>
<dt><a name="5"><b class="cmd">math::numtheory::uniquePrimeFactors</b> <i class="arg">N</i></a></dt>
<dt><a name="7"><b class="cmd">math::numtheory::uniquePrimeFactors</b> <i class="arg">N</i></a></dt>
<dd><p>Return a list of the <em>unique</em> prime numbers in the number N</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number to be factorised</p></dd>
</dl></dd>
<dt><a name="6"><b class="cmd">math::numtheory::factors</b> <i class="arg">N</i></a></dt>
<dt><a name="8"><b class="cmd">math::numtheory::factors</b> <i class="arg">N</i></a></dt>
<dd><p>Return a list of all <em>unique</em> factors in the number N, including 1 and N itself</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number to be factorised</p></dd>
</dl></dd>
<dt><a name="7"><b class="cmd">math::numtheory::totient</b> <i class="arg">N</i></a></dt>
<dt><a name="9"><b class="cmd">math::numtheory::totient</b> <i class="arg">N</i></a></dt>
<dd><p>Evaluate the Euler totient function for the number N (number of numbers
relatively prime to N)</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number in question</p></dd>
</dl></dd>
<dt><a name="8"><b class="cmd">math::numtheory::moebius</b> <i class="arg">N</i></a></dt>
<dt><a name="10"><b class="cmd">math::numtheory::moebius</b> <i class="arg">N</i></a></dt>
<dd><p>Evaluate the Moebius function for the number N</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number in question</p></dd>
</dl></dd>
<dt><a name="9"><b class="cmd">math::numtheory::legendre</b> <i class="arg">a</i> <i class="arg">p</i></a></dt>
<dt><a name="11"><b class="cmd">math::numtheory::legendre</b> <i class="arg">a</i> <i class="arg">p</i></a></dt>
<dd><p>Evaluate the Legendre symbol (a/p)</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">a</i> (in)</dt>
<dd><p>Upper number in the symbol</p></dd>
<dt>integer <i class="arg">p</i> (in)</dt>
<dd><p>Lower number in the symbol (must be non-zero)</p></dd>
</dl></dd>
<dt><a name="10"><b class="cmd">math::numtheory::jacobi</b> <i class="arg">a</i> <i class="arg">b</i></a></dt>
<dt><a name="12"><b class="cmd">math::numtheory::jacobi</b> <i class="arg">a</i> <i class="arg">b</i></a></dt>
<dd><p>Evaluate the Jacobi symbol (a/b)</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">a</i> (in)</dt>
<dd><p>Upper number in the symbol</p></dd>
<dt>integer <i class="arg">b</i> (in)</dt>
<dd><p>Lower number in the symbol (must be odd)</p></dd>
</dl></dd>
<dt><a name="11"><b class="cmd">math::numtheory::gcd</b> <i class="arg">m</i> <i class="arg">n</i></a></dt>
<dt><a name="13"><b class="cmd">math::numtheory::gcd</b> <i class="arg">m</i> <i class="arg">n</i></a></dt>
<dd><p>Return the greatest common divisor of <i class="term">m</i> and <i class="term">n</i></p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">m</i> (in)</dt>
<dd><p>First number</p></dd>
<dt>integer <i class="arg">n</i> (in)</dt>
<dd><p>Second number</p></dd>
</dl></dd>
<dt><a name="12"><b class="cmd">math::numtheory::lcm</b> <i class="arg">m</i> <i class="arg">n</i></a></dt>
<dt><a name="14"><b class="cmd">math::numtheory::lcm</b> <i class="arg">m</i> <i class="arg">n</i></a></dt>
<dd><p>Return the lowest common multiple of <i class="term">m</i> and <i class="term">n</i></p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">m</i> (in)</dt>
<dd><p>First number</p></dd>
<dt>integer <i class="arg">n</i> (in)</dt>
<dd><p>Second number</p></dd>
</dl></dd>
<dt><a name="13"><b class="cmd">math::numtheory::numberPrimesGauss</b> <i class="arg">N</i></a></dt>
<dt><a name="15"><b class="cmd">math::numtheory::numberPrimesGauss</b> <i class="arg">N</i></a></dt>
<dd><p>Estimate the number of primes according the formula by Gauss.</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number in question</p></dd>
</dl></dd>
<dt><a name="14"><b class="cmd">math::numtheory::numberPrimesLegendre</b> <i class="arg">N</i></a></dt>
<dt><a name="16"><b class="cmd">math::numtheory::numberPrimesLegendre</b> <i class="arg">N</i></a></dt>
<dd><p>Estimate the number of primes according the formula by Legendre.</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number in question</p></dd>
</dl></dd>
<dt><a name="15"><b class="cmd">math::numtheory::numberPrimesLegendreModified</b> <i class="arg">N</i></a></dt>
<dt><a name="17"><b class="cmd">math::numtheory::numberPrimesLegendreModified</b> <i class="arg">N</i></a></dt>
<dd><p>Estimate the number of primes according the modified formula by Legendre.</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number in question</p></dd>
</dl></dd>
</dl>
</div>

Added embedded/www/tcllib/files/modules/math/trig.html.




















































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

<div class='fossil-doc' data-title='math::trig - Tcl Math Library'>
<style>
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
	background: 	#FFFFFF;
	color:	 	black;
    }
    DIV.doctools {
	margin-left:	10%;
	margin-right:	10%;
    }
    DIV.doctools H1,DIV.doctools H2 {
	margin-left:	-5%;
    }
    H1, H2, H3, H4 {
	margin-top: 	1em;
	font-family:	sans-serif;
	font-size:	large;
	color:		#005A9C;
	background: 	transparent;
	text-align:		left;
    }
    H1.doctools_title {
	text-align: center;
    }
    UL,OL {
	margin-right: 0em;
	margin-top: 3pt;
	margin-bottom: 3pt;
    }
    UL LI {
	list-style: disc;
    }
    OL LI {
	list-style: decimal;
    }
    DT {
	padding-top: 	1ex;
    }
    UL.doctools_toc,UL.doctools_toc UL, UL.doctools_toc UL UL {
	font:		normal 12pt/14pt sans-serif;
	list-style:	none;
    }
    LI.doctools_section, LI.doctools_subsection {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding: 	0em;
    }
    PRE {
	display: 	block;
	font-family:	monospace;
	white-space:	pre;
	margin:		0%;
	padding-top:	0.5ex;
	padding-bottom:	0.5ex;
	padding-left:	1ex;
	padding-right:	1ex;
	width:		100%;
    }
    PRE.doctools_example {
	color: 		black;
	background: 	#f5dcb3;
	border:		1px solid black;
    }
    UL.doctools_requirements LI, UL.doctools_syntax LI {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding:	0em;
    }
    DIV.doctools_synopsis {
	color: 		black;
	background: 	#80ffff;
	border:		1px solid black;
	font-family:	serif;
	margin-top: 	1em;
	margin-bottom: 	1em;
    }
    UL.doctools_syntax {
	margin-top: 	1em;
	border-top:	1px solid black;
    }
    UL.doctools_requirements {
	margin-bottom: 	1em;
	border-bottom:	1px solid black;
    }
</style>
 <hr> [
   <a href="../../../../toc.html">Main Table Of Contents</a>
| <a href="../../../toc.html">Table Of Contents</a>
| <a href="../../../../index.html">Keyword Index</a>
| <a href="../../../../toc0.html">Categories</a>
| <a href="../../../../toc1.html">Modules</a>
| <a href="../../../../toc2.html">Applications</a>
 ] <hr>
<div class="doctools">
<h1 class="doctools_title">math::trig(n) 1.0.0 tcllib &quot;Tcl Math Library&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>math::trig - Trigonometric anf hyperbolic functions</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a></li>
<li class="doctools_section"><a href="#section2">FUNCTIONS</a></li>
<li class="doctools_section"><a href="#section3">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
<li class="doctools_section"><a href="#category">Category</a></li>
<li class="doctools_section"><a href="#copyright">Copyright</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl 8.5</b></li>
<li>package require <b class="pkgname">math::trig 1.0.0</b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><b class="cmd">::math::trig::radian_reduced</b> <i class="arg">angle</i></a></li>
<li><a href="#2"><b class="cmd">::math::trig::degree_reduced</b> <i class="arg">angle</i></a></li>
<li><a href="#3"><b class="cmd">::math::trig::cosec</b> <i class="arg">angle</i></a></li>
<li><a href="#4"><b class="cmd">::math::trig::sec</b> <i class="arg">angle</i></a></li>
<li><a href="#5"><b class="cmd">::math::trig::cotan</b> <i class="arg">angle</i></a></li>
<li><a href="#6"><b class="cmd">::math::trig::acosec</b> <i class="arg">value</i></a></li>
<li><a href="#7"><b class="cmd">::math::trig::asec</b> <i class="arg">value</i></a></li>
<li><a href="#8"><b class="cmd">::math::trig::acotan</b> <i class="arg">value</i></a></li>
<li><a href="#9"><b class="cmd">::math::trig::cosech</b> <i class="arg">value</i></a></li>
<li><a href="#10"><b class="cmd">::math::trig::sech</b> <i class="arg">value</i></a></li>
<li><a href="#11"><b class="cmd">::math::trig::cotanh</b> <i class="arg">value</i></a></li>
<li><a href="#12"><b class="cmd">::math::trig::asinh</b> <i class="arg">value</i></a></li>
<li><a href="#13"><b class="cmd">::math::trig::acosh</b> <i class="arg">value</i></a></li>
<li><a href="#14"><b class="cmd">::math::trig::atanh</b> <i class="arg">value</i></a></li>
<li><a href="#15"><b class="cmd">::math::trig::acosech</b> <i class="arg">value</i></a></li>
<li><a href="#16"><b class="cmd">::math::trig::asech</b> <i class="arg">value</i></a></li>
<li><a href="#17"><b class="cmd">::math::trig::acotanh</b> <i class="arg">value</i></a></li>
<li><a href="#18"><b class="cmd">::math::trig::sind</b> <i class="arg">angle</i></a></li>
<li><a href="#19"><b class="cmd">::math::trig::cosd</b> <i class="arg">angle</i></a></li>
<li><a href="#20"><b class="cmd">::math::trig::tand</b> <i class="arg">angle</i></a></li>
<li><a href="#21"><b class="cmd">::math::trig::cosecd</b> <i class="arg">angle</i></a></li>
<li><a href="#22"><b class="cmd">::math::trig::secd</b> <i class="arg">angle</i></a></li>
<li><a href="#23"><b class="cmd">::math::trig::cotand</b> <i class="arg">angle</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>The <i class="term">math::trig</i> package defines a set of trigonomic and hyperbolic functions
and their inverses. In addition it defines versions of the trigonomic functions
that take arguments in degrees instead of radians.</p>
<p>For easy use these functions may be imported into the <i class="term">tcl::mathfunc</i> namespace,
so that they can be used directly in the <i class="term">expr</i> command.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">FUNCTIONS</a></h2>
<p>The functions <i class="term">radian_reduced</i> and <i class="term">degree_reduced</i> return a reduced angle, in
respectively radians and degrees, in the intervals [0, 2pi) and [0, 360):</p>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">::math::trig::radian_reduced</b> <i class="arg">angle</i></a></dt>
<dd><p>Return the equivalent angle in the interval [0, 2pi).</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in radians)</p></dd>
</dl></dd>
<dt><a name="2"><b class="cmd">::math::trig::degree_reduced</b> <i class="arg">angle</i></a></dt>
<dd><p>Return the equivalent angle in the interval [0, 360).</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in degrees)</p></dd>
</dl></dd>
</dl>
<p>The following trigonomic functions are defined in addition to the ones defined
in the <i class="term">expr</i> command:</p>
<dl class="doctools_definitions">
<dt><a name="3"><b class="cmd">::math::trig::cosec</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the cosecant of the angle (1/cos(angle))</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in radians)</p></dd>
</dl></dd>
<dt><a name="4"><b class="cmd">::math::trig::sec</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the secant of the angle (1/sin(angle))</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in radians)</p></dd>
</dl></dd>
<dt><a name="5"><b class="cmd">::math::trig::cotan</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the cotangent of the angle (1/tan(angle))</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in radians)</p></dd>
</dl></dd>
</dl>
<p>For these functions also the inverses are defined:</p>
<dl class="doctools_definitions">
<dt><a name="6"><b class="cmd">::math::trig::acosec</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc cosecant of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="7"><b class="cmd">::math::trig::asec</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc secant of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="8"><b class="cmd">::math::trig::acotan</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc cotangent of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
</dl>
<p>The following hyperbolic and inverse hyperbolic functions are defined:</p>
<dl class="doctools_definitions">
<dt><a name="9"><b class="cmd">::math::trig::cosech</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the hyperbolic cosecant of the value (1/sinh(value))</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="10"><b class="cmd">::math::trig::sech</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the hyperbolic secant of the value (1/cosh(value))</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="11"><b class="cmd">::math::trig::cotanh</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the hyperbolic cotangent of the value (1/tanh(value))</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="12"><b class="cmd">::math::trig::asinh</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc hyperbolic sine of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="13"><b class="cmd">::math::trig::acosh</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc hyperbolic cosine of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="14"><b class="cmd">::math::trig::atanh</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc hyperbolic tangent of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="15"><b class="cmd">::math::trig::acosech</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc hyperbolic cosecant of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="16"><b class="cmd">::math::trig::asech</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc hyperbolic secant of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="17"><b class="cmd">::math::trig::acotanh</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc hyperbolic cotangent of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
</dl>
<p>The following versions of the common trigonometric functions and their
inverses are defined:</p>
<dl class="doctools_definitions">
<dt><a name="18"><b class="cmd">::math::trig::sind</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the sine of the angle (in degrees)</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in degrees)</p></dd>
</dl></dd>
<dt><a name="19"><b class="cmd">::math::trig::cosd</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the cosine of the angle (in degrees)</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in radians)</p></dd>
</dl></dd>
<dt><a name="20"><b class="cmd">::math::trig::tand</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the cotangent of the angle (in degrees)</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in degrees)</p></dd>
</dl></dd>
<dt><a name="21"><b class="cmd">::math::trig::cosecd</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the cosecant of the angle (in degrees)</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in degrees)</p></dd>
</dl></dd>
<dt><a name="22"><b class="cmd">::math::trig::secd</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the secant of the angle (in degrees)</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in degrees)</p></dd>
</dl></dd>
<dt><a name="23"><b class="cmd">::math::trig::cotand</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the cotangent of the angle (in degrees)</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in degrees)</p></dd>
</dl></dd>
</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">Bugs, Ideas, Feedback</a></h2>
<p>This document, and the package it describes, will undoubtedly contain
bugs and other problems.
Please report such in the category <em>math :: trig</em> of the
<a href="http://core.tcl.tk/tcllib/reportlist">Tcllib Trackers</a>.
Please also report any ideas for enhancements you may have for either
package and/or documentation.</p>
<p>When proposing code changes, please provide <em>unified diffs</em>,
i.e the output of <b class="const">diff -u</b>.</p>
<p>Note further that <em>attachments</em> are strongly preferred over
inlined patches. Attachments can be made by going to the <b class="const">Edit</b>
form of the ticket immediately after its creation, and then using the
left-most button in the secondary navigation bar.</p>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>
<p><a href="../../../../index.html#math">math</a>, <a href="../../../../index.html#trigonometry">trigonometry</a></p>
</div>
<div id="category" class="doctools_section"><h2><a name="category">Category</a></h2>
<p>Mathematics</p>
</div>
<div id="copyright" class="doctools_section"><h2><a name="copyright">Copyright</a></h2>
<p>Copyright &copy; 2018 Arjen Markus</p>
</div>
</div>

Changes to embedded/www/tcllib/files/modules/nns/nns_client.html.

219
220
221
222
223
224
225
226

227
228
229
230
231
232
233
219
220
221
222
223
224
225

226
227
228
229
230
231
232
233







-
+







be found in section <span class="sectref"><a href="#section5">OPTIONS</a></span>.</p></dd>
<dt><a name="8"><b class="cmd">::nameserv::configure</b></a></dt>
<dd><p>In this form the command returns a dictionary of all supported
options, and their current values. The list of supported options and
their meaning can be found in section <span class="sectref"><a href="#section5">OPTIONS</a></span>.</p></dd>
<dt><a name="9"><b class="cmd">::nameserv::configure</b> <b class="option">-option</b></a></dt>
<dd><p>In this form the command is an alias for
&quot;<b class="cmd">::nameserv::cget</b> <b class="option">-option</b>]&quot;.
&quot;<b class="cmd">::nameserv::cget</b> <b class="option">-option</b>&quot;.
The list of supported options and their meaning can be found in
section <span class="sectref"><a href="#section5">OPTIONS</a></span>.</p></dd>
<dt><a name="10"><b class="cmd">::nameserv::configure</b> <b class="option">-option</b> <i class="arg">value</i>...</a></dt>
<dd><p>In this form the command is used to configure one or more of the
supported options. At least one option has to be specified, and each
option is followed by its new value.
The list of supported options and their meaning can be found in

Changes to embedded/www/tcllib/files/modules/nns/nns_server.html.

174
175
176
177
178
179
180
181

182
183
184
185
186
187
188
174
175
176
177
178
179
180

181
182
183
184
185
186
187
188







-
+







be found in section <span class="sectref"><a href="#section3">OPTIONS</a></span>.</p></dd>
<dt><a name="5"><b class="cmd">::nameserv::server::configure</b></a></dt>
<dd><p>In this form the command returns a dictionary of all supported
options, and their current values. The list of supported options and
their meaning can be found in section <span class="sectref"><a href="#section3">OPTIONS</a></span>.</p></dd>
<dt><a name="6"><b class="cmd">::nameserv::server::configure</b> <b class="option">-option</b></a></dt>
<dd><p>In this form the command is an alias for
&quot;<b class="cmd">::nameserv::server::cget</b> <b class="option">-option</b>]&quot;.
&quot;<b class="cmd">::nameserv::server::cget</b> <b class="option">-option</b>&quot;.
The list of supported options and their meaning can be found in
section <span class="sectref"><a href="#section3">OPTIONS</a></span>.</p></dd>
<dt><a name="7"><b class="cmd">::nameserv::server::configure</b> <b class="option">-option</b> <i class="arg">value</i>...</a></dt>
<dd><p>In this form the command is used to configure one or more of the
supported options. At least one option has to be specified, and each
option is followed by its new value.
The list of supported options and their meaning can be found in

Changes to embedded/www/tcllib/files/modules/oometa/oometa.html.

233
234
235
236
237
238
239
240

241
242
243
244
245
246
247
233
234
235
236
237
238
239

240
241
242
243
244
245
246
247







-
+







<dt><a name="11"><b class="cmd">oo::object method meta</b></a></dt>
<dd><p>The package injects a new method <b class="cmd">meta</b> into <b class="cmd">oo::object</b>. <b class="cmd">oo::object</b> combines the data
for its class (as provided by <b class="cmd">oo::meta::metadata</b>), with a local variable <em>meta</em> to
produce a local picture of metadata.
This method provides the following additional commands:</p></dd>
<dt><a name="12"><b class="cmd">oo::object method meta cget</b> <span class="opt">?<i class="arg">field</i>?</span> <span class="opt">?<i class="arg">...</i>?</span> <i class="arg">field</i></a></dt>
<dd><p>Attempts to locate a singlar leaf, and return its value. For single option lookups, this
is faster than <b class="cmd">my meta getnull</b> <span class="opt">?<i class="arg">field</i>?</span> <span class="opt">?<i class="arg">...</i>?</span> <i class="arg">field</i>], because
is faster than <b class="cmd">my meta getnull</b> <span class="opt">?<i class="arg">field</i>?</span> <span class="opt">?<i class="arg">...</i>?</span> <i class="arg">field</i>, because
it performs a search instead directly instead of producing the recursive merge product
between the class metadata, the local <em>meta</em> variable, and THEN performing the search.</p></dd>
</dl>
</div>
<div id="section5" class="doctools_section"><h2><a name="section5">Bugs, Ideas, Feedback</a></h2>
<p>This document, and the package it describes, will undoubtedly contain
bugs and other problems.

Changes to embedded/www/tcllib/files/modules/pop3d/pop3d.html.

264
265
266
267
268
269
270
271
272

273
274
275
276
277
278
279
264
265
266
267
268
269
270


271
272
273
274
275
276
277
278







-
-
+







<p>Here we describe the interface which has to be provided by the storage
callback so that pop3 servers following the interface of this module
are able to use it. The <i class="arg">mbox</i> argument is the storage reference
as returned by the <b class="method">lookup</b> method of the authentication
command, see section <span class="sectref"><a href="#section3">Authentication</a></span>.</p>
<dl class="doctools_definitions">
<dt><a name="14"><i class="arg">storageCmd</i> <b class="method">dele</b> <i class="arg">mbox</i> <i class="arg">msgList</i></a></dt>
<dd><p>]
Deletes the messages whose numeric ids are contained in the
<dd><p>Deletes the messages whose numeric ids are contained in the
<i class="arg">msgList</i> from the mailbox specified via <i class="arg">mbox</i>.</p></dd>
<dt><a name="15"><i class="arg">storageCmd</i> <b class="method">lock</b> <i class="arg">mbox</i></a></dt>
<dd><p>This method locks the specified mailbox for use by a single connection
to the server. This is necessary to prevent havoc if several
connections to the same mailbox are open. The complementary method is
<b class="method">unlock</b>. The command will return true if the lock could be set
successfully or false if not.</p></dd>

Changes to embedded/www/tcllib/files/modules/practcl/practcl.html.

1

2
3


4
5
6
7
8
9
10
1
2


3
4
5
6
7
8
9
10
11

+
-
-
+
+








<!DOCTYPE html><html><head>
<div class='fossil-doc' data-title='practcl - The The Proper Rational API for C to Tool Command Language Module'>
<style>
<title>practcl - The The Proper Rational API for C to Tool Command Language Module</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
	background: 	#FFFFFF;
	color:	 	black;
85
86
87
88
89
90
91
92
93
94



95
96


97
98
99
100
101
102





103
104
105
106
107
108
109
110
111
112






































113
114
115
116
117
118
119
120
121
122

123
124


125
126
127
128
129
130
131
132
133
134







































































































































































































































































135
136
137
138
139
140
141
142
































































































































































































































































































































































































































































































































































































































































































































































































143
144

145
146

147
148
149




150
151
152
153
154
155
156
157
158
159
160










161
162
163
164
165
166
167
168

169

170
171
172
173
174
175
176
177
178
179
180
181
182
183
184

185
186
187
188
189
190
191
192

86
87
88
89
90
91
92



93
94
95


96
97






98
99
100
101
102
103
104
105
106
107
108
109
110


111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

158
159
160
161
162










163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432

433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201

1202


1203


1204
1205
1206
1207
1208
1209










1210
1211
1212
1213
1214
1215
1216
1217
1218
1219






1220
1221
1222

1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237

1238
1239
1240
1241
1242
1243
1244
1245

1246







-
-
-
+
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+








-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+


+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
-
-
+
-
-

+
+
+
+

-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-


+
-
+














-
+







-
+
	margin-top: 	1em;
	border-top:	1px solid black;
    }
    UL.doctools_requirements {
	margin-bottom: 	1em;
	border-bottom:	1px solid black;
    }
</style>
 <hr> [
   <a href="../../../../toc.html">Main Table Of Contents</a>
--></style>
</head>
<!-- Generated from file 'practcl.man' by tcllib/doctools with format 'html'
| <a href="../../../toc.html">Table Of Contents</a>
| <a href="../../../../index.html">Keyword Index</a>
   -->
<!-- Copyright &amp;copy; 2016-2018 Sean Woods &amp;lt;yoda@etoyoc.com&amp;gt;
| <a href="../../../../toc0.html">Categories</a>
| <a href="../../../../toc1.html">Modules</a>
| <a href="../../../../toc2.html">Applications</a>
 ] <hr>
<div class="doctools">
<h1 class="doctools_title">practcl(n) 0.11 tcllib &quot;The The Proper Rational API for C to Tool Command Language Module&quot;</h1>
   -->
<!-- practcl.n
   -->
<body><div class="doctools">
<h1 class="doctools_title">practcl(n) 0.12 practcl &quot;The The Proper Rational API for C to Tool Command Language Module&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>practcl - The Practcl Module</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a></li>
<li class="doctools_section"><a href="#section2">COMMANDS</a></li>
<li class="doctools_section"><a href="#section3">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#section2">Commands</a></li>
<li class="doctools_section"><a href="#section3">Classes</a>
<ul>
<li class="doctools_subsection"><a href="#subsection1">Class  practcl::metaclass</a></li>
<li class="doctools_subsection"><a href="#subsection2">Class  practcl::toolset</a></li>
<li class="doctools_subsection"><a href="#subsection3">Class  practcl::toolset.gcc</a></li>
<li class="doctools_subsection"><a href="#subsection4">Class  practcl::toolset.msvc</a></li>
<li class="doctools_subsection"><a href="#subsection5">Class  practcl::make_obj</a></li>
<li class="doctools_subsection"><a href="#subsection6">Class  practcl::object</a></li>
<li class="doctools_subsection"><a href="#subsection7">Class  practcl::dynamic</a></li>
<li class="doctools_subsection"><a href="#subsection8">Class  practcl::product</a></li>
<li class="doctools_subsection"><a href="#subsection9">Class  practcl::product.cheader</a></li>
<li class="doctools_subsection"><a href="#subsection10">Class  practcl::product.csource</a></li>
<li class="doctools_subsection"><a href="#subsection11">Class  practcl::product.clibrary</a></li>
<li class="doctools_subsection"><a href="#subsection12">Class  practcl::product.dynamic</a></li>
<li class="doctools_subsection"><a href="#subsection13">Class  practcl::product.critcl</a></li>
<li class="doctools_subsection"><a href="#subsection14">Class  practcl::module</a></li>
<li class="doctools_subsection"><a href="#subsection15">Class  practcl::project</a></li>
<li class="doctools_subsection"><a href="#subsection16">Class  practcl::library</a></li>
<li class="doctools_subsection"><a href="#subsection17">Class  practcl::tclkit</a></li>
<li class="doctools_subsection"><a href="#subsection18">Class  practcl::distribution</a></li>
<li class="doctools_subsection"><a href="#subsection19">Class  practcl::distribution.snapshot</a></li>
<li class="doctools_subsection"><a href="#subsection20">Class  practcl::distribution.fossil</a></li>
<li class="doctools_subsection"><a href="#subsection21">Class  practcl::distribution.git</a></li>
<li class="doctools_subsection"><a href="#subsection22">Class  practcl::subproject</a></li>
<li class="doctools_subsection"><a href="#subsection23">Class  practcl::subproject.source</a></li>
<li class="doctools_subsection"><a href="#subsection24">Class  practcl::subproject.teapot</a></li>
<li class="doctools_subsection"><a href="#subsection25">Class  practcl::subproject.kettle</a></li>
<li class="doctools_subsection"><a href="#subsection26">Class  practcl::subproject.critcl</a></li>
<li class="doctools_subsection"><a href="#subsection27">Class  practcl::subproject.sak</a></li>
<li class="doctools_subsection"><a href="#subsection28">Class  practcl::subproject.binary</a></li>
<li class="doctools_subsection"><a href="#subsection29">Class  practcl::subproject.tea</a></li>
<li class="doctools_subsection"><a href="#subsection30">Class  practcl::subproject.library</a></li>
<li class="doctools_subsection"><a href="#subsection31">Class  practcl::subproject.external</a></li>
<li class="doctools_subsection"><a href="#subsection32">Class  practcl::subproject.core</a></li>
</ul>
</li>
<li class="doctools_section"><a href="#section4">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
<li class="doctools_section"><a href="#category">Category</a></li>
<li class="doctools_section"><a href="#copyright">Copyright</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">TclOO 1.0</b></li>
<li>package require <b class="pkgname">practcl 0.11</b></li>
<li>package require <b class="pkgname">practcl 0.12</b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1">proc <b class="cmd">Proc</b> <i class="arg">name</i> <i class="arg">arglist</i> <i class="arg">body</i></a></li>
<li><a href="#2">proc <b class="cmd">noop</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#1"><b class="cmd">CPUTS</b> <i class="arg">varname</i> <i class="arg">body</i> <span class="opt">?<i class="arg">body</i>...?</span></a></li>
<li><a href="#2"><b class="cmd">practcl::_isdirectory</b> <i class="arg">path</i></a></li>
<li><a href="#3"><b class="cmd">practcl::object</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#4"><b class="cmd">practcl::library</b> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#5"><b class="cmd">practcl::exe</b> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#6"><b class="cmd">practcl::product</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#7"><b class="cmd">practcl::cheader</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#8"><b class="cmd">practcl::csource</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#9"><b class="cmd">practcl::module</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#10"><b class="cmd">practcl::submodule</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#3">proc <b class="cmd">practcl::debug</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#4">proc <b class="cmd">practcl::doexec</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#5">proc <b class="cmd">practcl::doexec_in</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#6">proc <b class="cmd">practcl::dotclexec</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#7">proc <b class="cmd">practcl::domake</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#8">proc <b class="cmd">practcl::domake.tcl</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#9">proc <b class="cmd">practcl::fossil</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#10">proc <b class="cmd">practcl::fossil_status</b> <i class="arg">dir</i></a></li>
<li><a href="#11">proc <b class="cmd">practcl::os</b></a></li>
<li><a href="#12">proc <b class="cmd">practcl::mkzip</b> <i class="arg">exename</i> <i class="arg">barekit</i> <i class="arg">vfspath</i></a></li>
<li><a href="#13">proc <b class="cmd">practcl::sort_dict</b> <i class="arg">list</i></a></li>
<li><a href="#14">proc <b class="cmd">practcl::local_os</b></a></li>
<li><a href="#15">proc <b class="cmd">practcl::config.tcl</b> <i class="arg">path</i></a></li>
<li><a href="#16">proc <b class="cmd">practcl::read_configuration</b> <i class="arg">path</i></a></li>
<li><a href="#17">proc <b class="cmd">practcl::tcllib_require</b> <i class="arg">pkg</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#18">proc <b class="cmd">practcl::platform::tcl_core_options</b> <i class="arg">os</i></a></li>
<li><a href="#19">proc <b class="cmd">practcl::platform::tk_core_options</b> <i class="arg">os</i></a></li>
<li><a href="#20">proc <b class="cmd">practcl::read_rc_file</b> <i class="arg">filename</i> <span class="opt">?<i class="arg">localdat</i> <b class="const"></b>?</span></a></li>
<li><a href="#21">proc <b class="cmd">practcl::read_sh_subst</b> <i class="arg">line</i> <i class="arg">info</i></a></li>
<li><a href="#22">proc <b class="cmd">practcl::read_sh_file</b> <i class="arg">filename</i> <span class="opt">?<i class="arg">localdat</i> <b class="const"></b>?</span></a></li>
<li><a href="#23">proc <b class="cmd">practcl::read_Config.sh</b> <i class="arg">filename</i></a></li>
<li><a href="#24">proc <b class="cmd">practcl::read_Makefile</b> <i class="arg">filename</i></a></li>
<li><a href="#25">proc <b class="cmd">practcl::cputs</b> <i class="arg">varname</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#26">proc <b class="cmd">practcl::tcl_to_c</b> <i class="arg">body</i></a></li>
<li><a href="#27">proc <b class="cmd">practcl::_tagblock</b> <i class="arg">text</i> <span class="opt">?<i class="arg">style</i> <b class="const">tcl</b>?</span> <span class="opt">?<i class="arg">note</i> <b class="const"></b>?</span></a></li>
<li><a href="#28">proc <b class="cmd">practcl::de_shell</b> <i class="arg">data</i></a></li>
<li><a href="#29">proc <b class="cmd">practcl::cat</b> <i class="arg">fname</i></a></li>
<li><a href="#30">proc <b class="cmd">practcl::grep</b> <i class="arg">pattern</i> <span class="opt">?<i class="arg">files</i> <b class="const"></b>?</span></a></li>
<li><a href="#31">proc <b class="cmd">practcl::file_lexnormalize</b> <i class="arg">sp</i></a></li>
<li><a href="#32">proc <b class="cmd">practcl::file_relative</b> <i class="arg">base</i> <i class="arg">dst</i></a></li>
<li><a href="#33">proc <b class="cmd">practcl::log</b> <i class="arg">fname</i> <i class="arg">comment</i></a></li>
<li><a href="#34">proc <b class="cmd">practcl::_isdirectory</b> <i class="arg">name</i></a></li>
<li><a href="#35">proc <b class="cmd">practcl::_pkgindex_directory</b> <i class="arg">path</i></a></li>
<li><a href="#36">proc <b class="cmd">practcl::_pkgindex_path_subdir</b> <i class="arg">path</i></a></li>
<li><a href="#37">proc <b class="cmd">practcl::pkgindex_path</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#38">proc <b class="cmd">practcl::installDir</b> <i class="arg">d1</i> <i class="arg">d2</i></a></li>
<li><a href="#39">proc <b class="cmd">practcl::copyDir</b> <i class="arg">d1</i> <i class="arg">d2</i> <span class="opt">?<i class="arg">toplevel</i> <b class="const">1</b>?</span></a></li>
<li><a href="#40">proc <b class="cmd">practcl::trigger</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#41">proc <b class="cmd">practcl::depends</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#42">proc <b class="cmd">practcl::target</b> <i class="arg">name</i> <i class="arg">info</i> <span class="opt">?<i class="arg">action</i> <b class="const"></b>?</span></a></li>
<li><a href="#43">method <b class="cmd">_MorphPatterns</b></a></li>
<li><a href="#44">method <b class="cmd">define</b> <i class="arg">submethod</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#45">method <b class="cmd">graft</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#46">method <b class="cmd">initialize</b></a></li>
<li><a href="#47">method <b class="cmd">link</b> <i class="arg">command</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#48">method <b class="cmd">morph</b> <i class="arg">classname</i></a></li>
<li><a href="#49">method <b class="cmd">mixin</b> <i class="arg">slot</i> <i class="arg">classname</i></a></li>
<li><a href="#50">method <b class="cmd">organ</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#51">method <b class="cmd">script</b> <i class="arg">script</i></a></li>
<li><a href="#52">method <b class="cmd">select</b></a></li>
<li><a href="#53">method <b class="cmd">source</b> <i class="arg">filename</i></a></li>
<li><a href="#54">method <b class="cmd">select</b> <i class="arg">object</i></a></li>
<li><a href="#55">method <b class="cmd">config.sh</b></a></li>
<li><a href="#56">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></li>
<li><a href="#57">method <b class="cmd">MakeDir</b> <i class="arg">srcdir</i></a></li>
<li><a href="#58">method <b class="cmd">read_configuration</b></a></li>
<li><a href="#59">method <b class="cmd">build-cflags</b> <i class="arg">PROJECT</i> <i class="arg">DEFS</i> <i class="arg">namevar</i> <i class="arg">versionvar</i> <i class="arg">defsvar</i></a></li>
<li><a href="#60">method <b class="cmd">critcl</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#61">method <b class="cmd">make-autodetect</b></a></li>
<li><a href="#62">method <b class="cmd">Autoconf</b></a></li>
<li><a href="#63">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></li>
<li><a href="#64">method <b class="cmd">ConfigureOpts</b></a></li>
<li><a href="#65">method <b class="cmd">MakeDir</b> <i class="arg">srcdir</i></a></li>
<li><a href="#66">method <b class="cmd">make-autodetect</b></a></li>
<li><a href="#67">method <b class="cmd">make-clean</b></a></li>
<li><a href="#68">method <b class="cmd">make-compile</b></a></li>
<li><a href="#69">method <b class="cmd">make-install</b> <i class="arg">DEST</i></a></li>
<li><a href="#70">method <b class="cmd">build-compile-sources</b> <i class="arg">PROJECT</i> <i class="arg">COMPILE</i> <i class="arg">CPPCOMPILE</i> <i class="arg">INCLUDES</i></a></li>
<li><a href="#71">method <b class="cmd">build-Makefile</b> <i class="arg">path</i> <i class="arg">PROJECT</i></a></li>
<li><a href="#72">method <b class="cmd">build-library</b> <i class="arg">outfile</i> <i class="arg">PROJECT</i></a></li>
<li><a href="#73">method <b class="cmd">build-tclsh</b> <i class="arg">outfile</i> <i class="arg">PROJECT</i></a></li>
<li><a href="#74">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></li>
<li><a href="#75">method <b class="cmd">make-autodetect</b></a></li>
<li><a href="#76">method <b class="cmd">make-clean</b></a></li>
<li><a href="#77">method <b class="cmd">make-compile</b></a></li>
<li><a href="#78">method <b class="cmd">make-install</b> <i class="arg">DEST</i></a></li>
<li><a href="#79">method <b class="cmd">MakeDir</b> <i class="arg">srcdir</i></a></li>
<li><a href="#80">method <b class="cmd">NmakeOpts</b></a></li>
<li><a href="#81">method <b class="cmd">constructor</b> <i class="arg">module_object</i> <i class="arg">name</i> <i class="arg">info</i> <span class="opt">?<i class="arg">action_body</i> <b class="const"></b>?</span></a></li>
<li><a href="#82">method <b class="cmd">do</b></a></li>
<li><a href="#83">method <b class="cmd">check</b></a></li>
<li><a href="#84">method <b class="cmd">output</b></a></li>
<li><a href="#85">method <b class="cmd">reset</b></a></li>
<li><a href="#86">method <b class="cmd">triggers</b></a></li>
<li><a href="#87">method <b class="cmd">constructor</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#88">method <b class="cmd">child</b> <i class="arg">method</i></a></li>
<li><a href="#89">method <b class="cmd">go</b></a></li>
<li><a href="#90">method <b class="cmd">cstructure</b> <i class="arg">name</i> <i class="arg">definition</i> <span class="opt">?<i class="arg">argdat</i> <b class="const"></b>?</span></a></li>
<li><a href="#91">method <b class="cmd">include</b> <i class="arg">header</i></a></li>
<li><a href="#92">method <b class="cmd">include_dir</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#93">method <b class="cmd">include_directory</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#94">method <b class="cmd">c_header</b> <i class="arg">body</i></a></li>
<li><a href="#95">method <b class="cmd">c_code</b> <i class="arg">body</i></a></li>
<li><a href="#96">method <b class="cmd">c_function</b> <i class="arg">header</i> <i class="arg">body</i> <span class="opt">?<i class="arg">info</i> <b class="const"></b>?</span></a></li>
<li><a href="#97">method <b class="cmd">c_tcloomethod</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></li>
<li><a href="#98">method <b class="cmd">cmethod</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></li>
<li><a href="#99">method <b class="cmd">c_tclproc_nspace</b> <i class="arg">nspace</i></a></li>
<li><a href="#100">method <b class="cmd">c_tclcmd</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></li>
<li><a href="#101">method <b class="cmd">c_tclproc_raw</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></li>
<li><a href="#102">method <b class="cmd">tcltype</b> <i class="arg">name</i> <i class="arg">argdat</i></a></li>
<li><a href="#103">method <b class="cmd">project-compile-products</b></a></li>
<li><a href="#104">method <b class="cmd">implement</b> <i class="arg">path</i></a></li>
<li><a href="#105">method <b class="cmd">initialize</b></a></li>
<li><a href="#106">method <b class="cmd">linktype</b></a></li>
<li><a href="#107">method <b class="cmd">generate-cfile-constant</b></a></li>
<li><a href="#108">method <b class="cmd">generate-cfile-header</b></a></li>
<li><a href="#109">method <b class="cmd">generate-cfile-tclapi</b></a></li>
<li><a href="#110">method <b class="cmd">generate-loader-module</b></a></li>
<li><a href="#111">method <b class="cmd">Collate_Source</b> <i class="arg">CWD</i></a></li>
<li><a href="#112">method <b class="cmd">select</b></a></li>
<li><a href="#113">method <b class="cmd">select</b> <i class="arg">object</i></a></li>
<li><a href="#114">method <b class="cmd">code</b> <i class="arg">section</i> <i class="arg">body</i></a></li>
<li><a href="#115">method <b class="cmd">Collate_Source</b> <i class="arg">CWD</i></a></li>
<li><a href="#116">method <b class="cmd">project-compile-products</b></a></li>
<li><a href="#117">method <b class="cmd">generate-debug</b> <span class="opt">?<i class="arg">spaces</i> <b class="const"></b>?</span></a></li>
<li><a href="#118">method <b class="cmd">generate-cfile-constant</b></a></li>
<li><a href="#119">method <b class="cmd">generate-cfile-public-structure</b></a></li>
<li><a href="#120">method <b class="cmd">generate-cfile-header</b></a></li>
<li><a href="#121">method <b class="cmd">generate-cfile-global</b></a></li>
<li><a href="#122">method <b class="cmd">generate-cfile-private-typedef</b></a></li>
<li><a href="#123">method <b class="cmd">generate-cfile-private-structure</b></a></li>
<li><a href="#124">method <b class="cmd">generate-cfile-functions</b></a></li>
<li><a href="#125">method <b class="cmd">generate-cfile-tclapi</b></a></li>
<li><a href="#126">method <b class="cmd">generate-hfile-public-define</b></a></li>
<li><a href="#127">method <b class="cmd">generate-hfile-public-macro</b></a></li>
<li><a href="#128">method <b class="cmd">generate-hfile-public-typedef</b></a></li>
<li><a href="#129">method <b class="cmd">generate-hfile-public-structure</b></a></li>
<li><a href="#130">method <b class="cmd">generate-hfile-public-headers</b></a></li>
<li><a href="#131">method <b class="cmd">generate-hfile-public-function</b></a></li>
<li><a href="#132">method <b class="cmd">generate-hfile-public-includes</b></a></li>
<li><a href="#133">method <b class="cmd">generate-hfile-public-verbatim</b></a></li>
<li><a href="#134">method <b class="cmd">generate-loader-external</b></a></li>
<li><a href="#135">method <b class="cmd">generate-loader-module</b></a></li>
<li><a href="#136">method <b class="cmd">generate-stub-function</b></a></li>
<li><a href="#137">method <b class="cmd">IncludeAdd</b> <i class="arg">headervar</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#138">method <b class="cmd">generate-tcl-loader</b></a></li>
<li><a href="#139">method <b class="cmd">generate-tcl-pre</b></a></li>
<li><a href="#140">method <b class="cmd">generate-tcl-post</b></a></li>
<li><a href="#141">method <b class="cmd">linktype</b></a></li>
<li><a href="#142">method <b class="cmd">Ofile</b> <i class="arg">filename</i></a></li>
<li><a href="#143">method <b class="cmd">project-static-packages</b></a></li>
<li><a href="#144">method <b class="cmd">toolset-include-directory</b></a></li>
<li><a href="#145">method <b class="cmd">target</b> <i class="arg">method</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#146">method <b class="cmd">project-compile-products</b></a></li>
<li><a href="#147">method <b class="cmd">generate-loader-module</b></a></li>
<li><a href="#148">method <b class="cmd">project-compile-products</b></a></li>
<li><a href="#149">method <b class="cmd">linker-products</b> <i class="arg">configdict</i></a></li>
<li><a href="#150">method <b class="cmd">initialize</b></a></li>
<li><a href="#151">method <b class="cmd">_MorphPatterns</b></a></li>
<li><a href="#152">method <b class="cmd">add</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#153">method <b class="cmd">install-headers</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#154">method <b class="cmd">make</b> <i class="arg">command</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#155">method <b class="cmd">child</b> <i class="arg">which</i></a></li>
<li><a href="#156">method <b class="cmd">generate-c</b></a></li>
<li><a href="#157">method <b class="cmd">generate-h</b></a></li>
<li><a href="#158">method <b class="cmd">generate-loader</b></a></li>
<li><a href="#159">method <b class="cmd">initialize</b></a></li>
<li><a href="#160">method <b class="cmd">implement</b> <i class="arg">path</i></a></li>
<li><a href="#161">method <b class="cmd">linktype</b></a></li>
<li><a href="#162">method <b class="cmd">_MorphPatterns</b></a></li>
<li><a href="#163">method <b class="cmd">constructor</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#164">method <b class="cmd">add_object</b> <i class="arg">object</i></a></li>
<li><a href="#165">method <b class="cmd">add_project</b> <i class="arg">pkg</i> <i class="arg">info</i> <span class="opt">?<i class="arg">oodefine</i> <b class="const"></b>?</span></a></li>
<li><a href="#166">method <b class="cmd">add_tool</b> <i class="arg">pkg</i> <i class="arg">info</i> <span class="opt">?<i class="arg">oodefine</i> <b class="const"></b>?</span></a></li>
<li><a href="#167">method <b class="cmd">build-tclcore</b></a></li>
<li><a href="#168">method <b class="cmd">child</b> <i class="arg">which</i></a></li>
<li><a href="#169">method <b class="cmd">linktype</b></a></li>
<li><a href="#170">method <b class="cmd">project</b> <i class="arg">pkg</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#171">method <b class="cmd">tclcore</b></a></li>
<li><a href="#172">method <b class="cmd">tkcore</b></a></li>
<li><a href="#173">method <b class="cmd">tool</b> <i class="arg">pkg</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#174">method <b class="cmd">clean</b> <i class="arg">PATH</i></a></li>
<li><a href="#175">method <b class="cmd">project-compile-products</b></a></li>
<li><a href="#176">method <b class="cmd">go</b></a></li>
<li><a href="#177">method <b class="cmd">generate-decls</b> <i class="arg">pkgname</i> <i class="arg">path</i></a></li>
<li><a href="#178">method <b class="cmd">implement</b> <i class="arg">path</i></a></li>
<li><a href="#179">method <b class="cmd">generate-make</b> <i class="arg">path</i></a></li>
<li><a href="#180">method <b class="cmd">linktype</b></a></li>
<li><a href="#181">method <b class="cmd">package-ifneeded</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#182">method <b class="cmd">shared_library</b> <span class="opt">?<i class="arg">filename</i> <b class="const"></b>?</span></a></li>
<li><a href="#183">method <b class="cmd">static_library</b> <span class="opt">?<i class="arg">filename</i> <b class="const"></b>?</span></a></li>
<li><a href="#184">method <b class="cmd">build-tclkit_main</b> <i class="arg">PROJECT</i> <i class="arg">PKG_OBJS</i></a></li>
<li><a href="#185">method <b class="cmd">Collate_Source</b> <i class="arg">CWD</i></a></li>
<li><a href="#186">method <b class="cmd">wrap</b> <i class="arg">PWD</i> <i class="arg">exename</i> <i class="arg">vfspath</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#187">method <b class="cmd">Sandbox</b> <i class="arg">object</i></a></li>
<li><a href="#188">method <b class="cmd">select</b> <i class="arg">object</i></a></li>
<li><a href="#189">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></li>
<li><a href="#190">method <b class="cmd">claim_object</b> <i class="arg">object</i></a></li>
<li><a href="#191">method <b class="cmd">scm_info</b></a></li>
<li><a href="#192">method <b class="cmd">DistroMixIn</b></a></li>
<li><a href="#193">method <b class="cmd">Sandbox</b></a></li>
<li><a href="#194">method <b class="cmd">SrcDir</b></a></li>
<li><a href="#195">method <b class="cmd">ScmTag</b></a></li>
<li><a href="#196">method <b class="cmd">ScmClone</b></a></li>
<li><a href="#197">method <b class="cmd">ScmUnpack</b></a></li>
<li><a href="#198">method <b class="cmd">ScmUpdate</b></a></li>
<li><a href="#199">method <b class="cmd">Unpack</b></a></li>
<li><a href="#200">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></li>
<li><a href="#201">method <b class="cmd">claim_object</b> <i class="arg">object</i></a></li>
<li><a href="#202">method <b class="cmd">ScmUnpack</b></a></li>
<li><a href="#203">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></li>
<li><a href="#204">method <b class="cmd">claim_object</b> <i class="arg">obj</i></a></li>
<li><a href="#205">method <b class="cmd">scm_info</b></a></li>
<li><a href="#206">method <b class="cmd">ScmClone</b></a></li>
<li><a href="#207">method <b class="cmd">ScmTag</b></a></li>
<li><a href="#208">method <b class="cmd">ScmUnpack</b></a></li>
<li><a href="#209">method <b class="cmd">ScmUpdate</b></a></li>
<li><a href="#210">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></li>
<li><a href="#211">method <b class="cmd">claim_object</b> <i class="arg">obj</i></a></li>
<li><a href="#212">method <b class="cmd">ScmTag</b></a></li>
<li><a href="#213">method <b class="cmd">ScmUnpack</b></a></li>
<li><a href="#214">method <b class="cmd">ScmUpdate</b></a></li>
<li><a href="#215">method <b class="cmd">_MorphPatterns</b></a></li>
<li><a href="#216">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></li>
<li><a href="#217">method <b class="cmd">child</b> <i class="arg">which</i></a></li>
<li><a href="#218">method <b class="cmd">compile</b></a></li>
<li><a href="#219">method <b class="cmd">go</b></a></li>
<li><a href="#220">method <b class="cmd">install</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#221">method <b class="cmd">linktype</b></a></li>
<li><a href="#222">method <b class="cmd">linker-products</b> <i class="arg">configdict</i></a></li>
<li><a href="#223">method <b class="cmd">linker-external</b> <i class="arg">configdict</i></a></li>
<li><a href="#224">method <b class="cmd">linker-extra</b> <i class="arg">configdict</i></a></li>
<li><a href="#225">method <b class="cmd">env-bootstrap</b></a></li>
<li><a href="#226">method <b class="cmd">env-exec</b></a></li>
<li><a href="#227">method <b class="cmd">env-install</b></a></li>
<li><a href="#228">method <b class="cmd">env-load</b></a></li>
<li><a href="#229">method <b class="cmd">env-present</b></a></li>
<li><a href="#230">method <b class="cmd">sources</b></a></li>
<li><a href="#231">method <b class="cmd">update</b></a></li>
<li><a href="#232">method <b class="cmd">unpack</b></a></li>
<li><a href="#233">method <b class="cmd">env-bootstrap</b></a></li>
<li><a href="#234">method <b class="cmd">env-present</b></a></li>
<li><a href="#235">method <b class="cmd">linktype</b></a></li>
<li><a href="#236">method <b class="cmd">env-bootstrap</b></a></li>
<li><a href="#237">method <b class="cmd">env-install</b></a></li>
<li><a href="#238">method <b class="cmd">env-present</b></a></li>
<li><a href="#239">method <b class="cmd">install</b> <i class="arg">DEST</i></a></li>
<li><a href="#240">method <b class="cmd">kettle</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#241">method <b class="cmd">install</b> <i class="arg">DEST</i></a></li>
<li><a href="#242">method <b class="cmd">install</b> <i class="arg">DEST</i></a></li>
<li><a href="#243">method <b class="cmd">env-bootstrap</b></a></li>
<li><a href="#244">method <b class="cmd">env-install</b></a></li>
<li><a href="#245">method <b class="cmd">env-present</b></a></li>
<li><a href="#246">method <b class="cmd">install</b> <i class="arg">DEST</i></a></li>
<li><a href="#247">method <b class="cmd">install-module</b> <i class="arg">DEST</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#248">method <b class="cmd">clean</b></a></li>
<li><a href="#249">method <b class="cmd">env-install</b></a></li>
<li><a href="#250">method <b class="cmd">project-compile-products</b></a></li>
<li><a href="#251">method <b class="cmd">ComputeInstall</b></a></li>
<li><a href="#252">method <b class="cmd">go</b></a></li>
<li><a href="#253">method <b class="cmd">linker-products</b> <i class="arg">configdict</i></a></li>
<li><a href="#254">method <b class="cmd">project-static-packages</b></a></li>
<li><a href="#255">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></li>
<li><a href="#256">method <b class="cmd">compile</b></a></li>
<li><a href="#257">method <b class="cmd">Configure</b></a></li>
<li><a href="#258">method <b class="cmd">install</b> <i class="arg">DEST</i></a></li>
<li><a href="#259">method <b class="cmd">install</b> <i class="arg">DEST</i></a></li>
<li><a href="#260">method <b class="cmd">install</b> <i class="arg">DEST</i></a></li>
<li><a href="#261">method <b class="cmd">env-bootstrap</b></a></li>
<li><a href="#262">method <b class="cmd">env-present</b></a></li>
<li><a href="#263">method <b class="cmd">env-install</b></a></li>
<li><a href="#264">method <b class="cmd">go</b></a></li>
<li><a href="#265">method <b class="cmd">linktype</b></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>The Practcl module is a tool for integrating large modules for C API
Tcl code that requires custom Tcl types and TclOO objects.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">COMMANDS</a></h2>
<div id="section2" class="doctools_section"><h2><a name="section2">Commands</a></h2>
<dl class="doctools_definitions">
<dt><a name="1">proc <b class="cmd">Proc</b> <i class="arg">name</i> <i class="arg">arglist</i> <i class="arg">body</i></a></dt>
<dd><p>Generate a proc if no command already exists by that name</p></dd>
<dt><a name="2">proc <b class="cmd">noop</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>A command to do nothing. A handy way of
negating an instruction without
having to comment it completely out.
It's also a handy attachment point for
an object to be named later</p></dd>
<dt><a name="3">proc <b class="cmd">practcl::debug</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="4">proc <b class="cmd">practcl::doexec</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Drop in a static copy of Tcl</p></dd>
<dt><a name="5">proc <b class="cmd">practcl::doexec_in</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="6">proc <b class="cmd">practcl::dotclexec</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="7">proc <b class="cmd">practcl::domake</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="8">proc <b class="cmd">practcl::domake.tcl</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="9">proc <b class="cmd">practcl::fossil</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="10">proc <b class="cmd">practcl::fossil_status</b> <i class="arg">dir</i></a></dt>
<dd></dd>
<dt><a name="11">proc <b class="cmd">practcl::os</b></a></dt>
<dd></dd>
<dt><a name="12">proc <b class="cmd">practcl::mkzip</b> <i class="arg">exename</i> <i class="arg">barekit</i> <i class="arg">vfspath</i></a></dt>
<dd><p>Build a zipfile. On tcl8.6 this invokes the native Zip implementation
on older interpreters this invokes zip via exec</p></dd>
<dt><a name="13">proc <b class="cmd">practcl::sort_dict</b> <i class="arg">list</i></a></dt>
<dd><p>Dictionary sort a key/value list. Needed because pre tcl8.6
does not have <em>lsort -stride 2</em></p></dd>
<dt><a name="14">proc <b class="cmd">practcl::local_os</b></a></dt>
<dd></dd>
<dt><a name="15">proc <b class="cmd">practcl::config.tcl</b> <i class="arg">path</i></a></dt>
<dd><p>Detect local platform</p></dd>
<dt><a name="16">proc <b class="cmd">practcl::read_configuration</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="17">proc <b class="cmd">practcl::tcllib_require</b> <i class="arg">pkg</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Try to load  a package, and failing that
retrieve tcllib</p></dd>
<dt><a name="18">proc <b class="cmd">practcl::platform::tcl_core_options</b> <i class="arg">os</i></a></dt>
<dd></dd>
<dt><a name="19">proc <b class="cmd">practcl::platform::tk_core_options</b> <i class="arg">os</i></a></dt>
<dd></dd>
<dt><a name="20">proc <b class="cmd">practcl::read_rc_file</b> <i class="arg">filename</i> <span class="opt">?<i class="arg">localdat</i> <b class="const"></b>?</span></a></dt>
<dd><p>Read a stylized key/value list stored in a file</p></dd>
<dt><a name="21">proc <b class="cmd">practcl::read_sh_subst</b> <i class="arg">line</i> <i class="arg">info</i></a></dt>
<dd></dd>
<dt><a name="22">proc <b class="cmd">practcl::read_sh_file</b> <i class="arg">filename</i> <span class="opt">?<i class="arg">localdat</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="23">proc <b class="cmd">practcl::read_Config.sh</b> <i class="arg">filename</i></a></dt>
<dd><p>A simpler form of read_sh_file tailored
to pulling data from (tcl|tk)Config.sh</p></dd>
<dt><a name="24">proc <b class="cmd">practcl::read_Makefile</b> <i class="arg">filename</i></a></dt>
<dd><p>A simpler form of read_sh_file tailored
to pulling data from a Makefile</p></dd>
<dt><a name="25">proc <b class="cmd">practcl::cputs</b> <i class="arg">varname</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Append arguments to a buffer
The command works like puts in that each call will also insert
a line feed. Unlike puts, blank links in the interstitial are
suppressed</p></dd>
<dt><a name="26">proc <b class="cmd">practcl::tcl_to_c</b> <i class="arg">body</i></a></dt>
<dd></dd>
<dt><a name="27">proc <b class="cmd">practcl::_tagblock</b> <i class="arg">text</i> <span class="opt">?<i class="arg">style</i> <b class="const">tcl</b>?</span> <span class="opt">?<i class="arg">note</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="28">proc <b class="cmd">practcl::de_shell</b> <i class="arg">data</i></a></dt>
<dd></dd>
<dt><a name="29">proc <b class="cmd">practcl::cat</b> <i class="arg">fname</i></a></dt>
<dd><p>Bits stolen from fileutil</p></dd>
<dt><a name="30">proc <b class="cmd">practcl::grep</b> <i class="arg">pattern</i> <span class="opt">?<i class="arg">files</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="31">proc <b class="cmd">practcl::file_lexnormalize</b> <i class="arg">sp</i></a></dt>
<dd></dd>
<dt><a name="32">proc <b class="cmd">practcl::file_relative</b> <i class="arg">base</i> <i class="arg">dst</i></a></dt>
<dd></dd>
<dt><a name="33">proc <b class="cmd">practcl::log</b> <i class="arg">fname</i> <i class="arg">comment</i></a></dt>
<dd></dd>
<dt><a name="34">proc <b class="cmd">practcl::_isdirectory</b> <i class="arg">name</i></a></dt>
<dd><p>Installer tools</p></dd>
<dt><a name="35">proc <b class="cmd">practcl::_pkgindex_directory</b> <i class="arg">path</i></a></dt>
<dd><p>Return true if the pkgindex file contains
any statement other than &quot;package ifneeded&quot;
and/or if any package ifneeded loads a DLL</p></dd>
<dt><a name="36">proc <b class="cmd">practcl::_pkgindex_path_subdir</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="37">proc <b class="cmd">practcl::pkgindex_path</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Index all paths given as though they will end up in the same
virtual file system</p></dd>
<dt><a name="38">proc <b class="cmd">practcl::installDir</b> <i class="arg">d1</i> <i class="arg">d2</i></a></dt>
<dd></dd>
<dt><a name="39">proc <b class="cmd">practcl::copyDir</b> <i class="arg">d1</i> <i class="arg">d2</i> <span class="opt">?<i class="arg">toplevel</i> <b class="const">1</b>?</span></a></dt>
<dd></dd>
<dt><a name="40">proc <b class="cmd">practcl::trigger</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="41">proc <b class="cmd">practcl::depends</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="42">proc <b class="cmd">practcl::target</b> <i class="arg">name</i> <i class="arg">info</i> <span class="opt">?<i class="arg">action</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">Classes</a></h2>
<div id="subsection1" class="doctools_subsection"><h3><a name="subsection1">Class  practcl::metaclass</a></h3>
<p><em>ancestors</em>: <b class="class">oo::object</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="43">method <b class="cmd">_MorphPatterns</b></a></dt>
<dd></dd>
<dt><a name="44">method <b class="cmd">define</b> <i class="arg">submethod</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="45">method <b class="cmd">graft</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="46">method <b class="cmd">initialize</b></a></dt>
<dd></dd>
<dt><a name="47">method <b class="cmd">link</b> <i class="arg">command</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="48">method <b class="cmd">morph</b> <i class="arg">classname</i></a></dt>
<dd></dd>
<dt><a name="49">method <b class="cmd">mixin</b> <i class="arg">slot</i> <i class="arg">classname</i></a></dt>
<dd></dd>
<dt><a name="50">method <b class="cmd">organ</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="51">method <b class="cmd">script</b> <i class="arg">script</i></a></dt>
<dd></dd>
<dt><a name="52">method <b class="cmd">select</b></a></dt>
<dd></dd>
<dt><a name="53">method <b class="cmd">source</b> <i class="arg">filename</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection2" class="doctools_subsection"><h3><a name="subsection2">Class  practcl::toolset</a></h3>
<p>Ancestor-less class intended to be a mixin
which defines a family of build related behaviors
that are modified when targetting either gcc or msvc</p>
<p><b class="class">Class Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="54">method <b class="cmd">select</b> <i class="arg">object</i></a></dt>
<dd></dd>
</dl>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="55">method <b class="cmd">config.sh</b></a></dt>
<dd><p>find or fake a key/value list describing this project</p></dd>
<dt><a name="56">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></dt>
<dd></dd>
<dt><a name="57">method <b class="cmd">MakeDir</b> <i class="arg">srcdir</i></a></dt>
<dd></dd>
<dt><a name="58">method <b class="cmd">read_configuration</b></a></dt>
<dd></dd>
<dt><a name="59">method <b class="cmd">build-cflags</b> <i class="arg">PROJECT</i> <i class="arg">DEFS</i> <i class="arg">namevar</i> <i class="arg">versionvar</i> <i class="arg">defsvar</i></a></dt>
<dd><p>method DEFS
This method populates 4 variables:
name - The name of the package
version - The version of the package
defs - C flags passed to the compiler
includedir - A list of paths to feed to the compiler for finding headers</p></dd>
<dt><a name="60">method <b class="cmd">critcl</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="61">method <b class="cmd">make-autodetect</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection3" class="doctools_subsection"><h3><a name="subsection3">Class  practcl::toolset.gcc</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::toolset</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="62">method <b class="cmd">Autoconf</b></a></dt>
<dd></dd>
<dt><a name="63">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></dt>
<dd></dd>
<dt><a name="64">method <b class="cmd">ConfigureOpts</b></a></dt>
<dd></dd>
<dt><a name="65">method <b class="cmd">MakeDir</b> <i class="arg">srcdir</i></a></dt>
<dd><p>Detect what directory contains the Makefile template</p></dd>
<dt><a name="66">method <b class="cmd">make-autodetect</b></a></dt>
<dd></dd>
<dt><a name="67">method <b class="cmd">make-clean</b></a></dt>
<dd></dd>
<dt><a name="68">method <b class="cmd">make-compile</b></a></dt>
<dd></dd>
<dt><a name="69">method <b class="cmd">make-install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
<dt><a name="70">method <b class="cmd">build-compile-sources</b> <i class="arg">PROJECT</i> <i class="arg">COMPILE</i> <i class="arg">CPPCOMPILE</i> <i class="arg">INCLUDES</i></a></dt>
<dd></dd>
<dt><a name="71">method <b class="cmd">build-Makefile</b> <i class="arg">path</i> <i class="arg">PROJECT</i></a></dt>
<dd></dd>
<dt><a name="72">method <b class="cmd">build-library</b> <i class="arg">outfile</i> <i class="arg">PROJECT</i></a></dt>
<dd><p>Produce a static or dynamic library</p></dd>
<dt><a name="73">method <b class="cmd">build-tclsh</b> <i class="arg">outfile</i> <i class="arg">PROJECT</i></a></dt>
<dd><p>Produce a static executable</p></dd>
</dl>
</div>
<div id="subsection4" class="doctools_subsection"><h3><a name="subsection4">Class  practcl::toolset.msvc</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::toolset</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="74">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></dt>
<dd><p>MSVC always builds in the source directory</p></dd>
<dt><a name="75">method <b class="cmd">make-autodetect</b></a></dt>
<dd><p>Do nothing</p></dd>
<dt><a name="76">method <b class="cmd">make-clean</b></a></dt>
<dd></dd>
<dt><a name="77">method <b class="cmd">make-compile</b></a></dt>
<dd></dd>
<dt><a name="78">method <b class="cmd">make-install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
<dt><a name="79">method <b class="cmd">MakeDir</b> <i class="arg">srcdir</i></a></dt>
<dd><p>Detect what directory contains the Makefile template</p></dd>
<dt><a name="80">method <b class="cmd">NmakeOpts</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection5" class="doctools_subsection"><h3><a name="subsection5">Class  practcl::make_obj</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::metaclass</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="81">method <b class="cmd">constructor</b> <i class="arg">module_object</i> <i class="arg">name</i> <i class="arg">info</i> <span class="opt">?<i class="arg">action_body</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="82">method <b class="cmd">do</b></a></dt>
<dd></dd>
<dt><a name="83">method <b class="cmd">check</b></a></dt>
<dd></dd>
<dt><a name="84">method <b class="cmd">output</b></a></dt>
<dd></dd>
<dt><a name="85">method <b class="cmd">reset</b></a></dt>
<dd></dd>
<dt><a name="86">method <b class="cmd">triggers</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection6" class="doctools_subsection"><h3><a name="subsection6">Class  practcl::object</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::metaclass</b></p>
<p>A generic Practcl object</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="87">method <b class="cmd">constructor</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="88">method <b class="cmd">child</b> <i class="arg">method</i></a></dt>
<dd></dd>
<dt><a name="89">method <b class="cmd">go</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection7" class="doctools_subsection"><h3><a name="subsection7">Class  practcl::dynamic</a></h3>
<p>Dynamic blocks do not generate their own .c files,
instead the contribute to the amalgamation
of the main library file</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="90">method <b class="cmd">cstructure</b> <i class="arg">name</i> <i class="arg">definition</i> <span class="opt">?<i class="arg">argdat</i> <b class="const"></b>?</span></a></dt>
<dd><p>Parser functions</p></dd>
<dt><a name="91">method <b class="cmd">include</b> <i class="arg">header</i></a></dt>
<dd></dd>
<dt><a name="92">method <b class="cmd">include_dir</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="93">method <b class="cmd">include_directory</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="94">method <b class="cmd">c_header</b> <i class="arg">body</i></a></dt>
<dd></dd>
<dt><a name="95">method <b class="cmd">c_code</b> <i class="arg">body</i></a></dt>
<dd></dd>
<dt><a name="96">method <b class="cmd">c_function</b> <i class="arg">header</i> <i class="arg">body</i> <span class="opt">?<i class="arg">info</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="97">method <b class="cmd">c_tcloomethod</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="98">method <b class="cmd">cmethod</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></dt>
<dd><p>Alias to classic name</p></dd>
<dt><a name="99">method <b class="cmd">c_tclproc_nspace</b> <i class="arg">nspace</i></a></dt>
<dd></dd>
<dt><a name="100">method <b class="cmd">c_tclcmd</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="101">method <b class="cmd">c_tclproc_raw</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></dt>
<dd><p>Alias to classic name</p></dd>
<dt><a name="102">method <b class="cmd">tcltype</b> <i class="arg">name</i> <i class="arg">argdat</i></a></dt>
<dd></dd>
<dt><a name="103">method <b class="cmd">project-compile-products</b></a></dt>
<dd><p>Module interactions</p></dd>
<dt><a name="104">method <b class="cmd">implement</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="105">method <b class="cmd">initialize</b></a></dt>
<dd><p>Practcl internals</p></dd>
<dt><a name="106">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
<dt><a name="107">method <b class="cmd">generate-cfile-constant</b></a></dt>
<dd></dd>
<dt><a name="108">method <b class="cmd">generate-cfile-header</b></a></dt>
<dd></dd>
<dt><a name="109">method <b class="cmd">generate-cfile-tclapi</b></a></dt>
<dd><p>Generate code that provides implements Tcl API
calls</p></dd>
<dt><a name="110">method <b class="cmd">generate-loader-module</b></a></dt>
<dd><p>Generate code that runs when the package/module is
initialized into the interpreter</p></dd>
<dt><a name="111">method <b class="cmd">Collate_Source</b> <i class="arg">CWD</i></a></dt>
<dd></dd>
<dt><a name="112">method <b class="cmd">select</b></a></dt>
<dd><p>Once an object marks itself as some
flavor of dynamic, stop trying to morph
it into something else</p></dd>
</dl>
</div>
<div id="subsection8" class="doctools_subsection"><h3><a name="subsection8">Class  practcl::product</a></h3>
<p>A deliverable for the build system</p>
<p><b class="class">Class Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="113">method <b class="cmd">select</b> <i class="arg">object</i></a></dt>
<dd></dd>
</dl>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="114">method <b class="cmd">code</b> <i class="arg">section</i> <i class="arg">body</i></a></dt>
<dd></dd>
<dt><a name="115">method <b class="cmd">Collate_Source</b> <i class="arg">CWD</i></a></dt>
<dd></dd>
<dt><a name="116">method <b class="cmd">project-compile-products</b></a></dt>
<dd></dd>
<dt><a name="117">method <b class="cmd">generate-debug</b> <span class="opt">?<i class="arg">spaces</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="118">method <b class="cmd">generate-cfile-constant</b></a></dt>
<dd></dd>
<dt><a name="119">method <b class="cmd">generate-cfile-public-structure</b></a></dt>
<dd><p>Populate const static data structures</p></dd>
<dt><a name="120">method <b class="cmd">generate-cfile-header</b></a></dt>
<dd></dd>
<dt><a name="121">method <b class="cmd">generate-cfile-global</b></a></dt>
<dd></dd>
<dt><a name="122">method <b class="cmd">generate-cfile-private-typedef</b></a></dt>
<dd></dd>
<dt><a name="123">method <b class="cmd">generate-cfile-private-structure</b></a></dt>
<dd></dd>
<dt><a name="124">method <b class="cmd">generate-cfile-functions</b></a></dt>
<dd><p>Generate code that provides subroutines called by
Tcl API methods</p></dd>
<dt><a name="125">method <b class="cmd">generate-cfile-tclapi</b></a></dt>
<dd><p>Generate code that provides implements Tcl API
calls</p></dd>
<dt><a name="126">method <b class="cmd">generate-hfile-public-define</b></a></dt>
<dd></dd>
<dt><a name="127">method <b class="cmd">generate-hfile-public-macro</b></a></dt>
<dd></dd>
<dt><a name="128">method <b class="cmd">generate-hfile-public-typedef</b></a></dt>
<dd></dd>
<dt><a name="129">method <b class="cmd">generate-hfile-public-structure</b></a></dt>
<dd></dd>
<dt><a name="130">method <b class="cmd">generate-hfile-public-headers</b></a></dt>
<dd></dd>
<dt><a name="131">method <b class="cmd">generate-hfile-public-function</b></a></dt>
<dd></dd>
<dt><a name="132">method <b class="cmd">generate-hfile-public-includes</b></a></dt>
<dd></dd>
<dt><a name="133">method <b class="cmd">generate-hfile-public-verbatim</b></a></dt>
<dd></dd>
<dt><a name="134">method <b class="cmd">generate-loader-external</b></a></dt>
<dd></dd>
<dt><a name="135">method <b class="cmd">generate-loader-module</b></a></dt>
<dd></dd>
<dt><a name="136">method <b class="cmd">generate-stub-function</b></a></dt>
<dd></dd>
<dt><a name="137">method <b class="cmd">IncludeAdd</b> <i class="arg">headervar</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="138">method <b class="cmd">generate-tcl-loader</b></a></dt>
<dd></dd>
<dt><a name="139">method <b class="cmd">generate-tcl-pre</b></a></dt>
<dd><p>This methods generates any Tcl script file
which is required to pre-initialize the C library</p></dd>
<dt><a name="140">method <b class="cmd">generate-tcl-post</b></a></dt>
<dd></dd>
<dt><a name="141">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
<dt><a name="142">method <b class="cmd">Ofile</b> <i class="arg">filename</i></a></dt>
<dd></dd>
<dt><a name="143">method <b class="cmd">project-static-packages</b></a></dt>
<dd><p>Methods called by the master project</p></dd>
<dt><a name="144">method <b class="cmd">toolset-include-directory</b></a></dt>
<dd><p>Methods called by the toolset</p></dd>
<dt><a name="145">method <b class="cmd">target</b> <i class="arg">method</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection9" class="doctools_subsection"><h3><a name="subsection9">Class  practcl::product.cheader</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::product</b></p>
<p>Flesh out several trivial varieties of product</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="146">method <b class="cmd">project-compile-products</b></a></dt>
<dd></dd>
<dt><a name="147">method <b class="cmd">generate-loader-module</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection10" class="doctools_subsection"><h3><a name="subsection10">Class  practcl::product.csource</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::product</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="148">method <b class="cmd">project-compile-products</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection11" class="doctools_subsection"><h3><a name="subsection11">Class  practcl::product.clibrary</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::product</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="149">method <b class="cmd">linker-products</b> <i class="arg">configdict</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection12" class="doctools_subsection"><h3><a name="subsection12">Class  practcl::product.dynamic</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::dynamic</b> <b class="class">practcl::product</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="150">method <b class="cmd">initialize</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection13" class="doctools_subsection"><h3><a name="subsection13">Class  practcl::product.critcl</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::dynamic</b> <b class="class">practcl::product</b></p>
</div>
<div id="subsection14" class="doctools_subsection"><h3><a name="subsection14">Class  practcl::module</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::object</b> <b class="class">practcl::product.dynamic</b></p>
<p>In the end, all C code must be loaded into a module
This will either be a dynamically loaded library implementing
a tcl extension, or a compiled in segment of a custom shell/app</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="151">method <b class="cmd">_MorphPatterns</b></a></dt>
<dd></dd>
<dt><a name="152">method <b class="cmd">add</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="153">method <b class="cmd">install-headers</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="154">method <b class="cmd">make</b> <i class="arg">command</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Target handling</p></dd>
<dt><a name="155">method <b class="cmd">child</b> <i class="arg">which</i></a></dt>
<dd></dd>
<dt><a name="156">method <b class="cmd">generate-c</b></a></dt>
<dd><p>This methods generates the contents of an amalgamated .c file
which implements the loader for a batch of tools</p></dd>
<dt><a name="157">method <b class="cmd">generate-h</b></a></dt>
<dd><p>This methods generates the contents of an amalgamated .h file
which describes the public API of this module</p></dd>
<dt><a name="158">method <b class="cmd">generate-loader</b></a></dt>
<dd></dd>
<dt><a name="159">method <b class="cmd">initialize</b></a></dt>
<dd></dd>
<dt><a name="160">method <b class="cmd">implement</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="161">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection15" class="doctools_subsection"><h3><a name="subsection15">Class  practcl::project</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::module</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="162">method <b class="cmd">_MorphPatterns</b></a></dt>
<dd></dd>
<dt><a name="163">method <b class="cmd">constructor</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="164">method <b class="cmd">add_object</b> <i class="arg">object</i></a></dt>
<dd></dd>
<dt><a name="165">method <b class="cmd">add_project</b> <i class="arg">pkg</i> <i class="arg">info</i> <span class="opt">?<i class="arg">oodefine</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="166">method <b class="cmd">add_tool</b> <i class="arg">pkg</i> <i class="arg">info</i> <span class="opt">?<i class="arg">oodefine</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="167">method <b class="cmd">build-tclcore</b></a></dt>
<dd></dd>
<dt><a name="168">method <b class="cmd">child</b> <i class="arg">which</i></a></dt>
<dd></dd>
<dt><a name="169">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
<dt><a name="170">method <b class="cmd">project</b> <i class="arg">pkg</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Exercise the methods of a sub-object</p></dd>
<dt><a name="171">method <b class="cmd">tclcore</b></a></dt>
<dd></dd>
<dt><a name="172">method <b class="cmd">tkcore</b></a></dt>
<dd></dd>
<dt><a name="173">method <b class="cmd">tool</b> <i class="arg">pkg</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection16" class="doctools_subsection"><h3><a name="subsection16">Class  practcl::library</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::project</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="174">method <b class="cmd">clean</b> <i class="arg">PATH</i></a></dt>
<dd></dd>
<dt><a name="175">method <b class="cmd">project-compile-products</b></a></dt>
<dd></dd>
<dt><a name="176">method <b class="cmd">go</b></a></dt>
<dd></dd>
<dt><a name="177">method <b class="cmd">generate-decls</b> <i class="arg">pkgname</i> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="178">method <b class="cmd">implement</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="179">method <b class="cmd">generate-make</b> <i class="arg">path</i></a></dt>
<dd><p>Backward compadible call</p></dd>
<dt><a name="180">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
<dt><a name="181">method <b class="cmd">package-ifneeded</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Create a &quot;package ifneeded&quot;
Args are a list of aliases for which this package will answer to</p></dd>
<dt><a name="182">method <b class="cmd">shared_library</b> <span class="opt">?<i class="arg">filename</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="183">method <b class="cmd">static_library</b> <span class="opt">?<i class="arg">filename</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection17" class="doctools_subsection"><h3><a name="subsection17">Class  practcl::tclkit</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::library</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="184">method <b class="cmd">build-tclkit_main</b> <i class="arg">PROJECT</i> <i class="arg">PKG_OBJS</i></a></dt>
<dd></dd>
<dt><a name="185">method <b class="cmd">Collate_Source</b> <i class="arg">CWD</i></a></dt>
<dd></dd>
<dt><a name="186">method <b class="cmd">wrap</b> <i class="arg">PWD</i> <i class="arg">exename</i> <i class="arg">vfspath</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Wrap an executable</p></dd>
</dl>
</div>
<div id="subsection18" class="doctools_subsection"><h3><a name="subsection18">Class  practcl::distribution</a></h3>
<p>Standalone class to manage code distribution
This class is intended to be mixed into another class
(Thus the lack of ancestors)</p>
<p><b class="class">Class Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="187">method <b class="cmd">Sandbox</b> <i class="arg">object</i></a></dt>
<dd></dd>
<dt><a name="188">method <b class="cmd">select</b> <i class="arg">object</i></a></dt>
<dd></dd>
<dt><a name="189">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="190">method <b class="cmd">claim_object</b> <i class="arg">object</i></a></dt>
<dd></dd>
</dl>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="191">method <b class="cmd">scm_info</b></a></dt>
<dd></dd>
<dt><a name="192">method <b class="cmd">DistroMixIn</b></a></dt>
<dd></dd>
<dt><a name="193">method <b class="cmd">Sandbox</b></a></dt>
<dd></dd>
<dt><a name="194">method <b class="cmd">SrcDir</b></a></dt>
<dd></dd>
<dt><a name="195">method <b class="cmd">ScmTag</b></a></dt>
<dd></dd>
<dt><a name="196">method <b class="cmd">ScmClone</b></a></dt>
<dd></dd>
<dt><a name="197">method <b class="cmd">ScmUnpack</b></a></dt>
<dd></dd>
<dt><a name="198">method <b class="cmd">ScmUpdate</b></a></dt>
<dd></dd>
<dt><a name="199">method <b class="cmd">Unpack</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection19" class="doctools_subsection"><h3><a name="subsection19">Class  practcl::distribution.snapshot</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::distribution</b></p>
<p><b class="class">Class Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="200">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="201">method <b class="cmd">claim_object</b> <i class="arg">object</i></a></dt>
<dd></dd>
</dl>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="202">method <b class="cmd">ScmUnpack</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection20" class="doctools_subsection"><h3><a name="subsection20">Class  practcl::distribution.fossil</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::distribution</b></p>
<p><b class="class">Class Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="203">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></dt>
<dd><p>Check for markers in the source root</p></dd>
<dt><a name="204">method <b class="cmd">claim_object</b> <i class="arg">obj</i></a></dt>
<dd><p>Check for markers in the metadata</p></dd>
</dl>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="205">method <b class="cmd">scm_info</b></a></dt>
<dd></dd>
<dt><a name="206">method <b class="cmd">ScmClone</b></a></dt>
<dd><p>Clone the source</p></dd>
<dt><a name="207">method <b class="cmd">ScmTag</b></a></dt>
<dd></dd>
<dt><a name="208">method <b class="cmd">ScmUnpack</b></a></dt>
<dd></dd>
<dt><a name="209">method <b class="cmd">ScmUpdate</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection21" class="doctools_subsection"><h3><a name="subsection21">Class  practcl::distribution.git</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::distribution</b></p>
<p><b class="class">Class Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="210">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="211">method <b class="cmd">claim_object</b> <i class="arg">obj</i></a></dt>
<dd></dd>
</dl>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="212">method <b class="cmd">ScmTag</b></a></dt>
<dd></dd>
<dt><a name="213">method <b class="cmd">ScmUnpack</b></a></dt>
<dd></dd>
<dt><a name="214">method <b class="cmd">ScmUpdate</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection22" class="doctools_subsection"><h3><a name="subsection22">Class  practcl::subproject</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::module</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="215">method <b class="cmd">_MorphPatterns</b></a></dt>
<dd></dd>
<dt><a name="216">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></dt>
<dd></dd>
<dt><a name="217">method <b class="cmd">child</b> <i class="arg">which</i></a></dt>
<dd></dd>
<dt><a name="218">method <b class="cmd">compile</b></a></dt>
<dd></dd>
<dt><a name="219">method <b class="cmd">go</b></a></dt>
<dd></dd>
<dt><a name="220">method <b class="cmd">install</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Install project into the local build system</p></dd>
<dt><a name="221">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
<dt><a name="222">method <b class="cmd">linker-products</b> <i class="arg">configdict</i></a></dt>
<dd></dd>
<dt><a name="223">method <b class="cmd">linker-external</b> <i class="arg">configdict</i></a></dt>
<dd></dd>
<dt><a name="224">method <b class="cmd">linker-extra</b> <i class="arg">configdict</i></a></dt>
<dd></dd>
<dt><a name="225">method <b class="cmd">env-bootstrap</b></a></dt>
<dd><p>Methods for packages/tools that can be downloaded
possibly built and used internally by this Practcl
process
Load the facility into the interpreter</p></dd>
<dt><a name="226">method <b class="cmd">env-exec</b></a></dt>
<dd><p>Return a file path that exec can call</p></dd>
<dt><a name="227">method <b class="cmd">env-install</b></a></dt>
<dd><p>Install the tool into the local environment</p></dd>
<dt><a name="228">method <b class="cmd">env-load</b></a></dt>
<dd><p>Do whatever is necessary to get the tool
into the local environment</p></dd>
<dt><a name="229">method <b class="cmd">env-present</b></a></dt>
<dd><p>Check if tool is available for load/already loaded</p></dd>
<dt><a name="230">method <b class="cmd">sources</b></a></dt>
<dd></dd>
<dt><a name="231">method <b class="cmd">update</b></a></dt>
<dd></dd>
<dt><a name="232">method <b class="cmd">unpack</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection23" class="doctools_subsection"><h3><a name="subsection23">Class  practcl::subproject.source</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject</b> <b class="class">practcl::library</b></p>
<p>A project which the kit compiles and integrates
the source for itself</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="233">method <b class="cmd">env-bootstrap</b></a></dt>
<dd></dd>
<dt><a name="234">method <b class="cmd">env-present</b></a></dt>
<dd></dd>
<dt><a name="235">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection24" class="doctools_subsection"><h3><a name="subsection24">Class  practcl::subproject.teapot</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject</b></p>
<p>a copy from the teapot</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="236">method <b class="cmd">env-bootstrap</b></a></dt>
<dd></dd>
<dt><a name="237">method <b class="cmd">env-install</b></a></dt>
<dd></dd>
<dt><a name="238">method <b class="cmd">env-present</b></a></dt>
<dd></dd>
<dt><a name="239">method <b class="cmd">install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection25" class="doctools_subsection"><h3><a name="subsection25">Class  practcl::subproject.kettle</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="240">method <b class="cmd">kettle</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="241">method <b class="cmd">install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection26" class="doctools_subsection"><h3><a name="subsection26">Class  practcl::subproject.critcl</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="242">method <b class="cmd">install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection27" class="doctools_subsection"><h3><a name="subsection27">Class  practcl::subproject.sak</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="243">method <b class="cmd">env-bootstrap</b></a></dt>
<dd></dd>
<dt><a name="244">method <b class="cmd">env-install</b></a></dt>
<dd></dd>
<dt><a name="245">method <b class="cmd">env-present</b></a></dt>
<dd></dd>
<dt><a name="246">method <b class="cmd">install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
<dt><a name="247">method <b class="cmd">install-module</b> <i class="arg">DEST</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection28" class="doctools_subsection"><h3><a name="subsection28">Class  practcl::subproject.binary</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject</b></p>
<p>A binary package</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="248">method <b class="cmd">clean</b></a></dt>
<dd></dd>
<dt><a name="249">method <b class="cmd">env-install</b></a></dt>
<dd></dd>
<dt><a name="250">method <b class="cmd">project-compile-products</b></a></dt>
<dd></dd>
<dt><a name="251">method <b class="cmd">ComputeInstall</b></a></dt>
<dd></dd>
<dt><a name="252">method <b class="cmd">go</b></a></dt>
<dd></dd>
<dt><a name="253">method <b class="cmd">linker-products</b> <i class="arg">configdict</i></a></dt>
<dd></dd>
<dt><a name="254">method <b class="cmd">project-static-packages</b></a></dt>
<dd></dd>
<dt><a name="255">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></dt>
<dd></dd>
<dt><a name="256">method <b class="cmd">compile</b></a></dt>
<dd></dd>
<dt><a name="257">method <b class="cmd">Configure</b></a></dt>
<dd></dd>
<dt><a name="258">method <b class="cmd">install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection29" class="doctools_subsection"><h3><a name="subsection29">Class  practcl::subproject.tea</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject.binary</b></p>
</div>
<div id="subsection30" class="doctools_subsection"><h3><a name="subsection30">Class  practcl::subproject.library</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject.binary</b> <b class="class">practcl::library</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="259">method <b class="cmd">install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection31" class="doctools_subsection"><h3><a name="subsection31">Class  practcl::subproject.external</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject.binary</b></p>
<p>An external library</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">CPUTS</b> <i class="arg">varname</i> <i class="arg">body</i> <span class="opt">?<i class="arg">body</i>...?</span></a></dt>
<dt><a name="260">method <b class="cmd">install</b> <i class="arg">DEST</i></a></dt>
<dd><p>Appends blocks of text to a buffer. This command tries to reduce the number
of line breaks between bodies.</p></dd>
<dd></dd>
<dt><a name="2"><b class="cmd">practcl::_isdirectory</b> <i class="arg">path</i></a></dt>
<dd><p>Returns true if <i class="arg">path</i> is a directory, using the test</p></dd>
</dl>
</div>
<div id="subsection32" class="doctools_subsection"><h3><a name="subsection32">Class  practcl::subproject.core</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject.binary</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="3"><b class="cmd">practcl::object</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A generic Practcl object</p></dd>
<dt><a name="4"><b class="cmd">practcl::library</b> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A Practcl object representing a library container</p></dd>
<dt><a name="5"><b class="cmd">practcl::exe</b> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A Practcl object representing a wrapped executable</p></dd>
<dt><a name="6"><b class="cmd">practcl::product</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A Practcl object representing a compiled product</p></dd>
<dt><a name="7"><b class="cmd">practcl::cheader</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A Practcl object representing an externally generated c header</p></dd>
<dt><a name="261">method <b class="cmd">env-bootstrap</b></a></dt>
<dd></dd>
<dt><a name="262">method <b class="cmd">env-present</b></a></dt>
<dd></dd>
<dt><a name="263">method <b class="cmd">env-install</b></a></dt>
<dd></dd>
<dt><a name="264">method <b class="cmd">go</b></a></dt>
<dd></dd>
<dt><a name="265">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
<dt><a name="8"><b class="cmd">practcl::csource</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A Practcl object representing an externally generated c source file</p></dd>
<dt><a name="9"><b class="cmd">practcl::module</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A Practcl object representing a dynamically generated C/H/Tcl suite</p></dd>
<dt><a name="10"><b class="cmd">practcl::submodule</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A Practcl object representing a dynamically generated C/H/Tcl suite, subordinate to a module</p></dd>
</dl>
</div>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">Bugs, Ideas, Feedback</a></h2>
<div id="section4" class="doctools_section"><h2><a name="section4">Bugs, Ideas, Feedback</a></h2>
<p>This document, and the package it describes, will undoubtedly contain
bugs and other problems.
Please report such in the category <em>practcl</em> of the
<a href="http://core.tcl.tk/tcllib/reportlist">Tcllib Trackers</a>.
Please also report any ideas for enhancements you may have for either
package and/or documentation.</p>
<p>When proposing code changes, please provide <em>unified diffs</em>,
i.e the output of <b class="const">diff -u</b>.</p>
<p>Note further that <em>attachments</em> are strongly preferred over
inlined patches. Attachments can be made by going to the <b class="const">Edit</b>
form of the ticket immediately after its creation, and then using the
left-most button in the secondary navigation bar.</p>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>
<p><a href="../../../../index.html#practcl">practcl</a></p>
<p>practcl</p>
</div>
<div id="category" class="doctools_section"><h2><a name="category">Category</a></h2>
<p>TclOO</p>
</div>
<div id="copyright" class="doctools_section"><h2><a name="copyright">Copyright</a></h2>
<p>Copyright &copy; 2016-2018 Sean Woods &lt;[email protected]&gt;</p>
</div>
</div>
</div></body></html>

Changes to embedded/www/tcllib/files/modules/pt/pt_peg_op.html.

95
96
97
98
99
100
101
102

103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

123
124
125
126
127
128
129
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

122
123
124
125
126
127
128
129







-
+



















-
+







| <a href="../../../toc.html">Table Of Contents</a>
| <a href="../../../../index.html">Keyword Index</a>
| <a href="../../../../toc0.html">Categories</a>
| <a href="../../../../toc1.html">Modules</a>
| <a href="../../../../toc2.html">Applications</a>
 ] <hr>
<div class="doctools">
<h1 class="doctools_title">pt_peg_op(i) 1.0.1 tcllib &quot;Parser Tools&quot;</h1>
<h1 class="doctools_title">pt_peg_op(i) 1.0.2 tcllib &quot;Parser Tools&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>pt_peg_op - Parser Tools PE Grammar Utility Operations</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a></li>
<li class="doctools_section"><a href="#section2">API</a></li>
<li class="doctools_section"><a href="#section3">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
<li class="doctools_section"><a href="#category">Category</a></li>
<li class="doctools_section"><a href="#copyright">Copyright</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl 8.5</b></li>
<li>package require <b class="pkgname">pt::peg::op 1.0.1</b></li>
<li>package require <b class="pkgname">pt::peg::op <span class="opt">?1.0.2?</span></b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><b class="cmd">::peg::peg::op</b> <b class="method">called</b> <i class="arg">container</i></a></li>
<li><a href="#2"><b class="cmd">::peg::peg::op</b> <b class="method">dechain</b> <i class="arg">container</i></a></li>
<li><a href="#3"><b class="cmd">::peg::peg::op</b> <b class="method">drop unreachable</b> <i class="arg">container</i></a></li>
<li><a href="#4"><b class="cmd">::peg::peg::op</b> <b class="method">drop unrealizable</b> <i class="arg">container</i></a></li>
<li><a href="#5"><b class="cmd">::peg::peg::op</b> <b class="method">flatten</b> <i class="arg">container</i></a></li>

Changes to embedded/www/tcllib/files/modules/smtpd/smtpd.html.

287
288
289
290
291
292
293
294
295

296
297
298
299
300
301
302
287
288
289
290
291
292
293


294
295
296
297
298
299
300
301







-
-
+







<p>The content of any error message will not be passed back to the client.</p></dd>
<dt><b class="cmd">validate_recipient</b> callback</dt>
<dd><p>The validate_recipient callback is similar to the validate_sender
callback and permits you to verify a local mailbox and accept mail for
a local user address during RCPT command handling. To reject mail,
throw an error as above. The error message is ignored.</p></dd>
<dt><b class="cmd">deliverMIME</b> callback</dt>
<dd><p>]
The deliverMIME callback is called once a mail message has been
<dd><p>The deliverMIME callback is called once a mail message has been
successfully passed to the server. A mime token is constructed from
the sender, recipients and data and the users procedure it called with
this single argument. When the call returns, the mime token is cleaned
up so if the user wishes to preserve the data she must make a copy.</p>
<pre class="doctools_example">
 proc deliverMIME {token} {
     set sender [lindex [mime::getheader $token From] 0]

Changes to embedded/www/tcllib/files/modules/stooop/switched.html.

305
306
307
308
309
310
311
312

313
314
315


316
317
318
319
320
321
322
305
306
307
308
309
310
311

312
313


314
315
316
317
318
319
320
321
322







-
+

-
-
+
+







listed in the <b class="method">options</b> procedure) but obviously does not check
the validity of the value passed to the <b class="method">set-<b class="option">option</b></b>
procedure, which should throw an error (for example by using the Tcl
error command) if the value is invalid.</p>
<p>The switched layer also keeps track of the options current
values, so that a <b class="method">set-<b class="option">option</b></b> procedure is called
only when the corresponding option value passed as parameter is
different from the current value (see  data members
different from the current value (see <b class="variable">-option</b> data members
description).</p></dd>
<dt></dt>
<dd><p>The  data member is an options current value.
<dt><b class="variable">-option</b></dt>
<dd><p>The <b class="variable">-option</b> data member is an options current value.
There is one for each option listed in the options procedure. It is a
read-only value which the switched layer checks against when an option
is changed.
It is rarely used at the layer derived from switched, except in the
few cases, such as in the following example:</p>
<pre class="doctools_example">
...
332
333
334
335
336
337
338
339
340
341



342
343
344
345
346
347
348
332
333
334
335
336
337
338



339
340
341
342
343
344
345
346
347
348







-
-
-
+
+
+







    puts &quot;manufacturer: $switched::($this,-manufacturer)&quot;
    ...
}
</pre>
<p>In this case, the manufacturer's name is stored at the switched
layer level (this is why the set-manufacturer procedure has nothing to
do) and later retrieved in the printData procedure.</p></dd>
<dt></dt>
<dd><p>The  data member (not to be confused with
the <b class="method">complete</b> procedure) is a boolean.
<dt><b class="variable">complete</b></dt>
<dd><p>The <b class="variable">complete</b> data member (not to be confused with the
<b class="method">complete</b> procedure) is a boolean.
Its initial value is <b class="const">false</b> and it is set to <b class="const">true</b> at
the very end of the switched <b class="method">complete</b> procedure.
It becomes useful when some options should be set at construction time
only and not dynamically, as the following example shows:</p>
<pre class="doctools_example">
proc car::set-width {this value} {
    if {$switched::($this,complete)} {

Changes to embedded/www/tcllib/files/modules/tepam/tepam_doc_gen.html.

315
316
317
318
319
320
321
322

323
324
325
326
327
328
329
315
316
317
318
319
320
321

322
323
324
325
326
327
328
329







-
+







<dd><p>Generates the part of the command line or the synopsis that is specific to an argument. The generated string has to indicate if an argument is optional, named and if it is a flag.</p>
<p>The following parameters are provided to this procedure:</p>
<dl class="doctools_definitions">
   
<dt><i class="arg">Name</i></dt>
<dd><p>Name of the argument</p></dd>
<dt><i class="arg">IsOptional</i></dt>
<dd><p>If true (=<b class="const">1</b>) the argument is optional which should be indicated by the generated string (for example by putting the argument into brackets {} or into question marks '?'):</p>
<dd><p>If true (=<b class="const">1</b>) the argument is optional which should be indicated by the generated string (for example by putting the argument into brackets {[]} or into question marks '?'):</p>
<pre class="doctools_example">gen(TXT,ArgumentString) mtype 1 0 string -&gt; <em>&quot;[mtype]&quot;</em></pre>
</dd>
<dt><i class="arg">IsNamed</i></dt>
<dd><p>If true (=<b class="const">1</b>) an argument is a named argument (option). The generated string should in this case contain the argument/option name, followed by the argument itself:</p>
<pre class="doctools_example">gen(TXT,ArgumentString) mtype 0 1 string -&gt; <em>&quot;-mtype &lt;mtype&gt;&quot;</em></pre>
<p>Named arguments can also be optional:</p>
<pre class="doctools_example">gen(TXT,ArgumentString) mtype 1 1 string -&gt; <em>&quot;[-mtype &lt;mtype&gt;]&quot;</em></pre>

Changes to embedded/www/tcllib/files/modules/tepam/tepam_procedure.html.

706
707
708
709
710
711
712
713

714
715
716
717
718
719
720
706
707
708
709
710
711
712

713
714
715
716
717
718
719
720







-
+







<p>Named arguments can be defined multiple times. If the named argument has the <em>-multiply</em> attribute, all argument values will be collected in a list. Otherwise, only the last provided attribute value will be retained:</p>
<pre class="doctools_example">my_proc <b class="cmd">-n1 N1 -n2 N2 -n1 M1 U1 U2</b>
<em>-&gt; n1:'M1', n2:'N2', u1:'U1', u2:'U2'</em></pre>
<p>The name of the first unnamed argument has therefore not to start with the '-' character. The unnamed argument is otherwise considered as name of another named argument. This is especially important if the first unnamed argument is given by a variable that can contain any character strings:</p>
<pre class="doctools_example">my_proc <b class="cmd">-n1 N1 -n2 N2 &quot;-&gt;&quot; &quot;&lt;-&quot;</b>
<em>-&gt; my_proc: Argument '-&gt;' not known</em>
set U1 &quot;-&gt;&quot;
my_proc -n1 N1 -n2 N2 $U1 U2}]
my_proc <b class="cmd">-n1 N1 -n2 N2 $U1 U2</b>
my_proc: Argument '-&gt;' not known</pre>
<p>The '--' flag allows separating unambiguously the unnamed arguments from the named arguments. All data after the '--' flag will be considered as unnamed argument:</p>
<pre class="doctools_example">my_proc <b class="cmd">-n1 N1 -n2 N2 -- &quot;-&gt;&quot; &quot;&lt;-&quot;</b>
<em>-&gt; n1:'N1', n2:'N2', u1:'-&gt;', u2:'&lt;-'</em>
set U1 &quot;-&gt;&quot;
my_proc <b class="cmd">-n1 N1 -n2 N2 -- $U1 U2</b>
<em>-&gt; n1:'N1', n2:'N2', u1:'-&gt;', u2:'&lt;-'</em></pre>

Changes to embedded/www/tcllib/files/modules/textutil/adjust.html.

199
200
201
202
203
204
205
206

207
208

209
210
211
212
213
214
215
199
200
201
202
203
204
205

206


207
208
209
210
211
212
213
214







-
+
-
-
+







there are no space chars at the end of this line, and there may be
some space chars at the beginning, despite of the <b class="option">-full</b> option.</p></dd>
</dl></dd>
<dt><b class="option">-length</b> <i class="arg">integer</i></dt>
<dd><p>Set the length of the <em>logical</em> line in the string to
<i class="arg">integer</i>.  <i class="arg">integer</i> must be a positive integer
value. Defaults to <b class="const">72</b>.</p></dd>
<dt><b class="option">-strictlength</b></dt>
<dt><b class="option">-strictlength</b> <i class="arg">boolean</i></dt>
<dd><p><i class="arg">boolean</i>]
If set to <b class="const">false</b> (default), a line can exceed the specified
<dd><p>If set to <b class="const">false</b> (default), a line can exceed the specified
<b class="option">-length</b> if a single word is longer than <b class="option">-length</b>. If
set to <b class="const">true</b>, words that are longer than <b class="option">-length</b> are
split so that no line exceeds the specified <b class="option">-length</b>.</p></dd>
</dl></dd>
<dt><a name="2"><b class="cmd">::textutil::adjust::readPatterns</b> <i class="arg">filename</i></a></dt>
<dd><p>Loads the internal storage for hyphenation patterns with the contents
of the file <i class="arg">filename</i>. This has to be done prior to calling

Changes to embedded/www/tcllib/files/modules/tool/tool_dict_ensemble.html.

117
118
119
120
121
122
123
124

125
126
127
128
129
130
131
132
133

134
135

136
137
138
139
140
141
142
117
118
119
120
121
122
123

124
125
126
127
128
129
130
131
132

133


134
135
136
137
138
139
140
141







-
+








-
+
-
-
+







</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">tool <span class="opt">?0.4.2?</span></b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><em>object</em> <i class="arg">ensemble</i> <b class="cmd">add</b> <i class="arg">field</i></a></li>
<li><a href="#1"><em>object</em> <i class="arg">ensemble</i> <b class="cmd">add</b> <i class="arg">field</i> <i class="arg">value</i> <i class="arg">value ...</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>The <b class="cmd">dict_ensemble</b> command is a keyword added by <b class="package"><a href="tool.html">tool</a></b>. It defines
a public variable (stored as a dict), and an access function to manipulated and
access the values stored in that dict.</p>
<dl class="doctools_definitions">
<dt><a name="1"><em>object</em> <i class="arg">ensemble</i> <b class="cmd">add</b> <i class="arg">field</i></a></dt>
<dt><a name="1"><em>object</em> <i class="arg">ensemble</i> <b class="cmd">add</b> <i class="arg">field</i> <i class="arg">value</i> <i class="arg">value ...</i></a></dt>
<dd><p>] <i class="arg">value</i> <i class="arg">value ...</i>]
Adds elements to a list maintained with the <i class="arg">field</i> leaf of the dict maintained
<dd><p>Adds elements to a list maintained with the <i class="arg">field</i> leaf of the dict maintained
my this ensemble.
Declares a variable <i class="arg">name</i> which will be initialized as an array, populated with <i class="arg">contents</i> for objects of this class, as well as any
objects for classes which are descendents of this class.</p></dd>
</dl>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">AUTHORS</a></h2>
<p>Sean Woods</p>

Changes to embedded/www/tcllib/files/modules/websocket/websocket.html.

283
284
285
286
287
288
289
290

291
292
293
294
295
296
297
283
284
285
286
287
288
289

290
291
292
293
294
295
296
297







-
+







<dt><a name="3"><b class="cmd">::websocket::server</b> <i class="arg">sock</i></a></dt>
<dd><p>This command registers the (accept) socket <i class="arg">sock</i> as the
identifier fo an HTTP server that is capable of doing WebSockets.
Paths onto which this server will listen for incoming connections
should be declared using <b class="cmd">::websocket::live</b>.</p></dd>
<dt><a name="4"><b class="cmd">::websocket::live</b> <i class="arg">sock</i> <i class="arg">path</i> <i class="arg">cb</i> <span class="opt">?<i class="arg">proto</i>?</span></a></dt>
<dd><p>This procedure registers callbacks that will be performed on a
WebSocket compliant server registered with <b class="cmd">::websocket::server</b>]
WebSocket compliant server registered with <b class="cmd">::websocket::server</b>
whenever a client connects to a matching path and protocol. 
<i class="arg">sock</i> is the listening socket of the websocket compliant server
declared using <b class="cmd">::websocket::server</b>.  <i class="arg">path</i> is a glob-style
path to match in client request, whenever this will occur.  <i class="arg">cb</i>
is the command to callback (see Callbacks).  <i class="arg">proto</i> is a
glob-style protocol name matcher.</p></dd>
<dt><a name="5"><b class="cmd">::websocket::test</b> <i class="arg">srvSock</i> <i class="arg">cliSock</i> <i class="arg">path</i> <span class="opt">?<i class="arg">hdrs</i>?</span> <span class="opt">?<i class="arg">qry</i>?</span></a></dt>

Changes to embedded/www/tcllib/toc.html.

847
848
849
850
851
852
853




854
855
856
857

858
859
860
861

862
863
864
865

866
867
868
869

870
871
872
873

874
875
876
877

878
879
880
881

882
883
884
885

886
887
888
889

890
891
892
893

894
895
896
897

898
899
900
901

902
903
904
905

906
907
908
909

910
911
912
913

914
915
916
917

918
919
920
921

922
923
924
925

926
927
928
929

930
931
932
933

934
935
936
937

938
939
940
941

942
943
944
945

946
947
948
949

950
951
952
953

954
955
956
957

958
959
960
961

962
963
964
965

966
967
968
969

970
971
972
973

974
975
976
977

978
979
980
981

982
983
984
985

986
987
988
989

990
991
992
993

994
995
996
997

998
999
1000
1001

1002
1003
1004
1005

1006
1007
1008
1009

1010
1011
1012
1013

1014
1015
1016
1017

1018
1019
1020
1021

1022
1023
1024
1025

1026
1027
1028
1029

1030
1031
1032
1033

1034
1035
1036
1037

1038
1039
1040
1041

1042
1043
1044
1045

1046
1047
1048
1049

1050
1051
1052
1053

1054
1055
1056
1057

1058
1059
1060
1061

1062
1063
1064
1065

1066
1067
1068
1069

1070
1071
1072
1073

1074
1075
1076
1077

1078
1079
1080
1081

1082
1083
1084
1085

1086
1087
1088
1089

1090
1091
1092
1093

1094
1095
1096
1097

1098
1099
1100
1101

1102
1103
1104
1105

1106
1107
1108
1109

1110
1111
1112
1113

1114
1115
1116
1117

1118
1119
1120
1121

1122
1123
1124
1125

1126
1127
1128
1129

1130
1131
1132
1133

1134
1135
1136
1137

1138
1139
1140
1141

1142
1143
1144
1145

1146
1147
1148
1149

1150
1151
1152
1153

1154
1155
1156
1157

1158
1159
1160
1161

1162
1163
1164
1165

1166
1167
1168
1169

1170
1171
1172
1173

1174
1175
1176
1177

1178
1179
1180
1181

1182
1183
1184
1185

1186
1187
1188
1189

1190
1191
1192
1193

1194
1195
1196
1197

1198
1199
1200
1201

1202
1203
1204
1205

1206
1207
1208
1209

1210
1211
1212
1213

1214
1215
1216
1217

1218
1219
1220
1221

1222
1223
1224
1225

1226
1227
1228
1229

1230
1231
1232
1233

1234
1235
1236
1237

1238
1239
1240
1241

1242
1243
1244
1245

1246
1247
1248
1249

1250
1251
1252
1253

1254
1255
1256
1257

1258
1259
1260
1261

1262
1263
1264
1265

1266
1267
1268
1269

1270
1271
1272
1273

1274
1275
1276
1277

1278
1279
1280
1281

1282
1283
1284
1285

1286
1287
1288
1289

1290
1291
1292
1293

1294
1295
1296
1297

1298
1299
1300
1301

1302
1303
1304
1305

1306
1307
1308
1309

1310
1311
1312
1313

1314
1315
1316
1317

1318
1319
1320
1321

1322
1323
1324
1325

1326
1327
1328
1329

1330
1331
1332
1333

1334
1335
1336
1337

1338
1339
1340
1341

1342
1343
1344
1345

1346
1347
1348
1349

1350
1351
1352
1353

1354
1355
1356
1357

1358
1359
1360
1361

1362
1363
1364
1365

1366
1367
1368
1369

1370
1371
1372
1373

1374
1375
1376
1377

1378
1379
1380
1381

1382
1383
1384
1385

1386
1387
1388
1389

1390
1391
1392
1393

1394
1395
1396
1397

1398
1399
1400
1401

1402
1403
1404
1405

1406
1407
1408
1409

1410
1411
1412
1413

1414
1415
1416
1417

1418
1419
1420
1421

1422
1423
1424
1425

1426
1427
1428
1429

1430
1431
1432
1433

1434
1435
1436
1437

1438
1439
1440
1441

1442
1443
1444
1445

1446
1447
1448
1449

1450
1451
1452
1453

1454
1455
1456
1457

1458
1459
1460
1461

1462
1463
1464
1465

1466
1467
1468
1469

1470
1471
1472
1473

1474
1475
1476
1477

1478
1479
1480
1481

1482
1483
1484
1485

1486
1487
1488
1489

1490
1491
1492
1493

1494
1495
1496
1497

1498
1499
1500
1501

1502
1503
1504
1505

1506
1507
1508
1509

1510
1511
1512
1513

1514
1515
1516
1517

1518
1519
1520
1521

1522
1523
1524
1525

1526
1527
1528
1529

1530
1531
1532
1533

1534
1535
1536
1537

1538
1539
1540
1541

1542
1543
1544
1545

1546
1547
1548
1549

1550
1551
1552
1553

1554
1555
1556
1557

1558
1559
1560
1561

1562
1563
1564
1565

1566
1567
1568
1569

1570
1571
1572
1573

1574
1575
1576
1577

1578
1579
1580
1581

1582
1583
1584
1585

1586
1587
1588
1589

1590
1591
1592
1593

1594
1595
1596
1597

1598
1599
1600
1601

1602
1603
1604
1605

1606
1607
1608
1609

1610
1611
1612
1613

1614
1615
1616
1617

1618
1619
1620
1621

1622
1623
1624
1625

1626
1627
1628
1629

1630
1631
1632
1633

1634
1635
1636
1637

1638
1639
1640
1641

1642
1643
1644
1645

1646
1647
1648
1649

1650
1651
1652
1653

1654
1655
1656
1657

1658
1659
1660
1661

1662
1663
1664
1665

1666
1667
1668
1669

1670
1671
1672
1673

1674
1675
1676
1677

1678
1679
1680
1681

1682
1683
1684
1685

1686
1687
1688
1689

1690
1691
1692
1693

1694
1695
1696
1697

1698
1699
1700
1701

1702
1703
1704
1705

1706
1707
1708
1709

1710
1711
1712
1713

1714
1715
1716
1717

1718
1719
1720
1721

1722
1723
1724
1725

1726
1727
1728
1729

1730
1731
1732
1733

1734
1735
1736
1737

1738
1739
1740
1741

1742
1743
1744
1745

1746
1747
1748
1749

1750
1751
1752
1753

1754
1755
1756
1757

1758
1759
1760
1761

1762
1763
1764
1765

1766
1767
1768
1769

1770
1771
1772
1773

1774
1775
1776
1777

1778
1779
1780
1781

1782
1783
1784
1785

1786
1787
1788
1789

1790
1791
1792
1793

1794
1795
1796
1797

1798
1799
1800
1801

1802
1803
1804
1805

1806
1807
1808
1809
1810
847
848
849
850
851
852
853
854
855
856
857
858
859
860

861
862
863
864

865
866
867
868

869
870
871
872

873
874
875
876

877
878
879
880

881
882
883
884

885
886
887
888

889
890
891
892

893
894
895
896

897
898
899
900

901
902
903
904

905
906
907
908

909
910
911
912

913
914
915
916

917
918
919
920

921
922
923
924

925
926
927
928

929
930
931
932

933
934
935
936

937
938
939
940

941
942
943
944

945
946
947
948

949
950
951
952

953
954
955
956

957
958
959
960

961
962
963
964

965
966
967
968

969
970
971
972

973
974
975
976

977
978
979
980

981
982
983
984

985
986
987
988

989
990
991
992

993
994
995
996

997
998
999
1000

1001
1002
1003
1004

1005
1006
1007
1008

1009
1010
1011
1012

1013
1014
1015
1016

1017
1018
1019
1020

1021
1022
1023
1024

1025
1026
1027
1028

1029
1030
1031
1032

1033
1034
1035
1036

1037
1038
1039
1040

1041
1042
1043
1044

1045
1046
1047
1048

1049
1050
1051
1052

1053
1054
1055
1056

1057
1058
1059
1060

1061
1062
1063
1064

1065
1066
1067
1068

1069
1070
1071
1072

1073
1074
1075
1076

1077
1078
1079
1080

1081
1082
1083
1084

1085
1086
1087
1088

1089
1090
1091
1092

1093
1094
1095
1096

1097
1098
1099
1100

1101
1102
1103
1104

1105
1106
1107
1108

1109
1110
1111
1112

1113
1114
1115
1116

1117
1118
1119
1120

1121
1122
1123
1124

1125
1126
1127
1128

1129
1130
1131
1132

1133
1134
1135
1136

1137
1138
1139
1140

1141
1142
1143
1144

1145
1146
1147
1148

1149
1150
1151
1152

1153
1154
1155
1156

1157
1158
1159
1160

1161
1162
1163
1164

1165
1166
1167
1168

1169
1170
1171
1172

1173
1174
1175
1176

1177
1178
1179
1180

1181
1182
1183
1184

1185
1186
1187
1188

1189
1190
1191
1192

1193
1194
1195
1196

1197
1198
1199
1200

1201
1202
1203
1204

1205
1206
1207
1208

1209
1210
1211
1212

1213
1214
1215
1216

1217
1218
1219
1220

1221
1222
1223
1224

1225
1226
1227
1228

1229
1230
1231
1232

1233
1234
1235
1236

1237
1238
1239
1240

1241
1242
1243
1244

1245
1246
1247
1248

1249
1250
1251
1252

1253
1254
1255
1256

1257
1258
1259
1260

1261
1262
1263
1264

1265
1266
1267
1268

1269
1270
1271
1272

1273
1274
1275
1276

1277
1278
1279
1280

1281
1282
1283
1284

1285
1286
1287
1288

1289
1290
1291
1292

1293
1294
1295
1296

1297
1298
1299
1300

1301
1302
1303
1304

1305
1306
1307
1308

1309
1310
1311
1312

1313
1314
1315
1316

1317
1318
1319
1320

1321
1322
1323
1324

1325
1326
1327
1328

1329
1330
1331
1332

1333
1334
1335
1336

1337
1338
1339
1340

1341
1342
1343
1344

1345
1346
1347
1348

1349
1350
1351
1352

1353
1354
1355
1356

1357
1358
1359
1360

1361
1362
1363
1364

1365
1366
1367
1368

1369
1370
1371
1372

1373
1374
1375
1376

1377
1378
1379
1380

1381
1382
1383
1384

1385
1386
1387
1388

1389
1390
1391
1392

1393
1394
1395
1396

1397
1398
1399
1400

1401
1402
1403
1404

1405
1406
1407
1408

1409
1410
1411
1412

1413
1414
1415
1416

1417
1418
1419
1420

1421
1422
1423
1424

1425
1426
1427
1428

1429
1430
1431
1432

1433
1434
1435
1436

1437
1438
1439
1440

1441
1442
1443
1444

1445
1446
1447
1448

1449
1450
1451
1452

1453
1454
1455
1456

1457
1458
1459
1460

1461
1462
1463
1464

1465
1466
1467
1468

1469
1470
1471
1472

1473
1474
1475
1476

1477
1478
1479
1480

1481
1482
1483
1484

1485
1486
1487
1488

1489
1490
1491
1492

1493
1494
1495
1496

1497
1498
1499
1500

1501
1502
1503
1504

1505
1506
1507
1508

1509
1510
1511
1512

1513
1514
1515
1516

1517
1518
1519
1520

1521
1522
1523
1524

1525
1526
1527
1528

1529
1530
1531
1532

1533
1534
1535
1536

1537
1538
1539
1540

1541
1542
1543
1544

1545
1546
1547
1548

1549
1550
1551
1552

1553
1554
1555
1556

1557
1558
1559
1560

1561
1562
1563
1564

1565
1566
1567
1568

1569
1570
1571
1572

1573
1574
1575
1576

1577
1578
1579
1580

1581
1582
1583
1584

1585
1586
1587
1588

1589
1590
1591
1592

1593
1594
1595
1596

1597
1598
1599
1600

1601
1602
1603
1604

1605
1606
1607
1608

1609
1610
1611
1612

1613
1614
1615
1616

1617
1618
1619
1620

1621
1622
1623
1624

1625
1626
1627
1628

1629
1630
1631
1632

1633
1634
1635
1636

1637
1638
1639
1640

1641
1642
1643
1644

1645
1646
1647
1648

1649
1650
1651
1652

1653
1654
1655
1656

1657
1658
1659
1660

1661
1662
1663
1664

1665
1666
1667
1668

1669
1670
1671
1672

1673
1674
1675
1676

1677
1678
1679
1680

1681
1682
1683
1684

1685
1686
1687
1688

1689
1690
1691
1692

1693
1694
1695
1696

1697
1698
1699
1700

1701
1702
1703
1704

1705
1706
1707
1708

1709
1710
1711
1712

1713
1714
1715
1716

1717
1718
1719
1720

1721
1722
1723
1724

1725
1726
1727
1728

1729
1730
1731
1732

1733
1734
1735
1736

1737
1738
1739
1740

1741
1742
1743
1744

1745
1746
1747
1748

1749
1750
1751
1752

1753
1754
1755
1756

1757
1758
1759
1760

1761
1762
1763
1764

1765
1766
1767
1768

1769
1770
1771
1772

1773
1774
1775
1776

1777
1778
1779
1780

1781
1782
1783
1784

1785
1786
1787
1788

1789
1790
1791
1792

1793
1794
1795
1796

1797
1798
1799
1800

1801
1802
1803
1804

1805
1806
1807
1808

1809
1810
1811
1812
1813
1814







+
+
+
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+





<td class="#doctools_tocright">Special mathematical functions</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='math_statistics'><a href="files/modules/math/statistics.html">math::statistics</a></td>
<td class="#doctools_tocright">Basic statistical functions and procedures</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='math_trig'><a href="files/modules/math/trig.html">math::trig</a></td>
<td class="#doctools_tocright">Trigonometric anf hyperbolic functions</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='md4'><a href="files/modules/md4/md4.html">md4</a></td>
<td class="#doctools_tocright">MD4 Message-Digest Algorithm</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='md5'><a href="files/modules/md5/md5.html">md5</a></td>
<td class="#doctools_tocright">MD5 Message-Digest Algorithm</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='md5crypt'><a href="files/modules/md5crypt/md5crypt.html">md5crypt</a></td>
<td class="#doctools_tocright">MD5-based password encryption</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='mime'><a href="files/modules/mime/mime.html">mime</a></td>
<td class="#doctools_tocright">Manipulation of MIME body parts</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='mpexpand'><a href="files/modules/doctools/mpexpand.html">mpexpand</a></td>
<td class="#doctools_tocright">Markup processor</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='multiplexer'><a href="files/modules/multiplexer/multiplexer.html">multiplexer</a></td>
<td class="#doctools_tocright">One-to-many communication with sockets.</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='nameserv'><a href="files/modules/nns/nns_client.html">nameserv</a></td>
<td class="#doctools_tocright">Name service facility, Client</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='nameserv_auto'><a href="files/modules/nns/nns_auto.html">nameserv::auto</a></td>
<td class="#doctools_tocright">Name service facility, Client Extension</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='nameserv_common'><a href="files/modules/nns/nns_common.html">nameserv::common</a></td>
<td class="#doctools_tocright">Name service facility, shared definitions</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='nameserv_protocol'><a href="files/modules/nns/nns_protocol.html">nameserv::protocol</a></td>
<td class="#doctools_tocright">Name service facility, client/server protocol</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='nameserv_server'><a href="files/modules/nns/nns_server.html">nameserv::server</a></td>
<td class="#doctools_tocright">Name service facility, Server</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='namespacex'><a href="files/modules/namespacex/namespacex.html">namespacex</a></td>
<td class="#doctools_tocright">Namespace utility commands</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='ncgi'><a href="files/modules/ncgi/ncgi.html">ncgi</a></td>
<td class="#doctools_tocright">Procedures to manipulate CGI values.</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='nettool'><a href="files/modules/nettool/nettool.html">nettool</a></td>
<td class="#doctools_tocright">Tools for networked applications</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='nmea'><a href="files/modules/nmea/nmea.html">nmea</a></td>
<td class="#doctools_tocright">Process NMEA data</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='nns'><a href="files/apps/nns.html">nns</a></td>
<td class="#doctools_tocright">Name service facility, Commandline Client Application</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='nns_intro'><a href="files/modules/nns/nns_intro.html">nns_intro</a></td>
<td class="#doctools_tocright">Name service facility, introduction</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='nnsd'><a href="files/apps/nnsd.html">nnsd</a></td>
<td class="#doctools_tocright">Name service facility, Commandline Server Application</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='nnslog'><a href="files/apps/nnslog.html">nnslog</a></td>
<td class="#doctools_tocright">Name service facility, Commandline Logging Client Application</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='nntp'><a href="files/modules/nntp/nntp.html">nntp</a></td>
<td class="#doctools_tocright">Tcl client for the NNTP protocol</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='ntp_time'><a href="files/modules/ntp/ntp_time.html">ntp_time</a></td>
<td class="#doctools_tocright">Tcl Time Service Client</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='oauth'><a href="files/modules/oauth/oauth.html">oauth</a></td>
<td class="#doctools_tocright">oauth API base signature</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='oo_util'><a href="files/modules/tool/meta.html">oo::util</a></td>
<td class="#doctools_tocright">Utility commands for TclOO</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='oo_util'><a href="files/modules/ooutil/ooutil.html">oo::util</a></td>
<td class="#doctools_tocright">Utility commands for TclOO</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='oometa'><a href="files/modules/oometa/oometa.html">oometa</a></td>
<td class="#doctools_tocright">oo::meta A data registry for classess</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='otp'><a href="files/modules/otp/otp.html">otp</a></td>
<td class="#doctools_tocright">One-Time Passwords</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='page'><a href="files/apps/page.html">page</a></td>
<td class="#doctools_tocright">Parser Generator</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='page_intro'><a href="files/modules/page/page_intro.html">page_intro</a></td>
<td class="#doctools_tocright">page introduction</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='page_pluginmgr'><a href="files/modules/page/page_pluginmgr.html">page_pluginmgr</a></td>
<td class="#doctools_tocright">page plugin manager</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='page_util_flow'><a href="files/modules/page/page_util_flow.html">page_util_flow</a></td>
<td class="#doctools_tocright">page dataflow/treewalker utility</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='page_util_norm_lemon'><a href="files/modules/page/page_util_norm_lemon.html">page_util_norm_lemon</a></td>
<td class="#doctools_tocright">page AST normalization, LEMON</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='page_util_norm_peg'><a href="files/modules/page/page_util_norm_peg.html">page_util_norm_peg</a></td>
<td class="#doctools_tocright">page AST normalization, PEG</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='page_util_peg'><a href="files/modules/page/page_util_peg.html">page_util_peg</a></td>
<td class="#doctools_tocright">page PEG transformation utilities</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='page_util_quote'><a href="files/modules/page/page_util_quote.html">page_util_quote</a></td>
<td class="#doctools_tocright">page character quoting utilities</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='picoirc'><a href="files/modules/irc/picoirc.html">picoirc</a></td>
<td class="#doctools_tocright">Small and simple embeddable IRC client.</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pki'><a href="files/modules/pki/pki.html">pki</a></td>
<td class="#doctools_tocright">Implementation of the public key cipher</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pluginmgr'><a href="files/modules/pluginmgr/pluginmgr.html">pluginmgr</a></td>
<td class="#doctools_tocright">Manage a plugin</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='png'><a href="files/modules/png/png.html">png</a></td>
<td class="#doctools_tocright">PNG querying and manipulation of meta data</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pop3'><a href="files/modules/pop3/pop3.html">pop3</a></td>
<td class="#doctools_tocright">Tcl client for POP3 email protocol</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pop3d'><a href="files/modules/pop3d/pop3d.html">pop3d</a></td>
<td class="#doctools_tocright">Tcl POP3 server implementation</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pop3d_dbox'><a href="files/modules/pop3d/pop3d_dbox.html">pop3d::dbox</a></td>
<td class="#doctools_tocright">Simple mailbox database for pop3d</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pop3d_udb'><a href="files/modules/pop3d/pop3d_udb.html">pop3d::udb</a></td>
<td class="#doctools_tocright">Simple user database for pop3d</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='practcl'><a href="files/modules/practcl/practcl.html">practcl</a></td>
<td class="#doctools_tocright">The Practcl Module</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='processman'><a href="files/modules/processman/processman.html">processman</a></td>
<td class="#doctools_tocright">Tool for automating the period callback of commands</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='profiler'><a href="files/modules/profiler/profiler.html">profiler</a></td>
<td class="#doctools_tocright">Tcl source code profiler</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt'><a href="files/apps/pt.html">pt</a></td>
<td class="#doctools_tocright">Parser Tools Application</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_ast'><a href="files/modules/pt/pt_astree.html">pt::ast</a></td>
<td class="#doctools_tocright">Abstract Syntax Tree Serialization</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_cparam_configuration_critcl'><a href="files/modules/pt/pt_cparam_config_critcl.html">pt::cparam::configuration::critcl</a></td>
<td class="#doctools_tocright">C/PARAM, Canned configuration, Critcl</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_cparam_configuration_tea'><a href="files/modules/pt/pt_cparam_config_tea.html">pt::cparam::configuration::tea</a></td>
<td class="#doctools_tocright">C/PARAM, Canned configuration, TEA</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_json_language'><a href="files/modules/pt/pt_json_language.html">pt::json_language</a></td>
<td class="#doctools_tocright">The JSON Grammar Exchange Format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_param'><a href="files/modules/pt/pt_param.html">pt::param</a></td>
<td class="#doctools_tocright">PackRat Machine Specification</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_pe'><a href="files/modules/pt/pt_pexpression.html">pt::pe</a></td>
<td class="#doctools_tocright">Parsing Expression Serialization</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_pe_op'><a href="files/modules/pt/pt_pexpr_op.html">pt::pe::op</a></td>
<td class="#doctools_tocright">Parsing Expression Utilities</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg'><a href="files/modules/pt/pt_pegrammar.html">pt::peg</a></td>
<td class="#doctools_tocright">Parsing Expression Grammar Serialization</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_container'><a href="files/modules/pt/pt_peg_container.html">pt::peg::container</a></td>
<td class="#doctools_tocright">PEG Storage</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_container_peg'><a href="files/modules/pt/pt_peg_container_peg.html">pt::peg::container::peg</a></td>
<td class="#doctools_tocright">PEG Storage. Canned PEG grammar specification</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_export'><a href="files/modules/pt/pt_peg_export.html">pt::peg::export</a></td>
<td class="#doctools_tocright">PEG Export</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_export_container'><a href="files/modules/pt/pt_peg_export_container.html">pt::peg::export::container</a></td>
<td class="#doctools_tocright">PEG Export Plugin. Write CONTAINER format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_export_json'><a href="files/modules/pt/pt_peg_export_json.html">pt::peg::export::json</a></td>
<td class="#doctools_tocright">PEG Export Plugin. Write JSON format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_export_peg'><a href="files/modules/pt/pt_peg_export_peg.html">pt::peg::export::peg</a></td>
<td class="#doctools_tocright">PEG Export Plugin. Write PEG format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_from_container'><a href="files/modules/pt/pt_peg_from_container.html">pt::peg::from::container</a></td>
<td class="#doctools_tocright">PEG Conversion. From CONTAINER format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_from_json'><a href="files/modules/pt/pt_peg_from_json.html">pt::peg::from::json</a></td>
<td class="#doctools_tocright">PEG Conversion. Read JSON format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_from_peg'><a href="files/modules/pt/pt_peg_from_peg.html">pt::peg::from::peg</a></td>
<td class="#doctools_tocright">PEG Conversion. Read PEG format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_import'><a href="files/modules/pt/pt_peg_import.html">pt::peg::import</a></td>
<td class="#doctools_tocright">PEG Import</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_import_container'><a href="files/modules/pt/pt_peg_import_container.html">pt::peg::import::container</a></td>
<td class="#doctools_tocright">PEG Import Plugin. From CONTAINER format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_import_json'><a href="files/modules/pt/pt_peg_import_json.html">pt::peg::import::json</a></td>
<td class="#doctools_tocright">PEG Import Plugin. Read JSON format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_import_peg'><a href="files/modules/pt/pt_peg_import_peg.html">pt::peg::import::peg</a></td>
<td class="#doctools_tocright">PEG Import Plugin. Read PEG format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_interp'><a href="files/modules/pt/pt_peg_interp.html">pt::peg::interp</a></td>
<td class="#doctools_tocright">Interpreter for parsing expression grammars</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_to_container'><a href="files/modules/pt/pt_peg_to_container.html">pt::peg::to::container</a></td>
<td class="#doctools_tocright">PEG Conversion. Write CONTAINER format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_to_cparam'><a href="files/modules/pt/pt_peg_to_cparam.html">pt::peg::to::cparam</a></td>
<td class="#doctools_tocright">PEG Conversion. Write CPARAM format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_to_json'><a href="files/modules/pt/pt_peg_to_json.html">pt::peg::to::json</a></td>
<td class="#doctools_tocright">PEG Conversion. Write JSON format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_to_param'><a href="files/modules/pt/pt_peg_to_param.html">pt::peg::to::param</a></td>
<td class="#doctools_tocright">PEG Conversion. Write PARAM format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_to_peg'><a href="files/modules/pt/pt_peg_to_peg.html">pt::peg::to::peg</a></td>
<td class="#doctools_tocright">PEG Conversion. Write PEG format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_to_tclparam'><a href="files/modules/pt/pt_peg_to_tclparam.html">pt::peg::to::tclparam</a></td>
<td class="#doctools_tocright">PEG Conversion. Write TCLPARAM format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_language'><a href="files/modules/pt/pt_peg_language.html">pt::peg_language</a></td>
<td class="#doctools_tocright">PEG Language Tutorial</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_pegrammar'><a href="files/modules/pt/pt_peg_introduction.html">pt::pegrammar</a></td>
<td class="#doctools_tocright">Introduction to Parsing Expression Grammars</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_pgen'><a href="files/modules/pt/pt_pgen.html">pt::pgen</a></td>
<td class="#doctools_tocright">Parser Generator</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_rde'><a href="files/modules/pt/pt_rdengine.html">pt::rde</a></td>
<td class="#doctools_tocright">Parsing Runtime Support, PARAM based</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_tclparam_configuration_nx'><a href="files/modules/pt/pt_tclparam_config_nx.html">pt::tclparam::configuration::nx</a></td>
<td class="#doctools_tocright">Tcl/PARAM, Canned configuration, NX</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_tclparam_configuration_snit'><a href="files/modules/pt/pt_tclparam_config_snit.html">pt::tclparam::configuration::snit</a></td>
<td class="#doctools_tocright">Tcl/PARAM, Canned configuration, Snit</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_tclparam_configuration_tcloo'><a href="files/modules/pt/pt_tclparam_config_tcloo.html">pt::tclparam::configuration::tcloo</a></td>
<td class="#doctools_tocright">Tcl/PARAM, Canned configuration, Tcloo</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_util'><a href="files/modules/pt/pt_util.html">pt::util</a></td>
<td class="#doctools_tocright">General utilities</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_export_api'><a href="files/modules/pt/pt_to_api.html">pt_export_api</a></td>
<td class="#doctools_tocright">Parser Tools Export API</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_import_api'><a href="files/modules/pt/pt_from_api.html">pt_import_api</a></td>
<td class="#doctools_tocright">Parser Tools Import API</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_introduction'><a href="files/modules/pt/pt_introduction.html">pt_introduction</a></td>
<td class="#doctools_tocright">Introduction to Parser Tools</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_parse_peg'><a href="files/modules/pt/pt_parse_peg.html">pt_parse_peg</a></td>
<td class="#doctools_tocright">Parser Tools PEG Parser</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_parser_api'><a href="files/modules/pt/pt_parser_api.html">pt_parser_api</a></td>
<td class="#doctools_tocright">Parser API</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_op'><a href="files/modules/pt/pt_peg_op.html">pt_peg_op</a></td>
<td class="#doctools_tocright">Parser Tools PE Grammar Utility Operations</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='rc4'><a href="files/modules/rc4/rc4.html">rc4</a></td>
<td class="#doctools_tocright">Implementation of the RC4 stream cipher</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='rcs'><a href="files/modules/rcs/rcs.html">rcs</a></td>
<td class="#doctools_tocright">RCS low level utilities</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='report'><a href="files/modules/report/report.html">report</a></td>
<td class="#doctools_tocright">Create and manipulate report objects</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='rest'><a href="files/modules/rest/rest.html">rest</a></td>
<td class="#doctools_tocright">define REST web APIs and call them inline or asychronously</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='ripemd128'><a href="files/modules/ripemd/ripemd128.html">ripemd128</a></td>
<td class="#doctools_tocright">RIPEMD-128 Message-Digest Algorithm</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='ripemd160'><a href="files/modules/ripemd/ripemd160.html">ripemd160</a></td>
<td class="#doctools_tocright">RIPEMD-160 Message-Digest Algorithm</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='s3'><a href="files/modules/amazon-s3/S3.html">S3</a></td>
<td class="#doctools_tocright">Amazon S3 Web Service Interface</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='sasl'><a href="files/modules/sasl/sasl.html">SASL</a></td>
<td class="#doctools_tocright">Implementation of SASL mechanisms for Tcl</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='sasl_ntlm'><a href="files/modules/sasl/ntlm.html">SASL::NTLM</a></td>
<td class="#doctools_tocright">Implementation of SASL NTLM mechanism for Tcl</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='sasl_scram'><a href="files/modules/sasl/scram.html">SASL::SCRAM</a></td>
<td class="#doctools_tocright">Implementation of SASL SCRAM mechanism for Tcl</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='sasl_xgoogletoken'><a href="files/modules/sasl/gtoken.html">SASL::XGoogleToken</a></td>
<td class="#doctools_tocright">Implementation of SASL NTLM mechanism for Tcl</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='sha1'><a href="files/modules/sha1/sha1.html">sha1</a></td>
<td class="#doctools_tocright">SHA1 Message-Digest Algorithm</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='sha256'><a href="files/modules/sha1/sha256.html">sha256</a></td>
<td class="#doctools_tocright">SHA256 Message-Digest Algorithm</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='simulation_annealing'><a href="files/modules/simulation/annealing.html">simulation::annealing</a></td>
<td class="#doctools_tocright">Simulated annealing</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='simulation_montecarlo'><a href="files/modules/simulation/montecarlo.html">simulation::montecarlo</a></td>
<td class="#doctools_tocright">Monte Carlo simulations</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='simulation_random'><a href="files/modules/simulation/simulation_random.html">simulation::random</a></td>
<td class="#doctools_tocright">Pseudo-random number generators</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='smtp'><a href="files/modules/mime/smtp.html">smtp</a></td>
<td class="#doctools_tocright">Client-side tcl implementation of the smtp protocol</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='smtpd'><a href="files/modules/smtpd/smtpd.html">smtpd</a></td>
<td class="#doctools_tocright">Tcl SMTP server implementation</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='snit'><a href="files/modules/snit/snit.html">snit</a></td>
<td class="#doctools_tocright">Snit's Not Incr Tcl</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='snitfaq'><a href="files/modules/snit/snitfaq.html">snitfaq</a></td>
<td class="#doctools_tocright">Snit Frequently Asked Questions</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='soundex'><a href="files/modules/soundex/soundex.html">soundex</a></td>
<td class="#doctools_tocright">Soundex</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='stooop'><a href="files/modules/stooop/stooop.html">stooop</a></td>
<td class="#doctools_tocright">Object oriented extension.</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='string_token'><a href="files/modules/string/token.html">string::token</a></td>
<td class="#doctools_tocright">Regex based iterative lexing</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='string_token_shell'><a href="files/modules/string/token_shell.html">string::token::shell</a></td>
<td class="#doctools_tocright">Parsing of shell command line</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='stringprep'><a href="files/modules/stringprep/stringprep.html">stringprep</a></td>
<td class="#doctools_tocright">Implementation of stringprep</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='stringprep_data'><a href="files/modules/stringprep/stringprep_data.html">stringprep::data</a></td>
<td class="#doctools_tocright">stringprep data tables, generated, internal</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_disjointset'><a href="files/modules/struct/disjointset.html">struct::disjointset</a></td>
<td class="#doctools_tocright">Disjoint set data structure</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_graph'><a href="files/modules/struct/graph.html">struct::graph</a></td>
<td class="#doctools_tocright">Create and manipulate directed graph objects</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_graph_op'><a href="files/modules/struct/graphops.html">struct::graph::op</a></td>
<td class="#doctools_tocright">Operation for (un)directed graph objects</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_graph_v1'><a href="files/modules/struct/graph1.html">struct::graph_v1</a></td>
<td class="#doctools_tocright">Create and manipulate directed graph objects</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_list'><a href="files/modules/struct/struct_list.html">struct::list</a></td>
<td class="#doctools_tocright">Procedures for manipulating lists</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_matrix'><a href="files/modules/struct/matrix.html">struct::matrix</a></td>
<td class="#doctools_tocright">Create and manipulate matrix objects</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_matrix_v1'><a href="files/modules/struct/matrix1.html">struct::matrix_v1</a></td>
<td class="#doctools_tocright">Create and manipulate matrix objects</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_pool'><a href="files/modules/struct/pool.html">struct::pool</a></td>
<td class="#doctools_tocright">Create and manipulate pool objects (of discrete items)</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_prioqueue'><a href="files/modules/struct/prioqueue.html">struct::prioqueue</a></td>
<td class="#doctools_tocright">Create and manipulate prioqueue objects</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_queue'><a href="files/modules/struct/queue.html">struct::queue</a></td>
<td class="#doctools_tocright">Create and manipulate queue objects</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_record'><a href="files/modules/struct/record.html">struct::record</a></td>
<td class="#doctools_tocright">Define and create records (similar to 'C' structures)</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_set'><a href="files/modules/struct/struct_set.html">struct::set</a></td>
<td class="#doctools_tocright">Procedures for manipulating sets</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_skiplist'><a href="files/modules/struct/skiplist.html">struct::skiplist</a></td>
<td class="#doctools_tocright">Create and manipulate skiplists</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_stack'><a href="files/modules/struct/stack.html">struct::stack</a></td>
<td class="#doctools_tocright">Create and manipulate stack objects</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_tree'><a href="files/modules/struct/struct_tree.html">struct::tree</a></td>
<td class="#doctools_tocright">Create and manipulate tree objects</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_tree_v1'><a href="files/modules/struct/struct_tree1.html">struct::tree_v1</a></td>
<td class="#doctools_tocright">Create and manipulate tree objects</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='sum'><a href="files/modules/crc/sum.html">sum</a></td>
<td class="#doctools_tocright">Calculate a sum(1) compatible checksum</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='switched'><a href="files/modules/stooop/switched.html">switched</a></td>
<td class="#doctools_tocright">switch/option management.</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tar'><a href="files/modules/tar/tar.html">tar</a></td>
<td class="#doctools_tocright">Tar file creation, extraction &amp; manipulation</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_cat'><a href="files/modules/virtchannel_base/cat.html">tcl::chan::cat</a></td>
<td class="#doctools_tocright">Concatenation channel</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_core'><a href="files/modules/virtchannel_core/core.html">tcl::chan::core</a></td>
<td class="#doctools_tocright">Basic reflected/virtual channel support</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_events'><a href="files/modules/virtchannel_core/events.html">tcl::chan::events</a></td>
<td class="#doctools_tocright">Event support for reflected/virtual channels</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_facade'><a href="files/modules/virtchannel_base/facade.html">tcl::chan::facade</a></td>
<td class="#doctools_tocright">Facade channel</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_fifo'><a href="files/modules/virtchannel_base/tcllib_fifo.html">tcl::chan::fifo</a></td>
<td class="#doctools_tocright">In-memory fifo channel</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_fifo2'><a href="files/modules/virtchannel_base/tcllib_fifo2.html">tcl::chan::fifo2</a></td>
<td class="#doctools_tocright">In-memory interconnected fifo channels</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_halfpipe'><a href="files/modules/virtchannel_base/halfpipe.html">tcl::chan::halfpipe</a></td>
<td class="#doctools_tocright">In-memory channel, half of a fifo2</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_memchan'><a href="files/modules/virtchannel_base/tcllib_memchan.html">tcl::chan::memchan</a></td>
<td class="#doctools_tocright">In-memory channel</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_null'><a href="files/modules/virtchannel_base/tcllib_null.html">tcl::chan::null</a></td>
<td class="#doctools_tocright">Null channel</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_nullzero'><a href="files/modules/virtchannel_base/nullzero.html">tcl::chan::nullzero</a></td>
<td class="#doctools_tocright">Null/Zero channel combination</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_random'><a href="files/modules/virtchannel_base/tcllib_random.html">tcl::chan::random</a></td>
<td class="#doctools_tocright">Random channel</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_std'><a href="files/modules/virtchannel_base/std.html">tcl::chan::std</a></td>
<td class="#doctools_tocright">Standard I/O, unification of stdin and stdout</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_string'><a href="files/modules/virtchannel_base/tcllib_string.html">tcl::chan::string</a></td>
<td class="#doctools_tocright">Read-only in-memory channel</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_textwindow'><a href="files/modules/virtchannel_base/textwindow.html">tcl::chan::textwindow</a></td>
<td class="#doctools_tocright">Textwindow channel</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_variable'><a href="files/modules/virtchannel_base/tcllib_variable.html">tcl::chan::variable</a></td>
<td class="#doctools_tocright">In-memory channel using variable for storage</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_zero'><a href="files/modules/virtchannel_base/tcllib_zero.html">tcl::chan::zero</a></td>
<td class="#doctools_tocright">Zero channel</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_randomseed'><a href="files/modules/virtchannel_base/randseed.html">tcl::randomseed</a></td>
<td class="#doctools_tocright">Utilities for random channels</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_transform_adler32'><a href="files/modules/virtchannel_transform/adler32.html">tcl::transform::adler32</a></td>
<td class="#doctools_tocright">Adler32 transformation</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_transform_base64'><a href="files/modules/virtchannel_transform/vt_base64.html">tcl::transform::base64</a></td>
<td class="#doctools_tocright">Base64 encoding transformation</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_transform_core'><a href="files/modules/virtchannel_core/transformcore.html">tcl::transform::core</a></td>
<td class="#doctools_tocright">Basic reflected/virtual channel transform support</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_transform_counter'><a href="files/modules/virtchannel_transform/vt_counter.html">tcl::transform::counter</a></td>
<td class="#doctools_tocright">Counter transformation</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_transform_crc32'><a href="files/modules/virtchannel_transform/vt_crc32.html">tcl::transform::crc32</a></td>
<td class="#doctools_tocright">Crc32 transformation</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_transform_hex'><a href="files/modules/virtchannel_transform/hex.html">tcl::transform::hex</a></td>
<td class="#doctools_tocright">Hexadecimal encoding transformation</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_transform_identity'><a href="files/modules/virtchannel_transform/identity.html">tcl::transform::identity</a></td>
<td class="#doctools_tocright">Identity transformation</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_transform_limitsize'><a href="files/modules/virtchannel_transform/limitsize.html">tcl::transform::limitsize</a></td>
<td class="#doctools_tocright">limiting input</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_transform_observe'><a href="files/modules/virtchannel_transform/observe.html">tcl::transform::observe</a></td>
<td class="#doctools_tocright">Observer transformation, stream copy</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_transform_otp'><a href="files/modules/virtchannel_transform/vt_otp.html">tcl::transform::otp</a></td>
<td class="#doctools_tocright">Encryption via one-time pad</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_transform_rot'><a href="files/modules/virtchannel_transform/rot.html">tcl::transform::rot</a></td>
<td class="#doctools_tocright">rot-encryption</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_transform_spacer'><a href="files/modules/virtchannel_transform/spacer.html">tcl::transform::spacer</a></td>
<td class="#doctools_tocright">Space insertation and removal</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_transform_zlib'><a href="files/modules/virtchannel_transform/tcllib_zlib.html">tcl::transform::zlib</a></td>
<td class="#doctools_tocright">zlib (de)compression</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcldes'><a href="files/modules/des/tcldes.html">tclDES</a></td>
<td class="#doctools_tocright">Implementation of the DES and triple-DES ciphers</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcldesjr'><a href="files/modules/des/tcldesjr.html">tclDESjr</a></td>
<td class="#doctools_tocright">Implementation of the DES and triple-DES ciphers</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcldocstrip'><a href="files/apps/tcldocstrip.html">tcldocstrip</a></td>
<td class="#doctools_tocright">Tcl-based Docstrip Processor</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcllib_ip'><a href="files/modules/dns/tcllib_ip.html">tcllib_ip</a></td>
<td class="#doctools_tocright">IPv4 and IPv6 address manipulation</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tclrep_machineparameters'><a href="files/modules/math/machineparameters.html">tclrep/machineparameters</a></td>
<td class="#doctools_tocright">Compute double precision machine parameters.</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tepam'><a href="files/modules/tepam/tepam_introduction.html">tepam</a></td>
<td class="#doctools_tocright">An introduction into TEPAM, Tcl's Enhanced Procedure and Argument Manager</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tepam_argument_dialogbox'><a href="files/modules/tepam/tepam_argument_dialogbox.html">tepam::argument_dialogbox</a></td>
<td class="#doctools_tocright">TEPAM argument_dialogbox, reference manual</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tepam_doc_gen'><a href="files/modules/tepam/tepam_doc_gen.html">tepam::doc_gen</a></td>
<td class="#doctools_tocright">TEPAM DOC Generation, reference manual</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tepam_procedure'><a href="files/modules/tepam/tepam_procedure.html">tepam::procedure</a></td>
<td class="#doctools_tocright">TEPAM procedure, reference manual</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='term'><a href="files/modules/term/term.html">term</a></td>
<td class="#doctools_tocright">General terminal control</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='term_ansi_code'><a href="files/modules/term/ansi_code.html">term::ansi::code</a></td>
<td class="#doctools_tocright">Helper for control sequences</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='term_ansi_code_attr'><a href="files/modules/term/ansi_cattr.html">term::ansi::code::attr</a></td>
<td class="#doctools_tocright">ANSI attribute sequences</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='term_ansi_code_ctrl'><a href="files/modules/term/ansi_cctrl.html">term::ansi::code::ctrl</a></td>
<td class="#doctools_tocright">ANSI control sequences</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='term_ansi_code_macros'><a href="files/modules/term/ansi_cmacros.html">term::ansi::code::macros</a></td>
<td class="#doctools_tocright">Macro sequences</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='term_ansi_ctrl_unix'><a href="files/modules/term/ansi_ctrlu.html">term::ansi::ctrl::unix</a></td>
<td class="#doctools_tocright">Control operations and queries</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='term_ansi_send'><a href="files/modules/term/ansi_send.html">term::ansi::send</a></td>
<td class="#doctools_tocright">Output of ANSI control sequences to terminals</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='term_interact_menu'><a href="files/modules/term/imenu.html">term::interact::menu</a></td>
<td class="#doctools_tocright">Terminal widget, menu</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='term_interact_pager'><a href="files/modules/term/ipager.html">term::interact::pager</a></td>
<td class="#doctools_tocright">Terminal widget, paging</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='term_receive'><a href="files/modules/term/receive.html">term::receive</a></td>
<td class="#doctools_tocright">General input from terminals</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='term_receive_bind'><a href="files/modules/term/term_bind.html">term::receive::bind</a></td>
<td class="#doctools_tocright">Keyboard dispatch from terminals</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='term_send'><a href="files/modules/term/term_send.html">term::send</a></td>
<td class="#doctools_tocright">General output to terminals</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='textutil'><a href="files/modules/textutil/textutil.html">textutil</a></td>
<td class="#doctools_tocright">Procedures to manipulate texts and strings.</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='textutil_adjust'><a href="files/modules/textutil/adjust.html">textutil::adjust</a></td>
<td class="#doctools_tocright">Procedures to adjust, indent, and undent paragraphs</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='textutil_expander'><a href="files/modules/textutil/expander.html">textutil::expander</a></td>
<td class="#doctools_tocright">Procedures to process templates and expand text.</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='textutil_repeat'><a href="files/modules/textutil/repeat.html">textutil::repeat</a></td>
<td class="#doctools_tocright">Procedures to repeat strings.</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='textutil_split'><a href="files/modules/textutil/textutil_split.html">textutil::split</a></td>
<td class="#doctools_tocright">Procedures to split texts</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='textutil_string'><a href="files/modules/textutil/textutil_string.html">textutil::string</a></td>
<td class="#doctools_tocright">Procedures to manipulate texts and strings.</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='textutil_tabify'><a href="files/modules/textutil/tabify.html">textutil::tabify</a></td>
<td class="#doctools_tocright">Procedures to (un)tabify strings</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='textutil_trim'><a href="files/modules/textutil/trim.html">textutil::trim</a></td>
<td class="#doctools_tocright">Procedures to trim strings</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='throw'><a href="files/modules/try/tcllib_throw.html">throw</a></td>
<td class="#doctools_tocright">throw - Throw an error exception with a message</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tie'><a href="files/modules/tie/tie_std.html">tie</a></td>
<td class="#doctools_tocright">Array persistence, standard data sources</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tie'><a href="files/modules/tie/tie.html">tie</a></td>
<td class="#doctools_tocright">Array persistence</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tiff'><a href="files/modules/tiff/tiff.html">tiff</a></td>
<td class="#doctools_tocright">TIFF reading, writing, and querying and manipulation of meta data</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tool'><a href="files/modules/httpd/httpd.html">tool</a></td>
<td class="#doctools_tocright">A TclOO and coroutine based web server</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tool'><a href="files/modules/tool/tool.html">tool</a></td>
<td class="#doctools_tocright">TclOO Library (TOOL) Framework</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tool_dict_ensemble'><a href="files/modules/tool/tool_dict_ensemble.html">tool::dict_ensemble</a></td>
<td class="#doctools_tocright">Dictionary Tools</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='transfer_connect'><a href="files/modules/transfer/connect.html">transfer::connect</a></td>
<td class="#doctools_tocright">Connection setup</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='transfer_copy'><a href="files/modules/transfer/copyops.html">transfer::copy</a></td>
<td class="#doctools_tocright">Data transfer foundation</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='transfer_copy_queue'><a href="files/modules/transfer/tqueue.html">transfer::copy::queue</a></td>
<td class="#doctools_tocright">Queued transfers</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='transfer_data_destination'><a href="files/modules/transfer/ddest.html">transfer::data::destination</a></td>
<td class="#doctools_tocright">Data destination</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='transfer_data_source'><a href="files/modules/transfer/dsource.html">transfer::data::source</a></td>
<td class="#doctools_tocright">Data source</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='transfer_receiver'><a href="files/modules/transfer/receiver.html">transfer::receiver</a></td>
<td class="#doctools_tocright">Data source</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='transfer_transmitter'><a href="files/modules/transfer/transmitter.html">transfer::transmitter</a></td>
<td class="#doctools_tocright">Data source</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='treeql'><a href="files/modules/treeql/treeql.html">treeql</a></td>
<td class="#doctools_tocright">Query tree objects</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='try'><a href="files/modules/try/tcllib_try.html">try</a></td>
<td class="#doctools_tocright">try - Trap and process errors and exceptions</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='udpcluster'><a href="files/modules/udpcluster/udpcluster.html">udpcluster</a></td>
<td class="#doctools_tocright">UDP Peer-to-Peer cluster</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='uevent'><a href="files/modules/uev/uevent.html">uevent</a></td>
<td class="#doctools_tocright">User events</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='uevent_onidle'><a href="files/modules/uev/uevent_onidle.html">uevent::onidle</a></td>
<td class="#doctools_tocright">Request merging and deferal to idle time</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='unicode'><a href="files/modules/stringprep/unicode.html">unicode</a></td>
<td class="#doctools_tocright">Implementation of Unicode normalization</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='unicode_data'><a href="files/modules/stringprep/unicode_data.html">unicode::data</a></td>
<td class="#doctools_tocright">unicode data tables, generated, internal</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='units'><a href="files/modules/units/units.html">units</a></td>
<td class="#doctools_tocright">unit conversion</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='uri'><a href="files/modules/uri/uri.html">uri</a></td>
<td class="#doctools_tocright">URI utilities</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='uri_urn'><a href="files/modules/uri/urn-scheme.html">uri_urn</a></td>
<td class="#doctools_tocright">URI utilities, URN scheme</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='uuencode'><a href="files/modules/base64/uuencode.html">uuencode</a></td>
<td class="#doctools_tocright">UU-encode/decode binary data</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='uuid'><a href="files/modules/uuid/uuid.html">uuid</a></td>
<td class="#doctools_tocright">UUID generation and comparison</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='valtype_common'><a href="files/modules/valtype/valtype_common.html">valtype::common</a></td>
<td class="#doctools_tocright">Validation, common code</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='valtype_creditcard_amex'><a href="files/modules/valtype/cc_amex.html">valtype::creditcard::amex</a></td>
<td class="#doctools_tocright">Validation for AMEX creditcard number</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='valtype_creditcard_discover'><a href="files/modules/valtype/cc_discover.html">valtype::creditcard::discover</a></td>
<td class="#doctools_tocright">Validation for Discover creditcard number</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='valtype_creditcard_mastercard'><a href="files/modules/valtype/cc_mastercard.html">valtype::creditcard::mastercard</a></td>
<td class="#doctools_tocright">Validation for Mastercard creditcard number</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='valtype_creditcard_visa'><a href="files/modules/valtype/cc_visa.html">valtype::creditcard::visa</a></td>
<td class="#doctools_tocright">Validation for VISA creditcard number</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='valtype_gs1_ean13'><a href="files/modules/valtype/ean13.html">valtype::gs1::ean13</a></td>
<td class="#doctools_tocright">Validation for EAN13</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='valtype_iban'><a href="files/modules/valtype/iban.html">valtype::iban</a></td>
<td class="#doctools_tocright">Validation for IBAN</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='valtype_imei'><a href="files/modules/valtype/imei.html">valtype::imei</a></td>
<td class="#doctools_tocright">Validation for IMEI</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='valtype_isbn'><a href="files/modules/valtype/isbn.html">valtype::isbn</a></td>
<td class="#doctools_tocright">Validation for ISBN</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='valtype_luhn'><a href="files/modules/valtype/luhn.html">valtype::luhn</a></td>
<td class="#doctools_tocright">Validation for plain number with a LUHN checkdigit</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='valtype_luhn5'><a href="files/modules/valtype/luhn5.html">valtype::luhn5</a></td>
<td class="#doctools_tocright">Validation for plain number with a LUHN5 checkdigit</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='valtype_usnpi'><a href="files/modules/valtype/usnpi.html">valtype::usnpi</a></td>
<td class="#doctools_tocright">Validation for USNPI</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='valtype_verhoeff'><a href="files/modules/valtype/verhoeff.html">valtype::verhoeff</a></td>
<td class="#doctools_tocright">Validation for plain number with a VERHOEFF checkdigit</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='websocket'><a href="files/modules/websocket/websocket.html">websocket</a></td>
<td class="#doctools_tocright">Tcl implementation of the websocket protocol</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='wip'><a href="files/modules/wip/wip.html">wip</a></td>
<td class="#doctools_tocright">Word Interpreter</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='xsxp'><a href="files/modules/amazon-s3/xsxp.html">xsxp</a></td>
<td class="#doctools_tocright">eXtremely Simple Xml Parser</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='yaml'><a href="files/modules/yaml/yaml.html">yaml</a></td>
<td class="#doctools_tocright">YAML Format Encoder/Decoder</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='yencode'><a href="files/modules/base64/yencode.html">yencode</a></td>
<td class="#doctools_tocright">Y-encode/decode binary data</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='zipfile_decode'><a href="files/modules/zip/decode.html">zipfile::decode</a></td>
<td class="#doctools_tocright">Access to zip archives</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='zipfile_encode'><a href="files/modules/zip/encode.html">zipfile::encode</a></td>
<td class="#doctools_tocright">Generation of zip archives</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='zipfile_mkzip'><a href="files/modules/zip/mkzip.html">zipfile::mkzip</a></td>
<td class="#doctools_tocright">Build a zip archive</td>
</tr>
</table>
</dl><hr>

Changes to embedded/www/toc.html.

893
894
895
896
897
898
899




900
901
902
903

904
905
906
907

908
909
910
911
912
913
914
893
894
895
896
897
898
899
900
901
902
903
904
905
906

907
908
909
910

911
912
913
914
915
916
917
918







+
+
+
+



-
+



-
+







<td class="#doctools_tocright">Special mathematical functions</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='math_statistics'><a href="tcllib/files/modules/math/statistics.html">math::statistics</a></td>
<td class="#doctools_tocright">Basic statistical functions and procedures</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='math_trig'><a href="tcllib/files/modules/math/trig.html">math::trig</a></td>
<td class="#doctools_tocright">Trigonometric anf hyperbolic functions</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='simulation_annealing'><a href="tcllib/files/modules/simulation/annealing.html">simulation::annealing</a></td>
<td class="#doctools_tocright">Simulated annealing</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='simulation_montecarlo'><a href="tcllib/files/modules/simulation/montecarlo.html">simulation::montecarlo</a></td>
<td class="#doctools_tocright">Monte Carlo simulations</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='simulation_random'><a href="tcllib/files/modules/simulation/simulation_random.html">simulation::random</a></td>
<td class="#doctools_tocright">Pseudo-random number generators</td>
</tr>
</table></dl>
<dl><dt><a name='networking'>Networking<dd>
<table class="#doctools_toc">
<tr class="#doctools_toceven" >
2887
2888
2889
2890
2891
2892
2893




2894
2895
2896
2897
2898
2899
2900
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908







+
+
+
+







<td class="#doctools_tocright">Special mathematical functions</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='math_statistics'><a href="tcllib/files/modules/math/statistics.html">math::statistics</a></td>
<td class="#doctools_tocright">Basic statistical functions and procedures</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='math_trig'><a href="tcllib/files/modules/math/trig.html">math::trig</a></td>
<td class="#doctools_tocright">Trigonometric anf hyperbolic functions</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tclrep_machineparameters'><a href="tcllib/files/modules/math/machineparameters.html">tclrep/machineparameters</a></td>
<td class="#doctools_tocright">Compute double precision machine parameters.</td>
</tr>
</table></dl>
<dl><dt><a name='md4'>md4<dd>
<table class="#doctools_toc">
<tr class="#doctools_toceven" >

Changes to embedded/www/toc0.html.

893
894
895
896
897
898
899




900
901
902
903

904
905
906
907

908
909
910
911
912
913
914
893
894
895
896
897
898
899
900
901
902
903
904
905
906

907
908
909
910

911
912
913
914
915
916
917
918







+
+
+
+



-
+



-
+







<td class="#doctools_tocright">Special mathematical functions</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='math_statistics'><a href="tcllib/files/modules/math/statistics.html">math::statistics</a></td>
<td class="#doctools_tocright">Basic statistical functions and procedures</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='math_trig'><a href="tcllib/files/modules/math/trig.html">math::trig</a></td>
<td class="#doctools_tocright">Trigonometric anf hyperbolic functions</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='simulation_annealing'><a href="tcllib/files/modules/simulation/annealing.html">simulation::annealing</a></td>
<td class="#doctools_tocright">Simulated annealing</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='simulation_montecarlo'><a href="tcllib/files/modules/simulation/montecarlo.html">simulation::montecarlo</a></td>
<td class="#doctools_tocright">Monte Carlo simulations</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='simulation_random'><a href="tcllib/files/modules/simulation/simulation_random.html">simulation::random</a></td>
<td class="#doctools_tocright">Pseudo-random number generators</td>
</tr>
</table></dl>
<dl><dt><a name='networking'>Networking<dd>
<table class="#doctools_toc">
<tr class="#doctools_toceven" >

Changes to embedded/www/toc1.html.

1062
1063
1064
1065
1066
1067
1068




1069
1070
1071
1072
1073
1074
1075
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079







+
+
+
+







<td class="#doctools_tocright">Special mathematical functions</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='math_statistics'><a href="tcllib/files/modules/math/statistics.html">math::statistics</a></td>
<td class="#doctools_tocright">Basic statistical functions and procedures</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='math_trig'><a href="tcllib/files/modules/math/trig.html">math::trig</a></td>
<td class="#doctools_tocright">Trigonometric anf hyperbolic functions</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tclrep_machineparameters'><a href="tcllib/files/modules/math/machineparameters.html">tclrep/machineparameters</a></td>
<td class="#doctools_tocright">Compute double precision machine parameters.</td>
</tr>
</table></dl>
<dl><dt><a name='md4'>md4<dd>
<table class="#doctools_toc">
<tr class="#doctools_toceven" >

Added examples/httpd/htdocs/html_static_page.html.






1
2
3
4
5
+
+
+
+
+
<html><header><title>Static Content</title></header>
<body>
<h1>Static Content</h1>
This page is static content embedded in the file system. Nothing fancy.
</body></html>

Changes to examples/httpd/htdocs/index.md.

1
2
3
4
5
6

7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
14






+







Your test server works!

* [Tcllib embedded docs](/tcllib/index.html)
* [Tcllib's fossil repo (hosted via SCGI)](/fossil)
* [Standard Markdown Example Page](example.md)
* [Static HTML Page](html_static_page.html)
* [Template HTML Page](header.tml)

A locally served image:
![Locally Served Image](/tcllib/image/arch_core_container.png "Core Container")

Internal documentation for httpd:

* [Operating Principals](operations.md)

Changes to examples/httpd/httpd.tcl.

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

49
50
51
52
53
54


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75


76
77
78
79
80
81
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96
97

98
99
100
101
102


103
104
105
106
107
108
109
23
24
25
26
27
28
29



















30
31
32
33
34


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55


56
57
58
59
60
61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
78

79
80
81
82


83
84
85
86
87
88
89
90
91







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+




-
-
+
+



















-
-
+
+












-
+








-
+



-
-
+
+







  }
  if {[llength $args]==0} {
    return $::fossil_exe
  }
  return [exec ${::fossil_exe} {*}$args]
}

tool::define httpd::content.fossil_root {

  method content {} {
    my puts "<HTML><HEAD><TITLE>Local Fossil Repositories</TITLE></HEAD><BODY>"
    global recipe
    my puts "<UL>"
    set dbfiles [::fossil-list]
    foreach file [lsort -dictionary $dbfiles]  {
      dict set result [file rootname [file tail $file]] $file
    }
    foreach {module dbfile} [lsort -dictionary -stride 2 $result] {
      my puts "<li><a HREF=/fossil/$module>$module</a>"
    }
    my puts {</UL></BODY></HTML>}
  }
}


tool::define httpd::content.fossil_node_proxy {
clay::define httpd::content.fossil_node_proxy {

  superclass httpd::content.proxy

  method FileName {} {
    set uri    [my http_info get REQUEST_URI]
    set prefix [my http_info get prefix]
    set uri    [my request get REQUEST_URI]
    set prefix [my clay get prefix]
    set module [lindex [split $uri /] 2]
    if {![info exists ::fossil_process($module)]} {
      set dbfiles [::fossil-list]
      foreach file [lsort -dictionary $dbfiles]  {
        dict set result [file rootname [file tail $file]] $file
      }
      if {![dict exists $result $module]} {
        return {}
      }
      set dbfile [dict get $result $module]
      if {![file exists $dbfile]} {
        return {}
      }
      set ::fossil_process($module) $dbfile
    }
    return [list $module $::fossil_process($module)]
  }

  method proxy_path {} {
    set uri [string trimleft [my http_info get REQUEST_URI] /]
    set prefix [my http_info get prefix]
    set uri [string trimleft [my request get REQUEST_URI] /]
    set prefix [my clay get prefix]
    set module [lindex [split $uri /] 1]
    set path /[string range $uri [string length $prefix/$module] end]
    return $path
  }

  method proxy_channel {} {
    ###
    # This method returns a channel to the
    # proxied socket/stdout/etc
    ###
    lassign [my FileName] module dbfile
    set EXE [my Cgi_Executable fossil]
    set baseurl http://[my http_info get HTTP_HOST][my http_info get prefix]/$module
    set baseurl http://[my request get HTTP_HOST][my clay get prefix]/$module
    if { $::tcl_platform(platform) eq "windows"} {
      return [open "|fossil.exe http $dbfile -baseurl $baseurl" r+]
    } else {
      return [open "|fossil http $dbfile -baseurl $baseurl 2>@1" r+]
    }
  }
}

tool::define httpd::content.fossil_node_scgi {
clay::define httpd::content.fossil_node_scgi {

  superclass httpd::content.scgi
  method scgi_info {} {
    set uri    [my http_info get REQUEST_URI]
    set prefix [my http_info get prefix]
    set uri    [my request get REQUEST_URI]
    set prefix [my clay get prefix]
    set module [lindex [split $uri /] 2]
    file mkdir ~/tmp
    if {![info exists ::fossil_process($module)]} {
      package require processman
      package require nettool
      set port [::nettool::allocate_port 40000]
      set handle fossil:$port
129
130
131
132
133
134
135
136

137
138
139
140


141
142
143
144
145
146
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162


163
164
165
166
167
168
169
170
171
172
173

174
175
176
177

178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193

194
195
196


197
198
199
200
201
202
203
204

205
206
207
208
209

210
211
212
213
214
215
216
217
















218
219
220





































221
222

111
112
113
114
115
116
117

118
119
120


121
122
123






124















125
126
127










128




129
















130



131
132
133
134
135





136





137
138
139
140
141




142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

196







-
+


-
-
+
+

-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+

-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
+
+



-
-
-
-
-
+
-
-
-
-
-
+




-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
      my varname paused
      after 500
    }
    return [list localhost $port $SCRIPT_NAME]
  }
}

tool::class create ::docserver::server {
::clay::define ::docserver::server {
  superclass ::httpd::server

  method log args {
    #puts [list {*}$args]
  method debug args {
    #puts [list DEBUG {*}$args]
  }

}

tool::define ::docserver::dynamic {

  method content {} {
  method log args {
    my puts "<HTML><HEAD><TITLE>IRM Dispatch Server</TITLE></HEAD><BODY>"
    my puts "<TABLE width=100%>"
    foreach {f v} [my request dump] {
        my puts "<tr><th>$f</th><td>$v</td></tr>"
    }
    my puts "<tr><td colspan=10><hr></td></tr>"
    foreach {f v} [my http_info dump] {
        my puts "<tr><th>$f</th><td>$v</td></tr>"
    }
    my puts "<tr><th>File Size</th><td>[my http_info get CONTENT_LENGTH]</td></tr>"
    my puts </TABLE>
    my puts </BODY></HTML>
  }

}
    #puts [list LOG {*}$args]
  }

tool::define ::docserver::upload {
  superclass ::docserver::dynamic

  method content {} {
    my puts "<HTML><HEAD><TITLE>IRM Dispatch Server</TITLE></HEAD><BODY>"
    my puts "<TABLE width=100%>"
    set FORMDAT [my FormData]
    foreach {f v} [my FormData] {
        my puts "<tr><th>$f</th><td>$v</td></tr>"
    }
}
    my puts "<tr><td colspan=10><hr></td></tr>"
    foreach {f v} [my http_info dump] {
        my puts "<tr><th>$f</th><td>$v</td></tr>"
    }

    my puts "<tr><td colspan=10><hr></td></tr>"
    foreach part [dict getnull $FORMDAT MIME_PARTS] {
      my puts "<tr><td colspan=10><hr></td></tr>"
      foreach f [::mime::getheader $part -names] {
        my puts "<tr><th>$f</th><td>[mime::getheader $part $f]</td></tr>"
      }
      my puts "<tr><td colspan=10>[::mime::getbody $part -decode]</td></tr>"
    }
    my puts "<tr><th>File Size</th><td>[my http_info get CONTENT_LENGTH]</td></tr>"
    my puts </TABLE>
    my puts </BODY></HTML>
  }
}

set opts [::tool::args_to_options {*}$argv]
set serveropts {}
set serveropts [::httpd::server clay get server/]
set optinfo [::docserver::server meta getnull option]
foreach {f v} $opts {
  if {[dict exists $optinfo $f]} {
foreach {f v}  [::clay::args_to_options {*}$::argv] {
  if {[dict exists $serveropts $f]} {
    dict set serveropts $f $v
  }
}
puts $serveropts
set fossilopts {}
set optinfo [::httpd::content.fossil_root meta getnull option]
foreach {f v} $opts {
  if {[dict exists $optinfo $f]} {
if {[dict exists $serveropts fossil]} {
    dict set fossilopts $f $v
  }
}
if {[dict exists $opts fossil]} {
  set ::fossil_exe [dict get $opts fossil]
  set ::fossil_exe [dict get $serveropts fossil]
}

::docserver::server create appmain doc_root $DEMOROOT {*}$argv
appmain plugin basic_url ::httpd::plugin.dict_dispatch
appmain uri add /tcllib* [list mixin httpd::content.file path [file join $tcllibroot embedded www]]
appmain uri add /fossil [list mixin httpd::content.fossil_root {*}$fossilopts]
appmain uri add /fossil/* [list mixin httpd::content.fossil_node_proxy {*}$fossilopts]
appmain uri add /upload [list mixin ::docserver::upload]
appmain uri add * /tcllib* [list mixin {reply httpd::content.file} path [file join $tcllibroot embedded www]]
appmain uri direct * /fossil {} {
  my puts "<HTML><HEAD><TITLE>Local Fossil Repositories</TITLE></HEAD><BODY>"
  global recipe
  my puts "<UL>"
  set dbfiles [::fossil-list]
  foreach file [lsort -dictionary $dbfiles]  {
    dict set result [file rootname [file tail $file]] $file
  }
  foreach {module dbfile} [lsort -dictionary -stride 2 $result] {
    my puts "<li><a HREF=/fossil/$module>$module</a>"
  }
  my puts {</UL></BODY></HTML>}
}
appmain uri add * /fossil/* [list mixin {reply httpd::content.fossil_node_proxy}]
appmain uri direct * /upload {} {
appmain uri add /dynamic [list mixin ::docserver::dynamic]
appmain uri add /listen [list mixin ::docserver::listen]
appmain uri add /send   [list mixin ::docserver::send]
  my puts "<HTML><HEAD><TITLE>IRM Dispatch Server</TITLE></HEAD><BODY>"
  my puts "<TABLE width=100%>"
  set FORMDAT [my FormData]
  foreach {f v} [my FormData] {
      my puts "<tr><th>$f</th><td>$v</td></tr>"
  }
  my puts "<tr><td colspan=10><hr></td></tr>"
  foreach {f v} [my clay dump] {
      my puts "<tr><th>$f</th><td>$v</td></tr>"
  }
  my puts "<tr><td colspan=10><hr></td></tr>"
  foreach part [dict getnull $FORMDAT MIME_PARTS] {
    my puts "<tr><td colspan=10><hr></td></tr>"
    foreach f [::mime::getheader $part -names] {
      my puts "<tr><th>$f</th><td>[mime::getheader $part $f]</td></tr>"
    }
    my puts "<tr><td colspan=10>[::mime::getbody $part -decode]</td></tr>"
  }
  my puts "<tr><th>File Size</th><td>[my request get CONTENT_LENGTH]</td></tr>"
  my puts </TABLE>
  my puts </BODY></HTML>
}
appmain uri direct * /dynamic {} {
  my puts "<HTML><HEAD><TITLE>IRM Dispatch Server</TITLE></HEAD><BODY>"
  my puts "<TABLE width=100%>"
  foreach {f v} [my request dump] {
    my puts "<tr><th>$f</th><td>$v</td></tr>"
  }
  my puts "<tr><td colspan=10><hr></td></tr>"
  foreach {f v} [my clay dump] {
    my puts "<tr><th>$f</th><td>$v</td></tr>"
  }
  my puts "<tr><th>File Size</th><td>[my request get CONTENT_LENGTH]</td></tr>"
  my puts </TABLE>
  my puts </BODY></HTML>
}

puts [list LISTENING on [appmain port_listening]]
tool::main
cron::main

Changes to idoc/man/files/modules/cron/cron.n.

296
297
298
299
300
301
302
303

304
305

306
307
308
309
310
311
312
296
297
298
299
300
301
302

303
304

305
306
307
308
309
310
311
312







-
+

-
+







.sp
\fB::cron::task info\fR \fIprocess\fR
.sp
\fB::cron::task set\fR \fIprocess\fR \fIfield\fR \fIvalue\fR \fI?field\&.\&.\&.?\fR \fI?value\&.\&.\&.?\fR
.sp
\fB::cron::wake\fR \fI?who?\fR
.sp
\fB::cron::clock_step\fR \fImilleseconds\fR
\fB::cron::clock_step\fR \fImilliseconds\fR
.sp
\fB::cron::clock_delay\fR \fImilleseconds\fR
\fB::cron::clock_delay\fR \fImilliseconds\fR
.sp
\fB::cron::clock_sleep\fR \fIseconds\fR \fI?offset?\fR
.sp
\fB::cron::clock_set\fR \fInewtime\fR
.sp
.BE
.SH DESCRIPTION
419
420
421
422
423
424
425

426
427
428

429
430

431
432
433
434



435
436

437
438

439

440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458

459
460
461

462
463

464
465
466

467
468
469
470
471
472
473
474
475
476
477
478
479

480
481
482
483
484
485
486
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435


436
437
438
439
440
441
442

443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463

464
465
466

467
468

469
470
471

472
473
474
475
476
477
478
479
480
481
482
483
484

485
486
487
488
489
490
491
492







+



+


+


-
-
+
+
+


+

-
+

+


















-
+


-
+

-
+


-
+












-
+







\fB::cron::task info\fR \fIprocess\fR
Returns a dict describing \fIprocess\fR\&. See \fB::cron::task set\fR for a description of the options\&.
.TP
\fB::cron::task set\fR \fIprocess\fR \fIfield\fR \fIvalue\fR \fI?field\&.\&.\&.?\fR \fI?value\&.\&.\&.?\fR
.sp
If \fIprocess\fR does not exist, it is created\&. Options Include:
.RS
.TP
\fBcommand\fR
If \fBcoroutine\fR is black, a global command which implements this process\&. If \fBcoroutine\fR is not
black, the command to invoke to create or recreate the coroutine\&.
.TP
\fBcoroutine\fR
The name of the coroutine (if any) which implements this process\&.
.TP
\fBfrequency\fR
If -1, this process is terminated after the next event\&. If 0 this process should be called during every
idle event\&. If positive, this process should generate events periodically\&. The frequency is an interger number
of milleseconds between events\&.
idle event\&. If positive, this process should generate events periodically\&. The frequency is an integer number
of milliseconds between events\&.
.TP
\fBobject\fR
The object associated with this process or coroutine\&.
.TP
\fBscheduled\fR
If non-zero, the absolute time from the epoch (in milleseconds) that this process will trigger an event\&.
If non-zero, the absolute time from the epoch (in milliseconds) that this process will trigger an event\&.
If zero, and the \fBfrequency\fR is also zero, this process is called every idle loop\&.
.TP
\fBrunning\fR
A boolean flag\&. If true it indicates the process never returned or yielded during the event loop,
and will not be called again until it does so\&.
.RE
.TP
\fB::cron::wake\fR \fI?who?\fR
Wake up cron, and arrange for its event loop to be run during the next Idle cycle\&.
.CS


::cron::wake {I just did something important}

.CE
.PP
.PP
Several utility commands are provided that are used internally within cron and for
testing cron, but may or may not be useful in the general cases\&.
.TP
\fB::cron::clock_step\fR \fImilleseconds\fR
\fB::cron::clock_step\fR \fImilliseconds\fR
.sp
Return a clock time absolute to the epoch which falls on the next
border between one second and the next for the value of \fImilleseconds\fR
border between one second and the next for the value of \fImilliseconds\fR
.TP
\fB::cron::clock_delay\fR \fImilleseconds\fR
\fB::cron::clock_delay\fR \fImilliseconds\fR
.sp
Return a clock time absolute to the epoch which falls on the next
border between one second and the next \fImilleseconds\fR in the future\&.
border between one second and the next \fImilliseconds\fR in the future\&.
.TP
\fB::cron::clock_sleep\fR \fIseconds\fR \fI?offset?\fR
.sp
Return a clock time absolute to the epoch which falls exactly \fIseconds\fR in
the future\&. If offset is given it may be positive or negative, and will shift
the final time to before or after the second would flip\&.
.TP
\fB::cron::clock_set\fR \fInewtime\fR
.sp
Sets the internal clock for cron\&. This command will advance the time in 100ms
increment, triggering events, until the internal time catches up with \fInewtime\fR\&.
.sp
\fInewtime\fR is expressed in absolute milleseconds since the beginning of the epoch\&.
\fInewtime\fR is expressed in absolute milliseconds since the beginning of the epoch\&.
.PP
.PP
.SH "BUGS, IDEAS, FEEDBACK"
This document, and the package it describes, will undoubtedly contain
bugs and other problems\&.
Please report such in the category \fIodie\fR of the
\fITcllib Trackers\fR [http://core\&.tcl\&.tk/tcllib/reportlist]\&.

Changes to idoc/man/files/modules/doctools/cvs.n.

332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
332
333
334
335
336
337
338

339
340
341
342
343
344
345







-







order, separated by commas\&.
.sp
The values are lists of the files the entry is touching\&.
.RE
.sp
.TP
\fB::doctools::cvs::toChangeLog\fR \fIevar\fR \fIcvar\fR \fIfvar\fR
]
The three arguments for this command are the same as the last three
arguments of the command \fB::doctools::cvs::scanLog\fR\&. This command
however expects them to be filled with information about one or more
logs\&. It takes this information and converts it into a text in the
format of a ChangeLog as accepted and generated by \fBemacs\fR\&. The
constructed text is returned as the result of the command\&.
.PP

Changes to idoc/man/files/modules/doctools/doctools_lang_intro.n.

372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
372
373
374
375
376
377
378










379
380
381
382
383
384
385
386
387
388
389
390
391
392










393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411










412
413
414
415
416
417
418







-
-
-
-
-
-
-
-
-
-














-
-
-
-
-
-
-
-
-
-



















-
-
-
-
-
-
-
-
-
-







in the given order\&. Regular text is not allowed within the header\&.
.PP
Given the above a less minimal example of a document is
.CS


[manpage_begin NAME SECTION VERSION]










[\fBcopyright {YEAR AUTHOR}\fR]
[\fBtitledesc TITLE\fR]
[\fBmoddesc   MODULE_TITLE\fR]
[\fBrequire   PACKAGE VERSION\fR]
[\fBrequire   PACKAGE\fR]
[description]
[manpage_end]

.CE
Remember that the whitespace is optional\&. The document
.CS


    [manpage_begin NAME SECTION VERSION]
[see_also doctools_intro]
[see_also doctools_lang_cmdref]
[see_also doctools_lang_faq]
[see_also doctools_lang_syntax]
[keywords {doctools commands}]
[keywords {doctools language}]
[keywords {doctools markup}]
[keywords {doctools syntax}]
[keywords markup]
[keywords {semantic markup}]
    [copyright {YEAR AUTHOR}][titledesc TITLE][moddesc MODULE_TITLE]
    [require PACKAGE VERSION][require PACKAGE][description]
    [vset CATEGORY doctools]
[include \&.\&./doctools2base/include/feedback\&.inc]
[manpage_end]

.CE
has the same meaning as the example before\&.
.PP
On the other hand, if \fIwhitespace\fR is present it consists not
only of any sequence of characters containing the space character,
horizontal and vertical tabs, carriage return, and newline, but it may
contain comment markup as well, in the form of the \fBcomment\fR
command\&.
.CS


[\fBcomment { \&.\&.\&. }\fR]
[manpage_begin NAME SECTION VERSION]










[copyright {YEAR AUTHOR}]
[titledesc TITLE]
[moddesc   MODULE_TITLE][\fBcomment { \&.\&.\&. }\fR]
[require   PACKAGE VERSION]
[require   PACKAGE]
[description]
[manpage_end]
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
430
431
432
433
434
435
436










437
438
439
440
441
442
443
444
445
446
447










448
449
450
451
452
453
454







-
-
-
-
-
-
-
-
-
-











-
-
-
-
-
-
-
-
-
-







is possible to write
.CS


[\fBinclude FILE\fR]
[\fBvset VAR VALUE\fR]
[manpage_begin NAME SECTION VERSION]










[description]
[manpage_end]

.CE
Even more important, these two commands are allowed anywhere where a
markup command is allowed, without regard for any other
structure\&. I\&.e\&. for example in the header as well\&.
.CS


[manpage_begin NAME SECTION VERSION]










[\fBinclude FILE\fR]
[\fBvset VAR VALUE\fR]
[description]
[manpage_end]

.CE
The only restriction \fBinclude\fR has to obey is that the contents of
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
471
472
473
474
475
476
477










478
479
480
481
482
483
484







-
-
-
-
-
-
-
-
-
-







the next\&. The first paragraph is automatically opened at the beginning
of the body, by \fBdescription\fR\&. In the same manner the last
paragraph automatically ends at \fBmanpage_end\fR\&.
.CS


[manpage_begin NAME SECTION VERSION]










[description]
 \&.\&.\&.
[\fBpara\fR]
 \&.\&.\&.
[\fBpara\fR]
 \&.\&.\&.
[manpage_end]
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
497
498
499
500
501
502
503










504
505
506
507
508
509
510







-
-
-
-
-
-
-
-
-
-







.PP
Empty sections are \fInot\fR ignored\&. We are free to (not) use
paragraphs within sections\&.
.CS


[manpage_begin NAME SECTION VERSION]










[description]
 \&.\&.\&.
[\fBsection {Section A}\fR]
 \&.\&.\&.
[para]
 \&.\&.\&.
[\fBsection {Section B}\fR]
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
522
523
524
525
526
527
528










529
530
531
532
533
534
535







-
-
-
-
-
-
-
-
-
-







.PP
Empty subsections are \fInot\fR ignored\&. We are free to (not) use
paragraphs within subsections\&.
.CS


[manpage_begin NAME SECTION VERSION]










[description]
 \&.\&.\&.
[section {Section A}]
 \&.\&.\&.
[\fBsubsection {Sub 1}\fR]
 \&.\&.\&.
[para]
707
708
709
710
711
712
713
714

715
716
717
718
719
720
721
627
628
629
630
631
632
633

634
635
636
637
638
639
640
641







-
+







highlighting added\&.
It shows their use within a block of text, as the arguments of a list
item command (\fBcall\fR), and our ability to nest them\&.
.CS


  \&.\&.\&.
  [call [\fBcmd arg_def\fR] [\fBarg type\fR] [\fBarg name\fR]] [\fBopt\fR [\fBarg mode\fR]]]
  [call [\fBcmd arg_def\fR] [\fBarg type\fR] [\fBarg name\fR] [\fBopt\fR [\fBarg mode\fR]]]

  Text structure\&. List element\&. Argument list\&. Automatically closes the
  previous list element\&. Specifies the data-[\fBarg type\fR] of the described
  argument of a command, its [\fBarg name\fR] and its i/o-[\fBarg mode\fR]\&. The
  latter is optional\&.
  \&.\&.\&.

Changes to idoc/man/files/modules/fumagic/cfront.n.

297
298
299
300
301
302
303
304
305
306





307
308
309
310
311
312
313
297
298
299
300
301
302
303



304
305
306
307
308
309
310
311
312
313
314
315







-
-
-
+
+
+
+
+







This package provides the frontend of a compiler of magic(5) files
into recognizers based on the \fBfileutil::magic::rt\fR recognizer
runtime package\&. For the generator backed used by this compiler see
the package \fBfileutil::magic::cgen\fR\&.
.SH COMMANDS
.TP
\fB::fileutil::magic::cfront::compile\fR \fIpath\fR\&.\&.\&.
This command takes the paths of one or more files and directories and
compiles all the files, and the files in all the directories into a
single recognizer for all the file types specified in these files\&.
This command takes the paths of one or more files and directories and compiles
all the files, and the files in all the directories into a single analyzer for
all the file types specified in these files\&.  It returns a list whose first
item is a list per-file dictionaries of analyzer scripts and whose second item
is a list of analyzer commands\&.
.sp
All the files have to be in the format specified by magic(5)\&.
.sp
The result of the command is a Tcl script containing the generated
recognizer\&.
.TP
\fB::fileutil::magic::cfront::procdef\fR \fIprocname\fR \fIpath\fR\&.\&.\&.

Changes to idoc/man/files/modules/fumagic/rtcore.n.

277
278
279
280
281
282
283
284
285
286

287
288
289
290

291
292
293
294

295
296

297
298

299
300

301
302
303
304
305
306

307
308
309
310
311
312

313
314
315
316
317
318
319
320
321
322
323
324
325

326
327
328
329
330
331

332
333
334

335
336
337
338



339
340
341
342
343


344
345
346
347




348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392




393
394
395

396
397
398
399
400
401
402
403
404
405


406
407

408
409
410
411
412
413


414
415

416
417
418
419
420

421
422
423



424
425
426
427


428
429
430
431

432
433
434
435
436
437
438
439
440
441
442
443

444
445
446

447
448
449
450
451
452
453

454
455
456
457
458
459
460
461
462
463
464

465
466
467

468
469
470
471
472
473

474
475
476
477



478
479
480
481
482
483
484
485

486
487
488
489
490
491
492
277
278
279
280
281
282
283



284
285
286
287

288
289



290
291

292
293

294
295

296
297





298
299


300
301

302
303
304
305
306
307
308
309
310
311
312



313
314
315
316
317
318

319
320
321

322
323



324
325
326





327
328




329
330
331
332






333
334
335
336
337
















338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361

362










363
364


365






366
367


368



369

370



371
372
373

374


375
376
377
378
379

380
381
382
383
384
385
386
387





388



389







390


391






392

393
394
395

396






397
398
399


400
401
402
403




404
405

406
407
408
409
410
411
412
413







-
-
-
+



-
+

-
-
-
+

-
+

-
+

-
+

-
-
-
-
-
+

-
-


-
+










-
-
-
+





-
+


-
+

-
-
-
+
+
+
-
-
-
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-





-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-


















+
+
+
+


-
+
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
-
-
-
-
-
-
+
+
-
-
+
-
-
-

-
+
-
-
-
+
+
+
-

-
-
+
+



-
+







-
-
-
-
-
+
-
-
-
+
-
-
-
-
-
-
-
+
-
-

-
-
-
-
-
-

-
+


-
+
-
-
-
-
-
-
+


-
-
+
+
+

-
-
-
-


-
+







.sp
package require \fBfileutil::magic::rt  ?2\&.0?\fR
.sp
\fB::fileutil::magic::rt::>\fR
.sp
\fB::fileutil::magic::rt::<\fR
.sp
\fB::fileutil::magic::rt::open\fR \fIfilename\fR
.sp
\fB::fileutil::magic::rt::close\fR
\fB::fileutil::magic::rt::new\fR \fIchan\fR \fInamed\fR \fIanalyze\fR
.sp
\fB::fileutil::magic::rt::file_start\fR \fIname\fR
.sp
\fB::fileutil::magic::rt::result\fR ?\fImsg\fR?
\fB::fileutil::magic::rt::emit\fR \fImsg\fR
.sp
\fB::fileutil::magic::rt::resultv\fR ?\fImsg\fR?
.sp
\fB::fileutil::magic::rt::emit\fR \fImsg\fR
\fB::fileutil::magic::rt::O\fR \fIwhere\fR
.sp
\fB::fileutil::magic::rt::offset\fR \fIwhere\fR
\fB::fileutil::magic::rt::R\fR \fIwhere\fR
.sp
\fB::fileutil::magic::rt::Nv\fR \fItype\fR \fIoffset\fR ?\fIqual\fR?
\fB::fileutil::magic::rt::Nv\fR \fItype\fR \fIoffset\fR \fIcompinvert\fR \fIcomp\fR \fIexpected\fR
.sp
\fB::fileutil::magic::rt::N\fR \fItype\fR \fIoffset\fR \fIcomp\fR \fIval\fR ?\fIqual\fR?
\fB::fileutil::magic::rt::N\fR \fItype\fR \fIoffset\fR \fItestinvert\fR \fIcompinvert\fR \fImod\fR \fImand\fR \fIcomp\fR \fIexpected\fR
.sp
\fB::fileutil::magic::rt::Nvx\fR \fItype\fR \fIoffset\fR ?\fIqual\fR?
.sp
\fB::fileutil::magic::rt::Nx\fR \fItype\fR \fIoffset\fR \fIcomp\fR \fIval\fR ?\fIqual\fR?
.sp
\fB::fileutil::magic::rt::S\fR \fIoffset\fR \fIcomp\fR \fIval\fR ?\fIqual\fR?
\fB::fileutil::magic::rt::S\fR \fItype\fR \fIoffset\fR \fItestinvert\fR \fImod\fR \fImand\fR \fIcomp\fR \fIval\fR
.sp
\fB::fileutil::magic::rt::Sx\fR \fIoffset\fR \fIcomp\fR \fIval\fR ?\fIqual\fR?
.sp
\fB::fileutil::magic::rt::L\fR \fInewlevel\fR
.sp
\fB::fileutil::magic::rt::I\fR \fIbase\fR \fItype\fR \fIdelta\fR
\fB::fileutil::magic::rt::I\fR \fIoffset\fR \fIit\fR \fIioi\fR \fIioo\fR \fIiir\fR \fIio\fR
.sp
\fB::fileutil::magic::rt::R\fR \fIoffset\fR
.sp
\fB::fileutil::magic::rt::U\fR \fIfileindex\fR \fIname\fR
.sp
.BE
.SH DESCRIPTION
.PP
This package provides the runtime core for file type recognition
engines written in pure Tcl and is thus used by all other packages in
this module, i\&.e\&. the two frontend packages
\fBfileutil::magic::mimetypes\fR and
\fBfileutil::magic::filetypes\fR, and the two engine compiler
this module such as \fBfileutil::magic::filetype\fR and the two compiler
packages \fBfileutil::magic::cgen\fR and
\fBfileutil::magic::cfront\fR\&.
.SH COMMANDS
.TP
\fB::fileutil::magic::rt::>\fR
Shorthand for \fBincr level\fR\&.
Increment the level and perform related housekeeping
.TP
\fB::fileutil::magic::rt::<\fR
Shorthand for \fBincr level -1\fR\&.
Decrement the level and perform related housekeeping
.TP
\fB::fileutil::magic::rt::open\fR \fIfilename\fR
This command initializes the runtime and prepares the file
\fIfilename\fR for use by the system\&.
\fB::fileutil::magic::rt::new\fR \fIchan\fR \fInamed\fR \fIanalyze\fR
Create a new command which returns one description of the file each time it is
called, and a code of \fIbreak\fR when there are no more descriptions\&.
This command has to be invoked first, before any other command of this
package\&.
.sp
The command returns the channel handle of the opened file as its
result\&.
\fIchan\fR is the channel containing the data to describe\&.  The channel
configuration is then managed as needed\&.
.TP
\fB::fileutil::magic::rt::close\fR
This command closes the last file opened via
\fB::fileutil::magic::rt::open\fR and shuts the runtime down\&.
\fInamed\fR is a dictionary of named tests, as generated by
\fBfileutil::magic::cfront::compile\fR\&.
\fItest\fR is a command prefix for a routine composed of the list of commands
as returned by \fBfileutil::magic::cfront::compile\fR\&.
This command has to be invoked last, after the file has been dealt
with completely\&.
Afterward another invokation of \fB::fileutil::magic::rt::open\fR  is
required to process another file\&.
.sp
This command returns the empty string as its result\&.
.TP
\fB::fileutil::magic::rt::file_start\fR \fIname\fR
This command marks the start of a magic file when debugging\&. It
returns the empty string as its result\&.
.TP
\fB::fileutil::magic::rt::result\fR ?\fImsg\fR?
This command returns the current result and stops processing\&.
.sp
If \fImsg\fR is specified its text is added to the result before it is
returned\&. See \fB::fileutil::magic::rt::emit\fR for the allowed
special character sequences\&.
.TP
\fB::fileutil::magic::rt::resultv\fR ?\fImsg\fR?
This command returns the current result\&.
In contrast to \fB::fileutil::magic::rt::result\fR processing
continues\&.
.sp
If \fImsg\fR is specified its text is added to the result before it is
returned\&. See \fB::fileutil::magic::rt::emit\fR for the allowed
special character sequences\&.
.TP
\fB::fileutil::magic::rt::emit\fR \fImsg\fR
This command adds the text \fImsg\fR to the result buffer\&. The
message may contain the following special character sequences\&. They
will be replaced with buffered values before the message is added to
the result\&. The command returns the empty string as its result\&.
.RS
.TP
\fB\\b\fR
This sequence is removed
.TP
\fB%s\fR
Replaced with the last buffered string value\&.
.TP
\fB%ld\fR
Replaced with the last buffered numeric value\&.
.TP
\fB%d\fR
See above\&.
.TP
\fB${x:\&.\&.\&.?\&.\&.\&.}\fR
Substitute one string if the file is executable, and
another string otherwise\&.
.RE
.TP
\fB::fileutil::magic::rt::Nv\fR \fItype\fR \fIoffset\fR ?\fIqual\fR?
\fB::fileutil::magic::rt::O\fR \fIwhere\fR
This command fetches the numeric value with \fItype\fR from the
absolute location \fIoffset\fR and returns it as its result\&. The
fetched value is further stored in the numeric buffer\&.
.sp
If \fIqual\fR is specified it is considered to be a mask and applied
to the fetched value before it is stored and returned\&. It has to have
the form of a partial Tcl bit-wise expression, i\&.e\&.
.CS


Produce an offset from \fIwhere\fR, relative to the cursor one level up\&.
Produce an offset from \fIwhere\fR, relative to the offset one level up\&.
	& number

.TP
.CE
.IP
For example:
.CS


\fB::fileutil::magic::rt::Nv\fR \fItype\fR \fIoffset\fR \fIcompinvert\fR \fIcomp\fR \fIexpected\fR
A limited form of \fB::fileutile::magic::rt::N\fR that only checks for
	Nv lelong 0 &0x8080ffff

equality and can't be told to invert the test\&.
.CE
.IP
For the possible types see section \fBNUMERIC TYPES\fR\&.
.TP
\fB::fileutil::magic::rt::N\fR \fItype\fR \fIoffset\fR \fIcomp\fR \fIval\fR ?\fIqual\fR?
\fB::fileutil::magic::rt::N\fR \fItype\fR \fIoffset\fR \fItestinvert\fR \fIcompinvert\fR \fImod\fR \fImand\fR \fIcomp\fR \fIexpected\fR
This command behaves mostly like \fB::fileutil::magic::rt::Nv\fR,
except that it compares the fetched and masked value against \fIval\fR
as specified with \fIcomp\fR and returns the result of that
Fetch the numeric value with \fItype\fR from the absolute location
\fIoffset\fR, compare it with \fIexpected\fR using \fIcomp\fR as the comparision
operator,  and returns the result\&.
comparison\&.
.sp
The argument \fIcomp\fR has to contain one of Tcl's comparison
operators, and the comparison made will be
The argument \fIcomp\fR must be one of Tcl's comparison
operators\&.
.CS


	<val> <comp> <fetched-and-masked-value>
	<comp> <fetched-and-masked-value> <comp> <expected>

.CE
.sp
The special comparison operator \fBx\fR signals that no comparison
should be done, or, in other words, that the fetched value will always
match \fIval\fR\&.
.TP
\fB::fileutil::magic::rt::Nvx\fR \fItype\fR \fIoffset\fR ?\fIqual\fR?
This command behaves like \fB::fileutil::magic::rt::Nv\fR, except that
it additionally remembers the location in the file after the fetch in
the calling context, for the current level, for later use by
\fB::fileutil::magic::rt::R\fR\&.
\fB::fileutil::magic::rt::S\fR \fItype\fR \fIoffset\fR \fItestinvert\fR \fImod\fR \fImand\fR \fIcomp\fR \fIval\fR
.TP
\fB::fileutil::magic::rt::Nx\fR \fItype\fR \fIoffset\fR \fIcomp\fR \fIval\fR ?\fIqual\fR?
This command behaves like \fB::fileutil::magic::rt::N\fR, except that
Like \fB::fileutil::magic::rt::N\fR except that it fetches and compares string
it additionally remembers the location in the file after the fetch in
the calling context, for the current, for later use by
\fB::fileutil::magic::rt::R\fR\&.
.TP
\fB::fileutil::magic::rt::S\fR \fIoffset\fR \fIcomp\fR \fIval\fR ?\fIqual\fR?
This command behaves like \fB::fileutil::magic::rt::N\fR, except that
it fetches and compares strings, not numeric data\&. The fetched value
types , not numeric data\&.
is also stored in the internal string buffer instead of the numeric
buffer\&.
.TP
\fB::fileutil::magic::rt::Sx\fR \fIoffset\fR \fIcomp\fR \fIval\fR ?\fIqual\fR?
This command behaves like \fB::fileutil::magic::rt::S\fR, except that
it additionally remembers the location in the file after the fetch in
the calling context, for the current level, for later use by
\fB::fileutil::magic::rt::R\fR\&.
.TP
\fB::fileutil::magic::rt::L\fR \fInewlevel\fR
This command sets the current level in the calling context to
Sets the current level in the calling context to
\fInewlevel\fR\&. The command returns the empty string as its result\&.
.TP
\fB::fileutil::magic::rt::I\fR \fIbase\fR \fItype\fR \fIdelta\fR
\fB::fileutil::magic::rt::I\fR \fIoffset\fR \fIit\fR \fIioi\fR \fIioo\fR \fIiir\fR \fIio\fR
This command handles base locations specified indirectly through the
contents of the inspected file\&. It returns the sum of \fIdelta\fR and
the value of numeric \fItype\fR fetched from the absolute location
\fIbase\fR\&.
.sp
For the possible types see section \fBNUMERIC TYPES\fR\&.
Calculates an offset based on an initial offset and the provided modifiers\&.
.TP
\fB::fileutil::magic::rt::R\fR \fIoffset\fR
This command handles base locations specified relative to the end of
the last field one level above\&.
Given an initial offset, calculates an offset relative to the cursor at the
next level up\&. The cursor is the position in the data one character after the
data extracted from the file one level up\&.
.sp
In other words, the command computes an absolute location in the file
based on the relative \fIoffset\fR and returns it as its result\&. The
base the offset is added to is the last location remembered for the
level in the calling context\&.
.TP
\fB::fileutil::magic::rt::U\fR \fIfileindex\fR \fIname\fR
Use a named test script at the current level\&.
Add a level and use a named test script\&.
.PP
.SH "NUMERIC TYPES"
.TP
\fBbyte\fR
8-bit integer
.TP
\fBshort\fR

Changes to idoc/man/files/modules/htmlparse/htmlparse.n.

387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
387
388
389
390
391
392
393

394
395
396
397
398
399
400







-







The name of the variable where to store any incomplete HTML into\&. This
makes most sense for the incremental mode\&. The parser will throw an
error if it sees incomplete HTML and has no place to store it to\&. This
makes sense for the normal mode\&. Only incomplete tags are detected,
not missing tags\&.  Optional, defaults to 'no variable'\&.
.RE
.RS
.sp
.TP
\fIInterface to the command prefix\fR
In normal mode the parser will invoke the command prefix with four
arguments appended\&. See \fB::htmlparse::debugCallback\fR for a
description\&.
.sp
In incremental mode, however, the generated scripts will invoke the

Changes to idoc/man/files/modules/httpd/httpd.n.

608
609
610
611
612
613
614
615

616
617
618
619
620
621
622
608
609
610
611
612
613
614

615
616
617
618
619
620
621
622







-
+







tool::define ::test::content\&.file {
	superclass ::httpd::content\&.file
	# Return a file
	# Note: this is using the content\&.file mixin which looks for the reply_file variable
	# and will auto-compute the Content-Type
	method content {} {
	  my reset
    set doc_root [my http_info get doc_root]
    set doc_root [my clay get doc_root]
    my variable reply_file
    set reply_file [file join $doc_root index\&.html]
	}
}
tool::define ::test::content\&.time {
  # return the current system time
	method content {} {
641
642
643
644
645
646
647
648

649
650
651
652
653
654
655
641
642
643
644
645
646
647

648
649
650
651
652
653
654
655







-
+







    my puts "You Sent<p>"
    my puts "<TABLE>"
    foreach {f v} $form {
      my puts "<TR><TH>$f</TH><TD><verbatim>$v</verbatim></TD>"
    }
    my puts "</TABLE><p>"
    my puts "Send some info:<p>"
    my puts "<FORM action=/[my http_info get REQUEST_PATH] method POST>"
    my puts "<FORM action=/[my request get REQUEST_PATH] method POST>"
    my puts "<TABLE>"
    foreach field {name rank serial_number} {
      set line "<TR><TH>$field</TH><TD><input name=\\"$field\\" "
      if {[dict exists $form $field]} {
        append line " value=\\"[dict get $form $field]\\"""
      }
      append line " /></TD></TR>"

Changes to idoc/man/files/modules/log/log.n.

429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
429
430
431
432
433
434
435

436
437
438
439

440
441
442
443
444
445
446







-




-







their priority\&. This command can be used by the -command option of
lsort\&. The result is one of -1, 0 or 1 or an error\&. A result of -1
signals that level1 is of less priority than level2\&. 0 signals that
both levels have the same priority\&. 1 signals that level1 has higher
priority than level2\&.
.TP
\fB::log::lvSuppress\fR \fIlevel\fR {\fIsuppress\fR 1}
]
(Un)suppresses the output of messages having the specified
level\&. Unique abbreviations for the level are allowed here too\&.
.TP
\fB::log::lvSuppressLE\fR \fIlevel\fR {\fIsuppress\fR 1}
]
(Un)suppresses the output of messages having the specified level or
one of lesser priority\&. Unique abbreviations for the level are allowed
here too\&.
.TP
\fB::log::lvIsSuppressed\fR \fIlevel\fR
Asks the package whether the specified level is currently
suppressed\&. Unique abbreviations of level names are allowed\&.

Changes to idoc/man/files/modules/math/math_geometry.n.

412
413
414
415
416
417
418
419

420
421
422
423
424
425
426
412
413
414
415
416
417
418

419
420
421
422
423
424
425
426







-
+







\fIpolygon\fR - like a polyline, but the implicit assumption is that
the polyline is closed (if the first and last points do not coincide,
the missing segment is automatically added)\&.
.IP \(bu
\fIpoint set\fR - again a list of an even number of coordinates, but
the points are regarded without any ordering\&.
.IP \(bu
\fIcircle\fR - a list of thtee numbers, the first two are the coordinates of the
\fIcircle\fR - a list of three numbers, the first two are the coordinates of the
centre and the third is the radius\&.
.PP
.SH PROCEDURES
The package defines the following public procedures:
.TP
\fB::math::geometry::+\fR \fIpoint1\fR \fIpoint2\fR
Compute the sum of the two vectors given as points and return it\&.
950
951
952
953
954
955
956
957
958


959
960
961
962
963
964
965
950
951
952
953
954
955
956


957
958
959
960
961
962
963
964
965







-
-
+
+







list \fIcircle\fR
Circle that may or may not be intersected
.RE
.TP
\fB::math::geometry::intersectionCircleWithCircle\fR \fIcircle1\fR \fIcircle2\fR
Determine the points at which the given two circles intersect\&. There can
be zero, one or two points\&. (If the two circles touch the circle or are very close,
then one point is returned\&. An arbitrary margin of 1\&.0e-10 times the radius of
the first circle is used to determine this situation\&.)
then one point is returned\&. An arbitrary margin of 1\&.0e-10 times the mean of the radii of
the two circles is used to determine this situation\&.)
.RS
.TP
list \fIcircle1\fR
First circle
.TP
list \fIcircle2\fR
Second circle

Changes to idoc/man/files/modules/math/numtheory.n.

1
2
3
4
5

6
7
8
9
10
11
12
1
2
3
4

5
6
7
8
9
10
11
12




-
+







'\"
'\" Generated from file 'numtheory\&.man' by tcllib/doctools with format 'nroff'
'\" Copyright (c) 2010 Lars Hellström <Lars dot Hellstrom at residenset dot net>
'\"
.TH "math::numtheory" n 1\&.0 tcllib "Tcl Math Library"
.TH "math::numtheory" n 1\&.1\&.1 tcllib "Tcl Math Library"
.\" The -*- nroff -*- definitions below are for supplemental macros used
.\" in Tcl/Tk manual entries.
.\"
.\" .AP type name in/out ?indent?
.\"	Start paragraph describing an argument to a library procedure.
.\"	type is type of argument (int, etc.), in/out is either "in", "out",
.\"	or "in/out" to describe whether procedure reads or modifies arg,
272
273
274
275
276
277
278
279

280
281
282
283




284
285
286
287
288
289
290
272
273
274
275
276
277
278

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







-
+




+
+
+
+







..
.BS
.SH NAME
math::numtheory \- Number Theory
.SH SYNOPSIS
package require \fBTcl  ?8\&.5?\fR
.sp
package require \fBmath::numtheory  ?1\&.0?\fR
package require \fBmath::numtheory  ?1\&.1\&.1?\fR
.sp
\fBmath::numtheory::isprime\fR \fIN\fR ?\fIoption\fR \fIvalue\fR \&.\&.\&.?
.sp
\fBmath::numtheory::firstNprimes\fR \fIN\fR
.sp
\fBmath::numtheory::primesLowerThan\fR \fIN\fR
.sp
\fBmath::numtheory::primeFactors\fR \fIN\fR
.sp
\fBmath::numtheory::primesLowerThan\fR \fIN\fR
.sp
\fBmath::numtheory::primeFactors\fR \fIN\fR
.sp
\fBmath::numtheory::uniquePrimeFactors\fR \fIN\fR
.sp
348
349
350
351
352
353
354
















355
356
357
358
359
360
361
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







\fBmath::numtheory::firstNprimes\fR \fIN\fR
Return the first N primes
.RS
.TP
integer \fIN\fR (in)
Number of primes to return
.RE
.TP
\fBmath::numtheory::primesLowerThan\fR \fIN\fR
Return the prime numbers lower/equal to N
.RS
.TP
integer \fIN\fR (in)
Maximum number to consider
.RE
.TP
\fBmath::numtheory::primeFactors\fR \fIN\fR
Return a list of the prime numbers in the number N
.RS
.TP
integer \fIN\fR (in)
Number to be factorised
.RE
.TP
\fBmath::numtheory::primesLowerThan\fR \fIN\fR
Return the prime numbers lower/equal to N
.RS
.TP
integer \fIN\fR (in)
Maximum number to consider

Changes to idoc/man/files/modules/math/statistics.n.

836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
836
837
838
839
840
841
842

843
844
845
846
847
848
849







-







- One or more groups of data to be checked
.RE
.sp
.TP
\fB::math::statistics::quantiles\fR \fIdata\fR \fIconfidence\fR
Return the quantiles for a given set of data
.RS
.sp
.TP
list \fIdata\fR
- List of raw data values
.sp
.TP
float \fIconfidence\fR
- Confidence level (0\&.95 or 0\&.99 for instance) or a list of confidence levels\&.

Added idoc/man/files/modules/math/trig.n.














































































































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
'\"
'\" Generated from file 'trig\&.man' by tcllib/doctools with format 'nroff'
'\" Copyright (c) 2018 Arjen Markus
'\"
.TH "math::trig" n 1\&.0\&.0 tcllib "Tcl Math Library"
.\" The -*- nroff -*- definitions below are for supplemental macros used
.\" in Tcl/Tk manual entries.
.\"
.\" .AP type name in/out ?indent?
.\"	Start paragraph describing an argument to a library procedure.
.\"	type is type of argument (int, etc.), in/out is either "in", "out",
.\"	or "in/out" to describe whether procedure reads or modifies arg,
.\"	and indent is equivalent to second arg of .IP (shouldn't ever be
.\"	needed;  use .AS below instead)
.\"
.\" .AS ?type? ?name?
.\"	Give maximum sizes of arguments for setting tab stops.  Type and
.\"	name are examples of largest possible arguments that will be passed
.\"	to .AP later.  If args are omitted, default tab stops are used.
.\"
.\" .BS
.\"	Start box enclosure.  From here until next .BE, everything will be
.\"	enclosed in one large box.
.\"
.\" .BE
.\"	End of box enclosure.
.\"
.\" .CS
.\"	Begin code excerpt.
.\"
.\" .CE
.\"	End code excerpt.
.\"
.\" .VS ?version? ?br?
.\"	Begin vertical sidebar, for use in marking newly-changed parts
.\"	of man pages.  The first argument is ignored and used for recording
.\"	the version when the .VS was added, so that the sidebars can be
.\"	found and removed when they reach a certain age.  If another argument
.\"	is present, then a line break is forced before starting the sidebar.
.\"
.\" .VE
.\"	End of vertical sidebar.
.\"
.\" .DS
.\"	Begin an indented unfilled display.
.\"
.\" .DE
.\"	End of indented unfilled display.
.\"
.\" .SO ?manpage?
.\"	Start of list of standard options for a Tk widget. The manpage
.\"	argument defines where to look up the standard options; if
.\"	omitted, defaults to "options". The options follow on successive
.\"	lines, in three columns separated by tabs.
.\"
.\" .SE
.\"	End of list of standard options for a Tk widget.
.\"
.\" .OP cmdName dbName dbClass
.\"	Start of description of a specific option.  cmdName gives the
.\"	option's name as specified in the class command, dbName gives
.\"	the option's name in the option database, and dbClass gives
.\"	the option's class in the option database.
.\"
.\" .UL arg1 arg2
.\"	Print arg1 underlined, then print arg2 normally.
.\"
.\" .QW arg1 ?arg2?
.\"	Print arg1 in quotes, then arg2 normally (for trailing punctuation).
.\"
.\" .PQ arg1 ?arg2?
.\"	Print an open parenthesis, arg1 in quotes, then arg2 normally
.\"	(for trailing punctuation) and then a closing parenthesis.
.\"
.\"	# Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
.if t .wh -1.3i ^B
.nr ^l \n(.l
.ad b
.\"	# Start an argument description
.de AP
.ie !"\\$4"" .TP \\$4
.el \{\
.   ie !"\\$2"" .TP \\n()Cu
.   el          .TP 15
.\}
.ta \\n()Au \\n()Bu
.ie !"\\$3"" \{\
\&\\$1 \\fI\\$2\\fP (\\$3)
.\".b
.\}
.el \{\
.br
.ie !"\\$2"" \{\
\&\\$1	\\fI\\$2\\fP
.\}
.el \{\
\&\\fI\\$1\\fP
.\}
.\}
..
.\"	# define tabbing values for .AP
.de AS
.nr )A 10n
.if !"\\$1"" .nr )A \\w'\\$1'u+3n
.nr )B \\n()Au+15n
.\"
.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
.nr )C \\n()Bu+\\w'(in/out)'u+2n
..
.AS Tcl_Interp Tcl_CreateInterp in/out
.\"	# BS - start boxed text
.\"	# ^y = starting y location
.\"	# ^b = 1
.de BS
.br
.mk ^y
.nr ^b 1u
.if n .nf
.if n .ti 0
.if n \l'\\n(.lu\(ul'
.if n .fi
..
.\"	# BE - end boxed text (draw box now)
.de BE
.nf
.ti 0
.mk ^t
.ie n \l'\\n(^lu\(ul'
.el \{\
.\"	Draw four-sided box normally, but don't draw top of
.\"	box if the box started on an earlier page.
.ie !\\n(^b-1 \{\
\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.el \}\
\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
.\}
.\}
.fi
.br
.nr ^b 0
..
.\"	# VS - start vertical sidebar
.\"	# ^Y = starting y location
.\"	# ^v = 1 (for troff;  for nroff this doesn't matter)
.de VS
.if !"\\$2"" .br
.mk ^Y
.ie n 'mc \s12\(br\s0
.el .nr ^v 1u
..
.\"	# VE - end of vertical sidebar
.de VE
.ie n 'mc
.el \{\
.ev 2
.nf
.ti 0
.mk ^t
\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
.sp -1
.fi
.ev
.\}
.nr ^v 0
..
.\"	# Special macro to handle page bottom:  finish off current
.\"	# box/sidebar if in box/sidebar mode, then invoked standard
.\"	# page bottom macro.
.de ^B
.ev 2
'ti 0
'nf
.mk ^t
.if \\n(^b \{\
.\"	Draw three-sided box if this is the box's first page,
.\"	draw two sides but no top otherwise.
.ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
.el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
.\}
.if \\n(^v \{\
.nr ^x \\n(^tu+1v-\\n(^Yu
\kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c
.\}
.bp
'fi
.ev
.if \\n(^b \{\
.mk ^y
.nr ^b 2
.\}
.if \\n(^v \{\
.mk ^Y
.\}
..
.\"	# DS - begin display
.de DS
.RS
.nf
.sp
..
.\"	# DE - end display
.de DE
.fi
.RE
.sp
..
.\"	# SO - start of list of standard options
.de SO
'ie '\\$1'' .ds So \\fBoptions\\fR
'el .ds So \\fB\\$1\\fR
.SH "STANDARD OPTIONS"
.LP
.nf
.ta 5.5c 11c
.ft B
..
.\"	# SE - end of list of standard options
.de SE
.fi
.ft R
.LP
See the \\*(So manual entry for details on the standard options.
..
.\"	# OP - start of full description for a single option
.de OP
.LP
.nf
.ta 4c
Command-Line Name:	\\fB\\$1\\fR
Database Name:	\\fB\\$2\\fR
Database Class:	\\fB\\$3\\fR
.fi
.IP
..
.\"	# CS - begin code excerpt
.de CS
.RS
.nf
.ta .25i .5i .75i 1i
..
.\"	# CE - end code excerpt
.de CE
.fi
.RE
..
.\"	# UL - underline word
.de UL
\\$1\l'|0\(ul'\\$2
..
.\"	# QW - apply quotation marks to word
.de QW
.ie '\\*(lq'"' ``\\$1''\\$2
.\"" fix emacs highlighting
.el \\*(lq\\$1\\*(rq\\$2
..
.\"	# PQ - apply parens and quotation marks to word
.de PQ
.ie '\\*(lq'"' (``\\$1''\\$2)\\$3
.\"" fix emacs highlighting
.el (\\*(lq\\$1\\*(rq\\$2)\\$3
..
.\"	# QR - quoted range
.de QR
.ie '\\*(lq'"' ``\\$1''\\-``\\$2''\\$3
.\"" fix emacs highlighting
.el \\*(lq\\$1\\*(rq\\-\\*(lq\\$2\\*(rq\\$3
..
.\"	# MT - "empty" string
.de MT
.QW ""
..
.BS
.SH NAME
math::trig \- Trigonometric anf hyperbolic functions
.SH SYNOPSIS
package require \fBTcl  8\&.5\fR
.sp
package require \fBmath::trig  1\&.0\&.0\fR
.sp
\fB::math::trig::radian_reduced\fR \fIangle\fR
.sp
\fB::math::trig::degree_reduced\fR \fIangle\fR
.sp
\fB::math::trig::cosec\fR \fIangle\fR
.sp
\fB::math::trig::sec\fR \fIangle\fR
.sp
\fB::math::trig::cotan\fR \fIangle\fR
.sp
\fB::math::trig::acosec\fR \fIvalue\fR
.sp
\fB::math::trig::asec\fR \fIvalue\fR
.sp
\fB::math::trig::acotan\fR \fIvalue\fR
.sp
\fB::math::trig::cosech\fR \fIvalue\fR
.sp
\fB::math::trig::sech\fR \fIvalue\fR
.sp
\fB::math::trig::cotanh\fR \fIvalue\fR
.sp
\fB::math::trig::asinh\fR \fIvalue\fR
.sp
\fB::math::trig::acosh\fR \fIvalue\fR
.sp
\fB::math::trig::atanh\fR \fIvalue\fR
.sp
\fB::math::trig::acosech\fR \fIvalue\fR
.sp
\fB::math::trig::asech\fR \fIvalue\fR
.sp
\fB::math::trig::acotanh\fR \fIvalue\fR
.sp
\fB::math::trig::sind\fR \fIangle\fR
.sp
\fB::math::trig::cosd\fR \fIangle\fR
.sp
\fB::math::trig::tand\fR \fIangle\fR
.sp
\fB::math::trig::cosecd\fR \fIangle\fR
.sp
\fB::math::trig::secd\fR \fIangle\fR
.sp
\fB::math::trig::cotand\fR \fIangle\fR
.sp
.BE
.SH DESCRIPTION
.PP
The \fImath::trig\fR package defines a set of trigonomic and hyperbolic functions
and their inverses\&. In addition it defines versions of the trigonomic functions
that take arguments in degrees instead of radians\&.
.PP
For easy use these functions may be imported into the \fItcl::mathfunc\fR namespace,
so that they can be used directly in the \fIexpr\fR command\&.
.SH FUNCTIONS
The functions \fIradian_reduced\fR and \fIdegree_reduced\fR return a reduced angle, in
respectively radians and degrees, in the intervals [0, 2pi) and [0, 360):
.TP
\fB::math::trig::radian_reduced\fR \fIangle\fR
Return the equivalent angle in the interval [0, 2pi)\&.
.RS
.TP
float \fIangle\fR
Angle (in radians)
.RE
.TP
\fB::math::trig::degree_reduced\fR \fIangle\fR
Return the equivalent angle in the interval [0, 360)\&.
.RS
.TP
float \fIangle\fR
Angle (in degrees)
.RE
.PP
The following trigonomic functions are defined in addition to the ones defined
in the \fIexpr\fR command:
.TP
\fB::math::trig::cosec\fR \fIangle\fR
Calculate the cosecant of the angle (1/cos(angle))
.RS
.TP
float \fIangle\fR
Angle (in radians)
.RE
.TP
\fB::math::trig::sec\fR \fIangle\fR
Calculate the secant of the angle (1/sin(angle))
.RS
.TP
float \fIangle\fR
Angle (in radians)
.RE
.TP
\fB::math::trig::cotan\fR \fIangle\fR
Calculate the cotangent of the angle (1/tan(angle))
.RS
.TP
float \fIangle\fR
Angle (in radians)
.RE
.PP
For these functions also the inverses are defined:
.TP
\fB::math::trig::acosec\fR \fIvalue\fR
Calculate the arc cosecant of the value
.RS
.TP
float \fIvalue\fR
Value of the argument
.RE
.TP
\fB::math::trig::asec\fR \fIvalue\fR
Calculate the arc secant of the value
.RS
.TP
float \fIvalue\fR
Value of the argument
.RE
.TP
\fB::math::trig::acotan\fR \fIvalue\fR
Calculate the arc cotangent of the value
.RS
.TP
float \fIvalue\fR
Value of the argument
.RE
.PP
The following hyperbolic and inverse hyperbolic functions are defined:
.TP
\fB::math::trig::cosech\fR \fIvalue\fR
Calculate the hyperbolic cosecant of the value (1/sinh(value))
.RS
.TP
float \fIvalue\fR
Value of the argument
.RE
.TP
\fB::math::trig::sech\fR \fIvalue\fR
Calculate the hyperbolic secant of the value (1/cosh(value))
.RS
.TP
float \fIvalue\fR
Value of the argument
.RE
.TP
\fB::math::trig::cotanh\fR \fIvalue\fR
Calculate the hyperbolic cotangent of the value (1/tanh(value))
.RS
.TP
float \fIvalue\fR
Value of the argument
.RE
.TP
\fB::math::trig::asinh\fR \fIvalue\fR
Calculate the arc hyperbolic sine of the value
.RS
.TP
float \fIvalue\fR
Value of the argument
.RE
.TP
\fB::math::trig::acosh\fR \fIvalue\fR
Calculate the arc hyperbolic cosine of the value
.RS
.TP
float \fIvalue\fR
Value of the argument
.RE
.TP
\fB::math::trig::atanh\fR \fIvalue\fR
Calculate the arc hyperbolic tangent of the value
.RS
.TP
float \fIvalue\fR
Value of the argument
.RE
.TP
\fB::math::trig::acosech\fR \fIvalue\fR
Calculate the arc hyperbolic cosecant of the value
.RS
.TP
float \fIvalue\fR
Value of the argument
.RE
.TP
\fB::math::trig::asech\fR \fIvalue\fR
Calculate the arc hyperbolic secant of the value
.RS
.TP
float \fIvalue\fR
Value of the argument
.RE
.TP
\fB::math::trig::acotanh\fR \fIvalue\fR
Calculate the arc hyperbolic cotangent of the value
.RS
.TP
float \fIvalue\fR
Value of the argument
.RE
.PP
The following versions of the common trigonometric functions and their
inverses are defined:
.TP
\fB::math::trig::sind\fR \fIangle\fR
Calculate the sine of the angle (in degrees)
.RS
.TP
float \fIangle\fR
Angle (in degrees)
.RE
.TP
\fB::math::trig::cosd\fR \fIangle\fR
Calculate the cosine of the angle (in degrees)
.RS
.TP
float \fIangle\fR
Angle (in radians)
.RE
.TP
\fB::math::trig::tand\fR \fIangle\fR
Calculate the cotangent of the angle (in degrees)
.RS
.TP
float \fIangle\fR
Angle (in degrees)
.RE
.TP
\fB::math::trig::cosecd\fR \fIangle\fR
Calculate the cosecant of the angle (in degrees)
.RS
.TP
float \fIangle\fR
Angle (in degrees)
.RE
.TP
\fB::math::trig::secd\fR \fIangle\fR
Calculate the secant of the angle (in degrees)
.RS
.TP
float \fIangle\fR
Angle (in degrees)
.RE
.TP
\fB::math::trig::cotand\fR \fIangle\fR
Calculate the cotangent of the angle (in degrees)
.RS
.TP
float \fIangle\fR
Angle (in degrees)
.RE
.PP
.SH "BUGS, IDEAS, FEEDBACK"
This document, and the package it describes, will undoubtedly contain
bugs and other problems\&.
Please report such in the category \fImath :: trig\fR of the
\fITcllib Trackers\fR [http://core\&.tcl\&.tk/tcllib/reportlist]\&.
Please also report any ideas for enhancements you may have for either
package and/or documentation\&.
.PP
When proposing code changes, please provide \fIunified diffs\fR,
i\&.e the output of \fBdiff -u\fR\&.
.PP
Note further that \fIattachments\fR are strongly preferred over
inlined patches\&. Attachments can be made by going to the \fBEdit\fR
form of the ticket immediately after its creation, and then using the
left-most button in the secondary navigation bar\&.
.SH KEYWORDS
math, trigonometry
.SH CATEGORY
Mathematics
.SH COPYRIGHT
.nf
Copyright (c) 2018 Arjen Markus

.fi

Changes to idoc/man/files/modules/nns/nns_client.n.

402
403
404
405
406
407
408
409

410
411
412
413
414
415
416
402
403
404
405
406
407
408

409
410
411
412
413
414
415
416







-
+







\fB::nameserv::configure\fR
In this form the command returns a dictionary of all supported
options, and their current values\&. The list of supported options and
their meaning can be found in section \fBOPTIONS\fR\&.
.TP
\fB::nameserv::configure\fR \fB-option\fR
In this form the command is an alias for
"\fB::nameserv::cget\fR \fB-option\fR]"\&.
"\fB::nameserv::cget\fR \fB-option\fR"\&.
The list of supported options and their meaning can be found in
section \fBOPTIONS\fR\&.
.TP
\fB::nameserv::configure\fR \fB-option\fR \fIvalue\fR\&.\&.\&.
In this form the command is used to configure one or more of the
supported options\&. At least one option has to be specified, and each
option is followed by its new value\&.

Changes to idoc/man/files/modules/nns/nns_server.n.

344
345
346
347
348
349
350
351

352
353
354
355
356
357
358
344
345
346
347
348
349
350

351
352
353
354
355
356
357
358







-
+







\fB::nameserv::server::configure\fR
In this form the command returns a dictionary of all supported
options, and their current values\&. The list of supported options and
their meaning can be found in section \fBOPTIONS\fR\&.
.TP
\fB::nameserv::server::configure\fR \fB-option\fR
In this form the command is an alias for
"\fB::nameserv::server::cget\fR \fB-option\fR]"\&.
"\fB::nameserv::server::cget\fR \fB-option\fR"\&.
The list of supported options and their meaning can be found in
section \fBOPTIONS\fR\&.
.TP
\fB::nameserv::server::configure\fR \fB-option\fR \fIvalue\fR\&.\&.\&.
In this form the command is used to configure one or more of the
supported options\&. At least one option has to be specified, and each
option is followed by its new value\&.

Changes to idoc/man/files/modules/oometa/oometa.n.

420
421
422
423
424
425
426
427

428
429
430
431
432
433
434
420
421
422
423
424
425
426

427
428
429
430
431
432
433
434







-
+







The package injects a new method \fBmeta\fR into \fBoo::object\fR\&. \fBoo::object\fR combines the data
for its class (as provided by \fBoo::meta::metadata\fR), with a local variable \fImeta\fR to
produce a local picture of metadata\&.
This method provides the following additional commands:
.TP
\fBoo::object method meta cget\fR ?\fIfield\fR? ?\fI\&.\&.\&.\fR? \fIfield\fR
Attempts to locate a singlar leaf, and return its value\&. For single option lookups, this
is faster than \fBmy meta getnull\fR ?\fIfield\fR? ?\fI\&.\&.\&.\fR? \fIfield\fR], because
is faster than \fBmy meta getnull\fR ?\fIfield\fR? ?\fI\&.\&.\&.\fR? \fIfield\fR, because
it performs a search instead directly instead of producing the recursive merge product
between the class metadata, the local \fImeta\fR variable, and THEN performing the search\&.
.PP
.SH "BUGS, IDEAS, FEEDBACK"
This document, and the package it describes, will undoubtedly contain
bugs and other problems\&.
Please report such in the category \fItcloo\fR of the

Changes to idoc/man/files/modules/pop3d/pop3d.n.

450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
450
451
452
453
454
455
456

457
458
459
460
461
462
463







-







Here we describe the interface which has to be provided by the storage
callback so that pop3 servers following the interface of this module
are able to use it\&. The \fImbox\fR argument is the storage reference
as returned by the \fBlookup\fR method of the authentication
command, see section \fBAuthentication\fR\&.
.TP
\fIstorageCmd\fR \fBdele\fR \fImbox\fR \fImsgList\fR
]
Deletes the messages whose numeric ids are contained in the
\fImsgList\fR from the mailbox specified via \fImbox\fR\&.
.TP
\fIstorageCmd\fR \fBlock\fR \fImbox\fR
This method locks the specified mailbox for use by a single connection
to the server\&. This is necessary to prevent havoc if several
connections to the same mailbox are open\&. The complementary method is

Changes to idoc/man/files/modules/pt/pt_peg_op.n.

1
2
3
4
5

6
7
8
9
10
11
12
1
2
3
4

5
6
7
8
9
10
11
12




-
+







'\"
'\" Generated from file 'pt_peg_op\&.man' by tcllib/doctools with format 'nroff'
'\" Copyright (c) 2009 Andreas Kupries <andreas_kupries@users\&.sourceforge\&.net>
'\"
.TH "pt_peg_op" i 1\&.0\&.1 tcllib "Parser Tools"
.TH "pt_peg_op" i 1\&.0\&.2 tcllib "Parser Tools"
.\" The -*- nroff -*- definitions below are for supplemental macros used
.\" in Tcl/Tk manual entries.
.\"
.\" .AP type name in/out ?indent?
.\"	Start paragraph describing an argument to a library procedure.
.\"	type is type of argument (int, etc.), in/out is either "in", "out",
.\"	or "in/out" to describe whether procedure reads or modifies arg,
272
273
274
275
276
277
278
279

280
281
282
283
284
285
286
272
273
274
275
276
277
278

279
280
281
282
283
284
285
286







-
+







..
.BS
.SH NAME
pt_peg_op \- Parser Tools PE Grammar Utility Operations
.SH SYNOPSIS
package require \fBTcl  8\&.5\fR
.sp
package require \fBpt::peg::op  1\&.0\&.1\fR
package require \fBpt::peg::op  ?1\&.0\&.2?\fR
.sp
\fB::peg::peg::op\fR \fBcalled\fR \fIcontainer\fR
.sp
\fB::peg::peg::op\fR \fBdechain\fR \fIcontainer\fR
.sp
\fB::peg::peg::op\fR \fBdrop unreachable\fR \fIcontainer\fR
.sp

Changes to idoc/man/files/modules/smtpd/smtpd.n.

480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
480
481
482
483
484
485
486

487
488
489
490
491
492
493







-







\fBvalidate_recipient\fR callback
The validate_recipient callback is similar to the validate_sender
callback and permits you to verify a local mailbox and accept mail for
a local user address during RCPT command handling\&. To reject mail,
throw an error as above\&. The error message is ignored\&.
.TP
\fBdeliverMIME\fR callback
]
The deliverMIME callback is called once a mail message has been
successfully passed to the server\&. A mime token is constructed from
the sender, recipients and data and the users procedure it called with
this single argument\&. When the call returns, the mime token is cleaned
up so if the user wishes to preserve the data she must make a copy\&.
.sp
.CS

Changes to idoc/man/files/modules/stooop/switched.n.

502
503
504
505
506
507
508
509

510
511

512
513

514
515
516
517
518
519
520
502
503
504
505
506
507
508

509
510
511
512
513

514
515
516
517
518
519
520
521







-
+


+

-
+







the validity of the value passed to the \fBset-\fBoption\fR\fR
procedure, which should throw an error (for example by using the Tcl
error command) if the value is invalid\&.
.sp
The switched layer also keeps track of the options current
values, so that a \fBset-\fBoption\fR\fR procedure is called
only when the corresponding option value passed as parameter is
different from the current value (see  data members
different from the current value (see \fB-option\fR data members
description)\&.
.TP
\fB-option\fR
.sp
The  data member is an options current value\&.
The \fB-option\fR data member is an options current value\&.
There is one for each option listed in the options procedure\&. It is a
read-only value which the switched layer checks against when an option
is changed\&.
It is rarely used at the layer derived from switched, except in the
few cases, such as in the following example:
.sp
.CS
538
539
540
541
542
543
544

545
546
547


548
549
550
551
552
553
554
539
540
541
542
543
544
545
546
547


548
549
550
551
552
553
554
555
556







+

-
-
+
+








.CE
.sp
In this case, the manufacturer's name is stored at the switched
layer level (this is why the set-manufacturer procedure has nothing to
do) and later retrieved in the printData procedure\&.
.TP
\fBcomplete\fR
.sp
The  data member (not to be confused with
the \fBcomplete\fR procedure) is a boolean\&.
The \fBcomplete\fR data member (not to be confused with the
\fBcomplete\fR procedure) is a boolean\&.
Its initial value is \fBfalse\fR and it is set to \fBtrue\fR at
the very end of the switched \fBcomplete\fR procedure\&.
It becomes useful when some options should be set at construction time
only and not dynamically, as the following example shows:
.sp
.CS

Changes to idoc/man/files/modules/tepam/tepam_doc_gen.n.

509
510
511
512
513
514
515
516

517
518
519
520
521
522
523
509
510
511
512
513
514
515

516
517
518
519
520
521
522
523







-
+







The following parameters are provided to this procedure:
.RS
.TP
\fIName\fR
Name of the argument
.TP
\fIIsOptional\fR
If true (=\fB1\fR) the argument is optional which should be indicated by the generated string (for example by putting the argument into brackets {} or into question marks '?'):
If true (=\fB1\fR) the argument is optional which should be indicated by the generated string (for example by putting the argument into brackets {[]} or into question marks '?'):
.CS

gen(TXT,ArgumentString) mtype 1 0 string -> \fI"[mtype]"\fR
.CE
.TP
\fIIsNamed\fR
If true (=\fB1\fR) an argument is a named argument (option)\&. The generated string should in this case contain the argument/option name, followed by the argument itself:

Changes to idoc/man/files/modules/tepam/tepam_procedure.n.

1120
1121
1122
1123
1124
1125
1126
1127

1128
1129
1130
1131
1132
1133
1134
1120
1121
1122
1123
1124
1125
1126

1127
1128
1129
1130
1131
1132
1133
1134







-
+







The name of the first unnamed argument has therefore not to start with the '-' character\&. The unnamed argument is otherwise considered as name of another named argument\&. This is especially important if the first unnamed argument is given by a variable that can contain any character strings:
.CS

my_proc \fB-n1 N1 -n2 N2 "->" "<-"\fR
\fI-> my_proc: Argument '->' not known\fR

set U1 "->"
my_proc -n1 N1 -n2 N2 $U1 U2}]
my_proc \fB-n1 N1 -n2 N2 $U1 U2\fR
my_proc: Argument '->' not known
.CE
The '--' flag allows separating unambiguously the unnamed arguments from the named arguments\&. All data after the '--' flag will be considered as unnamed argument:
.CS

my_proc \fB-n1 N1 -n2 N2 -- "->" "<-"\fR
\fI-> n1:'N1', n2:'N2', u1:'->', u2:'<-'\fR

Changes to idoc/man/files/modules/textutil/adjust.n.

372
373
374
375
376
377
378
379

380
381
382
383
384
385
386
387
372
373
374
375
376
377
378

379

380
381
382
383
384
385
386







-
+
-







.RE
.TP
\fB-length\fR \fIinteger\fR
Set the length of the \fIlogical\fR line in the string to
\fIinteger\fR\&.  \fIinteger\fR must be a positive integer
value\&. Defaults to \fB72\fR\&.
.TP
\fB-strictlength\fR
\fB-strictlength\fR \fIboolean\fR
\fIboolean\fR]
If set to \fBfalse\fR (default), a line can exceed the specified
\fB-length\fR if a single word is longer than \fB-length\fR\&. If
set to \fBtrue\fR, words that are longer than \fB-length\fR are
split so that no line exceeds the specified \fB-length\fR\&.
.RE
.TP
\fB::textutil::adjust::readPatterns\fR \fIfilename\fR

Changes to idoc/man/files/modules/tool/tool_dict_ensemble.n.

272
273
274
275
276
277
278
279

280
281
282
283
284
285
286
287
288

289
290
291
292
293
294
295
296
272
273
274
275
276
277
278

279
280
281
282
283
284
285
286
287

288

289
290
291
292
293
294
295







-
+








-
+
-







..
.BS
.SH NAME
tool::dict_ensemble \- Dictionary Tools
.SH SYNOPSIS
package require \fBtool  ?0\&.4\&.2?\fR
.sp
\fIobject\fR \fIensemble\fR \fBadd\fR \fIfield\fR
\fIobject\fR \fIensemble\fR \fBadd\fR \fIfield\fR \fIvalue\fR \fIvalue \&.\&.\&.\fR
.sp
.BE
.SH DESCRIPTION
.PP
The \fBdict_ensemble\fR command is a keyword added by \fBtool\fR\&. It defines
a public variable (stored as a dict), and an access function to manipulated and
access the values stored in that dict\&.
.TP
\fIobject\fR \fIensemble\fR \fBadd\fR \fIfield\fR
\fIobject\fR \fIensemble\fR \fBadd\fR \fIfield\fR \fIvalue\fR \fIvalue \&.\&.\&.\fR
] \fIvalue\fR \fIvalue \&.\&.\&.\fR]
Adds elements to a list maintained with the \fIfield\fR leaf of the dict maintained
my this ensemble\&.
Declares a variable \fIname\fR which will be initialized as an array, populated with \fIcontents\fR for objects of this class, as well as any
objects for classes which are descendents of this class\&.
.PP
.SH AUTHORS
Sean Woods

Changes to idoc/man/files/modules/websocket/websocket.n.

470
471
472
473
474
475
476
477

478
479
480
481
482
483
484
470
471
472
473
474
475
476

477
478
479
480
481
482
483
484







-
+







This command registers the (accept) socket \fIsock\fR as the
identifier fo an HTTP server that is capable of doing WebSockets\&.
Paths onto which this server will listen for incoming connections
should be declared using \fB::websocket::live\fR\&.
.TP
\fB::websocket::live\fR \fIsock\fR \fIpath\fR \fIcb\fR ?\fIproto\fR?
This procedure registers callbacks that will be performed on a
WebSocket compliant server registered with \fB::websocket::server\fR]
WebSocket compliant server registered with \fB::websocket::server\fR
whenever a client connects to a matching path and protocol\&.
\fIsock\fR is the listening socket of the websocket compliant server
declared using \fB::websocket::server\fR\&.  \fIpath\fR is a glob-style
path to match in client request, whenever this will occur\&.  \fIcb\fR
is the command to callback (see Callbacks)\&.  \fIproto\fR is a
glob-style protocol name matcher\&.
.TP

Changes to idoc/man/index.n.

5871
5872
5873
5874
5875
5876
5877



5878
5879
5880
5881
5882
5883
5884
5871
5872
5873
5874
5875
5876
5877
5878
5879
5880
5881
5882
5883
5884
5885
5886
5887







+
+
+







math::polynomials
.TP
\fBfiles/modules/math/rational_funcs\&.n\fR
math::rationalfunctions
.TP
\fBfiles/modules/math/special\&.n\fR
math::special
.TP
\fBfiles/modules/math/trig\&.n\fR
math::trig
.TP
\fBfiles/modules/simulation/annealing\&.n\fR
simulation::annealing
.TP
\fBfiles/modules/simulation/montecarlo\&.n\fR
simulation::montecarlo
.TP
11004
11005
11006
11007
11008
11009
11010






11011
11012
11013
11014
11015
11016
11017
11007
11008
11009
11010
11011
11012
11013
11014
11015
11016
11017
11018
11019
11020
11021
11022
11023
11024
11025
11026







+
+
+
+
+
+







page_util_norm_peg
.RE
TreeQL
.RS
.TP
\fBfiles/modules/treeql/treeql\&.n\fR
treeql
.RE
trigonometry
.RS
.TP
\fBfiles/modules/math/trig\&.n\fR
math::trig
.RE
trimming
.RS
.TP
\fBfiles/modules/textutil/textutil\&.n\fR
textutil
.TP

Changes to idoc/man/toc.n.

833
834
835
836
837
838
839



840
841
842
843
844
845
846
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849







+
+
+







.TP
\fBmath::special\fR
\fIfiles/modules/math/special\&.n\fR: Special mathematical functions
.TP
\fBmath::statistics\fR
\fIfiles/modules/math/statistics\&.n\fR: Basic statistical functions and procedures
.TP
\fBmath::trig\fR
\fIfiles/modules/math/trig\&.n\fR: Trigonometric anf hyperbolic functions
.TP
\fBmd4\fR
\fIfiles/modules/md4/md4\&.n\fR: MD4 Message-Digest Algorithm
.TP
\fBmd5\fR
\fIfiles/modules/md5/md5\&.n\fR: MD5 Message-Digest Algorithm
.TP
\fBmd5crypt\fR

Changes to idoc/www/index.html.

2261
2262
2263
2264
2265
2266
2267
2268

2269
2270
2271
2272
2273
2274
2275
2261
2262
2263
2264
2265
2266
2267

2268
2269
2270
2271
2272
2273
2274
2275







-
+







<td class="#doctools_idxleft" width="35%"><a name="matching"> matching </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/grammar_me/me_intro.html"> grammar::me_intro </a> &#183; <a href="tcllib/files/modules/grammar_peg/peg_interp.html"> grammar::peg::interp </a> &#183; <a href="tcllib/files/apps/pt.html"> pt </a> &#183; <a href="tcllib/files/modules/pt/pt_astree.html"> pt::ast </a> &#183; <a href="tcllib/files/modules/pt/pt_cparam_config_critcl.html"> pt::cparam::configuration::critcl </a> &#183; <a href="tcllib/files/modules/pt/pt_cparam_config_tea.html"> pt::cparam::configuration::tea </a> &#183; <a href="tcllib/files/modules/pt/pt_json_language.html"> pt::json_language </a> &#183; <a href="tcllib/files/modules/pt/pt_param.html"> pt::param </a> &#183; <a href="tcllib/files/modules/pt/pt_pexpression.html"> pt::pe </a> &#183; <a href="tcllib/files/modules/pt/pt_pexpr_op.html"> pt::pe::op </a> &#183; <a href="tcllib/files/modules/pt/pt_pegrammar.html"> pt::peg </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_container.html"> pt::peg::container </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_container_peg.html"> pt::peg::container::peg </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_export.html"> pt::peg::export </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_export_container.html"> pt::peg::export::container </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_export_json.html"> pt::peg::export::json </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_export_peg.html"> pt::peg::export::peg </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_from_container.html"> pt::peg::from::container </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_from_json.html"> pt::peg::from::json </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_from_peg.html"> pt::peg::from::peg </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_import.html"> pt::peg::import </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_import_container.html"> pt::peg::import::container </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_import_json.html"> pt::peg::import::json </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_import_peg.html"> pt::peg::import::peg </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_interp.html"> pt::peg::interp </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_to_container.html"> pt::peg::to::container </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_to_cparam.html"> pt::peg::to::cparam </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_to_json.html"> pt::peg::to::json </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_to_param.html"> pt::peg::to::param </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_to_peg.html"> pt::peg::to::peg </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_to_tclparam.html"> pt::peg::to::tclparam </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_language.html"> pt::peg_language </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_introduction.html"> pt::pegrammar </a> &#183; <a href="tcllib/files/modules/pt/pt_pgen.html"> pt::pgen </a> &#183; <a href="tcllib/files/modules/pt/pt_rdengine.html"> pt::rde </a> &#183; <a href="tcllib/files/modules/pt/pt_tclparam_config_nx.html"> pt::tclparam::configuration::nx </a> &#183; <a href="tcllib/files/modules/pt/pt_tclparam_config_snit.html"> pt::tclparam::configuration::snit </a> &#183; <a href="tcllib/files/modules/pt/pt_tclparam_config_tcloo.html"> pt::tclparam::configuration::tcloo </a> &#183; <a href="tcllib/files/modules/pt/pt_util.html"> pt::util </a> &#183; <a href="tcllib/files/modules/pt/pt_to_api.html"> pt_export_api </a> &#183; <a href="tcllib/files/modules/pt/pt_from_api.html"> pt_import_api </a> &#183; <a href="tcllib/files/modules/pt/pt_introduction.html"> pt_introduction </a> &#183; <a href="tcllib/files/modules/pt/pt_parse_peg.html"> pt_parse_peg </a> &#183; <a href="tcllib/files/modules/pt/pt_parser_api.html"> pt_parser_api </a> &#183; <a href="tcllib/files/modules/pt/pt_peg_op.html"> pt_peg_op </a> &#183; <a href="tcllib/files/modules/struct/graphops.html"> struct::graph::op </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="math"> math </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/math/math.html"> math </a> &#183; <a href="tcllib/files/modules/math/bigfloat.html"> math::bigfloat </a> &#183; <a href="tcllib/files/modules/math/bignum.html"> math::bignum </a> &#183; <a href="tcllib/files/modules/math/calculus.html"> math::calculus </a> &#183; <a href="tcllib/files/modules/math/qcomplex.html"> math::complexnumbers </a> &#183; <a href="tcllib/files/modules/math/constants.html"> math::constants </a> &#183; <a href="tcllib/files/modules/math/decimal.html"> math::decimal </a> &#183; <a href="tcllib/files/modules/math/fuzzy.html"> math::fuzzy </a> &#183; <a href="tcllib/files/modules/math/math_geometry.html"> math::geometry </a> &#183; <a href="tcllib/files/modules/math/interpolate.html"> math::interpolate </a> &#183; <a href="tcllib/files/modules/math/linalg.html"> math::linearalgebra </a> &#183; <a href="tcllib/files/modules/math/optimize.html"> math::optimize </a> &#183; <a href="tcllib/files/modules/math/pca.html"> math::PCA </a> &#183; <a href="tcllib/files/modules/math/polynomials.html"> math::polynomials </a> &#183; <a href="tcllib/files/modules/math/rational_funcs.html"> math::rationalfunctions </a> &#183; <a href="tcllib/files/modules/math/special.html"> math::special </a> &#183; <a href="tcllib/files/modules/simulation/annealing.html"> simulation::annealing </a> &#183; <a href="tcllib/files/modules/simulation/montecarlo.html"> simulation::montecarlo </a> &#183; <a href="tcllib/files/modules/simulation/simulation_random.html"> simulation::random </a>
<a href="tcllib/files/modules/math/math.html"> math </a> &#183; <a href="tcllib/files/modules/math/bigfloat.html"> math::bigfloat </a> &#183; <a href="tcllib/files/modules/math/bignum.html"> math::bignum </a> &#183; <a href="tcllib/files/modules/math/calculus.html"> math::calculus </a> &#183; <a href="tcllib/files/modules/math/qcomplex.html"> math::complexnumbers </a> &#183; <a href="tcllib/files/modules/math/constants.html"> math::constants </a> &#183; <a href="tcllib/files/modules/math/decimal.html"> math::decimal </a> &#183; <a href="tcllib/files/modules/math/fuzzy.html"> math::fuzzy </a> &#183; <a href="tcllib/files/modules/math/math_geometry.html"> math::geometry </a> &#183; <a href="tcllib/files/modules/math/interpolate.html"> math::interpolate </a> &#183; <a href="tcllib/files/modules/math/linalg.html"> math::linearalgebra </a> &#183; <a href="tcllib/files/modules/math/optimize.html"> math::optimize </a> &#183; <a href="tcllib/files/modules/math/pca.html"> math::PCA </a> &#183; <a href="tcllib/files/modules/math/polynomials.html"> math::polynomials </a> &#183; <a href="tcllib/files/modules/math/rational_funcs.html"> math::rationalfunctions </a> &#183; <a href="tcllib/files/modules/math/special.html"> math::special </a> &#183; <a href="tcllib/files/modules/math/trig.html"> math::trig </a> &#183; <a href="tcllib/files/modules/simulation/annealing.html"> simulation::annealing </a> &#183; <a href="tcllib/files/modules/simulation/montecarlo.html"> simulation::montecarlo </a> &#183; <a href="tcllib/files/modules/simulation/simulation_random.html"> simulation::random </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="mathematics"> mathematics </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/math/fourier.html"> math::fourier </a> &#183; <a href="tcllib/files/modules/math/statistics.html"> math::statistics </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
4060
4061
4062
4063
4064
4065
4066





4067
4068
4069
4070
4071

4072
4073
4074
4075
4076

4077
4078
4079
4080
4081

4082
4083
4084
4085
4086
4087
4088
4089

4090
4091
4092
4093
4094

4095
4096
4097
4098
4099

4100
4101
4102
4103
4104

4105
4106
4107
4108
4109

4110
4111
4112
4113
4114

4115
4116
4117
4118
4119

4120
4121
4122
4123
4124

4125
4126
4127
4128
4129

4130
4131
4132
4133
4134

4135
4136
4137
4138
4139

4140
4141
4142
4143
4144

4145
4146
4147
4148
4149

4150
4151
4152
4153
4154

4155
4156
4157
4158
4159

4160
4161
4162
4163
4164

4165
4166
4167
4168
4169

4170
4171
4172
4173
4174
4175
4176
4177

4178
4179
4180
4181
4182

4183
4184
4185
4186
4187

4188
4189
4190
4191
4192

4193
4194
4195
4196
4197

4198
4199
4200
4201
4202

4203
4204
4205
4206
4207

4208
4209
4210
4211
4212

4213
4214
4215
4216
4217

4218
4219
4220
4221
4222

4223
4224
4225
4226
4227
4228
4229
4230

4231
4232
4233
4234
4235

4236
4237
4238
4239
4240

4241
4242
4243
4244
4245

4246
4247
4248
4249
4250

4251
4252
4253
4254
4255

4256
4257
4258
4259
4260

4261
4262
4263
4264
4265
4266
4267
4268

4269
4270
4271
4272
4273

4274
4275
4276
4277
4278

4279
4280
4281
4282
4283

4284
4285
4286
4287
4288

4289
4290
4291
4292
4293

4294
4295
4296
4297
4298

4299
4300
4301
4302
4303

4304
4305
4306
4307
4308
4309
4310
4311

4312
4313
4314
4315
4316

4317
4318
4319
4320
4321

4322
4323
4324
4325
4326

4327
4328
4329
4330
4331
4332
4333
4334

4335
4336
4337
4338
4339

4340
4341
4342
4343
4344

4345
4346
4347
4348
4349

4350
4351
4352
4353
4354
4355
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075

4076
4077
4078
4079
4080

4081
4082
4083
4084
4085

4086
4087
4088
4089
4090
4091
4092
4093

4094
4095
4096
4097
4098

4099
4100
4101
4102
4103

4104
4105
4106
4107
4108

4109
4110
4111
4112
4113

4114
4115
4116
4117
4118

4119
4120
4121
4122
4123

4124
4125
4126
4127
4128

4129
4130
4131
4132
4133

4134
4135
4136
4137
4138

4139
4140
4141
4142
4143

4144
4145
4146
4147
4148

4149
4150
4151
4152
4153

4154
4155
4156
4157
4158

4159
4160
4161
4162
4163

4164
4165
4166
4167
4168

4169
4170
4171
4172
4173

4174
4175
4176
4177
4178
4179
4180
4181

4182
4183
4184
4185
4186

4187
4188
4189
4190
4191

4192
4193
4194
4195
4196

4197
4198
4199
4200
4201

4202
4203
4204
4205
4206

4207
4208
4209
4210
4211

4212
4213
4214
4215
4216

4217
4218
4219
4220
4221

4222
4223
4224
4225
4226

4227
4228
4229
4230
4231
4232
4233
4234

4235
4236
4237
4238
4239

4240
4241
4242
4243
4244

4245
4246
4247
4248
4249

4250
4251
4252
4253
4254

4255
4256
4257
4258
4259

4260
4261
4262
4263
4264

4265
4266
4267
4268
4269
4270
4271
4272

4273
4274
4275
4276
4277

4278
4279
4280
4281
4282

4283
4284
4285
4286
4287

4288
4289
4290
4291
4292

4293
4294
4295
4296
4297

4298
4299
4300
4301
4302

4303
4304
4305
4306
4307

4308
4309
4310
4311
4312
4313
4314
4315

4316
4317
4318
4319
4320

4321
4322
4323
4324
4325

4326
4327
4328
4329
4330

4331
4332
4333
4334
4335
4336
4337
4338

4339
4340
4341
4342
4343

4344
4345
4346
4347
4348

4349
4350
4351
4352
4353

4354
4355
4356
4357
4358
4359
4360







+
+
+
+
+




-
+




-
+




-
+







-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+







-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+







-
+




-
+




-
+




-
+




-
+




-
+




-
+







-
+




-
+




-
+




-
+




-
+




-
+




-
+




-
+







-
+




-
+




-
+




-
+







-
+




-
+




-
+




-
+






</td></tr>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="treeql"> TreeQL </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/treeql/treeql.html"> treeql </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="trigonometry"> trigonometry </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/math/trig.html"> math::trig </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="trimming"> trimming </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/textutil/textutil.html"> textutil </a> &#183; <a href="tcllib/files/modules/textutil/trim.html"> textutil::trim </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="twitter"> twitter </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/oauth/oauth.html"> oauth </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="type"> type </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/fileutil/fileutil.html"> fileutil </a> &#183; <a href="tcllib/files/modules/fumagic/cfront.html"> fileutil::magic::cfront </a> &#183; <a href="tcllib/files/modules/fumagic/cgen.html"> fileutil::magic::cgen </a> &#183; <a href="tcllib/files/modules/fumagic/filetypes.html"> fileutil::magic::filetype </a> &#183; <a href="tcllib/files/modules/fumagic/rtcore.html"> fileutil::magic::rt </a> &#183; <a href="tcllib/files/modules/snit/snit.html"> snit </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="type_checking"> Type checking </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/valtype/valtype_common.html"> valtype::common </a> &#183; <a href="tcllib/files/modules/valtype/cc_amex.html"> valtype::creditcard::amex </a> &#183; <a href="tcllib/files/modules/valtype/cc_discover.html"> valtype::creditcard::discover </a> &#183; <a href="tcllib/files/modules/valtype/cc_mastercard.html"> valtype::creditcard::mastercard </a> &#183; <a href="tcllib/files/modules/valtype/cc_visa.html"> valtype::creditcard::visa </a> &#183; <a href="tcllib/files/modules/valtype/ean13.html"> valtype::gs1::ean13 </a> &#183; <a href="tcllib/files/modules/valtype/iban.html"> valtype::iban </a> &#183; <a href="tcllib/files/modules/valtype/imei.html"> valtype::imei </a> &#183; <a href="tcllib/files/modules/valtype/isbn.html"> valtype::isbn </a> &#183; <a href="tcllib/files/modules/valtype/luhn.html"> valtype::luhn </a> &#183; <a href="tcllib/files/modules/valtype/luhn5.html"> valtype::luhn5 </a> &#183; <a href="tcllib/files/modules/valtype/usnpi.html"> valtype::usnpi </a> &#183; <a href="tcllib/files/modules/valtype/verhoeff.html"> valtype::verhoeff </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cU">Keywords: U</a>
</th></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="uevent"> uevent </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/hook/hook.html"> hook </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="unbind"> unbind </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/uev/uevent.html"> uevent </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="uncapitalize"> uncapitalize </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/textutil/textutil_string.html"> textutil::string </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="undenting"> undenting </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/textutil/adjust.html"> textutil::adjust </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="unicode"> unicode </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/stringprep/stringprep.html"> stringprep </a> &#183; <a href="tcllib/files/modules/stringprep/stringprep_data.html"> stringprep::data </a> &#183; <a href="tcllib/files/modules/stringprep/unicode.html"> unicode </a> &#183; <a href="tcllib/files/modules/stringprep/unicode_data.html"> unicode::data </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="union"> union </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/struct/disjointset.html"> struct::disjointset </a> &#183; <a href="tcllib/files/modules/struct/struct_set.html"> struct::set </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="unit"> unit </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/units/units.html"> units </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="unknown_hooking"> unknown hooking </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/namespacex/namespacex.html"> namespacex </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="untie"> untie </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/tie/tie_std.html"> tie </a> &#183; <a href="tcllib/files/modules/tie/tie.html"> tie </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="update"> update </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/coroutine/tcllib_coroutine.html"> coroutine </a> &#183; <a href="tcllib/files/modules/coroutine/coro_auto.html"> coroutine::auto </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="uri"> uri </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/uri/uri.html"> uri </a> &#183; <a href="tcllib/files/modules/uri/urn-scheme.html"> uri_urn </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="url"> url </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/doctools2idx/idx_container.html"> doctools::idx </a> &#183; <a href="tcllib/files/modules/doctools2idx/idx_export.html"> doctools::idx::export </a> &#183; <a href="tcllib/files/modules/doctools2idx/idx_import.html"> doctools::idx::import </a> &#183; <a href="tcllib/files/modules/doctools2toc/toc_export.html"> doctools::toc::export </a> &#183; <a href="tcllib/files/modules/doctools2toc/toc_import.html"> doctools::toc::import </a> &#183; <a href="tcllib/files/modules/map/map_geocode_nominatim.html"> map::geocode::nominatim </a> &#183; <a href="tcllib/files/modules/map/map_slippy_fetcher.html"> map::slippy::fetcher </a> &#183; <a href="tcllib/files/modules/uri/uri.html"> uri </a> &#183; <a href="tcllib/files/modules/uri/urn-scheme.html"> uri_urn </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="urn"> urn </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/uri/urn-scheme.html"> uri_urn </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="us_npi"> US-NPI </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/valtype/usnpi.html"> valtype::usnpi </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="utilities"> utilities </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/namespacex/namespacex.html"> namespacex </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="uuencode"> uuencode </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/base64/uuencode.html"> uuencode </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="uuid"> UUID </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/uuid/uuid.html"> uuid </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cV">Keywords: V</a>
</th></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="validation"> Validation </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/valtype/valtype_common.html"> valtype::common </a> &#183; <a href="tcllib/files/modules/valtype/cc_amex.html"> valtype::creditcard::amex </a> &#183; <a href="tcllib/files/modules/valtype/cc_discover.html"> valtype::creditcard::discover </a> &#183; <a href="tcllib/files/modules/valtype/cc_mastercard.html"> valtype::creditcard::mastercard </a> &#183; <a href="tcllib/files/modules/valtype/cc_visa.html"> valtype::creditcard::visa </a> &#183; <a href="tcllib/files/modules/valtype/ean13.html"> valtype::gs1::ean13 </a> &#183; <a href="tcllib/files/modules/valtype/iban.html"> valtype::iban </a> &#183; <a href="tcllib/files/modules/valtype/imei.html"> valtype::imei </a> &#183; <a href="tcllib/files/modules/valtype/isbn.html"> valtype::isbn </a> &#183; <a href="tcllib/files/modules/valtype/luhn.html"> valtype::luhn </a> &#183; <a href="tcllib/files/modules/valtype/luhn5.html"> valtype::luhn5 </a> &#183; <a href="tcllib/files/modules/valtype/usnpi.html"> valtype::usnpi </a> &#183; <a href="tcllib/files/modules/valtype/verhoeff.html"> valtype::verhoeff </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="value_checking"> Value checking </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/valtype/valtype_common.html"> valtype::common </a> &#183; <a href="tcllib/files/modules/valtype/cc_amex.html"> valtype::creditcard::amex </a> &#183; <a href="tcllib/files/modules/valtype/cc_discover.html"> valtype::creditcard::discover </a> &#183; <a href="tcllib/files/modules/valtype/cc_mastercard.html"> valtype::creditcard::mastercard </a> &#183; <a href="tcllib/files/modules/valtype/cc_visa.html"> valtype::creditcard::visa </a> &#183; <a href="tcllib/files/modules/valtype/ean13.html"> valtype::gs1::ean13 </a> &#183; <a href="tcllib/files/modules/valtype/iban.html"> valtype::iban </a> &#183; <a href="tcllib/files/modules/valtype/imei.html"> valtype::imei </a> &#183; <a href="tcllib/files/modules/valtype/isbn.html"> valtype::isbn </a> &#183; <a href="tcllib/files/modules/valtype/luhn.html"> valtype::luhn </a> &#183; <a href="tcllib/files/modules/valtype/luhn5.html"> valtype::luhn5 </a> &#183; <a href="tcllib/files/modules/valtype/usnpi.html"> valtype::usnpi </a> &#183; <a href="tcllib/files/modules/valtype/verhoeff.html"> valtype::verhoeff </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="vectors"> vectors </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/math/linalg.html"> math::linearalgebra </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="verhoeff"> verhoeff </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/valtype/verhoeff.html"> valtype::verhoeff </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="vertex"> vertex </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/struct/graph.html"> struct::graph </a> &#183; <a href="tcllib/files/modules/struct/graphops.html"> struct::graph::op </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="vertex_cover"> vertex cover </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/struct/graphops.html"> struct::graph::op </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="virtual_channel"> virtual channel </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/virtchannel_base/cat.html"> tcl::chan::cat </a> &#183; <a href="tcllib/files/modules/virtchannel_core/core.html"> tcl::chan::core </a> &#183; <a href="tcllib/files/modules/virtchannel_core/events.html"> tcl::chan::events </a> &#183; <a href="tcllib/files/modules/virtchannel_base/facade.html"> tcl::chan::facade </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_fifo.html"> tcl::chan::fifo </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_fifo2.html"> tcl::chan::fifo2 </a> &#183; <a href="tcllib/files/modules/virtchannel_base/halfpipe.html"> tcl::chan::halfpipe </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_memchan.html"> tcl::chan::memchan </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_null.html"> tcl::chan::null </a> &#183; <a href="tcllib/files/modules/virtchannel_base/nullzero.html"> tcl::chan::nullzero </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_random.html"> tcl::chan::random </a> &#183; <a href="tcllib/files/modules/virtchannel_base/std.html"> tcl::chan::std </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_string.html"> tcl::chan::string </a> &#183; <a href="tcllib/files/modules/virtchannel_base/textwindow.html"> tcl::chan::textwindow </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_variable.html"> tcl::chan::variable </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_zero.html"> tcl::chan::zero </a> &#183; <a href="tcllib/files/modules/virtchannel_base/randseed.html"> tcl::randomseed </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/adler32.html"> tcl::transform::adler32 </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/vt_base64.html"> tcl::transform::base64 </a> &#183; <a href="tcllib/files/modules/virtchannel_core/transformcore.html"> tcl::transform::core </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/vt_counter.html"> tcl::transform::counter </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/vt_crc32.html"> tcl::transform::crc32 </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/hex.html"> tcl::transform::hex </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/identity.html"> tcl::transform::identity </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/limitsize.html"> tcl::transform::limitsize </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/observe.html"> tcl::transform::observe </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/vt_otp.html"> tcl::transform::otp </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/rot.html"> tcl::transform::rot </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/spacer.html"> tcl::transform::spacer </a> &#183; <a href="tcllib/files/modules/virtchannel_transform/tcllib_zlib.html"> tcl::transform::zlib </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="virtual_machine"> virtual machine </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/grammar_me/me_cpu.html"> grammar::me::cpu </a> &#183; <a href="tcllib/files/modules/grammar_me/me_cpucore.html"> grammar::me::cpu::core </a> &#183; <a href="tcllib/files/modules/grammar_me/gasm.html"> grammar::me::cpu::gasm </a> &#183; <a href="tcllib/files/modules/grammar_me/me_tcl.html"> grammar::me::tcl </a> &#183; <a href="tcllib/files/modules/grammar_me/me_intro.html"> grammar::me_intro </a> &#183; <a href="tcllib/files/modules/grammar_me/me_vm.html"> grammar::me_vm </a> &#183; <a href="tcllib/files/modules/grammar_peg/peg_interp.html"> grammar::peg::interp </a> &#183; <a href="tcllib/files/modules/pt/pt_param.html"> pt::param </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="visa"> VISA </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/valtype/cc_visa.html"> valtype::creditcard::visa </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="vwait"> vwait </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/coroutine/tcllib_coroutine.html"> coroutine </a> &#183; <a href="tcllib/files/modules/coroutine/coro_auto.html"> coroutine::auto </a> &#183; <a href="tcllib/files/modules/smtpd/smtpd.html"> smtpd </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cW">Keywords: W</a>
</th></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="wais"> wais </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/uri/uri.html"> uri </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="widget"> widget </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/snit/snit.html"> snit </a> &#183; <a href="tcllib/files/modules/snit/snitfaq.html"> snitfaq </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="widget_adaptors"> widget adaptors </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/snit/snit.html"> snit </a> &#183; <a href="tcllib/files/modules/snit/snitfaq.html"> snitfaq </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="wiki"> wiki </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/doctools/docidx.html"> doctools::idx </a> &#183; <a href="tcllib/files/modules/doctools2idx/idx_container.html"> doctools::idx </a> &#183; <a href="tcllib/files/modules/doctools2idx/idx_export.html"> doctools::idx::export </a> &#183; <a href="tcllib/files/modules/doctools2idx/idx_export_wiki.html"> doctools::idx::export::wiki </a> &#183; <a href="tcllib/files/modules/doctools2toc/toc_container.html"> doctools::toc </a> &#183; <a href="tcllib/files/modules/doctools/doctoc.html"> doctools::toc </a> &#183; <a href="tcllib/files/modules/doctools2toc/toc_export.html"> doctools::toc::export </a> &#183; <a href="tcllib/files/modules/doctools2toc/toc_export_wiki.html"> doctools::toc::export::wiki </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="word"> word </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/doctools2base/tcl_parse.html"> doctools::tcl::parse </a> &#183; <a href="tcllib/files/modules/wip/wip.html"> wip </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="www"> WWW </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/httpd/httpd.html"> tool </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="www"> www </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/uri/uri.html"> uri </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cX">Keywords: X</a>
</th></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="x_208"> x.208 </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/asn/asn.html"> asn </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="x_209"> x.209 </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/asn/asn.html"> asn </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="x_500"> x.500 </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/ldap/ldap.html"> ldap </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="xgoogletoken"> XGoogleToken </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/sasl/gtoken.html"> SASL::XGoogleToken </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="xml"> xml </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/amazon-s3/xsxp.html"> xsxp </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="xor"> xor </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/virtchannel_transform/vt_otp.html"> tcl::transform::otp </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="xpath"> XPath </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/treeql/treeql.html"> treeql </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="xslt"> XSLT </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/treeql/treeql.html"> treeql </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cY">Keywords: Y</a>
</th></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="yaml"> yaml </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/yaml/huddle.html"> huddle </a> &#183; <a href="tcllib/files/modules/yaml/yaml.html"> yaml </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="ydecode"> ydecode </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/base64/yencode.html"> yencode </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="yenc"> yEnc </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/base64/yencode.html"> yencode </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="yencode"> yencode </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/base64/yencode.html"> yencode </a>
</td></tr>
<tr class="#doctools_idxheader"><th colspan="2">
<a name="cZ">Keywords: Z</a>
</th></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="zero"> zero </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/virtchannel_base/nullzero.html"> tcl::chan::nullzero </a> &#183; <a href="tcllib/files/modules/virtchannel_base/tcllib_zero.html"> tcl::chan::zero </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="zip"> zip </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/zip/decode.html"> zipfile::decode </a> &#183; <a href="tcllib/files/modules/zip/encode.html"> zipfile::encode </a> &#183; <a href="tcllib/files/modules/zip/mkzip.html"> zipfile::mkzip </a>
</td></tr>
<tr class="#doctools_idxeven" valign=top>
<tr class="#doctools_idxodd" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="zlib"> zlib </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/virtchannel_transform/tcllib_zlib.html"> tcl::transform::zlib </a>
</td></tr>
<tr class="#doctools_idxodd" valign=top>
<tr class="#doctools_idxeven" valign=top>
<td class="#doctools_idxleft" width="35%"><a name="zoom"> zoom </a></td>
<td class="#doctools_idxright" width="65%">
<a href="tcllib/files/modules/map/map_slippy.html"> map::slippy </a> &#183; <a href="tcllib/files/modules/map/map_slippy_cache.html"> map::slippy::cache </a> &#183; <a href="tcllib/files/modules/map/map_slippy_fetcher.html"> map::slippy::fetcher </a>
</td></tr>
</table>
</body></html>

Added idoc/www/tcllib/files/modules/clay/clay.html.









































































































































































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

<!DOCTYPE html><html><head>
<title>clay - Clay Framework</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
	background: 	#FFFFFF;
	color:	 	black;
    }
    DIV.doctools {
	margin-left:	10%;
	margin-right:	10%;
    }
    DIV.doctools H1,DIV.doctools H2 {
	margin-left:	-5%;
    }
    H1, H2, H3, H4 {
	margin-top: 	1em;
	font-family:	sans-serif;
	font-size:	large;
	color:		#005A9C;
	background: 	transparent;
	text-align:		left;
    }
    H1.doctools_title {
	text-align: center;
    }
    UL,OL {
	margin-right: 0em;
	margin-top: 3pt;
	margin-bottom: 3pt;
    }
    UL LI {
	list-style: disc;
    }
    OL LI {
	list-style: decimal;
    }
    DT {
	padding-top: 	1ex;
    }
    UL.doctools_toc,UL.doctools_toc UL, UL.doctools_toc UL UL {
	font:		normal 12pt/14pt sans-serif;
	list-style:	none;
    }
    LI.doctools_section, LI.doctools_subsection {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding: 	0em;
    }
    PRE {
	display: 	block;
	font-family:	monospace;
	white-space:	pre;
	margin:		0%;
	padding-top:	0.5ex;
	padding-bottom:	0.5ex;
	padding-left:	1ex;
	padding-right:	1ex;
	width:		100%;
    }
    PRE.doctools_example {
	color: 		black;
	background: 	#f5dcb3;
	border:		1px solid black;
    }
    UL.doctools_requirements LI, UL.doctools_syntax LI {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding:	0em;
    }
    DIV.doctools_synopsis {
	color: 		black;
	background: 	#80ffff;
	border:		1px solid black;
	font-family:	serif;
	margin-top: 	1em;
	margin-bottom: 	1em;
    }
    UL.doctools_syntax {
	margin-top: 	1em;
	border-top:	1px solid black;
    }
    UL.doctools_requirements {
	margin-bottom: 	1em;
	border-bottom:	1px solid black;
    }
--></style>
</head>
<!-- Generated from file 'clay.man' by tcllib/doctools with format 'html'
   -->
<!-- Copyright &amp;copy; 2018 Sean Woods &amp;lt;[email protected]&amp;gt;
   -->
<!-- clay.n
   -->
<body><div class="doctools">
<h1 class="doctools_title">clay(n) 0.3 clay &quot;Clay Framework&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>clay - A minimalist framework for large scale OO Projects</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a>
<ul>
<li class="doctools_subsection"><a href="#subsection1">Structured Data</a></li>
<li class="doctools_subsection"><a href="#subsection2">Clay Dialect</a></li>
<li class="doctools_subsection"><a href="#subsection3">Method Delegation</a></li>
</ul>
</li>
<li class="doctools_section"><a href="#section2">Commands</a></li>
<li class="doctools_section"><a href="#section3">Classes</a>
<ul>
<li class="doctools_subsection"><a href="#subsection4">Class  oo::class</a></li>
<li class="doctools_subsection"><a href="#subsection5">Class  oo::object</a></li>
<li class="doctools_subsection"><a href="#subsection6">Class  clay::object</a></li>
<li class="doctools_subsection"><a href="#subsection7">Class  clay::doctool</a></li>
</ul>
</li>
<li class="doctools_section"><a href="#section4">AUTHORS</a></li>
<li class="doctools_section"><a href="#section5">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
<li class="doctools_section"><a href="#category">Category</a></li>
<li class="doctools_section"><a href="#copyright">Copyright</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl 8.6</b></li>
<li>package require <b class="pkgname">uuid</b></li>
<li>package require <b class="pkgname">oo::dialect</b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1">proc <b class="cmd">putb</b> <span class="opt">?<i class="arg">map</i>?</span> <i class="arg">text</i></a></li>
<li><a href="#2">proc <b class="cmd">clay::ancestors</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#3">proc <b class="cmd">clay::args_to_dict</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#4">proc <b class="cmd">clay::args_to_options</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#5">proc <b class="cmd">clay::dictmerge</b> <i class="arg">varname</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#6">proc <b class="cmd">clay::_dictmerge</b> <i class="arg">a</i> <i class="arg">b</i></a></li>
<li><a href="#7">proc <b class="cmd">clay::dictputb</b> <i class="arg">dict</i></a></li>
<li><a href="#8">proc <b class="cmd">clay::_dictputb</b> <i class="arg">leaf</i> <i class="arg">level</i> <i class="arg">varname</i> <i class="arg">dict</i></a></li>
<li><a href="#9">proc <b class="cmd">clay::dynamic_arguments</b> <i class="arg">ensemble</i> <i class="arg">method</i> <i class="arg">arglist</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#10">proc <b class="cmd">clay::dynamic_wrongargs_message</b> <i class="arg">arglist</i></a></li>
<li><a href="#11">proc <b class="cmd">clay::is_dict</b> <i class="arg">d</i></a></li>
<li><a href="#12">proc <b class="cmd">clay::is_null</b> <i class="arg">value</i></a></li>
<li><a href="#13">proc <b class="cmd">clay::leaf</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#14">proc <b class="cmd">clay::path</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#15">proc <b class="cmd">clay::script_path</b></a></li>
<li><a href="#16">proc <b class="cmd">clay::NSNormalize</b> <i class="arg">qualname</i></a></li>
<li><a href="#17">proc <b class="cmd">clay::uuid_generate</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#18">proc <b class="cmd">clay::dynamic_methods</b> <i class="arg">class</i></a></li>
<li><a href="#19">proc <b class="cmd">clay::dynamic_methods_class</b> <i class="arg">thisclass</i></a></li>
<li><a href="#20">proc <b class="cmd">clay::define::Array</b> <i class="arg">name</i> <span class="opt">?<i class="arg">values</i> <b class="const"></b>?</span></a></li>
<li><a href="#21">proc <b class="cmd">clay::define::component</b> <i class="arg">name</i> <i class="arg">info</i></a></li>
<li><a href="#22">proc <b class="cmd">clay::define::constructor</b> <i class="arg">arglist</i> <i class="arg">rawbody</i></a></li>
<li><a href="#23">proc <b class="cmd">clay::define::class_method</b> <i class="arg">name</i> <i class="arg">arglist</i> <i class="arg">body</i></a></li>
<li><a href="#24">proc <b class="cmd">clay::define::clay</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#25">proc <b class="cmd">clay::define::destructor</b> <i class="arg">rawbody</i></a></li>
<li><a href="#26">proc <b class="cmd">clay::define::Dict</b> <i class="arg">name</i> <span class="opt">?<i class="arg">values</i> <b class="const"></b>?</span></a></li>
<li><a href="#27">proc <b class="cmd">clay::define::Variable</b> <i class="arg">name</i> <span class="opt">?<i class="arg">default</i> <b class="const"></b>?</span></a></li>
<li><a href="#28">proc <b class="cmd">clay::object_create</b> <i class="arg">objname</i> <span class="opt">?<i class="arg">class</i> <b class="const"></b>?</span></a></li>
<li><a href="#29">proc <b class="cmd">clay::object_rename</b> <i class="arg">object</i> <i class="arg">newname</i></a></li>
<li><a href="#30">proc <b class="cmd">clay::object_destroy</b> <i class="arg">objname</i></a></li>
<li><a href="#31">proc <b class="cmd">clay::ensemble_methodbody</b> <i class="arg">ensemble</i> <i class="arg">einfo</i></a></li>
<li><a href="#32">proc <b class="cmd">clay::define::Ensemble</b> <i class="arg">rawmethod</i> <i class="arg">arglist</i> <i class="arg">body</i></a></li>
<li><a href="#33">proc <b class="cmd">clay::cat</b> <i class="arg">fname</i></a></li>
<li><a href="#34">proc <b class="cmd">clay::docstrip</b> <i class="arg">text</i></a></li>
<li><a href="#35">method <b class="cmd">clay ancestors</b></a></li>
<li><a href="#36">method <b class="cmd">clay dump</b></a></li>
<li><a href="#37">method <b class="cmd">clay get</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></li>
<li><a href="#38">method <b class="cmd">clay merge</b> <i class="arg">dict</i> <span class="opt">?<b class="option">dict...</b>?</span></a></li>
<li><a href="#39">method <b class="cmd">clay replace</b> <i class="arg">dictionary</i></a></li>
<li><a href="#40">method <b class="cmd">clay search</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></li>
<li><a href="#41">method <b class="cmd">clay set</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span> <i class="arg">value</i></a></li>
<li><a href="#42">method <b class="cmd">clay ancestors</b></a></li>
<li><a href="#43">method <b class="cmd">clay cget</b> <i class="arg">field</i></a></li>
<li><a href="#44">method <b class="cmd">clay delegate</b> <span class="opt">?<i class="arg">stub</i>?</span> <span class="opt">?<i class="arg">object</i>?</span></a></li>
<li><a href="#45">method <b class="cmd">clay dump</b></a></li>
<li><a href="#46">method <b class="cmd">clay ensemble_map</b></a></li>
<li><a href="#47">method <b class="cmd">clay eval</b> <i class="arg">script</i></a></li>
<li><a href="#48">method <b class="cmd">clay evolve</b></a></li>
<li><a href="#49">method <b class="cmd">clay exists</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></li>
<li><a href="#50">method <b class="cmd">clay flush</b></a></li>
<li><a href="#51">method <b class="cmd">clay forward</b> <i class="arg">method</i> <i class="arg">object</i></a></li>
<li><a href="#52">method <b class="cmd">clay get</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></li>
<li><a href="#53">method <b class="cmd">clay leaf</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></li>
<li><a href="#54">method <b class="cmd">clay merge</b> <i class="arg">dict</i> <span class="opt">?<b class="option">dict...</b>?</span></a></li>
<li><a href="#55">method <b class="cmd">clay mixin</b> <i class="arg">class</i> <span class="opt">?<b class="option">class...</b>?</span></a></li>
<li><a href="#56">method <b class="cmd">clay mixinmap</b> <span class="opt">?<i class="arg">stub</i>?</span> <span class="opt">?<i class="arg">classes</i>?</span></a></li>
<li><a href="#57">method <b class="cmd">clay provenance</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></li>
<li><a href="#58">method <b class="cmd">clay replace</b> <i class="arg">dictionary</i></a></li>
<li><a href="#59">method <b class="cmd">clay source</b> <i class="arg">filename</i></a></li>
<li><a href="#60">method <b class="cmd">clay set</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span> <i class="arg">value</i></a></li>
<li><a href="#61">method <b class="cmd">InitializePublic</b></a></li>
<li><a href="#62">method <b class="cmd">InitializePublic</b></a></li>
<li><a href="#63">method <b class="cmd">constructor</b></a></li>
<li><a href="#64">method <b class="cmd">arglist</b> <i class="arg">arglist</i></a></li>
<li><a href="#65">method <b class="cmd">comment</b> <i class="arg">block</i></a></li>
<li><a href="#66">method <b class="cmd">keyword.Class</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <i class="arg">body</i></a></li>
<li><a href="#67">method <b class="cmd">keyword.class</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <i class="arg">body</i></a></li>
<li><a href="#68">method <b class="cmd">keyword.class_method</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#69">method <b class="cmd">keyword.method</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#70">method <b class="cmd">keyword.proc</b> <i class="arg">commentblock</i> <i class="arg">name</i> <i class="arg">arglist</i> <i class="arg">body</i></a></li>
<li><a href="#71">method <b class="cmd">reset</b></a></li>
<li><a href="#72">method <b class="cmd">Main</b></a></li>
<li><a href="#73">method <b class="cmd">section.method</b> <i class="arg">keyword</i> <i class="arg">method</i> <i class="arg">minfo</i></a></li>
<li><a href="#74">method <b class="cmd">section.class</b> <i class="arg">class_name</i> <i class="arg">class_info</i></a></li>
<li><a href="#75">method <b class="cmd">section.command</b> <i class="arg">procinfo</i></a></li>
<li><a href="#76">method <b class="cmd">manpage</b> <span class="opt">?<b class="option">header <em>value</em></b>?</span> <span class="opt">?<b class="option">footer <em>value</em></b>?</span> <span class="opt">?<b class="option">authors <em>list</em></b>?</span></a></li>
<li><a href="#77">method <b class="cmd">scan_text</b> <i class="arg">text</i></a></li>
<li><a href="#78">method <b class="cmd">scan_file</b> <i class="arg">filename</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>Clay introduces a method ensemble to both <b class="class">oo::class</b> and <b class="class">oo::object</b> called
clay. This ensemble handles all of the high level interactions within the framework.
Clay stores structured data. Clan manages method delegation. Clay has facilities to
manage the complex interactions that come about with mixins.</p>
<p>The central concept is that inside of every object and class
(which are actually objects too) is a dict called clay. What is stored in that dict is
left to the imagination. But because this dict is exposed via a public method, we can
share structured data between object, classes, and mixins.</p>
<div id="subsection1" class="doctools_subsection"><h3><a name="subsection1">Structured Data</a></h3>
<p>Clay uses a standardized set of method interactions and introspection that TclOO already provides to perform on-the-fly searches. On-the-fly searches mean that the data is never stale, and we avoid many of the sorts of collisions that would arise when objects start mixing in other classes during operation.</p>
<p>The <b class="method">clay</b> methods for both classes and objects have a get and a set method. For objects, get will search through the local clay dict. If the requested leaf is not found, or the query is for a branch, the system will then begin to poll the clay methods of all of the class that implements the object, all of that classes’ ancestors, as well as all of the classes that have been mixed into this object, and all of their ancestors.</p>
<p>Intended branches on a tree end with a directory slash (/). Intended leaves are left unadorned. This is a guide for the tool that builds the search
results to know what parts of a dict are intended to be branches and which are intended to be leaves.
For simple cases, branch marking can be ignored:</p>
<pre class="doctools_example">
::oo::class create ::foo { }
::foo clay set property/ color blue
::foo clay set property/ shape round
set A [::foo new]
$A clay get property/
{color blue shape round}
$A clay set property/ shape square
$A clay get property/
{color blue shape square}
</pre>
<p>But when you start storing blocks of text, guessing what field is a dict and what isn’t gets messy:</p>
<pre class="doctools_example">
::foo clay set description {A generic thing of designated color and shape}
$A clay get description
{A generic thing of designated color and shape}
Without a convention for discerning branches for leaves what should have been a value can be accidentally parsed as a dictionary, and merged with all of the other values that were never intended to be merge. Here is an example of it all going wrong:
::oo::class create ::foo { }
# Add description as a leaf
::foo clay set description  {A generic thing of designated color and shape}
# Add description as a branch
::foo clay set description/  {A generic thing of designated color and shape}
::oo::class create ::bar {
  superclass foo
}
# Add description as a leaf
::bar clay set description  {A drinking establishment of designated color and shape and size}
# Add description as a branch
::bar clay set description/  {A drinking establishment of designated color and shape and size}
set B [::bar new]
# As a leaf we get the value verbatim from he nearest ancestor
$B clay get description
  {A drinking establishment of designated color and shape and size}
# As a branch we get a recursive merge
$B clay get description/
{A drinking establishment of designated color and size thing of}
</pre>
</div>
<div id="subsection2" class="doctools_subsection"><h3><a name="subsection2">Clay Dialect</a></h3>
<p>Clay is built using the oo::dialect module from Tcllib. oo::dialect allows you to either add keywords directly to clay, or to create your own
metaclass and keyword set using Clay as a foundation. For details on the keywords and what they do, consult the functions in the ::clay::define namespace.</p>
</div>
<div id="subsection3" class="doctools_subsection"><h3><a name="subsection3">Method Delegation</a></h3>
<p>Method Delegation
It is sometimes useful to have an external object that can be invoked as if it were a method of the object. Clay provides a delegate ensemble method to perform that delegation, as well as introspect which methods are delegated in that manner. All delegated methods are marked with html-like tag markings (&lt; &gt;) around them.</p>
<pre class="doctools_example">
::clay::define counter {
  Variable counter 0
  method incr {{howmuch 1}} {
    my variable counter
    incr counter $howmuch
  }
  method value {} {
    my variable counter
    return $counter
  }
  method reset {} {
    my variable counter
    set counter 0
  }
}
::clay::define example {
  variable buffer
  constructor {} {
    # Build a counter object
    set obj [namespace current]::counter
    ::counter create $obj
    # Delegate the counter
    my delegate &lt;counter&gt; $obj
  }
  method line {text} {
    my &lt;counter&gt; incr
    append buffer $text
  }
}
set A [example new]
$A line {Who’s line is it anyway?}
$A &lt;counter&gt; value
1
</pre>
</div>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">Commands</a></h2>
<dl class="doctools_definitions">
<dt><a name="1">proc <b class="cmd">putb</b> <span class="opt">?<i class="arg">map</i>?</span> <i class="arg">text</i></a></dt>
<dd><p>Append a line of text to a variable. Optionally apply a string mapping.</p></dd>
<dt><a name="2">proc <b class="cmd">clay::ancestors</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="3">proc <b class="cmd">clay::args_to_dict</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="4">proc <b class="cmd">clay::args_to_options</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="5">proc <b class="cmd">clay::dictmerge</b> <i class="arg">varname</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="6">proc <b class="cmd">clay::_dictmerge</b> <i class="arg">a</i> <i class="arg">b</i></a></dt>
<dd></dd>
<dt><a name="7">proc <b class="cmd">clay::dictputb</b> <i class="arg">dict</i></a></dt>
<dd></dd>
<dt><a name="8">proc <b class="cmd">clay::_dictputb</b> <i class="arg">leaf</i> <i class="arg">level</i> <i class="arg">varname</i> <i class="arg">dict</i></a></dt>
<dd></dd>
<dt><a name="9">proc <b class="cmd">clay::dynamic_arguments</b> <i class="arg">ensemble</i> <i class="arg">method</i> <i class="arg">arglist</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="10">proc <b class="cmd">clay::dynamic_wrongargs_message</b> <i class="arg">arglist</i></a></dt>
<dd></dd>
<dt><a name="11">proc <b class="cmd">clay::is_dict</b> <i class="arg">d</i></a></dt>
<dd></dd>
<dt><a name="12">proc <b class="cmd">clay::is_null</b> <i class="arg">value</i></a></dt>
<dd></dd>
<dt><a name="13">proc <b class="cmd">clay::leaf</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="14">proc <b class="cmd">clay::path</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="15">proc <b class="cmd">clay::script_path</b></a></dt>
<dd></dd>
<dt><a name="16">proc <b class="cmd">clay::NSNormalize</b> <i class="arg">qualname</i></a></dt>
<dd></dd>
<dt><a name="17">proc <b class="cmd">clay::uuid_generate</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="18">proc <b class="cmd">clay::dynamic_methods</b> <i class="arg">class</i></a></dt>
<dd></dd>
<dt><a name="19">proc <b class="cmd">clay::dynamic_methods_class</b> <i class="arg">thisclass</i></a></dt>
<dd></dd>
<dt><a name="20">proc <b class="cmd">clay::define::Array</b> <i class="arg">name</i> <span class="opt">?<i class="arg">values</i> <b class="const"></b>?</span></a></dt>
<dd><p>New OO Keywords for clay</p></dd>
<dt><a name="21">proc <b class="cmd">clay::define::component</b> <i class="arg">name</i> <i class="arg">info</i></a></dt>
<dd></dd>
<dt><a name="22">proc <b class="cmd">clay::define::constructor</b> <i class="arg">arglist</i> <i class="arg">rawbody</i></a></dt>
<dd></dd>
<dt><a name="23">proc <b class="cmd">clay::define::class_method</b> <i class="arg">name</i> <i class="arg">arglist</i> <i class="arg">body</i></a></dt>
<dd></dd>
<dt><a name="24">proc <b class="cmd">clay::define::clay</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="25">proc <b class="cmd">clay::define::destructor</b> <i class="arg">rawbody</i></a></dt>
<dd></dd>
<dt><a name="26">proc <b class="cmd">clay::define::Dict</b> <i class="arg">name</i> <span class="opt">?<i class="arg">values</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="27">proc <b class="cmd">clay::define::Variable</b> <i class="arg">name</i> <span class="opt">?<i class="arg">default</i> <b class="const"></b>?</span></a></dt>
<dd><p>This keyword can also be expressed:</p>
<pre class="doctools_example">property variable NAME {default DEFAULT}</pre>
<p>Variables registered in the variable property are also initialized
    (if missing) when the object changes class via the <em>morph</em> method.</p></dd>
<dt><a name="28">proc <b class="cmd">clay::object_create</b> <i class="arg">objname</i> <span class="opt">?<i class="arg">class</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="29">proc <b class="cmd">clay::object_rename</b> <i class="arg">object</i> <i class="arg">newname</i></a></dt>
<dd></dd>
<dt><a name="30">proc <b class="cmd">clay::object_destroy</b> <i class="arg">objname</i></a></dt>
<dd></dd>
<dt><a name="31">proc <b class="cmd">clay::ensemble_methodbody</b> <i class="arg">ensemble</i> <i class="arg">einfo</i></a></dt>
<dd></dd>
<dt><a name="32">proc <b class="cmd">clay::define::Ensemble</b> <i class="arg">rawmethod</i> <i class="arg">arglist</i> <i class="arg">body</i></a></dt>
<dd></dd>
<dt><a name="33">proc <b class="cmd">clay::cat</b> <i class="arg">fname</i></a></dt>
<dd><p>Concatenate a file</p></dd>
<dt><a name="34">proc <b class="cmd">clay::docstrip</b> <i class="arg">text</i></a></dt>
<dd><p>Strip the global comments from tcl code. Used to
 prevent the documentation markup comments from clogging
 up files intended for distribution in machine readable format.</p></dd>
</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">Classes</a></h2>
<div id="subsection4" class="doctools_subsection"><h3><a name="subsection4">Class  oo::class</a></h3>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="35">method <b class="cmd">clay ancestors</b></a></dt>
<dd><p>Return this class and all ancestors in search order.</p></dd>
<dt><a name="36">method <b class="cmd">clay dump</b></a></dt>
<dd><p>Return a complete dump of this object's clay data, but only this object's clay data.</p></dd>
<dt><a name="37">method <b class="cmd">clay get</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></dt>
<dd><p>Pull a chunk of data from the clay system. If the last element of <em>path</em> is a branch (ends in a slash /),
     returns a recursive merge of all data from this object and it's constituent classes of the data in that branch.
     If the last element is a leaf, search this object for a matching leaf, or search all  constituent classes for a matching
     leaf and return the first value found.
     If no value is found, returns an empty string.</p></dd>
<dt><a name="38">method <b class="cmd">clay merge</b> <i class="arg">dict</i> <span class="opt">?<b class="option">dict...</b>?</span></a></dt>
<dd><p>Recursively merge the dictionaries given into the object's local clay storage.</p></dd>
<dt><a name="39">method <b class="cmd">clay replace</b> <i class="arg">dictionary</i></a></dt>
<dd><p>Replace the contents of the internal clay storage with the dictionary given.</p></dd>
<dt><a name="40">method <b class="cmd">clay search</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></dt>
<dd><p>Return the first matching value for the path in either this class's clay data or one of its ancestors</p></dd>
<dt><a name="41">method <b class="cmd">clay set</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span> <i class="arg">value</i></a></dt>
<dd><p>Merge the conents of <b class="const">value</b> with the object's clay storage at <b class="const">path</b>.</p></dd>
</dl>
</div>
<div id="subsection5" class="doctools_subsection"><h3><a name="subsection5">Class  oo::object</a></h3>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="42">method <b class="cmd">clay ancestors</b></a></dt>
<dd><p>Return the class this object belongs to, all classes mixed into this object, and all ancestors of those classes in search order.</p></dd>
<dt><a name="43">method <b class="cmd">clay cget</b> <i class="arg">field</i></a></dt>
<dd><p>Pull a value from either the object's clay structure or one of its constituent classes that matches the field name.
 The order of search us:</p>
<p>1. The as a value in local dict variable config</p>
<p>2. The as a value in local dict variable clay</p>
<p>3. As a leaf in any ancestor as a root of the clay tree</p>
<p>4. As a leaf in any ancestor under the const/ branch of the clay tree</p></dd>
<dt><a name="44">method <b class="cmd">clay delegate</b> <span class="opt">?<i class="arg">stub</i>?</span> <span class="opt">?<i class="arg">object</i>?</span></a></dt>
<dd><p>Introspect or control method delegation. With no arguments, the method will return a
 key/value list of stubs and objects. With just the <i class="arg">stub</i> argument, the method will
 return the object (if any) attached to the stub. With a <i class="arg">stub</i> and an <i class="arg">object</i>
 this command will forward all calls to the method <i class="arg">stub</i> to the <i class="arg">object</i>.</p></dd>
<dt><a name="45">method <b class="cmd">clay dump</b></a></dt>
<dd><p>Return a complete dump of this object's clay data, as well as the data from all constituent classes recursively blended in.</p></dd>
<dt><a name="46">method <b class="cmd">clay ensemble_map</b></a></dt>
<dd><p>Return a dictionary describing the method ensembles to be assembled for this object</p></dd>
<dt><a name="47">method <b class="cmd">clay eval</b> <i class="arg">script</i></a></dt>
<dd><p>Evaluated a script in the namespace of this object</p></dd>
<dt><a name="48">method <b class="cmd">clay evolve</b></a></dt>
<dd><p>Trigger the <b class="method">InitializePublic</b> private method</p></dd>
<dt><a name="49">method <b class="cmd">clay exists</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></dt>
<dd><p>Returns 1 if <em>path</em> exists in either the object's clay data. Values greater than one indicate the element exists in one of the object's constituent classes. A value of zero indicates the path could not be found.</p></dd>
<dt><a name="50">method <b class="cmd">clay flush</b></a></dt>
<dd><p>Wipe any caches built by the clay implementation</p></dd>
<dt><a name="51">method <b class="cmd">clay forward</b> <i class="arg">method</i> <i class="arg">object</i></a></dt>
<dd><p>A convenience wrapper for</p>
<pre class="doctools_example">oo::objdefine [self] forward {*}$args</pre>
</dd>
<dt><a name="52">method <b class="cmd">clay get</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></dt>
<dd><p>Pull a chunk of data from the clay system. If the last element of <em>path</em> is a branch (ends in a slash /),
   returns a recursive merge of all data from this object and it's constituent classes of the data in that branch.
   If the last element is a leaf, search this object for a matching leaf, or search all  constituent classes for a matching
   leaf and return the first value found.
   If no value is found, returns an empty string.</p></dd>
<dt><a name="53">method <b class="cmd">clay leaf</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></dt>
<dd><p>A modified get which is tailored to pull only leaf elements</p></dd>
<dt><a name="54">method <b class="cmd">clay merge</b> <i class="arg">dict</i> <span class="opt">?<b class="option">dict...</b>?</span></a></dt>
<dd><p>Recursively merge the dictionaries given into the object's local clay storage.</p></dd>
<dt><a name="55">method <b class="cmd">clay mixin</b> <i class="arg">class</i> <span class="opt">?<b class="option">class...</b>?</span></a></dt>
<dd><p>Perform [oo::objdefine [self] mixin] on this object, with a few additional rules:
   Prior to the call, for any class was previously mixed in, but not in the new result, execute the script registered to mixin/ unmap-script (if given.)
   For all new classes, that were not present prior to this call, after the native TclOO mixin is invoked, execute the script registered to mixin/ map-script (if given.)
   Fall all classes that are now present and “mixed in”, execute the script registered to mixin/ react-script (if given.)</p></dd>
<dt><a name="56">method <b class="cmd">clay mixinmap</b> <span class="opt">?<i class="arg">stub</i>?</span> <span class="opt">?<i class="arg">classes</i>?</span></a></dt>
<dd><p>With no arguments returns the map of stubs and classes mixed into the current object. When only stub is given,
  returns the classes mixed in on that stub. When stub and classlist given, replace the classes currently on that stub with the given
  classes and invoke clay mixin on the new matrix of mixed in classes.</p></dd>
<dt><a name="57">method <b class="cmd">clay provenance</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span></a></dt>
<dd><p>Return either <b class="const">self</b> if that path exists in the current object, or return the first class (if any) along the clay search path which contains that element.</p></dd>
<dt><a name="58">method <b class="cmd">clay replace</b> <i class="arg">dictionary</i></a></dt>
<dd><p>Replace the contents of the internal clay storage with the dictionary given.</p></dd>
<dt><a name="59">method <b class="cmd">clay source</b> <i class="arg">filename</i></a></dt>
<dd><p>Source the given filename within the object's namespace</p></dd>
<dt><a name="60">method <b class="cmd">clay set</b> <i class="arg">path</i> <span class="opt">?<b class="option">path...</b>?</span> <i class="arg">value</i></a></dt>
<dd><p>Merge the conents of <b class="const">value</b> with the object's clay storage at <b class="const">path</b>.</p></dd>
<dt><a name="61">method <b class="cmd">InitializePublic</b></a></dt>
<dd><p>Instantiate variables. Called on object creation and during clay mixin.</p></dd>
</dl>
</div>
<div id="subsection6" class="doctools_subsection"><h3><a name="subsection6">Class  clay::object</a></h3>
<p>clay::object
 This class is inherited by all classes that have options.</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="62">method <b class="cmd">InitializePublic</b></a></dt>
<dd><p>Instantiate variables and build ensemble methods.</p></dd>
</dl>
</div>
<div id="subsection7" class="doctools_subsection"><h3><a name="subsection7">Class  clay::doctool</a></h3>
<pre class="doctools_example">{ set authors {
   {John Doe} {[email protected]}
   {Tom RichardHarry} {[email protected]}
 }
 # Create the object
 ::clay::doctool create AutoDoc
 set fout [open [file join $moddir module.tcl] w]
 foreach file [glob [file join $srcdir *.tcl]] {
   set content [::clay::cat [file join $srcdir $file]]
    # Scan the file
    AutoDoc scan_text $content
    # Strip the comments from the distribution
    puts $fout [::clay::docstrip $content]
 }
 # Write out the manual page
 set manout [open [file join $moddir module.man] w]
 dict set arglist header [string map $modmap [::clay::cat [file join $srcdir manual.txt]]]
 dict set arglist footer [string map $modmap [::clay::cat [file join $srcdir footer.txt]]]
 dict set arglist authors $authors
 puts $manout [AutoDoc manpage {*}$arglist]
 close $manout
}</pre>
<p>Tool for build scripts to dynamically generate manual files from comments
 in source code files</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="63">method <b class="cmd">constructor</b></a></dt>
<dd></dd>
<dt><a name="64">method <b class="cmd">arglist</b> <i class="arg">arglist</i></a></dt>
<dd><p>Process an argument list into an informational dict.
 This method also understands non-positional
 arguments expressed in the notation of Tip 471
 <a href="https://core.tcl-lang.org/tips/doc/trunk/tip/479.md">https://core.tcl-lang.org/tips/doc/trunk/tip/479.md</a>.</p>
<p>The output will be a dictionary of all of the fields and whether the fields
 are <b class="const">positional</b>, <b class="const">mandatory</b>, and whether they have a
 <b class="const">default</b> value.</p>
<p>Example:</p>
<pre class="doctools_example">   my arglist {a b {c 10}}
   &gt; a {positional 1 mandatory 1} b {positional 1 mandatory 1} c {positional 1 mandatory 0 default 10}
</pre>
</dd>
<dt><a name="65">method <b class="cmd">comment</b> <i class="arg">block</i></a></dt>
<dd><p>Convert a block of comments into an informational dictionary.
 If lines in the comment start with a single word ending in a colon,
 all subsequent lines are appended to a dictionary field of that name.
 If no fields are given, all of the text is appended to the <b class="const">description</b>
 field.</p>
<p>Example:</p>
<pre class="doctools_example"> my comment {Does something cool}
 &gt; description {Does something cool}
 my comment {
 title : Something really cool
 author : Sean Woods
 author : John Doe
 description :
 This does something really cool!
 }
 &gt; description {This does something really cool!}
   title {Something really cool}
   author {Sean Woods
   John Doe}
</pre>
</dd>
<dt><a name="66">method <b class="cmd">keyword.Class</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <i class="arg">body</i></a></dt>
<dd><p>Process an oo::objdefine call that modifies the class object
 itself</p></dd>
<dt><a name="67">method <b class="cmd">keyword.class</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <i class="arg">body</i></a></dt>
<dd><p>Process an oo::define, clay::define, etc statement.</p></dd>
<dt><a name="68">method <b class="cmd">keyword.class_method</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Process a statement for a clay style class method</p></dd>
<dt><a name="69">method <b class="cmd">keyword.method</b> <i class="arg">resultvar</i> <i class="arg">commentblock</i> <i class="arg">name</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Process a statement for a tcloo style object method</p></dd>
<dt><a name="70">method <b class="cmd">keyword.proc</b> <i class="arg">commentblock</i> <i class="arg">name</i> <i class="arg">arglist</i> <i class="arg">body</i></a></dt>
<dd><p>Process a proc statement</p></dd>
<dt><a name="71">method <b class="cmd">reset</b></a></dt>
<dd><p>Reset the state of the object and its embedded coroutine</p></dd>
<dt><a name="72">method <b class="cmd">Main</b></a></dt>
<dd><p>Main body of the embedded coroutine for the object</p></dd>
<dt><a name="73">method <b class="cmd">section.method</b> <i class="arg">keyword</i> <i class="arg">method</i> <i class="arg">minfo</i></a></dt>
<dd><p>Generate the manual page text for a method or proc</p></dd>
<dt><a name="74">method <b class="cmd">section.class</b> <i class="arg">class_name</i> <i class="arg">class_info</i></a></dt>
<dd><p>Generate the manual page text for a class</p></dd>
<dt><a name="75">method <b class="cmd">section.command</b> <i class="arg">procinfo</i></a></dt>
<dd><p>Generate the manual page text for the commands section</p></dd>
<dt><a name="76">method <b class="cmd">manpage</b> <span class="opt">?<b class="option">header <em>value</em></b>?</span> <span class="opt">?<b class="option">footer <em>value</em></b>?</span> <span class="opt">?<b class="option">authors <em>list</em></b>?</span></a></dt>
<dd><p>Generate the manual page. Returns the completed text suitable for saving in .man file.
 The header argument is a block of doctools text to go in before the machine generated
 section. footer is a block of doctools text to go in after the machine generated
 section. authors is a list of individual authors and emails in the form of AUTHOR EMAIL ?AUTHOR EMAIL?...</p></dd>
<dt><a name="77">method <b class="cmd">scan_text</b> <i class="arg">text</i></a></dt>
<dd><p>Scan a block of text</p></dd>
<dt><a name="78">method <b class="cmd">scan_file</b> <i class="arg">filename</i></a></dt>
<dd><p>Scan a file of text</p></dd>
</dl>
</div>
</div>
<div id="section4" class="doctools_section"><h2><a name="section4">AUTHORS</a></h2>
<p>Sean Woods <a href="mailto:<[email protected]>">mailto:&lt;[email protected]&gt;</a></p>
</div>
<div id="section5" class="doctools_section"><h2><a name="section5">Bugs, Ideas, Feedback</a></h2>
<p>This document, and the package it describes, will undoubtedly contain
bugs and other problems.
Please report such in the category <em>oo</em> of the
<a href="http://core.tcl.tk/tcllib/reportlist">Tcllib Trackers</a>.
Please also report any ideas for enhancements you may have for either
package and/or documentation.</p>
<p>When proposing code changes, please provide <em>unified diffs</em>,
i.e the output of <b class="const">diff -u</b>.</p>
<p>Note further that <em>attachments</em> are strongly preferred over
inlined patches. Attachments can be made by going to the <b class="const">Edit</b>
form of the ticket immediately after its creation, and then using the
left-most button in the secondary navigation bar.</p>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>
<p>TclOO, oo</p>
</div>
<div id="category" class="doctools_section"><h2><a name="category">Category</a></h2>
<p>Programming tools</p>
</div>
<div id="copyright" class="doctools_section"><h2><a name="copyright">Copyright</a></h2>
<p>Copyright &copy; 2018 Sean Woods &lt;[email protected]&gt;</p>
</div>
</div></body></html>

Changes to idoc/www/tcllib/files/modules/cron/cron.html.

138
139
140
141
142
143
144
145
146


147
148
149
150
151
152
153
138
139
140
141
142
143
144


145
146
147
148
149
150
151
152
153







-
-
+
+







<li><a href="#5"><b class="cmd">::cron::object_coroutine</b> <i class="arg">object</i> <i class="arg">coroutine</i> <i class="arg">?info?</i></a></li>
<li><a href="#6"><b class="cmd">::cron::sleep</b> <i class="arg">milliseconds</i></a></li>
<li><a href="#7"><b class="cmd">::cron::task delete</b> <i class="arg">process</i></a></li>
<li><a href="#8"><b class="cmd">::cron::task exists</b> <i class="arg">process</i></a></li>
<li><a href="#9"><b class="cmd">::cron::task info</b> <i class="arg">process</i></a></li>
<li><a href="#10"><b class="cmd">::cron::task set</b> <i class="arg">process</i> <i class="arg">field</i> <i class="arg">value</i> <i class="arg">?field...?</i> <i class="arg">?value...?</i></a></li>
<li><a href="#11"><b class="cmd">::cron::wake</b> <i class="arg">?who?</i></a></li>
<li><a href="#12"><b class="cmd">::cron::clock_step</b> <i class="arg">milleseconds</i></a></li>
<li><a href="#13"><b class="cmd">::cron::clock_delay</b> <i class="arg">milleseconds</i></a></li>
<li><a href="#12"><b class="cmd">::cron::clock_step</b> <i class="arg">milliseconds</i></a></li>
<li><a href="#13"><b class="cmd">::cron::clock_delay</b> <i class="arg">milliseconds</i></a></li>
<li><a href="#14"><b class="cmd">::cron::clock_sleep</b> <i class="arg">seconds</i> <i class="arg">?offset?</i></a></li>
<li><a href="#15"><b class="cmd">::cron::clock_set</b> <i class="arg">newtime</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>The <b class="package">cron</b> package provides a Pure-tcl set of tools to allow
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256

















257
258
259
260
261
262
263
264
265
266
267
268

269
270
271


272
273

274
275
276
277
278
279
280
281

282
283
284
285
286
287
288
233
234
235
236
237
238
239

















240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267

268
269


270
271
272

273
274
275
276
277
278
279
280

281
282
283
284
285
286
287
288







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
+

-
-
+
+

-
+







-
+







<dt><a name="8"><b class="cmd">::cron::task exists</b> <i class="arg">process</i></a></dt>
<dd><p>Returns true if <i class="arg">process</i> is registered with cron.</p></dd>
<dt><a name="9"><b class="cmd">::cron::task info</b> <i class="arg">process</i></a></dt>
<dd><p>Returns a dict describing <i class="arg">process</i>. See <b class="cmd">::cron::task set</b> for a description of the options.</p></dd>
<dt><a name="10"><b class="cmd">::cron::task set</b> <i class="arg">process</i> <i class="arg">field</i> <i class="arg">value</i> <i class="arg">?field...?</i> <i class="arg">?value...?</i></a></dt>
<dd><p>If <i class="arg">process</i> does not exist, it is created. Options Include:</p>
<dl class="doctools_definitions">
<b class="cmd"><a href="../../../../index.html#command">command</a></b>
If <b class="cmd"><a href="../coroutine/tcllib_coroutine.html">coroutine</a></b> is black, a global command which implements this process. If <b class="cmd"><a href="../coroutine/tcllib_coroutine.html">coroutine</a></b> is not
black, the command to invoke to create or recreate the coroutine.
<b class="cmd"><a href="../coroutine/tcllib_coroutine.html">coroutine</a></b>
The name of the coroutine (if any) which implements this process.
<b class="cmd">frequency</b>
If -1, this process is terminated after the next event. If 0 this process should be called during every
idle event. If positive, this process should generate events periodically. The frequency is an interger number
of milleseconds between events.
<b class="cmd"><a href="../../../../index.html#object">object</a></b>
The object associated with this process or coroutine.
<b class="cmd">scheduled</b>
If non-zero, the absolute time from the epoch (in milleseconds) that this process will trigger an event.
If zero, and the <b class="cmd">frequency</b> is also zero, this process is called every idle loop.
<b class="cmd"><a href="../../../../index.html#running">running</a></b>
A boolean flag. If true it indicates the process never returned or yielded during the event loop,
and will not be called again until it does so.
<dt><b class="cmd"><a href="../../../../index.html#command">command</a></b></dt>
<dd><p>If <b class="cmd"><a href="../coroutine/tcllib_coroutine.html">coroutine</a></b> is black, a global command which implements this process. If <b class="cmd"><a href="../coroutine/tcllib_coroutine.html">coroutine</a></b> is not
black, the command to invoke to create or recreate the coroutine.</p></dd>
<dt><b class="cmd"><a href="../coroutine/tcllib_coroutine.html">coroutine</a></b></dt>
<dd><p>The name of the coroutine (if any) which implements this process.</p></dd>
<dt><b class="cmd">frequency</b></dt>
<dd><p>If -1, this process is terminated after the next event. If 0 this process should be called during every
idle event. If positive, this process should generate events periodically. The frequency is an integer number
of milliseconds between events.</p></dd>
<dt><b class="cmd"><a href="../../../../index.html#object">object</a></b></dt>
<dd><p>The object associated with this process or coroutine.</p></dd>
<dt><b class="cmd">scheduled</b></dt>
<dd><p>If non-zero, the absolute time from the epoch (in milliseconds) that this process will trigger an event.
If zero, and the <b class="cmd">frequency</b> is also zero, this process is called every idle loop.</p></dd>
<dt><b class="cmd"><a href="../../../../index.html#running">running</a></b></dt>
<dd><p>A boolean flag. If true it indicates the process never returned or yielded during the event loop,
and will not be called again until it does so.</p></dd>
</dl></dd>
<dt><a name="11"><b class="cmd">::cron::wake</b> <i class="arg">?who?</i></a></dt>
<dd><p>Wake up cron, and arrange for its event loop to be run during the next Idle cycle.</p>
<pre class="doctools_example">
::cron::wake {I just did something important}
</pre>
</dd>
</dl>
<p>Several utility commands are provided that are used internally within cron and for
testing cron, but may or may not be useful in the general cases.</p>
<dl class="doctools_definitions">
<dt><a name="12"><b class="cmd">::cron::clock_step</b> <i class="arg">milleseconds</i></a></dt>
<dt><a name="12"><b class="cmd">::cron::clock_step</b> <i class="arg">milliseconds</i></a></dt>
<dd><p>Return a clock time absolute to the epoch which falls on the next
border between one second and the next for the value of <i class="arg">milleseconds</i></p></dd>
<dt><a name="13"><b class="cmd">::cron::clock_delay</b> <i class="arg">milleseconds</i></a></dt>
border between one second and the next for the value of <i class="arg">milliseconds</i></p></dd>
<dt><a name="13"><b class="cmd">::cron::clock_delay</b> <i class="arg">milliseconds</i></a></dt>
<dd><p>Return a clock time absolute to the epoch which falls on the next
border between one second and the next <i class="arg">milleseconds</i> in the future.</p></dd>
border between one second and the next <i class="arg">milliseconds</i> in the future.</p></dd>
<dt><a name="14"><b class="cmd">::cron::clock_sleep</b> <i class="arg">seconds</i> <i class="arg">?offset?</i></a></dt>
<dd><p>Return a clock time absolute to the epoch which falls exactly <i class="arg">seconds</i> in
the future. If offset is given it may be positive or negative, and will shift
the final time to before or after the second would flip.</p></dd>
<dt><a name="15"><b class="cmd">::cron::clock_set</b> <i class="arg">newtime</i></a></dt>
<dd><p>Sets the internal clock for cron. This command will advance the time in 100ms
increment, triggering events, until the internal time catches up with <i class="arg">newtime</i>.</p>
<p><i class="arg">newtime</i> is expressed in absolute milleseconds since the beginning of the epoch.</p></dd>
<p><i class="arg">newtime</i> is expressed in absolute milliseconds since the beginning of the epoch.</p></dd>
</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">Bugs, Ideas, Feedback</a></h2>
<p>This document, and the package it describes, will undoubtedly contain
bugs and other problems.
Please report such in the category <em>odie</em> of the
<a href="http://core.tcl.tk/tcllib/reportlist">Tcllib Trackers</a>.

Changes to idoc/www/tcllib/files/modules/doctools/cvs.html.

175
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
175
176
177
178
179
180
181


182
183
184
185
186
187
188
189







-
-
+







<dt>varname <i class="arg">fvar</i> (in)</dt>
<dd><p>Has to refer to an array variable. Keys are strings containing
date, author of a log entry, and a comment for that entry, in this
order, separated by commas.</p>
<p>The values are lists of the files the entry is touching.</p></dd>
</dl></dd>
<dt><a name="2"><b class="cmd">::doctools::cvs::toChangeLog</b> <i class="arg">evar</i> <i class="arg">cvar</i> <i class="arg">fvar</i></a></dt>
<dd><p>]
The three arguments for this command are the same as the last three
<dd><p>The three arguments for this command are the same as the last three
arguments of the command <b class="cmd">::doctools::cvs::scanLog</b>. This command
however expects them to be filled with information about one or more
logs. It takes this information and converts it into a text in the
format of a ChangeLog as accepted and generated by <b class="syscmd"><a href="../../../../index.html#emacs">emacs</a></b>. The
constructed text is returned as the result of the command.</p></dd>
</dl>
</div>

Changes to idoc/www/tcllib/files/modules/doctools/doctools_lang_intro.html.

225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
225
226
227
228
229
230
231










232
233
234
235
236
237
238







-
-
-
-
-
-
-
-
-
-







[<b class="cmd">require   PACKAGE</b>]
[description]
[manpage_end]
</pre>
<p>Remember that the whitespace is optional. The document</p>
<pre class="doctools_example">
    [manpage_begin NAME SECTION VERSION]
[see_also doctools_intro]
[see_also doctools_lang_cmdref]
[see_also doctools_lang_faq]
[see_also doctools_lang_syntax]
[keywords {doctools commands}]
[keywords {doctools language}]
[keywords {doctools markup}]
[keywords {doctools syntax}]
[keywords markup]
[keywords {semantic markup}]
    [copyright {YEAR AUTHOR}][titledesc TITLE][moddesc MODULE_TITLE]
    [require PACKAGE VERSION][require PACKAGE][description]
    [vset CATEGORY doctools]
[include ../doctools2base/include/feedback.inc]
[manpage_end]
</pre>
<p>has the same meaning as the example before.</p>
429
430
431
432
433
434
435
436

437
438
439
440
441
442
443
419
420
421
422
423
424
425

426
427
428
429
430
431
432
433







-
+







<p>The example demonstrating the use of text markup is an excerpt from
the <i class="term"><a href="doctools_lang_cmdref.html">doctools language command reference</a></i>, with some
highlighting added.
It shows their use within a block of text, as the arguments of a list
item command (<b class="cmd">call</b>), and our ability to nest them.</p>
<pre class="doctools_example">
  ...
  [call [<b class="cmd">cmd arg_def</b>] [<b class="cmd">arg type</b>] [<b class="cmd">arg name</b>]] [<b class="cmd">opt</b> [<b class="cmd">arg mode</b>]]]
  [call [<b class="cmd">cmd arg_def</b>] [<b class="cmd">arg type</b>] [<b class="cmd">arg name</b>] [<b class="cmd">opt</b> [<b class="cmd">arg mode</b>]]]
  Text structure. List element. Argument list. Automatically closes the
  previous list element. Specifies the data-[<b class="cmd">arg type</b>] of the described
  argument of a command, its [<b class="cmd">arg name</b>] and its i/o-[<b class="cmd">arg mode</b>]. The
  latter is optional.
  ...
</pre>
</div>

Changes to idoc/www/tcllib/files/modules/fumagic/cfront.html.

144
145
146
147
148
149
150
151
152
153





154
155
156
157
158
159
160
144
145
146
147
148
149
150



151
152
153
154
155
156
157
158
159
160
161
162







-
-
-
+
+
+
+
+







into recognizers based on the <b class="package"><a href="rtcore.html">fileutil::magic::rt</a></b> recognizer
runtime package. For the generator backed used by this compiler see
the package <b class="package"><a href="cgen.html">fileutil::magic::cgen</a></b>.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">COMMANDS</a></h2>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">::fileutil::magic::cfront::compile</b> <i class="arg">path</i>...</a></dt>
<dd><p>This command takes the paths of one or more files and directories and
compiles all the files, and the files in all the directories into a
single recognizer for all the file types specified in these files.</p>
<dd><p>This command takes the paths of one or more files and directories and compiles
all the files, and the files in all the directories into a single analyzer for
all the file types specified in these files.  It returns a list whose first
item is a list per-file dictionaries of analyzer scripts and whose second item
is a list of analyzer commands.</p>
<p>All the files have to be in the format specified by magic(5).</p>
<p>The result of the command is a Tcl script containing the generated
recognizer.</p></dd>
<dt><a name="2"><b class="cmd">::fileutil::magic::cfront::procdef</b> <i class="arg">procname</i> <i class="arg">path</i>...</a></dt>
<dd><p>This command behaves like <b class="cmd">::fileutil::magic::cfront::compile</b>
with regard to the specified path arguments, then wraps the resulting
recognizer script into a procedure named <i class="arg">procname</i>, puts code

Changes to idoc/www/tcllib/files/modules/fumagic/rtcore.html.

128
129
130
131
132
133
134
135
136
137
138
139
140
141







142
143

144
145
146
147
148
149
150
151




152
153
154
155
156
157
158
159

160
161
162
163
164
165
166
167

168
169
170
171
172
173
174
175







176
177
178
179



180
181
182
183
184
185

186
187
188
189
190
191
192
193
194
195
196
197
198
199
200

201
202
203
204
205
206
207
208
209
210
211
212
213



214
215
216
217



218
219

220
221
222

223
224
225
226
227
228
229
230


231
232
233



234
235
236


237
238

239
240
241
242
243
244
245
246
247
248
249


250
251
252
253
254
255
256
257

258
259
260
261
262
263
264


265
266

267
268
269
270
271
272
273
274




275
276
277
278
279
280



281
282
283
284
285
286
287
128
129
130
131
132
133
134







135
136
137
138
139
140
141


142








143
144
145
146
147
148
149
150
151
152


153

154
155
156
157
158
159

160
161







162
163
164
165
166
167
168




169
170
171






172
173
174













175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192



193
194
195


196



197








198
199



200
201
202



203
204
205

206
207
208
209
210







211
212








213







214
215
216

217








218
219
220
221






222
223
224
225
226
227
228
229
230
231







-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
-
-
-
+
+
+
+






-
-
+
-






-
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
+
+
+
-
-
-
-
-
-
+


-
-
-
-
-
-
-
-
-
-
-
-
-
+













+
+
+

-
-
-
+
+
+
-
-
+
-
-
-
+
-
-
-
-
-
-
-
-
+
+
-
-
-
+
+
+
-
-
-
+
+

-
+




-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
+

-
+
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+







<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl 8.5</b></li>
<li>package require <b class="pkgname">fileutil::magic::rt <span class="opt">?2.0?</span></b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><b class="cmd">::fileutil::magic::rt::&gt;</b></a></li>
<li><a href="#2"><b class="cmd">::fileutil::magic::rt::&lt;</b></a></li>
<li><a href="#3"><b class="cmd">::fileutil::magic::rt::open</b> <i class="arg">filename</i></a></li>
<li><a href="#4"><b class="cmd">::fileutil::magic::rt::close</b></a></li>
<li><a href="#5"><b class="cmd">::fileutil::magic::rt::file_start</b> <i class="arg">name</i></a></li>
<li><a href="#6"><b class="cmd">::fileutil::magic::rt::result</b> <span class="opt">?<i class="arg">msg</i>?</span></a></li>
<li><a href="#7"><b class="cmd">::fileutil::magic::rt::resultv</b> <span class="opt">?<i class="arg">msg</i>?</span></a></li>
<li><a href="#8"><b class="cmd">::fileutil::magic::rt::emit</b> <i class="arg">msg</i></a></li>
<li><a href="#9"><b class="cmd">::fileutil::magic::rt::offset</b> <i class="arg">where</i></a></li>
<li><a href="#3"><b class="cmd">::fileutil::magic::rt::new</b> <i class="arg">chan</i> <i class="arg">named</i> <i class="arg">analyze</i></a></li>
<li><a href="#4"><b class="cmd">::fileutil::magic::rt::file_start</b> <i class="arg">name</i></a></li>
<li><a href="#5"><b class="cmd">::fileutil::magic::rt::emit</b> <i class="arg">msg</i></a></li>
<li><a href="#6"><b class="cmd">::fileutil::magic::rt::O</b> <i class="arg">where</i></a></li>
<li><a href="#7"><b class="cmd">::fileutil::magic::rt::R</b> <i class="arg">where</i></a></li>
<li><a href="#8"><b class="cmd">::fileutil::magic::rt::Nv</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">compinvert</i> <i class="arg">comp</i> <i class="arg">expected</i></a></li>
<li><a href="#9"><b class="cmd">::fileutil::magic::rt::N</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">testinvert</i> <i class="arg">compinvert</i> <i class="arg">mod</i> <i class="arg">mand</i> <i class="arg">comp</i> <i class="arg">expected</i></a></li>
<li><a href="#10"><b class="cmd">::fileutil::magic::rt::Nv</b> <i class="arg">type</i> <i class="arg">offset</i> <span class="opt">?<i class="arg">qual</i>?</span></a></li>
<li><a href="#11"><b class="cmd">::fileutil::magic::rt::N</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></li>
<li><a href="#10"><b class="cmd">::fileutil::magic::rt::S</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">testinvert</i> <i class="arg">mod</i> <i class="arg">mand</i> <i class="arg">comp</i> <i class="arg">val</i></a></li>
<li><a href="#12"><b class="cmd">::fileutil::magic::rt::Nvx</b> <i class="arg">type</i> <i class="arg">offset</i> <span class="opt">?<i class="arg">qual</i>?</span></a></li>
<li><a href="#13"><b class="cmd">::fileutil::magic::rt::Nx</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></li>
<li><a href="#14"><b class="cmd">::fileutil::magic::rt::S</b> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></li>
<li><a href="#15"><b class="cmd">::fileutil::magic::rt::Sx</b> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></li>
<li><a href="#16"><b class="cmd">::fileutil::magic::rt::L</b> <i class="arg">newlevel</i></a></li>
<li><a href="#17"><b class="cmd">::fileutil::magic::rt::I</b> <i class="arg">base</i> <i class="arg">type</i> <i class="arg">delta</i></a></li>
<li><a href="#18"><b class="cmd">::fileutil::magic::rt::R</b> <i class="arg">offset</i></a></li>
<li><a href="#19"><b class="cmd">::fileutil::magic::rt::U</b> <i class="arg">fileindex</i> <i class="arg">name</i></a></li>
<li><a href="#11"><b class="cmd">::fileutil::magic::rt::L</b> <i class="arg">newlevel</i></a></li>
<li><a href="#12"><b class="cmd">::fileutil::magic::rt::I</b> <i class="arg">offset</i> <i class="arg">it</i> <i class="arg">ioi</i> <i class="arg">ioo</i> <i class="arg">iir</i> <i class="arg">io</i></a></li>
<li><a href="#13"><b class="cmd">::fileutil::magic::rt::R</b> <i class="arg">offset</i></a></li>
<li><a href="#14"><b class="cmd">::fileutil::magic::rt::U</b> <i class="arg">fileindex</i> <i class="arg">name</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>This package provides the runtime core for file type recognition
engines written in pure Tcl and is thus used by all other packages in
this module, i.e. the two frontend packages
<b class="package">fileutil::magic::mimetypes</b> and
this module such as <b class="package"><a href="filetypes.html">fileutil::magic::filetype</a></b> and the two compiler
<b class="package">fileutil::magic::filetypes</b>, and the two engine compiler
packages <b class="package"><a href="cgen.html">fileutil::magic::cgen</a></b> and
<b class="package"><a href="cfront.html">fileutil::magic::cfront</a></b>.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">COMMANDS</a></h2>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">::fileutil::magic::rt::&gt;</b></a></dt>
<dd><p>Shorthand for <b class="cmd">incr level</b>.</p></dd>
<dd><p>Increment the level and perform related housekeeping</p></dd>
<dt><a name="2"><b class="cmd">::fileutil::magic::rt::&lt;</b></a></dt>
<dd><p>Shorthand for <b class="cmd">incr level -1</b>.</p></dd>
<dt><a name="3"><b class="cmd">::fileutil::magic::rt::open</b> <i class="arg">filename</i></a></dt>
<dd><p>This command initializes the runtime and prepares the file
<i class="arg">filename</i> for use by the system.
This command has to be invoked first, before any other command of this
package.</p>
<p>The command returns the channel handle of the opened file as its
<dd><p>Decrement the level and perform related housekeeping</p></dd>
<dt><a name="3"><b class="cmd">::fileutil::magic::rt::new</b> <i class="arg">chan</i> <i class="arg">named</i> <i class="arg">analyze</i></a></dt>
<dd><p>Create a new command which returns one description of the file each time it is
called, and a code of <i class="arg">break</i> when there are no more descriptions.
<i class="arg">chan</i> is the channel containing the data to describe.  The channel
configuration is then managed as needed.
<i class="arg">named</i> is a dictionary of named tests, as generated by
result.</p></dd>
<dt><a name="4"><b class="cmd">::fileutil::magic::rt::close</b></a></dt>
<dd><p>This command closes the last file opened via
<b class="cmd">::fileutil::magic::rt::open</b> and shuts the runtime down.
<b class="cmd">fileutil::magic::cfront::compile</b>.
<i class="arg">test</i> is a command prefix for a routine composed of the list of commands
as returned by <b class="cmd">fileutil::magic::cfront::compile</b>.</p></dd>
This command has to be invoked last, after the file has been dealt
with completely.
Afterward another invokation of <b class="cmd">::fileutil::magic::rt::open</b>  is
required to process another file.</p>
<p>This command returns the empty string as its result.</p></dd>
<dt><a name="5"><b class="cmd">::fileutil::magic::rt::file_start</b> <i class="arg">name</i></a></dt>
<dt><a name="4"><b class="cmd">::fileutil::magic::rt::file_start</b> <i class="arg">name</i></a></dt>
<dd><p>This command marks the start of a magic file when debugging. It
returns the empty string as its result.</p></dd>
<dt><a name="6"><b class="cmd">::fileutil::magic::rt::result</b> <span class="opt">?<i class="arg">msg</i>?</span></a></dt>
<dd><p>This command returns the current result and stops processing.</p>
<p>If <i class="arg">msg</i> is specified its text is added to the result before it is
returned. See <b class="cmd">::fileutil::magic::rt::emit</b> for the allowed
special character sequences.</p></dd>
<dt><a name="7"><b class="cmd">::fileutil::magic::rt::resultv</b> <span class="opt">?<i class="arg">msg</i>?</span></a></dt>
<dd><p>This command returns the current result.
In contrast to <b class="cmd">::fileutil::magic::rt::result</b> processing
continues.</p>
<p>If <i class="arg">msg</i> is specified its text is added to the result before it is
returned. See <b class="cmd">::fileutil::magic::rt::emit</b> for the allowed
special character sequences.</p></dd>
<dt><a name="8"><b class="cmd">::fileutil::magic::rt::emit</b> <i class="arg">msg</i></a></dt>
<dt><a name="5"><b class="cmd">::fileutil::magic::rt::emit</b> <i class="arg">msg</i></a></dt>
<dd><p>This command adds the text <i class="arg">msg</i> to the result buffer. The
message may contain the following special character sequences. They
will be replaced with buffered values before the message is added to
the result. The command returns the empty string as its result.</p>
<dl class="doctools_definitions">
<dt><b class="const">\b</b></dt>
<dd><p>This sequence is removed</p></dd>
<dt><b class="const">%s</b></dt>
<dd><p>Replaced with the last buffered string value.</p></dd>
<dt><b class="const">%ld</b></dt>
<dd><p>Replaced with the last buffered numeric value.</p></dd>
<dt><b class="const">%d</b></dt>
<dd><p>See above.</p></dd>
<dt><b class="const">${x:...?...}</b></dt>
<dd><p>Substitute one string if the file is executable, and
another string otherwise.</p></dd>
</dl></dd>
<dt><a name="10"><b class="cmd">::fileutil::magic::rt::Nv</b> <i class="arg">type</i> <i class="arg">offset</i> <span class="opt">?<i class="arg">qual</i>?</span></a></dt>
<dd><p>This command fetches the numeric value with <i class="arg">type</i> from the
absolute location <i class="arg">offset</i> and returns it as its result. The
<dt><a name="6"><b class="cmd">::fileutil::magic::rt::O</b> <i class="arg">where</i></a></dt>
<dd><p>Produce an offset from <i class="arg">where</i>, relative to the cursor one level up.
Produce an offset from <i class="arg">where</i>, relative to the offset one level up.</p></dd>
fetched value is further stored in the numeric buffer.</p>
<p>If <i class="arg">qual</i> is specified it is considered to be a mask and applied
<dt><a name="8"><b class="cmd">::fileutil::magic::rt::Nv</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">compinvert</i> <i class="arg">comp</i> <i class="arg">expected</i></a></dt>
to the fetched value before it is stored and returned. It has to have
the form of a partial Tcl bit-wise expression, i.e.</p>
<pre class="doctools_example">
<dd><p>A limited form of <b class="cmd">::fileutile::magic::rt::N</b> that only checks for
	&amp; number
</pre>
<p>For example:</p>
<pre class="doctools_example">
	Nv lelong 0 &amp;0x8080ffff
</pre>
<p>For the possible types see section <span class="sectref"><a href="#section3">NUMERIC TYPES</a></span>.</p></dd>
<dt><a name="11"><b class="cmd">::fileutil::magic::rt::N</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></dt>
equality and can't be told to invert the test.</p></dd>
<dt><a name="9"><b class="cmd">::fileutil::magic::rt::N</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">testinvert</i> <i class="arg">compinvert</i> <i class="arg">mod</i> <i class="arg">mand</i> <i class="arg">comp</i> <i class="arg">expected</i></a></dt>
<dd><p>This command behaves mostly like <b class="cmd">::fileutil::magic::rt::Nv</b>,
except that it compares the fetched and masked value against <i class="arg">val</i>
as specified with <i class="arg">comp</i> and returns the result of that
<dd><p>Fetch the numeric value with <i class="arg">type</i> from the absolute location
<i class="arg">offset</i>, compare it with <i class="arg">expected</i> using <i class="arg">comp</i> as the comparision
operator,  and returns the result.</p>
comparison.</p>
<p>The argument <i class="arg">comp</i> has to contain one of Tcl's comparison
operators, and the comparison made will be</p>
<p>The argument <i class="arg">comp</i> must be one of Tcl's comparison
operators.</p>
<pre class="doctools_example">
	&lt;val&gt; &lt;comp&gt; &lt;fetched-and-masked-value&gt;
	&lt;comp&gt; &lt;fetched-and-masked-value&gt; &lt;comp&gt; &lt;expected&gt;
</pre>
<p>The special comparison operator <b class="const">x</b> signals that no comparison
should be done, or, in other words, that the fetched value will always
match <i class="arg">val</i>.</p></dd>
<dt><a name="12"><b class="cmd">::fileutil::magic::rt::Nvx</b> <i class="arg">type</i> <i class="arg">offset</i> <span class="opt">?<i class="arg">qual</i>?</span></a></dt>
<dd><p>This command behaves like <b class="cmd">::fileutil::magic::rt::Nv</b>, except that
it additionally remembers the location in the file after the fetch in
the calling context, for the current level, for later use by
<b class="cmd">::fileutil::magic::rt::R</b>.</p></dd>
<dt><a name="13"><b class="cmd">::fileutil::magic::rt::Nx</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></dt>
<dd><p>This command behaves like <b class="cmd">::fileutil::magic::rt::N</b>, except that
<dt><a name="10"><b class="cmd">::fileutil::magic::rt::S</b> <i class="arg">type</i> <i class="arg">offset</i> <i class="arg">testinvert</i> <i class="arg">mod</i> <i class="arg">mand</i> <i class="arg">comp</i> <i class="arg">val</i></a></dt>
<dd><p>Like <b class="cmd">::fileutil::magic::rt::N</b> except that it fetches and compares string
it additionally remembers the location in the file after the fetch in
the calling context, for the current, for later use by
<b class="cmd">::fileutil::magic::rt::R</b>.</p></dd>
<dt><a name="14"><b class="cmd">::fileutil::magic::rt::S</b> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></dt>
<dd><p>This command behaves like <b class="cmd">::fileutil::magic::rt::N</b>, except that
it fetches and compares strings, not numeric data. The fetched value
is also stored in the internal string buffer instead of the numeric
buffer.</p></dd>
types , not numeric data.</p></dd>
<dt><a name="15"><b class="cmd">::fileutil::magic::rt::Sx</b> <i class="arg">offset</i> <i class="arg">comp</i> <i class="arg">val</i> <span class="opt">?<i class="arg">qual</i>?</span></a></dt>
<dd><p>This command behaves like <b class="cmd">::fileutil::magic::rt::S</b>, except that
it additionally remembers the location in the file after the fetch in
the calling context, for the current level, for later use by
<b class="cmd">::fileutil::magic::rt::R</b>.</p></dd>
<dt><a name="16"><b class="cmd">::fileutil::magic::rt::L</b> <i class="arg">newlevel</i></a></dt>
<dd><p>This command sets the current level in the calling context to
<dt><a name="11"><b class="cmd">::fileutil::magic::rt::L</b> <i class="arg">newlevel</i></a></dt>
<dd><p>Sets the current level in the calling context to
<i class="arg">newlevel</i>. The command returns the empty string as its result.</p></dd>
<dt><a name="17"><b class="cmd">::fileutil::magic::rt::I</b> <i class="arg">base</i> <i class="arg">type</i> <i class="arg">delta</i></a></dt>
<dt><a name="12"><b class="cmd">::fileutil::magic::rt::I</b> <i class="arg">offset</i> <i class="arg">it</i> <i class="arg">ioi</i> <i class="arg">ioo</i> <i class="arg">iir</i> <i class="arg">io</i></a></dt>
<dd><p>This command handles base locations specified indirectly through the
contents of the inspected file. It returns the sum of <i class="arg">delta</i> and
the value of numeric <i class="arg">type</i> fetched from the absolute location
<i class="arg">base</i>.</p>
<p>For the possible types see section <span class="sectref"><a href="#section3">NUMERIC TYPES</a></span>.</p></dd>
<dt><a name="18"><b class="cmd">::fileutil::magic::rt::R</b> <i class="arg">offset</i></a></dt>
<dd><p>This command handles base locations specified relative to the end of
the last field one level above.</p>
<dd><p>Calculates an offset based on an initial offset and the provided modifiers.</p></dd>
<dt><a name="13"><b class="cmd">::fileutil::magic::rt::R</b> <i class="arg">offset</i></a></dt>
<dd><p>Given an initial offset, calculates an offset relative to the cursor at the
next level up. The cursor is the position in the data one character after the
<p>In other words, the command computes an absolute location in the file
based on the relative <i class="arg">offset</i> and returns it as its result. The
base the offset is added to is the last location remembered for the
level in the calling context.</p></dd>
<dt><a name="19"><b class="cmd">::fileutil::magic::rt::U</b> <i class="arg">fileindex</i> <i class="arg">name</i></a></dt>
<dd><p>Use a named test script at the current level.</p></dd>
data extracted from the file one level up.</p></dd>
<dt><a name="14"><b class="cmd">::fileutil::magic::rt::U</b> <i class="arg">fileindex</i> <i class="arg">name</i></a></dt>
<dd><p>Add a level and use a named test script.</p></dd>
</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">NUMERIC TYPES</a></h2>
<dl class="doctools_definitions">
<dt><b class="const">byte</b></dt>
<dd><p>8-bit integer</p></dd>
<dt><b class="const">short</b></dt>

Changes to idoc/www/tcllib/files/modules/httpd/httpd.html.

1
2
3

4
5
6
7
8
9
10
1
2

3
4
5
6
7
8
9
10


-
+








<!DOCTYPE html><html><head>
<title>tool - Tcl Web Server</title>
<title>httpd - Tcl Web Server</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
	background: 	#FFFFFF;
92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
107
108
109
110
111


112
113

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

























139
140
141
142
143
144
145
146
147
148
149
150



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210




































































































211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228



229
230
231
232
233
234
235
236



237
238
239
240
241



242
243
244
245
246
247



248
249

250
251
252
253
254
255
256
257
258
259
260
261
262
263
264




265
266
267
268
269




270
271
272
273
274






275
276
277
278


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

295
296
297


298
299
300
301
302
303
304
305







306

307
308
309
310

311
312
313


314
315
316
317
318
319
320






321
322
323
324
325
326















327
328
329
330



331
332
333
334
335








336
337
338
339




340
341
342
343
344
345
346
347








348
349
350




351












352
353
354
355


356
357

358
359




360
361
362
363
364
365
366
367
368
369
370
371
372
















































373
374
375
376


377
378
379
380

381
382
383
384
385
386
387

388
389
390
391

392
393
394
395
396
397
398
399
400
401
402
403
404

405
406
407




408
409
410


411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433



434
435

436
437

438
439
440

441
442
443

444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463

464
465
466








467
468
469


470
471
472
473
474




475
476
477



478
479


480
481

482
483

















484
485
486
487
488
489
490
491
492






493
494
495






496
497
498
499
500





501
502
503
504
505



















506
507
508
509
510
511
512
513











































514
515
516

517





518
519
520
521
522




523
524


525

526
527
















528
529
530


531
532
533
534















535

















536

537

538
539
540

541
542
543
544
545
546
547


















548














549











550

551
552

553
554
555










556
557





558



559
560
561








562


563
564
565









566

567

568
569
570

571
572
573
574
575
576
577
578
579
580
581
582
583
584
585

586
587
588
589
590
591
592
593
92
93
94
95
96
97
98

99
100











101
102
103

104
105
106
107
108
109
110
111


















112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145



146
147
148



149
150
151
152
153
154
155
156
157
158















































159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273



274
275
276
277
278
279
280
281
282


283
284
285

286



287
288
289






290
291
292


293















294
295
296
297





298
299
300
301





302
303
304
305
306
307




308
309
















310
311
312

313
314
315







316
317
318
319
320
321
322
323
324
325
326
327

328
329


330
331
332
333





334
335
336
337
338
339






340
341
342
343
344
345
346
347
348
349
350
351
352
353
354




355
356
357





358
359
360
361
362
363
364
365




366
367
368
369








370
371
372
373
374
375
376
377



378
379
380
381

382
383
384
385
386
387
388
389
390
391
392
393




394
395


396

397
398
399
400
401













402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449




450
451




452







453
454



455













456



457
458
459
460



461
462























463
464
465


466


467



468



469
470



















471
472


473
474
475
476
477
478
479
480
481


482
483
484




485
486
487
488
489
490
491
492
493
494


495
496


497


498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514









515
516
517
518
519
520



521
522
523
524
525
526





527
528
529
530
531





532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550








551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595

596

597
598
599
600
601
602




603
604
605
606
607
608
609
610

611


612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628


629
630




631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665

666



667
668






669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713

714


715
716


717
718
719
720
721
722
723
724
725
726


727
728
729
730
731
732
733
734
735



736
737
738
739
740
741
742
743
744
745
746



747
748
749
750
751
752
753
754
755
756
757

758
759
760

761
762
763
764
765
766
767
768
769
770
771
772
773
774
775

776
777
778
779
780
781
782
783
784







-
+

-
-
-
-
-
-
-
-
-
-
-
+
+

-
+







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
-
-
+
+
+
-
-
-










-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+















-
-
-
+
+
+






-
-
+
+
+
-

-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+


-
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+

+



-
+

-
-
+
+


-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
-
-
+
-

+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
-
-
-
-
+
-
-
-
-
-
-
-
+

-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
+
-
-
+
-
-
-
+
-
-
-
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+

-
-
+
+
+
+
+
+
+
+

-
-
+
+

-
-
-
-
+
+
+
+



+
+
+
-
-
+
+
-
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+
-
+
+
+
+
+

-
-
-
-
+
+
+
+


+
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
+
-
-
-
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
+

-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+

+
+
+
-
-
-
+
+
+
+
+
+
+
+

+
+
-
-
-
+
+
+
+
+
+
+
+
+

+
-
+


-
+














-
+








    }
--></style>
</head>
<!-- Generated from file 'httpd.man' by tcllib/doctools with format 'html'
   -->
<!-- Copyright &amp;copy; 2018 Sean Woods &amp;lt;[email protected]&amp;gt;
   -->
<!-- tool.n
<!-- httpd.n
   -->
<body><hr> [
   <a href="../../../../../../../../home">Tcllib Home</a>
| <a href="../../../../toc.html">Main Table Of Contents</a>
| <a href="../../../toc.html">Table Of Contents</a>
| <a href="../../../../index.html">Keyword Index</a>
| <a href="../../../../toc0.html">Categories</a>
| <a href="../../../../toc1.html">Modules</a>
| <a href="../../../../toc2.html">Applications</a>
 ] <hr>
<div class="doctools">
<h1 class="doctools_title">tool(n) 4.1.1 tcllib &quot;Tcl Web Server&quot;</h1>
<body><div class="doctools">
<h1 class="doctools_title">httpd(n) 4.3 httpd &quot;Tcl Web Server&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>tool - A TclOO and coroutine based web server</p>
<p>httpd - A TclOO and coroutine based web server</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a></li>
<li class="doctools_section"><a href="#section2">Minimal Example</a></li>
<li class="doctools_section"><a href="#section3">Class ::httpd::server</a></li>
<li class="doctools_section"><a href="#section4">Class ::httpd::reply</a></li>
<li class="doctools_section"><a href="#section5">Reply Method Ensembles</a></li>
<li class="doctools_section"><a href="#section6">Reply Method Ensemble: http_info</a></li>
<li class="doctools_section"><a href="#section7">Reply Method Ensemble: request</a></li>
<li class="doctools_section"><a href="#section8">Reply Method Ensemble: reply</a></li>
<li class="doctools_section"><a href="#section9">Reply Methods</a></li>
<li class="doctools_section"><a href="#section10">Class ::httpd::content</a></li>
<li class="doctools_section"><a href="#section11">Class ::httpd::content.cgi</a></li>
<li class="doctools_section"><a href="#section12">Class ::httpd::content.file</a></li>
<li class="doctools_section"><a href="#section13">Class ::httpd::content.proxy</a></li>
<li class="doctools_section"><a href="#section14">Class ::httpd::content.scgi</a></li>
<li class="doctools_section"><a href="#section15">Class ::httpd::content.websocket</a></li>
<li class="doctools_section"><a href="#section16">SCGI Server Functions</a></li>
<li class="doctools_section"><a href="#section17">Class ::httpd::reply.scgi</a></li>
<li class="doctools_section"><a href="#section18">Class ::httpd::server.scgi</a></li>
<li class="doctools_section"><a href="#section19">AUTHORS</a></li>
<li class="doctools_section"><a href="#section20">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#section3">Classes</a>
<ul>
<li class="doctools_subsection"><a href="#subsection1">Class  httpd::mime</a></li>
<li class="doctools_subsection"><a href="#subsection2">Class  httpd::reply</a></li>
<li class="doctools_subsection"><a href="#subsection3">Class  httpd::server</a></li>
<li class="doctools_subsection"><a href="#subsection4">Class  httpd::server::dispatch</a></li>
<li class="doctools_subsection"><a href="#subsection5">Class  httpd::content.redirect</a></li>
<li class="doctools_subsection"><a href="#subsection6">Class  httpd::content.cache</a></li>
<li class="doctools_subsection"><a href="#subsection7">Class  httpd::content.template</a></li>
<li class="doctools_subsection"><a href="#subsection8">Class  httpd::content.file</a></li>
<li class="doctools_subsection"><a href="#subsection9">Class  httpd::content.exec</a></li>
<li class="doctools_subsection"><a href="#subsection10">Class  httpd::content.proxy</a></li>
<li class="doctools_subsection"><a href="#subsection11">Class  httpd::content.cgi</a></li>
<li class="doctools_subsection"><a href="#subsection12">Class  httpd::protocol.scgi</a></li>
<li class="doctools_subsection"><a href="#subsection13">Class  httpd::content.scgi</a></li>
<li class="doctools_subsection"><a href="#subsection14">Class  httpd::server.scgi</a></li>
<li class="doctools_subsection"><a href="#subsection15">Class  httpd::content.websocket</a></li>
<li class="doctools_subsection"><a href="#subsection16">Class  httpd::plugin</a></li>
<li class="doctools_subsection"><a href="#subsection17">Class  httpd::plugin.dict_dispatch</a></li>
<li class="doctools_subsection"><a href="#subsection18">Class  httpd::reply.memchan</a></li>
<li class="doctools_subsection"><a href="#subsection19">Class  httpd::plugin.local_memchan</a></li>
</ul>
</li>
<li class="doctools_section"><a href="#section4">AUTHORS</a></li>
<li class="doctools_section"><a href="#section5">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
<li class="doctools_section"><a href="#category">Category</a></li>
<li class="doctools_section"><a href="#copyright">Copyright</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl 8.6</b></li>
<li>package require <b class="pkgname">httpd <span class="opt">?4.1.1?</span></b></li>
<li>package require <b class="pkgname">sha1</b></li>
<li>package require <b class="pkgname">dicttool</b></li>
<li>package require <b class="pkgname">httpd <span class="opt">?4.3?</span></b></li>
<li>package require <b class="pkgname">uuid</b></li>
<li>package require <b class="pkgname">clay</b></li>
<li>package require <b class="pkgname">oo::meta</b></li>
<li>package require <b class="pkgname">oo::dialect</b></li>
<li>package require <b class="pkgname">tool</b></li>
<li>package require <b class="pkgname">coroutine</b></li>
<li>package require <b class="pkgname">fileutil</b></li>
<li>package require <b class="pkgname">fileutil::magic::filetype</b></li>
<li>package require <b class="pkgname">websocket</b></li>
<li>package require <b class="pkgname">mime</b></li>
<li>package require <b class="pkgname">cron</b></li>
<li>package require <b class="pkgname">uri</b></li>
<li>package require <b class="pkgname">Markdown</b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1">constructor ?port <span class="opt">?port?</span>? ?myaddr <span class="opt">?ipaddr?</span>|all? ?server_string <span class="opt">?string?</span>? ?server_name <span class="opt">?string?</span>?</a></li>
<li><a href="#2">method <b class="cmd">add_uri</b> <i class="arg">pattern</i> <i class="arg">dict</i></a></li>
<li><a href="#3">method <b class="cmd">connect</b> <i class="arg">sock</i> <i class="arg">ip</i> <i class="arg">port</i></a></li>
<li><a href="#4">method <b class="cmd">Connect</b> <i class="arg">uuid</i> <i class="arg">sock</i> <i class="arg">ip</i></a></li>
<li><a href="#5">method <b class="cmd"><a href="../counter/counter.html">counter</a></b> <i class="arg">which</i></a></li>
<li><a href="#6">method <b class="cmd">CheckTimeout</b></a></li>
<li><a href="#7">method <b class="cmd">dispatch</b> <i class="arg">header_dict</i></a></li>
<li><a href="#8">method <b class="cmd"><a href="../log/log.html">log</a></b> <i class="arg">args</i></a></li>
<li><a href="#9">method <b class="cmd">port_listening</b></a></li>
<li><a href="#10">method <b class="cmd">PrefixNormalize</b> <i class="arg">prefix</i></a></li>
<li><a href="#11">method <b class="cmd">start</b></a></li>
<li><a href="#12">method <b class="cmd">stop</b></a></li>
<li><a href="#13">method <b class="cmd">template</b> <i class="arg">page</i></a></li>
<li><a href="#14">method <b class="cmd">TemplateSearch</b> <i class="arg">page</i></a></li>
<li><a href="#15">method <b class="cmd">Validate_Connection</b> <i class="arg">sock</i> <i class="arg">ip</i></a></li>
<li><a href="#16">method <b class="cmd">ENSEMBLE::add</b> <i class="arg">field</i> <i class="arg">element</i></a></li>
<li><a href="#17">method <b class="cmd">ENSEMBLE::dump</b></a></li>
<li><a href="#18">method <b class="cmd">ENSEMBLE::get</b> <i class="arg">field</i></a></li>
<li><a href="#19">method <b class="cmd">ENSEMBLE::reset</b></a></li>
<li><a href="#20">method <b class="cmd">ENSEMBLE::remove</b> <i class="arg">field</i> <i class="arg">element</i></a></li>
<li><a href="#21">method <b class="cmd">ENSEMBLE::replace</b> <i class="arg">keyvaluelist</i></a></li>
<li><a href="#22">method <b class="cmd">ENSEMBLE::reset</b></a></li>
<li><a href="#23">method <b class="cmd">ENSEMBLE::set</b> <i class="arg">field</i> <i class="arg">value</i></a></li>
<li><a href="#24">method <b class="cmd">http_info::netstring</b></a></li>
<li><a href="#25">method <b class="cmd">request::parse</b> <i class="arg">string</i></a></li>
<li><a href="#26">method <b class="cmd">reply::output</b></a></li>
<li><a href="#27">method <b class="cmd">close</b></a></li>
<li><a href="#28">method <b class="cmd">HttpHeaders</b> <i class="arg">sock</i> <i class="arg">?debug?</i></a></li>
<li><a href="#29">method <b class="cmd">dispatch</b> <i class="arg">newsock</i> <i class="arg">datastate</i></a></li>
<li><a href="#30">method <b class="cmd"><a href="../../../../index.html#error">error</a></b> <i class="arg">code</i> <i class="arg">?message?</i> <i class="arg">?errorInfo?</i></a></li>
<li><a href="#31">method <b class="cmd">content</b></a></li>
<li><a href="#32">method <b class="cmd">EncodeStatus</b> <i class="arg">status</i></a></li>
<li><a href="#33">method FormData</a></li>
<li><a href="#34">method MimeParse <i class="arg">mimetext</i></a></li>
<li><a href="#35">method <b class="cmd">DoOutput</b></a></li>
<li><a href="#36">method PostData <i class="arg">length</i></a></li>
<li><a href="#37">method <b class="cmd">puts</b> <i class="arg">string</i></a></li>
<li><a href="#38">method <b class="cmd">reset</b></a></li>
<li><a href="#39">method <b class="cmd">timeOutCheck</b></a></li>
<li><a href="#40">method <b class="cmd"><a href="../../../../index.html#timestamp">timestamp</a></b></a></li>
<li><a href="#41">method <b class="cmd">TransferComplete</b> <i class="arg">args</i></a></li>
<li><a href="#42">method <b class="cmd">Url_Decode</b> <i class="arg">string</i></a></li>
<li><a href="#43">method cgi_info</a></li>
<li><a href="#44">option <b class="cmd">path</b></a></li>
<li><a href="#45">option <b class="cmd"><a href="../../../../index.html#prefix">prefix</a></b></a></li>
<li><a href="#46">method proxy_info</a></li>
<li><a href="#47">method scgi_info</a></li>
<li><a href="#1">method <b class="cmd">ChannelCopy</b> <i class="arg">in</i> <i class="arg">out</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#2">method <b class="cmd">html_header</b> <span class="opt">?<i class="arg">title</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#3">method <b class="cmd">html_footer</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#4">method <b class="cmd">http_code_string</b> <i class="arg">code</i></a></li>
<li><a href="#5">method <b class="cmd">HttpHeaders</b> <i class="arg">sock</i> <span class="opt">?<i class="arg">debug</i> <b class="const"></b>?</span></a></li>
<li><a href="#6">method <b class="cmd">HttpHeaders_Default</b></a></li>
<li><a href="#7">method <b class="cmd">HttpServerHeaders</b></a></li>
<li><a href="#8">method <b class="cmd">MimeParse</b> <i class="arg">mimetext</i></a></li>
<li><a href="#9">method <b class="cmd">Url_Decode</b> <i class="arg">data</i></a></li>
<li><a href="#10">method <b class="cmd">Url_PathCheck</b> <i class="arg">urlsuffix</i></a></li>
<li><a href="#11">method <b class="cmd">wait</b> <i class="arg">mode</i> <i class="arg">sock</i></a></li>
<li><a href="#12">method <b class="cmd">constructor</b> <i class="arg">ServerObj</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#13">method <b class="cmd">destructor</b> <span class="opt">?<i class="arg">dictargs</i>?</span></a></li>
<li><a href="#14">method <b class="cmd">close</b></a></li>
<li><a href="#15">method <b class="cmd">Log_Dispatched</b></a></li>
<li><a href="#16">method <b class="cmd">dispatch</b> <i class="arg">newsock</i> <i class="arg">datastate</i></a></li>
<li><a href="#17">method <b class="cmd">Dispatch</b></a></li>
<li><a href="#18">method <b class="cmd">html_css</b></a></li>
<li><a href="#19">method <b class="cmd">html_header</b> <i class="arg">title</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#20">method <b class="cmd">html_footer</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#21">method <b class="cmd">error</b> <i class="arg">code</i> <span class="opt">?<i class="arg">msg</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">errorInfo</i> <b class="const"></b>?</span></a></li>
<li><a href="#22">method <b class="cmd">content</b></a></li>
<li><a href="#23">method <b class="cmd">EncodeStatus</b> <i class="arg">status</i></a></li>
<li><a href="#24">method <b class="cmd">log</b> <i class="arg">type</i> <span class="opt">?<i class="arg">info</i> <b class="const"></b>?</span></a></li>
<li><a href="#25">method <b class="cmd">CoroName</b></a></li>
<li><a href="#26">method <b class="cmd">DoOutput</b></a></li>
<li><a href="#27">method <b class="cmd">FormData</b></a></li>
<li><a href="#28">method <b class="cmd">PostData</b> <i class="arg">length</i></a></li>
<li><a href="#29">method <b class="cmd">Session_Load</b></a></li>
<li><a href="#30">method <b class="cmd">TransferComplete</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#31">method <b class="cmd">puts</b> <i class="arg">line</i></a></li>
<li><a href="#32">method <b class="cmd">RequestFind</b> <i class="arg">field</i></a></li>
<li><a href="#33">method <b class="cmd">request</b> <i class="arg">subcommand</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#34">method <b class="cmd">reply</b> <i class="arg">subcommand</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#35">method <b class="cmd">reset</b></a></li>
<li><a href="#36">method <b class="cmd">timeOutCheck</b></a></li>
<li><a href="#37">method <b class="cmd">timestamp</b></a></li>
<li><a href="#38">method <b class="cmd">constructor</b> <i class="arg">args</i> <span class="opt">?<i class="arg">port</i> <b class="const">auto</b>?</span> <span class="opt">?<i class="arg">myaddr</i> <b class="const">127.0.0.1</b>?</span> <span class="opt">?<i class="arg">string</i> <b class="const">auto</b>?</span> <span class="opt">?<i class="arg">name</i> <b class="const">auto</b>?</span> <span class="opt">?<i class="arg">doc_root</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">reverse_dns</i> <b class="const">0</b>?</span> <span class="opt">?<i class="arg">configuration_file</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">protocol</i> <b class="const">HTTP/1.1</b>?</span></a></li>
<li><a href="#39">method <b class="cmd">destructor</b> <span class="opt">?<i class="arg">dictargs</i>?</span></a></li>
<li><a href="#40">method <b class="cmd">connect</b> <i class="arg">sock</i> <i class="arg">ip</i> <i class="arg">port</i></a></li>
<li><a href="#41">method <b class="cmd">ServerHeaders</b> <i class="arg">ip</i> <i class="arg">http_request</i> <i class="arg">mimetxt</i></a></li>
<li><a href="#42">method <b class="cmd">Connect</b> <i class="arg">uuid</i> <i class="arg">sock</i> <i class="arg">ip</i></a></li>
<li><a href="#43">method <b class="cmd">counter</b> <i class="arg">which</i></a></li>
<li><a href="#44">method <b class="cmd">CheckTimeout</b></a></li>
<li><a href="#45">method <b class="cmd">debug</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#46">method <b class="cmd">dispatch</b> <i class="arg">data</i></a></li>
<li><a href="#47">method <b class="cmd">Dispatch_Default</b> <i class="arg">reply</i></a></li>
<li><a href="#48">method <b class="cmd">Dispatch_Local</b> <i class="arg">data</i></a></li>
<li><a href="#49">method <b class="cmd">Headers_Local</b> <i class="arg">varname</i></a></li>
<li><a href="#50">method <b class="cmd">Headers_Process</b> <i class="arg">varname</i></a></li>
<li><a href="#51">method <b class="cmd">HostName</b> <i class="arg">ipaddr</i></a></li>
<li><a href="#52">method <b class="cmd">log</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#53">method <b class="cmd">plugin</b> <i class="arg">slot</i> <span class="opt">?<i class="arg">class</i> <b class="const"></b>?</span></a></li>
<li><a href="#54">method <b class="cmd">port_listening</b></a></li>
<li><a href="#55">method <b class="cmd">PrefixNormalize</b> <i class="arg">prefix</i></a></li>
<li><a href="#56">method <b class="cmd">source</b> <i class="arg">filename</i></a></li>
<li><a href="#57">method <b class="cmd">start</b></a></li>
<li><a href="#58">method <b class="cmd">stop</b></a></li>
<li><a href="#59">method <b class="cmd">SubObject {} db</b></a></li>
<li><a href="#60">method <b class="cmd">SubObject {} default</b></a></li>
<li><a href="#61">method <b class="cmd">template</b> <i class="arg">page</i></a></li>
<li><a href="#62">method <b class="cmd">TemplateSearch</b> <i class="arg">page</i></a></li>
<li><a href="#63">method <b class="cmd">Thread_start</b></a></li>
<li><a href="#64">method <b class="cmd">Uuid_Generate</b></a></li>
<li><a href="#65">method <b class="cmd">Validate_Connection</b> <i class="arg">sock</i> <i class="arg">ip</i></a></li>
<li><a href="#66">method <b class="cmd">reset</b></a></li>
<li><a href="#67">method <b class="cmd">content</b></a></li>
<li><a href="#68">method <b class="cmd">Dispatch</b></a></li>
<li><a href="#69">method <b class="cmd">content</b></a></li>
<li><a href="#70">method <b class="cmd">FileName</b></a></li>
<li><a href="#71">method <b class="cmd">DirectoryListing</b> <i class="arg">local_file</i></a></li>
<li><a href="#72">method <b class="cmd">content</b></a></li>
<li><a href="#73">method <b class="cmd">Dispatch</b></a></li>
<li><a href="#74">method <b class="cmd">CgiExec</b> <i class="arg">execname</i> <i class="arg">script</i> <i class="arg">arglist</i></a></li>
<li><a href="#75">method <b class="cmd">Cgi_Executable</b> <i class="arg">script</i></a></li>
<li><a href="#76">method <b class="cmd">proxy_channel</b></a></li>
<li><a href="#77">method <b class="cmd">proxy_path</b></a></li>
<li><a href="#78">method <b class="cmd">ProxyRequest</b> <i class="arg">chana</i> <i class="arg">chanb</i></a></li>
<li><a href="#79">method <b class="cmd">ProxyReply</b> <i class="arg">chana</i> <i class="arg">chanb</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#80">method <b class="cmd">Dispatch</b></a></li>
<li><a href="#81">method <b class="cmd">FileName</b></a></li>
<li><a href="#82">method <b class="cmd">proxy_channel</b></a></li>
<li><a href="#83">method <b class="cmd">ProxyRequest</b> <i class="arg">chana</i> <i class="arg">chanb</i></a></li>
<li><a href="#84">method <b class="cmd">ProxyReply</b> <i class="arg">chana</i> <i class="arg">chanb</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#85">method <b class="cmd">DirectoryListing</b> <i class="arg">local_file</i></a></li>
<li><a href="#86">method <b class="cmd">EncodeStatus</b> <i class="arg">status</i></a></li>
<li><a href="#87">method <b class="cmd">scgi_info</b></a></li>
<li><a href="#88">method <b class="cmd">proxy_channel</b></a></li>
<li><a href="#89">method <b class="cmd">ProxyRequest</b> <i class="arg">chana</i> <i class="arg">chanb</i></a></li>
<li><a href="#90">method <b class="cmd">ProxyReply</b> <i class="arg">chana</i> <i class="arg">chanb</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#91">method <b class="cmd">debug</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#92">method <b class="cmd">Connect</b> <i class="arg">uuid</i> <i class="arg">sock</i> <i class="arg">ip</i></a></li>
<li><a href="#93">method <b class="cmd">Dispatch_Dict</b> <i class="arg">data</i></a></li>
<li><a href="#94">method <b class="cmd">uri {} add</b> <i class="arg">vhosts</i> <i class="arg">patterns</i> <i class="arg">info</i></a></li>
<li><a href="#95">method <b class="cmd">uri {} direct</b> <i class="arg">vhosts</i> <i class="arg">patterns</i> <i class="arg">info</i> <i class="arg">body</i></a></li>
<li><a href="#96">method <b class="cmd">output</b></a></li>
<li><a href="#97">method <b class="cmd">DoOutput</b></a></li>
<li><a href="#98">method <b class="cmd">close</b></a></li>
<li><a href="#99">method <b class="cmd">local_memchan</b> <i class="arg">command</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#100">method <b class="cmd">Connect_Local</b> <i class="arg">uuid</i> <i class="arg">sock</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>This module implements a web server, suitable for embedding in an
application. The server is object oriented, and contains all of the
fundamentals needed for a full service website.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">Minimal Example</a></h2>
<p>Starting a web service requires starting a class of type
<b class="cmd">httpd::server</b>, and providing that server with one or more URIs
to service, and <b class="cmd">httpd::reply</b> derived classes to generate them.</p>
<pre class="doctools_example">
tool::define ::reply.hello {
  method content {} {
    my puts &quot;&lt;HTML&gt;&lt;HEAD&gt;&lt;TITLE&gt;IRM Dispatch Server&lt;/TITLE&gt;&lt;/HEAD&gt;&lt;BODY&gt;&quot;
    my puts &quot;&lt;h1&gt;Hello World!&lt;/h1&gt;&quot;
    my puts &lt;/BODY&gt;&lt;/HTML&gt;
my puts &quot;&lt;HTML&gt;&lt;HEAD&gt;&lt;TITLE&gt;IRM Dispatch Server&lt;/TITLE&gt;&lt;/HEAD&gt;&lt;BODY&gt;&quot;
my puts &quot;&lt;h1&gt;Hello World!&lt;/h1&gt;&quot;
my puts &lt;/BODY&gt;&lt;/HTML&gt;
  }
}
::docserver::server create HTTPD port 8015 myaddr 127.0.0.1
HTTPD add_uri /* [list mixin reply.hello]
</pre>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">Class ::httpd::server</a></h2>
<p>This class is the root object of the webserver. It is responsible
<div id="section3" class="doctools_section"><h2><a name="section3">Classes</a></h2>
<div id="subsection1" class="doctools_subsection"><h3><a name="subsection1">Class  httpd::mime</a></h3>
<p><b class="class">Methods</b></p>
for opening the socket and providing the initial connection negotiation.</p>
<dl class="doctools_definitions">
<dt><a name="1">constructor ?port <span class="opt">?port?</span>? ?myaddr <span class="opt">?ipaddr?</span>|all? ?server_string <span class="opt">?string?</span>? ?server_name <span class="opt">?string?</span>?</a></dt>
<dd><p>Build a new server object. <span class="opt">?port?</span> is the port to listen on</p></dd>
<dt><a name="2">method <b class="cmd">add_uri</b> <i class="arg">pattern</i> <i class="arg">dict</i></a></dt>
<dt><a name="1">method <b class="cmd">ChannelCopy</b> <i class="arg">in</i> <i class="arg">out</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="2">method <b class="cmd">html_header</b> <span class="opt">?<i class="arg">title</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Set the hander for a URI pattern. Information given in the <i class="arg">dict</i> is stored
in the data structure the <b class="cmd">dispatch</b> method uses. If a field called
<i class="arg">mixin</i> is given, that class will be mixed into the reply object immediately
after construction.</p></dd>
<dt><a name="3">method <b class="cmd">connect</b> <i class="arg">sock</i> <i class="arg">ip</i> <i class="arg">port</i></a></dt>
<dd><p>Reply to an open socket. This method builds a coroutine to manage the remainder
<dd></dd>
<dt><a name="3">method <b class="cmd">html_footer</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
of the connection. The coroutine's operations are driven by the <b class="cmd">Connect</b> method.</p></dd>
<dt><a name="4">method <b class="cmd">Connect</b> <i class="arg">uuid</i> <i class="arg">sock</i> <i class="arg">ip</i></a></dt>
<dt><a name="4">method <b class="cmd">http_code_string</b> <i class="arg">code</i></a></dt>
<dd><p>This method reads HTTP headers, and then consults the <b class="cmd">dispatch</b> method to
determine if the request is valid, and/or what kind of reply to generate. Under
normal cases, an object of class <b class="cmd">::http::reply</b> is created.
Fields the server are looking for in particular are:
class: A class to use instead of the server's own <i class="arg">reply_class</i>
mixin: A class to be mixed into the new object after construction.
All other fields are passed along to the <b class="cmd">http_info</b> structure of the
reply object.
After the class is created and the mixin is mixed in, the server invokes the
reply objects <b class="cmd">dispatch</b> method. This action passes control of the socket to
the reply object. The reply object manages the rest of the transaction, including
closing the socket.</p></dd>
<dt><a name="5">method <b class="cmd"><a href="../counter/counter.html">counter</a></b> <i class="arg">which</i></a></dt>
<dd><p>Increment an internal counter.</p></dd>
<dt><a name="6">method <b class="cmd">CheckTimeout</b></a></dt>
<dd></dd>
<dt><a name="5">method <b class="cmd">HttpHeaders</b> <i class="arg">sock</i> <span class="opt">?<i class="arg">debug</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="6">method <b class="cmd">HttpHeaders_Default</b></a></dt>
<dd><p>Check open connections for a time out event.</p></dd>
<dt><a name="7">method <b class="cmd">dispatch</b> <i class="arg">header_dict</i></a></dt>
<dd><p>Given a key/value list of information, return a data structure describing how
the server should reply.</p></dd>
<dt><a name="8">method <b class="cmd"><a href="../log/log.html">log</a></b> <i class="arg">args</i></a></dt>
<dd></dd>
<dt><a name="7">method <b class="cmd">HttpServerHeaders</b></a></dt>
<dd></dd>
<dt><a name="8">method <b class="cmd">MimeParse</b> <i class="arg">mimetext</i></a></dt>
<dd><p>Log an event. The input for args is free form. This method is intended
to be replaced by the user, and is a noop for a stock http::server object.</p></dd>
<dt><a name="9">method <b class="cmd">port_listening</b></a></dt>
<dd><p>Return the actual port that httpd is listening on.</p></dd>
<dt><a name="10">method <b class="cmd">PrefixNormalize</b> <i class="arg">prefix</i></a></dt>
<dd><p>Converts a block of mime encoded text to a key/value list. If an exception is encountered,
 the method will generate its own call to the <b class="cmd">error</b> method, and immediately invoke
 the <b class="cmd">output</b> method to produce an error code and close the connection.</p></dd>
<dt><a name="9">method <b class="cmd">Url_Decode</b> <i class="arg">data</i></a></dt>
<dd><p>De-httpizes a string.</p></dd>
<dt><a name="10">method <b class="cmd">Url_PathCheck</b> <i class="arg">urlsuffix</i></a></dt>
<dd><p>For the stock version, trim trailing /'s and *'s from a prefix. This
method can be replaced by the end user to perform any other transformations
needed for the application.</p></dd>
<dt><a name="11">method <b class="cmd">start</b></a></dt>
<dd></dd>
<dt><a name="11">method <b class="cmd">wait</b> <i class="arg">mode</i> <i class="arg">sock</i></a></dt>
<dd><p>Open the socket listener.</p></dd>
<dt><a name="12">method <b class="cmd">stop</b></a></dt>
<dd><p>Shut off the socket listener, and destroy any pending replies.</p></dd>
<dt><a name="13">method <b class="cmd">template</b> <i class="arg">page</i></a></dt>
<dd><p>Return a template for the string <i class="arg">page</i></p></dd>
<dt><a name="14">method <b class="cmd">TemplateSearch</b> <i class="arg">page</i></a></dt>
<dd><p>Perform a search for the template that best matches <i class="arg">page</i>. This
can include local file searches, in-memory structures, or even
database lookups. The stock implementation simply looks for files
with a .tml or .html extension in the <span class="opt">?doc_root?</span> directory.</p></dd>
<dt><a name="15">method <b class="cmd">Validate_Connection</b> <i class="arg">sock</i> <i class="arg">ip</i></a></dt>
<dd><p>Given a socket and an ip address, return true if this connection should
be terminated, or false if it should be allowed to continue. The stock
implementation always returns 0. This is intended for applications to
be able to implement black lists and/or provide security based on IP
address.</p></dd>
<dd></dd>
</dl>
</div>
<div id="section4" class="doctools_section"><h2><a name="section4">Class ::httpd::reply</a></h2>
<div id="subsection2" class="doctools_subsection"><h3><a name="subsection2">Class  httpd::reply</a></h3>
<p><em>ancestors</em>: <b class="class">httpd::mime</b></p>
<p>A class which shephards a request through the process of generating a
reply.
The socket associated with the reply is available at all times as the <i class="arg">chan</i>
variable.
The process of generating a reply begins with an <b class="cmd">httpd::server</b> generating a
<b class="cmd">http::class</b> object, mixing in a set of behaviors and then invoking the reply
object's <b class="cmd">dispatch</b> method.
In normal operations the <b class="cmd">dispatch</b> method:</p>
 reply.
 The socket associated with the reply is available at all times as the <i class="arg">chan</i>
 variable.
 The process of generating a reply begins with an <b class="cmd">httpd::server</b> generating a
 <b class="cmd">http::class</b> object, mixing in a set of behaviors and then invoking the reply
 object's <b class="cmd">dispatch</b> method.
 In normal operations the <b class="cmd">dispatch</b> method:</p>
<ol class="doctools_enumerated">
 
<li><p>Invokes the <b class="cmd">reset</b> method for the object to populate default headers.</p></li>
<li><p>Invokes the <b class="cmd">HttpHeaders</b> method to stream the MIME headers out of the socket</p></li>
<li><p>Invokes the <b class="cmd">request parse</b> method to convert the stream of MIME headers into a
dict that can be read via the <b class="cmd">request</b> method.</p></li>
 dict that can be read via the <b class="cmd">request</b> method.</p></li>
<li><p>Stores the raw stream of MIME headers in the <i class="arg">rawrequest</i> variable of the object.</p></li>
<li><p>Invokes the <b class="cmd">content</b> method for the object, generating an call to the <b class="cmd"><a href="../../../../index.html#error">error</a></b>
method if an exception is raised.</p></li>
<li><p>Invokes the <b class="cmd">content</b> method for the object, generating an call to the <b class="cmd">error</b>
 method if an exception is raised.</p></li>
<li><p>Invokes the <b class="cmd">output</b> method for the object</p></li>
</ol>
</div>
<div id="section5" class="doctools_section"><h2><a name="section5">Reply Method Ensembles</a></h2>
<p>The <b class="cmd">http::reply</b> class and its derivatives maintain several variables as dictionaries
internally. Access to these dictionaries is managed through a dedicated ensemble. The
ensemble implements most of the same behaviors as the <b class="cmd"><a href="../../../../index.html#dict">dict</a></b> command.
<p>Developers have the option of streaming output to a buffer via the <b class="cmd">puts</b> method of the
 reply, or simply populating the <i class="arg">reply_body</i> variable of the object.
 The information returned by the <b class="cmd">content</b> method is not interpreted in any way.
 If an exception is thrown (via the <b class="cmd">error</b> command in Tcl, for example) the caller will
 auto-generate a 500 {Internal Error} message.
 A typical implementation of <b class="cmd">content</b> look like:</p>
Each ensemble implements the following methods above, beyond, or modifying standard dicts:</p>
<dl class="doctools_definitions">
<dt><a name="16">method <b class="cmd">ENSEMBLE::add</b> <i class="arg">field</i> <i class="arg">element</i></a></dt>
<dd><p>Add <i class="arg">element</i> to a list stored in <i class="arg">field</i>, but only if it is not already present om the list.</p></dd>
<dt><a name="17">method <b class="cmd">ENSEMBLE::dump</b></a></dt>
<dd><p>Return the current contents of the data structure as a key/value list.</p></dd>
<pre class="doctools_example">
 clay::define ::test::content.file {
 	superclass ::httpd::content.file
 	# Return a file
 	# Note: this is using the content.file mixin which looks for the reply_file variable
 	# and will auto-compute the Content-Type
 	method content {} {
 	  my reset
     set doc_root [my request get DOCUMENT_ROOT]
     my variable reply_file
     set reply_file [file join $doc_root index.html]
 	}
 }
 clay::define ::test::content.time {
   # return the current system time
<dt><a name="18">method <b class="cmd">ENSEMBLE::get</b> <i class="arg">field</i></a></dt>
<dd><p>Return the value of the field <i class="arg">field</i>, or an empty string if it does not exist.</p></dd>
<dt><a name="19">method <b class="cmd">ENSEMBLE::reset</b></a></dt>
<dd><p>Return a key/value list of the default contents for this data structure.</p></dd>
 	method content {} {
 		my variable reply_body
     my reply set Content-Type text/plain
<dt><a name="20">method <b class="cmd">ENSEMBLE::remove</b> <i class="arg">field</i> <i class="arg">element</i></a></dt>
<dd><p>Remove all instances of <i class="arg">element</i> from the list stored in <i class="arg">field</i>.</p></dd>
<dt><a name="21">method <b class="cmd">ENSEMBLE::replace</b> <i class="arg">keyvaluelist</i></a></dt>
<dd><p>Replace the internal dict with the contents of <i class="arg">keyvaluelist</i></p></dd>
<dt><a name="22">method <b class="cmd">ENSEMBLE::reset</b></a></dt>
 		set reply_body [clock seconds]
 	}
 }
 clay::define ::test::content.echo {
 	method content {} {
 		my variable reply_body
     my reply set Content-Type [my request get CONTENT_TYPE]
 		set reply_body [my PostData [my request get CONTENT_LENGTH]]
<dd><p>Replace the internal dict with the default state.</p></dd>
<dt><a name="23">method <b class="cmd">ENSEMBLE::set</b> <i class="arg">field</i> <i class="arg">value</i></a></dt>
<dd><p>Set the value of <i class="arg">field</i> to <i class="arg">value</i>.</p></dd>
</dl>
 	}
 }
 clay::define ::test::content.form_handler {
 	method content {} {
</div>
<div id="section6" class="doctools_section"><h2><a name="section6">Reply Method Ensemble: http_info</a></h2>
<p>Manages HTTP headers passed in by the server.
Ensemble Methods:</p>
<dl class="doctools_definitions">
<dt><a name="24">method <b class="cmd">http_info::netstring</b></a></dt>
<dd><p>Return the contents of this data structure as a netstring encoded block.</p></dd>
</dl>
 	  set form [my FormData]
 	  my reply set Content-Type {text/html; charset=UTF-8}
     my puts [my html_header {My Dynamic Page}]
     my puts &quot;&lt;BODY&gt;&quot;
     my puts &quot;You Sent&lt;p&gt;&quot;
     my puts &quot;&lt;TABLE&gt;&quot;
     foreach {f v} $form {
       my puts &quot;&lt;TR&gt;&lt;TH&gt;$f&lt;/TH&gt;&lt;TD&gt;&lt;verbatim&gt;$v&lt;/verbatim&gt;&lt;/TD&gt;&quot;
</div>
<div id="section7" class="doctools_section"><h2><a name="section7">Reply Method Ensemble: request</a></h2>
<p>Managed data from MIME headers of the request.</p>
     }
     my puts &quot;&lt;/TABLE&gt;&lt;p&gt;&quot;
     my puts &quot;Send some info:&lt;p&gt;&quot;
     my puts &quot;&lt;FORM action=/[my request get REQUEST_PATH] method POST&gt;&quot;
<dl class="doctools_definitions">
     my puts &quot;&lt;TABLE&gt;&quot;
     foreach field {name rank serial_number} {
       set line &quot;&lt;TR&gt;&lt;TH&gt;$field&lt;/TH&gt;&lt;TD&gt;&lt;input name=\&quot;$field\&quot; &quot;
       if {[dict exists $form $field]} {
         append line &quot; value=\&quot;[dict get $form $field]\&quot;&quot;&quot;
       }
       append line &quot; /&gt;&lt;/TD&gt;&lt;/TR&gt;&quot;
       my puts $line
     }
     my puts &quot;&lt;/TABLE&gt;&quot;
     my puts [my html footer]
 	}
<dt><a name="25">method <b class="cmd">request::parse</b> <i class="arg">string</i></a></dt>
<dd><p>Replace the contents of the data structure with information encoded in a MIME
formatted block of text (<i class="arg">string</i>).</p></dd>
</dl>
 }
 </pre>
</div>
<div id="section8" class="doctools_section"><h2><a name="section8">Reply Method Ensemble: reply</a></h2>
<p><b class="class">Methods</b></p>
<p>Manage the headers sent in the reply.</p>
<dl class="doctools_definitions">
<dt><a name="12">method <b class="cmd">constructor</b> <i class="arg">ServerObj</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="13">method <b class="cmd">destructor</b> <span class="opt">?<i class="arg">dictargs</i>?</span></a></dt>
<dd><p>clean up on exit</p></dd>
<dt><a name="26">method <b class="cmd">reply::output</b></a></dt>
<dd><p>Return the contents of this data structure as a MIME encoded block appropriate
for an HTTP response.</p></dd>
</dl>
</div>
<div id="section9" class="doctools_section"><h2><a name="section9">Reply Methods</a></h2>
<dl class="doctools_definitions">
<dt><a name="27">method <b class="cmd">close</b></a></dt>
<dd><p>Terminate the transaction, and close the socket.</p></dd>
<dt><a name="28">method <b class="cmd">HttpHeaders</b> <i class="arg">sock</i> <i class="arg">?debug?</i></a></dt>
<dd><p>Stream MIME headers from the socket <i class="arg">sock</i>, stopping at an empty line. Returns
the stream as a block of text.</p></dd>
<dt><a name="29">method <b class="cmd">dispatch</b> <i class="arg">newsock</i> <i class="arg">datastate</i></a></dt>
<dt><a name="14">method <b class="cmd">close</b></a></dt>
<dd><p>Close channels opened by this object</p></dd>
<dt><a name="15">method <b class="cmd">Log_Dispatched</b></a></dt>
<dd><p>Record a dispatch event</p></dd>
<dt><a name="16">method <b class="cmd">dispatch</b> <i class="arg">newsock</i> <i class="arg">datastate</i></a></dt>
<dd><p>Accept the handoff from the server object of the socket
 <em>newsock</em> and feed it the state <em>datastate</em>.
 Fields the <em>datastate</em> are looking for in particular are:</p>
<p>* <b class="const">mixin</b> - A key/value list of slots and classes to be mixed into the
 object prior to invoking <b class="cmd">Dispatch</b>.</p>
<p>* <b class="const">http</b> - A key/value list of values to populate the object's <em>request</em>
 ensemble</p>
<p>All other fields are passed along to the <b class="method">clay</b> structure of the object.</p></dd>
<dt><a name="17">method <b class="cmd">Dispatch</b></a></dt>
<dd></dd>
<dt><a name="18">method <b class="cmd">html_css</b></a></dt>
<dd></dd>
<dt><a name="19">method <b class="cmd">html_header</b> <i class="arg">title</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="20">method <b class="cmd">html_footer</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="21">method <b class="cmd">error</b> <i class="arg">code</i> <span class="opt">?<i class="arg">msg</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">errorInfo</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="22">method <b class="cmd">content</b></a></dt>
<dd><p>REPLACE ME:
 This method is the &quot;meat&quot; of your application.
 It writes to the result buffer via the &quot;puts&quot; method
 and can tweak the headers via &quot;clay put header_reply&quot;</p></dd>
<dt><a name="23">method <b class="cmd">EncodeStatus</b> <i class="arg">status</i></a></dt>
<dd><p>Formulate a standard HTTP status header from he string provided.</p></dd>
<dt><a name="24">method <b class="cmd">log</b> <i class="arg">type</i> <span class="opt">?<i class="arg">info</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="25">method <b class="cmd">CoroName</b></a></dt>
<dd></dd>
<dt><a name="26">method <b class="cmd">DoOutput</b></a></dt>
<dd><p>Generates the the HTTP reply, streams that reply back across <i class="arg">chan</i>,
 and destroys the object.</p></dd>
<dt><a name="27">method <b class="cmd">FormData</b></a></dt>
<dd><p>For GET requests, converts the QUERY_DATA header into a key/value list.
 For POST requests, reads the Post data and converts that information to
 a key/value list for application/x-www-form-urlencoded posts. For multipart
 posts, it composites all of the MIME headers of the post to a singular key/value
 list, and provides MIME_* information as computed by the <b class="cmd">mime</b> package, including
 the MIME_TOKEN, which can be fed back into the mime package to read out the contents.</p></dd>
<dt><a name="28">method <b class="cmd">PostData</b> <i class="arg">length</i></a></dt>
<dd><p>Stream <i class="arg">length</i> bytes from the <i class="arg">chan</i> socket, but only of the request is a
 POST or PUSH. Returns an empty string otherwise.</p></dd>
<dt><a name="29">method <b class="cmd">Session_Load</b></a></dt>
<dd><p>Take over control of the socket <i class="arg">newsock</i>, and store that as the <i class="arg">chan</i> variable
for the object. This method runs through all of the steps of reading HTTP headers, generating
content, and closing the connection. (See class writetup).</p></dd>
<dt><a name="30">method <b class="cmd"><a href="../../../../index.html#error">error</a></b> <i class="arg">code</i> <i class="arg">?message?</i> <i class="arg">?errorInfo?</i></a></dt>
<dd><p>Manage session data</p></dd>
<dt><a name="30">method <b class="cmd">TransferComplete</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Generate an error message of the specified <i class="arg">code</i>, and display the <i class="arg">message</i> as the
reason for the exception. <i class="arg">errorInfo</i> is passed in from calls, but how or if it should be
displayed is a prerogative of the developer.</p></dd>
<dt><a name="31">method <b class="cmd">content</b></a></dt>
<dd><p>Intended to be invoked from <b class="cmd">chan copy</b> as a callback. This closes every channel
<dd><p>Generate the content for the reply. This method is intended to be replaced by the mixin.
Developers have the option of streaming output to a buffer via the <b class="cmd">puts</b> method of the
reply, or simply populating the <i class="arg">reply_body</i> variable of the object.
The information returned by the <b class="cmd">content</b> method is not interpreted in any way.
If an exception is thrown (via the <b class="cmd"><a href="../../../../index.html#error">error</a></b> command in Tcl, for example) the caller will
auto-generate a 500 {Internal Error} message.
A typical implementation of <b class="cmd">content</b> look like:</p>
 fed to it on the command line, and then destroys the object.</p>
<pre class="doctools_example">
tool::define ::test::content.file {
	superclass ::httpd::content.file
	# Return a file
     ###
	# Note: this is using the content.file mixin which looks for the reply_file variable
	# and will auto-compute the Content-Type
	method content {} {
	  my reset
    set doc_root [my http_info get doc_root]
    my variable reply_file
    set reply_file [file join $doc_root index.html]
	}
}
tool::define ::test::content.time {
  # return the current system time
	method content {} {
		my variable reply_body
     # Output the body
    my reply set Content-Type text/plain
		set reply_body [clock seconds]
	}
     ###
     chan configure $sock -translation binary -blocking 0 -buffering full -buffersize 4096
     chan configure $chan -translation binary -blocking 0 -buffering full -buffersize 4096
     if {$length} {
}
tool::define ::test::content.echo {
	method content {} {
       ###
       # Send any POST/PUT/etc content
		my variable reply_body
    my reply set Content-Type [my request get CONTENT_TYPE]
		set reply_body [my PostData [my request get CONTENT_LENGTH]]
	}
}
tool::define ::test::content.form_handler {
	method content {} {
	  set form [my FormData]
	  my reply set Content-Type {text/html; charset=UTF-8}
    my puts [my html header {My Dynamic Page}]
    my puts &quot;&lt;BODY&gt;&quot;
    my puts &quot;You Sent&lt;p&gt;&quot;
    my puts &quot;&lt;TABLE&gt;&quot;
    foreach {f v} $form {
      my puts &quot;&lt;TR&gt;&lt;TH&gt;$f&lt;/TH&gt;&lt;TD&gt;&lt;verbatim&gt;$v&lt;/verbatim&gt;&lt;/TD&gt;&quot;
    }
    my puts &quot;&lt;/TABLE&gt;&lt;p&gt;&quot;
    my puts &quot;Send some info:&lt;p&gt;&quot;
    my puts &quot;&lt;FORM action=/[my http_info get REQUEST_PATH] method POST&gt;&quot;
    my puts &quot;&lt;TABLE&gt;&quot;
    foreach field {name rank serial_number} {
      set line &quot;&lt;TR&gt;&lt;TH&gt;$field&lt;/TH&gt;&lt;TD&gt;&lt;input name=\&quot;$field\&quot; &quot;
      if {[dict exists $form $field]} {
       ###
       chan copy $sock $chan -size $SIZE -command [info coroutine]
       yield
        append line &quot; value=\&quot;[dict get $form $field]\&quot;&quot;&quot;
      }
     }
      append line &quot; /&gt;&lt;/TD&gt;&lt;/TR&gt;&quot;
      my puts $line
     catch {close $sock}
    }
    my puts &quot;&lt;/TABLE&gt;&quot;
    my puts [my html footer]
     chan flush $chan
	}
}
</pre>
 </pre>
</dd>
<dt><a name="32">method <b class="cmd">EncodeStatus</b> <i class="arg">status</i></a></dt>
<dd><p>Formulate a standard HTTP status header from he string provided.</p></dd>
<dt><a name="33">method FormData</a></dt>
<dd><p>For GET requests, converts the QUERY_DATA header into a key/value list.
For POST requests, reads the Post data and converts that information to
a key/value list for application/x-www-form-urlencoded posts. For multipart
posts, it composites all of the MIME headers of the post to a singular key/value
list, and provides MIME_* information as computed by the <b class="cmd"><a href="../mime/mime.html">mime</a></b> package, including
the MIME_TOKEN, which can be fed back into the mime package to read out the contents.</p></dd>
<dt><a name="34">method MimeParse <i class="arg">mimetext</i></a></dt>
<dd><p>Converts a block of mime encoded text to a key/value list. If an exception is encountered,
the method will generate its own call to the <b class="cmd"><a href="../../../../index.html#error">error</a></b> method, and immediately invoke
the <b class="cmd">output</b> method to produce an error code and close the connection.</p></dd>
<dt><a name="35">method <b class="cmd">DoOutput</b></a></dt>
<dd><p>Generates the the HTTP reply, and streams that reply back across <i class="arg">chan</i>.</p></dd>
<dt><a name="36">method PostData <i class="arg">length</i></a></dt>
<dd><p>Stream <i class="arg">length</i> bytes from the <i class="arg">chan</i> socket, but only of the request is a
POST or PUSH. Returns an empty string otherwise.</p></dd>
<dt><a name="37">method <b class="cmd">puts</b> <i class="arg">string</i></a></dt>
<dt><a name="31">method <b class="cmd">puts</b> <i class="arg">line</i></a></dt>
<dd><p>Appends the value of <i class="arg">string</i> to the end of <i class="arg">reply_body</i>, as well as a trailing newline
character.</p></dd>
<dt><a name="38">method <b class="cmd">reset</b></a></dt>
 character.</p></dd>
<dt><a name="32">method <b class="cmd">RequestFind</b> <i class="arg">field</i></a></dt>
<dd></dd>
<dt><a name="33">method <b class="cmd">request</b> <i class="arg">subcommand</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="34">method <b class="cmd">reply</b> <i class="arg">subcommand</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="35">method <b class="cmd">reset</b></a></dt>
<dd><p>Clear the contents of the <i class="arg">reply_body</i> variable, and reset all headers in the <b class="cmd">reply</b>
structure back to the defaults for this object.</p></dd>
<dt><a name="39">method <b class="cmd">timeOutCheck</b></a></dt>
 structure back to the defaults for this object.</p></dd>
<dt><a name="36">method <b class="cmd">timeOutCheck</b></a></dt>
<dd><p>Called from the <b class="cmd">http::server</b> object which spawned this reply. Checks to see
if too much time has elapsed while waiting for data or generating a reply, and issues
a timeout error to the request if it has, as well as destroy the object and close the
<i class="arg">chan</i> socket.</p></dd>
<dt><a name="40">method <b class="cmd"><a href="../../../../index.html#timestamp">timestamp</a></b></a></dt>
 if too much time has elapsed while waiting for data or generating a reply, and issues
 a timeout error to the request if it has, as well as destroy the object and close the
 <i class="arg">chan</i> socket.</p></dd>
<dt><a name="37">method <b class="cmd">timestamp</b></a></dt>
<dd><p>Return the current system time in the format:</p>
<pre class="doctools_example">%a, %d %b %Y %T %Z</pre>
</dd>
</dl>
</div>
<div id="subsection3" class="doctools_subsection"><h3><a name="subsection3">Class  httpd::server</a></h3>
<dt><a name="41">method <b class="cmd">TransferComplete</b> <i class="arg">args</i></a></dt>
<dd><p>Intended to be invoked from <b class="cmd">chan copy</b> as a callback. This closes every channel
<p><em>ancestors</em>: <b class="class">httpd::mime</b></p>
<p><b class="class">Methods</b></p>
fed to it on the command line, and then destroys the object.</p>
<pre class="doctools_example">
<dl class="doctools_definitions">
    ###
    # Output the body
<dt><a name="38">method <b class="cmd">constructor</b> <i class="arg">args</i> <span class="opt">?<i class="arg">port</i> <b class="const">auto</b>?</span> <span class="opt">?<i class="arg">myaddr</i> <b class="const">127.0.0.1</b>?</span> <span class="opt">?<i class="arg">string</i> <b class="const">auto</b>?</span> <span class="opt">?<i class="arg">name</i> <b class="const">auto</b>?</span> <span class="opt">?<i class="arg">doc_root</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">reverse_dns</i> <b class="const">0</b>?</span> <span class="opt">?<i class="arg">configuration_file</i> <b class="const"></b>?</span> <span class="opt">?<i class="arg">protocol</i> <b class="const">HTTP/1.1</b>?</span></a></dt>
<dd></dd>
<dt><a name="39">method <b class="cmd">destructor</b> <span class="opt">?<i class="arg">dictargs</i>?</span></a></dt>
<dd></dd>
<dt><a name="40">method <b class="cmd">connect</b> <i class="arg">sock</i> <i class="arg">ip</i> <i class="arg">port</i></a></dt>
<dd><p>Reply to an open socket. This method builds a coroutine to manage the remainder
 of the connection. The coroutine's operations are driven by the <b class="cmd">Connect</b> method.</p></dd>
<dt><a name="41">method <b class="cmd">ServerHeaders</b> <i class="arg">ip</i> <i class="arg">http_request</i> <i class="arg">mimetxt</i></a></dt>
<dd></dd>
<dt><a name="42">method <b class="cmd">Connect</b> <i class="arg">uuid</i> <i class="arg">sock</i> <i class="arg">ip</i></a></dt>
<dd><p>This method reads HTTP headers, and then consults the <b class="cmd">dispatch</b> method to
 determine if the request is valid, and/or what kind of reply to generate. Under
 normal cases, an object of class <b class="cmd">::http::reply</b> is created, and that class's
 <b class="cmd">dispatch</b> method.
 This action passes control of the socket to
 the reply object. The reply object manages the rest of the transaction, including
 closing the socket.</p></dd>
    ###
    chan configure $sock -translation binary -blocking 0 -buffering full -buffersize 4096
    chan configure $chan -translation binary -blocking 0 -buffering full -buffersize 4096
    if {$length} {
      ###
      # Send any POST/PUT/etc content
      ###
      chan copy $sock $chan -size $SIZE -command [info coroutine]
      yield
<dt><a name="43">method <b class="cmd">counter</b> <i class="arg">which</i></a></dt>
<dd><p>Increment an internal counter.</p></dd>
<dt><a name="44">method <b class="cmd">CheckTimeout</b></a></dt>
<dd><p>Check open connections for a time out event.</p></dd>
<dt><a name="45">method <b class="cmd">debug</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
    }
    catch {close $sock}
    chan flush $chan
<dt><a name="46">method <b class="cmd">dispatch</b> <i class="arg">data</i></a></dt>
<dd><p>Given a key/value list of information, return a data structure describing how
 the server should reply.</p></dd>
<dt><a name="47">method <b class="cmd">Dispatch_Default</b> <i class="arg">reply</i></a></dt>
<dd><p>Method dispatch method of last resort before returning a 404 NOT FOUND error.
 The default behavior is to look for a file in <em>DOCUMENT_ROOT</em> which
</pre>
</dd>
<dt><a name="42">method <b class="cmd">Url_Decode</b> <i class="arg">string</i></a></dt>
<dd><p>De-httpizes a string.</p></dd>
</dl>
 matches the query.</p></dd>
<dt><a name="48">method <b class="cmd">Dispatch_Local</b> <i class="arg">data</i></a></dt>
<dd><p>Method dispatch method invoked prior to invoking methods implemented by plugins.
 If this method returns a non-empty dictionary, that structure will be passed to
 the reply. The default is an empty implementation.</p></dd>
</div>
<div id="section10" class="doctools_section"><h2><a name="section10">Class ::httpd::content</a></h2>
<p>The httpd module includes several ready to use implementations of content mixins
for common use cases. Options are passed in to the <b class="cmd">add_uri</b> method of the server.</p>
</div>
<dt><a name="49">method <b class="cmd">Headers_Local</b> <i class="arg">varname</i></a></dt>
<dd><p>Introspect and possibly modify a data structure destined for a reply. This
 method is invoked before invoking Header methods implemented by plugins.
 The default implementation is empty.</p></dd>
<dt><a name="50">method <b class="cmd">Headers_Process</b> <i class="arg">varname</i></a></dt>
<dd><p>Introspect and possibly modify a data structure destined for a reply. This
 method is built dynamically by the <b class="cmd">plugin</b> method.</p></dd>
<dt><a name="51">method <b class="cmd">HostName</b> <i class="arg">ipaddr</i></a></dt>
<dd><p>Convert an ip address to a host name. If the server/ reverse_dns flag
 is false, this method simply returns the IP address back.
 Internally, this method uses the <em>dns</em> module from tcllib.</p></dd>
<dt><a name="52">method <b class="cmd">log</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Log an event. The input for args is free form. This method is intended
 to be replaced by the user, and is a noop for a stock http::server object.</p></dd>
<dt><a name="53">method <b class="cmd">plugin</b> <i class="arg">slot</i> <span class="opt">?<i class="arg">class</i> <b class="const"></b>?</span></a></dt>
<dd><p>Incorporate behaviors from a plugin.
 This method dynamically rebuilds the <b class="cmd">Dispatch</b> and <b class="cmd">Headers</b>
 method. For every plugin, the server looks for the following entries in
 <em>clay plugin/</em>:</p>
<div id="section11" class="doctools_section"><h2><a name="section11">Class ::httpd::content.cgi</a></h2>
<p>An implementation to relay requests to process which will accept post data
streamed in vie stdin, and sent a reply streamed to stdout.</p>
<dl class="doctools_definitions">
<dt><a name="43">method cgi_info</a></dt>
<dd><p>Mandatory method to be replaced by the end user. If needed, activates the
process to proxy, and then returns a list of three values:
<i class="arg">exec</i> - The arguments to send to exec to fire off the responding process, minus the stdin/stdout redirection.</p></dd>
<p><em>load</em> - A script to invoke in the server's namespace during the <b class="cmd">plugin</b> method invokation.</p>
<p><em>dispatch</em> - A script to stitch into the server's <b class="cmd">Dispatch</b> method.</p>
<p><em>headers</em> - A script to stitch into the server's <b class="cmd">Headers</b> method.</p>
<p><em>thread</em> - A script to stitch into the server's <b class="cmd">Thread_start</b> method.</p></dd>
<dt><a name="54">method <b class="cmd">port_listening</b></a></dt>
<dd><p>Return the actual port that httpd is listening on.</p></dd>
<dt><a name="55">method <b class="cmd">PrefixNormalize</b> <i class="arg">prefix</i></a></dt>
<dd><p>For the stock version, trim trailing /'s and *'s from a prefix. This
 method can be replaced by the end user to perform any other transformations
 needed for the application.</p></dd>
<dt><a name="56">method <b class="cmd">source</b> <i class="arg">filename</i></a></dt>
<dd></dd>
<dt><a name="57">method <b class="cmd">start</b></a></dt>
<dd><p>Open the socket listener.</p></dd>
<dt><a name="58">method <b class="cmd">stop</b></a></dt>
<dd><p>Shut off the socket listener, and destroy any pending replies.</p></dd>
<dt><a name="59">method <b class="cmd">SubObject {} db</b></a></dt>
<dd></dd>
<dt><a name="60">method <b class="cmd">SubObject {} default</b></a></dt>
<dd></dd>
<dt><a name="61">method <b class="cmd">template</b> <i class="arg">page</i></a></dt>
<dd><p>Return a template for the string <i class="arg">page</i></p></dd>
<dt><a name="62">method <b class="cmd">TemplateSearch</b> <i class="arg">page</i></a></dt>
<dd><p>Perform a search for the template that best matches <i class="arg">page</i>. This
 can include local file searches, in-memory structures, or even
 database lookups. The stock implementation simply looks for files
 with a .tml or .html extension in the <span class="opt">?doc_root?</span> directory.</p></dd>
<dt><a name="63">method <b class="cmd">Thread_start</b></a></dt>
<dd><p>Built by the <b class="cmd">plugin</b> method. Called by the <b class="cmd">start</b> method. Intended
 to allow plugins to spawn worker threads.</p></dd>
<dt><a name="64">method <b class="cmd">Uuid_Generate</b></a></dt>
<dd><p>Generate a GUUID. Used to ensure every request has a unique ID.
 The default implementation is:</p>
<pre class="doctools_example">
   return [::uuid::uuid generate]
 </pre>
</dd>
<dt><a name="65">method <b class="cmd">Validate_Connection</b> <i class="arg">sock</i> <i class="arg">ip</i></a></dt>
<dd><p>Given a socket and an ip address, return true if this connection should
 be terminated, or false if it should be allowed to continue. The stock
 implementation always returns 0. This is intended for applications to
 be able to implement black lists and/or provide security based on IP
 address.</p></dd>
</dl>
</div>
<div id="section12" class="doctools_section"><h2><a name="section12">Class ::httpd::content.file</a></h2>
<div id="subsection4" class="doctools_subsection"><h3><a name="subsection4">Class  httpd::server::dispatch</a></h3>
<p>An implementation to deliver files from the local file system.</p>
<p><em>ancestors</em>: <b class="class">httpd::server</b></p>
<p>Provide a backward compadible alias</p>
</div>
<div id="subsection5" class="doctools_subsection"><h3><a name="subsection5">Class  httpd::content.redirect</a></h3>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="44">option <b class="cmd">path</b></a></dt>
<dd><p>The root directory on the local file system to be exposed via http.</p></dd>
<dt><a name="45">option <b class="cmd"><a href="../../../../index.html#prefix">prefix</a></b></a></dt>
<dd><p>The prefix of the URI portion to ignore when calculating relative file paths.</p></dd>
<dt><a name="66">method <b class="cmd">reset</b></a></dt>
<dd></dd>
<dt><a name="67">method <b class="cmd">content</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection6" class="doctools_subsection"><h3><a name="subsection6">Class  httpd::content.cache</a></h3>
<p><b class="class">Methods</b></p>
<div id="section13" class="doctools_section"><h2><a name="section13">Class ::httpd::content.proxy</a></h2>
<dl class="doctools_definitions">
<p>An implementation to relay requests to another HTTP server, and relay
the results back across the request channel.</p>
<dt><a name="68">method <b class="cmd">Dispatch</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection7" class="doctools_subsection"><h3><a name="subsection7">Class  httpd::content.template</a></h3>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="69">method <b class="cmd">content</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection8" class="doctools_subsection"><h3><a name="subsection8">Class  httpd::content.file</a></h3>
<p>Class to deliver Static content
 When utilized, this class is fed a local filename
 by the dispatcher</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="46">method proxy_info</a></dt>
<dd><p>Mandatory method to be replaced by the end user. If needed, activates the
<dt><a name="70">method <b class="cmd">FileName</b></a></dt>
<dd></dd>
process to proxy, and then returns a list of three values:
<i class="arg">proxyhost</i> - The hostname where the proxy is located
<i class="arg">proxyport</i> - The port to connect to
<i class="arg">proxyscript</i> - A pre-amble block of text to send prior to the mirrored request</p></dd>
<dt><a name="71">method <b class="cmd">DirectoryListing</b> <i class="arg">local_file</i></a></dt>
<dd></dd>
<dt><a name="72">method <b class="cmd">content</b></a></dt>
<dd></dd>
<dt><a name="73">method <b class="cmd">Dispatch</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection9" class="doctools_subsection"><h3><a name="subsection9">Class  httpd::content.exec</a></h3>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="74">method <b class="cmd">CgiExec</b> <i class="arg">execname</i> <i class="arg">script</i> <i class="arg">arglist</i></a></dt>
<dd></dd>
<dt><a name="75">method <b class="cmd">Cgi_Executable</b> <i class="arg">script</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection10" class="doctools_subsection"><h3><a name="subsection10">Class  httpd::content.proxy</a></h3>
<p><em>ancestors</em>: <b class="class">httpd::content.exec</b></p>
<p>Return data from an proxy process</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="76">method <b class="cmd">proxy_channel</b></a></dt>
<dd></dd>
<dt><a name="77">method <b class="cmd">proxy_path</b></a></dt>
<dd></dd>
<dt><a name="78">method <b class="cmd">ProxyRequest</b> <i class="arg">chana</i> <i class="arg">chanb</i></a></dt>
<dd></dd>
<dt><a name="79">method <b class="cmd">ProxyReply</b> <i class="arg">chana</i> <i class="arg">chanb</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="80">method <b class="cmd">Dispatch</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection11" class="doctools_subsection"><h3><a name="subsection11">Class  httpd::content.cgi</a></h3>
<div id="section14" class="doctools_section"><h2><a name="section14">Class ::httpd::content.scgi</a></h2>
<p><em>ancestors</em>: <b class="class">httpd::content.proxy</b></p>
<p>An implementation to relay requests to a server listening on a socket
expecting SCGI encoded requests, and relay
the results back across the request channel.</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="47">method scgi_info</a></dt>
<dd><p>Mandatory method to be replaced by the end user. If needed, activates the
process to proxy, and then returns a list of three values:
<i class="arg">scgihost</i> - The hostname where the scgi listener is located
<i class="arg">scgiport</i> - The port to connect to
<i class="arg">scgiscript</i> - The contents of the <i class="arg">SCRIPT_NAME</i> header to be sent</p></dd>
<dt><a name="81">method <b class="cmd">FileName</b></a></dt>
<dd></dd>
<dt><a name="82">method <b class="cmd">proxy_channel</b></a></dt>
<dd></dd>
<dt><a name="83">method <b class="cmd">ProxyRequest</b> <i class="arg">chana</i> <i class="arg">chanb</i></a></dt>
<dd></dd>
<dt><a name="84">method <b class="cmd">ProxyReply</b> <i class="arg">chana</i> <i class="arg">chanb</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="85">method <b class="cmd">DirectoryListing</b> <i class="arg">local_file</i></a></dt>
<dd><p>For most CGI applications a directory list is vorboten</p></dd>
</dl>
</div>
<div id="subsection12" class="doctools_subsection"><h3><a name="subsection12">Class  httpd::protocol.scgi</a></h3>
<p>Return data from an SCGI process</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="86">method <b class="cmd">EncodeStatus</b> <i class="arg">status</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection13" class="doctools_subsection"><h3><a name="subsection13">Class  httpd::content.scgi</a></h3>
<p><em>ancestors</em>: <b class="class">httpd::content.proxy</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="87">method <b class="cmd">scgi_info</b></a></dt>
<dd></dd>
<dt><a name="88">method <b class="cmd">proxy_channel</b></a></dt>
<dd></dd>
<dt><a name="89">method <b class="cmd">ProxyRequest</b> <i class="arg">chana</i> <i class="arg">chanb</i></a></dt>
<dd></dd>
<dt><a name="90">method <b class="cmd">ProxyReply</b> <i class="arg">chana</i> <i class="arg">chanb</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection14" class="doctools_subsection"><h3><a name="subsection14">Class  httpd::server.scgi</a></h3>
<p><em>ancestors</em>: <b class="class">httpd::server</b></p>
<p>Act as an  SCGI Server</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="91">method <b class="cmd">debug</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="92">method <b class="cmd">Connect</b> <i class="arg">uuid</i> <i class="arg">sock</i> <i class="arg">ip</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="section15" class="doctools_section"><h2><a name="section15">Class ::httpd::content.websocket</a></h2>
<div id="subsection15" class="doctools_subsection"><h3><a name="subsection15">Class  httpd::content.websocket</a></h3>
<p>A placeholder for a future implementation to manage requests that can expect to be
promoted to a Websocket. Currently it is an empty class.</p>
<p>Upgrade a connection to a websocket</p>
</div>
<div id="section16" class="doctools_section"><h2><a name="section16">SCGI Server Functions</a></h2>
<p>The HTTP module also provides an SCGI server implementation, as well as an HTTP
<div id="subsection16" class="doctools_subsection"><h3><a name="subsection16">Class  httpd::plugin</a></h3>
<p>httpd plugin template</p>
</div>
<div id="subsection17" class="doctools_subsection"><h3><a name="subsection17">Class  httpd::plugin.dict_dispatch</a></h3>
<p>A rudimentary plugin that dispatches URLs from a dict
 data structure</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="93">method <b class="cmd">Dispatch_Dict</b> <i class="arg">data</i></a></dt>
<dd><p>Implementation of the dispatcher</p></dd>
implementation. To use the SCGI functions, create an object of the <b class="cmd">http::server.scgi</b>
class instead of the <b class="cmd">http::server</b> class.</p>
<dt><a name="94">method <b class="cmd">uri {} add</b> <i class="arg">vhosts</i> <i class="arg">patterns</i> <i class="arg">info</i></a></dt>
<dd></dd>
<dt><a name="95">method <b class="cmd">uri {} direct</b> <i class="arg">vhosts</i> <i class="arg">patterns</i> <i class="arg">info</i> <i class="arg">body</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection18" class="doctools_subsection"><h3><a name="subsection18">Class  httpd::reply.memchan</a></h3>
<p><em>ancestors</em>: <b class="class">httpd::reply</b></p>
<p><b class="class">Methods</b></p>
<div id="section17" class="doctools_section"><h2><a name="section17">Class ::httpd::reply.scgi</a></h2>
<p>An modified <b class="cmd">http::reply</b> implementation that understands how to deal with
netstring encoded headers.</p>
<dl class="doctools_definitions">
<dt><a name="96">method <b class="cmd">output</b></a></dt>
<dd></dd>
<dt><a name="97">method <b class="cmd">DoOutput</b></a></dt>
<dd></dd>
<dt><a name="98">method <b class="cmd">close</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection19" class="doctools_subsection"><h3><a name="subsection19">Class  httpd::plugin.local_memchan</a></h3>
<p><b class="class">Methods</b></p>
<div id="section18" class="doctools_section"><h2><a name="section18">Class ::httpd::server.scgi</a></h2>
<p>A modified <b class="cmd">http::server</b> which is tailored to replying to request according to
the SCGI standard instead of the HTTP standard.</p>
<dl class="doctools_definitions">
<dt><a name="99">method <b class="cmd">local_memchan</b> <i class="arg">command</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="100">method <b class="cmd">Connect_Local</b> <i class="arg">uuid</i> <i class="arg">sock</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>A modified connection method that passes simple GET request to an object
 and pulls data directly from the reply_body data variable in the object
 Needed because memchan is bidirectional, and we can't seem to communicate that
 the server is one side of the link and the reply is another</p></dd>
</dl>
</div>
</div>
<div id="section19" class="doctools_section"><h2><a name="section19">AUTHORS</a></h2>
<div id="section4" class="doctools_section"><h2><a name="section4">AUTHORS</a></h2>
<p>Sean Woods</p>
</div>
<div id="section20" class="doctools_section"><h2><a name="section20">Bugs, Ideas, Feedback</a></h2>
<div id="section5" class="doctools_section"><h2><a name="section5">Bugs, Ideas, Feedback</a></h2>
<p>This document, and the package it describes, will undoubtedly contain
bugs and other problems.
Please report such in the category <em>network</em> of the
<a href="http://core.tcl.tk/tcllib/reportlist">Tcllib Trackers</a>.
Please also report any ideas for enhancements you may have for either
package and/or documentation.</p>
<p>When proposing code changes, please provide <em>unified diffs</em>,
i.e the output of <b class="const">diff -u</b>.</p>
<p>Note further that <em>attachments</em> are strongly preferred over
inlined patches. Attachments can be made by going to the <b class="const">Edit</b>
form of the ticket immediately after its creation, and then using the
left-most button in the secondary navigation bar.</p>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>
<p><a href="../../../../index.html#tcloo">TclOO</a>, <a href="../../../../index.html#www">WWW</a>, <a href="../../../../index.html#http">http</a>, <a href="../../../../index.html#httpd">httpd</a>, <a href="../../../../index.html#httpserver">httpserver</a>, <a href="../../../../index.html#services">services</a></p>
<p>TclOO, WWW, http, httpd, httpserver, services</p>
</div>
<div id="category" class="doctools_section"><h2><a name="category">Category</a></h2>
<p>Networking</p>
</div>
<div id="copyright" class="doctools_section"><h2><a name="copyright">Copyright</a></h2>
<p>Copyright &copy; 2018 Sean Woods &lt;[email protected]&gt;</p>
</div>
</div></body></html>

Changes to idoc/www/tcllib/files/modules/log/log.html.

233
234
235
236
237
238
239
240
241

242
243
244
245

246
247
248
249
250
251
252
233
234
235
236
237
238
239


240
241
242


243
244
245
246
247
248
249
250







-
-
+


-
-
+







<dd><p>Compares two levels (including unique abbreviations) with respect to
their priority. This command can be used by the -command option of
lsort. The result is one of -1, 0 or 1 or an error. A result of -1
signals that level1 is of less priority than level2. 0 signals that
both levels have the same priority. 1 signals that level1 has higher
priority than level2.</p></dd>
<dt><a name="8"><b class="cmd">::log::lvSuppress</b> <i class="arg">level</i> {<i class="arg">suppress</i> 1}</a></dt>
<dd><p>]
(Un)suppresses the output of messages having the specified
<dd><p>(Un)suppresses the output of messages having the specified
level. Unique abbreviations for the level are allowed here too.</p></dd>
<dt><a name="9"><b class="cmd">::log::lvSuppressLE</b> <i class="arg">level</i> {<i class="arg">suppress</i> 1}</a></dt>
<dd><p>]
(Un)suppresses the output of messages having the specified level or
<dd><p>(Un)suppresses the output of messages having the specified level or
one of lesser priority. Unique abbreviations for the level are allowed
here too.</p></dd>
<dt><a name="10"><b class="cmd">::log::lvIsSuppressed</b> <i class="arg">level</i></a></dt>
<dd><p>Asks the package whether the specified level is currently
suppressed. Unique abbreviations of level names are allowed.</p></dd>
<dt><a name="11"><b class="cmd">::log::lvCmd</b> <i class="arg">level</i> <i class="arg">cmd</i></a></dt>
<dd><p>Defines for the specified level with which command to write the

Changes to idoc/www/tcllib/files/modules/math/math_geometry.html.

207
208
209
210
211
212
213
214

215
216
217
218
219
220
221
207
208
209
210
211
212
213

214
215
216
217
218
219
220
221







-
+







<li><p><em>polyline</em> - a list of an even number of coordinates,
interpreted as the x- and y-coordinates of an ordered set of points.</p></li>
<li><p><em>polygon</em> - like a polyline, but the implicit assumption is that
the polyline is closed (if the first and last points do not coincide,
the missing segment is automatically added).</p></li>
<li><p><em>point set</em> - again a list of an even number of coordinates, but
the points are regarded without any ordering.</p></li>
<li><p><em>circle</em> - a list of thtee numbers, the first two are the coordinates of the
<li><p><em>circle</em> - a list of three numbers, the first two are the coordinates of the
centre and the third is the radius.</p></li>
</ul>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">PROCEDURES</a></h2>
<p>The package defines the following public procedures:</p>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">::math::geometry::+</b> <i class="arg">point1</i> <i class="arg">point2</i></a></dt>
591
592
593
594
595
596
597
598
599


600
601
602
603
604
605
606
591
592
593
594
595
596
597


598
599
600
601
602
603
604
605
606







-
-
+
+







<dd><p>Line to be checked</p></dd>
<dt>list <i class="arg">circle</i></dt>
<dd><p>Circle that may or may not be intersected</p></dd>
</dl></dd>
<dt><a name="47"><b class="cmd">::math::geometry::intersectionCircleWithCircle</b> <i class="arg">circle1</i> <i class="arg">circle2</i></a></dt>
<dd><p>Determine the points at which the given two circles intersect. There can
be zero, one or two points. (If the two circles touch the circle or are very close,
then one point is returned. An arbitrary margin of 1.0e-10 times the radius of
the first circle is used to determine this situation.)</p>
then one point is returned. An arbitrary margin of 1.0e-10 times the mean of the radii of
the two circles is used to determine this situation.)</p>
<dl class="doctools_arguments">
<dt>list <i class="arg">circle1</i></dt>
<dd><p>First circle</p></dd>
<dt>list <i class="arg">circle2</i></dt>
<dd><p>Second circle</p></dd>
</dl></dd>
<dt><a name="48"><b class="cmd">::math::geometry::tangentLinesToCircle</b> <i class="arg">point</i> <i class="arg">circle</i></a></dt>

Changes to idoc/www/tcllib/files/modules/math/numtheory.html.


1
2
3
4
5
6
7
1
2
3
4
5
6
7
8
+








<!DOCTYPE html><html><head>
<title>math::numtheory - Tcl Math Library</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
103
104
105
106
107
108
109
110

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

130
131
132
133
134
135


136
137
138
139
140
141
142
143
144
145
146











147
148
149
150
151
152
153
104
105
106
107
108
109
110

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

130
131
132
133
134
135
136
137
138











139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156







-
+


















-
+






+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+







| <a href="../../../toc.html">Table Of Contents</a>
| <a href="../../../../index.html">Keyword Index</a>
| <a href="../../../../toc0.html">Categories</a>
| <a href="../../../../toc1.html">Modules</a>
| <a href="../../../../toc2.html">Applications</a>
 ] <hr>
<div class="doctools">
<h1 class="doctools_title">math::numtheory(n) 1.0 tcllib &quot;Tcl Math Library&quot;</h1>
<h1 class="doctools_title">math::numtheory(n) 1.1.1 tcllib &quot;Tcl Math Library&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>math::numtheory - Number Theory</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a></li>
<li class="doctools_section"><a href="#section2">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
<li class="doctools_section"><a href="#category">Category</a></li>
<li class="doctools_section"><a href="#copyright">Copyright</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl <span class="opt">?8.5?</span></b></li>
<li>package require <b class="pkgname">math::numtheory <span class="opt">?1.0?</span></b></li>
<li>package require <b class="pkgname">math::numtheory <span class="opt">?1.1.1?</span></b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><b class="cmd">math::numtheory::isprime</b> <i class="arg">N</i> <span class="opt">?<i class="arg">option</i> <i class="arg">value</i> ...?</span></a></li>
<li><a href="#2"><b class="cmd">math::numtheory::firstNprimes</b> <i class="arg">N</i></a></li>
<li><a href="#3"><b class="cmd">math::numtheory::primesLowerThan</b> <i class="arg">N</i></a></li>
<li><a href="#4"><b class="cmd">math::numtheory::primeFactors</b> <i class="arg">N</i></a></li>
<li><a href="#5"><b class="cmd">math::numtheory::primesLowerThan</b> <i class="arg">N</i></a></li>
<li><a href="#6"><b class="cmd">math::numtheory::primeFactors</b> <i class="arg">N</i></a></li>
<li><a href="#5"><b class="cmd">math::numtheory::uniquePrimeFactors</b> <i class="arg">N</i></a></li>
<li><a href="#6"><b class="cmd">math::numtheory::factors</b> <i class="arg">N</i></a></li>
<li><a href="#7"><b class="cmd">math::numtheory::totient</b> <i class="arg">N</i></a></li>
<li><a href="#8"><b class="cmd">math::numtheory::moebius</b> <i class="arg">N</i></a></li>
<li><a href="#9"><b class="cmd">math::numtheory::legendre</b> <i class="arg">a</i> <i class="arg">p</i></a></li>
<li><a href="#10"><b class="cmd">math::numtheory::jacobi</b> <i class="arg">a</i> <i class="arg">b</i></a></li>
<li><a href="#11"><b class="cmd">math::numtheory::gcd</b> <i class="arg">m</i> <i class="arg">n</i></a></li>
<li><a href="#12"><b class="cmd">math::numtheory::lcm</b> <i class="arg">m</i> <i class="arg">n</i></a></li>
<li><a href="#13"><b class="cmd">math::numtheory::numberPrimesGauss</b> <i class="arg">N</i></a></li>
<li><a href="#14"><b class="cmd">math::numtheory::numberPrimesLegendre</b> <i class="arg">N</i></a></li>
<li><a href="#15"><b class="cmd">math::numtheory::numberPrimesLegendreModified</b> <i class="arg">N</i></a></li>
<li><a href="#7"><b class="cmd">math::numtheory::uniquePrimeFactors</b> <i class="arg">N</i></a></li>
<li><a href="#8"><b class="cmd">math::numtheory::factors</b> <i class="arg">N</i></a></li>
<li><a href="#9"><b class="cmd">math::numtheory::totient</b> <i class="arg">N</i></a></li>
<li><a href="#10"><b class="cmd">math::numtheory::moebius</b> <i class="arg">N</i></a></li>
<li><a href="#11"><b class="cmd">math::numtheory::legendre</b> <i class="arg">a</i> <i class="arg">p</i></a></li>
<li><a href="#12"><b class="cmd">math::numtheory::jacobi</b> <i class="arg">a</i> <i class="arg">b</i></a></li>
<li><a href="#13"><b class="cmd">math::numtheory::gcd</b> <i class="arg">m</i> <i class="arg">n</i></a></li>
<li><a href="#14"><b class="cmd">math::numtheory::lcm</b> <i class="arg">m</i> <i class="arg">n</i></a></li>
<li><a href="#15"><b class="cmd">math::numtheory::numberPrimesGauss</b> <i class="arg">N</i></a></li>
<li><a href="#16"><b class="cmd">math::numtheory::numberPrimesLegendre</b> <i class="arg">N</i></a></li>
<li><a href="#17"><b class="cmd">math::numtheory::numberPrimesLegendreModified</b> <i class="arg">N</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>This package is for collecting various number-theoretic operations, with
a slight bias to prime numbers.</p>
<dl class="doctools_definitions">
192
193
194
195
196
197
198












199

200
201
202
203
204
205

206
207
208
209
210
211

212
213
214
215
216
217
218

219
220
221
222
223
224

225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240

241
242
243
244
245
246
247
248

249
250
251
252
253
254
255
256

257
258
259
260
261
262

263
264
265
266
267
268

269
270
271
272
273
274
275
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

214
215
216
217
218
219

220
221
222
223
224
225

226
227
228
229
230
231
232

233
234
235
236
237
238

239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
254

255
256
257
258
259
260
261
262

263
264
265
266
267
268
269
270

271
272
273
274
275
276

277
278
279
280
281
282

283
284
285
286
287
288
289
290







+
+
+
+
+
+
+
+
+
+
+
+
-
+





-
+





-
+






-
+





-
+







-
+







-
+







-
+







-
+





-
+





-
+







</dl></dd>
<dt><a name="4"><b class="cmd">math::numtheory::primeFactors</b> <i class="arg">N</i></a></dt>
<dd><p>Return a list of the prime numbers in the number N</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number to be factorised</p></dd>
</dl></dd>
<dt><a name="5"><b class="cmd">math::numtheory::primesLowerThan</b> <i class="arg">N</i></a></dt>
<dd><p>Return the prime numbers lower/equal to N</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Maximum number to consider</p></dd>
</dl></dd>
<dt><a name="6"><b class="cmd">math::numtheory::primeFactors</b> <i class="arg">N</i></a></dt>
<dd><p>Return a list of the prime numbers in the number N</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number to be factorised</p></dd>
</dl></dd>
<dt><a name="5"><b class="cmd">math::numtheory::uniquePrimeFactors</b> <i class="arg">N</i></a></dt>
<dt><a name="7"><b class="cmd">math::numtheory::uniquePrimeFactors</b> <i class="arg">N</i></a></dt>
<dd><p>Return a list of the <em>unique</em> prime numbers in the number N</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number to be factorised</p></dd>
</dl></dd>
<dt><a name="6"><b class="cmd">math::numtheory::factors</b> <i class="arg">N</i></a></dt>
<dt><a name="8"><b class="cmd">math::numtheory::factors</b> <i class="arg">N</i></a></dt>
<dd><p>Return a list of all <em>unique</em> factors in the number N, including 1 and N itself</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number to be factorised</p></dd>
</dl></dd>
<dt><a name="7"><b class="cmd">math::numtheory::totient</b> <i class="arg">N</i></a></dt>
<dt><a name="9"><b class="cmd">math::numtheory::totient</b> <i class="arg">N</i></a></dt>
<dd><p>Evaluate the Euler totient function for the number N (number of numbers
relatively prime to N)</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number in question</p></dd>
</dl></dd>
<dt><a name="8"><b class="cmd">math::numtheory::moebius</b> <i class="arg">N</i></a></dt>
<dt><a name="10"><b class="cmd">math::numtheory::moebius</b> <i class="arg">N</i></a></dt>
<dd><p>Evaluate the Moebius function for the number N</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number in question</p></dd>
</dl></dd>
<dt><a name="9"><b class="cmd">math::numtheory::legendre</b> <i class="arg">a</i> <i class="arg">p</i></a></dt>
<dt><a name="11"><b class="cmd">math::numtheory::legendre</b> <i class="arg">a</i> <i class="arg">p</i></a></dt>
<dd><p>Evaluate the Legendre symbol (a/p)</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">a</i> (in)</dt>
<dd><p>Upper number in the symbol</p></dd>
<dt>integer <i class="arg">p</i> (in)</dt>
<dd><p>Lower number in the symbol (must be non-zero)</p></dd>
</dl></dd>
<dt><a name="10"><b class="cmd">math::numtheory::jacobi</b> <i class="arg">a</i> <i class="arg">b</i></a></dt>
<dt><a name="12"><b class="cmd">math::numtheory::jacobi</b> <i class="arg">a</i> <i class="arg">b</i></a></dt>
<dd><p>Evaluate the Jacobi symbol (a/b)</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">a</i> (in)</dt>
<dd><p>Upper number in the symbol</p></dd>
<dt>integer <i class="arg">b</i> (in)</dt>
<dd><p>Lower number in the symbol (must be odd)</p></dd>
</dl></dd>
<dt><a name="11"><b class="cmd">math::numtheory::gcd</b> <i class="arg">m</i> <i class="arg">n</i></a></dt>
<dt><a name="13"><b class="cmd">math::numtheory::gcd</b> <i class="arg">m</i> <i class="arg">n</i></a></dt>
<dd><p>Return the greatest common divisor of <i class="term">m</i> and <i class="term">n</i></p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">m</i> (in)</dt>
<dd><p>First number</p></dd>
<dt>integer <i class="arg">n</i> (in)</dt>
<dd><p>Second number</p></dd>
</dl></dd>
<dt><a name="12"><b class="cmd">math::numtheory::lcm</b> <i class="arg">m</i> <i class="arg">n</i></a></dt>
<dt><a name="14"><b class="cmd">math::numtheory::lcm</b> <i class="arg">m</i> <i class="arg">n</i></a></dt>
<dd><p>Return the lowest common multiple of <i class="term">m</i> and <i class="term">n</i></p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">m</i> (in)</dt>
<dd><p>First number</p></dd>
<dt>integer <i class="arg">n</i> (in)</dt>
<dd><p>Second number</p></dd>
</dl></dd>
<dt><a name="13"><b class="cmd">math::numtheory::numberPrimesGauss</b> <i class="arg">N</i></a></dt>
<dt><a name="15"><b class="cmd">math::numtheory::numberPrimesGauss</b> <i class="arg">N</i></a></dt>
<dd><p>Estimate the number of primes according the formula by Gauss.</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number in question</p></dd>
</dl></dd>
<dt><a name="14"><b class="cmd">math::numtheory::numberPrimesLegendre</b> <i class="arg">N</i></a></dt>
<dt><a name="16"><b class="cmd">math::numtheory::numberPrimesLegendre</b> <i class="arg">N</i></a></dt>
<dd><p>Estimate the number of primes according the formula by Legendre.</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number in question</p></dd>
</dl></dd>
<dt><a name="15"><b class="cmd">math::numtheory::numberPrimesLegendreModified</b> <i class="arg">N</i></a></dt>
<dt><a name="17"><b class="cmd">math::numtheory::numberPrimesLegendreModified</b> <i class="arg">N</i></a></dt>
<dd><p>Estimate the number of primes according the modified formula by Legendre.</p>
<dl class="doctools_arguments">
<dt>integer <i class="arg">N</i> (in)</dt>
<dd><p>Number in question</p></dd>
</dl></dd>
</dl>
</div>

Added idoc/www/tcllib/files/modules/math/trig.html.





























































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

<!DOCTYPE html><html><head>
<title>math::trig - Tcl Math Library</title>
<style type="text/css"><!--
    HTML {
	background: 	#FFFFFF;
	color: 		black;
    }
    BODY {
	background: 	#FFFFFF;
	color:	 	black;
    }
    DIV.doctools {
	margin-left:	10%;
	margin-right:	10%;
    }
    DIV.doctools H1,DIV.doctools H2 {
	margin-left:	-5%;
    }
    H1, H2, H3, H4 {
	margin-top: 	1em;
	font-family:	sans-serif;
	font-size:	large;
	color:		#005A9C;
	background: 	transparent;
	text-align:		left;
    }
    H1.doctools_title {
	text-align: center;
    }
    UL,OL {
	margin-right: 0em;
	margin-top: 3pt;
	margin-bottom: 3pt;
    }
    UL LI {
	list-style: disc;
    }
    OL LI {
	list-style: decimal;
    }
    DT {
	padding-top: 	1ex;
    }
    UL.doctools_toc,UL.doctools_toc UL, UL.doctools_toc UL UL {
	font:		normal 12pt/14pt sans-serif;
	list-style:	none;
    }
    LI.doctools_section, LI.doctools_subsection {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding: 	0em;
    }
    PRE {
	display: 	block;
	font-family:	monospace;
	white-space:	pre;
	margin:		0%;
	padding-top:	0.5ex;
	padding-bottom:	0.5ex;
	padding-left:	1ex;
	padding-right:	1ex;
	width:		100%;
    }
    PRE.doctools_example {
	color: 		black;
	background: 	#f5dcb3;
	border:		1px solid black;
    }
    UL.doctools_requirements LI, UL.doctools_syntax LI {
	list-style: 	none;
	margin-left: 	0em;
	text-indent:	0em;
	padding:	0em;
    }
    DIV.doctools_synopsis {
	color: 		black;
	background: 	#80ffff;
	border:		1px solid black;
	font-family:	serif;
	margin-top: 	1em;
	margin-bottom: 	1em;
    }
    UL.doctools_syntax {
	margin-top: 	1em;
	border-top:	1px solid black;
    }
    UL.doctools_requirements {
	margin-bottom: 	1em;
	border-bottom:	1px solid black;
    }
--></style>
</head>
<!-- Generated from file 'trig.man' by tcllib/doctools with format 'html'
   -->
<!-- Copyright &amp;copy; 2018 Arjen Markus
   -->
<!-- math::trig.n
   -->
<body><hr> [
   <a href="../../../../../../../../home">Tcllib Home</a>
| <a href="../../../../toc.html">Main Table Of Contents</a>
| <a href="../../../toc.html">Table Of Contents</a>
| <a href="../../../../index.html">Keyword Index</a>
| <a href="../../../../toc0.html">Categories</a>
| <a href="../../../../toc1.html">Modules</a>
| <a href="../../../../toc2.html">Applications</a>
 ] <hr>
<div class="doctools">
<h1 class="doctools_title">math::trig(n) 1.0.0 tcllib &quot;Tcl Math Library&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>math::trig - Trigonometric anf hyperbolic functions</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a></li>
<li class="doctools_section"><a href="#section2">FUNCTIONS</a></li>
<li class="doctools_section"><a href="#section3">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
<li class="doctools_section"><a href="#category">Category</a></li>
<li class="doctools_section"><a href="#copyright">Copyright</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl 8.5</b></li>
<li>package require <b class="pkgname">math::trig 1.0.0</b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><b class="cmd">::math::trig::radian_reduced</b> <i class="arg">angle</i></a></li>
<li><a href="#2"><b class="cmd">::math::trig::degree_reduced</b> <i class="arg">angle</i></a></li>
<li><a href="#3"><b class="cmd">::math::trig::cosec</b> <i class="arg">angle</i></a></li>
<li><a href="#4"><b class="cmd">::math::trig::sec</b> <i class="arg">angle</i></a></li>
<li><a href="#5"><b class="cmd">::math::trig::cotan</b> <i class="arg">angle</i></a></li>
<li><a href="#6"><b class="cmd">::math::trig::acosec</b> <i class="arg">value</i></a></li>
<li><a href="#7"><b class="cmd">::math::trig::asec</b> <i class="arg">value</i></a></li>
<li><a href="#8"><b class="cmd">::math::trig::acotan</b> <i class="arg">value</i></a></li>
<li><a href="#9"><b class="cmd">::math::trig::cosech</b> <i class="arg">value</i></a></li>
<li><a href="#10"><b class="cmd">::math::trig::sech</b> <i class="arg">value</i></a></li>
<li><a href="#11"><b class="cmd">::math::trig::cotanh</b> <i class="arg">value</i></a></li>
<li><a href="#12"><b class="cmd">::math::trig::asinh</b> <i class="arg">value</i></a></li>
<li><a href="#13"><b class="cmd">::math::trig::acosh</b> <i class="arg">value</i></a></li>
<li><a href="#14"><b class="cmd">::math::trig::atanh</b> <i class="arg">value</i></a></li>
<li><a href="#15"><b class="cmd">::math::trig::acosech</b> <i class="arg">value</i></a></li>
<li><a href="#16"><b class="cmd">::math::trig::asech</b> <i class="arg">value</i></a></li>
<li><a href="#17"><b class="cmd">::math::trig::acotanh</b> <i class="arg">value</i></a></li>
<li><a href="#18"><b class="cmd">::math::trig::sind</b> <i class="arg">angle</i></a></li>
<li><a href="#19"><b class="cmd">::math::trig::cosd</b> <i class="arg">angle</i></a></li>
<li><a href="#20"><b class="cmd">::math::trig::tand</b> <i class="arg">angle</i></a></li>
<li><a href="#21"><b class="cmd">::math::trig::cosecd</b> <i class="arg">angle</i></a></li>
<li><a href="#22"><b class="cmd">::math::trig::secd</b> <i class="arg">angle</i></a></li>
<li><a href="#23"><b class="cmd">::math::trig::cotand</b> <i class="arg">angle</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>The <i class="term">math::trig</i> package defines a set of trigonomic and hyperbolic functions
and their inverses. In addition it defines versions of the trigonomic functions
that take arguments in degrees instead of radians.</p>
<p>For easy use these functions may be imported into the <i class="term">tcl::mathfunc</i> namespace,
so that they can be used directly in the <i class="term">expr</i> command.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">FUNCTIONS</a></h2>
<p>The functions <i class="term">radian_reduced</i> and <i class="term">degree_reduced</i> return a reduced angle, in
respectively radians and degrees, in the intervals [0, 2pi) and [0, 360):</p>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">::math::trig::radian_reduced</b> <i class="arg">angle</i></a></dt>
<dd><p>Return the equivalent angle in the interval [0, 2pi).</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in radians)</p></dd>
</dl></dd>
<dt><a name="2"><b class="cmd">::math::trig::degree_reduced</b> <i class="arg">angle</i></a></dt>
<dd><p>Return the equivalent angle in the interval [0, 360).</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in degrees)</p></dd>
</dl></dd>
</dl>
<p>The following trigonomic functions are defined in addition to the ones defined
in the <i class="term">expr</i> command:</p>
<dl class="doctools_definitions">
<dt><a name="3"><b class="cmd">::math::trig::cosec</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the cosecant of the angle (1/cos(angle))</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in radians)</p></dd>
</dl></dd>
<dt><a name="4"><b class="cmd">::math::trig::sec</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the secant of the angle (1/sin(angle))</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in radians)</p></dd>
</dl></dd>
<dt><a name="5"><b class="cmd">::math::trig::cotan</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the cotangent of the angle (1/tan(angle))</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in radians)</p></dd>
</dl></dd>
</dl>
<p>For these functions also the inverses are defined:</p>
<dl class="doctools_definitions">
<dt><a name="6"><b class="cmd">::math::trig::acosec</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc cosecant of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="7"><b class="cmd">::math::trig::asec</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc secant of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="8"><b class="cmd">::math::trig::acotan</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc cotangent of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
</dl>
<p>The following hyperbolic and inverse hyperbolic functions are defined:</p>
<dl class="doctools_definitions">
<dt><a name="9"><b class="cmd">::math::trig::cosech</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the hyperbolic cosecant of the value (1/sinh(value))</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="10"><b class="cmd">::math::trig::sech</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the hyperbolic secant of the value (1/cosh(value))</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="11"><b class="cmd">::math::trig::cotanh</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the hyperbolic cotangent of the value (1/tanh(value))</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="12"><b class="cmd">::math::trig::asinh</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc hyperbolic sine of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="13"><b class="cmd">::math::trig::acosh</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc hyperbolic cosine of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="14"><b class="cmd">::math::trig::atanh</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc hyperbolic tangent of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="15"><b class="cmd">::math::trig::acosech</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc hyperbolic cosecant of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="16"><b class="cmd">::math::trig::asech</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc hyperbolic secant of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
<dt><a name="17"><b class="cmd">::math::trig::acotanh</b> <i class="arg">value</i></a></dt>
<dd><p>Calculate the arc hyperbolic cotangent of the value</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">value</i></dt>
<dd><p>Value of the argument</p></dd>
</dl></dd>
</dl>
<p>The following versions of the common trigonometric functions and their
inverses are defined:</p>
<dl class="doctools_definitions">
<dt><a name="18"><b class="cmd">::math::trig::sind</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the sine of the angle (in degrees)</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in degrees)</p></dd>
</dl></dd>
<dt><a name="19"><b class="cmd">::math::trig::cosd</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the cosine of the angle (in degrees)</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in radians)</p></dd>
</dl></dd>
<dt><a name="20"><b class="cmd">::math::trig::tand</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the cotangent of the angle (in degrees)</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in degrees)</p></dd>
</dl></dd>
<dt><a name="21"><b class="cmd">::math::trig::cosecd</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the cosecant of the angle (in degrees)</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in degrees)</p></dd>
</dl></dd>
<dt><a name="22"><b class="cmd">::math::trig::secd</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the secant of the angle (in degrees)</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in degrees)</p></dd>
</dl></dd>
<dt><a name="23"><b class="cmd">::math::trig::cotand</b> <i class="arg">angle</i></a></dt>
<dd><p>Calculate the cotangent of the angle (in degrees)</p>
<dl class="doctools_arguments">
<dt>float <i class="arg">angle</i></dt>
<dd><p>Angle (in degrees)</p></dd>
</dl></dd>
</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">Bugs, Ideas, Feedback</a></h2>
<p>This document, and the package it describes, will undoubtedly contain
bugs and other problems.
Please report such in the category <em>math :: trig</em> of the
<a href="http://core.tcl.tk/tcllib/reportlist">Tcllib Trackers</a>.
Please also report any ideas for enhancements you may have for either
package and/or documentation.</p>
<p>When proposing code changes, please provide <em>unified diffs</em>,
i.e the output of <b class="const">diff -u</b>.</p>
<p>Note further that <em>attachments</em> are strongly preferred over
inlined patches. Attachments can be made by going to the <b class="const">Edit</b>
form of the ticket immediately after its creation, and then using the
left-most button in the secondary navigation bar.</p>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>
<p><a href="../../../../index.html#math">math</a>, <a href="../../../../index.html#trigonometry">trigonometry</a></p>
</div>
<div id="category" class="doctools_section"><h2><a name="category">Category</a></h2>
<p>Mathematics</p>
</div>
<div id="copyright" class="doctools_section"><h2><a name="copyright">Copyright</a></h2>
<p>Copyright &copy; 2018 Arjen Markus</p>
</div>
</div></body></html>

Changes to idoc/www/tcllib/files/modules/nns/nns_client.html.

228
229
230
231
232
233
234
235

236
237
238
239
240
241
242
228
229
230
231
232
233
234

235
236
237
238
239
240
241
242







-
+







be found in section <span class="sectref"><a href="#section5">OPTIONS</a></span>.</p></dd>
<dt><a name="8"><b class="cmd">::nameserv::configure</b></a></dt>
<dd><p>In this form the command returns a dictionary of all supported
options, and their current values. The list of supported options and
their meaning can be found in section <span class="sectref"><a href="#section5">OPTIONS</a></span>.</p></dd>
<dt><a name="9"><b class="cmd">::nameserv::configure</b> <b class="option">-option</b></a></dt>
<dd><p>In this form the command is an alias for
&quot;<b class="cmd">::nameserv::cget</b> <b class="option">-option</b>]&quot;.
&quot;<b class="cmd">::nameserv::cget</b> <b class="option">-option</b>&quot;.
The list of supported options and their meaning can be found in
section <span class="sectref"><a href="#section5">OPTIONS</a></span>.</p></dd>
<dt><a name="10"><b class="cmd">::nameserv::configure</b> <b class="option">-option</b> <i class="arg">value</i>...</a></dt>
<dd><p>In this form the command is used to configure one or more of the
supported options. At least one option has to be specified, and each
option is followed by its new value.
The list of supported options and their meaning can be found in

Changes to idoc/www/tcllib/files/modules/nns/nns_server.html.

183
184
185
186
187
188
189
190

191
192
193
194
195
196
197
183
184
185
186
187
188
189

190
191
192
193
194
195
196
197







-
+







be found in section <span class="sectref"><a href="#section3">OPTIONS</a></span>.</p></dd>
<dt><a name="5"><b class="cmd">::nameserv::server::configure</b></a></dt>
<dd><p>In this form the command returns a dictionary of all supported
options, and their current values. The list of supported options and
their meaning can be found in section <span class="sectref"><a href="#section3">OPTIONS</a></span>.</p></dd>
<dt><a name="6"><b class="cmd">::nameserv::server::configure</b> <b class="option">-option</b></a></dt>
<dd><p>In this form the command is an alias for
&quot;<b class="cmd">::nameserv::server::cget</b> <b class="option">-option</b>]&quot;.
&quot;<b class="cmd">::nameserv::server::cget</b> <b class="option">-option</b>&quot;.
The list of supported options and their meaning can be found in
section <span class="sectref"><a href="#section3">OPTIONS</a></span>.</p></dd>
<dt><a name="7"><b class="cmd">::nameserv::server::configure</b> <b class="option">-option</b> <i class="arg">value</i>...</a></dt>
<dd><p>In this form the command is used to configure one or more of the
supported options. At least one option has to be specified, and each
option is followed by its new value.
The list of supported options and their meaning can be found in

Changes to idoc/www/tcllib/files/modules/oometa/oometa.html.

242
243
244
245
246
247
248
249

250
251
252
253
254
255
256
242
243
244
245
246
247
248

249
250
251
252
253
254
255
256







-
+







<dt><a name="11"><b class="cmd">oo::object method meta</b></a></dt>
<dd><p>The package injects a new method <b class="cmd">meta</b> into <b class="cmd">oo::object</b>. <b class="cmd">oo::object</b> combines the data
for its class (as provided by <b class="cmd">oo::meta::metadata</b>), with a local variable <em>meta</em> to
produce a local picture of metadata.
This method provides the following additional commands:</p></dd>
<dt><a name="12"><b class="cmd">oo::object method meta cget</b> <span class="opt">?<i class="arg">field</i>?</span> <span class="opt">?<i class="arg">...</i>?</span> <i class="arg">field</i></a></dt>
<dd><p>Attempts to locate a singlar leaf, and return its value. For single option lookups, this
is faster than <b class="cmd">my meta getnull</b> <span class="opt">?<i class="arg">field</i>?</span> <span class="opt">?<i class="arg">...</i>?</span> <i class="arg">field</i>], because
is faster than <b class="cmd">my meta getnull</b> <span class="opt">?<i class="arg">field</i>?</span> <span class="opt">?<i class="arg">...</i>?</span> <i class="arg">field</i>, because
it performs a search instead directly instead of producing the recursive merge product
between the class metadata, the local <em>meta</em> variable, and THEN performing the search.</p></dd>
</dl>
</div>
<div id="section5" class="doctools_section"><h2><a name="section5">Bugs, Ideas, Feedback</a></h2>
<p>This document, and the package it describes, will undoubtedly contain
bugs and other problems.

Changes to idoc/www/tcllib/files/modules/pop3d/pop3d.html.

273
274
275
276
277
278
279
280
281

282
283
284
285
286
287
288
273
274
275
276
277
278
279


280
281
282
283
284
285
286
287







-
-
+







<p>Here we describe the interface which has to be provided by the storage
callback so that pop3 servers following the interface of this module
are able to use it. The <i class="arg">mbox</i> argument is the storage reference
as returned by the <b class="method">lookup</b> method of the authentication
command, see section <span class="sectref"><a href="#section3">Authentication</a></span>.</p>
<dl class="doctools_definitions">
<dt><a name="14"><i class="arg">storageCmd</i> <b class="method">dele</b> <i class="arg">mbox</i> <i class="arg">msgList</i></a></dt>
<dd><p>]
Deletes the messages whose numeric ids are contained in the
<dd><p>Deletes the messages whose numeric ids are contained in the
<i class="arg">msgList</i> from the mailbox specified via <i class="arg">mbox</i>.</p></dd>
<dt><a name="15"><i class="arg">storageCmd</i> <b class="method">lock</b> <i class="arg">mbox</i></a></dt>
<dd><p>This method locks the specified mailbox for use by a single connection
to the server. This is necessary to prevent havoc if several
connections to the same mailbox are open. The complementary method is
<b class="method">unlock</b>. The command will return true if the lock could be set
successfully or false if not.</p></dd>

Changes to idoc/www/tcllib/files/modules/practcl/practcl.html.

94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111


112
113
114
115
116
117
118
119
120
121






































122
123
124
125
126
127
128
129
130
131

132
133


134
135
136
137
138
139
140
141
142
143







































































































































































































































































144
145
146
147
148
149
150
151
































































































































































































































































































































































































































































































































































































































































































































































































152
153

154
155

156
157
158




159
160
161
162
163
164
165
166
167
168
169










170
171
172
173
174
175
176
177

178

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
201
94
95
96
97
98
99
100











101
102
103
104
105
106
107
108
109
110


111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

158
159
160
161
162










163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432

433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201

1202


1203


1204
1205
1206
1207
1208
1209










1210
1211
1212
1213
1214
1215
1216
1217
1218
1219






1220
1221
1222

1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237

1238
1239
1240
1241
1242
1243
1244
1245
1246







-
-
-
-
-
-
-
-
-
-
-
+
+








-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+


+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
-
-
+
-
-

+
+
+
+

-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-


+
-
+














-
+








</head>
<!-- Generated from file 'practcl.man' by tcllib/doctools with format 'html'
   -->
<!-- Copyright &amp;copy; 2016-2018 Sean Woods &amp;lt;[email protected]&amp;gt;
   -->
<!-- practcl.n
   -->
<body><hr> [
   <a href="../../../../../../../../home">Tcllib Home</a>
| <a href="../../../../toc.html">Main Table Of Contents</a>
| <a href="../../../toc.html">Table Of Contents</a>
| <a href="../../../../index.html">Keyword Index</a>
| <a href="../../../../toc0.html">Categories</a>
| <a href="../../../../toc1.html">Modules</a>
| <a href="../../../../toc2.html">Applications</a>
 ] <hr>
<div class="doctools">
<h1 class="doctools_title">practcl(n) 0.11 tcllib &quot;The The Proper Rational API for C to Tool Command Language Module&quot;</h1>
<body><div class="doctools">
<h1 class="doctools_title">practcl(n) 0.12 practcl &quot;The The Proper Rational API for C to Tool Command Language Module&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>practcl - The Practcl Module</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a></li>
<li class="doctools_section"><a href="#section2">COMMANDS</a></li>
<li class="doctools_section"><a href="#section3">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#section2">Commands</a></li>
<li class="doctools_section"><a href="#section3">Classes</a>
<ul>
<li class="doctools_subsection"><a href="#subsection1">Class  practcl::metaclass</a></li>
<li class="doctools_subsection"><a href="#subsection2">Class  practcl::toolset</a></li>
<li class="doctools_subsection"><a href="#subsection3">Class  practcl::toolset.gcc</a></li>
<li class="doctools_subsection"><a href="#subsection4">Class  practcl::toolset.msvc</a></li>
<li class="doctools_subsection"><a href="#subsection5">Class  practcl::make_obj</a></li>
<li class="doctools_subsection"><a href="#subsection6">Class  practcl::object</a></li>
<li class="doctools_subsection"><a href="#subsection7">Class  practcl::dynamic</a></li>
<li class="doctools_subsection"><a href="#subsection8">Class  practcl::product</a></li>
<li class="doctools_subsection"><a href="#subsection9">Class  practcl::product.cheader</a></li>
<li class="doctools_subsection"><a href="#subsection10">Class  practcl::product.csource</a></li>
<li class="doctools_subsection"><a href="#subsection11">Class  practcl::product.clibrary</a></li>
<li class="doctools_subsection"><a href="#subsection12">Class  practcl::product.dynamic</a></li>
<li class="doctools_subsection"><a href="#subsection13">Class  practcl::product.critcl</a></li>
<li class="doctools_subsection"><a href="#subsection14">Class  practcl::module</a></li>
<li class="doctools_subsection"><a href="#subsection15">Class  practcl::project</a></li>
<li class="doctools_subsection"><a href="#subsection16">Class  practcl::library</a></li>
<li class="doctools_subsection"><a href="#subsection17">Class  practcl::tclkit</a></li>
<li class="doctools_subsection"><a href="#subsection18">Class  practcl::distribution</a></li>
<li class="doctools_subsection"><a href="#subsection19">Class  practcl::distribution.snapshot</a></li>
<li class="doctools_subsection"><a href="#subsection20">Class  practcl::distribution.fossil</a></li>
<li class="doctools_subsection"><a href="#subsection21">Class  practcl::distribution.git</a></li>
<li class="doctools_subsection"><a href="#subsection22">Class  practcl::subproject</a></li>
<li class="doctools_subsection"><a href="#subsection23">Class  practcl::subproject.source</a></li>
<li class="doctools_subsection"><a href="#subsection24">Class  practcl::subproject.teapot</a></li>
<li class="doctools_subsection"><a href="#subsection25">Class  practcl::subproject.kettle</a></li>
<li class="doctools_subsection"><a href="#subsection26">Class  practcl::subproject.critcl</a></li>
<li class="doctools_subsection"><a href="#subsection27">Class  practcl::subproject.sak</a></li>
<li class="doctools_subsection"><a href="#subsection28">Class  practcl::subproject.binary</a></li>
<li class="doctools_subsection"><a href="#subsection29">Class  practcl::subproject.tea</a></li>
<li class="doctools_subsection"><a href="#subsection30">Class  practcl::subproject.library</a></li>
<li class="doctools_subsection"><a href="#subsection31">Class  practcl::subproject.external</a></li>
<li class="doctools_subsection"><a href="#subsection32">Class  practcl::subproject.core</a></li>
</ul>
</li>
<li class="doctools_section"><a href="#section4">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
<li class="doctools_section"><a href="#category">Category</a></li>
<li class="doctools_section"><a href="#copyright">Copyright</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">TclOO 1.0</b></li>
<li>package require <b class="pkgname">practcl 0.11</b></li>
<li>package require <b class="pkgname">practcl 0.12</b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1">proc <b class="cmd">Proc</b> <i class="arg">name</i> <i class="arg">arglist</i> <i class="arg">body</i></a></li>
<li><a href="#2">proc <b class="cmd">noop</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#1"><b class="cmd">CPUTS</b> <i class="arg">varname</i> <i class="arg">body</i> <span class="opt">?<i class="arg">body</i>...?</span></a></li>
<li><a href="#2"><b class="cmd">practcl::_isdirectory</b> <i class="arg">path</i></a></li>
<li><a href="#3"><b class="cmd">practcl::object</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#4"><b class="cmd">practcl::library</b> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#5"><b class="cmd">practcl::exe</b> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#6"><b class="cmd">practcl::product</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#7"><b class="cmd">practcl::cheader</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#8"><b class="cmd">practcl::csource</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#9"><b class="cmd">practcl::module</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#10"><b class="cmd">practcl::submodule</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></li>
<li><a href="#3">proc <b class="cmd">practcl::debug</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#4">proc <b class="cmd">practcl::doexec</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#5">proc <b class="cmd">practcl::doexec_in</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#6">proc <b class="cmd">practcl::dotclexec</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#7">proc <b class="cmd">practcl::domake</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#8">proc <b class="cmd">practcl::domake.tcl</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#9">proc <b class="cmd">practcl::fossil</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#10">proc <b class="cmd">practcl::fossil_status</b> <i class="arg">dir</i></a></li>
<li><a href="#11">proc <b class="cmd">practcl::os</b></a></li>
<li><a href="#12">proc <b class="cmd">practcl::mkzip</b> <i class="arg">exename</i> <i class="arg">barekit</i> <i class="arg">vfspath</i></a></li>
<li><a href="#13">proc <b class="cmd">practcl::sort_dict</b> <i class="arg">list</i></a></li>
<li><a href="#14">proc <b class="cmd">practcl::local_os</b></a></li>
<li><a href="#15">proc <b class="cmd">practcl::config.tcl</b> <i class="arg">path</i></a></li>
<li><a href="#16">proc <b class="cmd">practcl::read_configuration</b> <i class="arg">path</i></a></li>
<li><a href="#17">proc <b class="cmd">practcl::tcllib_require</b> <i class="arg">pkg</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#18">proc <b class="cmd">practcl::platform::tcl_core_options</b> <i class="arg">os</i></a></li>
<li><a href="#19">proc <b class="cmd">practcl::platform::tk_core_options</b> <i class="arg">os</i></a></li>
<li><a href="#20">proc <b class="cmd">practcl::read_rc_file</b> <i class="arg">filename</i> <span class="opt">?<i class="arg">localdat</i> <b class="const"></b>?</span></a></li>
<li><a href="#21">proc <b class="cmd">practcl::read_sh_subst</b> <i class="arg">line</i> <i class="arg">info</i></a></li>
<li><a href="#22">proc <b class="cmd">practcl::read_sh_file</b> <i class="arg">filename</i> <span class="opt">?<i class="arg">localdat</i> <b class="const"></b>?</span></a></li>
<li><a href="#23">proc <b class="cmd">practcl::read_Config.sh</b> <i class="arg">filename</i></a></li>
<li><a href="#24">proc <b class="cmd">practcl::read_Makefile</b> <i class="arg">filename</i></a></li>
<li><a href="#25">proc <b class="cmd">practcl::cputs</b> <i class="arg">varname</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#26">proc <b class="cmd">practcl::tcl_to_c</b> <i class="arg">body</i></a></li>
<li><a href="#27">proc <b class="cmd">practcl::_tagblock</b> <i class="arg">text</i> <span class="opt">?<i class="arg">style</i> <b class="const">tcl</b>?</span> <span class="opt">?<i class="arg">note</i> <b class="const"></b>?</span></a></li>
<li><a href="#28">proc <b class="cmd">practcl::de_shell</b> <i class="arg">data</i></a></li>
<li><a href="#29">proc <b class="cmd">practcl::cat</b> <i class="arg">fname</i></a></li>
<li><a href="#30">proc <b class="cmd">practcl::grep</b> <i class="arg">pattern</i> <span class="opt">?<i class="arg">files</i> <b class="const"></b>?</span></a></li>
<li><a href="#31">proc <b class="cmd">practcl::file_lexnormalize</b> <i class="arg">sp</i></a></li>
<li><a href="#32">proc <b class="cmd">practcl::file_relative</b> <i class="arg">base</i> <i class="arg">dst</i></a></li>
<li><a href="#33">proc <b class="cmd">practcl::log</b> <i class="arg">fname</i> <i class="arg">comment</i></a></li>
<li><a href="#34">proc <b class="cmd">practcl::_isdirectory</b> <i class="arg">name</i></a></li>
<li><a href="#35">proc <b class="cmd">practcl::_pkgindex_directory</b> <i class="arg">path</i></a></li>
<li><a href="#36">proc <b class="cmd">practcl::_pkgindex_path_subdir</b> <i class="arg">path</i></a></li>
<li><a href="#37">proc <b class="cmd">practcl::pkgindex_path</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#38">proc <b class="cmd">practcl::installDir</b> <i class="arg">d1</i> <i class="arg">d2</i></a></li>
<li><a href="#39">proc <b class="cmd">practcl::copyDir</b> <i class="arg">d1</i> <i class="arg">d2</i> <span class="opt">?<i class="arg">toplevel</i> <b class="const">1</b>?</span></a></li>
<li><a href="#40">proc <b class="cmd">practcl::trigger</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#41">proc <b class="cmd">practcl::depends</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#42">proc <b class="cmd">practcl::target</b> <i class="arg">name</i> <i class="arg">info</i> <span class="opt">?<i class="arg">action</i> <b class="const"></b>?</span></a></li>
<li><a href="#43">method <b class="cmd">_MorphPatterns</b></a></li>
<li><a href="#44">method <b class="cmd">define</b> <i class="arg">submethod</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#45">method <b class="cmd">graft</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#46">method <b class="cmd">initialize</b></a></li>
<li><a href="#47">method <b class="cmd">link</b> <i class="arg">command</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#48">method <b class="cmd">morph</b> <i class="arg">classname</i></a></li>
<li><a href="#49">method <b class="cmd">mixin</b> <i class="arg">slot</i> <i class="arg">classname</i></a></li>
<li><a href="#50">method <b class="cmd">organ</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#51">method <b class="cmd">script</b> <i class="arg">script</i></a></li>
<li><a href="#52">method <b class="cmd">select</b></a></li>
<li><a href="#53">method <b class="cmd">source</b> <i class="arg">filename</i></a></li>
<li><a href="#54">method <b class="cmd">select</b> <i class="arg">object</i></a></li>
<li><a href="#55">method <b class="cmd">config.sh</b></a></li>
<li><a href="#56">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></li>
<li><a href="#57">method <b class="cmd">MakeDir</b> <i class="arg">srcdir</i></a></li>
<li><a href="#58">method <b class="cmd">read_configuration</b></a></li>
<li><a href="#59">method <b class="cmd">build-cflags</b> <i class="arg">PROJECT</i> <i class="arg">DEFS</i> <i class="arg">namevar</i> <i class="arg">versionvar</i> <i class="arg">defsvar</i></a></li>
<li><a href="#60">method <b class="cmd">critcl</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#61">method <b class="cmd">make-autodetect</b></a></li>
<li><a href="#62">method <b class="cmd">Autoconf</b></a></li>
<li><a href="#63">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></li>
<li><a href="#64">method <b class="cmd">ConfigureOpts</b></a></li>
<li><a href="#65">method <b class="cmd">MakeDir</b> <i class="arg">srcdir</i></a></li>
<li><a href="#66">method <b class="cmd">make-autodetect</b></a></li>
<li><a href="#67">method <b class="cmd">make-clean</b></a></li>
<li><a href="#68">method <b class="cmd">make-compile</b></a></li>
<li><a href="#69">method <b class="cmd">make-install</b> <i class="arg">DEST</i></a></li>
<li><a href="#70">method <b class="cmd">build-compile-sources</b> <i class="arg">PROJECT</i> <i class="arg">COMPILE</i> <i class="arg">CPPCOMPILE</i> <i class="arg">INCLUDES</i></a></li>
<li><a href="#71">method <b class="cmd">build-Makefile</b> <i class="arg">path</i> <i class="arg">PROJECT</i></a></li>
<li><a href="#72">method <b class="cmd">build-library</b> <i class="arg">outfile</i> <i class="arg">PROJECT</i></a></li>
<li><a href="#73">method <b class="cmd">build-tclsh</b> <i class="arg">outfile</i> <i class="arg">PROJECT</i></a></li>
<li><a href="#74">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></li>
<li><a href="#75">method <b class="cmd">make-autodetect</b></a></li>
<li><a href="#76">method <b class="cmd">make-clean</b></a></li>
<li><a href="#77">method <b class="cmd">make-compile</b></a></li>
<li><a href="#78">method <b class="cmd">make-install</b> <i class="arg">DEST</i></a></li>
<li><a href="#79">method <b class="cmd">MakeDir</b> <i class="arg">srcdir</i></a></li>
<li><a href="#80">method <b class="cmd">NmakeOpts</b></a></li>
<li><a href="#81">method <b class="cmd">constructor</b> <i class="arg">module_object</i> <i class="arg">name</i> <i class="arg">info</i> <span class="opt">?<i class="arg">action_body</i> <b class="const"></b>?</span></a></li>
<li><a href="#82">method <b class="cmd">do</b></a></li>
<li><a href="#83">method <b class="cmd">check</b></a></li>
<li><a href="#84">method <b class="cmd">output</b></a></li>
<li><a href="#85">method <b class="cmd">reset</b></a></li>
<li><a href="#86">method <b class="cmd">triggers</b></a></li>
<li><a href="#87">method <b class="cmd">constructor</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#88">method <b class="cmd">child</b> <i class="arg">method</i></a></li>
<li><a href="#89">method <b class="cmd">go</b></a></li>
<li><a href="#90">method <b class="cmd">cstructure</b> <i class="arg">name</i> <i class="arg">definition</i> <span class="opt">?<i class="arg">argdat</i> <b class="const"></b>?</span></a></li>
<li><a href="#91">method <b class="cmd">include</b> <i class="arg">header</i></a></li>
<li><a href="#92">method <b class="cmd">include_dir</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#93">method <b class="cmd">include_directory</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#94">method <b class="cmd">c_header</b> <i class="arg">body</i></a></li>
<li><a href="#95">method <b class="cmd">c_code</b> <i class="arg">body</i></a></li>
<li><a href="#96">method <b class="cmd">c_function</b> <i class="arg">header</i> <i class="arg">body</i> <span class="opt">?<i class="arg">info</i> <b class="const"></b>?</span></a></li>
<li><a href="#97">method <b class="cmd">c_tcloomethod</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></li>
<li><a href="#98">method <b class="cmd">cmethod</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></li>
<li><a href="#99">method <b class="cmd">c_tclproc_nspace</b> <i class="arg">nspace</i></a></li>
<li><a href="#100">method <b class="cmd">c_tclcmd</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></li>
<li><a href="#101">method <b class="cmd">c_tclproc_raw</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></li>
<li><a href="#102">method <b class="cmd">tcltype</b> <i class="arg">name</i> <i class="arg">argdat</i></a></li>
<li><a href="#103">method <b class="cmd">project-compile-products</b></a></li>
<li><a href="#104">method <b class="cmd">implement</b> <i class="arg">path</i></a></li>
<li><a href="#105">method <b class="cmd">initialize</b></a></li>
<li><a href="#106">method <b class="cmd">linktype</b></a></li>
<li><a href="#107">method <b class="cmd">generate-cfile-constant</b></a></li>
<li><a href="#108">method <b class="cmd">generate-cfile-header</b></a></li>
<li><a href="#109">method <b class="cmd">generate-cfile-tclapi</b></a></li>
<li><a href="#110">method <b class="cmd">generate-loader-module</b></a></li>
<li><a href="#111">method <b class="cmd">Collate_Source</b> <i class="arg">CWD</i></a></li>
<li><a href="#112">method <b class="cmd">select</b></a></li>
<li><a href="#113">method <b class="cmd">select</b> <i class="arg">object</i></a></li>
<li><a href="#114">method <b class="cmd">code</b> <i class="arg">section</i> <i class="arg">body</i></a></li>
<li><a href="#115">method <b class="cmd">Collate_Source</b> <i class="arg">CWD</i></a></li>
<li><a href="#116">method <b class="cmd">project-compile-products</b></a></li>
<li><a href="#117">method <b class="cmd">generate-debug</b> <span class="opt">?<i class="arg">spaces</i> <b class="const"></b>?</span></a></li>
<li><a href="#118">method <b class="cmd">generate-cfile-constant</b></a></li>
<li><a href="#119">method <b class="cmd">generate-cfile-public-structure</b></a></li>
<li><a href="#120">method <b class="cmd">generate-cfile-header</b></a></li>
<li><a href="#121">method <b class="cmd">generate-cfile-global</b></a></li>
<li><a href="#122">method <b class="cmd">generate-cfile-private-typedef</b></a></li>
<li><a href="#123">method <b class="cmd">generate-cfile-private-structure</b></a></li>
<li><a href="#124">method <b class="cmd">generate-cfile-functions</b></a></li>
<li><a href="#125">method <b class="cmd">generate-cfile-tclapi</b></a></li>
<li><a href="#126">method <b class="cmd">generate-hfile-public-define</b></a></li>
<li><a href="#127">method <b class="cmd">generate-hfile-public-macro</b></a></li>
<li><a href="#128">method <b class="cmd">generate-hfile-public-typedef</b></a></li>
<li><a href="#129">method <b class="cmd">generate-hfile-public-structure</b></a></li>
<li><a href="#130">method <b class="cmd">generate-hfile-public-headers</b></a></li>
<li><a href="#131">method <b class="cmd">generate-hfile-public-function</b></a></li>
<li><a href="#132">method <b class="cmd">generate-hfile-public-includes</b></a></li>
<li><a href="#133">method <b class="cmd">generate-hfile-public-verbatim</b></a></li>
<li><a href="#134">method <b class="cmd">generate-loader-external</b></a></li>
<li><a href="#135">method <b class="cmd">generate-loader-module</b></a></li>
<li><a href="#136">method <b class="cmd">generate-stub-function</b></a></li>
<li><a href="#137">method <b class="cmd">IncludeAdd</b> <i class="arg">headervar</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#138">method <b class="cmd">generate-tcl-loader</b></a></li>
<li><a href="#139">method <b class="cmd">generate-tcl-pre</b></a></li>
<li><a href="#140">method <b class="cmd">generate-tcl-post</b></a></li>
<li><a href="#141">method <b class="cmd">linktype</b></a></li>
<li><a href="#142">method <b class="cmd">Ofile</b> <i class="arg">filename</i></a></li>
<li><a href="#143">method <b class="cmd">project-static-packages</b></a></li>
<li><a href="#144">method <b class="cmd">toolset-include-directory</b></a></li>
<li><a href="#145">method <b class="cmd">target</b> <i class="arg">method</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#146">method <b class="cmd">project-compile-products</b></a></li>
<li><a href="#147">method <b class="cmd">generate-loader-module</b></a></li>
<li><a href="#148">method <b class="cmd">project-compile-products</b></a></li>
<li><a href="#149">method <b class="cmd">linker-products</b> <i class="arg">configdict</i></a></li>
<li><a href="#150">method <b class="cmd">initialize</b></a></li>
<li><a href="#151">method <b class="cmd">_MorphPatterns</b></a></li>
<li><a href="#152">method <b class="cmd">add</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#153">method <b class="cmd">install-headers</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#154">method <b class="cmd">make</b> <i class="arg">command</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#155">method <b class="cmd">child</b> <i class="arg">which</i></a></li>
<li><a href="#156">method <b class="cmd">generate-c</b></a></li>
<li><a href="#157">method <b class="cmd">generate-h</b></a></li>
<li><a href="#158">method <b class="cmd">generate-loader</b></a></li>
<li><a href="#159">method <b class="cmd">initialize</b></a></li>
<li><a href="#160">method <b class="cmd">implement</b> <i class="arg">path</i></a></li>
<li><a href="#161">method <b class="cmd">linktype</b></a></li>
<li><a href="#162">method <b class="cmd">_MorphPatterns</b></a></li>
<li><a href="#163">method <b class="cmd">constructor</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#164">method <b class="cmd">add_object</b> <i class="arg">object</i></a></li>
<li><a href="#165">method <b class="cmd">add_project</b> <i class="arg">pkg</i> <i class="arg">info</i> <span class="opt">?<i class="arg">oodefine</i> <b class="const"></b>?</span></a></li>
<li><a href="#166">method <b class="cmd">add_tool</b> <i class="arg">pkg</i> <i class="arg">info</i> <span class="opt">?<i class="arg">oodefine</i> <b class="const"></b>?</span></a></li>
<li><a href="#167">method <b class="cmd">build-tclcore</b></a></li>
<li><a href="#168">method <b class="cmd">child</b> <i class="arg">which</i></a></li>
<li><a href="#169">method <b class="cmd">linktype</b></a></li>
<li><a href="#170">method <b class="cmd">project</b> <i class="arg">pkg</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#171">method <b class="cmd">tclcore</b></a></li>
<li><a href="#172">method <b class="cmd">tkcore</b></a></li>
<li><a href="#173">method <b class="cmd">tool</b> <i class="arg">pkg</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#174">method <b class="cmd">clean</b> <i class="arg">PATH</i></a></li>
<li><a href="#175">method <b class="cmd">project-compile-products</b></a></li>
<li><a href="#176">method <b class="cmd">go</b></a></li>
<li><a href="#177">method <b class="cmd">generate-decls</b> <i class="arg">pkgname</i> <i class="arg">path</i></a></li>
<li><a href="#178">method <b class="cmd">implement</b> <i class="arg">path</i></a></li>
<li><a href="#179">method <b class="cmd">generate-make</b> <i class="arg">path</i></a></li>
<li><a href="#180">method <b class="cmd">linktype</b></a></li>
<li><a href="#181">method <b class="cmd">package-ifneeded</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#182">method <b class="cmd">shared_library</b> <span class="opt">?<i class="arg">filename</i> <b class="const"></b>?</span></a></li>
<li><a href="#183">method <b class="cmd">static_library</b> <span class="opt">?<i class="arg">filename</i> <b class="const"></b>?</span></a></li>
<li><a href="#184">method <b class="cmd">build-tclkit_main</b> <i class="arg">PROJECT</i> <i class="arg">PKG_OBJS</i></a></li>
<li><a href="#185">method <b class="cmd">Collate_Source</b> <i class="arg">CWD</i></a></li>
<li><a href="#186">method <b class="cmd">wrap</b> <i class="arg">PWD</i> <i class="arg">exename</i> <i class="arg">vfspath</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#187">method <b class="cmd">Sandbox</b> <i class="arg">object</i></a></li>
<li><a href="#188">method <b class="cmd">select</b> <i class="arg">object</i></a></li>
<li><a href="#189">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></li>
<li><a href="#190">method <b class="cmd">claim_object</b> <i class="arg">object</i></a></li>
<li><a href="#191">method <b class="cmd">scm_info</b></a></li>
<li><a href="#192">method <b class="cmd">DistroMixIn</b></a></li>
<li><a href="#193">method <b class="cmd">Sandbox</b></a></li>
<li><a href="#194">method <b class="cmd">SrcDir</b></a></li>
<li><a href="#195">method <b class="cmd">ScmTag</b></a></li>
<li><a href="#196">method <b class="cmd">ScmClone</b></a></li>
<li><a href="#197">method <b class="cmd">ScmUnpack</b></a></li>
<li><a href="#198">method <b class="cmd">ScmUpdate</b></a></li>
<li><a href="#199">method <b class="cmd">Unpack</b></a></li>
<li><a href="#200">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></li>
<li><a href="#201">method <b class="cmd">claim_object</b> <i class="arg">object</i></a></li>
<li><a href="#202">method <b class="cmd">ScmUnpack</b></a></li>
<li><a href="#203">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></li>
<li><a href="#204">method <b class="cmd">claim_object</b> <i class="arg">obj</i></a></li>
<li><a href="#205">method <b class="cmd">scm_info</b></a></li>
<li><a href="#206">method <b class="cmd">ScmClone</b></a></li>
<li><a href="#207">method <b class="cmd">ScmTag</b></a></li>
<li><a href="#208">method <b class="cmd">ScmUnpack</b></a></li>
<li><a href="#209">method <b class="cmd">ScmUpdate</b></a></li>
<li><a href="#210">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></li>
<li><a href="#211">method <b class="cmd">claim_object</b> <i class="arg">obj</i></a></li>
<li><a href="#212">method <b class="cmd">ScmTag</b></a></li>
<li><a href="#213">method <b class="cmd">ScmUnpack</b></a></li>
<li><a href="#214">method <b class="cmd">ScmUpdate</b></a></li>
<li><a href="#215">method <b class="cmd">_MorphPatterns</b></a></li>
<li><a href="#216">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></li>
<li><a href="#217">method <b class="cmd">child</b> <i class="arg">which</i></a></li>
<li><a href="#218">method <b class="cmd">compile</b></a></li>
<li><a href="#219">method <b class="cmd">go</b></a></li>
<li><a href="#220">method <b class="cmd">install</b> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#221">method <b class="cmd">linktype</b></a></li>
<li><a href="#222">method <b class="cmd">linker-products</b> <i class="arg">configdict</i></a></li>
<li><a href="#223">method <b class="cmd">linker-external</b> <i class="arg">configdict</i></a></li>
<li><a href="#224">method <b class="cmd">linker-extra</b> <i class="arg">configdict</i></a></li>
<li><a href="#225">method <b class="cmd">env-bootstrap</b></a></li>
<li><a href="#226">method <b class="cmd">env-exec</b></a></li>
<li><a href="#227">method <b class="cmd">env-install</b></a></li>
<li><a href="#228">method <b class="cmd">env-load</b></a></li>
<li><a href="#229">method <b class="cmd">env-present</b></a></li>
<li><a href="#230">method <b class="cmd">sources</b></a></li>
<li><a href="#231">method <b class="cmd">update</b></a></li>
<li><a href="#232">method <b class="cmd">unpack</b></a></li>
<li><a href="#233">method <b class="cmd">env-bootstrap</b></a></li>
<li><a href="#234">method <b class="cmd">env-present</b></a></li>
<li><a href="#235">method <b class="cmd">linktype</b></a></li>
<li><a href="#236">method <b class="cmd">env-bootstrap</b></a></li>
<li><a href="#237">method <b class="cmd">env-install</b></a></li>
<li><a href="#238">method <b class="cmd">env-present</b></a></li>
<li><a href="#239">method <b class="cmd">install</b> <i class="arg">DEST</i></a></li>
<li><a href="#240">method <b class="cmd">kettle</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#241">method <b class="cmd">install</b> <i class="arg">DEST</i></a></li>
<li><a href="#242">method <b class="cmd">install</b> <i class="arg">DEST</i></a></li>
<li><a href="#243">method <b class="cmd">env-bootstrap</b></a></li>
<li><a href="#244">method <b class="cmd">env-install</b></a></li>
<li><a href="#245">method <b class="cmd">env-present</b></a></li>
<li><a href="#246">method <b class="cmd">install</b> <i class="arg">DEST</i></a></li>
<li><a href="#247">method <b class="cmd">install-module</b> <i class="arg">DEST</i> <span class="opt">?<i class="arg">args</i>?</span></a></li>
<li><a href="#248">method <b class="cmd">clean</b></a></li>
<li><a href="#249">method <b class="cmd">env-install</b></a></li>
<li><a href="#250">method <b class="cmd">project-compile-products</b></a></li>
<li><a href="#251">method <b class="cmd">ComputeInstall</b></a></li>
<li><a href="#252">method <b class="cmd">go</b></a></li>
<li><a href="#253">method <b class="cmd">linker-products</b> <i class="arg">configdict</i></a></li>
<li><a href="#254">method <b class="cmd">project-static-packages</b></a></li>
<li><a href="#255">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></li>
<li><a href="#256">method <b class="cmd">compile</b></a></li>
<li><a href="#257">method <b class="cmd">Configure</b></a></li>
<li><a href="#258">method <b class="cmd">install</b> <i class="arg">DEST</i></a></li>
<li><a href="#259">method <b class="cmd">install</b> <i class="arg">DEST</i></a></li>
<li><a href="#260">method <b class="cmd">install</b> <i class="arg">DEST</i></a></li>
<li><a href="#261">method <b class="cmd">env-bootstrap</b></a></li>
<li><a href="#262">method <b class="cmd">env-present</b></a></li>
<li><a href="#263">method <b class="cmd">env-install</b></a></li>
<li><a href="#264">method <b class="cmd">go</b></a></li>
<li><a href="#265">method <b class="cmd">linktype</b></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>The Practcl module is a tool for integrating large modules for C API
Tcl code that requires custom Tcl types and TclOO objects.</p>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">COMMANDS</a></h2>
<div id="section2" class="doctools_section"><h2><a name="section2">Commands</a></h2>
<dl class="doctools_definitions">
<dt><a name="1">proc <b class="cmd">Proc</b> <i class="arg">name</i> <i class="arg">arglist</i> <i class="arg">body</i></a></dt>
<dd><p>Generate a proc if no command already exists by that name</p></dd>
<dt><a name="2">proc <b class="cmd">noop</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>A command to do nothing. A handy way of
negating an instruction without
having to comment it completely out.
It's also a handy attachment point for
an object to be named later</p></dd>
<dt><a name="3">proc <b class="cmd">practcl::debug</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="4">proc <b class="cmd">practcl::doexec</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Drop in a static copy of Tcl</p></dd>
<dt><a name="5">proc <b class="cmd">practcl::doexec_in</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="6">proc <b class="cmd">practcl::dotclexec</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="7">proc <b class="cmd">practcl::domake</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="8">proc <b class="cmd">practcl::domake.tcl</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="9">proc <b class="cmd">practcl::fossil</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="10">proc <b class="cmd">practcl::fossil_status</b> <i class="arg">dir</i></a></dt>
<dd></dd>
<dt><a name="11">proc <b class="cmd">practcl::os</b></a></dt>
<dd></dd>
<dt><a name="12">proc <b class="cmd">practcl::mkzip</b> <i class="arg">exename</i> <i class="arg">barekit</i> <i class="arg">vfspath</i></a></dt>
<dd><p>Build a zipfile. On tcl8.6 this invokes the native Zip implementation
on older interpreters this invokes zip via exec</p></dd>
<dt><a name="13">proc <b class="cmd">practcl::sort_dict</b> <i class="arg">list</i></a></dt>
<dd><p>Dictionary sort a key/value list. Needed because pre tcl8.6
does not have <em>lsort -stride 2</em></p></dd>
<dt><a name="14">proc <b class="cmd">practcl::local_os</b></a></dt>
<dd></dd>
<dt><a name="15">proc <b class="cmd">practcl::config.tcl</b> <i class="arg">path</i></a></dt>
<dd><p>Detect local platform</p></dd>
<dt><a name="16">proc <b class="cmd">practcl::read_configuration</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="17">proc <b class="cmd">practcl::tcllib_require</b> <i class="arg">pkg</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Try to load  a package, and failing that
retrieve tcllib</p></dd>
<dt><a name="18">proc <b class="cmd">practcl::platform::tcl_core_options</b> <i class="arg">os</i></a></dt>
<dd></dd>
<dt><a name="19">proc <b class="cmd">practcl::platform::tk_core_options</b> <i class="arg">os</i></a></dt>
<dd></dd>
<dt><a name="20">proc <b class="cmd">practcl::read_rc_file</b> <i class="arg">filename</i> <span class="opt">?<i class="arg">localdat</i> <b class="const"></b>?</span></a></dt>
<dd><p>Read a stylized key/value list stored in a file</p></dd>
<dt><a name="21">proc <b class="cmd">practcl::read_sh_subst</b> <i class="arg">line</i> <i class="arg">info</i></a></dt>
<dd></dd>
<dt><a name="22">proc <b class="cmd">practcl::read_sh_file</b> <i class="arg">filename</i> <span class="opt">?<i class="arg">localdat</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="23">proc <b class="cmd">practcl::read_Config.sh</b> <i class="arg">filename</i></a></dt>
<dd><p>A simpler form of read_sh_file tailored
to pulling data from (tcl|tk)Config.sh</p></dd>
<dt><a name="24">proc <b class="cmd">practcl::read_Makefile</b> <i class="arg">filename</i></a></dt>
<dd><p>A simpler form of read_sh_file tailored
to pulling data from a Makefile</p></dd>
<dt><a name="25">proc <b class="cmd">practcl::cputs</b> <i class="arg">varname</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Append arguments to a buffer
The command works like puts in that each call will also insert
a line feed. Unlike puts, blank links in the interstitial are
suppressed</p></dd>
<dt><a name="26">proc <b class="cmd">practcl::tcl_to_c</b> <i class="arg">body</i></a></dt>
<dd></dd>
<dt><a name="27">proc <b class="cmd">practcl::_tagblock</b> <i class="arg">text</i> <span class="opt">?<i class="arg">style</i> <b class="const">tcl</b>?</span> <span class="opt">?<i class="arg">note</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="28">proc <b class="cmd">practcl::de_shell</b> <i class="arg">data</i></a></dt>
<dd></dd>
<dt><a name="29">proc <b class="cmd">practcl::cat</b> <i class="arg">fname</i></a></dt>
<dd><p>Bits stolen from fileutil</p></dd>
<dt><a name="30">proc <b class="cmd">practcl::grep</b> <i class="arg">pattern</i> <span class="opt">?<i class="arg">files</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="31">proc <b class="cmd">practcl::file_lexnormalize</b> <i class="arg">sp</i></a></dt>
<dd></dd>
<dt><a name="32">proc <b class="cmd">practcl::file_relative</b> <i class="arg">base</i> <i class="arg">dst</i></a></dt>
<dd></dd>
<dt><a name="33">proc <b class="cmd">practcl::log</b> <i class="arg">fname</i> <i class="arg">comment</i></a></dt>
<dd></dd>
<dt><a name="34">proc <b class="cmd">practcl::_isdirectory</b> <i class="arg">name</i></a></dt>
<dd><p>Installer tools</p></dd>
<dt><a name="35">proc <b class="cmd">practcl::_pkgindex_directory</b> <i class="arg">path</i></a></dt>
<dd><p>Return true if the pkgindex file contains
any statement other than &quot;package ifneeded&quot;
and/or if any package ifneeded loads a DLL</p></dd>
<dt><a name="36">proc <b class="cmd">practcl::_pkgindex_path_subdir</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="37">proc <b class="cmd">practcl::pkgindex_path</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Index all paths given as though they will end up in the same
virtual file system</p></dd>
<dt><a name="38">proc <b class="cmd">practcl::installDir</b> <i class="arg">d1</i> <i class="arg">d2</i></a></dt>
<dd></dd>
<dt><a name="39">proc <b class="cmd">practcl::copyDir</b> <i class="arg">d1</i> <i class="arg">d2</i> <span class="opt">?<i class="arg">toplevel</i> <b class="const">1</b>?</span></a></dt>
<dd></dd>
<dt><a name="40">proc <b class="cmd">practcl::trigger</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="41">proc <b class="cmd">practcl::depends</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="42">proc <b class="cmd">practcl::target</b> <i class="arg">name</i> <i class="arg">info</i> <span class="opt">?<i class="arg">action</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
</dl>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">Classes</a></h2>
<div id="subsection1" class="doctools_subsection"><h3><a name="subsection1">Class  practcl::metaclass</a></h3>
<p><em>ancestors</em>: <b class="class">oo::object</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="43">method <b class="cmd">_MorphPatterns</b></a></dt>
<dd></dd>
<dt><a name="44">method <b class="cmd">define</b> <i class="arg">submethod</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="45">method <b class="cmd">graft</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="46">method <b class="cmd">initialize</b></a></dt>
<dd></dd>
<dt><a name="47">method <b class="cmd">link</b> <i class="arg">command</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="48">method <b class="cmd">morph</b> <i class="arg">classname</i></a></dt>
<dd></dd>
<dt><a name="49">method <b class="cmd">mixin</b> <i class="arg">slot</i> <i class="arg">classname</i></a></dt>
<dd></dd>
<dt><a name="50">method <b class="cmd">organ</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="51">method <b class="cmd">script</b> <i class="arg">script</i></a></dt>
<dd></dd>
<dt><a name="52">method <b class="cmd">select</b></a></dt>
<dd></dd>
<dt><a name="53">method <b class="cmd">source</b> <i class="arg">filename</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection2" class="doctools_subsection"><h3><a name="subsection2">Class  practcl::toolset</a></h3>
<p>Ancestor-less class intended to be a mixin
which defines a family of build related behaviors
that are modified when targetting either gcc or msvc</p>
<p><b class="class">Class Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="54">method <b class="cmd">select</b> <i class="arg">object</i></a></dt>
<dd></dd>
</dl>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="55">method <b class="cmd">config.sh</b></a></dt>
<dd><p>find or fake a key/value list describing this project</p></dd>
<dt><a name="56">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></dt>
<dd></dd>
<dt><a name="57">method <b class="cmd">MakeDir</b> <i class="arg">srcdir</i></a></dt>
<dd></dd>
<dt><a name="58">method <b class="cmd">read_configuration</b></a></dt>
<dd></dd>
<dt><a name="59">method <b class="cmd">build-cflags</b> <i class="arg">PROJECT</i> <i class="arg">DEFS</i> <i class="arg">namevar</i> <i class="arg">versionvar</i> <i class="arg">defsvar</i></a></dt>
<dd><p>method DEFS
This method populates 4 variables:
name - The name of the package
version - The version of the package
defs - C flags passed to the compiler
includedir - A list of paths to feed to the compiler for finding headers</p></dd>
<dt><a name="60">method <b class="cmd">critcl</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="61">method <b class="cmd">make-autodetect</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection3" class="doctools_subsection"><h3><a name="subsection3">Class  practcl::toolset.gcc</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::toolset</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="62">method <b class="cmd">Autoconf</b></a></dt>
<dd></dd>
<dt><a name="63">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></dt>
<dd></dd>
<dt><a name="64">method <b class="cmd">ConfigureOpts</b></a></dt>
<dd></dd>
<dt><a name="65">method <b class="cmd">MakeDir</b> <i class="arg">srcdir</i></a></dt>
<dd><p>Detect what directory contains the Makefile template</p></dd>
<dt><a name="66">method <b class="cmd">make-autodetect</b></a></dt>
<dd></dd>
<dt><a name="67">method <b class="cmd">make-clean</b></a></dt>
<dd></dd>
<dt><a name="68">method <b class="cmd">make-compile</b></a></dt>
<dd></dd>
<dt><a name="69">method <b class="cmd">make-install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
<dt><a name="70">method <b class="cmd">build-compile-sources</b> <i class="arg">PROJECT</i> <i class="arg">COMPILE</i> <i class="arg">CPPCOMPILE</i> <i class="arg">INCLUDES</i></a></dt>
<dd></dd>
<dt><a name="71">method <b class="cmd">build-Makefile</b> <i class="arg">path</i> <i class="arg">PROJECT</i></a></dt>
<dd></dd>
<dt><a name="72">method <b class="cmd">build-library</b> <i class="arg">outfile</i> <i class="arg">PROJECT</i></a></dt>
<dd><p>Produce a static or dynamic library</p></dd>
<dt><a name="73">method <b class="cmd">build-tclsh</b> <i class="arg">outfile</i> <i class="arg">PROJECT</i></a></dt>
<dd><p>Produce a static executable</p></dd>
</dl>
</div>
<div id="subsection4" class="doctools_subsection"><h3><a name="subsection4">Class  practcl::toolset.msvc</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::toolset</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="74">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></dt>
<dd><p>MSVC always builds in the source directory</p></dd>
<dt><a name="75">method <b class="cmd">make-autodetect</b></a></dt>
<dd><p>Do nothing</p></dd>
<dt><a name="76">method <b class="cmd">make-clean</b></a></dt>
<dd></dd>
<dt><a name="77">method <b class="cmd">make-compile</b></a></dt>
<dd></dd>
<dt><a name="78">method <b class="cmd">make-install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
<dt><a name="79">method <b class="cmd">MakeDir</b> <i class="arg">srcdir</i></a></dt>
<dd><p>Detect what directory contains the Makefile template</p></dd>
<dt><a name="80">method <b class="cmd">NmakeOpts</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection5" class="doctools_subsection"><h3><a name="subsection5">Class  practcl::make_obj</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::metaclass</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="81">method <b class="cmd">constructor</b> <i class="arg">module_object</i> <i class="arg">name</i> <i class="arg">info</i> <span class="opt">?<i class="arg">action_body</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="82">method <b class="cmd">do</b></a></dt>
<dd></dd>
<dt><a name="83">method <b class="cmd">check</b></a></dt>
<dd></dd>
<dt><a name="84">method <b class="cmd">output</b></a></dt>
<dd></dd>
<dt><a name="85">method <b class="cmd">reset</b></a></dt>
<dd></dd>
<dt><a name="86">method <b class="cmd">triggers</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection6" class="doctools_subsection"><h3><a name="subsection6">Class  practcl::object</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::metaclass</b></p>
<p>A generic Practcl object</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="87">method <b class="cmd">constructor</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="88">method <b class="cmd">child</b> <i class="arg">method</i></a></dt>
<dd></dd>
<dt><a name="89">method <b class="cmd">go</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection7" class="doctools_subsection"><h3><a name="subsection7">Class  practcl::dynamic</a></h3>
<p>Dynamic blocks do not generate their own .c files,
instead the contribute to the amalgamation
of the main library file</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="90">method <b class="cmd">cstructure</b> <i class="arg">name</i> <i class="arg">definition</i> <span class="opt">?<i class="arg">argdat</i> <b class="const"></b>?</span></a></dt>
<dd><p>Parser functions</p></dd>
<dt><a name="91">method <b class="cmd">include</b> <i class="arg">header</i></a></dt>
<dd></dd>
<dt><a name="92">method <b class="cmd">include_dir</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="93">method <b class="cmd">include_directory</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="94">method <b class="cmd">c_header</b> <i class="arg">body</i></a></dt>
<dd></dd>
<dt><a name="95">method <b class="cmd">c_code</b> <i class="arg">body</i></a></dt>
<dd></dd>
<dt><a name="96">method <b class="cmd">c_function</b> <i class="arg">header</i> <i class="arg">body</i> <span class="opt">?<i class="arg">info</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="97">method <b class="cmd">c_tcloomethod</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="98">method <b class="cmd">cmethod</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></dt>
<dd><p>Alias to classic name</p></dd>
<dt><a name="99">method <b class="cmd">c_tclproc_nspace</b> <i class="arg">nspace</i></a></dt>
<dd></dd>
<dt><a name="100">method <b class="cmd">c_tclcmd</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="101">method <b class="cmd">c_tclproc_raw</b> <i class="arg">name</i> <i class="arg">body</i> <span class="opt">?<i class="arg">arginfo</i> <b class="const"></b>?</span></a></dt>
<dd><p>Alias to classic name</p></dd>
<dt><a name="102">method <b class="cmd">tcltype</b> <i class="arg">name</i> <i class="arg">argdat</i></a></dt>
<dd></dd>
<dt><a name="103">method <b class="cmd">project-compile-products</b></a></dt>
<dd><p>Module interactions</p></dd>
<dt><a name="104">method <b class="cmd">implement</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="105">method <b class="cmd">initialize</b></a></dt>
<dd><p>Practcl internals</p></dd>
<dt><a name="106">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
<dt><a name="107">method <b class="cmd">generate-cfile-constant</b></a></dt>
<dd></dd>
<dt><a name="108">method <b class="cmd">generate-cfile-header</b></a></dt>
<dd></dd>
<dt><a name="109">method <b class="cmd">generate-cfile-tclapi</b></a></dt>
<dd><p>Generate code that provides implements Tcl API
calls</p></dd>
<dt><a name="110">method <b class="cmd">generate-loader-module</b></a></dt>
<dd><p>Generate code that runs when the package/module is
initialized into the interpreter</p></dd>
<dt><a name="111">method <b class="cmd">Collate_Source</b> <i class="arg">CWD</i></a></dt>
<dd></dd>
<dt><a name="112">method <b class="cmd">select</b></a></dt>
<dd><p>Once an object marks itself as some
flavor of dynamic, stop trying to morph
it into something else</p></dd>
</dl>
</div>
<div id="subsection8" class="doctools_subsection"><h3><a name="subsection8">Class  practcl::product</a></h3>
<p>A deliverable for the build system</p>
<p><b class="class">Class Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="113">method <b class="cmd">select</b> <i class="arg">object</i></a></dt>
<dd></dd>
</dl>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="114">method <b class="cmd">code</b> <i class="arg">section</i> <i class="arg">body</i></a></dt>
<dd></dd>
<dt><a name="115">method <b class="cmd">Collate_Source</b> <i class="arg">CWD</i></a></dt>
<dd></dd>
<dt><a name="116">method <b class="cmd">project-compile-products</b></a></dt>
<dd></dd>
<dt><a name="117">method <b class="cmd">generate-debug</b> <span class="opt">?<i class="arg">spaces</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="118">method <b class="cmd">generate-cfile-constant</b></a></dt>
<dd></dd>
<dt><a name="119">method <b class="cmd">generate-cfile-public-structure</b></a></dt>
<dd><p>Populate const static data structures</p></dd>
<dt><a name="120">method <b class="cmd">generate-cfile-header</b></a></dt>
<dd></dd>
<dt><a name="121">method <b class="cmd">generate-cfile-global</b></a></dt>
<dd></dd>
<dt><a name="122">method <b class="cmd">generate-cfile-private-typedef</b></a></dt>
<dd></dd>
<dt><a name="123">method <b class="cmd">generate-cfile-private-structure</b></a></dt>
<dd></dd>
<dt><a name="124">method <b class="cmd">generate-cfile-functions</b></a></dt>
<dd><p>Generate code that provides subroutines called by
Tcl API methods</p></dd>
<dt><a name="125">method <b class="cmd">generate-cfile-tclapi</b></a></dt>
<dd><p>Generate code that provides implements Tcl API
calls</p></dd>
<dt><a name="126">method <b class="cmd">generate-hfile-public-define</b></a></dt>
<dd></dd>
<dt><a name="127">method <b class="cmd">generate-hfile-public-macro</b></a></dt>
<dd></dd>
<dt><a name="128">method <b class="cmd">generate-hfile-public-typedef</b></a></dt>
<dd></dd>
<dt><a name="129">method <b class="cmd">generate-hfile-public-structure</b></a></dt>
<dd></dd>
<dt><a name="130">method <b class="cmd">generate-hfile-public-headers</b></a></dt>
<dd></dd>
<dt><a name="131">method <b class="cmd">generate-hfile-public-function</b></a></dt>
<dd></dd>
<dt><a name="132">method <b class="cmd">generate-hfile-public-includes</b></a></dt>
<dd></dd>
<dt><a name="133">method <b class="cmd">generate-hfile-public-verbatim</b></a></dt>
<dd></dd>
<dt><a name="134">method <b class="cmd">generate-loader-external</b></a></dt>
<dd></dd>
<dt><a name="135">method <b class="cmd">generate-loader-module</b></a></dt>
<dd></dd>
<dt><a name="136">method <b class="cmd">generate-stub-function</b></a></dt>
<dd></dd>
<dt><a name="137">method <b class="cmd">IncludeAdd</b> <i class="arg">headervar</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="138">method <b class="cmd">generate-tcl-loader</b></a></dt>
<dd></dd>
<dt><a name="139">method <b class="cmd">generate-tcl-pre</b></a></dt>
<dd><p>This methods generates any Tcl script file
which is required to pre-initialize the C library</p></dd>
<dt><a name="140">method <b class="cmd">generate-tcl-post</b></a></dt>
<dd></dd>
<dt><a name="141">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
<dt><a name="142">method <b class="cmd">Ofile</b> <i class="arg">filename</i></a></dt>
<dd></dd>
<dt><a name="143">method <b class="cmd">project-static-packages</b></a></dt>
<dd><p>Methods called by the master project</p></dd>
<dt><a name="144">method <b class="cmd">toolset-include-directory</b></a></dt>
<dd><p>Methods called by the toolset</p></dd>
<dt><a name="145">method <b class="cmd">target</b> <i class="arg">method</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection9" class="doctools_subsection"><h3><a name="subsection9">Class  practcl::product.cheader</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::product</b></p>
<p>Flesh out several trivial varieties of product</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="146">method <b class="cmd">project-compile-products</b></a></dt>
<dd></dd>
<dt><a name="147">method <b class="cmd">generate-loader-module</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection10" class="doctools_subsection"><h3><a name="subsection10">Class  practcl::product.csource</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::product</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="148">method <b class="cmd">project-compile-products</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection11" class="doctools_subsection"><h3><a name="subsection11">Class  practcl::product.clibrary</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::product</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="149">method <b class="cmd">linker-products</b> <i class="arg">configdict</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection12" class="doctools_subsection"><h3><a name="subsection12">Class  practcl::product.dynamic</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::dynamic</b> <b class="class">practcl::product</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="150">method <b class="cmd">initialize</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection13" class="doctools_subsection"><h3><a name="subsection13">Class  practcl::product.critcl</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::dynamic</b> <b class="class">practcl::product</b></p>
</div>
<div id="subsection14" class="doctools_subsection"><h3><a name="subsection14">Class  practcl::module</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::object</b> <b class="class">practcl::product.dynamic</b></p>
<p>In the end, all C code must be loaded into a module
This will either be a dynamically loaded library implementing
a tcl extension, or a compiled in segment of a custom shell/app</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="151">method <b class="cmd">_MorphPatterns</b></a></dt>
<dd></dd>
<dt><a name="152">method <b class="cmd">add</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="153">method <b class="cmd">install-headers</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="154">method <b class="cmd">make</b> <i class="arg">command</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Target handling</p></dd>
<dt><a name="155">method <b class="cmd">child</b> <i class="arg">which</i></a></dt>
<dd></dd>
<dt><a name="156">method <b class="cmd">generate-c</b></a></dt>
<dd><p>This methods generates the contents of an amalgamated .c file
which implements the loader for a batch of tools</p></dd>
<dt><a name="157">method <b class="cmd">generate-h</b></a></dt>
<dd><p>This methods generates the contents of an amalgamated .h file
which describes the public API of this module</p></dd>
<dt><a name="158">method <b class="cmd">generate-loader</b></a></dt>
<dd></dd>
<dt><a name="159">method <b class="cmd">initialize</b></a></dt>
<dd></dd>
<dt><a name="160">method <b class="cmd">implement</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="161">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection15" class="doctools_subsection"><h3><a name="subsection15">Class  practcl::project</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::module</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="162">method <b class="cmd">_MorphPatterns</b></a></dt>
<dd></dd>
<dt><a name="163">method <b class="cmd">constructor</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="164">method <b class="cmd">add_object</b> <i class="arg">object</i></a></dt>
<dd></dd>
<dt><a name="165">method <b class="cmd">add_project</b> <i class="arg">pkg</i> <i class="arg">info</i> <span class="opt">?<i class="arg">oodefine</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="166">method <b class="cmd">add_tool</b> <i class="arg">pkg</i> <i class="arg">info</i> <span class="opt">?<i class="arg">oodefine</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="167">method <b class="cmd">build-tclcore</b></a></dt>
<dd></dd>
<dt><a name="168">method <b class="cmd">child</b> <i class="arg">which</i></a></dt>
<dd></dd>
<dt><a name="169">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
<dt><a name="170">method <b class="cmd">project</b> <i class="arg">pkg</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Exercise the methods of a sub-object</p></dd>
<dt><a name="171">method <b class="cmd">tclcore</b></a></dt>
<dd></dd>
<dt><a name="172">method <b class="cmd">tkcore</b></a></dt>
<dd></dd>
<dt><a name="173">method <b class="cmd">tool</b> <i class="arg">pkg</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection16" class="doctools_subsection"><h3><a name="subsection16">Class  practcl::library</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::project</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="174">method <b class="cmd">clean</b> <i class="arg">PATH</i></a></dt>
<dd></dd>
<dt><a name="175">method <b class="cmd">project-compile-products</b></a></dt>
<dd></dd>
<dt><a name="176">method <b class="cmd">go</b></a></dt>
<dd></dd>
<dt><a name="177">method <b class="cmd">generate-decls</b> <i class="arg">pkgname</i> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="178">method <b class="cmd">implement</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="179">method <b class="cmd">generate-make</b> <i class="arg">path</i></a></dt>
<dd><p>Backward compadible call</p></dd>
<dt><a name="180">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
<dt><a name="181">method <b class="cmd">package-ifneeded</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Create a &quot;package ifneeded&quot;
Args are a list of aliases for which this package will answer to</p></dd>
<dt><a name="182">method <b class="cmd">shared_library</b> <span class="opt">?<i class="arg">filename</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
<dt><a name="183">method <b class="cmd">static_library</b> <span class="opt">?<i class="arg">filename</i> <b class="const"></b>?</span></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection17" class="doctools_subsection"><h3><a name="subsection17">Class  practcl::tclkit</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::library</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="184">method <b class="cmd">build-tclkit_main</b> <i class="arg">PROJECT</i> <i class="arg">PKG_OBJS</i></a></dt>
<dd></dd>
<dt><a name="185">method <b class="cmd">Collate_Source</b> <i class="arg">CWD</i></a></dt>
<dd></dd>
<dt><a name="186">method <b class="cmd">wrap</b> <i class="arg">PWD</i> <i class="arg">exename</i> <i class="arg">vfspath</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Wrap an executable</p></dd>
</dl>
</div>
<div id="subsection18" class="doctools_subsection"><h3><a name="subsection18">Class  practcl::distribution</a></h3>
<p>Standalone class to manage code distribution
This class is intended to be mixed into another class
(Thus the lack of ancestors)</p>
<p><b class="class">Class Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="187">method <b class="cmd">Sandbox</b> <i class="arg">object</i></a></dt>
<dd></dd>
<dt><a name="188">method <b class="cmd">select</b> <i class="arg">object</i></a></dt>
<dd></dd>
<dt><a name="189">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="190">method <b class="cmd">claim_object</b> <i class="arg">object</i></a></dt>
<dd></dd>
</dl>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="191">method <b class="cmd">scm_info</b></a></dt>
<dd></dd>
<dt><a name="192">method <b class="cmd">DistroMixIn</b></a></dt>
<dd></dd>
<dt><a name="193">method <b class="cmd">Sandbox</b></a></dt>
<dd></dd>
<dt><a name="194">method <b class="cmd">SrcDir</b></a></dt>
<dd></dd>
<dt><a name="195">method <b class="cmd">ScmTag</b></a></dt>
<dd></dd>
<dt><a name="196">method <b class="cmd">ScmClone</b></a></dt>
<dd></dd>
<dt><a name="197">method <b class="cmd">ScmUnpack</b></a></dt>
<dd></dd>
<dt><a name="198">method <b class="cmd">ScmUpdate</b></a></dt>
<dd></dd>
<dt><a name="199">method <b class="cmd">Unpack</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection19" class="doctools_subsection"><h3><a name="subsection19">Class  practcl::distribution.snapshot</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::distribution</b></p>
<p><b class="class">Class Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="200">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="201">method <b class="cmd">claim_object</b> <i class="arg">object</i></a></dt>
<dd></dd>
</dl>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="202">method <b class="cmd">ScmUnpack</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection20" class="doctools_subsection"><h3><a name="subsection20">Class  practcl::distribution.fossil</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::distribution</b></p>
<p><b class="class">Class Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="203">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></dt>
<dd><p>Check for markers in the source root</p></dd>
<dt><a name="204">method <b class="cmd">claim_object</b> <i class="arg">obj</i></a></dt>
<dd><p>Check for markers in the metadata</p></dd>
</dl>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="205">method <b class="cmd">scm_info</b></a></dt>
<dd></dd>
<dt><a name="206">method <b class="cmd">ScmClone</b></a></dt>
<dd><p>Clone the source</p></dd>
<dt><a name="207">method <b class="cmd">ScmTag</b></a></dt>
<dd></dd>
<dt><a name="208">method <b class="cmd">ScmUnpack</b></a></dt>
<dd></dd>
<dt><a name="209">method <b class="cmd">ScmUpdate</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection21" class="doctools_subsection"><h3><a name="subsection21">Class  practcl::distribution.git</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::distribution</b></p>
<p><b class="class">Class Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="210">method <b class="cmd">claim_path</b> <i class="arg">path</i></a></dt>
<dd></dd>
<dt><a name="211">method <b class="cmd">claim_object</b> <i class="arg">obj</i></a></dt>
<dd></dd>
</dl>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="212">method <b class="cmd">ScmTag</b></a></dt>
<dd></dd>
<dt><a name="213">method <b class="cmd">ScmUnpack</b></a></dt>
<dd></dd>
<dt><a name="214">method <b class="cmd">ScmUpdate</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection22" class="doctools_subsection"><h3><a name="subsection22">Class  practcl::subproject</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::module</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="215">method <b class="cmd">_MorphPatterns</b></a></dt>
<dd></dd>
<dt><a name="216">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></dt>
<dd></dd>
<dt><a name="217">method <b class="cmd">child</b> <i class="arg">which</i></a></dt>
<dd></dd>
<dt><a name="218">method <b class="cmd">compile</b></a></dt>
<dd></dd>
<dt><a name="219">method <b class="cmd">go</b></a></dt>
<dd></dd>
<dt><a name="220">method <b class="cmd">install</b> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd><p>Install project into the local build system</p></dd>
<dt><a name="221">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
<dt><a name="222">method <b class="cmd">linker-products</b> <i class="arg">configdict</i></a></dt>
<dd></dd>
<dt><a name="223">method <b class="cmd">linker-external</b> <i class="arg">configdict</i></a></dt>
<dd></dd>
<dt><a name="224">method <b class="cmd">linker-extra</b> <i class="arg">configdict</i></a></dt>
<dd></dd>
<dt><a name="225">method <b class="cmd">env-bootstrap</b></a></dt>
<dd><p>Methods for packages/tools that can be downloaded
possibly built and used internally by this Practcl
process
Load the facility into the interpreter</p></dd>
<dt><a name="226">method <b class="cmd">env-exec</b></a></dt>
<dd><p>Return a file path that exec can call</p></dd>
<dt><a name="227">method <b class="cmd">env-install</b></a></dt>
<dd><p>Install the tool into the local environment</p></dd>
<dt><a name="228">method <b class="cmd">env-load</b></a></dt>
<dd><p>Do whatever is necessary to get the tool
into the local environment</p></dd>
<dt><a name="229">method <b class="cmd">env-present</b></a></dt>
<dd><p>Check if tool is available for load/already loaded</p></dd>
<dt><a name="230">method <b class="cmd">sources</b></a></dt>
<dd></dd>
<dt><a name="231">method <b class="cmd">update</b></a></dt>
<dd></dd>
<dt><a name="232">method <b class="cmd">unpack</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection23" class="doctools_subsection"><h3><a name="subsection23">Class  practcl::subproject.source</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject</b> <b class="class">practcl::library</b></p>
<p>A project which the kit compiles and integrates
the source for itself</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="233">method <b class="cmd">env-bootstrap</b></a></dt>
<dd></dd>
<dt><a name="234">method <b class="cmd">env-present</b></a></dt>
<dd></dd>
<dt><a name="235">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection24" class="doctools_subsection"><h3><a name="subsection24">Class  practcl::subproject.teapot</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject</b></p>
<p>a copy from the teapot</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="236">method <b class="cmd">env-bootstrap</b></a></dt>
<dd></dd>
<dt><a name="237">method <b class="cmd">env-install</b></a></dt>
<dd></dd>
<dt><a name="238">method <b class="cmd">env-present</b></a></dt>
<dd></dd>
<dt><a name="239">method <b class="cmd">install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection25" class="doctools_subsection"><h3><a name="subsection25">Class  practcl::subproject.kettle</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="240">method <b class="cmd">kettle</b> <i class="arg">path</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
<dt><a name="241">method <b class="cmd">install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection26" class="doctools_subsection"><h3><a name="subsection26">Class  practcl::subproject.critcl</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="242">method <b class="cmd">install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection27" class="doctools_subsection"><h3><a name="subsection27">Class  practcl::subproject.sak</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="243">method <b class="cmd">env-bootstrap</b></a></dt>
<dd></dd>
<dt><a name="244">method <b class="cmd">env-install</b></a></dt>
<dd></dd>
<dt><a name="245">method <b class="cmd">env-present</b></a></dt>
<dd></dd>
<dt><a name="246">method <b class="cmd">install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
<dt><a name="247">method <b class="cmd">install-module</b> <i class="arg">DEST</i> <span class="opt">?<i class="arg">args</i>?</span></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection28" class="doctools_subsection"><h3><a name="subsection28">Class  practcl::subproject.binary</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject</b></p>
<p>A binary package</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="248">method <b class="cmd">clean</b></a></dt>
<dd></dd>
<dt><a name="249">method <b class="cmd">env-install</b></a></dt>
<dd></dd>
<dt><a name="250">method <b class="cmd">project-compile-products</b></a></dt>
<dd></dd>
<dt><a name="251">method <b class="cmd">ComputeInstall</b></a></dt>
<dd></dd>
<dt><a name="252">method <b class="cmd">go</b></a></dt>
<dd></dd>
<dt><a name="253">method <b class="cmd">linker-products</b> <i class="arg">configdict</i></a></dt>
<dd></dd>
<dt><a name="254">method <b class="cmd">project-static-packages</b></a></dt>
<dd></dd>
<dt><a name="255">method <b class="cmd">BuildDir</b> <i class="arg">PWD</i></a></dt>
<dd></dd>
<dt><a name="256">method <b class="cmd">compile</b></a></dt>
<dd></dd>
<dt><a name="257">method <b class="cmd">Configure</b></a></dt>
<dd></dd>
<dt><a name="258">method <b class="cmd">install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection29" class="doctools_subsection"><h3><a name="subsection29">Class  practcl::subproject.tea</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject.binary</b></p>
</div>
<div id="subsection30" class="doctools_subsection"><h3><a name="subsection30">Class  practcl::subproject.library</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject.binary</b> <b class="class">practcl::library</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="259">method <b class="cmd">install</b> <i class="arg">DEST</i></a></dt>
<dd></dd>
</dl>
</div>
<div id="subsection31" class="doctools_subsection"><h3><a name="subsection31">Class  practcl::subproject.external</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject.binary</b></p>
<p>An external library</p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="1"><b class="cmd">CPUTS</b> <i class="arg">varname</i> <i class="arg">body</i> <span class="opt">?<i class="arg">body</i>...?</span></a></dt>
<dt><a name="260">method <b class="cmd">install</b> <i class="arg">DEST</i></a></dt>
<dd><p>Appends blocks of text to a buffer. This command tries to reduce the number
of line breaks between bodies.</p></dd>
<dd></dd>
<dt><a name="2"><b class="cmd">practcl::_isdirectory</b> <i class="arg">path</i></a></dt>
<dd><p>Returns true if <i class="arg">path</i> is a directory, using the test</p></dd>
</dl>
</div>
<div id="subsection32" class="doctools_subsection"><h3><a name="subsection32">Class  practcl::subproject.core</a></h3>
<p><em>ancestors</em>: <b class="class">practcl::subproject.binary</b></p>
<p><b class="class">Methods</b></p>
<dl class="doctools_definitions">
<dt><a name="3"><b class="cmd">practcl::object</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A generic Practcl object</p></dd>
<dt><a name="4"><b class="cmd">practcl::library</b> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A Practcl object representing a library container</p></dd>
<dt><a name="5"><b class="cmd">practcl::exe</b> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A Practcl object representing a wrapped executable</p></dd>
<dt><a name="6"><b class="cmd">practcl::product</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A Practcl object representing a compiled product</p></dd>
<dt><a name="7"><b class="cmd">practcl::cheader</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A Practcl object representing an externally generated c header</p></dd>
<dt><a name="261">method <b class="cmd">env-bootstrap</b></a></dt>
<dd></dd>
<dt><a name="262">method <b class="cmd">env-present</b></a></dt>
<dd></dd>
<dt><a name="263">method <b class="cmd">env-install</b></a></dt>
<dd></dd>
<dt><a name="264">method <b class="cmd">go</b></a></dt>
<dd></dd>
<dt><a name="265">method <b class="cmd">linktype</b></a></dt>
<dd></dd>
<dt><a name="8"><b class="cmd">practcl::csource</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A Practcl object representing an externally generated c source file</p></dd>
<dt><a name="9"><b class="cmd">practcl::module</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A Practcl object representing a dynamically generated C/H/Tcl suite</p></dd>
<dt><a name="10"><b class="cmd">practcl::submodule</b> <i class="arg">parent</i> <span class="opt">?<i class="arg">keyvaluelist</i>?</span></a></dt>
<dd><p>A Practcl object representing a dynamically generated C/H/Tcl suite, subordinate to a module</p></dd>
</dl>
</div>
</div>
<div id="section3" class="doctools_section"><h2><a name="section3">Bugs, Ideas, Feedback</a></h2>
<div id="section4" class="doctools_section"><h2><a name="section4">Bugs, Ideas, Feedback</a></h2>
<p>This document, and the package it describes, will undoubtedly contain
bugs and other problems.
Please report such in the category <em>practcl</em> of the
<a href="http://core.tcl.tk/tcllib/reportlist">Tcllib Trackers</a>.
Please also report any ideas for enhancements you may have for either
package and/or documentation.</p>
<p>When proposing code changes, please provide <em>unified diffs</em>,
i.e the output of <b class="const">diff -u</b>.</p>
<p>Note further that <em>attachments</em> are strongly preferred over
inlined patches. Attachments can be made by going to the <b class="const">Edit</b>
form of the ticket immediately after its creation, and then using the
left-most button in the secondary navigation bar.</p>
</div>
<div id="keywords" class="doctools_section"><h2><a name="keywords">Keywords</a></h2>
<p><a href="../../../../index.html#practcl">practcl</a></p>
<p>practcl</p>
</div>
<div id="category" class="doctools_section"><h2><a name="category">Category</a></h2>
<p>TclOO</p>
</div>
<div id="copyright" class="doctools_section"><h2><a name="copyright">Copyright</a></h2>
<p>Copyright &copy; 2016-2018 Sean Woods &lt;[email protected]&gt;</p>
</div>
</div></body></html>

Changes to idoc/www/tcllib/files/modules/pt/pt_peg_op.html.

104
105
106
107
108
109
110
111

112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

132
133
134
135
136
137
138
104
105
106
107
108
109
110

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

131
132
133
134
135
136
137
138







-
+



















-
+







| <a href="../../../toc.html">Table Of Contents</a>
| <a href="../../../../index.html">Keyword Index</a>
| <a href="../../../../toc0.html">Categories</a>
| <a href="../../../../toc1.html">Modules</a>
| <a href="../../../../toc2.html">Applications</a>
 ] <hr>
<div class="doctools">
<h1 class="doctools_title">pt_peg_op(i) 1.0.1 tcllib &quot;Parser Tools&quot;</h1>
<h1 class="doctools_title">pt_peg_op(i) 1.0.2 tcllib &quot;Parser Tools&quot;</h1>
<div id="name" class="doctools_section"><h2><a name="name">Name</a></h2>
<p>pt_peg_op - Parser Tools PE Grammar Utility Operations</p>
</div>
<div id="toc" class="doctools_section"><h2><a name="toc">Table Of Contents</a></h2>
<ul class="doctools_toc">
<li class="doctools_section"><a href="#toc">Table Of Contents</a></li>
<li class="doctools_section"><a href="#synopsis">Synopsis</a></li>
<li class="doctools_section"><a href="#section1">Description</a></li>
<li class="doctools_section"><a href="#section2">API</a></li>
<li class="doctools_section"><a href="#section3">Bugs, Ideas, Feedback</a></li>
<li class="doctools_section"><a href="#keywords">Keywords</a></li>
<li class="doctools_section"><a href="#category">Category</a></li>
<li class="doctools_section"><a href="#copyright">Copyright</a></li>
</ul>
</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">Tcl 8.5</b></li>
<li>package require <b class="pkgname">pt::peg::op 1.0.1</b></li>
<li>package require <b class="pkgname">pt::peg::op <span class="opt">?1.0.2?</span></b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><b class="cmd">::peg::peg::op</b> <b class="method">called</b> <i class="arg">container</i></a></li>
<li><a href="#2"><b class="cmd">::peg::peg::op</b> <b class="method">dechain</b> <i class="arg">container</i></a></li>
<li><a href="#3"><b class="cmd">::peg::peg::op</b> <b class="method">drop unreachable</b> <i class="arg">container</i></a></li>
<li><a href="#4"><b class="cmd">::peg::peg::op</b> <b class="method">drop unrealizable</b> <i class="arg">container</i></a></li>
<li><a href="#5"><b class="cmd">::peg::peg::op</b> <b class="method">flatten</b> <i class="arg">container</i></a></li>

Changes to idoc/www/tcllib/files/modules/smtpd/smtpd.html.

296
297
298
299
300
301
302
303
304

305
306
307
308
309
310
311
296
297
298
299
300
301
302


303
304
305
306
307
308
309
310







-
-
+







<p>The content of any error message will not be passed back to the client.</p></dd>
<dt><b class="cmd">validate_recipient</b> callback</dt>
<dd><p>The validate_recipient callback is similar to the validate_sender
callback and permits you to verify a local mailbox and accept mail for
a local user address during RCPT command handling. To reject mail,
throw an error as above. The error message is ignored.</p></dd>
<dt><b class="cmd">deliverMIME</b> callback</dt>
<dd><p>]
The deliverMIME callback is called once a mail message has been
<dd><p>The deliverMIME callback is called once a mail message has been
successfully passed to the server. A mime token is constructed from
the sender, recipients and data and the users procedure it called with
this single argument. When the call returns, the mime token is cleaned
up so if the user wishes to preserve the data she must make a copy.</p>
<pre class="doctools_example">
 proc deliverMIME {token} {
     set sender [lindex [mime::getheader $token From] 0]

Changes to idoc/www/tcllib/files/modules/stooop/switched.html.

312
313
314
315
316
317
318
319

320
321
322


323
324
325
326
327
328
329
312
313
314
315
316
317
318

319
320


321
322
323
324
325
326
327
328
329







-
+

-
-
+
+







listed in the <b class="method">options</b> procedure) but obviously does not check
the validity of the value passed to the <b class="method">set-<b class="option">option</b></b>
procedure, which should throw an error (for example by using the Tcl
error command) if the value is invalid.</p>
<p>The switched layer also keeps track of the options current
values, so that a <b class="method">set-<b class="option">option</b></b> procedure is called
only when the corresponding option value passed as parameter is
different from the current value (see  data members
different from the current value (see <b class="variable">-option</b> data members
description).</p></dd>
<dt></dt>
<dd><p>The  data member is an options current value.
<dt><b class="variable">-option</b></dt>
<dd><p>The <b class="variable">-option</b> data member is an options current value.
There is one for each option listed in the options procedure. It is a
read-only value which the switched layer checks against when an option
is changed.
It is rarely used at the layer derived from switched, except in the
few cases, such as in the following example:</p>
<pre class="doctools_example">
...
339
340
341
342
343
344
345
346
347
348



349
350
351
352
353
354
355
339
340
341
342
343
344
345



346
347
348
349
350
351
352
353
354
355







-
-
-
+
+
+







    puts &quot;manufacturer: $switched::($this,-manufacturer)&quot;
    ...
}
</pre>
<p>In this case, the manufacturer's name is stored at the switched
layer level (this is why the set-manufacturer procedure has nothing to
do) and later retrieved in the printData procedure.</p></dd>
<dt></dt>
<dd><p>The  data member (not to be confused with
the <b class="method">complete</b> procedure) is a boolean.
<dt><b class="variable">complete</b></dt>
<dd><p>The <b class="variable">complete</b> data member (not to be confused with the
<b class="method">complete</b> procedure) is a boolean.
Its initial value is <b class="const">false</b> and it is set to <b class="const">true</b> at
the very end of the switched <b class="method">complete</b> procedure.
It becomes useful when some options should be set at construction time
only and not dynamically, as the following example shows:</p>
<pre class="doctools_example">
proc car::set-width {this value} {
    if {$switched::($this,complete)} {

Changes to idoc/www/tcllib/files/modules/tepam/tepam_doc_gen.html.

324
325
326
327
328
329
330
331

332
333
334
335
336
337
338
324
325
326
327
328
329
330

331
332
333
334
335
336
337
338







-
+







<dd><p>Generates the part of the command line or the synopsis that is specific to an argument. The generated string has to indicate if an argument is optional, named and if it is a flag.</p>
<p>The following parameters are provided to this procedure:</p>
<dl class="doctools_definitions">
   
<dt><i class="arg">Name</i></dt>
<dd><p>Name of the argument</p></dd>
<dt><i class="arg">IsOptional</i></dt>
<dd><p>If true (=<b class="const">1</b>) the argument is optional which should be indicated by the generated string (for example by putting the argument into brackets {} or into question marks '?'):</p>
<dd><p>If true (=<b class="const">1</b>) the argument is optional which should be indicated by the generated string (for example by putting the argument into brackets {[]} or into question marks '?'):</p>
<pre class="doctools_example">gen(TXT,ArgumentString) mtype 1 0 string -&gt; <em>&quot;[mtype]&quot;</em></pre>
</dd>
<dt><i class="arg">IsNamed</i></dt>
<dd><p>If true (=<b class="const">1</b>) an argument is a named argument (option). The generated string should in this case contain the argument/option name, followed by the argument itself:</p>
<pre class="doctools_example">gen(TXT,ArgumentString) mtype 0 1 string -&gt; <em>&quot;-mtype &lt;mtype&gt;&quot;</em></pre>
<p>Named arguments can also be optional:</p>
<pre class="doctools_example">gen(TXT,ArgumentString) mtype 1 1 string -&gt; <em>&quot;[-mtype &lt;mtype&gt;]&quot;</em></pre>

Changes to idoc/www/tcllib/files/modules/tepam/tepam_procedure.html.

715
716
717
718
719
720
721
722

723
724
725
726
727
728
729
715
716
717
718
719
720
721

722
723
724
725
726
727
728
729







-
+







<p>Named arguments can be defined multiple times. If the named argument has the <em>-multiply</em> attribute, all argument values will be collected in a list. Otherwise, only the last provided attribute value will be retained:</p>
<pre class="doctools_example">my_proc <b class="cmd">-n1 N1 -n2 N2 -n1 M1 U1 U2</b>
<em>-&gt; n1:'M1', n2:'N2', u1:'U1', u2:'U2'</em></pre>
<p>The name of the first unnamed argument has therefore not to start with the '-' character. The unnamed argument is otherwise considered as name of another named argument. This is especially important if the first unnamed argument is given by a variable that can contain any character strings:</p>
<pre class="doctools_example">my_proc <b class="cmd">-n1 N1 -n2 N2 &quot;-&gt;&quot; &quot;&lt;-&quot;</b>
<em>-&gt; my_proc: Argument '-&gt;' not known</em>
set U1 &quot;-&gt;&quot;
my_proc -n1 N1 -n2 N2 $U1 U2}]
my_proc <b class="cmd">-n1 N1 -n2 N2 $U1 U2</b>
my_proc: Argument '-&gt;' not known</pre>
<p>The '--' flag allows separating unambiguously the unnamed arguments from the named arguments. All data after the '--' flag will be considered as unnamed argument:</p>
<pre class="doctools_example">my_proc <b class="cmd">-n1 N1 -n2 N2 -- &quot;-&gt;&quot; &quot;&lt;-&quot;</b>
<em>-&gt; n1:'N1', n2:'N2', u1:'-&gt;', u2:'&lt;-'</em>
set U1 &quot;-&gt;&quot;
my_proc <b class="cmd">-n1 N1 -n2 N2 -- $U1 U2</b>
<em>-&gt; n1:'N1', n2:'N2', u1:'-&gt;', u2:'&lt;-'</em></pre>

Changes to idoc/www/tcllib/files/modules/textutil/adjust.html.

206
207
208
209
210
211
212
213

214
215

216
217
218
219
220
221
222
206
207
208
209
210
211
212

213


214
215
216
217
218
219
220
221







-
+
-
-
+







there are no space chars at the end of this line, and there may be
some space chars at the beginning, despite of the <b class="option">-full</b> option.</p></dd>
</dl></dd>
<dt><b class="option">-length</b> <i class="arg">integer</i></dt>
<dd><p>Set the length of the <em>logical</em> line in the string to
<i class="arg">integer</i>.  <i class="arg">integer</i> must be a positive integer
value. Defaults to <b class="const">72</b>.</p></dd>
<dt><b class="option">-strictlength</b></dt>
<dt><b class="option">-strictlength</b> <i class="arg">boolean</i></dt>
<dd><p><i class="arg">boolean</i>]
If set to <b class="const">false</b> (default), a line can exceed the specified
<dd><p>If set to <b class="const">false</b> (default), a line can exceed the specified
<b class="option">-length</b> if a single word is longer than <b class="option">-length</b>. If
set to <b class="const">true</b>, words that are longer than <b class="option">-length</b> are
split so that no line exceeds the specified <b class="option">-length</b>.</p></dd>
</dl></dd>
<dt><a name="2"><b class="cmd">::textutil::adjust::readPatterns</b> <i class="arg">filename</i></a></dt>
<dd><p>Loads the internal storage for hyphenation patterns with the contents
of the file <i class="arg">filename</i>. This has to be done prior to calling

Changes to idoc/www/tcllib/files/modules/tool/tool_dict_ensemble.html.

126
127
128
129
130
131
132
133

134
135
136
137
138
139
140
141
142

143
144

145
146
147
148
149
150
151
126
127
128
129
130
131
132

133
134
135
136
137
138
139
140
141

142


143
144
145
146
147
148
149
150







-
+








-
+
-
-
+







</div>
<div id="synopsis" class="doctools_section"><h2><a name="synopsis">Synopsis</a></h2>
<div class="doctools_synopsis">
<ul class="doctools_requirements">
<li>package require <b class="pkgname">tool <span class="opt">?0.4.2?</span></b></li>
</ul>
<ul class="doctools_syntax">
<li><a href="#1"><em>object</em> <i class="arg">ensemble</i> <b class="cmd">add</b> <i class="arg">field</i></a></li>
<li><a href="#1"><em>object</em> <i class="arg">ensemble</i> <b class="cmd">add</b> <i class="arg">field</i> <i class="arg">value</i> <i class="arg">value ...</i></a></li>
</ul>
</div>
</div>
<div id="section1" class="doctools_section"><h2><a name="section1">Description</a></h2>
<p>The <b class="cmd">dict_ensemble</b> command is a keyword added by <b class="package"><a href="tool.html">tool</a></b>. It defines
a public variable (stored as a dict), and an access function to manipulated and
access the values stored in that dict.</p>
<dl class="doctools_definitions">
<dt><a name="1"><em>object</em> <i class="arg">ensemble</i> <b class="cmd">add</b> <i class="arg">field</i></a></dt>
<dt><a name="1"><em>object</em> <i class="arg">ensemble</i> <b class="cmd">add</b> <i class="arg">field</i> <i class="arg">value</i> <i class="arg">value ...</i></a></dt>
<dd><p>] <i class="arg">value</i> <i class="arg">value ...</i>]
Adds elements to a list maintained with the <i class="arg">field</i> leaf of the dict maintained
<dd><p>Adds elements to a list maintained with the <i class="arg">field</i> leaf of the dict maintained
my this ensemble.
Declares a variable <i class="arg">name</i> which will be initialized as an array, populated with <i class="arg">contents</i> for objects of this class, as well as any
objects for classes which are descendents of this class.</p></dd>
</dl>
</div>
<div id="section2" class="doctools_section"><h2><a name="section2">AUTHORS</a></h2>
<p>Sean Woods</p>

Changes to idoc/www/tcllib/files/modules/websocket/websocket.html.

290
291
292
293
294
295
296
297

298
299
300
301
302
303
304
290
291
292
293
294
295
296

297
298
299
300
301
302
303
304







-
+







<dt><a name="3"><b class="cmd">::websocket::server</b> <i class="arg">sock</i></a></dt>
<dd><p>This command registers the (accept) socket <i class="arg">sock</i> as the
identifier fo an HTTP server that is capable of doing WebSockets.
Paths onto which this server will listen for incoming connections
should be declared using <b class="cmd">::websocket::live</b>.</p></dd>
<dt><a name="4"><b class="cmd">::websocket::live</b> <i class="arg">sock</i> <i class="arg">path</i> <i class="arg">cb</i> <span class="opt">?<i class="arg">proto</i>?</span></a></dt>
<dd><p>This procedure registers callbacks that will be performed on a
WebSocket compliant server registered with <b class="cmd">::websocket::server</b>]
WebSocket compliant server registered with <b class="cmd">::websocket::server</b>
whenever a client connects to a matching path and protocol. 
<i class="arg">sock</i> is the listening socket of the websocket compliant server
declared using <b class="cmd">::websocket::server</b>.  <i class="arg">path</i> is a glob-style
path to match in client request, whenever this will occur.  <i class="arg">cb</i>
is the command to callback (see Callbacks).  <i class="arg">proto</i> is a
glob-style protocol name matcher.</p></dd>
<dt><a name="5"><b class="cmd">::websocket::test</b> <i class="arg">srvSock</i> <i class="arg">cliSock</i> <i class="arg">path</i> <span class="opt">?<i class="arg">hdrs</i>?</span> <span class="opt">?<i class="arg">qry</i>?</span></a></dt>

Changes to idoc/www/tcllib/toc.html.

765
766
767
768
769
770
771




772
773
774
775

776
777
778
779

780
781
782
783

784
785
786
787

788
789
790
791

792
793
794
795

796
797
798
799

800
801
802
803

804
805
806
807

808
809
810
811

812
813
814
815

816
817
818
819

820
821
822
823

824
825
826
827

828
829
830
831

832
833
834
835

836
837
838
839

840
841
842
843

844
845
846
847

848
849
850
851

852
853
854
855

856
857
858
859

860
861
862
863

864
865
866
867

868
869
870
871

872
873
874
875

876
877
878
879

880
881
882
883

884
885
886
887

888
889
890
891

892
893
894
895

896
897
898
899

900
901
902
903

904
905
906
907

908
909
910
911

912
913
914
915

916
917
918
919

920
921
922
923

924
925
926
927

928
929
930
931

932
933
934
935

936
937
938
939

940
941
942
943

944
945
946
947

948
949
950
951

952
953
954
955

956
957
958
959

960
961
962
963

964
965
966
967

968
969
970
971

972
973
974
975

976
977
978
979

980
981
982
983

984
985
986
987

988
989
990
991

992
993
994
995

996
997
998
999

1000
1001
1002
1003

1004
1005
1006
1007

1008
1009
1010
1011

1012
1013
1014
1015

1016
1017
1018
1019

1020
1021
1022
1023

1024
1025
1026
1027

1028
1029
1030
1031

1032
1033
1034
1035

1036
1037
1038
1039

1040
1041
1042
1043

1044
1045
1046
1047

1048
1049
1050
1051

1052
1053
1054
1055

1056
1057
1058
1059

1060
1061
1062
1063

1064
1065
1066
1067

1068
1069
1070
1071

1072
1073
1074
1075

1076
1077
1078
1079

1080
1081
1082
1083

1084
1085
1086
1087

1088
1089
1090
1091

1092
1093
1094
1095

1096
1097
1098
1099

1100
1101
1102
1103

1104
1105
1106
1107

1108
1109
1110
1111

1112
1113
1114
1115

1116
1117
1118
1119

1120
1121
1122
1123

1124
1125
1126
1127

1128
1129
1130
1131

1132
1133
1134
1135

1136
1137
1138
1139

1140
1141
1142
1143

1144
1145
1146
1147

1148
1149
1150
1151

1152
1153
1154
1155

1156
1157
1158
1159

1160
1161
1162
1163

1164
1165
1166
1167

1168
1169
1170
1171

1172
1173
1174
1175

1176
1177
1178
1179

1180
1181
1182
1183

1184
1185
1186
1187

1188
1189
1190
1191

1192
1193
1194
1195

1196
1197
1198
1199

1200
1201
1202
1203

1204
1205
1206
1207

1208
1209
1210
1211

1212
1213
1214
1215

1216
1217
1218
1219

1220
1221
1222
1223

1224
1225
1226
1227

1228
1229
1230
1231

1232
1233
1234
1235

1236
1237
1238
1239

1240
1241
1242
1243

1244
1245
1246
1247

1248
1249
1250
1251

1252
1253
1254
1255

1256
1257
1258
1259

1260
1261
1262
1263

1264
1265
1266
1267

1268
1269
1270
1271

1272
1273
1274
1275

1276
1277
1278
1279

1280
1281
1282
1283

1284
1285
1286
1287

1288
1289
1290
1291

1292
1293
1294
1295

1296
1297
1298
1299

1300
1301
1302
1303

1304
1305
1306
1307

1308
1309
1310
1311

1312
1313
1314
1315

1316
1317
1318
1319

1320
1321
1322
1323

1324
1325
1326
1327

1328
1329
1330
1331

1332
1333
1334
1335

1336
1337
1338
1339

1340
1341
1342
1343

1344
1345
1346
1347

1348
1349
1350
1351

1352
1353
1354
1355

1356
1357
1358
1359

1360
1361
1362
1363

1364
1365
1366
1367

1368
1369
1370
1371

1372
1373
1374
1375

1376
1377
1378
1379

1380
1381
1382
1383

1384
1385
1386
1387

1388
1389
1390
1391

1392
1393
1394
1395

1396
1397
1398
1399

1400
1401
1402
1403

1404
1405
1406
1407

1408
1409
1410
1411

1412
1413
1414
1415

1416
1417
1418
1419

1420
1421
1422
1423

1424
1425
1426
1427

1428
1429
1430
1431

1432
1433
1434
1435

1436
1437
1438
1439

1440
1441
1442
1443

1444
1445
1446
1447

1448
1449
1450
1451

1452
1453
1454
1455

1456
1457
1458
1459

1460
1461
1462
1463

1464
1465
1466
1467

1468
1469
1470
1471

1472
1473
1474
1475

1476
1477
1478
1479

1480
1481
1482
1483

1484
1485
1486
1487

1488
1489
1490
1491

1492
1493
1494
1495

1496
1497
1498
1499

1500
1501
1502
1503

1504
1505
1506
1507

1508
1509
1510
1511

1512
1513
1514
1515

1516
1517
1518
1519

1520
1521
1522
1523

1524
1525
1526
1527

1528
1529
1530
1531

1532
1533
1534
1535

1536
1537
1538
1539

1540
1541
1542
1543

1544
1545
1546
1547

1548
1549
1550
1551

1552
1553
1554
1555

1556
1557
1558
1559

1560
1561
1562
1563

1564
1565
1566
1567

1568
1569
1570
1571

1572
1573
1574
1575

1576
1577
1578
1579

1580
1581
1582
1583

1584
1585
1586
1587

1588
1589
1590
1591

1592
1593
1594
1595

1596
1597
1598
1599

1600
1601
1602
1603

1604
1605
1606
1607

1608
1609
1610
1611

1612
1613
1614
1615

1616
1617
1618
1619

1620
1621
1622
1623

1624
1625
1626
1627

1628
1629
1630
1631

1632
1633
1634
1635

1636
1637
1638
1639

1640
1641
1642
1643

1644
1645
1646
1647

1648
1649
1650
1651

1652
1653
1654
1655

1656
1657
1658
1659

1660
1661
1662
1663

1664
1665
1666
1667

1668
1669
1670
1671

1672
1673
1674
1675

1676
1677
1678
1679

1680
1681
1682
1683

1684
1685
1686
1687

1688
1689
1690
1691

1692
1693
1694
1695

1696
1697
1698
1699

1700
1701
1702
1703

1704
1705
1706
1707

1708
1709
1710
1711

1712
1713
1714
1715

1716
1717
1718
1719

1720
1721
1722
1723

1724
1725
1726
1727
1728
765
766
767
768
769
770
771
772
773
774
775
776
777
778

779
780
781
782

783
784
785
786

787
788
789
790

791
792
793
794

795
796
797
798

799
800
801
802

803
804
805
806

807
808
809
810

811
812
813
814

815
816
817
818

819
820
821
822

823
824
825
826

827
828
829
830

831
832
833
834

835
836
837
838

839
840
841
842

843
844
845
846

847
848
849
850

851
852
853
854

855
856
857
858

859
860
861
862

863
864
865
866

867
868
869
870

871
872
873
874

875
876
877
878

879
880
881
882

883
884
885
886

887
888
889
890

891
892
893
894

895
896
897
898

899
900
901
902

903
904
905
906

907
908
909
910

911
912
913
914

915
916
917
918

919
920
921
922

923
924
925
926

927
928
929
930

931
932
933
934

935
936
937
938

939
940
941
942

943
944
945
946

947
948
949
950

951
952
953
954

955
956
957
958

959
960
961
962

963
964
965
966

967
968
969
970

971
972
973
974

975
976
977
978

979
980
981
982

983
984
985
986

987
988
989
990

991
992
993
994

995
996
997
998

999
1000
1001
1002

1003
1004
1005
1006

1007
1008
1009
1010

1011
1012
1013
1014

1015
1016
1017
1018

1019
1020
1021
1022

1023
1024
1025
1026

1027
1028
1029
1030

1031
1032
1033
1034

1035
1036
1037
1038

1039
1040
1041
1042

1043
1044
1045
1046

1047
1048
1049
1050

1051
1052
1053
1054

1055
1056
1057
1058

1059
1060
1061
1062

1063
1064
1065
1066

1067
1068
1069
1070

1071
1072
1073
1074

1075
1076
1077
1078

1079
1080
1081
1082

1083
1084
1085
1086

1087
1088
1089
1090

1091
1092
1093
1094

1095
1096
1097
1098

1099
1100
1101
1102

1103
1104
1105
1106

1107
1108
1109
1110

1111
1112
1113
1114

1115
1116
1117
1118

1119
1120
1121
1122

1123
1124
1125
1126

1127
1128
1129
1130

1131
1132
1133
1134

1135
1136
1137
1138

1139
1140
1141
1142

1143
1144
1145
1146

1147
1148
1149
1150

1151
1152
1153
1154

1155
1156
1157
1158

1159
1160
1161
1162

1163
1164
1165
1166

1167
1168
1169
1170

1171
1172
1173
1174

1175
1176
1177
1178

1179
1180
1181
1182

1183
1184
1185
1186

1187
1188
1189
1190

1191
1192
1193
1194

1195
1196
1197
1198

1199
1200
1201
1202

1203
1204
1205
1206

1207
1208
1209
1210

1211
1212
1213
1214

1215
1216
1217
1218

1219
1220
1221
1222

1223
1224
1225
1226

1227
1228
1229
1230

1231
1232
1233
1234

1235
1236
1237
1238

1239
1240
1241
1242

1243
1244
1245
1246

1247
1248
1249
1250

1251
1252
1253
1254

1255
1256
1257
1258

1259
1260
1261
1262

1263
1264
1265
1266

1267
1268
1269
1270

1271
1272
1273
1274

1275
1276
1277
1278

1279
1280
1281
1282

1283
1284
1285
1286

1287
1288
1289
1290

1291
1292
1293
1294

1295
1296
1297
1298

1299
1300
1301
1302

1303
1304
1305
1306

1307
1308
1309
1310

1311
1312
1313
1314

1315
1316
1317
1318

1319
1320
1321
1322

1323
1324
1325
1326

1327
1328
1329
1330

1331
1332
1333
1334

1335
1336
1337
1338

1339
1340
1341
1342

1343
1344
1345
1346

1347
1348
1349
1350

1351
1352
1353
1354

1355
1356
1357
1358

1359
1360
1361
1362

1363
1364
1365
1366

1367
1368
1369
1370

1371
1372
1373
1374

1375
1376
1377
1378

1379
1380
1381
1382

1383
1384
1385
1386

1387
1388
1389
1390

1391
1392
1393
1394

1395
1396
1397
1398

1399
1400
1401
1402

1403
1404
1405
1406

1407
1408
1409
1410

1411
1412
1413
1414

1415
1416
1417
1418

1419
1420
1421
1422

1423
1424
1425
1426

1427
1428
1429
1430

1431
1432
1433
1434

1435
1436
1437
1438

1439
1440
1441
1442

1443
1444
1445
1446

1447
1448
1449
1450

1451
1452
1453
1454

1455
1456
1457
1458

1459
1460
1461
1462

1463
1464
1465
1466

1467
1468
1469
1470

1471
1472
1473
1474

1475
1476
1477
1478

1479
1480
1481
1482

1483
1484
1485
1486

1487
1488
1489
1490

1491
1492
1493
1494

1495
1496
1497
1498

1499
1500
1501
1502

1503
1504
1505
1506

1507
1508
1509
1510

1511
1512
1513
1514

1515
1516
1517
1518

1519
1520
1521
1522

1523
1524
1525
1526

1527
1528
1529
1530

1531
1532
1533
1534

1535
1536
1537
1538

1539
1540
1541
1542

1543
1544
1545
1546

1547
1548
1549
1550

1551
1552
1553
1554

1555
1556
1557
1558

1559
1560
1561
1562

1563
1564
1565
1566

1567
1568
1569
1570

1571
1572
1573
1574

1575
1576
1577
1578

1579
1580
1581
1582

1583
1584
1585
1586

1587
1588
1589
1590

1591
1592
1593
1594

1595
1596
1597
1598

1599
1600
1601
1602

1603
1604
1605
1606

1607
1608
1609
1610

1611
1612
1613
1614

1615
1616
1617
1618

1619
1620
1621
1622

1623
1624
1625
1626

1627
1628
1629
1630

1631
1632
1633
1634

1635
1636
1637
1638

1639
1640
1641
1642

1643
1644
1645
1646

1647
1648
1649
1650

1651
1652
1653
1654

1655
1656
1657
1658

1659
1660
1661
1662

1663
1664
1665
1666

1667
1668
1669
1670

1671
1672
1673
1674

1675
1676
1677
1678

1679
1680
1681
1682

1683
1684
1685
1686

1687
1688
1689
1690

1691
1692
1693
1694

1695
1696
1697
1698

1699
1700
1701
1702

1703
1704
1705
1706

1707
1708
1709
1710

1711
1712
1713
1714

1715
1716
1717
1718

1719
1720
1721
1722

1723
1724
1725
1726

1727
1728
1729
1730
1731
1732







+
+
+
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+



-
+





<td class="#doctools_tocright">Special mathematical functions</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='math_statistics'><a href="files/modules/math/statistics.html">math::statistics</a></td>
<td class="#doctools_tocright">Basic statistical functions and procedures</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='math_trig'><a href="files/modules/math/trig.html">math::trig</a></td>
<td class="#doctools_tocright">Trigonometric anf hyperbolic functions</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='md4'><a href="files/modules/md4/md4.html">md4</a></td>
<td class="#doctools_tocright">MD4 Message-Digest Algorithm</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='md5'><a href="files/modules/md5/md5.html">md5</a></td>
<td class="#doctools_tocright">MD5 Message-Digest Algorithm</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='md5crypt'><a href="files/modules/md5crypt/md5crypt.html">md5crypt</a></td>
<td class="#doctools_tocright">MD5-based password encryption</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='mime'><a href="files/modules/mime/mime.html">mime</a></td>
<td class="#doctools_tocright">Manipulation of MIME body parts</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='mpexpand'><a href="files/modules/doctools/mpexpand.html">mpexpand</a></td>
<td class="#doctools_tocright">Markup processor</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='multiplexer'><a href="files/modules/multiplexer/multiplexer.html">multiplexer</a></td>
<td class="#doctools_tocright">One-to-many communication with sockets.</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='nameserv'><a href="files/modules/nns/nns_client.html">nameserv</a></td>
<td class="#doctools_tocright">Name service facility, Client</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='nameserv_auto'><a href="files/modules/nns/nns_auto.html">nameserv::auto</a></td>
<td class="#doctools_tocright">Name service facility, Client Extension</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='nameserv_common'><a href="files/modules/nns/nns_common.html">nameserv::common</a></td>
<td class="#doctools_tocright">Name service facility, shared definitions</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='nameserv_protocol'><a href="files/modules/nns/nns_protocol.html">nameserv::protocol</a></td>
<td class="#doctools_tocright">Name service facility, client/server protocol</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='nameserv_server'><a href="files/modules/nns/nns_server.html">nameserv::server</a></td>
<td class="#doctools_tocright">Name service facility, Server</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='namespacex'><a href="files/modules/namespacex/namespacex.html">namespacex</a></td>
<td class="#doctools_tocright">Namespace utility commands</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='ncgi'><a href="files/modules/ncgi/ncgi.html">ncgi</a></td>
<td class="#doctools_tocright">Procedures to manipulate CGI values.</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='nettool'><a href="files/modules/nettool/nettool.html">nettool</a></td>
<td class="#doctools_tocright">Tools for networked applications</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='nmea'><a href="files/modules/nmea/nmea.html">nmea</a></td>
<td class="#doctools_tocright">Process NMEA data</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='nns'><a href="files/apps/nns.html">nns</a></td>
<td class="#doctools_tocright">Name service facility, Commandline Client Application</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='nns_intro'><a href="files/modules/nns/nns_intro.html">nns_intro</a></td>
<td class="#doctools_tocright">Name service facility, introduction</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='nnsd'><a href="files/apps/nnsd.html">nnsd</a></td>
<td class="#doctools_tocright">Name service facility, Commandline Server Application</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='nnslog'><a href="files/apps/nnslog.html">nnslog</a></td>
<td class="#doctools_tocright">Name service facility, Commandline Logging Client Application</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='nntp'><a href="files/modules/nntp/nntp.html">nntp</a></td>
<td class="#doctools_tocright">Tcl client for the NNTP protocol</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='ntp_time'><a href="files/modules/ntp/ntp_time.html">ntp_time</a></td>
<td class="#doctools_tocright">Tcl Time Service Client</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='oauth'><a href="files/modules/oauth/oauth.html">oauth</a></td>
<td class="#doctools_tocright">oauth API base signature</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='oo_util'><a href="files/modules/tool/meta.html">oo::util</a></td>
<td class="#doctools_tocright">Utility commands for TclOO</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='oo_util'><a href="files/modules/ooutil/ooutil.html">oo::util</a></td>
<td class="#doctools_tocright">Utility commands for TclOO</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='oometa'><a href="files/modules/oometa/oometa.html">oometa</a></td>
<td class="#doctools_tocright">oo::meta A data registry for classess</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='otp'><a href="files/modules/otp/otp.html">otp</a></td>
<td class="#doctools_tocright">One-Time Passwords</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='page'><a href="files/apps/page.html">page</a></td>
<td class="#doctools_tocright">Parser Generator</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='page_intro'><a href="files/modules/page/page_intro.html">page_intro</a></td>
<td class="#doctools_tocright">page introduction</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='page_pluginmgr'><a href="files/modules/page/page_pluginmgr.html">page_pluginmgr</a></td>
<td class="#doctools_tocright">page plugin manager</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='page_util_flow'><a href="files/modules/page/page_util_flow.html">page_util_flow</a></td>
<td class="#doctools_tocright">page dataflow/treewalker utility</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='page_util_norm_lemon'><a href="files/modules/page/page_util_norm_lemon.html">page_util_norm_lemon</a></td>
<td class="#doctools_tocright">page AST normalization, LEMON</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='page_util_norm_peg'><a href="files/modules/page/page_util_norm_peg.html">page_util_norm_peg</a></td>
<td class="#doctools_tocright">page AST normalization, PEG</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='page_util_peg'><a href="files/modules/page/page_util_peg.html">page_util_peg</a></td>
<td class="#doctools_tocright">page PEG transformation utilities</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='page_util_quote'><a href="files/modules/page/page_util_quote.html">page_util_quote</a></td>
<td class="#doctools_tocright">page character quoting utilities</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='picoirc'><a href="files/modules/irc/picoirc.html">picoirc</a></td>
<td class="#doctools_tocright">Small and simple embeddable IRC client.</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pki'><a href="files/modules/pki/pki.html">pki</a></td>
<td class="#doctools_tocright">Implementation of the public key cipher</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pluginmgr'><a href="files/modules/pluginmgr/pluginmgr.html">pluginmgr</a></td>
<td class="#doctools_tocright">Manage a plugin</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='png'><a href="files/modules/png/png.html">png</a></td>
<td class="#doctools_tocright">PNG querying and manipulation of meta data</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pop3'><a href="files/modules/pop3/pop3.html">pop3</a></td>
<td class="#doctools_tocright">Tcl client for POP3 email protocol</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pop3d'><a href="files/modules/pop3d/pop3d.html">pop3d</a></td>
<td class="#doctools_tocright">Tcl POP3 server implementation</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pop3d_dbox'><a href="files/modules/pop3d/pop3d_dbox.html">pop3d::dbox</a></td>
<td class="#doctools_tocright">Simple mailbox database for pop3d</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pop3d_udb'><a href="files/modules/pop3d/pop3d_udb.html">pop3d::udb</a></td>
<td class="#doctools_tocright">Simple user database for pop3d</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='practcl'><a href="files/modules/practcl/practcl.html">practcl</a></td>
<td class="#doctools_tocright">The Practcl Module</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='processman'><a href="files/modules/processman/processman.html">processman</a></td>
<td class="#doctools_tocright">Tool for automating the period callback of commands</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='profiler'><a href="files/modules/profiler/profiler.html">profiler</a></td>
<td class="#doctools_tocright">Tcl source code profiler</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt'><a href="files/apps/pt.html">pt</a></td>
<td class="#doctools_tocright">Parser Tools Application</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_ast'><a href="files/modules/pt/pt_astree.html">pt::ast</a></td>
<td class="#doctools_tocright">Abstract Syntax Tree Serialization</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_cparam_configuration_critcl'><a href="files/modules/pt/pt_cparam_config_critcl.html">pt::cparam::configuration::critcl</a></td>
<td class="#doctools_tocright">C/PARAM, Canned configuration, Critcl</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_cparam_configuration_tea'><a href="files/modules/pt/pt_cparam_config_tea.html">pt::cparam::configuration::tea</a></td>
<td class="#doctools_tocright">C/PARAM, Canned configuration, TEA</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_json_language'><a href="files/modules/pt/pt_json_language.html">pt::json_language</a></td>
<td class="#doctools_tocright">The JSON Grammar Exchange Format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_param'><a href="files/modules/pt/pt_param.html">pt::param</a></td>
<td class="#doctools_tocright">PackRat Machine Specification</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_pe'><a href="files/modules/pt/pt_pexpression.html">pt::pe</a></td>
<td class="#doctools_tocright">Parsing Expression Serialization</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_pe_op'><a href="files/modules/pt/pt_pexpr_op.html">pt::pe::op</a></td>
<td class="#doctools_tocright">Parsing Expression Utilities</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg'><a href="files/modules/pt/pt_pegrammar.html">pt::peg</a></td>
<td class="#doctools_tocright">Parsing Expression Grammar Serialization</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_container'><a href="files/modules/pt/pt_peg_container.html">pt::peg::container</a></td>
<td class="#doctools_tocright">PEG Storage</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_container_peg'><a href="files/modules/pt/pt_peg_container_peg.html">pt::peg::container::peg</a></td>
<td class="#doctools_tocright">PEG Storage. Canned PEG grammar specification</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_export'><a href="files/modules/pt/pt_peg_export.html">pt::peg::export</a></td>
<td class="#doctools_tocright">PEG Export</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_export_container'><a href="files/modules/pt/pt_peg_export_container.html">pt::peg::export::container</a></td>
<td class="#doctools_tocright">PEG Export Plugin. Write CONTAINER format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_export_json'><a href="files/modules/pt/pt_peg_export_json.html">pt::peg::export::json</a></td>
<td class="#doctools_tocright">PEG Export Plugin. Write JSON format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_export_peg'><a href="files/modules/pt/pt_peg_export_peg.html">pt::peg::export::peg</a></td>
<td class="#doctools_tocright">PEG Export Plugin. Write PEG format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_from_container'><a href="files/modules/pt/pt_peg_from_container.html">pt::peg::from::container</a></td>
<td class="#doctools_tocright">PEG Conversion. From CONTAINER format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_from_json'><a href="files/modules/pt/pt_peg_from_json.html">pt::peg::from::json</a></td>
<td class="#doctools_tocright">PEG Conversion. Read JSON format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_from_peg'><a href="files/modules/pt/pt_peg_from_peg.html">pt::peg::from::peg</a></td>
<td class="#doctools_tocright">PEG Conversion. Read PEG format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_import'><a href="files/modules/pt/pt_peg_import.html">pt::peg::import</a></td>
<td class="#doctools_tocright">PEG Import</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_import_container'><a href="files/modules/pt/pt_peg_import_container.html">pt::peg::import::container</a></td>
<td class="#doctools_tocright">PEG Import Plugin. From CONTAINER format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_import_json'><a href="files/modules/pt/pt_peg_import_json.html">pt::peg::import::json</a></td>
<td class="#doctools_tocright">PEG Import Plugin. Read JSON format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_import_peg'><a href="files/modules/pt/pt_peg_import_peg.html">pt::peg::import::peg</a></td>
<td class="#doctools_tocright">PEG Import Plugin. Read PEG format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_interp'><a href="files/modules/pt/pt_peg_interp.html">pt::peg::interp</a></td>
<td class="#doctools_tocright">Interpreter for parsing expression grammars</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_to_container'><a href="files/modules/pt/pt_peg_to_container.html">pt::peg::to::container</a></td>
<td class="#doctools_tocright">PEG Conversion. Write CONTAINER format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_to_cparam'><a href="files/modules/pt/pt_peg_to_cparam.html">pt::peg::to::cparam</a></td>
<td class="#doctools_tocright">PEG Conversion. Write CPARAM format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_to_json'><a href="files/modules/pt/pt_peg_to_json.html">pt::peg::to::json</a></td>
<td class="#doctools_tocright">PEG Conversion. Write JSON format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_to_param'><a href="files/modules/pt/pt_peg_to_param.html">pt::peg::to::param</a></td>
<td class="#doctools_tocright">PEG Conversion. Write PARAM format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_to_peg'><a href="files/modules/pt/pt_peg_to_peg.html">pt::peg::to::peg</a></td>
<td class="#doctools_tocright">PEG Conversion. Write PEG format</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_to_tclparam'><a href="files/modules/pt/pt_peg_to_tclparam.html">pt::peg::to::tclparam</a></td>
<td class="#doctools_tocright">PEG Conversion. Write TCLPARAM format</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_peg_language'><a href="files/modules/pt/pt_peg_language.html">pt::peg_language</a></td>
<td class="#doctools_tocright">PEG Language Tutorial</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_pegrammar'><a href="files/modules/pt/pt_peg_introduction.html">pt::pegrammar</a></td>
<td class="#doctools_tocright">Introduction to Parsing Expression Grammars</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_pgen'><a href="files/modules/pt/pt_pgen.html">pt::pgen</a></td>
<td class="#doctools_tocright">Parser Generator</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_rde'><a href="files/modules/pt/pt_rdengine.html">pt::rde</a></td>
<td class="#doctools_tocright">Parsing Runtime Support, PARAM based</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_tclparam_configuration_nx'><a href="files/modules/pt/pt_tclparam_config_nx.html">pt::tclparam::configuration::nx</a></td>
<td class="#doctools_tocright">Tcl/PARAM, Canned configuration, NX</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_tclparam_configuration_snit'><a href="files/modules/pt/pt_tclparam_config_snit.html">pt::tclparam::configuration::snit</a></td>
<td class="#doctools_tocright">Tcl/PARAM, Canned configuration, Snit</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_tclparam_configuration_tcloo'><a href="files/modules/pt/pt_tclparam_config_tcloo.html">pt::tclparam::configuration::tcloo</a></td>
<td class="#doctools_tocright">Tcl/PARAM, Canned configuration, Tcloo</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_util'><a href="files/modules/pt/pt_util.html">pt::util</a></td>
<td class="#doctools_tocright">General utilities</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_export_api'><a href="files/modules/pt/pt_to_api.html">pt_export_api</a></td>
<td class="#doctools_tocright">Parser Tools Export API</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_import_api'><a href="files/modules/pt/pt_from_api.html">pt_import_api</a></td>
<td class="#doctools_tocright">Parser Tools Import API</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_introduction'><a href="files/modules/pt/pt_introduction.html">pt_introduction</a></td>
<td class="#doctools_tocright">Introduction to Parser Tools</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_parse_peg'><a href="files/modules/pt/pt_parse_peg.html">pt_parse_peg</a></td>
<td class="#doctools_tocright">Parser Tools PEG Parser</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='pt_parser_api'><a href="files/modules/pt/pt_parser_api.html">pt_parser_api</a></td>
<td class="#doctools_tocright">Parser API</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='pt_peg_op'><a href="files/modules/pt/pt_peg_op.html">pt_peg_op</a></td>
<td class="#doctools_tocright">Parser Tools PE Grammar Utility Operations</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='rc4'><a href="files/modules/rc4/rc4.html">rc4</a></td>
<td class="#doctools_tocright">Implementation of the RC4 stream cipher</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='rcs'><a href="files/modules/rcs/rcs.html">rcs</a></td>
<td class="#doctools_tocright">RCS low level utilities</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='report'><a href="files/modules/report/report.html">report</a></td>
<td class="#doctools_tocright">Create and manipulate report objects</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='rest'><a href="files/modules/rest/rest.html">rest</a></td>
<td class="#doctools_tocright">define REST web APIs and call them inline or asychronously</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='ripemd128'><a href="files/modules/ripemd/ripemd128.html">ripemd128</a></td>
<td class="#doctools_tocright">RIPEMD-128 Message-Digest Algorithm</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='ripemd160'><a href="files/modules/ripemd/ripemd160.html">ripemd160</a></td>
<td class="#doctools_tocright">RIPEMD-160 Message-Digest Algorithm</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='s3'><a href="files/modules/amazon-s3/S3.html">S3</a></td>
<td class="#doctools_tocright">Amazon S3 Web Service Interface</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='sasl'><a href="files/modules/sasl/sasl.html">SASL</a></td>
<td class="#doctools_tocright">Implementation of SASL mechanisms for Tcl</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='sasl_ntlm'><a href="files/modules/sasl/ntlm.html">SASL::NTLM</a></td>
<td class="#doctools_tocright">Implementation of SASL NTLM mechanism for Tcl</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='sasl_scram'><a href="files/modules/sasl/scram.html">SASL::SCRAM</a></td>
<td class="#doctools_tocright">Implementation of SASL SCRAM mechanism for Tcl</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='sasl_xgoogletoken'><a href="files/modules/sasl/gtoken.html">SASL::XGoogleToken</a></td>
<td class="#doctools_tocright">Implementation of SASL NTLM mechanism for Tcl</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='sha1'><a href="files/modules/sha1/sha1.html">sha1</a></td>
<td class="#doctools_tocright">SHA1 Message-Digest Algorithm</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='sha256'><a href="files/modules/sha1/sha256.html">sha256</a></td>
<td class="#doctools_tocright">SHA256 Message-Digest Algorithm</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='simulation_annealing'><a href="files/modules/simulation/annealing.html">simulation::annealing</a></td>
<td class="#doctools_tocright">Simulated annealing</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='simulation_montecarlo'><a href="files/modules/simulation/montecarlo.html">simulation::montecarlo</a></td>
<td class="#doctools_tocright">Monte Carlo simulations</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='simulation_random'><a href="files/modules/simulation/simulation_random.html">simulation::random</a></td>
<td class="#doctools_tocright">Pseudo-random number generators</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='smtp'><a href="files/modules/mime/smtp.html">smtp</a></td>
<td class="#doctools_tocright">Client-side tcl implementation of the smtp protocol</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='smtpd'><a href="files/modules/smtpd/smtpd.html">smtpd</a></td>
<td class="#doctools_tocright">Tcl SMTP server implementation</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='snit'><a href="files/modules/snit/snit.html">snit</a></td>
<td class="#doctools_tocright">Snit's Not Incr Tcl</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='snitfaq'><a href="files/modules/snit/snitfaq.html">snitfaq</a></td>
<td class="#doctools_tocright">Snit Frequently Asked Questions</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='soundex'><a href="files/modules/soundex/soundex.html">soundex</a></td>
<td class="#doctools_tocright">Soundex</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='stooop'><a href="files/modules/stooop/stooop.html">stooop</a></td>
<td class="#doctools_tocright">Object oriented extension.</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='string_token'><a href="files/modules/string/token.html">string::token</a></td>
<td class="#doctools_tocright">Regex based iterative lexing</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='string_token_shell'><a href="files/modules/string/token_shell.html">string::token::shell</a></td>
<td class="#doctools_tocright">Parsing of shell command line</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='stringprep'><a href="files/modules/stringprep/stringprep.html">stringprep</a></td>
<td class="#doctools_tocright">Implementation of stringprep</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='stringprep_data'><a href="files/modules/stringprep/stringprep_data.html">stringprep::data</a></td>
<td class="#doctools_tocright">stringprep data tables, generated, internal</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_disjointset'><a href="files/modules/struct/disjointset.html">struct::disjointset</a></td>
<td class="#doctools_tocright">Disjoint set data structure</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_graph'><a href="files/modules/struct/graph.html">struct::graph</a></td>
<td class="#doctools_tocright">Create and manipulate directed graph objects</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_graph_op'><a href="files/modules/struct/graphops.html">struct::graph::op</a></td>
<td class="#doctools_tocright">Operation for (un)directed graph objects</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_graph_v1'><a href="files/modules/struct/graph1.html">struct::graph_v1</a></td>
<td class="#doctools_tocright">Create and manipulate directed graph objects</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_list'><a href="files/modules/struct/struct_list.html">struct::list</a></td>
<td class="#doctools_tocright">Procedures for manipulating lists</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_matrix'><a href="files/modules/struct/matrix.html">struct::matrix</a></td>
<td class="#doctools_tocright">Create and manipulate matrix objects</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_matrix_v1'><a href="files/modules/struct/matrix1.html">struct::matrix_v1</a></td>
<td class="#doctools_tocright">Create and manipulate matrix objects</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_pool'><a href="files/modules/struct/pool.html">struct::pool</a></td>
<td class="#doctools_tocright">Create and manipulate pool objects (of discrete items)</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_prioqueue'><a href="files/modules/struct/prioqueue.html">struct::prioqueue</a></td>
<td class="#doctools_tocright">Create and manipulate prioqueue objects</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_queue'><a href="files/modules/struct/queue.html">struct::queue</a></td>
<td class="#doctools_tocright">Create and manipulate queue objects</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_record'><a href="files/modules/struct/record.html">struct::record</a></td>
<td class="#doctools_tocright">Define and create records (similar to 'C' structures)</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_set'><a href="files/modules/struct/struct_set.html">struct::set</a></td>
<td class="#doctools_tocright">Procedures for manipulating sets</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_skiplist'><a href="files/modules/struct/skiplist.html">struct::skiplist</a></td>
<td class="#doctools_tocright">Create and manipulate skiplists</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_stack'><a href="files/modules/struct/stack.html">struct::stack</a></td>
<td class="#doctools_tocright">Create and manipulate stack objects</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='struct_tree'><a href="files/modules/struct/struct_tree.html">struct::tree</a></td>
<td class="#doctools_tocright">Create and manipulate tree objects</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='struct_tree_v1'><a href="files/modules/struct/struct_tree1.html">struct::tree_v1</a></td>
<td class="#doctools_tocright">Create and manipulate tree objects</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='sum'><a href="files/modules/crc/sum.html">sum</a></td>
<td class="#doctools_tocright">Calculate a sum(1) compatible checksum</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='switched'><a href="files/modules/stooop/switched.html">switched</a></td>
<td class="#doctools_tocright">switch/option management.</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tar'><a href="files/modules/tar/tar.html">tar</a></td>
<td class="#doctools_tocright">Tar file creation, extraction &amp; manipulation</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_cat'><a href="files/modules/virtchannel_base/cat.html">tcl::chan::cat</a></td>
<td class="#doctools_tocright">Concatenation channel</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_core'><a href="files/modules/virtchannel_core/core.html">tcl::chan::core</a></td>
<td class="#doctools_tocright">Basic reflected/virtual channel support</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_events'><a href="files/modules/virtchannel_core/events.html">tcl::chan::events</a></td>
<td class="#doctools_tocright">Event support for reflected/virtual channels</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_facade'><a href="files/modules/virtchannel_base/facade.html">tcl::chan::facade</a></td>
<td class="#doctools_tocright">Facade channel</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_fifo'><a href="files/modules/virtchannel_base/tcllib_fifo.html">tcl::chan::fifo</a></td>
<td class="#doctools_tocright">In-memory fifo channel</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_fifo2'><a href="files/modules/virtchannel_base/tcllib_fifo2.html">tcl::chan::fifo2</a></td>
<td class="#doctools_tocright">In-memory interconnected fifo channels</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_halfpipe'><a href="files/modules/virtchannel_base/halfpipe.html">tcl::chan::halfpipe</a></td>
<td class="#doctools_tocright">In-memory channel, half of a fifo2</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_memchan'><a href="files/modules/virtchannel_base/tcllib_memchan.html">tcl::chan::memchan</a></td>
<td class="#doctools_tocright">In-memory channel</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_null'><a href="files/modules/virtchannel_base/tcllib_null.html">tcl::chan::null</a></td>
<td class="#doctools_tocright">Null channel</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_nullzero'><a href="files/modules/virtchannel_base/nullzero.html">tcl::chan::nullzero</a></td>
<td class="#doctools_tocright">Null/Zero channel combination</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_random'><a href="files/modules/virtchannel_base/tcllib_random.html">tcl::chan::random</a></td>
<td class="#doctools_tocright">Random channel</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_std'><a href="files/modules/virtchannel_base/std.html">tcl::chan::std</a></td>
<td class="#doctools_tocright">Standard I/O, unification of stdin and stdout</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_string'><a href="files/modules/virtchannel_base/tcllib_string.html">tcl::chan::string</a></td>
<td class="#doctools_tocright">Read-only in-memory channel</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_textwindow'><a href="files/modules/virtchannel_base/textwindow.html">tcl::chan::textwindow</a></td>
<td class="#doctools_tocright">Textwindow channel</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_chan_variable'><a href="files/modules/virtchannel_base/tcllib_variable.html">tcl::chan::variable</a></td>
<td class="#doctools_tocright">In-memory channel using variable for storage</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_chan_zero'><a href="files/modules/virtchannel_base/tcllib_zero.html">tcl::chan::zero</a></td>
<td class="#doctools_tocright">Zero channel</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_randomseed'><a href="files/modules/virtchannel_base/randseed.html">tcl::randomseed</a></td>
<td class="#doctools_tocright">Utilities for random channels</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_transform_adler32'><a href="files/modules/virtchannel_transform/adler32.html">tcl::transform::adler32</a></td>
<td class="#doctools_tocright">Adler32 transformation</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_transform_base64'><a href="files/modules/virtchannel_transform/vt_base64.html">tcl::transform::base64</a></td>
<td class="#doctools_tocright">Base64 encoding transformation</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_transform_core'><a href="files/modules/virtchannel_core/transformcore.html">tcl::transform::core</a></td>
<td class="#doctools_tocright">Basic reflected/virtual channel transform support</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_transform_counter'><a href="files/modules/virtchannel_transform/vt_counter.html">tcl::transform::counter</a></td>
<td class="#doctools_tocright">Counter transformation</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_transform_crc32'><a href="files/modules/virtchannel_transform/vt_crc32.html">tcl::transform::crc32</a></td>
<td class="#doctools_tocright">Crc32 transformation</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_transform_hex'><a href="files/modules/virtchannel_transform/hex.html">tcl::transform::hex</a></td>
<td class="#doctools_tocright">Hexadecimal encoding transformation</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_transform_identity'><a href="files/modules/virtchannel_transform/identity.html">tcl::transform::identity</a></td>
<td class="#doctools_tocright">Identity transformation</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_transform_limitsize'><a href="files/modules/virtchannel_transform/limitsize.html">tcl::transform::limitsize</a></td>
<td class="#doctools_tocright">limiting input</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_transform_observe'><a href="files/modules/virtchannel_transform/observe.html">tcl::transform::observe</a></td>
<td class="#doctools_tocright">Observer transformation, stream copy</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_transform_otp'><a href="files/modules/virtchannel_transform/vt_otp.html">tcl::transform::otp</a></td>
<td class="#doctools_tocright">Encryption via one-time pad</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_transform_rot'><a href="files/modules/virtchannel_transform/rot.html">tcl::transform::rot</a></td>
<td class="#doctools_tocright">rot-encryption</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcl_transform_spacer'><a href="files/modules/virtchannel_transform/spacer.html">tcl::transform::spacer</a></td>
<td class="#doctools_tocright">Space insertation and removal</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcl_transform_zlib'><a href="files/modules/virtchannel_transform/tcllib_zlib.html">tcl::transform::zlib</a></td>
<td class="#doctools_tocright">zlib (de)compression</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcldes'><a href="files/modules/des/tcldes.html">tclDES</a></td>
<td class="#doctools_tocright">Implementation of the DES and triple-DES ciphers</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcldesjr'><a href="files/modules/des/tcldesjr.html">tclDESjr</a></td>
<td class="#doctools_tocright">Implementation of the DES and triple-DES ciphers</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tcldocstrip'><a href="files/apps/tcldocstrip.html">tcldocstrip</a></td>
<td class="#doctools_tocright">Tcl-based Docstrip Processor</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tcllib_ip'><a href="files/modules/dns/tcllib_ip.html">tcllib_ip</a></td>
<td class="#doctools_tocright">IPv4 and IPv6 address manipulation</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tclrep_machineparameters'><a href="files/modules/math/machineparameters.html">tclrep/machineparameters</a></td>
<td class="#doctools_tocright">Compute double precision machine parameters.</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tepam'><a href="files/modules/tepam/tepam_introduction.html">tepam</a></td>
<td class="#doctools_tocright">An introduction into TEPAM, Tcl's Enhanced Procedure and Argument Manager</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tepam_argument_dialogbox'><a href="files/modules/tepam/tepam_argument_dialogbox.html">tepam::argument_dialogbox</a></td>
<td class="#doctools_tocright">TEPAM argument_dialogbox, reference manual</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tepam_doc_gen'><a href="files/modules/tepam/tepam_doc_gen.html">tepam::doc_gen</a></td>
<td class="#doctools_tocright">TEPAM DOC Generation, reference manual</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tepam_procedure'><a href="files/modules/tepam/tepam_procedure.html">tepam::procedure</a></td>
<td class="#doctools_tocright">TEPAM procedure, reference manual</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='term'><a href="files/modules/term/term.html">term</a></td>
<td class="#doctools_tocright">General terminal control</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='term_ansi_code'><a href="files/modules/term/ansi_code.html">term::ansi::code</a></td>
<td class="#doctools_tocright">Helper for control sequences</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='term_ansi_code_attr'><a href="files/modules/term/ansi_cattr.html">term::ansi::code::attr</a></td>
<td class="#doctools_tocright">ANSI attribute sequences</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='term_ansi_code_ctrl'><a href="files/modules/term/ansi_cctrl.html">term::ansi::code::ctrl</a></td>
<td class="#doctools_tocright">ANSI control sequences</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='term_ansi_code_macros'><a href="files/modules/term/ansi_cmacros.html">term::ansi::code::macros</a></td>
<td class="#doctools_tocright">Macro sequences</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='term_ansi_ctrl_unix'><a href="files/modules/term/ansi_ctrlu.html">term::ansi::ctrl::unix</a></td>
<td class="#doctools_tocright">Control operations and queries</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='term_ansi_send'><a href="files/modules/term/ansi_send.html">term::ansi::send</a></td>
<td class="#doctools_tocright">Output of ANSI control sequences to terminals</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='term_interact_menu'><a href="files/modules/term/imenu.html">term::interact::menu</a></td>
<td class="#doctools_tocright">Terminal widget, menu</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='term_interact_pager'><a href="files/modules/term/ipager.html">term::interact::pager</a></td>
<td class="#doctools_tocright">Terminal widget, paging</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='term_receive'><a href="files/modules/term/receive.html">term::receive</a></td>
<td class="#doctools_tocright">General input from terminals</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='term_receive_bind'><a href="files/modules/term/term_bind.html">term::receive::bind</a></td>
<td class="#doctools_tocright">Keyboard dispatch from terminals</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='term_send'><a href="files/modules/term/term_send.html">term::send</a></td>
<td class="#doctools_tocright">General output to terminals</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='textutil'><a href="files/modules/textutil/textutil.html">textutil</a></td>
<td class="#doctools_tocright">Procedures to manipulate texts and strings.</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='textutil_adjust'><a href="files/modules/textutil/adjust.html">textutil::adjust</a></td>
<td class="#doctools_tocright">Procedures to adjust, indent, and undent paragraphs</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='textutil_expander'><a href="files/modules/textutil/expander.html">textutil::expander</a></td>
<td class="#doctools_tocright">Procedures to process templates and expand text.</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='textutil_repeat'><a href="files/modules/textutil/repeat.html">textutil::repeat</a></td>
<td class="#doctools_tocright">Procedures to repeat strings.</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='textutil_split'><a href="files/modules/textutil/textutil_split.html">textutil::split</a></td>
<td class="#doctools_tocright">Procedures to split texts</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='textutil_string'><a href="files/modules/textutil/textutil_string.html">textutil::string</a></td>
<td class="#doctools_tocright">Procedures to manipulate texts and strings.</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='textutil_tabify'><a href="files/modules/textutil/tabify.html">textutil::tabify</a></td>
<td class="#doctools_tocright">Procedures to (un)tabify strings</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='textutil_trim'><a href="files/modules/textutil/trim.html">textutil::trim</a></td>
<td class="#doctools_tocright">Procedures to trim strings</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='throw'><a href="files/modules/try/tcllib_throw.html">throw</a></td>
<td class="#doctools_tocright">throw - Throw an error exception with a message</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tie'><a href="files/modules/tie/tie_std.html">tie</a></td>
<td class="#doctools_tocright">Array persistence, standard data sources</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tie'><a href="files/modules/tie/tie.html">tie</a></td>
<td class="#doctools_tocright">Array persistence</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tiff'><a href="files/modules/tiff/tiff.html">tiff</a></td>
<td class="#doctools_tocright">TIFF reading, writing, and querying and manipulation of meta data</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tool'><a href="files/modules/httpd/httpd.html">tool</a></td>
<td class="#doctools_tocright">A TclOO and coroutine based web server</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='tool'><a href="files/modules/tool/tool.html">tool</a></td>
<td class="#doctools_tocright">TclOO Library (TOOL) Framework</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tool_dict_ensemble'><a href="files/modules/tool/tool_dict_ensemble.html">tool::dict_ensemble</a></td>
<td class="#doctools_tocright">Dictionary Tools</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='transfer_connect'><a href="files/modules/transfer/connect.html">transfer::connect</a></td>
<td class="#doctools_tocright">Connection setup</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='transfer_copy'><a href="files/modules/transfer/copyops.html">transfer::copy</a></td>
<td class="#doctools_tocright">Data transfer foundation</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='transfer_copy_queue'><a href="files/modules/transfer/tqueue.html">transfer::copy::queue</a></td>
<td class="#doctools_tocright">Queued transfers</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='transfer_data_destination'><a href="files/modules/transfer/ddest.html">transfer::data::destination</a></td>
<td class="#doctools_tocright">Data destination</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='transfer_data_source'><a href="files/modules/transfer/dsource.html">transfer::data::source</a></td>
<td class="#doctools_tocright">Data source</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='transfer_receiver'><a href="files/modules/transfer/receiver.html">transfer::receiver</a></td>
<td class="#doctools_tocright">Data source</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='transfer_transmitter'><a href="files/modules/transfer/transmitter.html">transfer::transmitter</a></td>
<td class="#doctools_tocright">Data source</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='treeql'><a href="files/modules/treeql/treeql.html">treeql</a></td>
<td class="#doctools_tocright">Query tree objects</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='try'><a href="files/modules/try/tcllib_try.html">try</a></td>
<td class="#doctools_tocright">try - Trap and process errors and exceptions</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='udpcluster'><a href="files/modules/udpcluster/udpcluster.html">udpcluster</a></td>
<td class="#doctools_tocright">UDP Peer-to-Peer cluster</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='uevent'><a href="files/modules/uev/uevent.html">uevent</a></td>
<td class="#doctools_tocright">User events</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='uevent_onidle'><a href="files/modules/uev/uevent_onidle.html">uevent::onidle</a></td>
<td class="#doctools_tocright">Request merging and deferal to idle time</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='unicode'><a href="files/modules/stringprep/unicode.html">unicode</a></td>
<td class="#doctools_tocright">Implementation of Unicode normalization</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='unicode_data'><a href="files/modules/stringprep/unicode_data.html">unicode::data</a></td>
<td class="#doctools_tocright">unicode data tables, generated, internal</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='units'><a href="files/modules/units/units.html">units</a></td>
<td class="#doctools_tocright">unit conversion</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='uri'><a href="files/modules/uri/uri.html">uri</a></td>
<td class="#doctools_tocright">URI utilities</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='uri_urn'><a href="files/modules/uri/urn-scheme.html">uri_urn</a></td>
<td class="#doctools_tocright">URI utilities, URN scheme</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='uuencode'><a href="files/modules/base64/uuencode.html">uuencode</a></td>
<td class="#doctools_tocright">UU-encode/decode binary data</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='uuid'><a href="files/modules/uuid/uuid.html">uuid</a></td>
<td class="#doctools_tocright">UUID generation and comparison</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='valtype_common'><a href="files/modules/valtype/valtype_common.html">valtype::common</a></td>
<td class="#doctools_tocright">Validation, common code</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='valtype_creditcard_amex'><a href="files/modules/valtype/cc_amex.html">valtype::creditcard::amex</a></td>
<td class="#doctools_tocright">Validation for AMEX creditcard number</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='valtype_creditcard_discover'><a href="files/modules/valtype/cc_discover.html">valtype::creditcard::discover</a></td>
<td class="#doctools_tocright">Validation for Discover creditcard number</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='valtype_creditcard_mastercard'><a href="files/modules/valtype/cc_mastercard.html">valtype::creditcard::mastercard</a></td>
<td class="#doctools_tocright">Validation for Mastercard creditcard number</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='valtype_creditcard_visa'><a href="files/modules/valtype/cc_visa.html">valtype::creditcard::visa</a></td>
<td class="#doctools_tocright">Validation for VISA creditcard number</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='valtype_gs1_ean13'><a href="files/modules/valtype/ean13.html">valtype::gs1::ean13</a></td>
<td class="#doctools_tocright">Validation for EAN13</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='valtype_iban'><a href="files/modules/valtype/iban.html">valtype::iban</a></td>
<td class="#doctools_tocright">Validation for IBAN</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='valtype_imei'><a href="files/modules/valtype/imei.html">valtype::imei</a></td>
<td class="#doctools_tocright">Validation for IMEI</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='valtype_isbn'><a href="files/modules/valtype/isbn.html">valtype::isbn</a></td>
<td class="#doctools_tocright">Validation for ISBN</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='valtype_luhn'><a href="files/modules/valtype/luhn.html">valtype::luhn</a></td>
<td class="#doctools_tocright">Validation for plain number with a LUHN checkdigit</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='valtype_luhn5'><a href="files/modules/valtype/luhn5.html">valtype::luhn5</a></td>
<td class="#doctools_tocright">Validation for plain number with a LUHN5 checkdigit</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='valtype_usnpi'><a href="files/modules/valtype/usnpi.html">valtype::usnpi</a></td>
<td class="#doctools_tocright">Validation for USNPI</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='valtype_verhoeff'><a href="files/modules/valtype/verhoeff.html">valtype::verhoeff</a></td>
<td class="#doctools_tocright">Validation for plain number with a VERHOEFF checkdigit</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='websocket'><a href="files/modules/websocket/websocket.html">websocket</a></td>
<td class="#doctools_tocright">Tcl implementation of the websocket protocol</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='wip'><a href="files/modules/wip/wip.html">wip</a></td>
<td class="#doctools_tocright">Word Interpreter</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='xsxp'><a href="files/modules/amazon-s3/xsxp.html">xsxp</a></td>
<td class="#doctools_tocright">eXtremely Simple Xml Parser</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='yaml'><a href="files/modules/yaml/yaml.html">yaml</a></td>
<td class="#doctools_tocright">YAML Format Encoder/Decoder</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='yencode'><a href="files/modules/base64/yencode.html">yencode</a></td>
<td class="#doctools_tocright">Y-encode/decode binary data</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='zipfile_decode'><a href="files/modules/zip/decode.html">zipfile::decode</a></td>
<td class="#doctools_tocright">Access to zip archives</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='zipfile_encode'><a href="files/modules/zip/encode.html">zipfile::encode</a></td>
<td class="#doctools_tocright">Generation of zip archives</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='zipfile_mkzip'><a href="files/modules/zip/mkzip.html">zipfile::mkzip</a></td>
<td class="#doctools_tocright">Build a zip archive</td>
</tr>
</table>
</dl><hr></body></html>

Changes to idoc/www/toc.html.

811
812
813
814
815
816
817




818
819
820
821

822
823
824
825

826
827
828
829
830
831
832
811
812
813
814
815
816
817
818
819
820
821
822
823
824

825
826
827
828

829
830
831
832
833
834
835
836







+
+
+
+



-
+



-
+







<td class="#doctools_tocright">Special mathematical functions</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='math_statistics'><a href="tcllib/files/modules/math/statistics.html">math::statistics</a></td>
<td class="#doctools_tocright">Basic statistical functions and procedures</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='math_trig'><a href="tcllib/files/modules/math/trig.html">math::trig</a></td>
<td class="#doctools_tocright">Trigonometric anf hyperbolic functions</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='simulation_annealing'><a href="tcllib/files/modules/simulation/annealing.html">simulation::annealing</a></td>
<td class="#doctools_tocright">Simulated annealing</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='simulation_montecarlo'><a href="tcllib/files/modules/simulation/montecarlo.html">simulation::montecarlo</a></td>
<td class="#doctools_tocright">Monte Carlo simulations</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='simulation_random'><a href="tcllib/files/modules/simulation/simulation_random.html">simulation::random</a></td>
<td class="#doctools_tocright">Pseudo-random number generators</td>
</tr>
</table></dl>
<dl><dt><a name='networking'>Networking<dd>
<table class="#doctools_toc">
<tr class="#doctools_toceven" >
2805
2806
2807
2808
2809
2810
2811




2812
2813
2814
2815
2816
2817
2818
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826







+
+
+
+







<td class="#doctools_tocright">Special mathematical functions</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='math_statistics'><a href="tcllib/files/modules/math/statistics.html">math::statistics</a></td>
<td class="#doctools_tocright">Basic statistical functions and procedures</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='math_trig'><a href="tcllib/files/modules/math/trig.html">math::trig</a></td>
<td class="#doctools_tocright">Trigonometric anf hyperbolic functions</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tclrep_machineparameters'><a href="tcllib/files/modules/math/machineparameters.html">tclrep/machineparameters</a></td>
<td class="#doctools_tocright">Compute double precision machine parameters.</td>
</tr>
</table></dl>
<dl><dt><a name='md4'>md4<dd>
<table class="#doctools_toc">
<tr class="#doctools_toceven" >

Changes to idoc/www/toc0.html.

811
812
813
814
815
816
817




818
819
820
821

822
823
824
825

826
827
828
829
830
831
832
811
812
813
814
815
816
817
818
819
820
821
822
823
824

825
826
827
828

829
830
831
832
833
834
835
836







+
+
+
+



-
+



-
+







<td class="#doctools_tocright">Special mathematical functions</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='math_statistics'><a href="tcllib/files/modules/math/statistics.html">math::statistics</a></td>
<td class="#doctools_tocright">Basic statistical functions and procedures</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='math_trig'><a href="tcllib/files/modules/math/trig.html">math::trig</a></td>
<td class="#doctools_tocright">Trigonometric anf hyperbolic functions</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='simulation_annealing'><a href="tcllib/files/modules/simulation/annealing.html">simulation::annealing</a></td>
<td class="#doctools_tocright">Simulated annealing</td>
</tr>
<tr class="#doctools_toceven" >
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='simulation_montecarlo'><a href="tcllib/files/modules/simulation/montecarlo.html">simulation::montecarlo</a></td>
<td class="#doctools_tocright">Monte Carlo simulations</td>
</tr>
<tr class="#doctools_tocodd"  >
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='simulation_random'><a href="tcllib/files/modules/simulation/simulation_random.html">simulation::random</a></td>
<td class="#doctools_tocright">Pseudo-random number generators</td>
</tr>
</table></dl>
<dl><dt><a name='networking'>Networking<dd>
<table class="#doctools_toc">
<tr class="#doctools_toceven" >

Changes to idoc/www/toc1.html.

980
981
982
983
984
985
986




987
988
989
990
991
992
993
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997







+
+
+
+







<td class="#doctools_tocright">Special mathematical functions</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='math_statistics'><a href="tcllib/files/modules/math/statistics.html">math::statistics</a></td>
<td class="#doctools_tocright">Basic statistical functions and procedures</td>
</tr>
<tr class="#doctools_toceven" >
<td class="#doctools_tocleft" ><a name='math_trig'><a href="tcllib/files/modules/math/trig.html">math::trig</a></td>
<td class="#doctools_tocright">Trigonometric anf hyperbolic functions</td>
</tr>
<tr class="#doctools_tocodd"  >
<td class="#doctools_tocleft" ><a name='tclrep_machineparameters'><a href="tcllib/files/modules/math/machineparameters.html">tclrep/machineparameters</a></td>
<td class="#doctools_tocright">Compute double precision machine parameters.</td>
</tr>
</table></dl>
<dl><dt><a name='md4'>md4<dd>
<table class="#doctools_toc">
<tr class="#doctools_toceven" >

Added modules/clay/build/build.tcl.







































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
set srcdir [file dirname [file normalize [file join [pwd] [info script]]]]
set moddir [file dirname $srcdir]

set version 0.3
set module clay
set filename clay
source [file join $srcdir doctool.tcl]
::clay::doctool create AutoDoc

set fout [open [file join $moddir ${filename}.tcl] w]
dict set modmap %module% $module
dict set modmap %version% $version
dict set modmap %license% BSD
dict set modmap %filename% $filename

set authors {{Sean Woods} {<[email protected]>}}

puts $fout [string map $modmap {###
# %filename%.tcl
#
# Copyright (c) 2018 Sean Woods
#
# BSD License
###
# @@ Meta Begin
# Package %module% %version%
# Meta platform     tcl
# Meta summary      A minimalist framework for complex TclOO development
# Meta description  This package introduces the method "clay" to both oo::object
# Meta description  and oo::class which facilitate complex interactions between objects
# Meta description  and their ancestor and mixed in classes.
# Meta category     TclOO
# Meta subject      framework
# Meta require      {Tcl 8.6}}]
foreach {name email} $authors {
  puts $fout   "# Meta author       $name"
}
puts $fout [string map $modmap {# Meta license      %license%
# @@ Meta End
}]

puts $fout [string map $modmap {###
# Amalgamated package for %module%
# Do not edit directly, tweak the source in build/ and rerun
# build.tcl
###
package provide %module% %version%
namespace eval ::%module% {}
}]


# Track what files we have included so far
set loaded {}
lappend loaded build.tcl test.tcl

# These files must be loaded in a particular order
foreach file {
  core.tcl
  procs.tcl
  class.tcl
  object.tcl
  metaclass.tcl
  ensemble.tcl
} {
  lappend loaded $file
  set content [::clay::cat [file join $srcdir {*}$file]]
  AutoDoc scan_text $content
  puts $fout "###\n# START: [file tail $file]\n###"
  puts $fout [::clay::docstrip $content]
  puts $fout "###\n# END: [file tail $file]\n###"
}
# These files can be loaded in any order
foreach file [lsort -dictionary [glob [file join $srcdir *.tcl]]] {
  if {[file tail $file] in $loaded} continue
  lappend loaded $file
  set content [::clay::cat [file join $srcdir {*}$file]]
  AutoDoc scan_text $content
  puts $fout "###\n# START: [file tail $file]\n###"
  puts $fout [::clay::docstrip $content]
  puts $fout "###\n# END: [file tail $file]\n###"
}

# Provide some cleanup and our final package provide
puts $fout [string map $modmap {
namespace eval ::%module% {
  namespace export *
}
}]
close $fout

###
# Build our pkgIndex.tcl file
###
set fout [open [file join $moddir pkgIndex.tcl] w]
puts $fout [string map $modmap {# Tcl package index file, version 1.1
# This file is generated by the "pkg_mkIndex" command
# and sourced either when an application starts up or
# by a "package unknown" script.  It invokes the
# "package ifneeded" command to set up package-related
# information so that packages will be loaded automatically
# in response to "package require" commands.  When this
# script is sourced, the variable $dir must contain the
# full path name of this file's directory.

if {![package vsatisfies [package provide Tcl] 8.6]} {return}
}]
puts $fout [string map $modmap {
package ifneeded %module% %version% [list source [file join $dir %module%.tcl]]
}]

#package ifneeded oo::meta 0.8 {package require %module% %version ; package provide oo::meta 0.8}
#package ifneeded oo::option 0.4 {package require %module% %version ; package provide oo::option 0.4}

puts $fout [string map $modmap {
package ifneeded oo::meta 0.8 [list source [file join $dir %module%.tcl]]
}]

close $fout

###
# Generate the test script
###
namespace eval ::clay {}
source [file join $srcdir procs.tcl]
set fout [open [file join $moddir $filename.test] w]
puts $fout [source [file join $srcdir test.tcl]]
close $fout
set manout [open [file join $moddir $filename.man] w]
puts $manout [AutoDoc manpage \
  header [string map $modmap [::clay::cat [file join $srcdir manual.txt]]] \
  authors $authors \
  footer [string map $modmap [::clay::cat [file join $srcdir footer.txt]]] \
]
close $manout

Added modules/clay/build/class.tcl.

























































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
oo::define oo::class {

  ###
  # description:
  # The [method clay] method allows a class object access
  # to a combination of its own clay data as
  # well as to that of its ancestors
  # ensemble:
  # ancestors {
  #   arglist {}
  #   description {Return this class and all ancestors in search order.}
  # }
  # dump {
  #   arglist {}
  #   description {Return a complete dump of this object's clay data, but only this object's clay data.}
  # }
  # get {
  #   arglist {path {mandatory 1 positional 1 repeating 1}}
  #   description {
  #     Pull a chunk of data from the clay system. If the last element of [emph path] is a branch (ends in a slash /),
  #     returns a recursive merge of all data from this object and it's constituent classes of the data in that branch.
  #     If the last element is a leaf, search this object for a matching leaf, or search all  constituent classes for a matching
  #     leaf and return the first value found.
  #     If no value is found, returns an empty string.
  #   }
  # }
  # merge {
  #   arglist {dict {mandatory 1 positional 1 repeating 1}}
  #   description {Recursively merge the dictionaries given into the object's local clay storage.}
  # }
  # replace {
  #   arglist {dictionary {mandatory 1 positional 1}}
  #   description {Replace the contents of the internal clay storage with the dictionary given.}
  # }
  # search {
  #   arglist {path {mandatory 1 positional 1 repeating 1}}
  #   description {Return the first matching value for the path in either this class's clay data or one of its ancestors}
  # }
  # set {
  #   arglist {path {mandatory 1 positional 1 repeating 1} value {mandatory 1 postional 1}}
  #   description {Merge the conents of [const value] with the object's clay storage at [const path].}
  # }
  ###
  method clay {submethod args} {
    my variable clay
    if {![info exists clay]} {
      set clay {}
    }
    switch $submethod {
      ancestors {
        tailcall ::clay::ancestors [self]
      }
      exists {
        set path [::clay::leaf {*}$args]
        if {![info exists clay]} {
          return 0
        }
        return [dict exists $clay {*}$path]
      }
      dump {
        return $clay
      }
      getnull -
      get {
        set path $args
        set leaf [expr {[string index [lindex $path end] end] ne "/"}]
        set clayorder [::clay::ancestors [self]]
        #puts [list [self] clay get {*}$path (leaf: $leaf)]
        if {$leaf} {
          #puts [list EXISTS: (clay) [dict exists $clay {*}$path]]
          if {[dict exists $clay {*}$path]} {
            return [dict get $clay {*}$path]
          }
          #puts [list Search in the in our list of classes for an answer]
          foreach class $clayorder {
            if {$class eq [self]} continue
            if {[$class clay exists {*}$path]} {
              set value [$class clay get {*}$path]
              return $value
            }
          }
        } else {
          set result {}
          # Leaf searches return one data field at a time
          # Search in our local dict
          # Search in the in our list of classes for an answer
          foreach class [lreverse $clayorder] {
            if {$class eq [self]} continue
            ::clay::dictmerge result [$class clay get {*}$path]
          }
          if {[dict exists $clay {*}$path]} {
            ::clay::dictmerge result [dict get $clay {*}$path]
          }
          return $result
        }
      }
      merge {
        foreach arg $args {
          ::clay::dictmerge clay {*}$arg
        }
      }
      search {
        foreach aclass [::clay::ancestors [self]] {
          if {[$aclass clay exists {*}$args]} {
            return [$aclass clay get {*}$args]
          }
        }
      }
      set {
        #puts [list [self] clay SET {*}$args]
        set value [lindex $args end]
        set path [::clay::leaf {*}[lrange $args 0 end-1]]
        ::clay::dictmerge clay {*}$path $value
      }
      default {
        dict $submethod clay {*}$args
      }
    }
  }
}

Added modules/clay/build/core.tcl.











1
2
3
4
5
6
7
8
9
10
+
+
+
+
+
+
+
+
+
+
package require Tcl 8.6 ;# try in pipeline.tcl. Possibly other things.
package require TclOO
package require uuid
package require oo::dialect

::oo::dialect::create ::clay

::namespace eval ::clay {}
::namespace eval ::clay::classes {}
::namespace eval ::clay::define {}

Added modules/clay/build/doctool.tcl.























































































































































































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
namespace eval ::clay {}

###
# Concatenate a file
###
proc ::clay::cat fname {
    if {![file exists $fname]} {
       return
    }
    set fin [open $fname r]
    set data [read $fin]
    close $fin
    return $data
}

###
# Strip the global comments from tcl code. Used to
# prevent the documentation markup comments from clogging
# up files intended for distribution in machine readable format.
###
proc ::clay::docstrip text {
  set result {}
  foreach line [split $text \n] {
    append thisline $line \n
    if {![info complete $thisline]} continue
    set outline $thisline
    set thisline {}
    if {[string trim $outline] eq {}} {
      continue
    }
    if {[string index [string trim $outline] 0] eq "#"} continue
    set cmd [string trim [lindex $outline 0] :]
    if {$cmd eq "namespace" && [lindex $outline 1] eq "eval"} {
      append result [list {*}[lrange $outline 0 end-1] [docstrip [lindex $outline end]]] \n
      continue
    }
    if {[string match "*::define" $cmd] && [llength $outline]==3} {
      append result [list {*}[lrange $outline 0 end-1] [docstrip [lindex $outline end]]] \n
      continue
    }
    if {$cmd eq "oo::class" && [lindex $outline 1] eq "create"} {
      append result [list {*}[lrange $outline 0 end-1] [docstrip [lindex $outline end]]] \n
      continue
    }
    append result $outline
  }
  return $result
}


###
# Append a line of text to a variable. Optionally apply a string mapping.
# arglist:
#   map {mandatory 0 positional 1}
#   text {mandatory 1 positional 1}
###
proc ::putb {buffername args} {
  upvar 1 $buffername buffer
  switch [llength $args] {
    1 {
      append buffer [lindex $args 0] \n
    }
    2 {
      append buffer [string map {*}$args] \n
    }
    default {
      error "usage: putb buffername ?map? string"
    }
  }
}

###
# Tool for build scripts to dynamically generate manual files from comments
# in source code files
# example:
# set authors {
#   {John Doe} {[email protected]}
#   {Tom RichardHarry} {[email protected]}
# }
# # Create the object
# ::clay::doctool create AutoDoc
# set fout [open [file join $moddir module.tcl] w]
# foreach file [glob [file join $srcdir *.tcl]] {
#   set content [::clay::cat [file join $srcdir $file]]
#    # Scan the file
#    AutoDoc scan_text $content
#    # Strip the comments from the distribution
#    puts $fout [::clay::docstrip $content]
# }
# # Write out the manual page
# set manout [open [file join $moddir module.man] w]
# dict set arglist header [string map $modmap [::clay::cat [file join $srcdir manual.txt]]]
# dict set arglist footer [string map $modmap [::clay::cat [file join $srcdir footer.txt]]]
# dict set arglist authors $authors
# puts $manout [AutoDoc manpage {*}$arglist]
# close $manout
###
oo::class create ::clay::doctool {
  constructor {} {
    my reset
  }

  ###
  # Process an argument list into an informational dict.
  # This method also understands non-positional
  # arguments expressed in the notation of Tip 471
  # [uri https://core.tcl-lang.org/tips/doc/trunk/tip/479.md].
  # [para]
  # The output will be a dictionary of all of the fields and whether the fields
  # are [const positional], [const mandatory], and whether they have a
  # [const default] value.
  # [para]
  # example:
  #   my arglist {a b {c 10}}
  #
  #   > a {positional 1 mandatory 1} b {positional 1 mandatory 1} c {positional 1 mandatory 0 default 10}
  ###
  method arglist {arglist} {
    set result [dict create]
    foreach arg $arglist {
      set name [lindex $arg 0]
      dict set result $name positional 1
      dict set result $name mandatory  1
      if {$name in {args dictargs}} {
        switch [llength $arg] {
          1 {
            dict set result $name mandatory 0
          }
          2 {
            dict for {optname optinfo} [lindex $arg 1] {
              set optname [string trim $optname -:]
              dict set result $optname {positional 1 mandatory 0}
              dict for {f v} $optinfo {
                dict set result $optname [string trim $f -:] $v
              }
            }
          }
          default {
            error "Bad argument"
          }
        }
      } else {
        switch [llength $arg] {
          1 {
            dict set result $name mandatory 1
          }
          2 {
            dict set result $name mandatory 0
            dict set result $name default   [lindex $arg 1]
          }
          default {
            error "Bad argument"
          }
        }
      }
    }
    return $result
  }

  ###
  # Convert a block of comments into an informational dictionary.
  # If lines in the comment start with a single word ending in a colon,
  # all subsequent lines are appended to a dictionary field of that name.
  # If no fields are given, all of the text is appended to the [const description]
  # field.
  # example:
  # my comment {Does something cool}
  # > description {Does something cool}
  #
  # my comment {
  # title : Something really cool
  # author : Sean Woods
  # author : John Doe
  # description :
  # This does something really cool!
  # }
  # > description {This does something really cool!}
  #   title {Something really cool}
  #   author {Sean Woods
  #   John Doe}
  ###
  method comment block {
    set count 0
    set field description
    set result [dict create description {}]
    foreach line [split $block \n] {
      set sline [string trim $line]
      set fwidx [string first " " $sline]
      if {$fwidx < 0} {
        set firstword [string range $sline 0 end]
        set restline {}
      } else {
        set firstword [string range $sline 0 [expr {$fwidx-1}]]
        set restline [string range $sline [expr {$fwidx+1}] end]
      }
      if {[string index $firstword end] eq ":"} {
        set field [string tolower [string trim $firstword -:]]
        switch $field {
          desc {
            set field description
          }
        }
        if {[string length $restline]} {
          dict append result $field "$restline\n"
        }
      } else {
        dict append result $field "$line\n"
      }
    }
    return $result
  }

  ###
  # Process an oo::objdefine call that modifies the class object
  # itself
  ####
  method keyword.Class {resultvar commentblock name body} {
    upvar 1 $resultvar result
    set name [string trim $name :]
    if {[dict exists $result class $name]} {
      set info [dict get $result class $name]
    } else {
      set info [my comment $commentblock]
    }
    set commentblock {}
    foreach line [split $body \n] {
      append thisline $line \n
      if {![info complete $thisline]} continue
      set thisline [string trim $thisline]
      if {[string index $thisline 0] eq "#"} {
        append commentblock [string trimleft $thisline #] \n
        set thisline {}
        continue
      }
      set cmd [string trim [lindex $thisline 0] ":"]
      switch $cmd {
        method -
        Ensemble {
          my keyword.class_method info $commentblock  {*}[lrange $thisline 1 end-1]
          set commentblock {}
        }
      }
      set thisline {}
    }
    dict set result class $name $info
  }

  ###
  # Process an oo::define, clay::define, etc statement.
  ###
  method keyword.class {resultvar commentblock name body} {
    upvar 1 $resultvar result
    set name [string trim $name :]
    if {[dict exists $result class $name]} {
      set info [dict get $result class $name]
    } else {
      set info [my comment $commentblock]
    }
    set commentblock {}
    foreach line [split $body \n] {
      append thisline $line \n
      if {![info complete $thisline]} continue
      set thisline [string trim $thisline]
      if {[string index $thisline 0] eq "#"} {
        append commentblock [string trimleft $thisline #] \n
        set thisline {}
        continue
      }
      set cmd [string trim [lindex $thisline 0] ":"]
      switch $cmd {
        superclass {
          dict set info ancestors [lrange $thisline 1 end]
          set commentblock {}
        }
        class_method {
          my keyword.class_method info $commentblock  {*}[lrange $thisline 1 end-1]
          set commentblock {}
        }
        destructor -
        constructor {
          my keyword.method info $commentblock {*}[lrange $thisline 0 end-1]
          set commentblock {}
        }
        method -
        Ensemble {
          my keyword.method info $commentblock  {*}[lrange $thisline 1 end-1]
          set commentblock {}
        }
      }
      set thisline {}
    }
    dict set result class $name $info
  }

  ###
  # Process a statement for a clay style class method
  ###
  method keyword.class_method {resultvar commentblock name args} {
    upvar 1 $resultvar result
    set info [my comment $commentblock]
    if {[dict exists $info ensemble]} {
      dict for {method minfo} [dict get $info ensemble] {
        dict set result class_method "${name} $method" $minfo
      }
    } else {
      switch [llength $args] {
        1 {
          set arglist [lindex $args 0]
        }
        0 {
          set arglist dictargs
          #set body [lindex $args 0]
        }
        default {error "could not interpret method $name {*}$args"}
      }
      if {![dict exists $info arglist]} {
        dict set info arglist [my arglist $arglist]
      }
      dict set result class_method [string trim $name :] $info
    }
  }

  ###
  # Process a statement for a tcloo style object method
  ###
  method keyword.method {resultvar commentblock name args} {
    upvar 1 $resultvar result
    set info [my comment $commentblock]
    if {[dict exists $info ensemble]} {
      dict for {method minfo} [dict get $info ensemble] {
        dict set result method "\"${name} $method\"" $minfo
      }
    } else {
      switch [llength $args] {
        1 {
          set arglist [lindex $args 0]
        }
        0 {
          set arglist dictargs
          #set body [lindex $args 0]
        }
        default {error "could not interpret method $name {*}$args"}
      }
      if {![dict exists $info arglist]} {
        dict set info arglist [my arglist $arglist]
      }
      dict set result method "\"[split [string trim $name :] ::]\"" $info
    }
  }

  ###
  # Process a proc statement
  ###
  method keyword.proc {commentblock name arglist body} {
    set info [my comment $commentblock]
    if {![dict exists $info arglist]} {
      dict set info arglist [my arglist $arglist]
    }
    return $info
  }

  ###
  # Reset the state of the object and its embedded coroutine
  ###
  method reset {} {
    my variable coro
    set coro [info object namespace [self]]::coro
    oo::objdefine [self] forward coro $coro
    if {[info command $coro] ne {}} {
      rename $coro {}
    }
    coroutine $coro {*}[namespace code {my Main}]
  }

  ###
  # Main body of the embedded coroutine for the object
  ###
  method Main {} {

    my variable info
    set info [dict create]
    yield [info coroutine]
    set thisline {}
    set commentblock {}
    set linec 0
    while 1 {
      set line [yield]
      append thisline $line \n
      if {![info complete $thisline]} continue
      set thisline [string trim $thisline]
      if {[string index $thisline 0] eq "#"} {
        append commentblock [string trimleft $thisline #] \n
        set thisline {}
        continue
      }
      set cmd [string trim [lindex $thisline 0] ":"]
      switch $cmd {
        Proc -
        proc {
          set procinfo [my keyword.proc $commentblock {*}[lrange $thisline 1 end]]
          dict set info proc [string trim [lindex $thisline 1] :] $procinfo
          set commentblock {}
        }
        oo::objdefine {
          if {[llength $thisline]==3} {
            lassign $thisline tcmd name body
            my keyword.Class info $commentblock $name $body
          } else {
            puts "Warning: bare oo::define in library"
          }
        }
        oo::define {
          if {[llength $thisline]==3} {
            lassign $thisline tcmd name body
            my keyword.class info $commentblock $name $body
          } else {
            puts "Warning: bare oo::define in library"
          }
        }
        tao::define -
        clay::define -
        tool::define {
          lassign $thisline tcmd name body
          my keyword.class info $commentblock $name $body
          set commentblock {}
        }
        oo::class {
          lassign $thisline tcmd mthd name body
          my keyword.class info $commentblock $name $body
          set commentblock {}
        }
        default {
          if {[lindex [split $cmd ::] end] eq "define"} {
            lassign $thisline tcmd name body
            my keyword.class info $commentblock $name $body
            set commentblock {}
          }
          set commentblock {}
        }
      }
      set thisline {}
    }
  }

  ###
  # Generate the manual page text for a method or proc
  ###
  method section.method {keyword method minfo} {
    set result {}
    set line "\[call $keyword \[cmd $method\]"
    if {[dict exists $minfo arglist]} {
      dict for {argname arginfo} [dict get $minfo arglist] {
        set positional 1
        set mandatory  1
        set repeating 0
        dict with arginfo {}
        if {$mandatory==0} {
          append line " \[opt \""
        } else {
          append line " "
        }
        if {$positional} {
          append line "\[arg $argname"
        } else {
          append line "\[option \"$argname"
          if {[dict exists $arginfo type]} {
            append line " \[emph [dict get $arginfo type]\]"
          } else {
            append line " \[emph value\]"
          }
          append line "\""
        }
        append line "\]"
        if {$mandatory==0} {
          if {[dict exists $arginfo default]} {
            append line " \[const \"[dict get $arginfo default]\"\]"
          }
          append line "\"\]"
        }
        if {$repeating} {
          append line " \[opt \[option \"$argname...\"\]\]"
        }
      }
    }
    append line \]
    putb result $line
    if {[dict exists $minfo description]} {
      putb result [dict get $minfo description]
    }
    if {[dict exists $minfo example]} {
      putb result "\[para\]Example: \[example [list [dict get $minfo example]]\]"
    }
    return $result
  }

  ###
  # Generate the manual page text for a class
  ###
  method section.class {class_name class_info} {
    set result {}
    putb result "\[subsection \{Class  $class_name\}\]"
    if {[dict exists $class_info ancestors]} {
      set line "\[emph \"ancestors\"\]:"
      foreach {c} [dict get $class_info ancestors] {
        append line " \[class [string trim $c :]\]"
      }
      putb result $line
      putb result {[para]}
    }
    dict for {f v} $class_info {
      if {$f in {class_method method description ancestors example}} continue
      putb result "\[emph \"$f\"\]: $v"
      putb result {[para]}
    }
    if {[dict exists $class_info example]} {
      putb result "\[example \{[list [dict get $class_info example]]\}\]"
      putb result {[para]}
    }
    if {[dict exists $class_info description]} {
      putb result [dict get $class_info description]
      putb result {[para]}
    }
    if {[dict exists $class_info class_method]} {
      putb result "\[class \{Class Methods\}\]"
      #putb result "Methods on the class object itself."
      putb result {[list_begin definitions]}
      dict for {method minfo} [dict get $class_info class_method] {
        putb result [my section.method classmethod $method $minfo]
      }
      putb result {[list_end]}
      putb result {[para]}
    }
    if {[dict exists $class_info method]} {
      putb result "\[class {Methods}\]"
      putb result {[list_begin definitions]}
      dict for {method minfo} [dict get $class_info method] {
        putb result [my section.method method $method $minfo]
      }
      putb result {[list_end]}
      putb result {[para]}
    }
    return $result
  }

  ###
  # Generate the manual page text for the commands section
  ###
  method section.command {procinfo} {
    set result {}
    putb result "\[section \{Commands\}\]"
    putb result {[list_begin definitions]}
    dict for {method minfo} $procinfo {
      putb result [my section.method proc $method $minfo]
    }
    putb result {[list_end]}
    return $result
  }

  ###
  # Generate the manual page. Returns the completed text suitable for saving in .man file.
  # The header argument is a block of doctools text to go in before the machine generated
  # section. footer is a block of doctools text to go in after the machine generated
  # section. authors is a list of individual authors and emails in the form of AUTHOR EMAIL ?AUTHOR EMAIL?...
  #
  # arglist:
  #   header {mandatory 0 positional 0}
  #   footer {mandatory 0 positional 0}
  #   authors {mandatory 0 positional 0 type list}
  ###
  method manpage args {
    my variable info map
    set result {}
    set header {}
    set footer {}
    set authors {}
    dict with args {}
    putb result $header
    dict for {sec_type sec_info} $info {
      switch $sec_type {
        proc {
          putb result [my section.command $sec_info]
        }
        class {
          putb result "\[section Classes\]"
          dict for {class_name class_info} $sec_info {
            putb result [my section.class $class_name $class_info]
          }
        }
        default {
          putb result "\[section [list $sec_type $sec_name]\]"
          if {[dict exists $sec_info description]} {
            putb result [dict get $sec_info description]
          }
        }
      }
    }
    if {[llength $authors]} {
      putb result {[section AUTHORS]}
      foreach {name email} $authors {
        putb result "$name \[uri mailto:$email\]\[para\]"
      }
    }
    putb result $footer
    putb result {[manpage_end]}
    return $result
  }

  # Scan a block of text
  method scan_text {text} {
    my variable linecount coro
    set linecount 0
    foreach line [split $text \n] {
      incr linecount
      $coro $line
    }
  }

  # Scan a file of text
  method scan_file {filename} {
    my variable linecount coro
    set fin [open $filename r]
    set linecount 0
    while {[gets $fin line]>=0} {
      incr linecount
      $coro $line
    }
    close $fin
  }
}

Added modules/clay/build/ensemble.tcl.
































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
::namespace eval ::clay::define {}

proc ::clay::ensemble_methodbody {ensemble einfo} {
  set default standard
  set preamble {}
  set eswitch {}
  if {[dict exists $einfo default]} {
    set emethodinfo [dict get $einfo default]
    set arglist     [dict getnull $emethodinfo arglist]
    set realbody    [dict get $emethodinfo body]
    if {[llength $arglist]==1 && [lindex $arglist 0] in {{} args arglist}} {
      set body {}
    } else {
      set body "\n      ::clay::dynamic_arguments $ensemble \$method [list $arglist] {*}\$args"
    }
    append body "\n      " [string trim $realbody] "      \n"
    set default $body
    dict unset einfo default
  }
  foreach {msubmethod esubmethodinfo} [lsort -dictionary -stride 2 $einfo] {
    set submethod [string trim $msubmethod :/-]
    if {$submethod eq "_body"} continue
    if {$submethod eq "_preamble"} {
      set preamble [dict getnull $esubmethodinfo body]
      continue
    }
    set arglist     [dict getnull $esubmethodinfo arglist]
    set realbody    [dict getnull $esubmethodinfo body]
    if {[string length [string trim $realbody]] eq {}} {
      dict set eswitch $submethod {}
    } else {
      if {[llength $arglist]==1 && [lindex $arglist 0] in {{} args arglist}} {
        set body {}
      } else {
        set body "\n      ::clay::dynamic_arguments $ensemble \$method [list $arglist] {*}\$args"
      }
      append body "\n      " [string trim $realbody] "      \n"
      if {$submethod eq "default"} {
        set default $body
      } else {
        dict set eswitch $submethod $body
      }
    }
  }
  set methodlist [lsort -dictionary [dict keys $eswitch]]
  if {![dict exists $eswitch <list>]} {
    dict set eswitch <list> {return $methodlist}
  }
  if {$default eq "standard"} {
    set default "error \"unknown method $ensemble \$method. Valid: \$methodlist\""
  }
  dict set eswitch default $default
  set mbody {}

  append mbody $preamble \n

  append mbody \n [list set methodlist $methodlist]
  append mbody \n "set code \[catch {switch -- \$method [list $eswitch]} result opts\]"
  append mbody \n {return -options $opts $result}
  return $mbody
}

::proc ::clay::define::Ensemble {rawmethod arglist body} {
  set class [current_class]
  #if {$::clay::trace>2} {
  #  puts [list $class Ensemble $rawmethod $arglist $body]
  #}
  set mlist [split $rawmethod "::"]
  set ensemble [string trim [lindex $mlist 0] :/]
  set mensemble ${ensemble}/
  if {[llength $mlist]==1 || [lindex $mlist 1] in "_body"} {
    set method _body
    ###
    # Simple method, needs no parsing, but we do need to record we have one
    ###
    $class clay set method_ensemble/ $mensemble _body [dict create arglist $arglist body $body]
    if {$::clay::trace>2} {
      puts [list $class clay set method_ensemble/ $mensemble _body ...]
    }
    set method $rawmethod
    if {$::clay::trace>2} {
      puts [list $class Ensemble $rawmethod $arglist $body]
      set rawbody $body
      set body {puts [list [self] $class [self method]]}
      append body \n $rawbody
    }
    ::oo::define $class method $rawmethod $arglist $body
    return
  }
  set method [join [lrange $mlist 2 end] "::"]
  $class clay set method_ensemble/ $mensemble [string trim $method :/] [dict create arglist $arglist body $body]
  if {$::clay::trace>2} {
    puts [list $class clay set method_ensemble/ $mensemble [string trim $method :/]  ...]
  }
}

Added modules/clay/build/footer.txt.



1
2
+
+
[vset CATEGORY oo]
[include ../doctools2base/include/feedback.inc]

Added modules/clay/build/manual.txt.



























































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
[vset VERSION %version%]
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin %module% n [vset VERSION]]
[keywords oo]
[copyright {2018 Sean Woods <[email protected]>}]
[moddesc   {Clay Framework}]
[titledesc {A minimalist framework for large scale OO Projects}]
[category  {Programming tools}]
[keywords TclOO]
[require Tcl 8.6]
[require uuid]
[require oo::dialect]
[description]
Clay introduces a method ensemble to both [class oo::class] and [class oo::object] called
clay. This ensemble handles all of the high level interactions within the framework.
Clay stores structured data. Clan manages method delegation. Clay has facilities to
manage the complex interactions that come about with mixins.
[para]
The central concept is that inside of every object and class
(which are actually objects too) is a dict called clay. What is stored in that dict is
left to the imagination. But because this dict is exposed via a public method, we can
share structured data between object, classes, and mixins.
[para]
[subsection {Structured Data}]
Clay uses a standardized set of method interactions and introspection that TclOO already provides to perform on-the-fly searches. On-the-fly searches mean that the data is never stale, and we avoid many of the sorts of collisions that would arise when objects start mixing in other classes during operation.
[para]
The [method clay] methods for both classes and objects have a get and a set method. For objects, get will search through the local clay dict. If the requested leaf is not found, or the query is for a branch, the system will then begin to poll the clay methods of all of the class that implements the object, all of that classes’ ancestors, as well as all of the classes that have been mixed into this object, and all of their ancestors.
[para]
Intended branches on a tree end with a directory slash (/). Intended leaves are left unadorned. This is a guide for the tool that builds the search
results to know what parts of a dict are intended to be branches and which are intended to be leaves.
For simple cases, branch marking can be ignored:
[example {
::oo::class create ::foo { }
::foo clay set property/ color blue
::foo clay set property/ shape round

set A [::foo new]
$A clay get property/
{color blue shape round}

$A clay set property/ shape square
$A clay get property/
{color blue shape square}
}]
[para]
But when you start storing blocks of text, guessing what field is a dict and what isn’t gets messy:
[example {
::foo clay set description {A generic thing of designated color and shape}

$A clay get description
{A generic thing of designated color and shape}

Without a convention for discerning branches for leaves what should have been a value can be accidentally parsed as a dictionary, and merged with all of the other values that were never intended to be merge. Here is an example of it all going wrong:
::oo::class create ::foo { }
# Add description as a leaf
::foo clay set description \
  {A generic thing of designated color and shape}
# Add description as a branch
::foo clay set description/ \
  {A generic thing of designated color and shape}

::oo::class create ::bar {
  superclass foo
}
# Add description as a leaf
::bar clay set description \
  {A drinking establishment of designated color and shape and size}
# Add description as a branch
::bar clay set description/ \
  {A drinking establishment of designated color and shape and size}

set B [::bar new]
# As a leaf we get the value verbatim from he nearest ancestor
$B clay get description
  {A drinking establishment of designated color and shape and size}
# As a branch we get a recursive merge
$B clay get description/
{A drinking establishment of designated color and size thing of}
}]
[subsection {Clay Dialect}]
Clay is built using the oo::dialect module from Tcllib. oo::dialect allows you to either add keywords directly to clay, or to create your own
metaclass and keyword set using Clay as a foundation. For details on the keywords and what they do, consult the functions in the ::clay::define namespace.
[subsection {Method Delegation}]
Method Delegation
It is sometimes useful to have an external object that can be invoked as if it were a method of the object. Clay provides a delegate ensemble method to perform that delegation, as well as introspect which methods are delegated in that manner. All delegated methods are marked with html-like tag markings (< >) around them.
[example {
::clay::define counter {
  Variable counter 0
  method incr {{howmuch 1}} {
    my variable counter
    incr counter $howmuch
  }
  method value {} {
    my variable counter
    return $counter
  }
  method reset {} {
    my variable counter
    set counter 0
  }
}
::clay::define example {
  variable buffer
  constructor {} {
    # Build a counter object
    set obj [namespace current]::counter
    ::counter create $obj
    # Delegate the counter
    my delegate <counter> $obj
  }
  method line {text} {
    my <counter> incr
    append buffer $text
  }
}

set A [example new]
$A line {Who’s line is it anyway?}
$A <counter> value
1
}]

Added modules/clay/build/metaclass.tcl.














































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#-------------------------------------------------------------------------
# TITLE:
#    clay.tcl
#
# PROJECT:
#    clay: TclOO Helper Library
#
# DESCRIPTION:
#    clay(n): Implementation File
#
#-------------------------------------------------------------------------


proc ::clay::dynamic_methods class {
  foreach command [info commands [namespace current]::dynamic_methods_*] {
    $command $class
  }
}

proc ::clay::dynamic_methods_class {thisclass} {
  set methods {}
  set mdata [$thisclass clay get class_typemethod/]
  foreach {method info} $mdata {
    set method [string trimright $method :/-]
    if {$method in $methods} continue
    lappend methods $method
    set arglist [dict getnull $info arglist]
    set body    [dict getnull $info body]
    ::oo::objdefine $thisclass method $method $arglist $body
  }
}

###
# New OO Keywords for clay
###
proc ::clay::define::Array {name {values {}}} {
  set class [current_class]
  set name [string trim $name :/]/
  if {![$class clay exists array/ $name]} {
    $class clay set array/ $name {}
  }
  foreach {var val} $values {
    $class clay set array/ $name $var $val
  }
}

###
# topic: 710a93168e4ba7a971d3dbb8a3e7bcbc
###
proc ::clay::define::component {name info} {
  set class [current_class]
  foreach {field value} $info {
    $class clay set component/ [string trim $name :/]/ $field $value
  }
}

###
# topic: 2cfc44a49f067124fda228458f77f177
# title: Specify the constructor for a class
###
proc ::clay::define::constructor {arglist rawbody} {
  set body {
my variable DestroyEvent
set DestroyEvent 0
::clay::object_create [self] [info object class [self]]
# Initialize public variables and options
my InitializePublic
  }
  append body $rawbody
  set class [current_class]
  ::oo::define $class constructor $arglist $body
}

###
# topic: 7a5c7e04989704eef117ff3c9dd88823
# title: Specify the a method for the class object itself, instead of for objects of the class
###
proc ::clay::define::class_method {name arglist body} {
  set class [current_class]
  $class clay set class_typemethod/ [string trim $name :/] [dict create arglist $arglist body $body]
}

proc ::clay::define::clay {args} {
  set class [current_class]
  if {[lindex $args 0] in "cget set branchset"} {
    $class clay {*}$args
  } else {
    $class clay set {*}$args
  }
}

###
# topic: 4cb3696bf06d1e372107795de7fe1545
# title: Specify the destructor for a class
###
proc ::clay::define::destructor rawbody {
  set body {
# Run the destructor once and only once
set self [self]
my variable DestroyEvent
if {$DestroyEvent} return
set DestroyEvent 1
::clay::object_destroy $self
}
  append body $rawbody
  ::oo::define [current_class] destructor $body
}

proc ::clay::define::Dict {name {values {}}} {
  set class [current_class]
  set name [string trim $name :/]/
  if {![$class clay exists dict/ $name]} {
    $class clay set dict/ $name {}
  }
  foreach {var val} $values {
    $class clay set dict/ $name $var $val
  }
}

###
# topic: 615b7c43b863b0d8d1f9107a8d126b21
# title: Specify a variable which should be initialized in the constructor
# description:
#    This keyword can also be expressed:
#    [example {property variable NAME {default DEFAULT}}]
#    [para]
#    Variables registered in the variable property are also initialized
#    (if missing) when the object changes class via the [emph morph] method.
###
proc ::clay::define::Variable {name {default {}}} {
  set class [current_class]
  set name [string trimright $name :/]
  $class clay set variable/ $name $default
  #::oo::define $class variable $name
}

proc ::clay::object_create {objname {class {}}} {
  #if {$::clay::trace>0} {
  #  puts [list $objname CREATE]
  #}
}

proc ::clay::object_rename {object newname} {
  if {$::clay::trace>0} {
    puts [list $object RENAME -> $newname]
  }
}

proc ::clay::object_destroy objname {
  if {$::clay::trace>0} {
    puts [list $objname DESTROY]
  }
  ::cron::object_destroy $objname
}


# clay::object
#
# This class is inherited by all classes that have options.
#
::clay::define ::clay::object {
  Variable clay {}
  Variable claycache {}
  Variable DestroyEvent 0

  ###
  # Instantiate variables and build ensemble methods.
  ###
  method InitializePublic {} {
    next
    my variable clayorder clay claycache
    if {[info exists clay]} {
      set emap [dict getnull $clay method_ensemble/]
    } else {
      set emap {}
    }
    foreach class [lreverse $clayorder] {
      ###
      # Build a compsite map of all ensembles defined by the object's current
      # class as well as all of the classes being mixed in
      ###
      foreach {mensemble einfo} [$class clay get method_ensemble/] {
        set ensemble [string trim $mensemble :/]
        if {$::clay::trace>2} {puts [list Defining $ensemble from $class]}

        foreach {method info} $einfo {
          dict set info source $class
          if {$::clay::trace>2} {puts [list Defining $ensemble -> $method from $class - $info]}
          dict set emap $ensemble $method $info
        }
      }
    }
    foreach {ensemble einfo} $emap {
      #if {[dict exists $einfo _body]} continue
      set body [::clay::ensemble_methodbody $ensemble $einfo]
      if {$::clay::trace>2} {
        set rawbody $body
        set body {puts [list [self] <object> [self method]]}
        append body \n $rawbody
      }
      oo::objdefine [self] method $ensemble {{method default} args} $body
    }
  }
}

Added modules/clay/build/object.tcl.
















































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
oo::define oo::object {

  ###
  # description:
  # The [method clay] method allows an object access
  # to a combination of its own clay data as
  # well as to that of its class
  # ensemble:
  # ancestors {
  #   arglist {}
  #   description {Return the class this object belongs to, all classes mixed into this object, and all ancestors of those classes in search order.}
  # }
  # cget {
  #   arglist {field {mandatory 1 positional 1}}
  #   description {
  # Pull a value from either the object's clay structure or one of its constituent classes that matches the field name.
  # The order of search us:
  # [para] 1. The as a value in local dict variable config
  # [para] 2. The as a value in local dict variable clay
  # [para] 3. As a leaf in any ancestor as a root of the clay tree
  # [para] 4. As a leaf in any ancestor under the const/ branch of the clay tree
  #   }
  # }
  # delegate {
  #   arglist {stub {mandatory 0 positional 1} object {mandatory 0 positional 1}}
  #   description {
  # Introspect or control method delegation. With no arguments, the method will return a
  # key/value list of stubs and objects. With just the [arg stub] argument, the method will
  # return the object (if any) attached to the stub. With a [arg stub] and an [arg object]
  # this command will forward all calls to the method [arg stub] to the [arg object].
  # }
  # }
  # dump { arglist {} description {Return a complete dump of this object's clay data, as well as the data from all constituent classes recursively blended in.}}
  # ensemble_map {arglist {} description {Return a dictionary describing the method ensembles to be assembled for this object}}
  # eval {arglist {script {mandatory 1 positional 1}} description {Evaluated a script in the namespace of this object}}
  # evolve {arglist {} description {Trigger the [method InitializePublic] private method}}
  # exists {arglist {path {mandatory 1 positional 1 repeating 1}} description {Returns 1 if [emph path] exists in either the object's clay data. Values greater than one indicate the element exists in one of the object's constituent classes. A value of zero indicates the path could not be found.}}
  # flush {arglist {} description {Wipe any caches built by the clay implementation}}
  # forward {arglist {method {positional 1 mandatory 1} object {positional 1 mandatory 1}} description {A convenience wrapper for
  # [example {oo::objdefine [self] forward {*}$args}]
  # }
  # }
  # get {arglist {path {mandatory 1 positional 1 repeating 1}}
  #   description {Pull a chunk of data from the clay system. If the last element of [emph path] is a branch (ends in a slash /),
  #   returns a recursive merge of all data from this object and it's constituent classes of the data in that branch.
  #   If the last element is a leaf, search this object for a matching leaf, or search all  constituent classes for a matching
  #   leaf and return the first value found.
  #   If no value is found, returns an empty string.
  # }
  # }
  # leaf {arglist {path {mandatory 1 positional 1 repeating 1}} description {A modified get which is tailored to pull only leaf elements}}
  # merge {arglist {dict {mandatory 1 positional 1 repeating 1}} description {Recursively merge the dictionaries given into the object's local clay storage.}}
  # mixin {arglist {class {mandatory 1 positional 1 repeating 1}} description {
  # Perform [lb]oo::objdefine [lb]self[rb] mixin[rb] on this object, with a few additional rules:
  #   Prior to the call, for any class was previously mixed in, but not in the new result, execute the script registered to mixin/ unmap-script (if given.)
  #   For all new classes, that were not present prior to this call, after the native TclOO mixin is invoked, execute the script registered to mixin/ map-script (if given.)
  #   Fall all classes that are now present and “mixed in”, execute the script registered to mixin/ react-script (if given.)
  # }}
  # mixinmap {
  #   arglist {stub {mandatory 0 positional 1} classes {mandatory 0 positional 1}}
  #   description {With no arguments returns the map of stubs and classes mixed into the current object. When only stub is given,
  #  returns the classes mixed in on that stub. When stub and classlist given, replace the classes currently on that stub with the given
  #  classes and invoke clay mixin on the new matrix of mixed in classes.
  # }
  # }
  # provenance {arglist {path {mandatory 1 positional 1 repeating 1}} description {Return either [const self] if that path exists in the current object, or return the first class (if any) along the clay search path which contains that element.}}
  # replace {arglist {dictionary {mandatory 1 positional 1}} description {Replace the contents of the internal clay storage with the dictionary given.}}
  # source {arglist {filename {mandatory 1 positional 1}} description {Source the given filename within the object's namespace}}
  # set {arglist {path {mandatory 1 positional 1 repeating 1} value {mandatory 1 postional 1}} description {Merge the conents of [const value] with the object's clay storage at [const path].}}
  ###
  method clay {submethod args} {
    my variable clay claycache clayorder config option_canonical
    if {![info exists clay]} {set clay {}}
    if {![info exists claycache]} {set claycache {}}
    if {![info exists config]} {set config {}}
    if {![info exists clayorder] || [llength $clayorder]==0} {
      set clayorder [::clay::ancestors [info object class [self]] {*}[info object mixins [self]]]
    }
    switch $submethod {
      ancestors {
        return $clayorder
      }
      cget {
        # Leaf searches return one data field at a time
        # Search in our local dict
        if {[llength $args]==1} {
          set field [string trim [lindex $args 0] -:/]
          if {[info exists option_canonical($field)]} {
            set field $option_canonical($field)
          }
          if {[dict exists $config $field]} {
            return [dict get $config $field]
          }
        }
        if {[dict exists $clay {*}$args]} {
          return [dict get $clay {*}$args]
        }
        # Search in our local cache
        if {[dict exists $claycache {*}$args]} {
          return [dict get $claycache {*}$args]
        }
        # Search in the in our list of classes for an answer
        foreach class $clayorder {
          if {[$class clay exists {*}$args]} {
            set value [$class clay get {*}$args]
            dict set claycache {*}$args $value
            return $value
          }
          if {[$class clay exists const/ {*}$args]} {
            set value [$class clay get const/ {*}$args]
            dict set claycache {*}$args $value
            return $value
          }
        }
        return {}
      }
      delegate {
        if {![dict exists $clay delegate/ <class>]} {
          dict set clay delegate/ <class> [info object class [self]]
        }
        if {[llength $args]==0} {
          return [dict get $clay delegate/]
        }
        if {[llength $args]==1} {
          set stub <[string trim [lindex $args 0] <>]>
          if {![dict exists $clay delegate/ $stub]} {
            return {}
          }
          return [dict get $clay delegate/ $stub]
        }
        if {([llength $args] % 2)} {
          error "Usage: delegate
    OR
    delegate stub
    OR
    delegate stub OBJECT ?stub OBJECT? ..."
        }
        foreach {stub object} $args {
          set stub <[string trim $stub <>]>
          dict set clay delegate/ $stub $object
          oo::objdefine [self] forward ${stub} $object
          oo::objdefine [self] export ${stub}
        }
      }
      dump {
        # Do a full dump of clay data
        set result $clay
        # Search in the in our list of classes for an answer
        foreach class $clayorder {
          ::clay::dictmerge result [$class clay dump]
        }
        ::clay::dictmerge result $clay
        return $result
      }
      ensemble_map {
        set ensemble [lindex $args 0]
        my variable claycache
        set mensemble [string trim $ensemble :/]/
        if {[dict exists $claycache method_ensemble/ $mensemble]} {
          return [dict get $claycache method_ensemble/ $mensemble]
        }
        set emap [my clay get method_ensemble/ $mensemble]
        dict set claycache method_ensemble/ $mensemble $emap
        return $emap
      }
      eval {
        set script [lindex $args 0]
        set buffer {}
        set thisline {}
        foreach line [split $script \n] {
          append thisline $line
          if {![info complete $thisline]} {
            append thisline \n
            continue
          }
          set thisline [string trim $thisline]
          if {[string index $thisline 0] eq "#"} continue
          if {[string length $thisline]==0} continue
          if {[lindex $thisline 0] eq "my"} {
            # Line already calls out "my", accept verbatim
            append buffer $thisline \n
          } elseif {[string range $thisline 0 2] eq "::"} {
            # Fully qualified commands accepted verbatim
            append buffer $thisline \n
          } elseif {
            append buffer "my $thisline" \n
          }
          set thisline {}
        }
        eval $buffer
      }
      evolve -
      initialize {
        my InitializePublic
      }
      exists {
        # Leaf searches return one data field at a time
        # Search in our local dict
        if {[dict exists $clay {*}$args]} {
          return 1
        }
        # Search in our local cache
        if {[dict exists $claycache {*}$args]} {
          return 2
        }
        set count 2
        # Search in the in our list of classes for an answer
        foreach class $clayorder {
          incr count
          if {[$class clay exists {*}$args]} {
            return $count
          }
        }
        return 0
      }
      flush {
        set claycache {}
        set clayorder [::clay::ancestors [info object class [self]] {*}[info object mixins [self]]]
      }
      forward {
        oo::objdefine [self] forward {*}$args
      }
      getnull -
      get {
        set leaf [expr {[string index [lindex $args end] end] ne "/"}]
        #puts [list [self] clay get {*}$args (leaf: $leaf)]
        if {$leaf} {
          #puts [list EXISTS: (clay) [dict exists $clay {*}$args]]
          if {[dict exists $clay {*}$args]} {
            return [dict get $clay {*}$args]
          }
          # Search in our local cache
          #puts [list EXISTS: (claycache) [dict exists $claycache {*}$args]]
          if {[dict exists $claycache {*}$args]} {
            return [dict get $claycache {*}$args]
          }
          # Search in the in our list of classes for an answer
          foreach class $clayorder {
            if {[$class clay exists {*}$args]} {
              set value [$class clay get {*}$args]
              dict set claycache {*}$args $value
              return $value
            }
          }
        } else {
          set result {}
          # Leaf searches return one data field at a time
          # Search in our local dict

          # Search in the in our list of classes for an answer
          foreach class [lreverse $clayorder] {
            ::clay::dictmerge result [$class clay get {*}$args]
          }
          if {[dict exists $clay {*}$args]} {
            ::clay::dictmerge result [dict get $clay {*}$args]
          }
          return $result
        }
      }
      leaf {
        # Leaf searches return one data field at a time
        # Search in our local dict
        if {[dict exists $clay {*}$args]} {
          return [dict get $clay {*}$args]
        }
        # Search in our local cache
        if {[dict exists $claycache {*}$args]} {
          return [dict get $claycache {*}$args]
        }
        # Search in the in our list of classes for an answer
        foreach class $clayorder {
          if {[$class clay exists {*}$args]} {
            set value [$class clay get {*}$args]
            dict set claycache {*}$args $value
            return $value
          }
        }
      }
      merge {
        foreach arg $args {
          ::clay::dictmerge clay {*}$arg
        }
      }
      mixin {
        ###
        # Mix in the class
        ###
        set prior  [info object mixins [self]]
        set newmixin {}
        foreach item $args {
          lappend newmixin ::[string trimleft $item :]
        }
        set newmap $args
        foreach class $prior {
          if {$class ni $newmixin} {
            set script [$class clay get mixin/ unmap-script]
            if {[string length $script]} {
              if {[catch $script err errdat]} {
                puts stderr "[self] MIXIN ERROR POPPING $class:\n[dict get $errdat -errorinfo]"
              }
            }
          }
        }
        ::oo::objdefine [self] mixin {*}$args
        ###
        # Build a compsite map of all ensembles defined by the object's current
        # class as well as all of the classes being mixed in
        ###
        my InitializePublic
        foreach class $newmixin {
          if {$class ni $prior} {
            set script [$class clay get mixin/ map-script]
            if {[string length $script]} {
              if {[catch $script err errdat]} {
                puts stderr "[self] MIXIN ERROR PUSHING $class:\n[dict get $errdat -errorinfo]"
              }
            }
          }
        }
        foreach class $newmixin {
          set script [$class clay search mixin/ react-script]
          if {[string length $script]} {
            if {[catch $script err errdat]} {
              puts stderr "[self] MIXIN ERROR PEEKING $class:\n[dict get $errdat -errorinfo]"
            }
            break
          }
        }
      }
      mixinmap {
        my variable clay
        if {![dict exists $clay mixin]} {
          dict set clay mixin {}
        }
        if {[llength $args]==0} {
          return [dict get $clay mixin]
        } elseif {[llength $args]==1} {
          return [dict getnull $clay mixin [lindex $args 0]]
        } else {
          foreach {slot classes} $args {
            dict set clay mixin $slot $classes
          }
          set claycache {}
          set classlist {}
          foreach {item class} [dict get $clay mixin] {
            if {$class ne {}} {
              lappend classlist $class
            }
          }
          my clay mixin {*}$classlist
        }
      }
      provenance {
        if {[dict exists $clay {*}$args]} {
          return self
        }
        foreach class $clayorder {
          if {[$class clay exists {*}$args]} {
            return $class
          }
        }
        return {}
      }
      replace {
        set clay [lindex $args 0]
      }
      source {
        source [lindex $args 0]
      }
      set {
        #puts [list [self] clay SET {*}$args]
        set claycache {}
        ::clay::dictmerge clay {*}$args
      }
      default {
        dict $submethod clay {*}$args
      }
    }
  }

  ###
  # Instantiate variables. Called on object creation and during clay mixin.
  ###
  method InitializePublic {} {
    my variable clayorder clay claycache config option_canonical
    set claycache {}
    set clayorder [::clay::ancestors [info object class [self]] {*}[info object mixins [self]]]
    if {![info exists config]} {
      set config {}
    }
    foreach {var value} [my clay get variable/] {
      set var [string trim $var :/]
      if { $var in {clay} } continue
      my variable $var
      if {![info exists $var]} {
        if {$::clay::trace>2} {puts [list initialize variable $var $value]}
        set $var $value
      }
    }
    foreach {var value} [my clay get dict/] {
      set var [string trim $var :/]
      my variable $var
      if {![info exists $var]} {
        set $var {}
      }
      foreach {f v} $value {
        if {![dict exists ${var} $f]} {
          if {$::clay::trace>2} {puts [list initialize dict $var $f $v]}
          dict set ${var} $f $v
        }
      }
    }
    foreach {var value} [my clay get dict/] {
      set var [string trim $var :/]
      foreach {f v} [my clay get $var/] {
        if {![dict exists ${var} $f]} {
          if {$::clay::trace>2} {puts [list initialize dict (from const) $var $f $v]}
          dict set ${var} $f $v
        }
      }
    }
    foreach {var value} [my clay get array/] {
      set var [string trim $var :/]
      if { $var eq {clay} } continue
      my variable $var
      if {![info exists $var]} { array set $var {} }
      foreach {f v} $value {
        if {![array exists ${var}($f)]} {
          if {$::clay::trace>2} {puts [list initialize array $var\($f\) $v]}
          set ${var}($f) $v
        }
      }
    }
    foreach {var value} [my clay get array/] {
      set var [string trim $var :/]
      foreach {f v} [my clay get $var/] {
        if {![array exists ${var}($f)]} {
          if {$::clay::trace>2} {puts [list initialize array (from const) $var\($f\) $v]}
          set ${var}($f) $v
        }
      }
    }
    foreach {field info} [my clay get option/] {
      set field [string trim $field -/:]
      foreach alias [dict getnull $info aliases] {
        set option_canonical($alias) $field
      }
      if {[dict exists $config $field]} continue
      set getcmd [dict getnull $info default-command]
      if {$getcmd ne {}} {
        set value [{*}[string map [list %field% $field %self% [namespace which my]] $getcmd]]
      } else {
        set value [dict getnull $info default]
      }
      dict set config $field $value
      set setcmd [dict getnull $info set-command]
      if {$setcmd ne {}} {
        {*}[string map [list %field% [list $field] %value% [list $value] %self% [namespace which my]] $setcmd]
      }
    }
  }
}

Added modules/clay/build/procs.tcl.










































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
###
# Global utilities
###
if {[info commands ::ladd] eq {}} {
  proc ladd {varname args} {
    upvar 1 $varname var
    if ![info exists var] {
        set var {}
    }
    foreach item $args {
      if {$item in $var} continue
      lappend var $item
    }
    return $var
  }
}

if {[info command ::ldelete] eq {}} {
  proc ::ldelete {varname args} {
    upvar 1 $varname var
    if ![info exists var] {
        return
    }
    foreach item [lsort -unique $args] {
      while {[set i [lsearch $var $item]]>=0} {
        set var [lreplace $var $i $i]
      }
    }
    return $var
  }
}

if {[info command ::lrandom] eq {}} {
  proc ::lrandom list {
    set len [llength $list]
    set idx [expr int(rand()*$len)]
    return [lindex $list $idx]
  }
}

if {[::info commands ::tcl::dict::getnull] eq {}} {
  proc ::tcl::dict::getnull {dictionary args} {
    if {[exists $dictionary {*}$args]} {
      get $dictionary {*}$args
    }
  }
  namespace ensemble configure dict -map [dict replace\
      [namespace ensemble configure dict -map] getnull ::tcl::dict::getnull]
}

proc ::putb {buffername args} {
  upvar 1 $buffername buffer
  switch [llength $args] {
    1 {
      append buffer [lindex $args 0] \n
    }
    2 {
      append buffer [string map {*}$args] \n
    }
    default {
      error "usage: putb buffername ?map? string"
    }
  }
}
namespace eval ::clay {}
set ::clay::trace 0

proc ::clay::ancestors args {
  set result {}
  set queue {}
  foreach class [lreverse $args] {
    lappend queue $class
  }

  # Rig things such that that the top superclasses
  # are evaluated first
  while {[llength $queue]} {
    set tqueue $queue
    set queue {}
    foreach qclass $tqueue {
      foreach aclass [::info class superclasses $qclass] {
        if { $aclass in $result } continue
        if { $aclass in $queue } continue
        lappend queue $aclass
      }
    }
    foreach item $tqueue {
      if { $item ni $result } {
        lappend result $item
      }
    }
  }
  return $result
}

proc ::clay::args_to_dict args {
  if {[llength $args]==1} {
    return [lindex $args 0]
  }
  return $args
}

proc ::clay::args_to_options args {
  set result {}
  foreach {var val} [args_to_dict {*}$args] {
    lappend result [string trim $var -:] $val
  }
  return $result
}

proc ::clay::dictmerge {varname args} {
  upvar 1 $varname result
  if {![info exists result]} {
    set result {}
  }
  switch [llength $args] {
    0 {
      return
    }
    1 {
      set result [_dictmerge $result [lindex $args 0]]
      return $result
    }
    2 {
      lassign $args path value
    }
    default {
      # Merge b into a, and handle nested dicts appropriately
      set value [lindex $args end]
      set path  [lrange $args 0 end-1]
    }
  }
  if {![dict exists $result {*}$path]} {
    dict set result {*}$path $value
    return $result
  }
  if {[string index [lindex $path end] end] ne "/"} {
    dict set result {*}$path $value
    return $result
  }
  ::dict for { k v } $value {
    # Element names that end in "/" are assumed to be branches
    if {[string index $k end] eq "/" && [::dict exists $result {*}$path $k]} {
      # key exists in a and b?  let's see if both values are dicts
      # both are dicts, so merge the dicts
      set dvalue [::dict get $result {*}$path $k]
      if { [is_dict $dvalue] && [is_dict $v] } {
        ::dict set result {*}$path $k [_dictmerge $dvalue $v]
      } else {
        ::dict set result {*}$path $k $v
      }
    } else {
      ::dict set result {*}$path $k $v
    }
  }
  return $result
}

proc ::clay::_dictmerge {a b} {
  ::set result $a
  # Merge b into a, and handle nested dicts appropriately
  ::dict for { k v } $b {
    if {[string index $k end] ne "/"} {
      # Element names that do not end in "/" are assumed to be literals
      # or dict trees we intend to replace wholly
      ::dict set result $k $v
    } elseif { [::dict exists $result $k] } {
      # key exists in a and b?  let's see if both values are dicts
      # both are dicts, so merge the dicts
      if { [is_dict [::dict get $result $k]] && [is_dict $v] } {
        ::dict set result $k [_dictmerge [::dict get $result $k] $v]
      } else {
        ::dict set result $k $v
      }
    } else {
      ::dict set result $k $v
    }
  }
  return $result
}

proc ::clay::dictputb {dict} {
  set result {}
  set level -1
  _dictputb 0 $level result $dict
  return $result
}

proc ::clay::_dictputb {leaf level varname dict} {
  upvar 1 $varname result
  incr level
  foreach {field value} $dict {
    if {[string index $field end] eq "/"} {
      putb result "[string repeat "  " $level]$field \{"
      _dictputb 0 $level result $value
      putb result "[string repeat "  " $level]\}"
    } else {
      putb result "[string repeat "  " $level][list $field $value]"
    }
  }
}

###
# topic: 4969d897a83d91a230a17f166dbcaede
###
proc ::clay::dynamic_arguments {ensemble method arglist args} {
  set idx 0
  set len [llength $args]
  if {$len > [llength $arglist]} {
    ###
    # Catch if the user supplies too many arguments
    ###
    set dargs 0
    if {[lindex $arglist end] ni {args dictargs}} {
      return -code error -level 2 "Usage: $ensemble $method [string trim [dynamic_wrongargs_message $arglist]]"
    }
  }
  foreach argdef $arglist {
    if {$argdef eq "args"} {
      ###
      # Perform args processing in the style of tcl
      ###
      uplevel 1 [list set args [lrange $args $idx end]]
      break
    }
    if {$argdef eq "dictargs"} {
      ###
      # Perform args processing in the style of tcl
      ###
      uplevel 1 [list set args [lrange $args $idx end]]
      ###
      # Perform args processing in the style of clay
      ###
      set dictargs [::clay::args_to_options {*}[lrange $args $idx end]]
      uplevel 1 [list set dictargs $dictargs]
      break
    }
    if {$idx > $len} {
      ###
      # Catch if the user supplies too few arguments
      ###
      if {[llength $argdef]==1} {
        return -code error -level 2 "Usage: $ensemble $method [string trim [dynamic_wrongargs_message $arglist]]"
      } else {
        uplevel 1 [list set [lindex $argdef 0] [lindex $argdef 1]]
      }
    } else {
      uplevel 1 [list set [lindex $argdef 0] [lindex $args $idx]]
    }
    incr idx
  }
}

###
# topic: 53ab28ac5c6ee601fe1fe07b073be88e
###
proc ::clay::dynamic_wrongargs_message {arglist} {
  set result ""
  set dargs 0
  foreach argdef $arglist {
    if {$argdef in {args dictargs}} {
      set dargs 1
      break
    }
    if {[llength $argdef]==1} {
      append result " $argdef"
    } else {
      append result " ?[lindex $argdef 0]?"
    }
  }
  if { $dargs } {
    append result " ?option value?..."
  }
  return $result
}

proc ::clay::is_dict { d } {
  # is it a dict, or can it be treated like one?
  if {[catch {::dict size $d} err]} {
    #::set ::errorInfo {}
    return 0
  }
  return 1
}

proc ::clay::is_null value {
  return [expr {$value in {{} NULL}}]
}

proc ::clay::leaf args {
  set marker [string index [lindex $args end] end]
  set result [path {*}${args}]
  if {$marker eq "/"} {
    return $result
  }
  return [list {*}[lrange $result 0 end-1] [string trim [string trim [lindex $result end]] /]]
}

proc ::clay::path args {
  set result {}
  foreach item $args {
    set item [string trim $item :./]
    foreach subitem [split $item /] {
      lappend result [string trim ${subitem}]/
    }
  }
  return $result
}

proc ::clay::script_path {} {
  set path [file dirname [file join [pwd] [info script]]]
  return $path
}

proc ::clay::NSNormalize qualname {
  if {![string match ::* $qualname]} {
    set qualname ::clay::classes::$qualname
  }
  regsub -all {::+} $qualname "::"
}

proc ::clay::uuid_generate args {
  return [uuid::uuid generate]
}

namespace eval ::clay {
  variable option_class {}
  variable core_classes {::oo::class ::oo::object}
}

Added modules/clay/build/test.tcl.































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
###
# Test script build functions
###

set result {}
putb result {# clay.test - Copyright (c) 2018 Sean Woods
# -------------------------------------------------------------------------

#source [file join \
#	[file dirname [file dirname [file dirname [file join [pwd] [info script]]]]] \
#	compat devtools testutilities.tcl]

source [file join  [file dirname [file dirname [file join [pwd] [info script]]]]  devtools testutilities.tcl]


testsNeedTcl     8.6
testsNeedTcltest 2
testsNeed        TclOO 1

support {
    use uuid/uuid.tcl uuid
    use oodialect/oodialect.tcl oo::dialect

}
testing {
    useLocal clay.tcl clay
}
}

putb result {
set ::clay::trace 0
}

putb result {
# -------------------------------------------------------------------------
# dictmerge Testing
unset -nocomplain foo
test dictmerge-0001 {Invoking dictmerge with empty args on a non existent variable create an empty variable} {
  ::clay::dictmerge foo
  set foo
} {}

unset -nocomplain foo
::clay::dictmerge foo bar/ baz/ bell/ bang
test dictmerge-0002 {For new entries dictmerge is essentially a set} {
  dict get $foo bar/ baz/ bell/
} {bang}

::clay::dictmerge foo bar/ baz/ boom/ bang
test dictmerge-0003 {For entries that do exist a zipper merge is performed} {
  dict get $foo bar/ baz/ bell/
} {bang}
test dictmerge-0004 {For entries that do exist a zipper merge is performed} {
  dict get $foo bar/ baz/ boom/
} {bang}

::clay::dictmerge foo bar/ baz/ bop {color green flavor strawberry}
test dictmerge-0005 {Leaves are replaced even if they look like a dict} {
  dict get $foo bar/ baz/ bop
} {color green flavor strawberry}

::clay::dictmerge foo bar/ baz/ bop {color yellow}
test dictmerge-0006 {Leaves are replaced even if they look like a dict} {
  dict get $foo bar/ baz/ bop
} {color yellow}

::clay::dictmerge foo bar/ baz/ bang/ {color green flavor strawberry}
test dictmerge-0005 {Branches are merged} {
  dict get $foo bar/ baz/ bang/
} {color green flavor strawberry}

::clay::dictmerge foo bar/ baz/ bang/ color yellow
test dictmerge-0006 {Branches are merged}  {
  dict get $foo bar/ baz/ bang/
} {color yellow flavor strawberry}

::clay::dictmerge foo bar/ baz/ bang/ {color blue}
test dictmerge-0007 {Branches are merged}  {
  dict get $foo bar/ baz/ bang/
} {color blue flavor strawberry}

::clay::dictmerge foo {option/ {color {type color} flavor {sense taste}}}
::clay::dictmerge foo {option/ {format {default ascii}}}

test dictmerge-0008 {Whole dicts are merged}  {
  dict get $foo option/ color
} {type color}
test dictmerge-0009 {Whole dicts are merged}  {
  dict get $foo option/ flavor
} {sense taste}
test dictmerge-0010 {Whole dicts are merged}  {
  dict get $foo option/ format
} {default ascii}

###
# Tests for the httpd module
###
test dictmerge-0010 {Test that leaves are merged properly}
set bar {}
::clay::dictmerge bar {
   proxy/ {port 10101 host myhost.localhost}
}
::clay::dictmerge bar {
   mimetxt {Host: localhost
Content_Type: text/plain
Content-Length: 15
}
   http {HTTP_HOST {} CONTENT_LENGTH 15 HOST localhost CONTENT_TYPE text/plain UUID 3a7b4cdc-28d7-49b7-b18d-9d7d18382b9e REMOTE_ADDR 127.0.0.1 REMOTE_HOST 127.0.0.1 REQUEST_METHOD POST REQUEST_URI /echo REQUEST_PATH echo REQUEST_VERSION 1.0 DOCUMENT_ROOT {} QUERY_STRING {} REQUEST_RAW {POST /echo HTTP/1.0} SERVER_PORT 10001 SERVER_NAME 127.0.0.1 SERVER_PROTOCOL HTTP/1.1 SERVER_SOFTWARE {TclHttpd 4.2.0} LOCALHOST 0} UUID 3a7b4cdc-28d7-49b7-b18d-9d7d18382b9e uriinfo {fragment {} port {} path echo scheme http host {} query {} pbare 0 pwd {} user {}}
   mixin {reply ::test::content.echo}
   prefix /echo
   proxy_port 10010
   proxy/ {host localhost}
}

test dictmerge-0011 {Whole dicts are merged}  {
  dict get $bar proxy_port
} {10010}

test dictmerge-0012 {Whole dicts are merged}  {
  dict get $bar http CONTENT_LENGTH
} 15
test dictmerge-0013 {Whole dicts are merged}  {
  dict get $bar proxy/ host
} localhost
test dictmerge-0014 {Whole dicts are merged}  {
  dict get $bar proxy/ port
} 10101
}

putb result {
# -------------------------------------------------------------------------

::oo::dialect::create ::alpha

proc ::alpha::define::is_alpha {} {
  dict set ::testinfo([current_class]) is_alpha 1
}

::alpha::define ::alpha::object {
  is_alpha
}

::oo::dialect::create ::bravo ::alpha

proc ::bravo::define::is_bravo {} {
  dict set ::testinfo([current_class]) is_bravo 1
}

::bravo::define ::bravo::object {
  is_bravo
}

::oo::dialect::create ::charlie ::bravo

proc ::charlie::define::is_charlie {} {
  dict set ::testinfo([current_class]) is_charlie 1
}

::charlie::define ::charlie::object {
  is_charlie
}

::oo::dialect::create ::delta ::charlie

proc ::delta::define::is_delta {} {
  dict set ::testinfo([current_class]) is_delta 1
}

::delta::define ::delta::object {
  is_delta
}

::delta::class create adam {
  is_alpha
  is_bravo
  is_charlie
  is_delta
}

test oodialect-keyword-001 {Testing keyword application} {
  set ::testinfo(::adam)
} {is_alpha 1 is_bravo 1 is_charlie 1 is_delta 1}

test oodialect-keyword-002 {Testing keyword application} {
  set ::testinfo(::alpha::object)
} {is_alpha 1}

test oodialect-keyword-003 {Testing keyword application} {
  set ::testinfo(::bravo::object)
} {is_bravo 1}

test oodialect-keyword-004 {Testing keyword application} {
  set ::testinfo(::charlie::object)
} {is_charlie 1}

test oodialect-keyword-005 {Testing keyword application} {
  set ::testinfo(::delta::object)
} {is_delta 1}

###
# Declare an object from a namespace
###
namespace eval ::test1 {
  ::alpha::class create a {
    aliases A
    is_alpha
  }
  ::alpha::define b {
    aliases B BEE
    is_alpha
  }
  ::alpha::class create ::c {
    aliases C
    is_alpha
  }
  ::alpha::define ::d {
    aliases D
    is_alpha
  }
}

test oodialect-naming-001 {Testing keyword application} {
  set ::testinfo(::test1::a)
} {is_alpha 1}

test oodialect-naming-002 {Testing keyword application} {
  set ::testinfo(::test1::b)
} {is_alpha 1}

test oodialect-naming-003 {Testing keyword application} {
  set ::testinfo(::c)
} {is_alpha 1}

test oodialect-naming-004 {Testing keyword application} {
  set ::testinfo(::d)
} {is_alpha 1}

test oodialect-aliasing-001 {Testing keyword application} {
namespace eval ::test1 {
    ::alpha::define e {
       superclass A
    }
}
} ::test1::e

test oodialect-aliasing-002 {Testing keyword application} {
namespace eval ::test1 {
    ::bravo::define f {
       superclass A
    }
}
} ::test1::f


test oodialect-aliasing-003 {Testing aliase method on class} {
  ::test1::a aliases
} {::test1::A}


test oodialect-ancestry-003 {Testing heritage} {
  ::clay::ancestors ::test1::f
} {::test1::f ::test1::a ::bravo::object ::alpha::object ::oo::object}

test oodialect-ancestry-004 {Testing heritage} {
  ::clay::ancestors ::alpha::object
} {::alpha::object ::oo::object}

test oodialect-ancestry-005 {Testing heritage} {
  ::clay::ancestors ::delta::object
} {::delta::object ::charlie::object ::bravo::object ::alpha::object ::oo::object}

# -------------------------------------------------------------------------
# clay submodule testing
# -------------------------------------------------------------------------
# Test canonical path building
set path {const/ foo/ bar/ baz/}
}
set testnum 0
foreach {pattern} {
  {const foo bar baz}
  {const/ foo/ bar/ baz}
  {const/foo/bar/baz}
  {const/foo bar/baz}
  {const/foo/bar baz}
  {const foo/bar/baz}
  {const foo bar/baz}
  {const/foo bar baz}
} {
  putb result [list %pattern% $pattern %testnum% [format %04d [incr testnum]]] {
test oo-clay-path-%testnum% "Test path: %pattern%" {
  ::clay::path %pattern%
} $path
}
}
putb result {set path {const/ foo/ bar/ baz/ bing}}
set testnum 0
foreach {pattern} {
  {const foo bar baz bing}
  {const/ foo/ bar/ baz/ bing}
  {const/foo/bar/baz/bing}
  {const/foo bar/baz/bing:}
  {const/foo/bar baz bing}
  {const/foo/bar baz bing:}
  {const foo/bar/baz/bing}
  {const foo bar/baz/bing}
  {const/foo bar baz bing}
} {
  putb result [list %pattern% $pattern %testnum% [format %04d [incr testnum]]] {
test oo-clay-leaf-%testnum% "Test leaf: %pattern%" {
  ::clay::leaf %pattern%
} $path
}
}

putb result {namespace eval ::foo {}}

set class-a ::foo::classa
set commands-a {
  clay set const color  blue
  clay set const/flavor strawberry
  clay set {const/ sound} zoink
  clay set info/ {
    animal no
    building no
    subelement {pedantic yes}
  }
}
set claydict-a {
  const/ {color blue flavor strawberry sound zoink}
  info/  {
    animal no
    building no
    subelement {pedantic yes}
  }
}

putb result [list %class% ${class-a} %commands% ${commands-a}] {
clay::define %class% {
%commands%
}
}

set testnum 0
foreach {top children} ${claydict-a} {
  foreach {child value} $children {
    set map {}
    dict set map %class% ${class-a}
    dict set map %top% $top
    dict set map %child% $child
    dict set map %value% $value
    dict set map %testnum% [format %04d [incr testnum]]
    putb result $map {
test oo-class-clay-method-%testnum% "Test %class% %top% %child% exists" {
  %class% clay exists %top% %child%
} 1
}
    dict set map %test% [format %04d [incr testnum]]
    putb result $map {
test oo-class-clay-method-%testnum% "Test %class% %top% %child% value" {
  %class% clay get %top% %child%
} {%value%}
}
  }
}


set class-b ::foo::classb
set claydict-b {
  const/ {color black flavor vanilla feeling dread}
  info/  {subelement {spoon yes}}
}
set commands-b {}
foreach {top children} ${claydict-b} {
  foreach {child value} $children {
    putb commands-b "  [list clay set $top $child $value]"
  }
}
putb result [list %class% ${class-b} %commands% ${commands-b}] {
clay::define %class% {
%commands%
}
}

foreach {top children} ${claydict-b} {
  foreach {child value} $children {
    set map {}
    dict set map %class% ${class-b}
    dict set map %top% $top
    dict set map %child% $child
    dict set map %value% $value
    dict set map %testnum% [format %04d [incr testnum]]
    putb result $map {
test oo-class-clay-method-%testnum% "Test %class% %top% %child% exists" {
  %class% clay exists %top% %child%
} 1
}
    dict set map %test% [format %04d [incr testnum]]
    putb result $map {
test oo-class-clay-method-%testnum% "Test %class% %top% %child% value" {
  %class% clay get %top% %child%
} {%value%}
}
  }
}

set commands-c {superclass ::foo::classb ::foo::classa}
set class-c ::foo::class.ab
putb result [list %class% ${class-c} %commands% ${commands-c}] {
clay::define %class% {
%commands%
}
}
set commands-d {superclass ::foo::classa ::foo::classb}
set class-d ::foo::class.ba
putb result [list %class% ${class-d} %commands% ${commands-d}] {
clay::define %class% {
%commands%
}
}

###
# Tests for objects
###

putb result {# -------------------------------------------------------------------------
# OBJECT of ::foo::classa
set OBJECTA [::foo::classa new]

###
# Test object degation
###
proc ::foo::fakeobject {a b} {
  return [expr {$a + $b}]
}

::clay::object create TEST
TEST clay delegate funct ::foo::fakeobject
test oo-object-delegate-001 {Test object delegation} {
  ::TEST clay delegate
} {<class> ::clay::object <funct> ::foo::fakeobject}

test oo-object-delegate-002 {Test object delegation} {
  ::TEST clay delegate funct
} {::foo::fakeobject}

test oo-object-delegate-002a {Test object delegation} {
  ::TEST clay delegate <funct>
} {::foo::fakeobject}

test oo-object-delegate-003 {Test object delegation} {
  ::TEST <funct> 1 1
} {2}
test oo-object-delegate-004 {Test object delegation} {
  ::TEST <funct> 10 -7
} {3}

# Replace the function out from under
proc ::foo::fakeobject {a b} {
  return [expr {$a * $b}]
}
test oo-object-delegate-005 {Test object delegation} {
  ::TEST <funct> 10 -7
} {-70}

# Object with ::foo::classa mixed in
set MIXINA  [::oo::object new]
oo::objdefine $MIXINA mixin ::foo::classa
}
set matrix ${claydict-a}
set testnum 0
foreach {top children} $matrix {
  foreach {child value} $children {
    set map {}
    dict set map %object1% OBJECTA
    dict set map %object2% MIXINA

    dict set map %top% $top
    dict set map %child% $child
    dict set map %value% $value
    dict set map %testnum% [format %04d [incr testnum]]
    putb result $map {
test oo-object-clay-method-native-%testnum% {Test native object gets the property} {
  $%object1% clay get %top% %child%
} {%value%}
test oo-object-clay-method-mixin-%testnum% {Test mixin object gets the property} {
  $%object2% clay get %top% %child%
} {%value%}
}
  }
}

putb result {# -------------------------------------------------------------------------
# OBJECT of ::foo::classb
set OBJECTB [::foo::classb new]
# Object with ::foo::classb mixed in
set MIXINB  [::oo::object new]
oo::objdefine $MIXINB mixin ::foo::classb
}
set matrix ${claydict-b}
#set testnum 0
foreach {top children} $matrix {
  foreach {child value} $children {
    set map {}
    dict set map %object1% OBJECTB
    dict set map %object2% MIXINB

    dict set map %top% $top
    dict set map %child% $child
    dict set map %value% $value
    dict set map %testnum% [format %04d [incr testnum]]
    putb result $map {
test oo-object-clay-method-native-%testnum% {Test native object gets the property} {
  $%object1% clay get %top% %child%
} {%value%}
test oo-object-clay-method-mixin-%testnum% {Test mixin object gets the property} {
  $%object2% clay get %top% %child%
} {%value%}
}
  }
}

putb result {# -------------------------------------------------------------------------
# OBJECT descended from ::foo::classa ::foo::classb
set OBJECTAB [::foo::class.ab new]
# Object where classes were mixed in ::foo::classa ::foo::classb
set MIXINAB  [::oo::object new]
oo::objdefine $MIXINAB mixin ::foo::classa ::foo::classb
}
set matrix ${claydict-b}
foreach {top children} ${claydict-a} {
  foreach {child value} $children {
    if {![dict exists $matrix $top $child]} {
      dict set matrix $top $child $value
    }
  }
}
#set testnum 0
foreach {top children} $matrix {
  foreach {child value} $children {
    set map {}
    dict set map %object1% OBJECTAB
    dict set map %object2% MIXINAB

    dict set map %top% $top
    dict set map %child% $child
    dict set map %value% $value
    dict set map %testnum% [format %04d [incr testnum]]
    putb result $map {
test oo-object-clay-method-native-%testnum% {Test native object gets the property} {
  $%object1% clay get %top% %child%
} {%value%}
test oo-object-clay-method-mixin-%testnum% {Test mixin object gets the property} {
  $%object2% clay get %top% %child%
} {%value%}
}
  }
}

putb result {# -------------------------------------------------------------------------
# OBJECT descended from ::foo::classb ::foo::classa
set OBJECTBA [::foo::class.ba new]
# Object where classes were mixed in ::foo::classb ::foo::classa
set MIXINBA  [::oo::object new]
oo::objdefine $MIXINBA mixin ::foo::classb ::foo::classa
}
set matrix ${claydict-a}
foreach {top children} ${claydict-b} {
  foreach {child value} $children {
    if {![dict exists $matrix $top $child]} {
      dict set matrix $top $child $value
    }
  }
}
#set testnum 0
foreach {top children} $matrix {
  foreach {child value} $children {
    set map {}
    dict set map %object1% OBJECTBA
    dict set map %object2% MIXINBA

    dict set map %top% $top
    dict set map %child% $child
    dict set map %value% $value
    dict set map %testnum% [format %04d [incr testnum]]
    putb result $map {
test oo-object-clay-method-native-%testnum% {Test native object gets the property} {
  $%object1% clay get %top% %child%
} {%value%}
test oo-object-clay-method-mixin-%testnum% {Test mixin object gets the property} {
  $%object2% clay get %top% %child%
} {%value%}
}
  }
}

putb resut {
###
# Test local setting if clay data in an object
###
set OBJECT [::foo::classa new]
test oo-object-clay-method-local-0001 {Test native object gets the property} {
  $OBJECT clay get const/ color
} {blue}
test oo-object-clay-method-local-0002 {Test that local settings override the inherited properties} {
  $OBJECT clay set const/ color black
  $OBJECT clay set const/
} {black}

test oo-object-clay-method-local-0003 {Test native object gets an empty property} {
  $OBJECT clay get color
} {}
test oo-object-clay-method-local-0004 {Test that local settings override the empty property} {
  $OBJECT clay set color orange
  $OBJECT clay get color
} {orange}

}

putb result {
###
# put a do-nothing constructor on the books
###
::clay::define ::clay::object {
  constructor args {}
}

oo::objdefine ::clay::object method foo args { return bar }

test clay-core-method-0001 {Test that adding methods to the core ::clay::object class works} {
  ::clay::object foo
} {bar}

namespace eval ::TEST {}
::clay::define ::TEST::myclass {
  clay color red
  clay flavor strawberry

}

###
# Test adding a clay property
###
test clay-class-clay-0001 {Test that a clay statement is recorded in the object of the class} {
  ::TEST::myclass clay get color
} red
test clay-class-clay-0002 {Test that a clay statement is recorded in the object of the class} {
  ::TEST::myclass clay get flavor
} strawberry

###
# Test that objects of the class get the same properties
###
set OBJ [::clay::object new {}]
set OBJ2 [::TEST::myclass new {}]

test clay-object-clay-a-0001 {Test that objects not thee class do not get properties} {
  $OBJ clay get color
} {}
test clay-object-clay-a-0002 {Test that objects not thee class do not get properties} {
  $OBJ clay get flavor
} {}
test clay-object-clay-a-0003 {Test that objects of the class get properties} {
  $OBJ2 clay get color
} red
test clay-object-clay-a-0004 {Test that objects of the class get properties} {
  $OBJ2 clay get flavor
} strawberry

test clay-object-clay-a-0005 {Test the clay ancestors function} {
  $OBJ clay ancestors
} {::clay::object ::oo::object}
test clay-object-clay-a-0006 {Test the clay ancestors function} {
  $OBJ2 clay ancestors
} {::TEST::myclass ::clay::object ::oo::object}
test clay-object-clay-a-0007 {Test the clay provenance  function} {
  $OBJ2 clay provenance  flavor
} ::TEST::myclass

###
# Test that object local setting override the class
###
test clay-object-clay-a-0008 {Test that object local setting override the class} {
  $OBJ2 clay set color purple
  $OBJ2 clay get color
} purple
test clay-object-clay-a-0009 {Test that object local setting override the class} {
  $OBJ2 clay provenance  color
} self

::clay::define ::TEST::myclasse {
  superclass ::TEST::myclass

  clay color blue

  method do args {
    return "I did $args"
  }

  Ensemble which::color {} {
    return [my clay get color]
  }
}

###
# Test clay information is passed town to subclasses
###
test clay-class-clay-0003 {Test that a clay statement is recorded in the object of the class} {
  ::TEST::myclasse clay get color
} blue
test clay-class-clay-0004 {Test that clay statements from the ancestors of this class are not present (we handle them seperately in objects)} {
  ::TEST::myclasse clay get flavor
} {strawberry}


###
# Test that properties reach objects
###
set OBJ3 [::TEST::myclasse new {}]
test clay-object-clay-b-0001 {Test that objects of the class get properties} {
  $OBJ3 clay get color
} blue
test clay-object-clay-b-0002 {Test the clay provenance  function} {
  $OBJ3 clay provenance  color
} ::TEST::myclasse
test clay-object-clay-b-0003 {Test that objects of the class get properties} {
  $OBJ3 clay get flavor
} strawberry
test clay-object-clay-b-0004 {Test the clay provenance  function} {
  $OBJ3 clay provenance  flavor
} ::TEST::myclass
test clay-object-clay-b-0005 {Test the clay provenance  function} {
  $OBJ3 clay ancestors
} {::TEST::myclasse ::TEST::myclass ::clay::object ::oo::object}

###
# Test defining a standard method
###
test clay-object-method-0001 {Test and standard method} {
  $OBJ3 do this really cool thing
} {I did this really cool thing}

test clay-object-method-0003 {Test an ensemble} {
  $OBJ3 which color
} blue
# Test setting properties
test clay-object-method-0004 {Test an ensemble} {
  $OBJ3 clay set color black
  $OBJ3 which color
} black

###
# Test that if you try to replace a global command you get an error
###
test clay-nspace-0001 {Test that if you try to replace a global command you get an error} -body {
::clay::define open {
  method bar {} { return foo }

}
}  -returnCodes {error} -result "::open does not refer to an object"

::clay::define fubar {
  method bar {} { return foo }
}
test clay-nspace-0002 {Test a non qualified class ends up in the current namespace} {
  info commands ::fubar
} {::fubar}

namespace eval ::cluster {
::clay::define fubar {
  method bar {} { return foo }
}

::clay::define ::clay::pot {
  method bar {} { return foo }
}

}
test clay-nspace-0003 {Test a non qualified class ends up in the current namespace} {
  info commands ::cluster::fubar
} {::cluster::fubar}
test clay-nspace-0003 {Test a fully qualified class ends up in the proper namespace} {
  info commands ::clay::pot
} {::clay::pot}

#set ::clay::trace 3

###
# Mixin tests
###

###
# Define a core class
###
::clay::define ::TEST::thing {

  method do args {
    return "I did $args"
  }
}


::clay::define ::TEST::vegetable {

  clay color unknown
  clay flavor unknown

  Ensemble which::flavor {} {
    return [my clay get flavor]
  }
  Ensemble which::color {} {
    return [my clay get color]
  }
}

::clay::define ::TEST::animal {

  clay color unknown
  clay sound unknown

  Ensemble which::sound {} {
    return [my clay get sound]
  }
  Ensemble which::color {} {
    return [my clay get color]
  }
}

::clay::define ::TEST::species.cat {
  superclass ::TEST::animal
  clay sound meow

}

::clay::define ::TEST::coloring.calico {
  clay color calico

}

::clay::define ::TEST::condition.dark {
  Ensemble which::color {} {
    return grey
  }
}

::clay::define ::TEST::mood.happy {
  Ensemble which::sound {} {
    return purr
  }
}
test clay-object-0001 {Test than an object is created when clay::define is invoked} {
  info commands ::TEST::mood.happy
} ::TEST::mood.happy

set OBJ [::TEST::thing new]
test clay-mixin-a-0001 {Test that prior to a mixin an ensemble doesn't exist} -body {
  $OBJ which color
} -returnCodes error -result {unknown method "which": must be clay, destroy or do}

test clay-mixin-a-0002 {Test and standard method from an ancestor} {
  $OBJ do this really cool thing
} {I did this really cool thing}

$OBJ clay mixinmap species ::TEST::animal
test clay-mixin-b-0001 {Test that an ensemble is created during a mixin} {
  $OBJ which color
} {unknown}

test clay-mixin-b-0002 {Test that an ensemble is created during a mixin} {
  $OBJ which sound
} {unknown}
test clay-mixin-b-0003 {Test that an ensemble is created during a mixin} \
  -body {$OBJ which flavor} -returnCodes {error} \
  -result {unknown method which flavor. Valid: color sound}
test clay-mixin-b-0004 {Test that mixins resolve in the correct order} {
  $OBJ clay ancestors
} {::TEST::animal ::TEST::thing ::clay::object ::oo::object}

###
# Replacing a mixin replaces the behaviors
###
$OBJ clay mixinmap species ::TEST::vegetable
test clay-mixin-c-0001 {Test that an ensemble is created during a mixin} {
  $OBJ which color
} {unknown}
test clay-mixin-c-0002 {Test that an ensemble is created during a mixin} \
  -body {$OBJ which sound} \
  -returnCodes {error} \
  -result {unknown method which sound. Valid: color flavor}
test clay-mixin-c-0003 {Test that an ensemble is created during a mixin} {
  $OBJ which flavor
} {unknown}
test clay-mixin-c-0004 {Test that mixins resolve in the correct order} {
  $OBJ clay ancestors
} {::TEST::vegetable ::TEST::thing ::clay::object ::oo::object}

###
# Replacing a mixin
$OBJ clay mixinmap species ::TEST::species.cat
test clay-mixin-e-0001 {Test that an ensemble is created during a mixin} {
  $OBJ which color
} {unknown}
test clay-mixin-e-0002 {Test that an ensemble is created during a mixin} {
  $OBJ which sound
} {meow}
test clay-mixin-e-0003 {Test that an ensemble is created during a mixin} \
  -body {$OBJ which flavor} -returnCodes {error} \
  -result {unknown method which flavor. Valid: color sound}
test clay-mixin-e-0004 {Test that clay data follows the rules of inheritence and order of mixin} {
  $OBJ clay ancestors
} {::TEST::species.cat ::TEST::thing ::TEST::animal ::clay::object ::oo::object}

$OBJ clay mixinmap coloring ::TEST::coloring.calico
test clay-mixin-f-0001 {Test that an ensemble is created during a mixin} {
  $OBJ which color
} {calico}
test clay-mixin-f-0002 {Test that an ensemble is created during a mixin} {
  $OBJ which sound
} {meow}
test clay-mixin-f-0003 {Test that an ensemble is created during a mixin} \
  -body {$OBJ which flavor} -returnCodes {error} \
  -result {unknown method which flavor. Valid: color sound}
test clay-mixin-f-0004 {Test that clay data follows the rules of inheritence and order of mixin} {
  $OBJ clay ancestors
} {::TEST::coloring.calico ::TEST::species.cat ::TEST::thing ::clay::object ::TEST::animal ::oo::object}

test clay-mixin-f-0005 {Test that clay data from a mixin works} {
  $OBJ clay provenance  color
} {::TEST::coloring.calico}

###
# Test variable initialization
###
::clay::define ::TEST::has_var {
  Variable my_variable 10

  method get_my_variable {} {
    my variable my_variable
    return $my_variable
  }
}

set OBJ [::TEST::has_var new]
test clay-class-variable-0001 {Test that the parser injected the right value in the right place for clay to catch it} {
  $OBJ clay get variable/ my_variable
} {10}

test clay-class-variable-0002 {Test that variables declared in the class definition are initialized} {
  $OBJ get_my_variable
} 10

###
# Test array initialization
###
::clay::define ::TEST::has_array {
  Array my_array {timeout 10}

  method get_my_array {field} {
    my variable my_array
    return $my_array($field)
  }
}

set OBJ [::TEST::has_array new]
test clay-class-array-0001 {Test that the parser injected the right value in the right place for clay to catch it} {
  $OBJ clay get array/
} {my_array/ {timeout 10}}

test clay-class-arrau-0002 {Test that variables declared in the class definition are initialized} {
  $OBJ get_my_array timeout
} 10

###
# Test dict initialization
###
::clay::define ::TEST::has_dict {
  Dict my_dict {timeout 10}

  method get_my_dict {args} {
    my variable my_dict
    return [dict get $my_dict {*}$args]
  }
}

set OBJ [::TEST::has_dict new]
test clay-class-dict-0001 {Test that the parser injected the right value in the right place for clay to catch it} {
  $OBJ clay get dict/
} {my_dict/ {timeout 10}}

test clay-class-dict-0002 {Test that variables declared in the class definition are initialized} {
  $OBJ get_my_dict timeout
} 10

###
# Test object delegation
###
::clay::define ::TEST::organelle {
  method add args {
    set total 0
    foreach item $args {
      set total [expr {$total+$item}]
    }
    return $total
  }
}
::clay::define ::TEST::master {
  constructor {} {
    set mysub [namespace current]::sub
    ::TEST::organelle create $mysub
    my clay delegate sub $mysub
  }
}

set OBJ [::TEST::master new]
###
# Test that delegation is working
###
test clay-delegation-0001 {Test an array driven ensemble} {
  $OBJ <sub> add 5 5
} 10


###
# Test the Ensemble keyword
###
::clay::define ::TEST::with_ensemble {

  Ensemble myensemble {pattern args} {
    set ensemble [self method]
    set emap [my clay ensemble_map $ensemble]
    set mlist [dict keys $emap [string tolower $pattern]]
    if {[llength $mlist] != 1} {
      error "Couldn't figure out what to do with $pattern"
    }
    set method [lindex $mlist 0]
    set arglist [dict get $emap $method arglist]
    set body    [dict get $emap $method body]
    if {$arglist ni {args {}}} {
      ::clay::dynamic_arguments $ensemble $method [list $arglist] {*}$args
    }
    eval $body
  }

  Ensemble myensemble::go args {
    return 1
  }
}

::clay::define ::TEST::with_ensemble.dance {
  Ensemble myensemble::dance args {
    return 1
  }
}
::clay::define ::TEST::with_ensemble.cannot_dance {
  Ensemble myensemble::dance args {
    return 0
  }
}

set OBJA [::clay::object new]
set OBJB [::clay::object new]

$OBJA clay mixinmap \
  core ::TEST::with_ensemble \
  friends ::TEST::with_ensemble.dance

$OBJB clay mixinmap \
  core ::TEST::with_ensemble \
  friends ::TEST::with_ensemble.cannot_dance
}

set testnum 0

set matrix {
  go {
    OBJA 1
    OBJB 1
  }
  dance {
    OBJA 1
    OBJB 0
  }
}
foreach {action output} $matrix {
  putb result "# Test $action"
  foreach {object value} $output {
    set map [dict create %object% $object %action% $action %value% $value]
    dict set map %testnum% [format %04d [incr testnum]]
    putb result $map {test clay-dynamic-ensemble-%testnum% {Test ensemble with static method} {
  $%object% myensemble %action%
} {%value%}}
  }
}

putb result {

###
# Class method testing
###

clay::class create WidgetClass {
  class_method working {} {
    return {Works}
  }

  class_method unknown args {
    set tkpath [lindex $args 0]
    if {[string index $tkpath 0] eq "."} {
      set obj [my new $tkpath {*}[lrange $args 1 end]]
      $obj tkalias $tkpath
      return $tkpath
    }
    next {*}$args
  }

  constructor {TkPath args} {
    my variable hull
    set hull $TkPath
    my clay delegate hull $TkPath
  }

  method tkalias tkname {
    set oldname $tkname
    my variable tkalias
    set tkalias $tkname
    set self [self]
    set hullwidget [::info object namespace $self]::tkwidget
    my clay delegate tkwidget $hullwidget
    #rename ::$tkalias $hullwidget
    my clay delegate hullwidget $hullwidget
    #::tool::object_rename [self] ::$tkalias
    rename [self] ::$tkalias
    #my Hull_Bind $tkname
    return $hullwidget
  }
}

test tool-class-method-000 {Test that class methods actually work...} {
  WidgetClass working
} {Works}

test tool-class-method-001 {Test Tk style creator} {
  WidgetClass .foo
  .foo clay delegate hull
} {.foo}

::clay::define WidgetNewClass {
  superclass WidgetClass
}

test tool-class-method-002 {Test Tk style creator inherited by morph} {
  WidgetNewClass .bar
  .bar clay delegate hull
} {.bar}



###
# Test ensemble inheritence
###
clay::define NestedClassA {
  Ensemble do::family {} {
    return NestedClassA
  }
  Ensemble do::something {} {
    return A
  }
  Ensemble do::whop {} {
    return A
  }
}
clay::define NestedClassB {
  superclass NestedClassA
  Ensemble do::family {} {
    set r [next family]
    lappend r NestedClassB
    return $r
  }
  Ensemble do::whop {} {
    return B
  }
}
clay::define NestedClassC {
  superclass NestedClassB

  Ensemble do::somethingelse {} {
    return C
  }
}
clay::define NestedClassD {
  superclass NestedClassB

  Ensemble do::somethingelse {} {
    return D
  }
}

clay::define NestedClassE {
  superclass NestedClassD NestedClassC
}

clay::define NestedClassF {
  superclass NestedClassC NestedClassD
}

NestedClassC create NestedObjectC

###
# These tests no longer work because method ensembles are now dynamically
# generated by object, that are not attached to the class anymore
#
####
#test tool-ensemble-001 {Test that an ensemble can access [next] even if no object of the ancestor class have been instantiated} {
#  NestedObjectC do family
#} {::NestedClassA ::NestedClassB ::NestedClassC}

test tool-ensemble-002 {Test that a later ensemble definition trumps a more primitive one} {
  NestedObjectC do whop
} {B}
test tool-ensemble-003 {Test that an ensemble definitions in an ancestor carry over} {
  NestedObjectC do something
} {A}

NestedClassE create NestedObjectE
NestedClassF create NestedObjectF


test tool-ensemble-004 {Test that ensembles follow the same rules for inheritance as methods} {
  NestedObjectE do somethingelse
} {D}

test tool-ensemble-005 {Test that ensembles follow the same rules for inheritance as methods} {
  NestedObjectF do somethingelse
} {C}

###
# Set of tests to exercise the mixinmap system
###
clay::define MixinMainClass {
  Variable mainvar unchanged

  Ensemble test::which {} {
    my variable mainvar
    return $mainvar
  }

  Ensemble test::main args {
    puts [list this is main $method $args]
  }

}

set mixoutscript {my test untool $class}
set mixinscript {my test tool $class}
clay::define MixinTool {
  Variable toolvar unchanged.mixin
  clay set mixin/ unmap-script $mixoutscript
  clay set mixin/ map-script $mixinscript
  clay set mixin/ name {Generic Tool}

  Ensemble test::untool class {
    my variable toolvar mainvar
    set mainvar {}
    set toolvar {}
  }

  Ensemble test::tool class {
    my variable toolvar mainvar
    set mainvar [$class clay get mixin/ name]
    set toolvar [$class clay get mixin/ name]
  }
}

clay::define MixinToolA {
  superclass MixinTool

  clay set mixin/ name {Tool A}
}

clay::define MixinToolB {
  superclass MixinTool

  clay set mixin/ name {Tool B}

  method test_newfunc {} {
    return "B"
  }
}

test tool-mixinspec-001 {Test application of mixin specs} {
  MixinTool clay get mixin/ map-script
} $mixinscript

test tool-mixinspec-002 {Test application of mixin specs} {
  MixinToolA clay get mixin/ map-script
} $mixinscript

test tool-mixinspec-003 {Test application of mixin specs} {
  MixinToolB clay get mixin/ map-script
} $mixinscript


MixinMainClass create mixintest

test tool-mixinmap-001 {Test object prior to mixins} {
  mixintest test which
} {unchanged}

mixintest clay mixinmap tool MixinToolA
test tool-mixinmap-002 {Test mixin map script ran} {
  mixintest test which
} {Tool A}

mixintest clay mixinmap tool MixinToolB

test tool-mixinmap-003 {Test mixin map script ran} {
  mixintest test which
} {Tool B}

test tool-mixinmap-003 {Test mixin map script ran} {
  mixintest test_newfunc
} {B}

mixintest clay mixinmap tool {}
test tool-mixinmap-004 {Test object prior to mixins} {
  mixintest test which
} {}
}

###
# TESTS NEEDED:
# destructor
###

putb result {
testsuiteCleanup

# Local variables:
# mode: tcl
# indent-tabs-mode: nil
# End:
}
return $result

Added modules/clay/clay.man.




















































































































































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
[vset VERSION 0.3]
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin clay n [vset VERSION]]
[keywords oo]
[copyright {2018 Sean Woods <[email protected]>}]
[moddesc   {Clay Framework}]
[titledesc {A minimalist framework for large scale OO Projects}]
[category  {Programming tools}]
[keywords TclOO]
[require Tcl 8.6]
[require uuid]
[require oo::dialect]
[description]
Clay introduces a method ensemble to both [class oo::class] and [class oo::object] called
clay. This ensemble handles all of the high level interactions within the framework.
Clay stores structured data. Clan manages method delegation. Clay has facilities to
manage the complex interactions that come about with mixins.
[para]
The central concept is that inside of every object and class
(which are actually objects too) is a dict called clay. What is stored in that dict is
left to the imagination. But because this dict is exposed via a public method, we can
share structured data between object, classes, and mixins.
[para]
[subsection {Structured Data}]
Clay uses a standardized set of method interactions and introspection that TclOO already provides to perform on-the-fly searches. On-the-fly searches mean that the data is never stale, and we avoid many of the sorts of collisions that would arise when objects start mixing in other classes during operation.
[para]
The [method clay] methods for both classes and objects have a get and a set method. For objects, get will search through the local clay dict. If the requested leaf is not found, or the query is for a branch, the system will then begin to poll the clay methods of all of the class that implements the object, all of that classes’ ancestors, as well as all of the classes that have been mixed into this object, and all of their ancestors.
[para]
Intended branches on a tree end with a directory slash (/). Intended leaves are left unadorned. This is a guide for the tool that builds the search
results to know what parts of a dict are intended to be branches and which are intended to be leaves.
For simple cases, branch marking can be ignored:
[example {
::oo::class create ::foo { }
::foo clay set property/ color blue
::foo clay set property/ shape round

set A [::foo new]
$A clay get property/
{color blue shape round}

$A clay set property/ shape square
$A clay get property/
{color blue shape square}
}]
[para]
But when you start storing blocks of text, guessing what field is a dict and what isn’t gets messy:
[example {
::foo clay set description {A generic thing of designated color and shape}

$A clay get description
{A generic thing of designated color and shape}

Without a convention for discerning branches for leaves what should have been a value can be accidentally parsed as a dictionary, and merged with all of the other values that were never intended to be merge. Here is an example of it all going wrong:
::oo::class create ::foo { }
# Add description as a leaf
::foo clay set description \
  {A generic thing of designated color and shape}
# Add description as a branch
::foo clay set description/ \
  {A generic thing of designated color and shape}

::oo::class create ::bar {
  superclass foo
}
# Add description as a leaf
::bar clay set description \
  {A drinking establishment of designated color and shape and size}
# Add description as a branch
::bar clay set description/ \
  {A drinking establishment of designated color and shape and size}

set B [::bar new]
# As a leaf we get the value verbatim from he nearest ancestor
$B clay get description
  {A drinking establishment of designated color and shape and size}
# As a branch we get a recursive merge
$B clay get description/
{A drinking establishment of designated color and size thing of}
}]
[subsection {Clay Dialect}]
Clay is built using the oo::dialect module from Tcllib. oo::dialect allows you to either add keywords directly to clay, or to create your own
metaclass and keyword set using Clay as a foundation. For details on the keywords and what they do, consult the functions in the ::clay::define namespace.
[subsection {Method Delegation}]
Method Delegation
It is sometimes useful to have an external object that can be invoked as if it were a method of the object. Clay provides a delegate ensemble method to perform that delegation, as well as introspect which methods are delegated in that manner. All delegated methods are marked with html-like tag markings (< >) around them.
[example {
::clay::define counter {
  Variable counter 0
  method incr {{howmuch 1}} {
    my variable counter
    incr counter $howmuch
  }
  method value {} {
    my variable counter
    return $counter
  }
  method reset {} {
    my variable counter
    set counter 0
  }
}
::clay::define example {
  variable buffer
  constructor {} {
    # Build a counter object
    set obj [namespace current]::counter
    ::counter create $obj
    # Delegate the counter
    my delegate <counter> $obj
  }
  method line {text} {
    my <counter> incr
    append buffer $text
  }
}

set A [example new]
$A line {Who’s line is it anyway?}
$A <counter> value
1
}]


[section {Commands}]
[list_begin definitions]
[call proc [cmd putb] [opt "[arg map]"] [arg text]]

 Append a line of text to a variable. Optionally apply a string mapping.


[call proc [cmd clay::ancestors] [opt "[arg args]"]]


[call proc [cmd clay::args_to_dict] [opt "[arg args]"]]


[call proc [cmd clay::args_to_options] [opt "[arg args]"]]


[call proc [cmd clay::dictmerge] [arg varname] [opt "[arg args]"]]


[call proc [cmd clay::_dictmerge] [arg a] [arg b]]


[call proc [cmd clay::dictputb] [arg dict]]


[call proc [cmd clay::_dictputb] [arg leaf] [arg level] [arg varname] [arg dict]]


[call proc [cmd clay::dynamic_arguments] [arg ensemble] [arg method] [arg arglist] [opt "[arg args]"]]



[call proc [cmd clay::dynamic_wrongargs_message] [arg arglist]]



[call proc [cmd clay::is_dict] [arg d]]


[call proc [cmd clay::is_null] [arg value]]


[call proc [cmd clay::leaf] [opt "[arg args]"]]


[call proc [cmd clay::path] [opt "[arg args]"]]


[call proc [cmd clay::script_path]]


[call proc [cmd clay::NSNormalize] [arg qualname]]


[call proc [cmd clay::uuid_generate] [opt "[arg args]"]]


[call proc [cmd clay::dynamic_methods] [arg class]]


[call proc [cmd clay::dynamic_methods_class] [arg thisclass]]


[call proc [cmd clay::define::Array] [arg name] [opt "[arg values] [const ""]"]]

 New OO Keywords for clay




[call proc [cmd clay::define::component] [arg name] [arg info]]



[call proc [cmd clay::define::constructor] [arg arglist] [arg rawbody]]



[call proc [cmd clay::define::class_method] [arg name] [arg arglist] [arg body]]



[call proc [cmd clay::define::clay] [opt "[arg args]"]]


[call proc [cmd clay::define::destructor] [arg rawbody]]



[call proc [cmd clay::define::Dict] [arg name] [opt "[arg values] [const ""]"]]


[call proc [cmd clay::define::Variable] [arg name] [opt "[arg default] [const ""]"]]

    This keyword can also be expressed:
    [example {property variable NAME {default DEFAULT}}]
    [para]
    Variables registered in the variable property are also initialized
    (if missing) when the object changes class via the [emph morph] method.




[call proc [cmd clay::object_create] [arg objname] [opt "[arg class] [const ""]"]]


[call proc [cmd clay::object_rename] [arg object] [arg newname]]


[call proc [cmd clay::object_destroy] [arg objname]]


[call proc [cmd clay::ensemble_methodbody] [arg ensemble] [arg einfo]]


[call proc [cmd clay::define::Ensemble] [arg rawmethod] [arg arglist] [arg body]]


[call proc [cmd clay::cat] [arg fname]]

 Concatenate a file




[call proc [cmd clay::docstrip] [arg text]]

 Strip the global comments from tcl code. Used to
 prevent the documentation markup comments from clogging
 up files intended for distribution in machine readable format.




[list_end]

[section Classes]
[subsection {Class  oo::class}]

[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "clay ancestors"]]
Return this class and all ancestors in search order.

[call method [cmd "clay dump"]]
Return a complete dump of this object's clay data, but only this object's clay data.

[call method [cmd "clay get"] [arg path] [opt [option "path..."]]]

     Pull a chunk of data from the clay system. If the last element of [emph path] is a branch (ends in a slash /),
     returns a recursive merge of all data from this object and it's constituent classes of the data in that branch.
     If the last element is a leaf, search this object for a matching leaf, or search all  constituent classes for a matching
     leaf and return the first value found.
     If no value is found, returns an empty string.
   

[call method [cmd "clay merge"] [arg dict] [opt [option "dict..."]]]
Recursively merge the dictionaries given into the object's local clay storage.

[call method [cmd "clay replace"] [arg dictionary]]
Replace the contents of the internal clay storage with the dictionary given.

[call method [cmd "clay search"] [arg path] [opt [option "path..."]]]
Return the first matching value for the path in either this class's clay data or one of its ancestors

[call method [cmd "clay set"] [arg path] [opt [option "path..."]] [arg value]]
Merge the conents of [const value] with the object's clay storage at [const path].

[list_end]
[para]

[subsection {Class  oo::object}]

[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "clay ancestors"]]
Return the class this object belongs to, all classes mixed into this object, and all ancestors of those classes in search order.

[call method [cmd "clay cget"] [arg field]]

 Pull a value from either the object's clay structure or one of its constituent classes that matches the field name.
 The order of search us:
 [para] 1. The as a value in local dict variable config
 [para] 2. The as a value in local dict variable clay
 [para] 3. As a leaf in any ancestor as a root of the clay tree
 [para] 4. As a leaf in any ancestor under the const/ branch of the clay tree
   

[call method [cmd "clay delegate"] [opt "[arg stub]"] [opt "[arg object]"]]

 Introspect or control method delegation. With no arguments, the method will return a
 key/value list of stubs and objects. With just the [arg stub] argument, the method will
 return the object (if any) attached to the stub. With a [arg stub] and an [arg object]
 this command will forward all calls to the method [arg stub] to the [arg object].
 

[call method [cmd "clay dump"]]
Return a complete dump of this object's clay data, as well as the data from all constituent classes recursively blended in.

[call method [cmd "clay ensemble_map"]]
Return a dictionary describing the method ensembles to be assembled for this object

[call method [cmd "clay eval"] [arg script]]
Evaluated a script in the namespace of this object

[call method [cmd "clay evolve"]]
Trigger the [method InitializePublic] private method

[call method [cmd "clay exists"] [arg path] [opt [option "path..."]]]
Returns 1 if [emph path] exists in either the object's clay data. Values greater than one indicate the element exists in one of the object's constituent classes. A value of zero indicates the path could not be found.

[call method [cmd "clay flush"]]
Wipe any caches built by the clay implementation

[call method [cmd "clay forward"] [arg method] [arg object]]
A convenience wrapper for
 [example {oo::objdefine [self] forward {*}$args}]
 

[call method [cmd "clay get"] [arg path] [opt [option "path..."]]]
Pull a chunk of data from the clay system. If the last element of [emph path] is a branch (ends in a slash /),
   returns a recursive merge of all data from this object and it's constituent classes of the data in that branch.
   If the last element is a leaf, search this object for a matching leaf, or search all  constituent classes for a matching
   leaf and return the first value found.
   If no value is found, returns an empty string.
 

[call method [cmd "clay leaf"] [arg path] [opt [option "path..."]]]
A modified get which is tailored to pull only leaf elements

[call method [cmd "clay merge"] [arg dict] [opt [option "dict..."]]]
Recursively merge the dictionaries given into the object's local clay storage.

[call method [cmd "clay mixin"] [arg class] [opt [option "class..."]]]

 Perform [lb]oo::objdefine [lb]self[rb] mixin[rb] on this object, with a few additional rules:
   Prior to the call, for any class was previously mixed in, but not in the new result, execute the script registered to mixin/ unmap-script (if given.)
   For all new classes, that were not present prior to this call, after the native TclOO mixin is invoked, execute the script registered to mixin/ map-script (if given.)
   Fall all classes that are now present and “mixed in”, execute the script registered to mixin/ react-script (if given.)
 

[call method [cmd "clay mixinmap"] [opt "[arg stub]"] [opt "[arg classes]"]]
With no arguments returns the map of stubs and classes mixed into the current object. When only stub is given,
  returns the classes mixed in on that stub. When stub and classlist given, replace the classes currently on that stub with the given
  classes and invoke clay mixin on the new matrix of mixed in classes.
 

[call method [cmd "clay provenance"] [arg path] [opt [option "path..."]]]
Return either [const self] if that path exists in the current object, or return the first class (if any) along the clay search path which contains that element.

[call method [cmd "clay replace"] [arg dictionary]]
Replace the contents of the internal clay storage with the dictionary given.

[call method [cmd "clay source"] [arg filename]]
Source the given filename within the object's namespace

[call method [cmd "clay set"] [arg path] [opt [option "path..."]] [arg value]]
Merge the conents of [const value] with the object's clay storage at [const path].

[call method [cmd "InitializePublic"]]

 Instantiate variables. Called on object creation and during clay mixin.




[list_end]
[para]

[subsection {Class  clay::object}]
 clay::object

 This class is inherited by all classes that have options.



[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "InitializePublic"]]

 Instantiate variables and build ensemble methods.




[list_end]
[para]

[subsection {Class  clay::doctool}]
[example {{ set authors {
   {John Doe} {[email protected]}
   {Tom RichardHarry} {[email protected]}
 }
 # Create the object
 ::clay::doctool create AutoDoc
 set fout [open [file join $moddir module.tcl] w]
 foreach file [glob [file join $srcdir *.tcl]] {
   set content [::clay::cat [file join $srcdir $file]]
    # Scan the file
    AutoDoc scan_text $content
    # Strip the comments from the distribution
    puts $fout [::clay::docstrip $content]
 }
 # Write out the manual page
 set manout [open [file join $moddir module.man] w]
 dict set arglist header [string map $modmap [::clay::cat [file join $srcdir manual.txt]]]
 dict set arglist footer [string map $modmap [::clay::cat [file join $srcdir footer.txt]]]
 dict set arglist authors $authors
 puts $manout [AutoDoc manpage {*}$arglist]
 close $manout


}}]
[para]

 Tool for build scripts to dynamically generate manual files from comments
 in source code files

[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "constructor"]]


[call method [cmd "arglist"] [arg arglist]]

 Process an argument list into an informational dict.
 This method also understands non-positional
 arguments expressed in the notation of Tip 471
 [uri https://core.tcl-lang.org/tips/doc/trunk/tip/479.md].
 [para]
 The output will be a dictionary of all of the fields and whether the fields
 are [const positional], [const mandatory], and whether they have a
 [const default] value.
 [para]

[para]Example: [example {   my arglist {a b {c 10}}

   > a {positional 1 mandatory 1} b {positional 1 mandatory 1} c {positional 1 mandatory 0 default 10}


}]

[call method [cmd "comment"] [arg block]]

 Convert a block of comments into an informational dictionary.
 If lines in the comment start with a single word ending in a colon,
 all subsequent lines are appended to a dictionary field of that name.
 If no fields are given, all of the text is appended to the [const description]
 field.

[para]Example: [example { my comment {Does something cool}
 > description {Does something cool}

 my comment {
 title : Something really cool
 author : Sean Woods
 author : John Doe
 description :
 This does something really cool!
 }
 > description {This does something really cool!}
   title {Something really cool}
   author {Sean Woods
   John Doe}


}]

[call method [cmd "keyword.Class"] [arg resultvar] [arg commentblock] [arg name] [arg body]]

 Process an oo::objdefine call that modifies the class object
 itself




[call method [cmd "keyword.class"] [arg resultvar] [arg commentblock] [arg name] [arg body]]

 Process an oo::define, clay::define, etc statement.




[call method [cmd "keyword.class_method"] [arg resultvar] [arg commentblock] [arg name] [opt "[arg args]"]]

 Process a statement for a clay style class method




[call method [cmd "keyword.method"] [arg resultvar] [arg commentblock] [arg name] [opt "[arg args]"]]

 Process a statement for a tcloo style object method




[call method [cmd "keyword.proc"] [arg commentblock] [arg name] [arg arglist] [arg body]]

 Process a proc statement




[call method [cmd "reset"]]

 Reset the state of the object and its embedded coroutine




[call method [cmd "Main"]]

 Main body of the embedded coroutine for the object




[call method [cmd "section.method"] [arg keyword] [arg method] [arg minfo]]

 Generate the manual page text for a method or proc




[call method [cmd "section.class"] [arg class_name] [arg class_info]]

 Generate the manual page text for a class




[call method [cmd "section.command"] [arg procinfo]]

 Generate the manual page text for the commands section




[call method [cmd "manpage"] [opt "[option "header [emph value]"]"] [opt "[option "footer [emph value]"]"] [opt "[option "authors [emph list]"]"]]

 Generate the manual page. Returns the completed text suitable for saving in .man file.
 The header argument is a block of doctools text to go in before the machine generated
 section. footer is a block of doctools text to go in after the machine generated
 section. authors is a list of individual authors and emails in the form of AUTHOR EMAIL ?AUTHOR EMAIL?...



[call method [cmd "scan_text"] [arg text]]
 Scan a block of text



[call method [cmd "scan_file"] [arg filename]]
 Scan a file of text



[list_end]
[para]

[section AUTHORS]
Sean Woods [uri mailto:<[email protected]>][para]
[vset CATEGORY oo]
[include ../doctools2base/include/feedback.inc]

[manpage_end]

Added modules/clay/clay.tcl.







































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
###
# clay.tcl
#
# Copyright (c) 2018 Sean Woods
#
# BSD License
###
# @@ Meta Begin
# Package clay 0.3
# Meta platform     tcl
# Meta summary      A minimalist framework for complex TclOO development
# Meta description  This package introduces the method "clay" to both oo::object
# Meta description  and oo::class which facilitate complex interactions between objects
# Meta description  and their ancestor and mixed in classes.
# Meta category     TclOO
# Meta subject      framework
# Meta require      {Tcl 8.6}
# Meta author       Sean Woods
# Meta license      BSD
# @@ Meta End

###
# Amalgamated package for clay
# Do not edit directly, tweak the source in build/ and rerun
# build.tcl
###
package provide clay 0.3
namespace eval ::clay {}

###
# START: core.tcl
###
package require Tcl 8.6 ;# try in pipeline.tcl. Possibly other things.
package require TclOO
package require uuid
package require oo::dialect
::oo::dialect::create ::clay
::namespace eval ::clay {}
::namespace eval ::clay::classes {}
::namespace eval ::clay::define {}

###
# END: core.tcl
###
###
# START: procs.tcl
###
if {[info commands ::ladd] eq {}} {
  proc ladd {varname args} {
    upvar 1 $varname var
    if ![info exists var] {
        set var {}
    }
    foreach item $args {
      if {$item in $var} continue
      lappend var $item
    }
    return $var
  }
}
if {[info command ::ldelete] eq {}} {
  proc ::ldelete {varname args} {
    upvar 1 $varname var
    if ![info exists var] {
        return
    }
    foreach item [lsort -unique $args] {
      while {[set i [lsearch $var $item]]>=0} {
        set var [lreplace $var $i $i]
      }
    }
    return $var
  }
}
if {[info command ::lrandom] eq {}} {
  proc ::lrandom list {
    set len [llength $list]
    set idx [expr int(rand()*$len)]
    return [lindex $list $idx]
  }
}
if {[::info commands ::tcl::dict::getnull] eq {}} {
  proc ::tcl::dict::getnull {dictionary args} {
    if {[exists $dictionary {*}$args]} {
      get $dictionary {*}$args
    }
  }
  namespace ensemble configure dict -map [dict replace\
      [namespace ensemble configure dict -map] getnull ::tcl::dict::getnull]
}
proc ::putb {buffername args} {
  upvar 1 $buffername buffer
  switch [llength $args] {
    1 {
      append buffer [lindex $args 0] \n
    }
    2 {
      append buffer [string map {*}$args] \n
    }
    default {
      error "usage: putb buffername ?map? string"
    }
  }
}
namespace eval ::clay {}
set ::clay::trace 0
proc ::clay::ancestors args {
  set result {}
  set queue {}
  foreach class [lreverse $args] {
    lappend queue $class
  }

  # Rig things such that that the top superclasses
  # are evaluated first
  while {[llength $queue]} {
    set tqueue $queue
    set queue {}
    foreach qclass $tqueue {
      foreach aclass [::info class superclasses $qclass] {
        if { $aclass in $result } continue
        if { $aclass in $queue } continue
        lappend queue $aclass
      }
    }
    foreach item $tqueue {
      if { $item ni $result } {
        lappend result $item
      }
    }
  }
  return $result
}
proc ::clay::args_to_dict args {
  if {[llength $args]==1} {
    return [lindex $args 0]
  }
  return $args
}
proc ::clay::args_to_options args {
  set result {}
  foreach {var val} [args_to_dict {*}$args] {
    lappend result [string trim $var -:] $val
  }
  return $result
}
proc ::clay::dictmerge {varname args} {
  upvar 1 $varname result
  if {![info exists result]} {
    set result {}
  }
  switch [llength $args] {
    0 {
      return
    }
    1 {
      set result [_dictmerge $result [lindex $args 0]]
      return $result
    }
    2 {
      lassign $args path value
    }
    default {
      # Merge b into a, and handle nested dicts appropriately
      set value [lindex $args end]
      set path  [lrange $args 0 end-1]
    }
  }
  if {![dict exists $result {*}$path]} {
    dict set result {*}$path $value
    return $result
  }
  if {[string index [lindex $path end] end] ne "/"} {
    dict set result {*}$path $value
    return $result
  }
  ::dict for { k v } $value {
    # Element names that end in "/" are assumed to be branches
    if {[string index $k end] eq "/" && [::dict exists $result {*}$path $k]} {
      # key exists in a and b?  let's see if both values are dicts
      # both are dicts, so merge the dicts
      set dvalue [::dict get $result {*}$path $k]
      if { [is_dict $dvalue] && [is_dict $v] } {
        ::dict set result {*}$path $k [_dictmerge $dvalue $v]
      } else {
        ::dict set result {*}$path $k $v
      }
    } else {
      ::dict set result {*}$path $k $v
    }
  }
  return $result
}
proc ::clay::_dictmerge {a b} {
  ::set result $a
  # Merge b into a, and handle nested dicts appropriately
  ::dict for { k v } $b {
    if {[string index $k end] ne "/"} {
      # Element names that do not end in "/" are assumed to be literals
      # or dict trees we intend to replace wholly
      ::dict set result $k $v
    } elseif { [::dict exists $result $k] } {
      # key exists in a and b?  let's see if both values are dicts
      # both are dicts, so merge the dicts
      if { [is_dict [::dict get $result $k]] && [is_dict $v] } {
        ::dict set result $k [_dictmerge [::dict get $result $k] $v]
      } else {
        ::dict set result $k $v
      }
    } else {
      ::dict set result $k $v
    }
  }
  return $result
}
proc ::clay::dictputb {dict} {
  set result {}
  set level -1
  _dictputb 0 $level result $dict
  return $result
}
proc ::clay::_dictputb {leaf level varname dict} {
  upvar 1 $varname result
  incr level
  foreach {field value} $dict {
    if {[string index $field end] eq "/"} {
      putb result "[string repeat "  " $level]$field \{"
      _dictputb 0 $level result $value
      putb result "[string repeat "  " $level]\}"
    } else {
      putb result "[string repeat "  " $level][list $field $value]"
    }
  }
}
proc ::clay::dynamic_arguments {ensemble method arglist args} {
  set idx 0
  set len [llength $args]
  if {$len > [llength $arglist]} {
    ###
    # Catch if the user supplies too many arguments
    ###
    set dargs 0
    if {[lindex $arglist end] ni {args dictargs}} {
      return -code error -level 2 "Usage: $ensemble $method [string trim [dynamic_wrongargs_message $arglist]]"
    }
  }
  foreach argdef $arglist {
    if {$argdef eq "args"} {
      ###
      # Perform args processing in the style of tcl
      ###
      uplevel 1 [list set args [lrange $args $idx end]]
      break
    }
    if {$argdef eq "dictargs"} {
      ###
      # Perform args processing in the style of tcl
      ###
      uplevel 1 [list set args [lrange $args $idx end]]
      ###
      # Perform args processing in the style of clay
      ###
      set dictargs [::clay::args_to_options {*}[lrange $args $idx end]]
      uplevel 1 [list set dictargs $dictargs]
      break
    }
    if {$idx > $len} {
      ###
      # Catch if the user supplies too few arguments
      ###
      if {[llength $argdef]==1} {
        return -code error -level 2 "Usage: $ensemble $method [string trim [dynamic_wrongargs_message $arglist]]"
      } else {
        uplevel 1 [list set [lindex $argdef 0] [lindex $argdef 1]]
      }
    } else {
      uplevel 1 [list set [lindex $argdef 0] [lindex $args $idx]]
    }
    incr idx
  }
}
proc ::clay::dynamic_wrongargs_message {arglist} {
  set result ""
  set dargs 0
  foreach argdef $arglist {
    if {$argdef in {args dictargs}} {
      set dargs 1
      break
    }
    if {[llength $argdef]==1} {
      append result " $argdef"
    } else {
      append result " ?[lindex $argdef 0]?"
    }
  }
  if { $dargs } {
    append result " ?option value?..."
  }
  return $result
}
proc ::clay::is_dict { d } {
  # is it a dict, or can it be treated like one?
  if {[catch {::dict size $d} err]} {
    #::set ::errorInfo {}
    return 0
  }
  return 1
}
proc ::clay::is_null value {
  return [expr {$value in {{} NULL}}]
}
proc ::clay::leaf args {
  set marker [string index [lindex $args end] end]
  set result [path {*}${args}]
  if {$marker eq "/"} {
    return $result
  }
  return [list {*}[lrange $result 0 end-1] [string trim [string trim [lindex $result end]] /]]
}
proc ::clay::path args {
  set result {}
  foreach item $args {
    set item [string trim $item :./]
    foreach subitem [split $item /] {
      lappend result [string trim ${subitem}]/
    }
  }
  return $result
}
proc ::clay::script_path {} {
  set path [file dirname [file join [pwd] [info script]]]
  return $path
}
proc ::clay::NSNormalize qualname {
  if {![string match ::* $qualname]} {
    set qualname ::clay::classes::$qualname
  }
  regsub -all {::+} $qualname "::"
}
proc ::clay::uuid_generate args {
  return [uuid::uuid generate]
}
namespace eval ::clay {  variable option_class {}
  variable core_classes {::oo::class ::oo::object}
}

###
# END: procs.tcl
###
###
# START: class.tcl
###
oo::define oo::class {  method clay {submethod args} {
    my variable clay
    if {![info exists clay]} {
      set clay {}
    }
    switch $submethod {
      ancestors {
        tailcall ::clay::ancestors [self]
      }
      exists {
        set path [::clay::leaf {*}$args]
        if {![info exists clay]} {
          return 0
        }
        return [dict exists $clay {*}$path]
      }
      dump {
        return $clay
      }
      getnull -
      get {
        set path $args
        set leaf [expr {[string index [lindex $path end] end] ne "/"}]
        set clayorder [::clay::ancestors [self]]
        #puts [list [self] clay get {*}$path (leaf: $leaf)]
        if {$leaf} {
          #puts [list EXISTS: (clay) [dict exists $clay {*}$path]]
          if {[dict exists $clay {*}$path]} {
            return [dict get $clay {*}$path]
          }
          #puts [list Search in the in our list of classes for an answer]
          foreach class $clayorder {
            if {$class eq [self]} continue
            if {[$class clay exists {*}$path]} {
              set value [$class clay get {*}$path]
              return $value
            }
          }
        } else {
          set result {}
          # Leaf searches return one data field at a time
          # Search in our local dict
          # Search in the in our list of classes for an answer
          foreach class [lreverse $clayorder] {
            if {$class eq [self]} continue
            ::clay::dictmerge result [$class clay get {*}$path]
          }
          if {[dict exists $clay {*}$path]} {
            ::clay::dictmerge result [dict get $clay {*}$path]
          }
          return $result
        }
      }
      merge {
        foreach arg $args {
          ::clay::dictmerge clay {*}$arg
        }
      }
      search {
        foreach aclass [::clay::ancestors [self]] {
          if {[$aclass clay exists {*}$args]} {
            return [$aclass clay get {*}$args]
          }
        }
      }
      set {
        #puts [list [self] clay SET {*}$args]
        set value [lindex $args end]
        set path [::clay::leaf {*}[lrange $args 0 end-1]]
        ::clay::dictmerge clay {*}$path $value
      }
      default {
        dict $submethod clay {*}$args
      }
    }
  }
}

###
# END: class.tcl
###
###
# START: object.tcl
###
oo::define oo::object {  method clay {submethod args} {
    my variable clay claycache clayorder config option_canonical
    if {![info exists clay]} {set clay {}}
    if {![info exists claycache]} {set claycache {}}
    if {![info exists config]} {set config {}}
    if {![info exists clayorder] || [llength $clayorder]==0} {
      set clayorder [::clay::ancestors [info object class [self]] {*}[info object mixins [self]]]
    }
    switch $submethod {
      ancestors {
        return $clayorder
      }
      cget {
        # Leaf searches return one data field at a time
        # Search in our local dict
        if {[llength $args]==1} {
          set field [string trim [lindex $args 0] -:/]
          if {[info exists option_canonical($field)]} {
            set field $option_canonical($field)
          }
          if {[dict exists $config $field]} {
            return [dict get $config $field]
          }
        }
        if {[dict exists $clay {*}$args]} {
          return [dict get $clay {*}$args]
        }
        # Search in our local cache
        if {[dict exists $claycache {*}$args]} {
          return [dict get $claycache {*}$args]
        }
        # Search in the in our list of classes for an answer
        foreach class $clayorder {
          if {[$class clay exists {*}$args]} {
            set value [$class clay get {*}$args]
            dict set claycache {*}$args $value
            return $value
          }
          if {[$class clay exists const/ {*}$args]} {
            set value [$class clay get const/ {*}$args]
            dict set claycache {*}$args $value
            return $value
          }
        }
        return {}
      }
      delegate {
        if {![dict exists $clay delegate/ <class>]} {
          dict set clay delegate/ <class> [info object class [self]]
        }
        if {[llength $args]==0} {
          return [dict get $clay delegate/]
        }
        if {[llength $args]==1} {
          set stub <[string trim [lindex $args 0] <>]>
          if {![dict exists $clay delegate/ $stub]} {
            return {}
          }
          return [dict get $clay delegate/ $stub]
        }
        if {([llength $args] % 2)} {
          error "Usage: delegate
    OR
    delegate stub
    OR
    delegate stub OBJECT ?stub OBJECT? ..."
        }
        foreach {stub object} $args {
          set stub <[string trim $stub <>]>
          dict set clay delegate/ $stub $object
          oo::objdefine [self] forward ${stub} $object
          oo::objdefine [self] export ${stub}
        }
      }
      dump {
        # Do a full dump of clay data
        set result $clay
        # Search in the in our list of classes for an answer
        foreach class $clayorder {
          ::clay::dictmerge result [$class clay dump]
        }
        ::clay::dictmerge result $clay
        return $result
      }
      ensemble_map {
        set ensemble [lindex $args 0]
        my variable claycache
        set mensemble [string trim $ensemble :/]/
        if {[dict exists $claycache method_ensemble/ $mensemble]} {
          return [dict get $claycache method_ensemble/ $mensemble]
        }
        set emap [my clay get method_ensemble/ $mensemble]
        dict set claycache method_ensemble/ $mensemble $emap
        return $emap
      }
      eval {
        set script [lindex $args 0]
        set buffer {}
        set thisline {}
        foreach line [split $script \n] {
          append thisline $line
          if {![info complete $thisline]} {
            append thisline \n
            continue
          }
          set thisline [string trim $thisline]
          if {[string index $thisline 0] eq "#"} continue
          if {[string length $thisline]==0} continue
          if {[lindex $thisline 0] eq "my"} {
            # Line already calls out "my", accept verbatim
            append buffer $thisline \n
          } elseif {[string range $thisline 0 2] eq "::"} {
            # Fully qualified commands accepted verbatim
            append buffer $thisline \n
          } elseif {
            append buffer "my $thisline" \n
          }
          set thisline {}
        }
        eval $buffer
      }
      evolve -
      initialize {
        my InitializePublic
      }
      exists {
        # Leaf searches return one data field at a time
        # Search in our local dict
        if {[dict exists $clay {*}$args]} {
          return 1
        }
        # Search in our local cache
        if {[dict exists $claycache {*}$args]} {
          return 2
        }
        set count 2
        # Search in the in our list of classes for an answer
        foreach class $clayorder {
          incr count
          if {[$class clay exists {*}$args]} {
            return $count
          }
        }
        return 0
      }
      flush {
        set claycache {}
        set clayorder [::clay::ancestors [info object class [self]] {*}[info object mixins [self]]]
      }
      forward {
        oo::objdefine [self] forward {*}$args
      }
      getnull -
      get {
        set leaf [expr {[string index [lindex $args end] end] ne "/"}]
        #puts [list [self] clay get {*}$args (leaf: $leaf)]
        if {$leaf} {
          #puts [list EXISTS: (clay) [dict exists $clay {*}$args]]
          if {[dict exists $clay {*}$args]} {
            return [dict get $clay {*}$args]
          }
          # Search in our local cache
          #puts [list EXISTS: (claycache) [dict exists $claycache {*}$args]]
          if {[dict exists $claycache {*}$args]} {
            return [dict get $claycache {*}$args]
          }
          # Search in the in our list of classes for an answer
          foreach class $clayorder {
            if {[$class clay exists {*}$args]} {
              set value [$class clay get {*}$args]
              dict set claycache {*}$args $value
              return $value
            }
          }
        } else {
          set result {}
          # Leaf searches return one data field at a time
          # Search in our local dict

          # Search in the in our list of classes for an answer
          foreach class [lreverse $clayorder] {
            ::clay::dictmerge result [$class clay get {*}$args]
          }
          if {[dict exists $clay {*}$args]} {
            ::clay::dictmerge result [dict get $clay {*}$args]
          }
          return $result
        }
      }
      leaf {
        # Leaf searches return one data field at a time
        # Search in our local dict
        if {[dict exists $clay {*}$args]} {
          return [dict get $clay {*}$args]
        }
        # Search in our local cache
        if {[dict exists $claycache {*}$args]} {
          return [dict get $claycache {*}$args]
        }
        # Search in the in our list of classes for an answer
        foreach class $clayorder {
          if {[$class clay exists {*}$args]} {
            set value [$class clay get {*}$args]
            dict set claycache {*}$args $value
            return $value
          }
        }
      }
      merge {
        foreach arg $args {
          ::clay::dictmerge clay {*}$arg
        }
      }
      mixin {
        ###
        # Mix in the class
        ###
        set prior  [info object mixins [self]]
        set newmixin {}
        foreach item $args {
          lappend newmixin ::[string trimleft $item :]
        }
        set newmap $args
        foreach class $prior {
          if {$class ni $newmixin} {
            set script [$class clay get mixin/ unmap-script]
            if {[string length $script]} {
              if {[catch $script err errdat]} {
                puts stderr "[self] MIXIN ERROR POPPING $class:\n[dict get $errdat -errorinfo]"
              }
            }
          }
        }
        ::oo::objdefine [self] mixin {*}$args
        ###
        # Build a compsite map of all ensembles defined by the object's current
        # class as well as all of the classes being mixed in
        ###
        my InitializePublic
        foreach class $newmixin {
          if {$class ni $prior} {
            set script [$class clay get mixin/ map-script]
            if {[string length $script]} {
              if {[catch $script err errdat]} {
                puts stderr "[self] MIXIN ERROR PUSHING $class:\n[dict get $errdat -errorinfo]"
              }
            }
          }
        }
        foreach class $newmixin {
          set script [$class clay search mixin/ react-script]
          if {[string length $script]} {
            if {[catch $script err errdat]} {
              puts stderr "[self] MIXIN ERROR PEEKING $class:\n[dict get $errdat -errorinfo]"
            }
            break
          }
        }
      }
      mixinmap {
        my variable clay
        if {![dict exists $clay mixin]} {
          dict set clay mixin {}
        }
        if {[llength $args]==0} {
          return [dict get $clay mixin]
        } elseif {[llength $args]==1} {
          return [dict getnull $clay mixin [lindex $args 0]]
        } else {
          foreach {slot classes} $args {
            dict set clay mixin $slot $classes
          }
          set claycache {}
          set classlist {}
          foreach {item class} [dict get $clay mixin] {
            if {$class ne {}} {
              lappend classlist $class
            }
          }
          my clay mixin {*}$classlist
        }
      }
      provenance {
        if {[dict exists $clay {*}$args]} {
          return self
        }
        foreach class $clayorder {
          if {[$class clay exists {*}$args]} {
            return $class
          }
        }
        return {}
      }
      replace {
        set clay [lindex $args 0]
      }
      source {
        source [lindex $args 0]
      }
      set {
        #puts [list [self] clay SET {*}$args]
        set claycache {}
        ::clay::dictmerge clay {*}$args
      }
      default {
        dict $submethod clay {*}$args
      }
    }
  }
  method InitializePublic {} {
    my variable clayorder clay claycache config option_canonical
    set claycache {}
    set clayorder [::clay::ancestors [info object class [self]] {*}[info object mixins [self]]]
    if {![info exists config]} {
      set config {}
    }
    foreach {var value} [my clay get variable/] {
      set var [string trim $var :/]
      if { $var in {clay} } continue
      my variable $var
      if {![info exists $var]} {
        if {$::clay::trace>2} {puts [list initialize variable $var $value]}
        set $var $value
      }
    }
    foreach {var value} [my clay get dict/] {
      set var [string trim $var :/]
      my variable $var
      if {![info exists $var]} {
        set $var {}
      }
      foreach {f v} $value {
        if {![dict exists ${var} $f]} {
          if {$::clay::trace>2} {puts [list initialize dict $var $f $v]}
          dict set ${var} $f $v
        }
      }
    }
    foreach {var value} [my clay get dict/] {
      set var [string trim $var :/]
      foreach {f v} [my clay get $var/] {
        if {![dict exists ${var} $f]} {
          if {$::clay::trace>2} {puts [list initialize dict (from const) $var $f $v]}
          dict set ${var} $f $v
        }
      }
    }
    foreach {var value} [my clay get array/] {
      set var [string trim $var :/]
      if { $var eq {clay} } continue
      my variable $var
      if {![info exists $var]} { array set $var {} }
      foreach {f v} $value {
        if {![array exists ${var}($f)]} {
          if {$::clay::trace>2} {puts [list initialize array $var\($f\) $v]}
          set ${var}($f) $v
        }
      }
    }
    foreach {var value} [my clay get array/] {
      set var [string trim $var :/]
      foreach {f v} [my clay get $var/] {
        if {![array exists ${var}($f)]} {
          if {$::clay::trace>2} {puts [list initialize array (from const) $var\($f\) $v]}
          set ${var}($f) $v
        }
      }
    }
    foreach {field info} [my clay get option/] {
      set field [string trim $field -/:]
      foreach alias [dict getnull $info aliases] {
        set option_canonical($alias) $field
      }
      if {[dict exists $config $field]} continue
      set getcmd [dict getnull $info default-command]
      if {$getcmd ne {}} {
        set value [{*}[string map [list %field% $field %self% [namespace which my]] $getcmd]]
      } else {
        set value [dict getnull $info default]
      }
      dict set config $field $value
      set setcmd [dict getnull $info set-command]
      if {$setcmd ne {}} {
        {*}[string map [list %field% [list $field] %value% [list $value] %self% [namespace which my]] $setcmd]
      }
    }
  }
}

###
# END: object.tcl
###
###
# START: metaclass.tcl
###
proc ::clay::dynamic_methods class {
  foreach command [info commands [namespace current]::dynamic_methods_*] {
    $command $class
  }
}
proc ::clay::dynamic_methods_class {thisclass} {
  set methods {}
  set mdata [$thisclass clay get class_typemethod/]
  foreach {method info} $mdata {
    set method [string trimright $method :/-]
    if {$method in $methods} continue
    lappend methods $method
    set arglist [dict getnull $info arglist]
    set body    [dict getnull $info body]
    ::oo::objdefine $thisclass method $method $arglist $body
  }
}
proc ::clay::define::Array {name {values {}}} {
  set class [current_class]
  set name [string trim $name :/]/
  if {![$class clay exists array/ $name]} {
    $class clay set array/ $name {}
  }
  foreach {var val} $values {
    $class clay set array/ $name $var $val
  }
}
proc ::clay::define::component {name info} {
  set class [current_class]
  foreach {field value} $info {
    $class clay set component/ [string trim $name :/]/ $field $value
  }
}
proc ::clay::define::constructor {arglist rawbody} {
  set body {
my variable DestroyEvent
set DestroyEvent 0
::clay::object_create [self] [info object class [self]]
# Initialize public variables and options
my InitializePublic
  }
  append body $rawbody
  set class [current_class]
  ::oo::define $class constructor $arglist $body
}
proc ::clay::define::class_method {name arglist body} {
  set class [current_class]
  $class clay set class_typemethod/ [string trim $name :/] [dict create arglist $arglist body $body]
}
proc ::clay::define::clay {args} {
  set class [current_class]
  if {[lindex $args 0] in "cget set branchset"} {
    $class clay {*}$args
  } else {
    $class clay set {*}$args
  }
}
proc ::clay::define::destructor rawbody {
  set body {
# Run the destructor once and only once
set self [self]
my variable DestroyEvent
if {$DestroyEvent} return
set DestroyEvent 1
::clay::object_destroy $self
}
  append body $rawbody
  ::oo::define [current_class] destructor $body
}
proc ::clay::define::Dict {name {values {}}} {
  set class [current_class]
  set name [string trim $name :/]/
  if {![$class clay exists dict/ $name]} {
    $class clay set dict/ $name {}
  }
  foreach {var val} $values {
    $class clay set dict/ $name $var $val
  }
}
proc ::clay::define::Variable {name {default {}}} {
  set class [current_class]
  set name [string trimright $name :/]
  $class clay set variable/ $name $default
  #::oo::define $class variable $name
}
proc ::clay::object_create {objname {class {}}} {
  #if {$::clay::trace>0} {
  #  puts [list $objname CREATE]
  #}
}
proc ::clay::object_rename {object newname} {
  if {$::clay::trace>0} {
    puts [list $object RENAME -> $newname]
  }
}
proc ::clay::object_destroy objname {
  if {$::clay::trace>0} {
    puts [list $objname DESTROY]
  }
  ::cron::object_destroy $objname
}
::clay::define ::clay::object {  Variable clay {}
  Variable claycache {}
  Variable DestroyEvent 0
  method InitializePublic {} {
    next
    my variable clayorder clay claycache
    if {[info exists clay]} {
      set emap [dict getnull $clay method_ensemble/]
    } else {
      set emap {}
    }
    foreach class [lreverse $clayorder] {
      ###
      # Build a compsite map of all ensembles defined by the object's current
      # class as well as all of the classes being mixed in
      ###
      foreach {mensemble einfo} [$class clay get method_ensemble/] {
        set ensemble [string trim $mensemble :/]
        if {$::clay::trace>2} {puts [list Defining $ensemble from $class]}

        foreach {method info} $einfo {
          dict set info source $class
          if {$::clay::trace>2} {puts [list Defining $ensemble -> $method from $class - $info]}
          dict set emap $ensemble $method $info
        }
      }
    }
    foreach {ensemble einfo} $emap {
      #if {[dict exists $einfo _body]} continue
      set body [::clay::ensemble_methodbody $ensemble $einfo]
      if {$::clay::trace>2} {
        set rawbody $body
        set body {puts [list [self] <object> [self method]]}
        append body \n $rawbody
      }
      oo::objdefine [self] method $ensemble {{method default} args} $body
    }
  }
}

###
# END: metaclass.tcl
###
###
# START: ensemble.tcl
###
::namespace eval ::clay::define {}
proc ::clay::ensemble_methodbody {ensemble einfo} {
  set default standard
  set preamble {}
  set eswitch {}
  if {[dict exists $einfo default]} {
    set emethodinfo [dict get $einfo default]
    set arglist     [dict getnull $emethodinfo arglist]
    set realbody    [dict get $emethodinfo body]
    if {[llength $arglist]==1 && [lindex $arglist 0] in {{} args arglist}} {
      set body {}
    } else {
      set body "\n      ::clay::dynamic_arguments $ensemble \$method [list $arglist] {*}\$args"
    }
    append body "\n      " [string trim $realbody] "      \n"
    set default $body
    dict unset einfo default
  }
  foreach {msubmethod esubmethodinfo} [lsort -dictionary -stride 2 $einfo] {
    set submethod [string trim $msubmethod :/-]
    if {$submethod eq "_body"} continue
    if {$submethod eq "_preamble"} {
      set preamble [dict getnull $esubmethodinfo body]
      continue
    }
    set arglist     [dict getnull $esubmethodinfo arglist]
    set realbody    [dict getnull $esubmethodinfo body]
    if {[string length [string trim $realbody]] eq {}} {
      dict set eswitch $submethod {}
    } else {
      if {[llength $arglist]==1 && [lindex $arglist 0] in {{} args arglist}} {
        set body {}
      } else {
        set body "\n      ::clay::dynamic_arguments $ensemble \$method [list $arglist] {*}\$args"
      }
      append body "\n      " [string trim $realbody] "      \n"
      if {$submethod eq "default"} {
        set default $body
      } else {
        dict set eswitch $submethod $body
      }
    }
  }
  set methodlist [lsort -dictionary [dict keys $eswitch]]
  if {![dict exists $eswitch <list>]} {
    dict set eswitch <list> {return $methodlist}
  }
  if {$default eq "standard"} {
    set default "error \"unknown method $ensemble \$method. Valid: \$methodlist\""
  }
  dict set eswitch default $default
  set mbody {}

  append mbody $preamble \n

  append mbody \n [list set methodlist $methodlist]
  append mbody \n "set code \[catch {switch -- \$method [list $eswitch]} result opts\]"
  append mbody \n {return -options $opts $result}
  return $mbody
}
::proc ::clay::define::Ensemble {rawmethod arglist body} {
  set class [current_class]
  #if {$::clay::trace>2} {
  #  puts [list $class Ensemble $rawmethod $arglist $body]
  #}
  set mlist [split $rawmethod "::"]
  set ensemble [string trim [lindex $mlist 0] :/]
  set mensemble ${ensemble}/
  if {[llength $mlist]==1 || [lindex $mlist 1] in "_body"} {
    set method _body
    ###
    # Simple method, needs no parsing, but we do need to record we have one
    ###
    $class clay set method_ensemble/ $mensemble _body [dict create arglist $arglist body $body]
    if {$::clay::trace>2} {
      puts [list $class clay set method_ensemble/ $mensemble _body ...]
    }
    set method $rawmethod
    if {$::clay::trace>2} {
      puts [list $class Ensemble $rawmethod $arglist $body]
      set rawbody $body
      set body {puts [list [self] $class [self method]]}
      append body \n $rawbody
    }
    ::oo::define $class method $rawmethod $arglist $body
    return
  }
  set method [join [lrange $mlist 2 end] "::"]
  $class clay set method_ensemble/ $mensemble [string trim $method :/] [dict create arglist $arglist body $body]
  if {$::clay::trace>2} {
    puts [list $class clay set method_ensemble/ $mensemble [string trim $method :/]  ...]
  }
}

###
# END: ensemble.tcl
###
###
# START: doctool.tcl
###
namespace eval ::clay {}
proc ::clay::cat fname {
    if {![file exists $fname]} {
       return
    }
    set fin [open $fname r]
    set data [read $fin]
    close $fin
    return $data
}
proc ::clay::docstrip text {
  set result {}
  foreach line [split $text \n] {
    append thisline $line \n
    if {![info complete $thisline]} continue
    set outline $thisline
    set thisline {}
    if {[string trim $outline] eq {}} {
      continue
    }
    if {[string index [string trim $outline] 0] eq "#"} continue
    set cmd [string trim [lindex $outline 0] :]
    if {$cmd eq "namespace" && [lindex $outline 1] eq "eval"} {
      append result [list {*}[lrange $outline 0 end-1] [docstrip [lindex $outline end]]] \n
      continue
    }
    if {[string match "*::define" $cmd] && [llength $outline]==3} {
      append result [list {*}[lrange $outline 0 end-1] [docstrip [lindex $outline end]]] \n
      continue
    }
    if {$cmd eq "oo::class" && [lindex $outline 1] eq "create"} {
      append result [list {*}[lrange $outline 0 end-1] [docstrip [lindex $outline end]]] \n
      continue
    }
    append result $outline
  }
  return $result
}
proc ::putb {buffername args} {
  upvar 1 $buffername buffer
  switch [llength $args] {
    1 {
      append buffer [lindex $args 0] \n
    }
    2 {
      append buffer [string map {*}$args] \n
    }
    default {
      error "usage: putb buffername ?map? string"
    }
  }
}
oo::class create ::clay::doctool {  constructor {} {
    my reset
  }
  method arglist {arglist} {
    set result [dict create]
    foreach arg $arglist {
      set name [lindex $arg 0]
      dict set result $name positional 1
      dict set result $name mandatory  1
      if {$name in {args dictargs}} {
        switch [llength $arg] {
          1 {
            dict set result $name mandatory 0
          }
          2 {
            dict for {optname optinfo} [lindex $arg 1] {
              set optname [string trim $optname -:]
              dict set result $optname {positional 1 mandatory 0}
              dict for {f v} $optinfo {
                dict set result $optname [string trim $f -:] $v
              }
            }
          }
          default {
            error "Bad argument"
          }
        }
      } else {
        switch [llength $arg] {
          1 {
            dict set result $name mandatory 1
          }
          2 {
            dict set result $name mandatory 0
            dict set result $name default   [lindex $arg 1]
          }
          default {
            error "Bad argument"
          }
        }
      }
    }
    return $result
  }
  method comment block {
    set count 0
    set field description
    set result [dict create description {}]
    foreach line [split $block \n] {
      set sline [string trim $line]
      set fwidx [string first " " $sline]
      if {$fwidx < 0} {
        set firstword [string range $sline 0 end]
        set restline {}
      } else {
        set firstword [string range $sline 0 [expr {$fwidx-1}]]
        set restline [string range $sline [expr {$fwidx+1}] end]
      }
      if {[string index $firstword end] eq ":"} {
        set field [string tolower [string trim $firstword -:]]
        switch $field {
          desc {
            set field description
          }
        }
        if {[string length $restline]} {
          dict append result $field "$restline\n"
        }
      } else {
        dict append result $field "$line\n"
      }
    }
    return $result
  }
  method keyword.Class {resultvar commentblock name body} {
    upvar 1 $resultvar result
    set name [string trim $name :]
    if {[dict exists $result class $name]} {
      set info [dict get $result class $name]
    } else {
      set info [my comment $commentblock]
    }
    set commentblock {}
    foreach line [split $body \n] {
      append thisline $line \n
      if {![info complete $thisline]} continue
      set thisline [string trim $thisline]
      if {[string index $thisline 0] eq "#"} {
        append commentblock [string trimleft $thisline #] \n
        set thisline {}
        continue
      }
      set cmd [string trim [lindex $thisline 0] ":"]
      switch $cmd {
        method -
        Ensemble {
          my keyword.class_method info $commentblock  {*}[lrange $thisline 1 end-1]
          set commentblock {}
        }
      }
      set thisline {}
    }
    dict set result class $name $info
  }
  method keyword.class {resultvar commentblock name body} {
    upvar 1 $resultvar result
    set name [string trim $name :]
    if {[dict exists $result class $name]} {
      set info [dict get $result class $name]
    } else {
      set info [my comment $commentblock]
    }
    set commentblock {}
    foreach line [split $body \n] {
      append thisline $line \n
      if {![info complete $thisline]} continue
      set thisline [string trim $thisline]
      if {[string index $thisline 0] eq "#"} {
        append commentblock [string trimleft $thisline #] \n
        set thisline {}
        continue
      }
      set cmd [string trim [lindex $thisline 0] ":"]
      switch $cmd {
        superclass {
          dict set info ancestors [lrange $thisline 1 end]
          set commentblock {}
        }
        class_method {
          my keyword.class_method info $commentblock  {*}[lrange $thisline 1 end-1]
          set commentblock {}
        }
        destructor -
        constructor {
          my keyword.method info $commentblock {*}[lrange $thisline 0 end-1]
          set commentblock {}
        }
        method -
        Ensemble {
          my keyword.method info $commentblock  {*}[lrange $thisline 1 end-1]
          set commentblock {}
        }
      }
      set thisline {}
    }
    dict set result class $name $info
  }
  method keyword.class_method {resultvar commentblock name args} {
    upvar 1 $resultvar result
    set info [my comment $commentblock]
    if {[dict exists $info ensemble]} {
      dict for {method minfo} [dict get $info ensemble] {
        dict set result class_method "${name} $method" $minfo
      }
    } else {
      switch [llength $args] {
        1 {
          set arglist [lindex $args 0]
        }
        0 {
          set arglist dictargs
          #set body [lindex $args 0]
        }
        default {error "could not interpret method $name {*}$args"}
      }
      if {![dict exists $info arglist]} {
        dict set info arglist [my arglist $arglist]
      }
      dict set result class_method [string trim $name :] $info
    }
  }
  method keyword.method {resultvar commentblock name args} {
    upvar 1 $resultvar result
    set info [my comment $commentblock]
    if {[dict exists $info ensemble]} {
      dict for {method minfo} [dict get $info ensemble] {
        dict set result method "\"${name} $method\"" $minfo
      }
    } else {
      switch [llength $args] {
        1 {
          set arglist [lindex $args 0]
        }
        0 {
          set arglist dictargs
          #set body [lindex $args 0]
        }
        default {error "could not interpret method $name {*}$args"}
      }
      if {![dict exists $info arglist]} {
        dict set info arglist [my arglist $arglist]
      }
      dict set result method "\"[split [string trim $name :] ::]\"" $info
    }
  }
  method keyword.proc {commentblock name arglist body} {
    set info [my comment $commentblock]
    if {![dict exists $info arglist]} {
      dict set info arglist [my arglist $arglist]
    }
    return $info
  }
  method reset {} {
    my variable coro
    set coro [info object namespace [self]]::coro
    oo::objdefine [self] forward coro $coro
    if {[info command $coro] ne {}} {
      rename $coro {}
    }
    coroutine $coro {*}[namespace code {my Main}]
  }
  method Main {} {

    my variable info
    set info [dict create]
    yield [info coroutine]
    set thisline {}
    set commentblock {}
    set linec 0
    while 1 {
      set line [yield]
      append thisline $line \n
      if {![info complete $thisline]} continue
      set thisline [string trim $thisline]
      if {[string index $thisline 0] eq "#"} {
        append commentblock [string trimleft $thisline #] \n
        set thisline {}
        continue
      }
      set cmd [string trim [lindex $thisline 0] ":"]
      switch $cmd {
        Proc -
        proc {
          set procinfo [my keyword.proc $commentblock {*}[lrange $thisline 1 end]]
          dict set info proc [string trim [lindex $thisline 1] :] $procinfo
          set commentblock {}
        }
        oo::objdefine {
          if {[llength $thisline]==3} {
            lassign $thisline tcmd name body
            my keyword.Class info $commentblock $name $body
          } else {
            puts "Warning: bare oo::define in library"
          }
        }
        oo::define {
          if {[llength $thisline]==3} {
            lassign $thisline tcmd name body
            my keyword.class info $commentblock $name $body
          } else {
            puts "Warning: bare oo::define in library"
          }
        }
        tao::define -
        clay::define -
        tool::define {
          lassign $thisline tcmd name body
          my keyword.class info $commentblock $name $body
          set commentblock {}
        }
        oo::class {
          lassign $thisline tcmd mthd name body
          my keyword.class info $commentblock $name $body
          set commentblock {}
        }
        default {
          if {[lindex [split $cmd ::] end] eq "define"} {
            lassign $thisline tcmd name body
            my keyword.class info $commentblock $name $body
            set commentblock {}
          }
          set commentblock {}
        }
      }
      set thisline {}
    }
  }
  method section.method {keyword method minfo} {
    set result {}
    set line "\[call $keyword \[cmd $method\]"
    if {[dict exists $minfo arglist]} {
      dict for {argname arginfo} [dict get $minfo arglist] {
        set positional 1
        set mandatory  1
        set repeating 0
        dict with arginfo {}
        if {$mandatory==0} {
          append line " \[opt \""
        } else {
          append line " "
        }
        if {$positional} {
          append line "\[arg $argname"
        } else {
          append line "\[option \"$argname"
          if {[dict exists $arginfo type]} {
            append line " \[emph [dict get $arginfo type]\]"
          } else {
            append line " \[emph value\]"
          }
          append line "\""
        }
        append line "\]"
        if {$mandatory==0} {
          if {[dict exists $arginfo default]} {
            append line " \[const \"[dict get $arginfo default]\"\]"
          }
          append line "\"\]"
        }
        if {$repeating} {
          append line " \[opt \[option \"$argname...\"\]\]"
        }
      }
    }
    append line \]
    putb result $line
    if {[dict exists $minfo description]} {
      putb result [dict get $minfo description]
    }
    if {[dict exists $minfo example]} {
      putb result "\[para\]Example: \[example [list [dict get $minfo example]]\]"
    }
    return $result
  }
  method section.class {class_name class_info} {
    set result {}
    putb result "\[subsection \{Class  $class_name\}\]"
    if {[dict exists $class_info ancestors]} {
      set line "\[emph \"ancestors\"\]:"
      foreach {c} [dict get $class_info ancestors] {
        append line " \[class [string trim $c :]\]"
      }
      putb result $line
      putb result {[para]}
    }
    dict for {f v} $class_info {
      if {$f in {class_method method description ancestors example}} continue
      putb result "\[emph \"$f\"\]: $v"
      putb result {[para]}
    }
    if {[dict exists $class_info example]} {
      putb result "\[example \{[list [dict get $class_info example]]\}\]"
      putb result {[para]}
    }
    if {[dict exists $class_info description]} {
      putb result [dict get $class_info description]
      putb result {[para]}
    }
    if {[dict exists $class_info class_method]} {
      putb result "\[class \{Class Methods\}\]"
      #putb result "Methods on the class object itself."
      putb result {[list_begin definitions]}
      dict for {method minfo} [dict get $class_info class_method] {
        putb result [my section.method classmethod $method $minfo]
      }
      putb result {[list_end]}
      putb result {[para]}
    }
    if {[dict exists $class_info method]} {
      putb result "\[class {Methods}\]"
      putb result {[list_begin definitions]}
      dict for {method minfo} [dict get $class_info method] {
        putb result [my section.method method $method $minfo]
      }
      putb result {[list_end]}
      putb result {[para]}
    }
    return $result
  }
  method section.command {procinfo} {
    set result {}
    putb result "\[section \{Commands\}\]"
    putb result {[list_begin definitions]}
    dict for {method minfo} $procinfo {
      putb result [my section.method proc $method $minfo]
    }
    putb result {[list_end]}
    return $result
  }
  method manpage args {
    my variable info map
    set result {}
    set header {}
    set footer {}
    set authors {}
    dict with args {}
    putb result $header
    dict for {sec_type sec_info} $info {
      switch $sec_type {
        proc {
          putb result [my section.command $sec_info]
        }
        class {
          putb result "\[section Classes\]"
          dict for {class_name class_info} $sec_info {
            putb result [my section.class $class_name $class_info]
          }
        }
        default {
          putb result "\[section [list $sec_type $sec_name]\]"
          if {[dict exists $sec_info description]} {
            putb result [dict get $sec_info description]
          }
        }
      }
    }
    if {[llength $authors]} {
      putb result {[section AUTHORS]}
      foreach {name email} $authors {
        putb result "$name \[uri mailto:$email\]\[para\]"
      }
    }
    putb result $footer
    putb result {[manpage_end]}
    return $result
  }
  method scan_text {text} {
    my variable linecount coro
    set linecount 0
    foreach line [split $text \n] {
      incr linecount
      $coro $line
    }
  }
  method scan_file {filename} {
    my variable linecount coro
    set fin [open $filename r]
    set linecount 0
    while {[gets $fin line]>=0} {
      incr linecount
      $coro $line
    }
    close $fin
  }
}

###
# END: doctool.tcl
###

namespace eval ::clay {
  namespace export *
}

Added modules/clay/clay.test.
































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# clay.test - Copyright (c) 2018 Sean Woods
# -------------------------------------------------------------------------

#source [file join  #	[file dirname [file dirname [file dirname [file join [pwd] [info script]]]]]  #	compat devtools testutilities.tcl]

source [file join  [file dirname [file dirname [file join [pwd] [info script]]]]  devtools testutilities.tcl]


testsNeedTcl     8.6
testsNeedTcltest 2
testsNeed        TclOO 1

support {
    use uuid/uuid.tcl uuid
    use oodialect/oodialect.tcl oo::dialect

}
testing {
    useLocal clay.tcl clay
}


set ::clay::trace 0


# -------------------------------------------------------------------------
# dictmerge Testing
unset -nocomplain foo
test dictmerge-0001 {Invoking dictmerge with empty args on a non existent variable create an empty variable} {
  ::clay::dictmerge foo
  set foo
} {}

unset -nocomplain foo
::clay::dictmerge foo bar/ baz/ bell/ bang
test dictmerge-0002 {For new entries dictmerge is essentially a set} {
  dict get $foo bar/ baz/ bell/
} {bang}

::clay::dictmerge foo bar/ baz/ boom/ bang
test dictmerge-0003 {For entries that do exist a zipper merge is performed} {
  dict get $foo bar/ baz/ bell/
} {bang}
test dictmerge-0004 {For entries that do exist a zipper merge is performed} {
  dict get $foo bar/ baz/ boom/
} {bang}

::clay::dictmerge foo bar/ baz/ bop {color green flavor strawberry}
test dictmerge-0005 {Leaves are replaced even if they look like a dict} {
  dict get $foo bar/ baz/ bop
} {color green flavor strawberry}

::clay::dictmerge foo bar/ baz/ bop {color yellow}
test dictmerge-0006 {Leaves are replaced even if they look like a dict} {
  dict get $foo bar/ baz/ bop
} {color yellow}

::clay::dictmerge foo bar/ baz/ bang/ {color green flavor strawberry}
test dictmerge-0005 {Branches are merged} {
  dict get $foo bar/ baz/ bang/
} {color green flavor strawberry}

::clay::dictmerge foo bar/ baz/ bang/ color yellow
test dictmerge-0006 {Branches are merged}  {
  dict get $foo bar/ baz/ bang/
} {color yellow flavor strawberry}

::clay::dictmerge foo bar/ baz/ bang/ {color blue}
test dictmerge-0007 {Branches are merged}  {
  dict get $foo bar/ baz/ bang/
} {color blue flavor strawberry}

::clay::dictmerge foo {option/ {color {type color} flavor {sense taste}}}
::clay::dictmerge foo {option/ {format {default ascii}}}

test dictmerge-0008 {Whole dicts are merged}  {
  dict get $foo option/ color
} {type color}
test dictmerge-0009 {Whole dicts are merged}  {
  dict get $foo option/ flavor
} {sense taste}
test dictmerge-0010 {Whole dicts are merged}  {
  dict get $foo option/ format
} {default ascii}

###
# Tests for the httpd module
###
test dictmerge-0010 {Test that leaves are merged properly}
set bar {}
::clay::dictmerge bar {
   proxy/ {port 10101 host myhost.localhost}
}
::clay::dictmerge bar {
   mimetxt {Host: localhost
Content_Type: text/plain
Content-Length: 15
}
   http {HTTP_HOST {} CONTENT_LENGTH 15 HOST localhost CONTENT_TYPE text/plain UUID 3a7b4cdc-28d7-49b7-b18d-9d7d18382b9e REMOTE_ADDR 127.0.0.1 REMOTE_HOST 127.0.0.1 REQUEST_METHOD POST REQUEST_URI /echo REQUEST_PATH echo REQUEST_VERSION 1.0 DOCUMENT_ROOT {} QUERY_STRING {} REQUEST_RAW {POST /echo HTTP/1.0} SERVER_PORT 10001 SERVER_NAME 127.0.0.1 SERVER_PROTOCOL HTTP/1.1 SERVER_SOFTWARE {TclHttpd 4.2.0} LOCALHOST 0} UUID 3a7b4cdc-28d7-49b7-b18d-9d7d18382b9e uriinfo {fragment {} port {} path echo scheme http host {} query {} pbare 0 pwd {} user {}}
   mixin {reply ::test::content.echo}
   prefix /echo
   proxy_port 10010
   proxy/ {host localhost}
}

test dictmerge-0011 {Whole dicts are merged}  {
  dict get $bar proxy_port
} {10010}

test dictmerge-0012 {Whole dicts are merged}  {
  dict get $bar http CONTENT_LENGTH
} 15
test dictmerge-0013 {Whole dicts are merged}  {
  dict get $bar proxy/ host
} localhost
test dictmerge-0014 {Whole dicts are merged}  {
  dict get $bar proxy/ port
} 10101


# -------------------------------------------------------------------------

::oo::dialect::create ::alpha

proc ::alpha::define::is_alpha {} {
  dict set ::testinfo([current_class]) is_alpha 1
}

::alpha::define ::alpha::object {
  is_alpha
}

::oo::dialect::create ::bravo ::alpha

proc ::bravo::define::is_bravo {} {
  dict set ::testinfo([current_class]) is_bravo 1
}

::bravo::define ::bravo::object {
  is_bravo
}

::oo::dialect::create ::charlie ::bravo

proc ::charlie::define::is_charlie {} {
  dict set ::testinfo([current_class]) is_charlie 1
}

::charlie::define ::charlie::object {
  is_charlie
}

::oo::dialect::create ::delta ::charlie

proc ::delta::define::is_delta {} {
  dict set ::testinfo([current_class]) is_delta 1
}

::delta::define ::delta::object {
  is_delta
}

::delta::class create adam {
  is_alpha
  is_bravo
  is_charlie
  is_delta
}

test oodialect-keyword-001 {Testing keyword application} {
  set ::testinfo(::adam)
} {is_alpha 1 is_bravo 1 is_charlie 1 is_delta 1}

test oodialect-keyword-002 {Testing keyword application} {
  set ::testinfo(::alpha::object)
} {is_alpha 1}

test oodialect-keyword-003 {Testing keyword application} {
  set ::testinfo(::bravo::object)
} {is_bravo 1}

test oodialect-keyword-004 {Testing keyword application} {
  set ::testinfo(::charlie::object)
} {is_charlie 1}

test oodialect-keyword-005 {Testing keyword application} {
  set ::testinfo(::delta::object)
} {is_delta 1}

###
# Declare an object from a namespace
###
namespace eval ::test1 {
  ::alpha::class create a {
    aliases A
    is_alpha
  }
  ::alpha::define b {
    aliases B BEE
    is_alpha
  }
  ::alpha::class create ::c {
    aliases C
    is_alpha
  }
  ::alpha::define ::d {
    aliases D
    is_alpha
  }
}

test oodialect-naming-001 {Testing keyword application} {
  set ::testinfo(::test1::a)
} {is_alpha 1}

test oodialect-naming-002 {Testing keyword application} {
  set ::testinfo(::test1::b)
} {is_alpha 1}

test oodialect-naming-003 {Testing keyword application} {
  set ::testinfo(::c)
} {is_alpha 1}

test oodialect-naming-004 {Testing keyword application} {
  set ::testinfo(::d)
} {is_alpha 1}

test oodialect-aliasing-001 {Testing keyword application} {
namespace eval ::test1 {
    ::alpha::define e {
       superclass A
    }
}
} ::test1::e

test oodialect-aliasing-002 {Testing keyword application} {
namespace eval ::test1 {
    ::bravo::define f {
       superclass A
    }
}
} ::test1::f


test oodialect-aliasing-003 {Testing aliase method on class} {
  ::test1::a aliases
} {::test1::A}


test oodialect-ancestry-003 {Testing heritage} {
  ::clay::ancestors ::test1::f
} {::test1::f ::test1::a ::bravo::object ::alpha::object ::oo::object}

test oodialect-ancestry-004 {Testing heritage} {
  ::clay::ancestors ::alpha::object
} {::alpha::object ::oo::object}

test oodialect-ancestry-005 {Testing heritage} {
  ::clay::ancestors ::delta::object
} {::delta::object ::charlie::object ::bravo::object ::alpha::object ::oo::object}

# -------------------------------------------------------------------------
# clay submodule testing
# -------------------------------------------------------------------------
# Test canonical path building
set path {const/ foo/ bar/ baz/}


test oo-clay-path-0001 "Test path: const foo bar baz" {
  ::clay::path const foo bar baz
} $path


test oo-clay-path-0002 "Test path: const/ foo/ bar/ baz" {
  ::clay::path const/ foo/ bar/ baz
} $path


test oo-clay-path-0003 "Test path: const/foo/bar/baz" {
  ::clay::path const/foo/bar/baz
} $path


test oo-clay-path-0004 "Test path: const/foo bar/baz" {
  ::clay::path const/foo bar/baz
} $path


test oo-clay-path-0005 "Test path: const/foo/bar baz" {
  ::clay::path const/foo/bar baz
} $path


test oo-clay-path-0006 "Test path: const foo/bar/baz" {
  ::clay::path const foo/bar/baz
} $path


test oo-clay-path-0007 "Test path: const foo bar/baz" {
  ::clay::path const foo bar/baz
} $path


test oo-clay-path-0008 "Test path: const/foo bar baz" {
  ::clay::path const/foo bar baz
} $path

set path {const/ foo/ bar/ baz/ bing}

test oo-clay-leaf-0001 "Test leaf: const foo bar baz bing" {
  ::clay::leaf const foo bar baz bing
} $path


test oo-clay-leaf-0002 "Test leaf: const/ foo/ bar/ baz/ bing" {
  ::clay::leaf const/ foo/ bar/ baz/ bing
} $path


test oo-clay-leaf-0003 "Test leaf: const/foo/bar/baz/bing" {
  ::clay::leaf const/foo/bar/baz/bing
} $path


test oo-clay-leaf-0004 "Test leaf: const/foo bar/baz/bing:" {
  ::clay::leaf const/foo bar/baz/bing:
} $path


test oo-clay-leaf-0005 "Test leaf: const/foo/bar baz bing" {
  ::clay::leaf const/foo/bar baz bing
} $path


test oo-clay-leaf-0006 "Test leaf: const/foo/bar baz bing:" {
  ::clay::leaf const/foo/bar baz bing:
} $path


test oo-clay-leaf-0007 "Test leaf: const foo/bar/baz/bing" {
  ::clay::leaf const foo/bar/baz/bing
} $path


test oo-clay-leaf-0008 "Test leaf: const foo bar/baz/bing" {
  ::clay::leaf const foo bar/baz/bing
} $path


test oo-clay-leaf-0009 "Test leaf: const/foo bar baz bing" {
  ::clay::leaf const/foo bar baz bing
} $path

namespace eval ::foo {}

clay::define ::foo::classa {

  clay set const color  blue
  clay set const/flavor strawberry
  clay set {const/ sound} zoink
  clay set info/ {
    animal no
    building no
    subelement {pedantic yes}
  }

}


test oo-class-clay-method-0001 "Test ::foo::classa const/ color exists" {
  ::foo::classa clay exists const/ color
} 1


test oo-class-clay-method-0001 "Test ::foo::classa const/ color value" {
  ::foo::classa clay get const/ color
} {blue}


test oo-class-clay-method-0003 "Test ::foo::classa const/ flavor exists" {
  ::foo::classa clay exists const/ flavor
} 1


test oo-class-clay-method-0003 "Test ::foo::classa const/ flavor value" {
  ::foo::classa clay get const/ flavor
} {strawberry}


test oo-class-clay-method-0005 "Test ::foo::classa const/ sound exists" {
  ::foo::classa clay exists const/ sound
} 1


test oo-class-clay-method-0005 "Test ::foo::classa const/ sound value" {
  ::foo::classa clay get const/ sound
} {zoink}


test oo-class-clay-method-0007 "Test ::foo::classa info/ animal exists" {
  ::foo::classa clay exists info/ animal
} 1


test oo-class-clay-method-0007 "Test ::foo::classa info/ animal value" {
  ::foo::classa clay get info/ animal
} {no}


test oo-class-clay-method-0009 "Test ::foo::classa info/ building exists" {
  ::foo::classa clay exists info/ building
} 1


test oo-class-clay-method-0009 "Test ::foo::classa info/ building value" {
  ::foo::classa clay get info/ building
} {no}


test oo-class-clay-method-0011 "Test ::foo::classa info/ subelement exists" {
  ::foo::classa clay exists info/ subelement
} 1


test oo-class-clay-method-0011 "Test ::foo::classa info/ subelement value" {
  ::foo::classa clay get info/ subelement
} {pedantic yes}


clay::define ::foo::classb {
  clay set const/ color black
  clay set const/ flavor vanilla
  clay set const/ feeling dread
  clay set info/ subelement {spoon yes}

}


test oo-class-clay-method-0013 "Test ::foo::classb const/ color exists" {
  ::foo::classb clay exists const/ color
} 1


test oo-class-clay-method-0013 "Test ::foo::classb const/ color value" {
  ::foo::classb clay get const/ color
} {black}


test oo-class-clay-method-0015 "Test ::foo::classb const/ flavor exists" {
  ::foo::classb clay exists const/ flavor
} 1


test oo-class-clay-method-0015 "Test ::foo::classb const/ flavor value" {
  ::foo::classb clay get const/ flavor
} {vanilla}


test oo-class-clay-method-0017 "Test ::foo::classb const/ feeling exists" {
  ::foo::classb clay exists const/ feeling
} 1


test oo-class-clay-method-0017 "Test ::foo::classb const/ feeling value" {
  ::foo::classb clay get const/ feeling
} {dread}


test oo-class-clay-method-0019 "Test ::foo::classb info/ subelement exists" {
  ::foo::classb clay exists info/ subelement
} 1


test oo-class-clay-method-0019 "Test ::foo::classb info/ subelement value" {
  ::foo::classb clay get info/ subelement
} {spoon yes}


clay::define ::foo::class.ab {
superclass ::foo::classb ::foo::classa
}


clay::define ::foo::class.ba {
superclass ::foo::classa ::foo::classb
}

# -------------------------------------------------------------------------
# OBJECT of ::foo::classa
set OBJECTA [::foo::classa new]

###
# Test object degation
###
proc ::foo::fakeobject {a b} {
  return [expr {$a + $b}]
}

::clay::object create TEST
TEST clay delegate funct ::foo::fakeobject
test oo-object-delegate-001 {Test object delegation} {
  ::TEST clay delegate
} {<class> ::clay::object <funct> ::foo::fakeobject}

test oo-object-delegate-002 {Test object delegation} {
  ::TEST clay delegate funct
} {::foo::fakeobject}

test oo-object-delegate-002a {Test object delegation} {
  ::TEST clay delegate <funct>
} {::foo::fakeobject}

test oo-object-delegate-003 {Test object delegation} {
  ::TEST <funct> 1 1
} {2}
test oo-object-delegate-004 {Test object delegation} {
  ::TEST <funct> 10 -7
} {3}

# Replace the function out from under
proc ::foo::fakeobject {a b} {
  return [expr {$a * $b}]
}
test oo-object-delegate-005 {Test object delegation} {
  ::TEST <funct> 10 -7
} {-70}

# Object with ::foo::classa mixed in
set MIXINA  [::oo::object new]
oo::objdefine $MIXINA mixin ::foo::classa


test oo-object-clay-method-native-0001 {Test native object gets the property} {
  $OBJECTA clay get const/ color
} {blue}
test oo-object-clay-method-mixin-0001 {Test mixin object gets the property} {
  $MIXINA clay get const/ color
} {blue}


test oo-object-clay-method-native-0002 {Test native object gets the property} {
  $OBJECTA clay get const/ flavor
} {strawberry}
test oo-object-clay-method-mixin-0002 {Test mixin object gets the property} {
  $MIXINA clay get const/ flavor
} {strawberry}


test oo-object-clay-method-native-0003 {Test native object gets the property} {
  $OBJECTA clay get const/ sound
} {zoink}
test oo-object-clay-method-mixin-0003 {Test mixin object gets the property} {
  $MIXINA clay get const/ sound
} {zoink}


test oo-object-clay-method-native-0004 {Test native object gets the property} {
  $OBJECTA clay get info/ animal
} {no}
test oo-object-clay-method-mixin-0004 {Test mixin object gets the property} {
  $MIXINA clay get info/ animal
} {no}


test oo-object-clay-method-native-0005 {Test native object gets the property} {
  $OBJECTA clay get info/ building
} {no}
test oo-object-clay-method-mixin-0005 {Test mixin object gets the property} {
  $MIXINA clay get info/ building
} {no}


test oo-object-clay-method-native-0006 {Test native object gets the property} {
  $OBJECTA clay get info/ subelement
} {pedantic yes}
test oo-object-clay-method-mixin-0006 {Test mixin object gets the property} {
  $MIXINA clay get info/ subelement
} {pedantic yes}

# -------------------------------------------------------------------------
# OBJECT of ::foo::classb
set OBJECTB [::foo::classb new]
# Object with ::foo::classb mixed in
set MIXINB  [::oo::object new]
oo::objdefine $MIXINB mixin ::foo::classb


test oo-object-clay-method-native-0007 {Test native object gets the property} {
  $OBJECTB clay get const/ color
} {black}
test oo-object-clay-method-mixin-0007 {Test mixin object gets the property} {
  $MIXINB clay get const/ color
} {black}


test oo-object-clay-method-native-0008 {Test native object gets the property} {
  $OBJECTB clay get const/ flavor
} {vanilla}
test oo-object-clay-method-mixin-0008 {Test mixin object gets the property} {
  $MIXINB clay get const/ flavor
} {vanilla}


test oo-object-clay-method-native-0009 {Test native object gets the property} {
  $OBJECTB clay get const/ feeling
} {dread}
test oo-object-clay-method-mixin-0009 {Test mixin object gets the property} {
  $MIXINB clay get const/ feeling
} {dread}


test oo-object-clay-method-native-0010 {Test native object gets the property} {
  $OBJECTB clay get info/ subelement
} {spoon yes}
test oo-object-clay-method-mixin-0010 {Test mixin object gets the property} {
  $MIXINB clay get info/ subelement
} {spoon yes}

# -------------------------------------------------------------------------
# OBJECT descended from ::foo::classa ::foo::classb
set OBJECTAB [::foo::class.ab new]
# Object where classes were mixed in ::foo::classa ::foo::classb
set MIXINAB  [::oo::object new]
oo::objdefine $MIXINAB mixin ::foo::classa ::foo::classb


test oo-object-clay-method-native-0011 {Test native object gets the property} {
  $OBJECTAB clay get const/ color
} {black}
test oo-object-clay-method-mixin-0011 {Test mixin object gets the property} {
  $MIXINAB clay get const/ color
} {black}


test oo-object-clay-method-native-0012 {Test native object gets the property} {
  $OBJECTAB clay get const/ flavor
} {vanilla}
test oo-object-clay-method-mixin-0012 {Test mixin object gets the property} {
  $MIXINAB clay get const/ flavor
} {vanilla}


test oo-object-clay-method-native-0013 {Test native object gets the property} {
  $OBJECTAB clay get const/ feeling
} {dread}
test oo-object-clay-method-mixin-0013 {Test mixin object gets the property} {
  $MIXINAB clay get const/ feeling
} {dread}


test oo-object-clay-method-native-0014 {Test native object gets the property} {
  $OBJECTAB clay get const/ sound
} {zoink}
test oo-object-clay-method-mixin-0014 {Test mixin object gets the property} {
  $MIXINAB clay get const/ sound
} {zoink}


test oo-object-clay-method-native-0015 {Test native object gets the property} {
  $OBJECTAB clay get info/ subelement
} {spoon yes}
test oo-object-clay-method-mixin-0015 {Test mixin object gets the property} {
  $MIXINAB clay get info/ subelement
} {spoon yes}


test oo-object-clay-method-native-0016 {Test native object gets the property} {
  $OBJECTAB clay get info/ animal
} {no}
test oo-object-clay-method-mixin-0016 {Test mixin object gets the property} {
  $MIXINAB clay get info/ animal
} {no}


test oo-object-clay-method-native-0017 {Test native object gets the property} {
  $OBJECTAB clay get info/ building
} {no}
test oo-object-clay-method-mixin-0017 {Test mixin object gets the property} {
  $MIXINAB clay get info/ building
} {no}

# -------------------------------------------------------------------------
# OBJECT descended from ::foo::classb ::foo::classa
set OBJECTBA [::foo::class.ba new]
# Object where classes were mixed in ::foo::classb ::foo::classa
set MIXINBA  [::oo::object new]
oo::objdefine $MIXINBA mixin ::foo::classb ::foo::classa


test oo-object-clay-method-native-0018 {Test native object gets the property} {
  $OBJECTBA clay get const/ color
} {blue}
test oo-object-clay-method-mixin-0018 {Test mixin object gets the property} {
  $MIXINBA clay get const/ color
} {blue}


test oo-object-clay-method-native-0019 {Test native object gets the property} {
  $OBJECTBA clay get const/ flavor
} {strawberry}
test oo-object-clay-method-mixin-0019 {Test mixin object gets the property} {
  $MIXINBA clay get const/ flavor
} {strawberry}


test oo-object-clay-method-native-0020 {Test native object gets the property} {
  $OBJECTBA clay get const/ sound
} {zoink}
test oo-object-clay-method-mixin-0020 {Test mixin object gets the property} {
  $MIXINBA clay get const/ sound
} {zoink}


test oo-object-clay-method-native-0021 {Test native object gets the property} {
  $OBJECTBA clay get const/ feeling
} {dread}
test oo-object-clay-method-mixin-0021 {Test mixin object gets the property} {
  $MIXINBA clay get const/ feeling
} {dread}


test oo-object-clay-method-native-0022 {Test native object gets the property} {
  $OBJECTBA clay get info/ animal
} {no}
test oo-object-clay-method-mixin-0022 {Test mixin object gets the property} {
  $MIXINBA clay get info/ animal
} {no}


test oo-object-clay-method-native-0023 {Test native object gets the property} {
  $OBJECTBA clay get info/ building
} {no}
test oo-object-clay-method-mixin-0023 {Test mixin object gets the property} {
  $MIXINBA clay get info/ building
} {no}


test oo-object-clay-method-native-0024 {Test native object gets the property} {
  $OBJECTBA clay get info/ subelement
} {pedantic yes}
test oo-object-clay-method-mixin-0024 {Test mixin object gets the property} {
  $MIXINBA clay get info/ subelement
} {pedantic yes}


###
# put a do-nothing constructor on the books
###
::clay::define ::clay::object {
  constructor args {}
}

oo::objdefine ::clay::object method foo args { return bar }

test clay-core-method-0001 {Test that adding methods to the core ::clay::object class works} {
  ::clay::object foo
} {bar}

namespace eval ::TEST {}
::clay::define ::TEST::myclass {
  clay color red
  clay flavor strawberry

}

###
# Test adding a clay property
###
test clay-class-clay-0001 {Test that a clay statement is recorded in the object of the class} {
  ::TEST::myclass clay get color
} red
test clay-class-clay-0002 {Test that a clay statement is recorded in the object of the class} {
  ::TEST::myclass clay get flavor
} strawberry

###
# Test that objects of the class get the same properties
###
set OBJ [::clay::object new {}]
set OBJ2 [::TEST::myclass new {}]

test clay-object-clay-a-0001 {Test that objects not thee class do not get properties} {
  $OBJ clay get color
} {}
test clay-object-clay-a-0002 {Test that objects not thee class do not get properties} {
  $OBJ clay get flavor
} {}
test clay-object-clay-a-0003 {Test that objects of the class get properties} {
  $OBJ2 clay get color
} red
test clay-object-clay-a-0004 {Test that objects of the class get properties} {
  $OBJ2 clay get flavor
} strawberry

test clay-object-clay-a-0005 {Test the clay ancestors function} {
  $OBJ clay ancestors
} {::clay::object ::oo::object}
test clay-object-clay-a-0006 {Test the clay ancestors function} {
  $OBJ2 clay ancestors
} {::TEST::myclass ::clay::object ::oo::object}
test clay-object-clay-a-0007 {Test the clay provenance  function} {
  $OBJ2 clay provenance  flavor
} ::TEST::myclass

###
# Test that object local setting override the class
###
test clay-object-clay-a-0008 {Test that object local setting override the class} {
  $OBJ2 clay set color purple
  $OBJ2 clay get color
} purple
test clay-object-clay-a-0009 {Test that object local setting override the class} {
  $OBJ2 clay provenance  color
} self

::clay::define ::TEST::myclasse {
  superclass ::TEST::myclass

  clay color blue

  method do args {
    return "I did $args"
  }

  Ensemble which::color {} {
    return [my clay get color]
  }
}

###
# Test clay information is passed town to subclasses
###
test clay-class-clay-0003 {Test that a clay statement is recorded in the object of the class} {
  ::TEST::myclasse clay get color
} blue
test clay-class-clay-0004 {Test that clay statements from the ancestors of this class are not present (we handle them seperately in objects)} {
  ::TEST::myclasse clay get flavor
} {strawberry}


###
# Test that properties reach objects
###
set OBJ3 [::TEST::myclasse new {}]
test clay-object-clay-b-0001 {Test that objects of the class get properties} {
  $OBJ3 clay get color
} blue
test clay-object-clay-b-0002 {Test the clay provenance  function} {
  $OBJ3 clay provenance  color
} ::TEST::myclasse
test clay-object-clay-b-0003 {Test that objects of the class get properties} {
  $OBJ3 clay get flavor
} strawberry
test clay-object-clay-b-0004 {Test the clay provenance  function} {
  $OBJ3 clay provenance  flavor
} ::TEST::myclass
test clay-object-clay-b-0005 {Test the clay provenance  function} {
  $OBJ3 clay ancestors
} {::TEST::myclasse ::TEST::myclass ::clay::object ::oo::object}

###
# Test defining a standard method
###
test clay-object-method-0001 {Test and standard method} {
  $OBJ3 do this really cool thing
} {I did this really cool thing}

test clay-object-method-0003 {Test an ensemble} {
  $OBJ3 which color
} blue
# Test setting properties
test clay-object-method-0004 {Test an ensemble} {
  $OBJ3 clay set color black
  $OBJ3 which color
} black

###
# Test that if you try to replace a global command you get an error
###
test clay-nspace-0001 {Test that if you try to replace a global command you get an error} -body {
::clay::define open {
  method bar {} { return foo }

}
}  -returnCodes {error} -result "::open does not refer to an object"

::clay::define fubar {
  method bar {} { return foo }
}
test clay-nspace-0002 {Test a non qualified class ends up in the current namespace} {
  info commands ::fubar
} {::fubar}

namespace eval ::cluster {
::clay::define fubar {
  method bar {} { return foo }
}

::clay::define ::clay::pot {
  method bar {} { return foo }
}

}
test clay-nspace-0003 {Test a non qualified class ends up in the current namespace} {
  info commands ::cluster::fubar
} {::cluster::fubar}
test clay-nspace-0003 {Test a fully qualified class ends up in the proper namespace} {
  info commands ::clay::pot
} {::clay::pot}

#set ::clay::trace 3

###
# Mixin tests
###

###
# Define a core class
###
::clay::define ::TEST::thing {

  method do args {
    return "I did $args"
  }
}


::clay::define ::TEST::vegetable {

  clay color unknown
  clay flavor unknown

  Ensemble which::flavor {} {
    return [my clay get flavor]
  }
  Ensemble which::color {} {
    return [my clay get color]
  }
}

::clay::define ::TEST::animal {

  clay color unknown
  clay sound unknown

  Ensemble which::sound {} {
    return [my clay get sound]
  }
  Ensemble which::color {} {
    return [my clay get color]
  }
}

::clay::define ::TEST::species.cat {
  superclass ::TEST::animal
  clay sound meow

}

::clay::define ::TEST::coloring.calico {
  clay color calico

}

::clay::define ::TEST::condition.dark {
  Ensemble which::color {} {
    return grey
  }
}

::clay::define ::TEST::mood.happy {
  Ensemble which::sound {} {
    return purr
  }
}
test clay-object-0001 {Test than an object is created when clay::define is invoked} {
  info commands ::TEST::mood.happy
} ::TEST::mood.happy

set OBJ [::TEST::thing new]
test clay-mixin-a-0001 {Test that prior to a mixin an ensemble doesn't exist} -body {
  $OBJ which color
} -returnCodes error -result {unknown method "which": must be clay, destroy or do}

test clay-mixin-a-0002 {Test and standard method from an ancestor} {
  $OBJ do this really cool thing
} {I did this really cool thing}

$OBJ clay mixinmap species ::TEST::animal
test clay-mixin-b-0001 {Test that an ensemble is created during a mixin} {
  $OBJ which color
} {unknown}

test clay-mixin-b-0002 {Test that an ensemble is created during a mixin} {
  $OBJ which sound
} {unknown}
test clay-mixin-b-0003 {Test that an ensemble is created during a mixin}  -body {$OBJ which flavor} -returnCodes {error}  -result {unknown method which flavor. Valid: color sound}
test clay-mixin-b-0004 {Test that mixins resolve in the correct order} {
  $OBJ clay ancestors
} {::TEST::animal ::TEST::thing ::clay::object ::oo::object}

###
# Replacing a mixin replaces the behaviors
###
$OBJ clay mixinmap species ::TEST::vegetable
test clay-mixin-c-0001 {Test that an ensemble is created during a mixin} {
  $OBJ which color
} {unknown}
test clay-mixin-c-0002 {Test that an ensemble is created during a mixin}  -body {$OBJ which sound}  -returnCodes {error}  -result {unknown method which sound. Valid: color flavor}
test clay-mixin-c-0003 {Test that an ensemble is created during a mixin} {
  $OBJ which flavor
} {unknown}
test clay-mixin-c-0004 {Test that mixins resolve in the correct order} {
  $OBJ clay ancestors
} {::TEST::vegetable ::TEST::thing ::clay::object ::oo::object}

###
# Replacing a mixin
$OBJ clay mixinmap species ::TEST::species.cat
test clay-mixin-e-0001 {Test that an ensemble is created during a mixin} {
  $OBJ which color
} {unknown}
test clay-mixin-e-0002 {Test that an ensemble is created during a mixin} {
  $OBJ which sound
} {meow}
test clay-mixin-e-0003 {Test that an ensemble is created during a mixin}  -body {$OBJ which flavor} -returnCodes {error}  -result {unknown method which flavor. Valid: color sound}
test clay-mixin-e-0004 {Test that clay data follows the rules of inheritence and order of mixin} {
  $OBJ clay ancestors
} {::TEST::species.cat ::TEST::thing ::TEST::animal ::clay::object ::oo::object}

$OBJ clay mixinmap coloring ::TEST::coloring.calico
test clay-mixin-f-0001 {Test that an ensemble is created during a mixin} {
  $OBJ which color
} {calico}
test clay-mixin-f-0002 {Test that an ensemble is created during a mixin} {
  $OBJ which sound
} {meow}
test clay-mixin-f-0003 {Test that an ensemble is created during a mixin}  -body {$OBJ which flavor} -returnCodes {error}  -result {unknown method which flavor. Valid: color sound}
test clay-mixin-f-0004 {Test that clay data follows the rules of inheritence and order of mixin} {
  $OBJ clay ancestors
} {::TEST::coloring.calico ::TEST::species.cat ::TEST::thing ::clay::object ::TEST::animal ::oo::object}

test clay-mixin-f-0005 {Test that clay data from a mixin works} {
  $OBJ clay provenance  color
} {::TEST::coloring.calico}

###
# Test variable initialization
###
::clay::define ::TEST::has_var {
  Variable my_variable 10

  method get_my_variable {} {
    my variable my_variable
    return $my_variable
  }
}

set OBJ [::TEST::has_var new]
test clay-class-variable-0001 {Test that the parser injected the right value in the right place for clay to catch it} {
  $OBJ clay get variable/ my_variable
} {10}

test clay-class-variable-0002 {Test that variables declared in the class definition are initialized} {
  $OBJ get_my_variable
} 10

###
# Test array initialization
###
::clay::define ::TEST::has_array {
  Array my_array {timeout 10}

  method get_my_array {field} {
    my variable my_array
    return $my_array($field)
  }
}

set OBJ [::TEST::has_array new]
test clay-class-array-0001 {Test that the parser injected the right value in the right place for clay to catch it} {
  $OBJ clay get array/
} {my_array/ {timeout 10}}

test clay-class-arrau-0002 {Test that variables declared in the class definition are initialized} {
  $OBJ get_my_array timeout
} 10

###
# Test dict initialization
###
::clay::define ::TEST::has_dict {
  Dict my_dict {timeout 10}

  method get_my_dict {args} {
    my variable my_dict
    return [dict get $my_dict {*}$args]
  }
}

set OBJ [::TEST::has_dict new]
test clay-class-dict-0001 {Test that the parser injected the right value in the right place for clay to catch it} {
  $OBJ clay get dict/
} {my_dict/ {timeout 10}}

test clay-class-dict-0002 {Test that variables declared in the class definition are initialized} {
  $OBJ get_my_dict timeout
} 10

###
# Test object delegation
###
::clay::define ::TEST::organelle {
  method add args {
    set total 0
    foreach item $args {
      set total [expr {$total+$item}]
    }
    return $total
  }
}
::clay::define ::TEST::master {
  constructor {} {
    set mysub [namespace current]::sub
    ::TEST::organelle create $mysub
    my clay delegate sub $mysub
  }
}

set OBJ [::TEST::master new]
###
# Test that delegation is working
###
test clay-delegation-0001 {Test an array driven ensemble} {
  $OBJ <sub> add 5 5
} 10


###
# Test the Ensemble keyword
###
::clay::define ::TEST::with_ensemble {

  Ensemble myensemble {pattern args} {
    set ensemble [self method]
    set emap [my clay ensemble_map $ensemble]
    set mlist [dict keys $emap [string tolower $pattern]]
    if {[llength $mlist] != 1} {
      error "Couldn't figure out what to do with $pattern"
    }
    set method [lindex $mlist 0]
    set arglist [dict get $emap $method arglist]
    set body    [dict get $emap $method body]
    if {$arglist ni {args {}}} {
      ::clay::dynamic_arguments $ensemble $method [list $arglist] {*}$args
    }
    eval $body
  }

  Ensemble myensemble::go args {
    return 1
  }
}

::clay::define ::TEST::with_ensemble.dance {
  Ensemble myensemble::dance args {
    return 1
  }
}
::clay::define ::TEST::with_ensemble.cannot_dance {
  Ensemble myensemble::dance args {
    return 0
  }
}

set OBJA [::clay::object new]
set OBJB [::clay::object new]

$OBJA clay mixinmap  core ::TEST::with_ensemble  friends ::TEST::with_ensemble.dance

$OBJB clay mixinmap  core ::TEST::with_ensemble  friends ::TEST::with_ensemble.cannot_dance

# Test go
test clay-dynamic-ensemble-0001 {Test ensemble with static method} {
  $OBJA myensemble go
} {1}
test clay-dynamic-ensemble-0002 {Test ensemble with static method} {
  $OBJB myensemble go
} {1}
# Test dance
test clay-dynamic-ensemble-0003 {Test ensemble with static method} {
  $OBJA myensemble dance
} {1}
test clay-dynamic-ensemble-0004 {Test ensemble with static method} {
  $OBJB myensemble dance
} {0}


###
# Class method testing
###

clay::class create WidgetClass {
  class_method working {} {
    return {Works}
  }

  class_method unknown args {
    set tkpath [lindex $args 0]
    if {[string index $tkpath 0] eq "."} {
      set obj [my new $tkpath {*}[lrange $args 1 end]]
      $obj tkalias $tkpath
      return $tkpath
    }
    next {*}$args
  }

  constructor {TkPath args} {
    my variable hull
    set hull $TkPath
    my clay delegate hull $TkPath
  }

  method tkalias tkname {
    set oldname $tkname
    my variable tkalias
    set tkalias $tkname
    set self [self]
    set hullwidget [::info object namespace $self]::tkwidget
    my clay delegate tkwidget $hullwidget
    #rename ::$tkalias $hullwidget
    my clay delegate hullwidget $hullwidget
    #::tool::object_rename [self] ::$tkalias
    rename [self] ::$tkalias
    #my Hull_Bind $tkname
    return $hullwidget
  }
}

test tool-class-method-000 {Test that class methods actually work...} {
  WidgetClass working
} {Works}

test tool-class-method-001 {Test Tk style creator} {
  WidgetClass .foo
  .foo clay delegate hull
} {.foo}

::clay::define WidgetNewClass {
  superclass WidgetClass
}

test tool-class-method-002 {Test Tk style creator inherited by morph} {
  WidgetNewClass .bar
  .bar clay delegate hull
} {.bar}



###
# Test ensemble inheritence
###
clay::define NestedClassA {
  Ensemble do::family {} {
    return NestedClassA
  }
  Ensemble do::something {} {
    return A
  }
  Ensemble do::whop {} {
    return A
  }
}
clay::define NestedClassB {
  superclass NestedClassA
  Ensemble do::family {} {
    set r [next family]
    lappend r NestedClassB
    return $r
  }
  Ensemble do::whop {} {
    return B
  }
}
clay::define NestedClassC {
  superclass NestedClassB

  Ensemble do::somethingelse {} {
    return C
  }
}
clay::define NestedClassD {
  superclass NestedClassB

  Ensemble do::somethingelse {} {
    return D
  }
}

clay::define NestedClassE {
  superclass NestedClassD NestedClassC
}

clay::define NestedClassF {
  superclass NestedClassC NestedClassD
}

NestedClassC create NestedObjectC

###
# These tests no longer work because method ensembles are now dynamically
# generated by object, that are not attached to the class anymore
#
####
#test tool-ensemble-001 {Test that an ensemble can access [next] even if no object of the ancestor class have been instantiated} {
#  NestedObjectC do family
#} {::NestedClassA ::NestedClassB ::NestedClassC}

test tool-ensemble-002 {Test that a later ensemble definition trumps a more primitive one} {
  NestedObjectC do whop
} {B}
test tool-ensemble-003 {Test that an ensemble definitions in an ancestor carry over} {
  NestedObjectC do something
} {A}

NestedClassE create NestedObjectE
NestedClassF create NestedObjectF


test tool-ensemble-004 {Test that ensembles follow the same rules for inheritance as methods} {
  NestedObjectE do somethingelse
} {D}

test tool-ensemble-005 {Test that ensembles follow the same rules for inheritance as methods} {
  NestedObjectF do somethingelse
} {C}

###
# Set of tests to exercise the mixinmap system
###
clay::define MixinMainClass {
  Variable mainvar unchanged

  Ensemble test::which {} {
    my variable mainvar
    return $mainvar
  }

  Ensemble test::main args {
    puts [list this is main $method $args]
  }

}

set mixoutscript {my test untool $class}
set mixinscript {my test tool $class}
clay::define MixinTool {
  Variable toolvar unchanged.mixin
  clay set mixin/ unmap-script $mixoutscript
  clay set mixin/ map-script $mixinscript
  clay set mixin/ name {Generic Tool}

  Ensemble test::untool class {
    my variable toolvar mainvar
    set mainvar {}
    set toolvar {}
  }

  Ensemble test::tool class {
    my variable toolvar mainvar
    set mainvar [$class clay get mixin/ name]
    set toolvar [$class clay get mixin/ name]
  }
}

clay::define MixinToolA {
  superclass MixinTool

  clay set mixin/ name {Tool A}
}

clay::define MixinToolB {
  superclass MixinTool

  clay set mixin/ name {Tool B}

  method test_newfunc {} {
    return "B"
  }
}

test tool-mixinspec-001 {Test application of mixin specs} {
  MixinTool clay get mixin/ map-script
} $mixinscript

test tool-mixinspec-002 {Test application of mixin specs} {
  MixinToolA clay get mixin/ map-script
} $mixinscript

test tool-mixinspec-003 {Test application of mixin specs} {
  MixinToolB clay get mixin/ map-script
} $mixinscript


MixinMainClass create mixintest

test tool-mixinmap-001 {Test object prior to mixins} {
  mixintest test which
} {unchanged}

mixintest clay mixinmap tool MixinToolA
test tool-mixinmap-002 {Test mixin map script ran} {
  mixintest test which
} {Tool A}

mixintest clay mixinmap tool MixinToolB

test tool-mixinmap-003 {Test mixin map script ran} {
  mixintest test which
} {Tool B}

test tool-mixinmap-003 {Test mixin map script ran} {
  mixintest test_newfunc
} {B}

mixintest clay mixinmap tool {}
test tool-mixinmap-004 {Test object prior to mixins} {
  mixintest test which
} {}


testsuiteCleanup

# Local variables:
# mode: tcl
# indent-tabs-mode: nil
# End:


Added modules/clay/pkgIndex.tcl.



















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Tcl package index file, version 1.1
# This file is generated by the "pkg_mkIndex" command
# and sourced either when an application starts up or
# by a "package unknown" script.  It invokes the
# "package ifneeded" command to set up package-related
# information so that packages will be loaded automatically
# in response to "package require" commands.  When this
# script is sourced, the variable $dir must contain the
# full path name of this file's directory.

if {![package vsatisfies [package provide Tcl] 8.6]} {return}


package ifneeded clay 0.3 [list source [file join $dir clay.tcl]]


package ifneeded oo::meta 0.8 [list source [file join $dir clay.tcl]]

Changes to modules/cron/cron.man.

119
120
121
122
123
124
125
126

127
128
129

130
131

132
133
134


135
136

137
138
139


140
141

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

159
160
161

162
163

164
165
166

167
168
169
170
171
172
173
174
175
176
177
178
179

180
181
182
183
184
185
186
119
120
121
122
123
124
125

126
127
128

129
130

131
132


133
134
135

136
137


138
139
140

141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

158
159
160

161
162

163
164
165

166
167
168
169
170
171
172
173
174
175
176
177
178

179
180
181
182
183
184
185
186







-
+


-
+

-
+

-
-
+
+

-
+

-
-
+
+

-
+
















-
+


-
+

-
+


-
+












-
+







[call [cmd {::cron::task info}] [arg process]]
Returns a dict describing [arg process]. See [cmd {::cron::task set}] for a description of the options.

[call [cmd {::cron::task set}] [arg process] [arg field] [arg value] [arg ?field...?] [arg ?value...?]]
[para]
If [arg process] does not exist, it is created. Options Include:
[list_begin definitions]
[cmd command]
[def [cmd command]]
If [cmd coroutine] is black, a global command which implements this process. If [cmd coroutine] is not
black, the command to invoke to create or recreate the coroutine.
[cmd coroutine]
[def [cmd coroutine]]
The name of the coroutine (if any) which implements this process.
[cmd frequency]
[def [cmd frequency]]
If -1, this process is terminated after the next event. If 0 this process should be called during every
idle event. If positive, this process should generate events periodically. The frequency is an interger number
of milleseconds between events.
idle event. If positive, this process should generate events periodically. The frequency is an integer number
of milliseconds between events.

[cmd object]
[def [cmd object]]
The object associated with this process or coroutine.
[cmd scheduled]
If non-zero, the absolute time from the epoch (in milleseconds) that this process will trigger an event.
[def [cmd scheduled]]
If non-zero, the absolute time from the epoch (in milliseconds) that this process will trigger an event.
If zero, and the [cmd frequency] is also zero, this process is called every idle loop.
[cmd running]
[def [cmd running]]
A boolean flag. If true it indicates the process never returned or yielded during the event loop,
and will not be called again until it does so.
[list_end]
[call [cmd ::cron::wake] [arg ?who?]]

Wake up cron, and arrange for its event loop to be run during the next Idle cycle.

[example_begin]
::cron::wake {I just did something important}
[example_end]
[list_end]
[para]
Several utility commands are provided that are used internally within cron and for
testing cron, but may or may not be useful in the general cases.
[list_begin definitions]

[call [cmd ::cron::clock_step] [arg milleseconds]]
[call [cmd ::cron::clock_step] [arg milliseconds]]
[para]
Return a clock time absolute to the epoch which falls on the next
border between one second and the next for the value of [arg milleseconds]
border between one second and the next for the value of [arg milliseconds]

[call [cmd ::cron::clock_delay] [arg milleseconds]]
[call [cmd ::cron::clock_delay] [arg milliseconds]]
[para]
Return a clock time absolute to the epoch which falls on the next
border between one second and the next [arg milleseconds] in the future.
border between one second and the next [arg milliseconds] in the future.

[call [cmd ::cron::clock_sleep] [arg seconds] [arg ?offset?]]
[para]
Return a clock time absolute to the epoch which falls exactly [arg seconds] in
the future. If offset is given it may be positive or negative, and will shift
the final time to before or after the second would flip.

[call [cmd ::cron::clock_set] [arg newtime]]
[para]
Sets the internal clock for cron. This command will advance the time in 100ms
increment, triggering events, until the internal time catches up with [arg newtime].
[para]
[arg newtime] is expressed in absolute milleseconds since the beginning of the epoch.
[arg newtime] is expressed in absolute milliseconds since the beginning of the epoch.


[list_end]
[para]
[vset CATEGORY odie]
[include ../doctools2base/include/feedback.inc]
[manpage_end]

Changes to modules/cron/cron.tcl.

262
263
264
265
266
267
268
269

270
271
272
273
274
275
276
262
263
264
265
266
267
268

269
270
271
272
273
274
275
276







-
+







}

proc ::cron::once_in_a_while body {
  set script {set _eventid_ $::cron::current_event}
  append script $body
  # Add a safety to allow this while to only execute once per call
  append script {if {$_eventid_==$::cron::current_event} yield}
  uplevel 1 [list while 1 $body]
  uplevel 1 [list while 1 $script]
}

proc ::cron::sleep ms {
  if {$::cron::trace > 1} {
    puts [list ::cron::sleep $ms [info coroutine]]
  }

Changes to modules/doctools/cvs.man.

81
82
83
84
85
86
87
88

89
90
91
92
93
94
95
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95







-
+







[para]

The values are lists of the files the entry is touching.

[list_end]
[para]

[call [cmd ::doctools::cvs::toChangeLog] [arg evar] [arg cvar] [arg fvar]]]
[call [cmd ::doctools::cvs::toChangeLog] [arg evar] [arg cvar] [arg fvar]]

The three arguments for this command are the same as the last three
arguments of the command [cmd ::doctools::cvs::scanLog]. This command
however expects them to be filled with information about one or more
logs. It takes this information and converts it into a text in the
format of a ChangeLog as accepted and generated by [syscmd emacs]. The
constructed text is returned as the result of the command.

Changes to modules/doctools/doctools_lang_intro.man.

116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
116
117
118
119
120
121
122










123
124
125
126
127
128
129
130
131
132
133
134
135










136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155










156
157
158
159
160
161
162
163
164
165
166
167
168
169










170
171
172
173
174
175
176
177
178
179
180
181
182










183
184
185
186
187
188
189
190
191
192










193
194
195
196
197
198
199
200
201










202
203
204
205
206
207
208







-
-
-
-
-
-
-
-
-
-













-
-
-
-
-
-
-
-
-
-




















-
-
-
-
-
-
-
-
-
-














-
-
-
-
-
-
-
-
-
-













-
-
-
-
-
-
-
-
-
-










-
-
-
-
-
-
-
-
-
-









-
-
-
-
-
-
-
-
-
-








[para]

Given the above a less minimal example of a document is

[example_begin]
[lb]manpage_begin NAME SECTION VERSION[rb]
[see_also doctools_intro]
[see_also doctools_lang_cmdref]
[see_also doctools_lang_faq]
[see_also doctools_lang_syntax]
[keywords {doctools commands}]
[keywords {doctools language}]
[keywords {doctools markup}]
[keywords {doctools syntax}]
[keywords markup]
[keywords {semantic markup}]
[lb][cmd {copyright {YEAR AUTHOR}}][rb]
[lb][cmd {titledesc TITLE}][rb]
[lb][cmd {moddesc   MODULE_TITLE}][rb]
[lb][cmd {require   PACKAGE VERSION}][rb]
[lb][cmd {require   PACKAGE}][rb]
[lb]description[rb]
[lb]manpage_end[rb]
[example_end]

Remember that the whitespace is optional. The document

[example {
    [manpage_begin NAME SECTION VERSION]
[see_also doctools_intro]
[see_also doctools_lang_cmdref]
[see_also doctools_lang_faq]
[see_also doctools_lang_syntax]
[keywords {doctools commands}]
[keywords {doctools language}]
[keywords {doctools markup}]
[keywords {doctools syntax}]
[keywords markup]
[keywords {semantic markup}]
    [copyright {YEAR AUTHOR}][titledesc TITLE][moddesc MODULE_TITLE]
    [require PACKAGE VERSION][require PACKAGE][description]
    [vset CATEGORY doctools]
[include ../doctools2base/include/feedback.inc]
[manpage_end]
}]

has the same meaning as the example before.

[para]

On the other hand, if [term whitespace] is present it consists not
only of any sequence of characters containing the space character,
horizontal and vertical tabs, carriage return, and newline, but it may
contain comment markup as well, in the form of the [cmd comment]
command.

[example_begin]
[lb][cmd {comment { ... }}][rb]
[lb]manpage_begin NAME SECTION VERSION[rb]
[see_also doctools_intro]
[see_also doctools_lang_cmdref]
[see_also doctools_lang_faq]
[see_also doctools_lang_syntax]
[keywords {doctools commands}]
[keywords {doctools language}]
[keywords {doctools markup}]
[keywords {doctools syntax}]
[keywords markup]
[keywords {semantic markup}]
[lb]copyright {YEAR AUTHOR}[rb]
[lb]titledesc TITLE[rb]
[lb]moddesc   MODULE_TITLE[rb][lb][cmd {comment { ... }}][rb]
[lb]require   PACKAGE VERSION[rb]
[lb]require   PACKAGE[rb]
[lb]description[rb]
[lb]manpage_end[rb]
[lb][cmd {comment { ... }}][rb]
[example_end]

[subsection {Advanced structure}]

In the simple examples of the last section we fudged a bit regarding
the markup actually allowed to be used before the [cmd manpage_begin]
[see_also doctools_intro]
[see_also doctools_lang_cmdref]
[see_also doctools_lang_faq]
[see_also doctools_lang_syntax]
[keywords {doctools commands}]
[keywords {doctools language}]
[keywords {doctools markup}]
[keywords {doctools syntax}]
[keywords markup]
[keywords {semantic markup}]
command opening the document.

[para]

Instead of only whitespace the two templating commands [cmd include]
and [cmd vset] are also allowed, to enable the writer to either set
and/or import configuration settings relevant to the document. I.e. it
is possible to write

[example_begin]
[lb][cmd {include FILE}][rb]
[lb][cmd {vset VAR VALUE}][rb]
[lb]manpage_begin NAME SECTION VERSION[rb]
[see_also doctools_intro]
[see_also doctools_lang_cmdref]
[see_also doctools_lang_faq]
[see_also doctools_lang_syntax]
[keywords {doctools commands}]
[keywords {doctools language}]
[keywords {doctools markup}]
[keywords {doctools syntax}]
[keywords markup]
[keywords {semantic markup}]
[lb]description[rb]
[lb]manpage_end[rb]
[example_end]

Even more important, these two commands are allowed anywhere where a
markup command is allowed, without regard for any other
structure. I.e. for example in the header as well.

[example_begin]
[lb]manpage_begin NAME SECTION VERSION[rb]
[see_also doctools_intro]
[see_also doctools_lang_cmdref]
[see_also doctools_lang_faq]
[see_also doctools_lang_syntax]
[keywords {doctools commands}]
[keywords {doctools language}]
[keywords {doctools markup}]
[keywords {doctools syntax}]
[keywords markup]
[keywords {semantic markup}]
[lb][cmd {include FILE}][rb]
[lb][cmd {vset VAR VALUE}][rb]
[lb]description[rb]
[lb]manpage_end[rb]
[example_end]

The only restriction [cmd include] has to obey is that the contents of
the included file must be valid at the place of the inclusion. I.e. a
file included before [cmd manpage_begin] may contain only the
[see_also doctools_intro]
[see_also doctools_lang_cmdref]
[see_also doctools_lang_faq]
[see_also doctools_lang_syntax]
[keywords {doctools commands}]
[keywords {doctools language}]
[keywords {doctools markup}]
[keywords {doctools syntax}]
[keywords markup]
[keywords {semantic markup}]
templating commands [cmd vset] and [cmd include], a file included in
the header may contain only header commands, etc.

[subsection {Text structure}]

The body of the document consists mainly of text, possibly split into
sections, subsections, and paragraphs, with parts marked up to
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
221
222
223
224
225
226
227










228
229
230
231
232
233
234







-
-
-
-
-
-
-
-
-
-







of this command closes the previous paragraph and automatically opens
the next. The first paragraph is automatically opened at the beginning
of the body, by [cmd description]. In the same manner the last
paragraph automatically ends at [cmd manpage_end].

[example_begin]
[lb]manpage_begin NAME SECTION VERSION[rb]
[see_also doctools_intro]
[see_also doctools_lang_cmdref]
[see_also doctools_lang_faq]
[see_also doctools_lang_syntax]
[keywords {doctools commands}]
[keywords {doctools language}]
[keywords {doctools markup}]
[keywords {doctools syntax}]
[keywords markup]
[keywords {semantic markup}]
[lb]description[rb]
 ...
[lb][cmd para][rb]
 ...
[lb][cmd para][rb]
 ...
[lb]manpage_end[rb]
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
250
251
252
253
254
255
256










257
258
259
260
261
262
263







-
-
-
-
-
-
-
-
-
-







[para]

Empty sections are [emph not] ignored. We are free to (not) use
paragraphs within sections.

[example_begin]
[lb]manpage_begin NAME SECTION VERSION[rb]
[see_also doctools_intro]
[see_also doctools_lang_cmdref]
[see_also doctools_lang_faq]
[see_also doctools_lang_syntax]
[keywords {doctools commands}]
[keywords {doctools language}]
[keywords {doctools markup}]
[keywords {doctools syntax}]
[keywords markup]
[keywords {semantic markup}]
[lb]description[rb]
 ...
[lb][cmd {section {Section A}}][rb]
 ...
[lb]para[rb]
 ...
[lb][cmd {section {Section B}}][rb]
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
277
278
279
280
281
282
283










284
285
286
287
288
289
290







-
-
-
-
-
-
-
-
-
-







[para]

Empty subsections are [emph not] ignored. We are free to (not) use
paragraphs within subsections.

[example_begin]
[lb]manpage_begin NAME SECTION VERSION[rb]
[see_also doctools_intro]
[see_also doctools_lang_cmdref]
[see_also doctools_lang_faq]
[see_also doctools_lang_syntax]
[keywords {doctools commands}]
[keywords {doctools language}]
[keywords {doctools markup}]
[keywords {doctools syntax}]
[keywords markup]
[keywords {semantic markup}]
[lb]description[rb]
 ...
[lb]section {Section A}[rb]
 ...
[lb][cmd {subsection {Sub 1}}][rb]
 ...
[lb]para[rb]
452
453
454
455
456
457
458
459

460
461
462
463
464
465
466
352
353
354
355
356
357
358

359
360
361
362
363
364
365
366







-
+







highlighting added.

It shows their use within a block of text, as the arguments of a list
item command ([cmd call]), and our ability to nest them.

[example_begin]
  ...
  [lb]call [lb][cmd {cmd arg_def}][rb] [lb][cmd {arg type}][rb] [lb][cmd {arg name}][rb]] [lb][cmd opt] [lb][cmd {arg mode}][rb][rb][rb]
  [lb]call [lb][cmd {cmd arg_def}][rb] [lb][cmd {arg type}][rb] [lb][cmd {arg name}][rb] [lb][cmd opt] [lb][cmd {arg mode}][rb][rb][rb]

  Text structure. List element. Argument list. Automatically closes the
  previous list element. Specifies the data-[lb][cmd {arg type}][rb] of the described
  argument of a command, its [lb][cmd {arg name}][rb] and its i/o-[lb][cmd {arg mode}][rb]. The
  latter is optional.
  ...
[example_end]
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
417
418
419
420
421
422
423










424
425
426
427
428
429
430







-
-
-
-
-
-
-
-
-
-








[list_end]

[para]

All the cross-reference commands can occur anywhere in the document
between [cmd manpage_begin] and [cmd manpage_end]. As such the writer
[see_also doctools_intro]
[see_also doctools_lang_cmdref]
[see_also doctools_lang_faq]
[see_also doctools_lang_syntax]
[keywords {doctools commands}]
[keywords {doctools language}]
[keywords {doctools markup}]
[keywords {doctools syntax}]
[keywords markup]
[keywords {semantic markup}]
can choose whether she wants to have them at the beginning of the
body, or at its end, maybe near the place a keyword is actually
defined by the main content, or considers them as meta data which
should be in the header, etc.

[para]

Changes to modules/fumagic/cfront.man.

27
28
29
30
31
32
33
34
35
36





37
38
39
40
41
42
43
27
28
29
30
31
32
33



34
35
36
37
38
39
40
41
42
43
44
45







-
-
-
+
+
+
+
+








[section COMMANDS]

[list_begin definitions]

[call [cmd ::fileutil::magic::cfront::compile] [arg path]...]

This command takes the paths of one or more files and directories and
compiles all the files, and the files in all the directories into a
single recognizer for all the file types specified in these files.
This command takes the paths of one or more files and directories and compiles
all the files, and the files in all the directories into a single analyzer for
all the file types specified in these files.  It returns a list whose first
item is a list per-file dictionaries of analyzer scripts and whose second item
is a list of analyzer commands.

[para]

All the files have to be in the format specified by magic(5).

[para]

Changes to modules/fumagic/cfront.tcl.

30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53


54
55
56
57
58
59
60
61
62
63
64
65
66
67
68



69
70




71
72
73

74
75

76
77

78
79
80


81
82
83


84
85
86
87

88
89
90
91



92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
30
31
32
33
34
35
36

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68


69
70
71
72

73
74
75
76
77
78

79


80
81
82
83
84
85

86
87
88


89
90
91
92
93

94

95


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114







-
+
















+
+













-
-
+
+
+

-
+
+
+
+


-
+
-
-
+


+


-
+
+

-
-
+
+



-
+
-

-
-
+
+
+








+







# file to compile the magic file from magic(5) into a tcl program
package require fileutil              ; # File processing (input)
package require fileutil::magic::cgen ; # Code generator.
package require fileutil::magic::rt   ; # Runtime (typemap)
package require struct::list          ; # lrepeat.
package require struct::tree          ; #

package provide fileutil::magic::cfront 1.2.0
package provide fileutil::magic::cfront 1.3.0

# ### ### ### ######### ######### #########
## Implementation

namespace eval ::fileutil::magic::cfront {
    # Configuration flag. (De)activate debugging output.
    # This is done during initialization.
    # Changes at runtime have no effect.

    variable debug 0

    # Make backend functionality accessible
    namespace import ::fileutil::magic::cgen

    namespace export compile generate install

    namespace upvar ::fileutil::magic::rt typemap typemap

    variable floattestops {= < > !}
    variable inttestops {= < > & ^ ~ !}
    variable stringtestops { > < = !}
    variable offsetopts {& | ^ + - * / %}
    variable stringmodifiers {W w c C t b T}
    variable typemodifiers [dict create \
	indirect r \
	search $stringmodifiers \
	string $stringmodifiers \
	pstring [list {*}$stringmodifiers B H h L l J] \
	regex {c s l} \
    ]
    set numeric_modifier_allowed {regex search}
	
    variable types_numeric_short { 

    variable types_numeric_short
    foreach {shortname name} {
	dC byte d1 byte C byte 1 byte ds short d2 short S short 2 short dI long
	dL long d4 long I long L long 4 long  d8 quad 8 quad dQ quad Q quad
	dL long d4 long I long L long 4 long d8 quad 8 quad dQ quad Q quad
    } {
	dict set types_numeric_short $shortname $name
	dict set types_numeric_short u$shortname u$name
    }

    variable types_numeric_re [join [list {*}[
    variable types_numeric_all [list {*}[
	array names ::fileutil::magic::rt::typemap] {*}[
	dict keys $types_numeric_short]] |]
	array names typemap] {*}[dict keys $types_numeric_short]]

    variable types_string_short [dict create s string] 
    variable types_string_short [dict create us ustring] 

    variable types_string {
	bestring clear default indirect lestring pstring regex search string
	bestring clear indirect lestring lestring16 pstring regex search
	string ustring
    }
    variable types_string_re [join [list {*}[
	dict keys $types_string_short] {*}$types_string] |]
    variable types_string_all [list {*}[
	dict keys $types_string_short] {*}$types_string]

    variable types_verbatim {name use}

    variable types_notimplemented {}
    variable types_notimplemented {der}
    variable types_notimplemented_re [join $types_notimplemented |]

    variable types_numeric_real {
	float double befloat bedouble lefloat ledouble
    variable types_numeric_real
    foreach name {float double befloat bedouble lefloat ledouble} {
	lappend types_numeric_real $name u$name
    }

    variable indir_typemap [dict create \
	b byte c byte e ledouble f ledouble g ledouble i leid3 h leshort \
	s leshort l lelong B byte C byte E bedouble F bedouble G bedouble \
	H beshort I beid3 L belong m ME S beshort]

}


proc ::fileutil::magic::cfront::advance {len args} {
    upvar node node tree tree
    if {[llength args]} {
	upvar [lindex $args 0] res
    }
    set res {}
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
126
127
128
129
130
131
132









































































































































































































































































































































































































































































































































































































































133
134
135
136
137
138
139







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







	}
    }
    set line [$tree get $node line]
    $tree set $node cursor $cursor
    return $res
}

proc ::fileutil::magic::cfront::rewind len {
    upvar node node tree tree
    set cursor [$tree get $node cursor]
    incr cursor -$len
    $tree set $node cursor $cursor
}

proc ::fileutil::magic::cfront::parseerror args {
    upvar node node tree tree
    set cursor [$tree get $node cursor]
    set line [$tree get $node line]
    set files [$tree get root files]
    set file [lindex files [$tree get $node file]]
    return -code error -errorcode [list fumagic {parse error}] [
	list [lmap arg $args {string trim $arg}] \
	file $file \
	linenenum [$tree get $node linenum] \
	cursor $cursor \
	line [list \
	    [string range $line 0 ${cursor}-1] \
	    [string range $line $cursor end]]]
}

proc ::fileutil::magic::cfront::parsewarning args {
    upvar node node tree tree
    catch {parseerror {*}$args} res options
    puts stderr [dict get $options -errorinfo]
}


# parse an individual line
variable ::fileutil::magic::cfront::parsedkeys {
}
proc ::fileutil::magic::cfront::parseline {tree node} {
    variable parsedkeys
    set line [$tree get $node line]
    $tree set $node cursor 0 
    parseoffset $tree $node
    parsetype $tree $node
    parsetest $tree $node
    parsemsg $tree $node

    set record [$tree getall $node]
    foreach key $parsedkeys {
	if {![dict exists $record $key]} {
	    return -code error [list {missing key} $key]
	}
    }
    ::fileutil::magic::cfront::Debug {
   	puts [list parsed $record]
    }
}

proc ::fileutil::magic::cfront::parsefloat {tree node} {
    set line [$tree get $node line]
    set cursor [$tree get $node cursor]
    # If only [scan] had a @ conversion character like [binary scan]
    set line2 [string range $line $cursor end]
    if {[scan $line2 %e%n num count] < 0} {
	parseerror {invalid floating point number}
    }
    set cursor [expr {$cursor + $count}]
    $tree set $node cursor $cursor

    # These suffixes are not used in magic files
    #if {[regexp -start $cursor {\A([fFlL)} -> modifier]} {
    #    advance [string length $modifier]]
    #}
    return $num
}

proc ::fileutil::magic::cfront::parseint {tree node} {
    set line [$tree get $node line]
    set cursor [$tree get $node cursor]
    # If only [scan] had a @ conversion character like [binary scan]
    set line2 [string range $line $cursor end]
    if {[set scanres [scan $line2 %lli%n num n]] < 1} {
	parseerror {invalid number}
    }
    set cursor [expr {$cursor + $n}]
    $tree set $node cursor $cursor
    # Thse suffixes are not used in magic files
    #if {[regexp -start $cursor {\A([uU]?[lL]{1,2})} -> modifier]} {
    #    advance [string length $modifier]]
    #}
    return $num
}


proc ::fileutil::magic::cfront::parsetype {tree node} {
    variable types_numeric_re
    variable types_numeric_short
    variable types_string_re
    variable types_string_short
    variable types_notimplemented_re
    set line [$tree get $node line]
    set cursor [$tree get $node cursor]
    $tree set $node mod {}
    $tree set $node mand {}
    set num_or_string {
    }
    if {[regexp -start $cursor {\A\s*(\w+)} $line match type]} {
	advance [string length $match]
	switch -regexp -matchvar match $type \
	    ^(u?)($types_numeric_re)$ - ^(u?)($types_string_re)$ {

	    lassign $match -> sgn type
	    # {to do} {Current design doesn't use sign, right?  Is it
	    # really not needed?}
	    $tree set $node sgn [dict get {{} 1 u 0} $sgn]

	    if {[regexp ^($types_numeric_re)$ $type]} {
		if {[dict exists $types_numeric_short $type]} {
		    set type [dict get $types_numeric_short $type]
		}
		$tree set $node type $type
		parsetypenummod $tree $node
	    } else {
		if {[dict exists $types_string_short $type]} {
		    set type [dict get $types_string_short $type]
		}
		$tree set $node type $type
		# No modifying operator for strings
		parsetypemod $tree $node

		if {$type eq {search} && [$tree get $node mand] eq {}} {
		    parseerror {search has no number}
		}
	    }


	} \
	^(name|use)$ {
	    $tree set $node type [lindex $match end] 
	} \
	$types_notimplemented_re {
	    parseerror {type not implemented}
	} \
	default {
	    parseerror {unknown type}
	}
    } else {
	parseerror {no type}
    }
}

proc ::fileutil::magic::cfront::parsetypemod {tree node} {
    # For numeric types , $mod is a list of modifiers and $mand is either a
    # number or the empty strinng .
    variable typemodifiers
    variable numeric_modifier_allowed
    set type [$tree get $node type]
    if {[advance 1 char] ne {/}} {
	rewind 1
	return
    }
    set res [dict create] 
    while 1 {
	if {[advance 1 char] eq {/}} {
	    continue
	}
	if {[string is space $char]} {
	    break
	}
	if {[dict exists $typemodifiers $type] && $char in [dict get $typemodifiers $type]} {
	    dict set res $char {}
	} elseif {$type in $numeric_modifier_allowed} {
	    rewind 1
	    if {[catch {parseint $tree $node} mand]} {
		# Whatever it is, it isn't a number.  Let the next parsing step
		# handle it .
		break
	    } else {
		$tree set $node mand $mand  ; # numeric modifier
	    }
	} else {
	    parseerror {bad modifier}
	}
    }
    $tree set $node mod [dict keys $res]
}

proc ::fileutil::magic::cfront::parsetypenummod {tree node} {
    # For numeric types, $mod is an operator and $mand is a number
    set line [$tree get $node line]
    set cursor [$tree get $node cursor]
    if {[regexp -start $cursor {\A([-&|^+*/%])} \
	$line match mod]} {
	advance [string length $match]
	$tree set $node mod $mod
	# {to do} {parse floats?}
	$tree set $node mand [parseint $tree $node] ; # mod operand
    } else {
	$tree set $node mod {}
	$tree set $node mand {} 
    }
}


proc ::fileutil::magic::cfront::parsestringval {tree node} {
    variable floattestops
    variable inttestops
    variable stringtestops
    advance w1 char 
    set val {}
    set line [$tree get $node line]
    while 1 {
	# break on whitespace or empty string
	if {[string is space $char] || $char eq {}} break
	switch $char [dict create  \
	    \\ {
		advance 1 char
		if {[string is space $char]} {
		    append val \\$char
		} else {
		    # extra backslashes because of interaction with glob
		    switch -glob $char [dict create \
			{\\} {
			    append val {\\}
			} \t {
			    parsewarning {use \t instead of \<tab>}
			    append val \\t
			} > - < - & - ^ - = - ! - ( - ) - . {
			    if {$char in [list {*}$stringtestops \
				{*}$floattestops {*}$inttestops]} {
				parsewarning {no need to escape operators}
			    }
			    append val $char 
			} a - b - f - n - r - t - v {
			    append val \\$char
			} x {
			    set cursor [$tree get $node cursor]
			    if {[regexp -start $cursor \
				{\A([0-9A-Fa-f]{1,2})} $line match char2]} {
				advance [string length $match] 
				append val \\x$char2
			    } else {
				parseerror {malformed \x escape sequence}
			    }
			} \[0-7\] {
			    set cursor [$tree get $node cursor]
			    append val \\$char
			    if {[regexp -start $cursor \
				{\A([0-7]{1,2})} $line match char2]} {
				advance [string length $match] 
				append val $char2
			    }
			} default {
			    parseerror {Could not handle escape sequence in value}
			}
		    ]
		}
	    } default {
		if {[string is space $char] || $char in [
		    list \# \{ \} \[  \] \" \$ \; \n]} {
		    append val \\
		}
		append val $char
	    }
	]
	advance 1 char
    }
    $tree set $node val $val
}

proc ::fileutil::magic::cfront::parsetestverbatim {tree node} {
    switch [$tree get $node type] {
	name {
	    $tree set $node rel 1
	}
	use {
	    set cursor [$tree get $node cursor]
	    # order matters in regular expression : longest match must come
	    # first in parenthesized
	    if {[regexp -start $cursor {\A\s*(?:\\\^|\^)} [$tree get $node line] match]} {
		advance [string length $match]
		$tree set $node iendian 1
	    } else {
		$tree set $node iendian 0
	    }
	}

    }
    parsestringval $tree $node
}

proc ::fileutil::magic::cfront::parsetest {tree node} {
    variable floattestops
    variable inttestops
    variable stringtestops
    variable types_numeric_real
    variable types_numeric_short
    variable types_string
    variable types_verbatim
    set type [$tree get $node type]
    if {$type in $types_verbatim} {
	parsetestverbatim $tree $node
	return
    }
    $tree set $node compinvert 0
    set testinvert 0
    set comp ==
    advance w1 char
    if {$char eq {x}} {
	advance 1 char
	if {[string is space $char]} {
	    $tree set $node testinvert 0
	    $tree set $node comp x
	    $tree set $node val {}
	    return
	} else {
	    rewind 1
	}
    }

    if {$type in $types_string} {
	while 1  {
	    if {$char in $stringtestops} {
		if {$char eq {!}} {
		    set testinvert 1
		} else {
		    set comp $char
		    # Exclamation must precede any normal operator
		    break
		}
		advance w1 char
	    } else {
		rewind 1
		break
	    }
	}
	parsestringval $tree $node
    } elseif {$type in [list {*}[
	array names ::fileutil::magic::rt::typemap] {*}[
	dict keys $types_numeric_short]]} {
	if {$type in $types_numeric_real} {
	    set ops $floattestops
	    set parsecmd parsefloat
	} else {
	    set ops $inttestops 
	    set parsecmd parseint
	}

	while 1 {
	    if {$char in $ops} {
		if {$char eq {~}} {
		    $tree set $node compinvert 1 
		} elseif {$char eq {!}} {
		    set testinvert 1
		} else {
		    set comp $char
		    # Exclamation and tilde must precede any normal operator
		    break
		}
		advance w1 char
	    } else {
		rewind 1
		break
	    }
	}
	$tree set $node val [$parsecmd $tree $node]
    } else {
	parseerror {don't know how to parse the test or this type}
    }
    switch $comp {
	= {
	    set comp ==
	}
    }
    # This facilitates Switch creation by [treegen1]
    if {$testinvert && ($comp eq {==})} {
	set comp !=
	set testinvert 0
    }
    $tree set $node testinvert $testinvert
    $tree set $node comp $comp 
}

proc ::fileutil::magic::cfront::parseoffset {tree node} {

    # Offset parser.
    # Syntax:
    #   ( ?&? number ?.[bslBSL]? ?[+-]? ?number? )

    # This was all fine and dandy, but didn't do spaces where spaces might
    # exist between lexical elements in the wild, and ididn't do offset
    # operators

    #set n {([-+]?[0-9]+|[-+]?0x[0-9A-Fa-f]+)[UL]*}

    ##"\\((&?)(${n})((\\.\[bslBSL])?)()(\[+-]?)(${n}?)\\)"
    #set o \
    #    "^(&?)${n}((?:\\.\[bslBSL])?)(?:(\[-+*/%&|^])${n})?(?:(\[+-])(\\()?${n}\\)?)?$"
    ##     |   |   |                     |            |        |      |    |
    ##     1   2   3                     4            5        6      7    8 
    ##                            1    2    3     4   5        6    7     8   
    #set ok [regexp $o $offset -> irel base type  iop ioperand sign ind idx]


    variable offsetopts
    variable indir_typemap
    $tree set $node rel 0 ;   # relative
    $tree set $node ind 0 ;   # indirect
    $tree set $node ir 0 ;    # indirect relative
    $tree set $node it {} ;   # indir_type
    $tree set $node ioi 0 ;   # indirect offset invert
    $tree set $node iir 0 ;   # indirect indirect relative 
    $tree set $node ioo + ;   # indirect_offset_op
    $tree set $node io 0 ;    # indirect offset
    advance w1 char
    if {$char eq {&}} {
	advance w1 char
	$tree set $node rel 1
    }

    if {$char eq {(}} {
	$tree set $node ind 1

	if {[advance w1] eq {&}} {
	    $tree set $node ir 1
	} else {
	    rewind 1
	}
	$tree set $node o [parseint $tree $node]

	# $char is used below if it's not "."
	if {[advance w1 char] eq {.}} {
	    advance w1 it
	    if {[dict exists $indir_typemap $it]} {
		set it [
		    dict get $indir_typemap $it]
	    } else {
		parseerror {bad indirect offset type}
	    }
	    advance w1 char
	} else {
	    set it long
	}
	$tree set $node it $it


	# The C implementation does this, so we will , too .
	if {$char eq {~}} {
	    advance w1 char
	    $tree set $node ioi 1
	}

	if {$char in $offsetopts} {
	    $tree set $node ioo $char
	    if {[advance w1] in {(}} {
		$tree set $node iir 1
	    } else {
		rewind 1
	    }
	    $tree set $node io [parseint $tree $node]
	    if {[$tree get $node iir]} {
		if {[advance w1] ne {)}} {
		    parseerror {
			expected closing parenthesis for indirect indirect offset offset
		    }
		}
	    }
	    advance w1 char
	}

	if {$char ne {)}} {
	    parseerror {
		expected close parenthesis for indirect offset 
	    }
	}
    } else {
	rewind 1
	$tree set $node o [parseint $tree $node]
    }
}

proc ::fileutil::magic::cfront::parseoffsetmod {tree node} {
    advance w1 char
    if {$char eq {~}} {
	$tree set $node offset_invert 1
	advance w1 char
    } else {
	$tree set $node offset_invert 0
    }
    switch $char {
	+ - - - * - / - % - & - | - ^ {
	    $tree set $node offset_mod_op $char
	    $tree set $node offset_mod [parseint $tree $node]
	}
	default {
	    $tree set $node offset_mod_op {}
	    $tree set $node offset_mod {}
	    rewind 1
	    # no offset modifier
	}
    }
}

proc ::fileutil::magic::cfront::parsemsg {tree node} {
    advance w
    set line [$tree get $node line]
    set cursor [$tree get $node cursor]
    ##leave \b in the message for [emit] to parse
    #regexp -start $cursor {\A(\b|\\b)?(.*)$} $line match b line
    #if {$b ne {}} {
    #    $tree set $node space 0
    #} else {
    #    $tree set $node space 1
    #}
    set line [string range $line $cursor end]
    $tree set $node desc $line
}

# process a magic file
proc ::fileutil::magic::cfront::process {tree file {maxlevel 10000}} {
    variable level	;# level of line
    variable linenum	;# line number

    set level  0

    set linenum 0
    set records {}
    set rejected 0
    set script {}
    if {[$tree keyexists root files]} {
	set files [$tree get root files]
    } else {
	set files {}
    }
    set fileidx [llength $files] 
    if {$file in $files} {
	return -code error [list {already processed file} $file]
    }
    lappend files $file
    $tree set root files $files
    $tree set root level -1
    set node root
    ::fileutil::foreachLine line $file {
   	incr linenum
	# Only trim the left side . White space on the the right side could be
	# part of an escape sequence , and trimming would munge it .
   	set line [string trimleft $line]
   	if {[string index $line 0] eq {#}} {
   	    continue	;# skip comments
   	} elseif {$line eq {}} {
   	    continue	;# skip blank lines
   	} else {
   	    # parse line
	    if {[regexp {!:(\S+)\s*([^\s]*).*$} $line -> extname extdata]} {
		if {$rejected} {
		    continue
		}
		if {$node eq {root}} {
		    return -code error [list {malformed magic file}]
		}
		$tree set $node ext_$extname $extdata
	    } else {
		# calculate the line's level
		set unlevel [string trimleft $line >]
		set level   [expr {[string length $line] - [string length $unlevel]}]
		set line $unlevel
		if {$level > $maxlevel} {
		    return -code continue "Skip - too high a level"
		}
		if {$level > 0} {
		    if {$rejected} {
			continue
		    }
		    while {[$tree keyexists $node level] && [$tree get $node level] >= $level} {
			set node [$tree parent $node]
		    }
		    if {$level > [$tree get $node level]+1} {
			return -code error [
			    list {level more than one greater than parent level} \
				file $file linenum $linenum line $line]
		    }
		    set node [$tree insert $node end]
		} else {
		    set rejected 0
		    set node [$tree insert root end]
		    set node0 $node
		}
		$tree set $node file $fileidx
		$tree set $node line $line
		$tree set $node linenum $linenum
		$tree set $node level $level
		if {[catch {parseline $tree $node} cres copts]} {
		    set errorcode [dict get $copts -errorcode]
		    if {[lindex $errorcode 0] eq {fumagic} && [
			lindex $errorcode 1] eq {parse error}} {
			$tree delete $node0
			set rejected 1
			puts stderr [list Rejected {bad parse}]
			puts stderr [dict get $copts -errorinfo]
			continue	;# skip erroring lines
		    } else {
			return -options $copts $cres
		    }

		}
	    }
   	}

   	# collect some summaries
   	::fileutil::magic::cfront::Debug {
   	    variable types
   	    set types($type) [$tree get $node type]
   	    variable quals
   	    set quals($qual) [$tree get $node qual]
   	}

   	#puts $linenum level:$level offset:$offset type:$type
	#puts qual:$qual compare:$compare val:'$val' desc:'$desc'

    }
}


# compile up magic files or directories of magic files into a single recognizer.
proc ::fileutil::magic::cfront::compile {args} {
    set tree [tree]

    foreach arg $args {
   	if {[file type $arg] eq  {directory}} {
781
782
783
784
785
786
787

788

789

790

791





792
793
794
795















796
797






798
799
800
801









802
803

804
805
806
807
808

809
810
811



















































































































































































































































































































































































































































































































































































































































































812
813
814
815
816
817
818
819
820
821
822

823
824
825
826
827
828
829
172
173
174
175
176
177
178
179

180
181
182

183
184
185
186
187
188
189




190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212




213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228

229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910







+
-
+

+
-
+

+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+


+




-
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











+








    ::fileutil::magic::cfront::Debug {puts [treedump $t]}
    #set tcl [run $script]

    return [list $named $tests]
}


proc ::fileutil::magic::cfront::generate {namespace args} {
proc ::fileutil::magic::cfront::generate args {

    set indent {}
    set pspace [namespace qualifiers $namespace]
    set pline {}

    while {[llength $args]} {
	set args [lassign $args[set args {}] key]
	switch $key {
	    compressed {
		set args [lassign $args[set args {}] val]
    if {$pspace eq ""} {
	return -code error "Cannot generate recognizer in the global namespace"
    }

		if {$val} {
		    set indent {}
		    set pline {}
		} else {
		    set indent \t
		    set pline \n
		}
	    }
	    -- break
	    default {
		error [list {unknown argument}]
	    }
	}
    }

    lassign [compile {*}$args] named tests

    append script "variable named {\n"
	dict for {key val} $named {
	    append script "${indent}[list $key]"
		append script "$pline${indent}${indent}[list [string map [
		    list \n \n${indent}] $val]]\n"
	    }
    set script "namespace eval [list $namespace] {
	variable named [list $named]
	variable tests [list $tests]
    }"
    append script "$pline}\n"

    append script "proc analyze {} {\n"
	    foreach item $tests {
		append script "${indent}[string map [
		    list \n \n${indent}] $item]\n"
	    }
    append script "$pline}\n"

    return $script 
}


proc ::fileutil::magic::cfront::install args {
    foreach arg $args {
	set path [file tail $arg]
	eval [generate ::fileutil::magic::/$path $arg]
	eval [generate compressed 1 -- ::fileutil::magic::/$path $arg]
    }
    return
}


proc ::fileutil::magic::cfront::parseerror args {
    upvar node node tree tree
    set cursor [$tree get $node cursor]
    set line [$tree get $node line]
    set files [$tree get root files]
    set file [lindex $files [$tree get $node file]]
    return -code error -errorcode [list fumagic {parse error}] [
	list [lmap arg $args {string trim $arg}] \
	file $file \
	linenenum [$tree get $node linenum] \
	cursor $cursor \
	line [list \
	    [string range $line 0 ${cursor}-1] \
	    [string range $line $cursor end]]]
}


proc ::fileutil::magic::cfront::parsewarning args {
    upvar node node tree tree
    catch {parseerror {*}$args} res options
    puts stderr [list parse warning $res]
    #puts stderr [dict get $options -errorinfo]
}


# parse an individual line
variable ::fileutil::magic::cfront::parsedkeys {
}
proc ::fileutil::magic::cfront::parseline {tree node} {
    variable parsedkeys
    set line [$tree get $node line]
    $tree set $node cursor 0 
    parseoffset $tree $node
    parsetype $tree $node
    parsetest $tree $node
    parsemsg $tree $node

    set record [$tree getall $node]
    foreach key $parsedkeys {
	if {![dict exists $record $key]} {
	    return -code error [list {missing key} $key]
	}
    }
    ::fileutil::magic::cfront::Debug {
   	puts [list parsed $record]
    }
}


proc ::fileutil::magic::cfront::parsefloat {tree node} {
    set line [$tree get $node line]
    set cursor [$tree get $node cursor]
    # If only [scan] had a @ conversion character like [binary scan]
    set line2 [string range $line $cursor end]
    if {[scan $line2 %e%n num count] < 0} {
	parseerror {invalid floating point number}
    }
    set cursor [expr {$cursor + $count}]
    $tree set $node cursor $cursor

    # These suffixes are not used in magic files
    #if {[regexp -start $cursor {\A([fFlL)} -> modifier]} {
    #    advance [string length $modifier]]
    #}
    return $num
}


proc ::fileutil::magic::cfront::parseint {tree node} {
    set line [$tree get $node line]
    set cursor [$tree get $node cursor]
    # If only [scan] had a @ conversion character like [binary scan]
    set line2 [string range $line $cursor end]
    if {[set scanres [scan $line2 %lli%n num n]] < 1} {
	parseerror [list {invalid number} $line2]
    }
    set cursor [expr {$cursor + $n}]
    $tree set $node cursor $cursor
    # Thse suffixes are not used in magic files
    #if {[regexp -start $cursor {\A([uU]?[lL]{1,2})} -> modifier]} {
    #    advance [string length $modifier]]
    #}
    return $num
}


proc ::fileutil::magic::cfront::parsetype {tree node} {
    variable types_numeric_all
    variable types_numeric_short
    variable types_string_all
    variable types_string_short
    variable types_notimplemented
    set line [$tree get $node line]
    set cursor [$tree get $node cursor]
    $tree set $node mod {}
    $tree set $node mand {}
    set num_or_string {
    }
    if {[regexp -start $cursor {\A\s*(\w+)} $line match type]} {
	advance [string length $match]
	if {$type in $types_numeric_all} {
	    if {[dict exists $types_numeric_short $type]} {
		set type [dict get $types_numeric_short $type]
	    }
	    $tree set $node type $type
	    parsetypenummod $tree $node
	} elseif {$type in $types_string_all} {
	    if {[dict exists $types_string_short $type]} {
		set type [dict get $types_string_short $type]
	    }
	    $tree set $node type $type
	    # No modifying operator for strings
	    parsetypemod $tree $node

	    if {$type eq {search} && [$tree get $node mand] eq {}} {
		parsewarning {search has no number}
		# set the same default that file(1) sets
		$tree set $node mand 100
	    }
	} elseif {$type in {default name use}} {
	    $tree set $node type $type
	} elseif {$type in $types_notimplemented} {
	    parseerror {type not implemented}
	} else {
	    parseerror {unknown type}
	}
    } else {
	parseerror {no type}
    }
}


proc ::fileutil::magic::cfront::parsetypemod {tree node} {
    # For numeric types , $mod is a list of modifiers and $mand is either a
    # number or the empty string .
    variable typemodifiers
    variable numeric_modifier_allowed
    set type [$tree get $node type]
    if {[advance 1 char] ne {/}} {
	rewind 1
	return
    }
    set res [dict create] 
    while 1 {
	if {[advance 1 char] eq {/}} {
	    continue
	}
	if {[string is space $char]} {
	    break
	}
	if {[dict exists $typemodifiers $type] && $char in [dict get $typemodifiers $type]} {
	    dict set res $char {}
	} elseif {$type in $numeric_modifier_allowed} {
	    rewind 1
	    if {[catch {parseint $tree $node} mand]} {
		# Whatever it is, it isn't a number.  Let the next parsing step
		# handle it .
		break
	    } else {
		$tree set $node mand $mand  ; # numeric modifier
	    }
	} else {
	    parseerror {bad modifier}
	}
    }
    $tree set $node mod [dict keys $res]
}


proc ::fileutil::magic::cfront::parsetypenummod {tree node} {
    variable typemap
    # For numeric types, $mod is an operator and $mand is a number
    set line [$tree get $node line]
    set type [$tree get $node type]
    set cursor [$tree get $node cursor]
    if {[regexp -start $cursor {\A([-&|^+*/%=])} $line match mod]} {
	advance [string length $match]
	$tree set $node mod $mod
	# {to do} {parse floats?}
	set mand [parseint $tree $node] ; # mod operand
	if {[info exists typemap($type)]} {
	    lassign $typemap($type) dummy scan

	    # the modifier for a numeric type is a number of the same
	    # type
	    binary scan [binary format $scan $mand] $scan mand
	}
	$tree set $node mand $mand 
    } else {
	$tree set $node mod {}
	$tree set $node mand {}
    }
}


proc ::fileutil::magic::cfront::parsestringval {tree node} {
    variable floattestops
    variable inttestops
    variable stringtestops
    advance w1 char 
    set val {}
    set nodetype [$tree get $node type]
    set line [$tree get $node line]
    while 1 {
	# break on whitespace or empty string
	if {[string is space $char] || $char eq {}} break
	switch $char [dict create  \
	    \\ {
		advance 1 char
		if {[string is space $char]} {
		    append val \\$char
		} else {
		    # extra backslashes because of interaction with glob
		    switch -glob -- $char {
			\\\\ {
			    append val {\\}
			} \t {
			    parsewarning {use \t instead of \<tab>}
			    append val \\t
			} > - < - & - ^ - = - ! - ( - ) - . {
			    if {$char in [list {*}$stringtestops \
				{*}$floattestops {*}$inttestops]} {
				parsewarning {no need to escape operators}
			    }
			    append val $char 
			} a - b - f - n - r - t - v {
			    append val \\$char
			} x {
			    set cursor [$tree get $node cursor]
			    if {[regexp -start $cursor \
				{\A([0-9A-Fa-f]{1,2})} $line match char2]} {
				advance [string length $match] 
				append val \\x$char2
			    } else {
				parseerror {malformed \x escape sequence}
			    }
			} [0-7] {
			    set cursor [$tree get $node cursor]
			    append val \\$char
			    if {[regexp -start $cursor \
				{\A([0-7]{1,2})} $line match char2]} {
				advance [string length $match] 
				append val $char2
			    }
			} default {
			    if {$nodetype eq {regex}} {
				if {$char ni {[ ] ( ) . * ? ^ $ | \{ \}}} {
				    parsewarning [list {no need to escape}]
				}
			    } elseif {[string is print $char]} {
				if {$char ni {< > & ^ = !}} {
				    parsewarning [list {no need to escape}]
				}
			    }
			    append val [tclescape $char]
			}
		    }
		}
	    } default {
		append val [tclescape $char]
	    }
	]
	advance 1 char
    }
    $tree set $node val $val
}


proc ::fileutil::magic::cfront::parsetest {tree node} {
    variable floattestops
    variable inttestops
    variable stringtestops
    variable types_numeric_real
    variable types_numeric_all
    variable types_string
    variable types_verbatim
    variable typemap
    set type [$tree get $node type]
    if {$type in $types_verbatim} {
	parsetestverbatim $tree $node
	return
    }
    $tree set $node compinvert 0
    set testinvert 0
    set comp ==
    advance w1 char
    if {$char eq {x}} {
	advance 1 char
	if {[string is space $char]} {
	    $tree set $node testinvert 0
	    $tree set $node comp x
	    $tree set $node val {}
	    return
	} else {
	    rewind 1
	}
    }

    if {$type in $types_string} {
	while 1  {
	    if {$char in $stringtestops} {
		if {$char eq {!}} {
		    set testinvert 1
		} else {
		    set comp $char
		    # Exclamation must precede any normal operator
		    break
		}
		advance w1 char
	    } else {
		rewind 1
		break
	    }
	}
	parsestringval $tree $node
    } elseif {$type in $types_numeric_all} {
	if {$type in $types_numeric_real} {
	    set ops $floattestops
	    set parsecmd parsefloat
	} else {
	    set ops $inttestops 
	    set parsecmd parseint
	}

	while 1 {
	    if {$char in $ops} {
		if {$char eq {~}} {
		    $tree set $node compinvert 1 
		} elseif {$char eq {!}} {
		    set testinvert 1
		} else {
		    set comp $char
		    # Exclamation and tilde must precede any normal operator
		    break
		}
		advance w1 char
	    } else {
		rewind 1
		break
	    }
	}
	set val [$parsecmd $tree $node]
	set scan [lindex $typemap([$tree get $node type]) 1]

	# get value in binary form, then back to numeric
	# this avoids problems with sign, as both values are
	# [binary scan]-converted identically
	binary scan [binary format $scan $val] $scan val
	$tree set $node val $val 
    } else {
	parseerror {don't know how to parse the test or this type}
    }
    switch $comp {
	= {
	    set comp ==
	}
    }
    # This facilitates Switch creation by [treegen1]
    if {$testinvert && ($comp eq {==})} {
	set comp !=
	set testinvert 0
    }
    $tree set $node testinvert $testinvert
    $tree set $node comp $comp 
}


proc ::fileutil::magic::cfront::parsetestverbatim {tree node} {
    switch [$tree get $node type] {
	name {
	    $tree set $node rel 1
	}
	use {
	    set cursor [$tree get $node cursor]
	    # order matters in regular expression : longest match must come
	    # first in parenthesized
	    if {[regexp -start $cursor {\A\s*(?:\\\^|\^)} [$tree get $node line] match]} {
		advance [string length $match]
		$tree set $node iendian 1
	    } else {
		$tree set $node iendian 0
	    }
	}

    }
    parsestringval $tree $node
}


proc ::fileutil::magic::cfront::parseoffset {tree node} {

    # Offset parser.
    # Syntax:
    #   ( ?&? number ?.[bslBSL]? ?[+-]? ?number? )

    # This was all fine and dandy, but didn't do spaces where spaces might
    # exist between lexical elements in the wild, and ididn't do offset
    # operators

    #set n {([-+]?[0-9]+|[-+]?0x[0-9A-Fa-f]+)[UL]*}

    ##"\\((&?)(${n})((\\.\[bslBSL])?)()(\[+-]?)(${n}?)\\)"
    #set o \
    #    "^(&?)${n}((?:\\.\[bslBSL])?)(?:(\[-+*/%&|^])${n})?(?:(\[+-])(\\()?${n}\\)?)?$"
    ##     |   |   |                     |            |        |      |    |
    ##     1   2   3                     4            5        6      7    8 
    ##                            1    2    3     4   5        6    7     8   
    #set ok [regexp $o $offset -> irel base type  iop ioperand sign ind idx]

    variable offsetopts
    variable indir_typemap
    $tree set $node rel 0 ;   # relative
    $tree set $node ind 0 ;   # indirect
    $tree set $node ir 0 ;    # indirect relative
    $tree set $node it {} ;   # indir_type
    $tree set $node ioi 0 ;   # indirect offset invert
    $tree set $node iir 0 ;   # indirect indirect relative 
    $tree set $node ioo + ;   # indirect_offset_op
    $tree set $node io 0 ;    # indirect offset
    advance w1 char
    if {$char eq {&}} {
	advance w1 char
	$tree set $node rel 1
    }

    if {$char eq {(}} {
	$tree set $node ind 1

	if {[advance w1] eq {&}} {
	    $tree set $node ir 1
	} else {
	    rewind 1
	}
	$tree set $node o [parseint $tree $node]

	# $char is used below if it's not "."
	if {[advance w1 char] in {. ,}} {
	    advance w1 it
	    if {[dict exists $indir_typemap $it]} {
		set it [
		    dict get $indir_typemap $it]
		    if {$char eq {.}} {
			set it u$it
		    } 
	    } else {
		parseerror {bad indirect offset type}
	    }
	    advance w1 char
	} else {
	    set it long
	}
	$tree set $node it $it


	# The C implementation does this, so we will , too .
	if {$char eq {~}} {
	    advance w1 char
	    $tree set $node ioi 1
	}

	if {$char in $offsetopts} {
	    $tree set $node ioo $char
	    if {[advance w1] in {(}} {
		$tree set $node iir 1
	    } else {
		rewind 1
	    }
	    $tree set $node io [parseint $tree $node]
	    if {[$tree get $node iir]} {
		if {[advance w1] ne {)}} {
		    parseerror {
			expected closing parenthesis for indirect indirect offset offset
		    }
		}
	    }
	    advance w1 char
	}

	if {$char ne {)}} {
	    parseerror {
		expected close parenthesis for indirect offset 
	    }
	}
    } else {
	rewind 1
	$tree set $node o [parseint $tree $node]
    }
}


proc ::fileutil::magic::cfront::parseoffsetmod {tree node} {
    advance w1 char
    if {$char eq {~}} {
	$tree set $node offset_invert 1
	advance w1 char
    } else {
	$tree set $node offset_invert 0
    }
    switch $char {
	+ - - - * - / - % - & - | - ^ {
	    $tree set $node offset_mod_op $char
	    $tree set $node offset_mod [parseint $tree $node]
	}
	default {
	    $tree set $node offset_mod_op {}
	    $tree set $node offset_mod {}
	    rewind 1
	    # no offset modifier
	}
    }
}


proc ::fileutil::magic::cfront::parsemsg {tree node} {
    advance w
    set line [$tree get $node line]
    set cursor [$tree get $node cursor]

    ##leave \b in the message for [emit] to parse
    #regexp -start $cursor {\A(\b|\\b)?(.*)$} $line match b line
    #if {$b ne {}} {
    #    $tree set $node space 0
    #} else {
    #    $tree set $node space 1
    #}

    set line [string range $line $cursor end]

    $tree set $node desc $line
}


# process a magic file
proc ::fileutil::magic::cfront::process {tree file {maxlevel 10000}} {
    variable level	;# level of line
    variable linenum	;# line number

    set level  0

    set linenum 0
    set records {}
    set rejected 0
    set script {}
    if {[$tree keyexists root files]} {
	set files [$tree get root files]
    } else {
	set files {}
    }
    set fileidx [llength $files] 
    if {$file in $files} {
	return -code error [list {already processed file} $file]
    }
    lappend files $file
    $tree set root files $files
    $tree set root level -1
    set node root
    ::fileutil::foreachLine line $file {
   	incr linenum
	# Only trim the left side . White space on the the right side could be
	# part of an escape sequence , and trimming would munge it .
   	set line [string trimleft $line]
   	if {[string index $line 0] eq {#}} {
   	    continue	;# skip comments
   	} elseif {$line eq {}} {
   	    continue	;# skip blank lines
   	} else {
   	    # parse line
	    if {[regexp {^\s*!:(\S+)\s*(.*?)\s*$} $line -> extname extdata]} {
		if {$rejected} {
		    continue
		}
		if {$node eq {root}} {
		    return -code error [list {malformed magic file}]
		}
		$tree set $node ext_$extname $extdata
	    } else {
		# calculate the line's level
		set unlevel [string trimleft $line >]
		set level   [expr {[string length $line] - [string length $unlevel]}]
		set line $unlevel
		if {$level > $maxlevel} {
		    return -code continue "Skip - too high a level"
		}
		if {$level > 0} {
		    if {$rejected} {
			continue
		    }
		    while {[$tree keyexists $node level] && [$tree get $node level] >= $level} {
			set node [$tree parent $node]
		    }
		    if {$level > [$tree get $node level]+1} {
			return -code error [
			    list {level more than one greater than parent level} \
				file $file linenum $linenum line $line]
		    }
		    set node [$tree insert $node end]
		} else {
		    set rejected 0
		    set node [$tree insert root end]
		    set node0 $node
		}
		$tree set $node file $fileidx
		$tree set $node line $line
		$tree set $node linenum $linenum
		$tree set $node level $level
		if {[catch {parseline $tree $node} cres copts]} {
		    set errorcode [dict get $copts -errorcode]
		    if {[lindex $errorcode 0] eq {fumagic} && [
			lindex $errorcode 1] eq {parse error}} {
			# don't delete the full node because the parts that
			# have been parsed so far might be useful
			#$tree delete $node0
			$tree delete $node
			set rejected 1
			puts stderr [list Rejected {bad parse}]
			puts stderr [dict get $copts -errorinfo]
			continue	;# skip erroring lines
		    } else {
			return -options $copts $cres
		    }

		}
	    }
   	}

   	# collect some summaries
   	::fileutil::magic::cfront::Debug {
   	    variable types
   	    set types($type) [$tree get $node type]
   	    variable quals
   	    set quals($qual) [$tree get $node qual]
   	}

   	#puts $linenum level:$level offset:$offset type:$type
	#puts qual:$qual compare:$compare val:'$val' desc:'$desc'

    }
}


proc ::fileutil::magic::cfront::rewind len {
    upvar node node tree tree
    set cursor [$tree get $node cursor]
    incr cursor -$len
    $tree set $node cursor $cursor
}


proc ::fileutil::magic::cfront::tclescape char {
	if {[string is space $char] || $char in [
	    list \# \{ \} \[  \] \" \$ \; \n]} {
	    append val \\
	}
	append val $char
	return $val
}


proc ::fileutil::magic::cfront::tree {} {
    set tree [::struct::tree]

    $tree set root path ""
    $tree set root otype Root
    $tree set root type root
    $tree set root named {}
    $tree set root message "unknown"
    return $tree
}


# ### ### ### ######### ######### #########
## Internal, debugging.

if {!$::fileutil::magic::cfront::debug} {
    # This procedure definition is optimized out of using code by the
    # core bcc. It knows that neither argument checks are required,

Changes to modules/fumagic/cgen.tcl.

25
26
27
28
29
30
31
32

33
34
35
36
37
38
39
25
26
27
28
29
30
31

32
33
34
35
36
37
38
39







-
+







# ### ### ### ######### ######### #########
## Requirements

package require Tcl 8.4
package require fileutil::magic::rt ; # Runtime core, for Access to the typemap
package require struct::list        ; # Our data structures.

package provide fileutil::magic::cgen 1.2.0
package provide fileutil::magic::cgen 1.3.0

# ### ### ### ######### ######### #########
## Implementation

namespace eval ::fileutil::magic {
    namespace export *
}
53
54
55
56
57
58
59




60
61
62
63
64
65
66
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70







+
+
+
+







    namespace export 2tree treedump treegen

   # Assumption : the parser folds the test inversion operator into equality and
   # inequality operators .
    variable offsetskey {
	type o rel ind ir it ioi ioo iir io compinvert mod mand
    }

    variable indent {}
    variable indents {}
    variable innamed 0
}


# Optimisations:

# reorder tests according to expected or observed frequency this
# conflicts with reduction in strength optimisations.
87
88
89
90
91
92
93




























94
95
96
97
98
99
100
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







#
# - String tests at same level over overlapping ranges can be
#   written as sub-string comparisons over the maximum range
#   this saves re-reading the same string from file.
#
# - common prefix strings will have to be guarded against, by
#   sorting string values, then sorting the tests in reverse length order.


proc ::fileutil::magic::cgen::LessIndent {} {
    variable indent
    variable indents
    set size [expr {[string length $indent] - 1}]
    if {[dict exists $indents $size]} {
	set indent [dict get $indents $size]
    } else {
	set indent [string repeat \t $size]
	dict set indents $size $indent
    }
    return
}

proc ::fileutil::magic::cgen::MoreIndent {} {
    variable indent
    variable indents
    set size [expr {[string length $indent] + 1}]
    if {[dict exists $indents $size]} {
        set indent [dict get $indents $size]
    } else {
        set indent [string repeat \t $size]
        dict set indents $size $indent
    }
    return
}


proc ::fileutil::magic::cgen::path {tree} {
    # Annotates the tree. In each node we store the path from the root
    # to this node, as list of nodes, with the current node the last
    # element. The root node is never stored in the path.

    $tree set root path {}
117
118
119
120
121
122
123


124
125
126
127
128
129
130
131
132
133
134

135
136
137
138
139
140
141

142
143
144
145
146
147
148
149
150






151
152
153
154
155
156
157
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175

176
177
178
179

180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197







+
+











+






-
+



-





+
+
+
+
+
+







    }
    lappend path [$tree index $node]
    $tree set $node path $path

    foreach name {type} {
	set $name [$tree get $node $name]
    }

    puts stderr [list frlaalm [$tree getall $node]]

    # Recursively creates and annotates a node for the specified
    # tests, and its sub-tests (args).


    # generate a proc call type for the type, Numeric or String
    variable ::fileutil::magic::rt::typemap

    switch -glob -- $type {
   	*byte* -
	*double* -
	*float* -
   	*short* -
   	*long* -
	*quad* -
   	*date* {
   	    $tree set $node otype N
   	}
   	clear - default - search - regex - *string* {
   	clear - search - regex - *string* {
   	    $tree set $node otype S
   	}
	name {
	    puts [list cromble otype [$tree getall $node]]
	    $tree set $node otype A
	}
	use {
	    $tree set $node otype U
	}
	default {
	    $tree set $node otype D
	}
	indirect {
	    $tree set $node otype T
	}
   	default {
   	    puts stderr "Unknown type: '$type'"
	    $tree set $node otype Unknown
   	}
    }

    # Stores the type determined above, and the arguments into
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
210
211
212
213
214
215
216




217
218
219
220
221
222
223







-
-
-
-







	tree_el $tree $child
    }
    optNum $tree root
    #optStr $tree root
    puts stderr "Script contains [llength [$tree children root]] discriminators"
    path $tree

    # Decoding the offsets, determination if we have to handle
    # relative offsets, and where. The less, the better.
    Offsets $tree

    return $tree
}

proc ::fileutil::magic::cgen::isStr {tree node} {
    return [expr {"S" eq [$tree get $node otype]}]
}

246
247
248
249
250
251
252



253



254
255
256
257
258
259
260
282
283
284
285
286
287
288
289
290
291

292
293
294
295
296
297
298
299
300
301







+
+
+
-
+
+
+







}

proc ::fileutil::magic::cgen::isNum {tree node} {
    return [expr {"N" eq [$tree get $node otype]}]
}

proc ::fileutil::magic::cgen::switchNSort {tree n1 n2} {

    # deal with the fact that [lsort] barfs if the result is larger than 32
    # bits
    return [expr {[$tree get $n1 val] - [$tree get $n1 val]}]
    set val1 [$tree get $n1 val]
    set val2 [$tree get $n2 val]
    expr {$val1 > $val2 ? 1 : $val1 < $val2 ? -1 : 0}
}

proc ::fileutil::magic::cgen::optNum {tree node} {
    variable offsetskey
    array set offsets {}

    # traverse each numeric element of this node's children,
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
359
360
361
362
363
364
365
















































366
367
368
369
370
371
372







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







	$tree set $switch path $path

	set level [$tree get [$tree parent $switch] level]
	$tree set $switch level [expr {$level+1}]
    }
}

proc ::fileutil::magic::cgen::Offsets {tree} {

    # Indicator if a node has to save field location information for
    # relative addressing. The 'kill' attribute is an accumulated
    # 'save' over the whole subtree. It will be used to determine when
    # level information was destroyed by subnodes and has to be
    # regenerated at the current level.

    $tree walk root -type dfs node {
	$tree set $node kill 0
	if {[$tree get $node otype] ne {Root} &&
	    ([$tree get $node rel] || [$tree get $node ir])} {
	    $tree set $node save 1
	} else {
	    $tree set $node save 0
	}
    }

    # We walk from the leafs up to the root, synthesizing the data
    # needed, as we go.
    $tree walk root -type dfs -order post node {
	if {$node eq {root}} continue

	# If the current node's parent is a switch, and the node has
	# to save, then the switch has to save. Because the current
	# node is not relevant during code generation anymore, the
	# switch is.

	if {[$tree get $node save]} {
	    # We save, therefore we kill.
	    $tree set $node kill 1
	    if {[$tree get [$tree parent $node] otype] eq {Switch}} {
		$tree set [$tree parent $node] save 1
	    }
	} else {
	    # We don't save i.e. kill, but we may inherit it from
	    # children which kill.

	    foreach c [$tree children $node] {
		if {[$tree get $c kill]} {
		    $tree set $node kill 1
		    break
		}
	    }
	}
    }
}


# Useful when debugging
proc ::fileutil::magic::cgen::stack {tree node} {
    set res {}
    set files [$tree get root files]
    while 1 {
	set s [dict create \
421
422
423
424
425
426
427
428

429
430
431
432
433
434
435
414
415
416
417
418
419
420

421
422
423
424
425
426
427
428







-
+







	    append result " " C([$tree get $node comp])
	}
	if {[$tree keyexists $node val]} {
	    append result " " V([$tree get $node val])
	}

	if {[$tree keyexists $node otype]} {
	    append result " " [$tree get $node otype]/[$tree get $node save]
	    append result " " [$tree get $node otype]
	}

	if {$depth == 1} {
	    set msg [$tree get $node desc]
	    set n $node
	    while {($n != {}) && ($msg == "")} {
		set n [lindex [$tree children $n] 0]
446
447
448
449
450
451
452


453
454
455
456
457
458
459
460
461
462
463
464


465
466
467
468
469
470
471
472
473
474
475
476
477
478

















479
480
481

482


483
484

485
486

487
488
489
490
491
492
493
494
495
496
497
498
499
500


501
502
503
504
505
506
507
508
509
510
511




512
513
514
515
516
517









518
519
520
521
522
523
524






525



526
527
528
529









530



531
532
533
534
535
536






537
538
539
540



541
542
543
544



545






546












547
548
549
550
551
552
553
554
555
556
557

558
559
560
561
562
563
564
565
566

567
568
569
570

571


572

573
574
575
576
577
578
579








580
581
582
583
584

585


586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601

























602
603
604
605




606







607
608
609










610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625


626
627
628
629
630
631
632
633
634
635
636
637
638








639
640
641
642
643
644
645
646
647
648
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453


454
455
456
457
458
459














460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480

481
482
483

484


485
486
487
488
489
490
491
492
493
494
495
496
497


498
499





500





501
502
503
504






505
506
507
508
509
510
511
512
513


514




515
516
517
518
519
520
521
522
523
524




525
526
527
528
529
530
531
532
533
534
535
536
537






538
539
540
541
542
543
544



545
546
547
548



549
550
551
552
553
554
555
556
557
558

559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580

581
582
583
584
585
586




587

588
589

590
591
592
593

594
595






596
597
598
599
600
601
602
603
604




605
606
607
608
















609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634



635
636
637
638
639
640
641
642
643
644
645
646



647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705







+
+






-
-




+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
-
+
+

-
+
-
-
+












-
-
+
+
-
-
-
-
-

-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-

-
-
-
-
+
+
+
+
+
+

+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+

+
+
+
-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
+
+
+

-
-
-
+
+
+

+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+










-
+





-
-
-
-
+
-


-
+

+
+
-
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
+

+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+
+
+

+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
















+
+













+
+
+
+
+
+
+
+










	#append result " <" [$tree getall $node] >
	append result \n
    }
    return $result
}

proc ::fileutil::magic::cgen::treegen {tree node} {
    variable indent
    variable innamed
    variable ::fileutil::magic::rt::typemap

    set result {} 
    set otype [$tree get $node otype]
    set level [$tree get $node level]

    set indent \n[string repeat \t [expr {$level > 0 ? $level-1 : 0}]]

    # Generate code for each node per its type.

    switch $otype {
	A {
	    incr innamed
	    try  {
	    set file [$tree get $node file]
	    set val [$tree get $node val]
	    if {[dict exists named $file$val]} {
		return -code error [list {name already exists} $file $val]
	    }
	    set aresult {}
	    foreach child [$tree children $node] {
		lappend aresult [treegen $tree $child]
	    }
	    set named [$tree get root named]
	    dict set named $file $val [join $aresult \n]
	    $tree set root named $named
	    return
	}
		set file [$tree get $node file]
		set val [$tree get $node val]
		if {[dict exists named $file$val]} {
		    return -code error [list {name already exists} $file $val]
		}
		set aresult {}
		foreach child [$tree children $node] {
		    lappend aresult [treegen $tree $child]
		}
		set named [$tree get root named]
		dict set named $file $val [join $aresult \n]
		$tree set root named $named
		return
	    } finally {
		incr innamed -1
	    }
	}
	U {
	    set file [$tree get $node file]
	    set val [$tree get $node val]
	    # generateOffset is expanded via subsitution
	    append result "U [list $file] [list $val]\n" 
	    append result "${indent}U [list $file] [list $val] [
		GenerateOffset $tree $node]\n" 
	}
	N -
	N - S - D {
	S {
	    set names {type mod mand testinvert compinvert comp val desc kill save path}
	    set names {type mod mand testinvert compinvert comp val desc path}
	    foreach name $names {
		set $name [$tree get $node $name]
	    }

	    set o [GenerateOffset $tree $node]

	    if {$val eq {}} {
		# If the value is the empty string, armor it.  Otherwise, it's
		# already been armored.
		set val [list $val]
	    }

	    if {$otype eq {N}} {
		if {$kill} {
	    switch $otype {
		N {
		    # We have to save field data for relative adressing under this
		    # leaf.
		    set type [list Nx $type]
		} else {
		    # Regular fetching of information.
		    set type [list N $type]
		}
		# $type and $o are expanded via substitution 
		append result "${indent}if \{\[$type $o [list $testinvert] [
		    list $compinvert] [list $mod] [list $mand] [
		    list $comp] $val\]\} \{>\n"
		    # $type and $o are expanded via substitution 
		    append result "${indent}if \{\[$type $o [list $testinvert] [
			list $compinvert] [list $mod] [list $mand] [
			list $comp] $val\]\} \{\n"
	    } elseif {$otype eq {S}} {
		switch $comp {
		    == {set comp eq}
		    != {set comp ne}
		}
		if {$kill} {
			MoreIndent
			append result "${indent}>\n"
		}
		S {
		    switch $comp {
			== {set comp eq}
			!= {set comp ne}
		    }

		    set type [list Sx $type]
		} else {
		    set type [list S $type]
		}
		append result "${indent}if \{\[$type $o [list $testinvert] [
		    list $mod] [list $mand] [list $comp] $val\]\} \{>\n"
	    }

		    append result "${indent}if \{\[$type $o [list $testinvert] [
			list $mod] [list $mand] [list $comp] $val\]\} \{\n"
			MoreIndent
			append result "${indent}>\n"
		}

		D {
		    set type [list D]
		    append result "${indent}if \{\[$type $o]\} \{\n" 
	    if {[$tree isleaf $node] && $desc ne {}} {
		append result "${indent}emit [list $desc]"
	    } else {
		if {$desc ne {}} {
			MoreIndent
			append result "${indent}>\n"

		}
	    }

	    MoreIndent

		if {[$tree isleaf $node] && $desc ne {}} {
		    append result "${indent}emit [list $desc]\n"
		} else {
		    if {$desc ne {}} {
			append result "${indent}emit [list $desc]\n"
		}
		foreach child [$tree children $node] {
		    append result [treegen $tree $child]
		}
		#append result "\nreturn \$result"
	    }
		    }
		    foreach child [$tree children $node] {
			append result [treegen $tree $child]\n
		    }
		    #append result "\nreturn \$result"
		}

	    if {[$tree keyexists $node ext_mime]} {
		append result "${indent}mime [$tree get $node ext_mime]\n"
	    }
		if {[$tree keyexists $node ext_mime]} {
		    append result "${indent}mime [list [$tree get $node ext_mime]]\n"
		}

	    if {[$tree keyexists $node ext_ext]} {
		append result "${indent}ext [$tree get $node ext_ext]\n"
	    }
		if {[$tree keyexists $node ext_ext]} {
		    append result "${indent}ext [list [$tree get $node ext_ext]]\n"
		}

		if {[$tree keyexists $node ext_strength]} {
		    append result "${indent}strength [list [$tree get $node ext_strength]]\n"
		}

	    LessIndent

	    append result "\n<\}\n"
	    append result ${indent}<\n
	    LessIndent
	    append result ${indent}\}\n
	}
	T {
	    set desc [$tree get $node desc]
	    if {$desc ne {}} {
		append result "${indent}emit [list $desc]\n"
	    }
	    set o [GenerateOffset $tree $node]
	    set mod [$tree get $node mod]
	    append result "${indent}T $o [list $mod]\n"
	}
	Root {
	    foreach child [$tree children $node] {
		lappend result [treegen $tree $child]
		if {[lindex $result end] eq {}} {
		    set result [lreplace $result[set result {}] end end]
		}
	    }
	}
	Switch {
	    set names {o type compinvert mod mand kill save}
	    set names {o type compinvert mod mand}
	    foreach name $names {
		set $name [$tree get $node $name]
	    }
	    set o [GenerateOffset $tree $node]

	    if {$kill} {
		set fetch Nvx
	    } else {
		set fetch Nv
	    set fetch Nv
	    }

	    append fetch " $type $o [list $compinvert] [list $mod] [list $mand]"
	    append result "${indent}switch -- \[$fetch\] "
	    append result "${indent}switch \[$fetch\] \{\n"

	    MoreIndent

	    set scan [lindex $typemap($type) 1]
		set scan [lindex $typemap($type) 1]

	    set ckilled 0
	    foreach child [$tree children $node] {
		# See ::fileutil::magic::rt::rtscan
		if {$scan eq {me}} {
		    set scan I
		}
		foreach child [lsort -command [
		    list ::fileutil::magic::cgen::switchNSort $tree] [
			$tree children $node]] {

		    # See ::fileutil::magic::rt::rtscan
		    if {$scan eq {me}} {
			set scan I
		    }

		# get value in binary form, then back to numeric
		# this avoids problems with sign, as both values are
		# [binary scan]-converted identically
		binary scan [binary format $scan [$tree get $child val]] $scan val
		    set val [$tree get $child val]

		    if {[info exists lastval] && $lastval != $val} {
			LessIndent
		append result "$val \{>;"

		set desc [$tree get $child desc]
		if {[$tree isleaf $child] && $desc ne {}} {
		    append result "emit [list [$tree get $child desc]]"
		} else {
		    if {$desc ne {}} {
			append result "emit [list [$tree get $child desc]]\n"
		    }
		    foreach grandchild [$tree children $child] {
			append result [treegen $tree $grandchild]
		    }
		}
		if {[$tree keyexists $child ext_mime]} {
		    append result "${indent}mime [$tree get $child ext_mime]\n"
		}
			append result "${indent}\}\n"
		    } 

		    if {![info exists lastval] || $lastval != $val} {
			append result "${indent}$val \{\n"
			MoreIndent
		    }

		    append result "${indent}>\n"

		    MoreIndent

			set desc [$tree get $child desc]

			# emit, mime, and ext come first so that they are
			# picked up when child nodes produce results

			if {$desc ne {}} {
			    append result "${indent}emit [list $desc]\n"
			}

			if {[$tree keyexists $child ext_mime]} {
			    append result "${indent}mime [list [
				$tree get $child ext_mime]]\n"
			}

		if {[$tree keyexists $child ext_ext]} {
		    append result "${indent}ext [$tree get $child ext_ext]\n"
		}
			if {[$tree keyexists $child ext_ext]} {
			    append result "${indent}ext [list [
				$tree get $child ext_ext]]\n"
			}

			if {![$tree isleaf $child]} {
			    foreach grandchild [$tree children $child] {
				append result [treegen $tree $grandchild]\n
			    }
			}
		    LessIndent

		append result ";<\} "
	    }
	    append result "\n"
		    append result "${indent}<\n"

		    set lastval $val
		}

	    LessIndent
	    append result "${indent}\}\n"

	    LessIndent
	    append result "${indent}\}\n"
	}
    }
    return $result
}

proc ::fileutil::magic::cgen::GenerateOffset {tree node} {
    # Examples:
    # direct absolute:     45      -> 45
    # direct relative:    &45      -> [R 45]
    # indirect absolute:  (45.s+1) -> [I 45 s + 0 1]
    # indirect absolute (indirect offset):  (45.s+(1)) -> [I 45 s + 1 1]
    # relative indirect absolute:  &(45.s+1) -> [R [I 45 s + 0 1]]
    # relative indirect absolute (indirect offset):  &(45.s+(1)) -> [R [I 45 s + 1 1]]
    # indirect relative: (&45.s+1) -> [I [R 45] s op 0 1]
    # relative indirect relative: &(&45.s+1) -> [R [I [R 45] s + 0 1]]
    # relative indirect relative: &(&45.s+(1)) -> [R [I [R 45] s + 1 1]]

    variable innamed

    foreach v {o rel ind ir it ioi iir ioo io} {
	set $v [$tree get $node $v]
    }

    #foreach v {ind rel base itype iop ioperand iindir idelta} {
    #    set $v [$tree get $node $v]
    #}

    if {$ind} {
	if {$ir} {set o "\[R $o]"}
	set o "\[I $o [list $it] [list $ioi] [list $ioo] [list $iir] [list $io]\]"
    }

    # spec
    #   named instance direct offsets are relative to the offset of the
    #   previous matched entry
    if {$innamed} {
	set o "\[O $o]"
    }

    if {$rel} {
	set o "\[R $o\]"
    }
    
    return $o
}

# ### ### ### ######### ######### #########
## Ready for use.
# EOF

Changes to modules/fumagic/filetypes.tcl.

more than 10,000 changes

Changes to modules/fumagic/filetypes.test.

30
31
32
33
34
35
36

37
38
39
40
41

42
43
44
45
46
47
48
49

50
51
52
53
54
55
56


57
58
59
60
61
62
63


64
65
66
67
68
69
70


71
72
73
74
75
76
77


78
79
80
81
82
83
84

85
86
87
88
89
90
91

92
93
94
95
96
97
98

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

118
119
120
121
122
123

124

125
126
127
128
129
130
131
132


133
134
135
136
137
138
139


140
141
142
143
144
145
146


147
148
149
150
151
152
153
154
155
156

157
158
159
160
161
162
163


164
165
166
167
168
169
170


171
172
173
174
175
176
177

178
179
180
181
182
183
184

185
186
187
188
189
190
191


192
193
194
195
196
197
198


199
200
201
202
203
204

205

206
207
208
209
210
211

212

213
214
215
216
217
218
219
220


221
222
223
224
225
226
227

228
229

230
231
232
233
234
235












































236
237
238
239
240
241
242
243
244
245

246
247
248
249
250
251
252
253
254




















255
256
257
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

59
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74

75
76
77
78
79
80
81
82

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135

136
137
138
139
140
141
142
143

144
145
146
147
148
149
150
151

152
153
154
155
156
157
158
159

160
161
162
163
164
165
166
167
168
169
170

171

172
173
174
175
176

177
178
179
180
181
182
183
184

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

209
210
211
212
213
214
215
216

217
218
219
220
221
222
223
224
225

226
227
228
229
230
231
232
233

234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253

254
255
256
257
258
259

260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322

323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345







+





+








+






-
+
+






-
+
+






-
+
+






-
+
+







+







+







+



















+






+
-
+







-
+
+






-
+
+






-
+
+









-
+
-





-
+
+






-
+
+







+







+






-
+
+






-
+
+






+
-
+






+
-
+








+
+







+

-
+





-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










+








-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



}

# -------------------------------------------------------------------------
# Now the package specific tests....

set path [makeFile {} bogus]
removeFile bogus


test fumagic.filetype-1.1 {test file non-existance} {
    set res [catch {fileutil::magic::filetype $path} msg]
    list $res $msg
} [list 1 "file not found: \"$path\""]


test fumagic.filetype-1.2 {test file directory} {
    set f [makeDirectory fileTypeTest]
    set res [catch {fileutil::magic::filetype $f} msg]
    regsub {file[0-9]+} $msg {fileXXX} msg
    removeDirectory fileTypeTest
    list $res $msg
} {0 {directory application/x-directory {}}}


test fumagic.filetype-1.3 {test file empty} {
    set f [makeEmptyFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeEmptyFile
    list $res $msg
} {0 {}}
} {0 {empty {} {}}}


test fumagic.filetype-1.4 {test simple binary} {
    set f [makeBinFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeBinFile
    list $res $msg
} {0 {}}
} {0 {binary {} {}}}


test fumagic.filetype-1.5 {test elf executable} {
    set f [makeElfFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeElfFile
    list $res $msg
} {0 {{ELF 32-bit LSB executable, (SYSV)} {application x-executable} {}}}
} {0 {{ELF 32-bit LSB executable {*unknown arch 0x0*} SYSV} {application x-executable} {}}}


test fumagic.filetype-1.6 {test simple text} {
    set f [makeTextFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeTextFile
    list $res $msg
} {0 {}}
} {0 {text {} {}}}


test fumagic.filetype-1.7 {test script file} {
    set f [makeScriptFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeScriptFile
    list $res $msg
} {0 {{a {/bin/tclsh script text executable}} {} {}}}


test fumagic.filetype-1.8 {test html text} {
    set f [makeHtmlFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeHtmlFile
    list $res $msg
} {0 {{{HTML document text}} {text html} {}}}


test fumagic.filetype-1.9 {test xml text} {
    set f [makeXmlFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeXmlFile
    list $res $msg
} {0 {{XML {1.0 document text}} {text xml} {}}}


test fumagic.filetype-1.10 {test xml with dtd text} {
    set f [makeXmlDTDFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeXmlDTDFile
    list $res $msg
} {0 {{XML {1.0 document text}} {text xml} {}}}


test fumagic.filetype-1.11 {
	test PGP message. Their are multiple matches, and the longest match should
	carry greater weight, and thus be the one returned.  If the match is "PGP
	armored data message", this isn't happening.
} {
    set f [makePGPFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removePGPFile
    list $res $msg
} {0 {{{PGP message}} {application pgp} {}}}


test fumagic.filetype-1.12.0 {test binary graphic jpeg} {
    set f [makeJpegFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeJpegFile
    list $res $msg
} {0 {{{JPEG image data} {JFIF standard 1.02} {resolution DPI} {density 300x316} {segment length 16}} {image jpeg} {jpeg jpg jpe jfif}}}
} {0 {{{JPEG image data, JFIF standard 1.02, resolution (DPI), density 300x316, segment length 16}} {image jpeg} {jpeg jpg jpe jfif}}}


#the result should actually be 128x112, but current magic files indicate "byte" instead of "ubyte"
test fumagic.filetype-1.12.1 {test binary graphic jpeg} {
    set f [makeJpeg2File]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeJpeg2File
    list $res $msg
} {0 {{{JPEG image data, JFIF standard 1.02, resolution (DPI), density 300x316, segment length 16, thumbnail -128x112}} {image jpeg} {jpeg jpg jpe jfif}}}
} {0 {{{JPEG image data} {JFIF standard 1.02} {resolution DPI} {density 300x316} {segment length 16} {thumbnail -128x112}} {image jpeg} {jpeg jpg jpe jfif}}}


test fumagic.filetype-1.13 {test binary graphic gif} {
    set f [makeGifFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeGifFile
    list $res $msg
} {0 {{{GIF image data, version 89a,} {43 x} 64} {image gif} {}}}
} {0 {{{GIF image data} {version 89a} {43 x} 64} {image gif} {}}}


test fumagic.filetype-1.14 {test binary graphic png} {
    set f [makePngFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removePngFile
    list $res $msg
} {0 {{{PNG image data, 0 x} 0, 0-bit} {image png} {}}}
} {0 {{{PNG image data} {0 x} 0 0-bit} {image png} {}}}


#{To do} {implement a "wild guess" mode}
#test fumagic.filetype-1.14.1 {test binary graphic png} {
#    set f [makePngFile]
#    set res [catch {fileutil::magic::filetype $f} msg]
#    removePngFile
#    list $res $msg
#} {0 {PNG image data, CORRUPTED, PNG image data, CORRUPTED}}

# The file doesn't really provide a direntries value, so not sure what the

# result means here, but any number is good enough for this test.
test fumagic.filetype-1.15 {test binary graphic tiff} {
    set f [makeTiffFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeTiffFile
    list $res $msg
} {0 {{{TIFF image data, big-endian, direntries=19789}} {image tiff} {}}}
} {0 {{{TIFF image data} big-endian direntries=0} {image tiff} {}}}


test fumagic.filetype-1.16 {test binary pdf} {
    set f [makePdfFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removePdfFile
    list $res $msg
} {0 {{{PDF document, version 1.2}} {application pdf} {}}}
} {0 {{{PDF document} {version 1.2}} {application pdf} {}}}


test fumagic.filetype-1.17 {test text ps} {
    set f [makePSFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removePSFile
    list $res $msg
} {0 {{{PostScript document text}} {application postscript} {}}}


test fumagic.filetype-1.18 {test text eps} {
    set f [makeEPSFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeEPSFile
    list $res $msg
} {0 {{{PostScript document text}} {application postscript} {}}}


test fumagic.filetype-1.19 {test binary gravity_wave_data_frame} {
    set f [makeIgwdFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeIgwdFile
    list $res $msg
} {0 {}}
} {0 {binary {} {}}}


test fumagic.filetype-1.20 {test binary compressed bzip} {
    set f [makeBzipFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeBzipFile
    list $res $msg
} {0 {{{bzip2 compressed data, block size = 900k}} {application x-bzip2} {}}}
} {0 {{{bzip2 compressed data} {block size = 900k}} {application x-bzip2} {}}}


test fumagic.filetype-1.21 {test binary compressed gzip} {
    set f [makeGzipFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeGzipFile
    list $res $msg
} {0 {{{gzip compressed data} {reserved method} ASCII {original size 0}} {application x-gzip} {}}}
} {0 {{{gzip compressed data, reserved method, ASCII, last modified: 1}} {application x-gzip} {}}}


test fumagic.filetype-1.22 {test pstring} {
    set f [makeWsdlFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeWsdlFile
    list $res $msg
} {0 {{{PHP WSDL cache} {version 0x03} {created 7} uri hello source {some source} target_ns {and a target}} {} {}}}
} {0 {{{PHP WSDL cache,} {version 0x03, created 7, uri: "hello", source: "some source", target_ns: "and a target"}} {} {}}}

 
test fumagic.filetype-1.23 {regular expressions} {
    set f [makeCSourceFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeCSourceFile
    list $res $msg
} {0 {{{C source text}} {text x-c} {}}}


# XZ is the one format whose magic record is of type "ustring"
test fumagic.filetype-1.24 {ustring} {
    set f [makeXzFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removeXzFile
    list $res $msg
} {0 {{{XZ compressed data}} {application x-xz} {}}}


test fumagic.filetype-1.25 {
    tests negative relative offsets 
    tests negative relative offsets
} {
    set f [makePdf2File]
    set res [catch {fileutil::magic::filetype $f} msg]
    removePdf2File
    list $res $msg
} {0 {{{PDF document, version 1.3}} {application pdf} {}}}
} {0 {{{PDF document} {version 1.3}} {application pdf} {}}}


test fumagic.filetype-1.25.1 {
    matches and strengths
} {
    set f [makePdf2File]

    set chan [open $f]

    set matches {}

    try {
        file stat $f stats
        set finfo [array get stats]
        dict set finfo name $f

        set coro [coroutine [info cmdcount] \
            ::fileutil::magic::rt::new $finfo $chan \
		$::fileutil::magic::filetype::named [
        	list [namespace which ::fileutil::magic::filetype::analyze]]]
	set class [$coro]
        while 1 {
            lassign [$coro] weight result mimetype ext 
            dict update matches $weight weight {
        	lappend weight [list $result $mimetype $ext]
            }
        }
    } finally {
        close $chan
    }

    removePdf2File
    return $matches
} [list \
    5.0  [
	list [
	    list [
		list {tar archive V7} type {} \
		    %PDF-1.3 {mode 5} {uid ndobj} {gid xref} {size 870 00000 n} {seconds xref} {linkname xref} comment
	] {application x-tar} tar]
] \
    66.0 {{{{PDF document} {version 1.3}} {application pdf} {}} {{{PDF document} {version 1.3}} {application pdf} {}}}]


test fumagic.filetype-1.26 {
    Tests comparisons against the empty string when a file is malformed or
    missing data at specified offsets.
} {
    set f [makePeFile]
    set res [catch {fileutil::magic::filetype $f} msg]
    removePeFile
    list $res $msg
} {0 {{{MS-DOS executable}} {application x-dosexec} {}}}


test fumagic.filetype-1.27 {
    Tests indirect offsets, as well as the "default" test type. 
} {
    set f [makePe2File]
    set res [catch {fileutil::magic::filetype $f} msg]
    removePe2File
    list $res $msg
} {0 {{{PE32 executable} (GUI) {Intel 80386, for MS Windows}} {application x-dosexec} {}}}
} {0 {{{PE32 executable} {Unknown PE signature} 0x10ba GUI {Intel 80386} {for MS Windows}} {application x-dosexec} {}}}

if 0 {
	to do

	ebml and webm both have a belong at 440786851

		make sure this is handled correctly
}

test fumagic.filetype-1.28 {
    Tests the "indirect" type and typed interpretation of values to be AND'ed with
    a numeric value.
} {
    set f [makeMp3File]
    set res [catch {fileutil::magic::filetype $f} msg]
    removePeFile
    list $res $msg
} {0 {{{Audio file with ID3 version 2.3.0} contains {MPEG ADTS} {layer III} v1 {128 kbps} {44.1 kHz} JntStereo} {audio mpeg} {}}}


testsuiteCleanup
return

Changes to modules/fumagic/fumagic.testsupport.

36
37
38
39
40
41
42



43

44
45
46
47
48
49
50
36
37
38
39
40
41
42
43
44
45

46
47
48
49
50
51
52
53







+
+
+
-
+







	Bin    "\u0000" \
	Elf    [cat "\x7F" "ELF" "\x01\x01\x01\x00\x00" "\x00\x00\x00\x00\x00\x00\x00" "\x02\x00"] \
	Bzip   "BZh91AY&SY\x01\x01\x01\x00\x00" \
	Gzip   "\x1f\x8b\x01\x01\x01\x00\x00" \
	Jpeg   [cat "\xFF\xD8\xFF\xE0\x00\x10JFIF" "\x00\x01\x02\x01\x01\x2c\x01\x3c"] \
	Jpeg2   [cat "\xFF\xD8\xFF\xE0\x00\x10JFIF" "\x00\x01\x02\x01\x01\x2c\x01\x3c\x80\x70"] \
	Gif    "GIF89a\x2b\x00\x40\x00\xf7\xff\x00" \
	Mp3    [binary format Hu* [join [string trim {
		    4944 3303 0000 0000 0000 fffb 9240
		}] {}]] \
	Png    "\x89PNG\x0D\x0A\x1A\x0A" \
	Png    "\x89PNG\x0D\x0A\x1A\x0A\x00\x00\x00\x0DIHDR" \
	PngMalformed "\x89PNG\x00\x01\x02\x01\x01\x2c" \
	Tiff   "MM\x00\*\x00\x01\x02\x01\x01\x2c" \
	Pdf    "%PDF-1.2 \x00\x01\x02\x01\x01\x2c" \
	Pdf2   {%PDF-1.3 %âãÏÓ
25 0 obj <<  /Linearized 1  /O 29  /H [ 1948 443 ]  /L 64573  /E 41907  /N 3  /T 63955  >>  endobj                                                           xref 25 67  0000000016 00000 n
0000001687 00000 n
0000001800 00000 n

Changes to modules/fumagic/pkgIndex.tcl.

1
2
3
4
5
6
7

8
9
10
11


12
13
14
1
2
3
4
5
6

7
8
9


10
11
12
13
14






-
+


-
-
+
+



if {![package vsatisfies [package provide Tcl] 8.6]} {return}

# Recognizers
package ifneeded fileutil::magic::filetype 2.0 [list source [file join $dir filetypes.tcl]]

# Runtime
package ifneeded fileutil::magic::rt 2.0 [list source [file join $dir rtcore.tcl]]
package ifneeded fileutil::magic::rt 3.0 [list source [file join $dir rtcore.tcl]]

# Compiler packages
package ifneeded fileutil::magic::cgen   1.2.0 [list source [file join $dir cgen.tcl]]
package ifneeded fileutil::magic::cfront 1.2.0 [list source [file join $dir cfront.tcl]]
package ifneeded fileutil::magic::cgen   1.3.0 [list source [file join $dir cgen.tcl]]
package ifneeded fileutil::magic::cfront 1.3.0 [list source [file join $dir cfront.tcl]]



Changes to modules/fumagic/rtcore.man.

15
16
17
18
19
20
21
22
23
24
25

26
27
28
29
30
31

32
33
34
35
36

37
38
39
40
41

42
43
44
45

46
47
48
49
50
51
52
53
54

55
56

57

58
59
60


61

62

63
64
65


66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108


109
110
111
112
113
114
115
116
117

118

119
120


121

122
123

124

125

126
127
128
129
130
131


132
133
134
135
136
137
138
139
140
141


142
143
144
145



146
147
148
149
150
151


152
153
154

155
156
157
158
159
160
161
162
163
164
165
166
167
168
169


170
171
172
173
174
175

176
177
178
179
180

181
182
183
184
185
186
187
188
189
190
191
192
193

194
195
196

197
198
199
200
201

202
203

204
205
206
207
208
209
210



211
212
213
214
215
216
217
218
219
220
221

222
223
224
225
226
227
228
15
16
17
18
19
20
21




22
23
24
25
26
27
28
29
30
31
32


33

34

35

36
37



38


39

40




41
42

43

44
45


46
47
48
49

50

51

52
53
54

55
56
57
58
59
60






















61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77





78

79
80
81


82
83

84
85

86
87
88

89


90



91
92
93

94



95



96
97
98



99
100
101

102
103
104


105
106
107
108

109
110
111
112
113
114
115
116
117

118





119
120

121




122





123


124

125





126
127

128
129
130

131





132
133

134
135

136
137
138


139
140
141
142
143
144




145
146
147

148
149
150
151
152
153
154
155







-
-
-
-
+






+



-
-
+
-

-

-
+

-
-
-
+
-
-

-

-
-
-
-
+

-
+
-
+

-
-
+
+

+
-
+
-

-
+
+

-






-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-













+
+


-
-
-
-
-

-
+

+
-
-
+
+
-
+

-
+

+
-
+
-
-

-
-
-
+
+

-

-
-
-

-
-
-
+
+

-
-
-
+
+
+
-



-
-
+
+


-
+








-

-
-
-
-
-
+
+
-

-
-
-
-
+
-
-
-
-
-
+
-
-

-

-
-
-
-
-


-
+


-
+
-
-
-
-
-
+

-
+

-



-
-
+
+
+



-
-
-
-



-
+







[require Tcl 8.5]
[require fileutil::magic::rt [opt [vset VERSION]]]
[description]
[para]

This package provides the runtime core for file type recognition
engines written in pure Tcl and is thus used by all other packages in
this module, i.e. the two frontend packages
[package fileutil::magic::mimetypes] and

[package fileutil::magic::filetypes], and the two engine compiler
this module such as [package fileutil::magic::filetype] and the two compiler
packages [package fileutil::magic::cgen] and
[package fileutil::magic::cfront].

[section COMMANDS]

[list_begin definitions]


[call [cmd ::fileutil::magic::rt::>]] 

Shorthand for [cmd {incr level}].

Increment the level and perform related housekeeping
[call [cmd ::fileutil::magic::rt::<]] 

Shorthand for [cmd {incr level -1}].

[call [cmd ::fileutil::magic::rt::open] [arg filename]]
[call [cmd ::fileutil::magic::rt::<]] 

This command initializes the runtime and prepares the file
[arg filename] for use by the system.

Decrement the level and perform related housekeeping
This command has to be invoked first, before any other command of this
package.

[para]

The command returns the channel handle of the opened file as its
result.

[call [cmd ::fileutil::magic::rt::close]]
[call [cmd ::fileutil::magic::rt::new] [arg chan] [arg named] [arg analyze]]

This command closes the last file opened via
Create a new command which returns one description of the file each time it is
[cmd ::fileutil::magic::rt::open] and shuts the runtime down.
called, and a code of [arg break] when there are no more descriptions.

This command has to be invoked last, after the file has been dealt
with completely.
[arg chan] is the channel containing the data to describe.  The channel
configuration is then managed as needed.

[arg named] is a dictionary of named tests, as generated by
Afterward another invokation of [cmd ::fileutil::magic::rt::open]  is
[cmd fileutil::magic::cfront::compile].
required to process another file.

[para]
[arg test] is a command prefix for a routine composed of the list of commands
as returned by [cmd fileutil::magic::cfront::compile].

This command returns the empty string as its result.

[call [cmd ::fileutil::magic::rt::file_start] [arg name]]

This command marks the start of a magic file when debugging. It
returns the empty string as its result.

[call [cmd ::fileutil::magic::rt::result] [opt [arg msg]]]

This command returns the current result and stops processing.

[para]

If [arg msg] is specified its text is added to the result before it is
returned. See [cmd ::fileutil::magic::rt::emit] for the allowed
special character sequences.

[call [cmd ::fileutil::magic::rt::resultv] [opt [arg msg]]]

This command returns the current result.

In contrast to [cmd ::fileutil::magic::rt::result] processing
continues.

[para]

If [arg msg] is specified its text is added to the result before it is
returned. See [cmd ::fileutil::magic::rt::emit] for the allowed
special character sequences.

[call [cmd ::fileutil::magic::rt::emit] [arg msg]]

This command adds the text [arg msg] to the result buffer. The
message may contain the following special character sequences. They
will be replaced with buffered values before the message is added to
the result. The command returns the empty string as its result.

[list_begin definitions]
[def [const \\b]] This sequence is removed
[def [const %s]]  Replaced with the last buffered string value.
[def [const %ld]] Replaced with the last buffered numeric value.
[def [const %d]]  See above.
[def [const {${x:...?...}}]] Substitute one string if the file is executable, and
another string otherwise.
[list_end]

[comment [call [cmd ::fileutil::magic::rt::offset] [arg where]]]
[comment {
	Handling of complex offsets. Currently not implemented.
	Always returns zero.
}]

[call [cmd ::fileutil::magic::rt::Nv] [arg type] [arg offset] [opt [arg qual]]]
[call [cmd ::fileutil::magic::rt::O] [arg where]]

Produce an offset from [arg where], relative to the cursor one level up.
This command fetches the numeric value with [arg type] from the
absolute location [arg offset] and returns it as its result. The


fetched value is further stored in the numeric buffer.
[comment [call [cmd ::fileutil::magic::rt::R] [arg where]]]

[para]
Produce an offset from [arg where], relative to the offset one level up.

[call [cmd ::fileutil::magic::rt::Nv] [arg type] [arg offset] [
If [arg qual] is specified it is considered to be a mask and applied
    arg compinvert] [arg comp] [arg expected]]
to the fetched value before it is stored and returned. It has to have
the form of a partial Tcl bit-wise expression, i.e.

[example {
	& number
}]
A limited form of [cmd ::fileutile::magic::rt::N] that only checks for
equality and can't be told to invert the test.

For example:

[example {
	Nv lelong 0 &0x8080ffff
}]

For the possible types see section [sectref {NUMERIC TYPES}].

[call [cmd ::fileutil::magic::rt::N] [arg type] [arg offset] [arg comp] [arg val] [opt [arg qual]]]
[call [cmd ::fileutil::magic::rt::N] [arg type] [arg offset] [arg testinvert] [
    arg compinvert] [arg mod] [arg mand] [arg comp] [arg expected]]

This command behaves mostly like [cmd ::fileutil::magic::rt::Nv],
except that it compares the fetched and masked value against [arg val]
as specified with [arg comp] and returns the result of that
Fetch the numeric value with [arg type] from the absolute location
[arg offset], compare it with [arg expected] using [arg comp] as the comparision
operator,  and returns the result.
comparison.

[para]

The argument [arg comp] has to contain one of Tcl's comparison
operators, and the comparison made will be
The argument [arg comp] must be one of Tcl's comparison
operators.

[example {
	<val> <comp> <fetched-and-masked-value>
	<comp> <fetched-and-masked-value> <comp> <expected>
}]

[para]

The special comparison operator [const x] signals that no comparison
should be done, or, in other words, that the fetched value will always
match [arg val].

[call [cmd ::fileutil::magic::rt::Nvx] [arg type] [arg offset] [opt [arg qual]]]

This command behaves like [cmd ::fileutil::magic::rt::Nv], except that
it additionally remembers the location in the file after the fetch in
the calling context, for the current level, for later use by
[cmd ::fileutil::magic::rt::R].

[call [cmd ::fileutil::magic::rt::S] [arg type] [arg offset] [arg testinvert] [
    arg mod] [arg mand] [arg comp] [arg val]]
[call [cmd ::fileutil::magic::rt::Nx] [arg type] [arg offset] [arg comp] [arg val] [opt [arg qual]]]

This command behaves like [cmd ::fileutil::magic::rt::N], except that
it additionally remembers the location in the file after the fetch in
the calling context, for the current, for later use by
[cmd ::fileutil::magic::rt::R].
Like [cmd ::fileutil::magic::rt::N] except that it fetches and compares string

[call [cmd ::fileutil::magic::rt::S] [arg offset] [arg comp] [arg val] [opt [arg qual]]]

This command behaves like [cmd ::fileutil::magic::rt::N], except that
it fetches and compares strings, not numeric data. The fetched value
types , not numeric data.
is also stored in the internal string buffer instead of the numeric
buffer.

[call [cmd ::fileutil::magic::rt::Sx] [arg offset] [arg comp] [arg val] [opt [arg qual]]]

This command behaves like [cmd ::fileutil::magic::rt::S], except that
it additionally remembers the location in the file after the fetch in
the calling context, for the current level, for later use by
[cmd ::fileutil::magic::rt::R].

[call [cmd ::fileutil::magic::rt::L] [arg newlevel]]

This command sets the current level in the calling context to
Sets the current level in the calling context to
[arg newlevel]. The command returns the empty string as its result.

[call [cmd ::fileutil::magic::rt::I] [arg base] [arg type] [arg delta]]
[call [cmd ::fileutil::magic::rt::I] [arg offset] [arg it] [arg ioi] [arg ioo] [

This command handles base locations specified indirectly through the
contents of the inspected file. It returns the sum of [arg delta] and
the value of numeric [arg type] fetched from the absolute location
[arg base].
    arg iir] [arg io]]

[para]
Calculates an offset based on an initial offset and the provided modifiers.

For the possible types see section [sectref {NUMERIC TYPES}].

[call [cmd ::fileutil::magic::rt::R] [arg offset]]

This command handles base locations specified relative to the end of
the last field one level above.
Given an initial offset, calculates an offset relative to the cursor at the
next level up. The cursor is the position in the data one character after the
data extracted from the file one level up.

[para]

In other words, the command computes an absolute location in the file
based on the relative [arg offset] and returns it as its result. The
base the offset is added to is the last location remembered for the
level in the calling context.

[call [cmd ::fileutil::magic::rt::U] [arg fileindex] [arg name]]

Use a named test script at the current level.
Add a level and use a named test script.

[list_end]

[section {NUMERIC TYPES}]

[list_begin definitions]
[def [const byte]]    8-bit integer

Changes to modules/fumagic/rtcore.tcl.

1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26



27
28
29
30
31
32
33

34

35
36
37
38
39
40
41
42

43
44
45
46
47
48
49
50



51
52
53
54
55
56
57


58


59




60
61
62









63















64
65
66



67
68
69
70



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

91
92
93
94
95
96





97
98
99
100


101
102
103
104
105
106
107

108
109
110
111
112
113
114
115
116

117
118
119
120

121



122

123








124
125

126









127





























128
129









130
131
132
133
134
135
136
137
138
139
140
141
142
143
144






145
146
147
148
149
150
151
152

153
154
155
156
157
158

159
160
161

162
163
164
165

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185














186
187
188
189
190
191
192
193
194
195
196
197
198
199





























































200
201
202
203
204
205
206

























207
208
209

210
211
212
213
214
215
216
217
218
219



220
221
222
223
224
225
226
227
228
229
230
1
2
3
4

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23



24
25
26
27
28
29
30
31

32
33

34
35
36
37
38
39
40
41

42
43
44
45
46
47



48
49
50
51
52
53
54
55

56
57
58

59
60
61
62
63
64
65



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

123
124





125
126
127
128
129




130
131







132


133

134




135
136
137
138
139
140
141
142
143
144

145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216





217
218
219
220
221
222



223
224
225


226






227



228




229



230









231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251














252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314





315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346







347
348
349

350
351

352
353
354
355
356
357
358




-


+
















-
-
-
+
+
+





-

+
-
+







-
+





-
-
-
+
+
+





-

+
+
-
+
+

+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+




+
+
+



















-
+

-
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
-
-
-
-
-
-
-
+
-
-

-

-
-
-
-
+




+

+
+
+
-
+

+
+
+
+
+
+
+
+


+

+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
+
+
+
+
+
+
+
+










-
-
-
-
-
+
+
+
+
+
+
-
-
-



-
-
+
-
-
-
-
-
-
+
-
-
-
+
-
-
-
-
+
-
-
-

-
-
-
-
-
-
-
-
-







+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+



-
-
-
-
-
-
-
+
+
+
-


-







# rtcore.tcl --
#
#	Runtime core for file type recognition engines written in pure Tcl.
#
# Copyright (c) 2016-2017 Poor Yorick     <[email protected]>
# Copyright (c) 2004-2005 Colin McCormack <[email protected]>
# Copyright (c) 2005      Andreas Kupries <[email protected]>
# Copyright (c) 2016-2018 Poor Yorick     <[email protected]>
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
# 
# RCS: @(#) $Id: rtcore.tcl,v 1.5 2005/09/28 04:51:19 andreas_kupries Exp $

#####
#
# "mime type recognition in pure tcl"
# http://wiki.tcl.tk/12526
#
# Tcl code harvested on:  10 Feb 2005, 04:06 GMT
# Wiki page last updated: ???
#
#####

#TODO  {
#    {Required Functionality} {
#	{implement full offset language} {
# TODO
#    Required Functionality
#	implement full offset language}
#	    done
#
#	    by pooryorick
#
#	    time {2016 06}
#	}
#
#
#	{implement pstring (pascal string, blerk)} {
#	implement pstring (pascal string)
#	    done
#
#	    by pooryorick
#
#	    time {2016 06}
#}
#
#	{implement regex form (blerk!)} {
#	implement regex form
#	    done
#
#	    by pooryorick
#
#	    time {2016 06}
#	}

#	{implement string qualifiers} {
#
#
#	implement string qualifiers
#	    done
#	    
#	    by pooryorick
#
#	    time {2016 06}
#	}
#
#	implement correct handling of date types
#
#	{finish implementing the indirect type}
#	finish implementing the indirect type} 
#	    done
#
#	    by pooryorick
#
#	    2018 08
#
#	{Maybe distinguish between binary and text tests, like file(n)}
#	
#	{process and use strength directives}
#	Maybe distinguish between binary and text tests, like file(n)
#
#	    done
#
#	    by pooryorick
#
#	    2018 08
#	
#	process and use strength directives
#
#	    done
#
#	    by pooryorick
#
#	    2018 08
#
#	handle the "indirect" type
#
#	    done
#
#	    by pooryorick
#
#	    2018 08
#
#
#    }
#}




# ### ### ### ######### ######### #########
## Requirements

package require Tcl 8.5




# ### ### ### ######### ######### #########
## Implementation

namespace eval ::fileutil::magic::rt {
    # Configuration flag. (De)activate debugging output.
    # This is done during initialization.
    # Changes at runtime have no effect.

    variable debug 0

    # The maximum size of a substring to inspect from the file in question 
    variable maxstring 64

    # The maximum length of any %s substitution in a resulting description is
    variable maxpstring 64

    variable regexdefaultlen 4096

    # Runtime state.
    # [*] The vast majority of magic strings are in the first 4k of the file.

    variable cursor 0      ; # The current offset
    variable fd     {}     ; # Channel to file under scrutiny
    variable found 0       ; # Whether the last test produced a match
    variable lfound {}     ; # For each level, whether a match was found
    variable level 0
    # Export APIs (full public, recognizer public)
    namespace export file_start result
    namespace export emit ext mime new offset strength \
	D Nv N O S Nvx Nx Sx L R T I U < >

    variable strbuf {}     ; # Input cache [*].
    variable cache         ; # Cache of fetched and decoded numeric
    array set cache {}	   ; # values.
    variable result {}     ; # Accumulated recognition result.
    namespace eval _ {}

    variable extracted     ; # The value extracted for inspection
    variable  last         ; # Behind last fetch locations,
    array set last {}      ; # per nesting level.
    variable weight 0      ; # The weight of the current part. 
                           ; # Basically string length of the contributing of
			   ; # the potentially-matching part.

}
    variable weighttotal 0 ; # The aggregate weight of the matching components of
			   ; # the current test.

    # [*] The vast majority of magic strings are in the first 4k of the file.

    # Export APIs (full public, recognizer public)
    namespace export open close file_start result
    namespace export emit ext mime offset Nv N S Nvx Nx Sx L R I resultv U < >
}


# ### ### ### ######### ######### #########
## Public API, general use.


proc ::fileutil::magic::rt::> {} {
    upvar #1 cursors cursors depth depth found found \
	level level lfound lfound strengths strengths \
	typematch typematch useful useful virtual virtual
    variable level
    set prevlevel $level
    incr level
    incr depth
    set cursors($level) $cursors($prevlevel)
    set strengths($level) 0
    set useful($level) 0
    set virtual($level) $virtual($prevlevel)
    set found 0
    dict set lfound $level 0
    return
}


proc ::fileutil::magic::rt::< {} {
    upvar #1 class class ext ext found found level level mime mime \
	result result strengths strengths typematch typematch useful useful

    if {$level == 1 && [llength $result]} {
	set leveln $level
	set weight 0
	while {$leveln >= 0} {
	    set weight [
		expr {$weight + $useful($leveln) + $strengths($leveln) + $typematch($leveln)}]
    variable level
	    incr leveln -1
	}

	foreach item $result[set result {}] {
	    set item [lmap {-> x ->} [regexp -all -inline \
		{(.+?)([[:punct:]][[:space:]]+|[:,+]*$)} $item[set item {}]] {

		regsub {"(.*)"} $x {\1} x
		regsub {'(.*)'} $x {\1} x
		regsub {\((.*)\)} $x {\1} x
		regsub {\{(.*)\}} $x {\1} x
		regsub {<(.*)>} $x {\1} x
		regsub {\[(.*)\]} $x {\1} x
		regsub {[[:space:]][[:space:]]+} $x { } x

		string trim $x
	    }]
	    lappend result {*}$item
	}

	yield [list $weight $result $mime $ext]
	set result {}
    }

    # $useful holds weight of the match at each level, Each weight is
    # basically length of the match.
    set useful($level) 0
    set strengths($level) 0

    incr level -1
}

    if {$level == 0} {
	set ext {}
	set found 0
	set mime {}
	set depth 0
    }
}


proc ::fileutil::magic::rt::classify {data} {
    set bin_rx {[\x00-\x08\x0b\x0e-\x1f]}
    if {[regexp $bin_rx $data] } {
        return binary
    } else {
        return text
    }
}

proc ::fileutil::magic::rt::mime value {
    upvar 1 mime mime
    set mime $value
}

proc ::fileutil::magic::rt::executable {} {
    upvar #1 finfo finfo
    if {![dict exists $finfo mode]} {
	return 0
    }
    expr {([dict get $finfo mode] & 0o111) > 0} 
proc ::fileutil::magic::rt::ext value {
    upvar 1 ext ext
    set ext $value
}


# open the file to be scanned
proc ::fileutil::magic::rt::open {file} {
proc ::fileutil::magic::rt::ext value {
    variable result {}
    variable extracted {} 
    variable strbuf
    variable fd
    variable cache

    upvar #1 ext ext
    set fd [::open $file]
    ::fconfigure $fd -translation binary
        
    set ext [split $value /]
    # fill the string cache
    set strbuf [::read $fd 4096]
	set class [classify $strbuf]

}
    # clear the fetch cache
    catch {unset cache}
    array set cache {}

    return $fd
}


proc ::fileutil::magic::rt::close {} {
    variable fd
    ::close $fd
    return
}

# mark the start of a magic file in debugging
proc ::fileutil::magic::rt::file_start {name} {
    ::fileutil::magic::rt::Debug {puts stderr "File: $name"}
}


proc ::fileutil::magic::rt::message msg {
    upvar #1 finfo finfo
    set ranges [regexp -all -inline -indices {\$\{([^\}]*)\}} $msg]
    foreach {orange irange} $ranges {
	lassign $irange first last
	set sub [string range $msg $first $last] 

	if {[regexp {^x\?([^:]*?):(.*)$} $sub -> tmsg fmsg]} {
	    set part [expr {[executable] ? $tmsg : $fmsg}]
	    set msg [string replace $msg[set line {}] {*}$orange $part]
	} else {
	    parseerror error [list {unrecognized variable in description}] 
	}
    }
# return the emitted result
proc ::fileutil::magic::rt::result {{msg {}}} {
    variable lfound {}
    variable found
    variable result
    variable weight
    variable weighttotal
    if {$msg ne {}} {emit $msg}
    set res [list $found $weighttotal $result]
    set found 0
    set weight 0
    set weighttotal 0
    set result {}
    return -code return $res 
    return $msg
}


proc ::fileutil::magic::rt::mime value {
    upvar #1 mime mime
    set mime [split [message $value] /]
}


proc ::fileutil::magic::rt::new {finfo chan named tests} {
    coroutine _::[info cmdcount] [list [
	namespace which coro]] $finfo $chan $named $tests
}

# level #1 of a coroutine
proc ::fileutil::magic::rt::coro {finfo chan named tests} {
    array set cache {}	    ; # Cache of fetched and decoded numeric
			    ; # values.

    ::fconfigure $chan -translation binary

    # fill the string cache
    set strbuf [::read $chan 4096]  ; # Input cache [*].
    set class [classify $strbuf]    ; # text or binary

    # clear the fetch cache
    catch {unset cache}
    array set cache {}

    set depth 0		; # depth of the current branch
    set ext {}
    set extracted {}    ; # The value extracted for inspection
    set found 1		; # Whether the last test produced a match
    set level 0
    set lfound {}	; # For each level, whether a match was found
    dict set lfound 0 1
    set mime {}
    set result {}	; # The accumulated recognition result that is
			; # in progress.

    array unset cursors	; # the offset just after the last matching bytes,
			; # per nesting level.

    array unset strengths ; #strengths at each level

    set virtual(0) 0	; # the virtual start of the file at each level

    set strengths(0) 0
    set typematch(0) 0

    yield [info coroutine]
    yield $class

    if {[string length $strbuf] == 0} {
	yield [list 0 empty {} {}]
    } else {
	{*}$tests
    }
    rename [info coroutine] {}
    return -code break
}

proc ::fileutil::magic::rt::resultv {{msg {}}} {
    try result on return result {
	return $result
    }
}
proc ::fileutil::magic::rt::strength {expr} {
    upvar #1 level level strengths strengths
    upvar 0 strengths($level) strength
    # this expr must not be braced
    set strength [expr double($strength) $expr]
}

proc ::fileutil::magic::rt::use {named file name} {
    if [dict exists $named $file $name] {
	set script [dict get $named $file $name]
    } else {
	dict for {file1 val} $named {
	    if {[dict exists $val $name]} {
		set script [dict get $val $name]
		break
	    }
	}
    }
    if {![info exists script]} {
	return -code error [list {name not found} $file $name]
    }
    return $script
}



# ### ### ### ######### ######### #########
## Public API, for use by a recognizer.


# emit a description 
proc ::fileutil::magic::rt::emit msg {
    variable found
    variable lfound
    variable level
    variable maxpstring
    variable extracted
    variable result
    variable weight
    upvar #1 extracted extracted found found level level lfound lfound \
	result result
    variable maxpstring
    variable weighttotal
    set found 1
    dict set lfound $level 1
    incr weighttotal $weight

    #set map [list \
    #    \\b "" \
    #    %c [apply {extracted {
    #        if {[catch {format %c $extracted} result]} {
    #    	return {}
    #        }
248
249
250
251
252
253
254



255
256
257
258
259
260
261
262
263









264
265
266
267
268
269

270
271
272

273
274
275


276
277

278
279


280
281

282

283
284
285

286
287
288
289
290

291
292
293


294
295
296
297




298
299
300
301






302

303
304
305
306
307
308
309
310




311
312
313
314
315
316
317
318
319
320
321
322
323
324
325




326
327
328
329

330
331
332
333

334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352

353
354
355
356
357
358
359
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393

394
395
396
397
398
399
400
401
402
403





404
405


406
407


408
409
410

411


412
413
414
415
416

417



418
419




420



421
422




423
424
425
426




427
428
429
430
431
432
433
434
435
436
437
438
439



440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465

466
467
468
469

470
471
472
473
474
475
476
477
478









479

480
481
482
483
484
485
486
487







+
+
+








-
+
+
+
+
+
+
+
+
+

-
-
-
-
-
+

-
-
+

-
-
+
+

-
+
-
-
+
+


+
-
+
-
-
-
+

-
-
-
-
+
-
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
+
+

+





-
-
-
+
+
+
+















+
+
+
+



-
+



-
+








-
-
-
-
-
-
-
-
-

-
+







    for {set i 0} {$i < $count} {incr i} {
	lappend arguments $extracted2
    }
    catch {set msg [format $msg {*}$arguments]}

    # Assumption: [regexp] leaves $msg untouched if it fails
    regexp {\A(\b|\\b)?(.*)$} $msg match b msg

    set msg [message $msg[set msg {}]]

    if {$b ne {} && [llength $result]} {
	lset result end [lindex $result end]$msg
    } else {
	lappend result $msg
    }
    return
}

proc ::fileutil::magic::rt::Nv {type offset compinvert mod mand} {
proc ::fileutil::magic::rt::D offset {
    upvar #1 found found
    expr {!$found}
}

proc ::fileutil::magic::rt::I {offset it ioi ioo iir io} {
    # Handling of base locations specified indirectly through the
    # contents of the inspected file.
    upvar #1 level level
    variable typemap
    variable extracted
    variable weight

    # unpack the type characteristics
    foreach {size scan} $typemap($type) break
    foreach {size scan} $typemap($it) break

    # fetch the numeric field from the file
    set extracted [Fetch $offset $size $scan]
    set offset [Fetch $offset $size $scan]

    if {$compinvert && $extracted ne {}} {
	set extracted [expr ~$extracted]
    if {[catch {expr {$offset + 0}}]} {
	return [expr {-1 * 2 ** 128}]
    }
    if {$mod ne {} && $extracted ne {}} {

	# there's a mask to be applied
	set extracted [expr $extracted $mod $mand]
    if {$ioi && ![catch {$offset + 0}]} {
	set offset [expr {~$offset}]
    }

    if {$iir} {
    ::fileutil::magic::rt::Debug {puts stderr "NV $type $offset $mod: $extracted"}
	set io [Fetch [expr {$offset + $io}] $size $scan]
    set weight [string length $extracted]
    return $extracted
}
    }

proc ::fileutil::magic::rt::use {named file name} {
    if [dict exists $named $file $name] {
	set script [dict get $named $file $name]
    } else {
    if {$ioo ne {}} {
	dict for {file val} $named {
	    if {[dict exists $val $name]} {
		set script [dict get $val $name]
	# no bracing this expression
	set offset [expr $offset $ioo $io]
		break
	    }
	}
    }
    }
    return $offset
}

    if {![info exists script]} {
	return -code error [list {name not found} $file $name]
    }
    return $script

proc ::fileutil::magic::rt::L newlevel {
    upvar #1 level level
    set level $newlevel
    # Regenerate level information in the calling context.
    return
}


# Numeric - get bytes of $type at $offset and $compare to $val
# qual might be a mask
proc ::fileutil::magic::rt::N {
    type offset testinvert compinvert mod mand comp val} {
    variable typemap
    variable extracted
    variable weight

    upvar #1 class class cursors cursors extracted extracted level level \
	typematch typematch useful useful
    variable typemap

    # unpack the type characteristics
    foreach {size scan} $typemap($type) break

    # fetch the numeric field
    set extracted [Fetch $offset $size $scan]
    if {$extracted eq {}} {

	# Rules like the following, from the jpeg file, imply that
	# in the absence of an extracted value, a numerical value of 
	# 0 should be used

	# From jpeg:
	    ## Next, show thumbnail info, if it exists:
	    #>>18    byte        !0      \b, thumbnail %dx
	#
	# pyk 2018-08-16:
	#    Not necessarily.  The failure to extract might cause the rule to
	#    be skipped.  Consider doing something different here.
	set extracted 0
    }

    # Would moving this before the fetch an optimisation ? The
    # Would moving this before the fetch be an optimisation ? The
    # tradeoff is that we give up filling the cache, and it is unclear
    # how often that value would be used. -- Profile!
    if {$comp eq {x}} {
	set weight 0
	set useful($level) 0
	# anything matches - don't care
	if {$testinvert} {
	    return 0
	} else {
	    return 1
	}
    }

    if {[string match $scan *me]} {
	set data [me4 $data]
	set scan I 
    }
    # get value in binary form, then back to numeric
    # this avoids problems with sign, as both values are
    # [binary scan]-converted identically (see [treegen1])
    binary scan [binary format $scan $val] $scan val

    if {$compinvert && $extracted ne {}} {
	set extracted [expr ~$extracted]
	set extracted [expr -$extracted]
    }

    # perform comparison
    if {$mod ne {}} {
	# there's a mask to be applied
	set extracted [expr $extracted $mod $mand]
    }
369
370
371
372
373
374
375
376







377
378
379
380
381
382
383
384
385
386
387
388

389
390
391
392
393
394






















































395
396
397



398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423














424
425
426
427
428
429
430
431



432
433
434
435
436
437
438
439
440
441



442
443
444
445
446
447
448
449
450
451
452
453
454


455
456

457
458

459


460



461
462
463
464
465
466
467
468

469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486

487
488

489
490

491
492
493
494
495
496
497
497
498
499
500
501
502
503

504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523






524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579

580
581
582
583
584
585


586
587
588
589
590
591
592
593
594
595
596
597
598
599







600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619


620
621
622
623
624
625
626
627
628
629
630


631
632
633
634
635
636
637
638
639
640
641
642
643
644


645
646
647

648
649

650
651
652
653

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

686
687

688
689
690
691
692
693
694
695







-
+
+
+
+
+
+
+












+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+
+
+



-
-














-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
-
+
+
+








-
-
+
+
+











-
-
+
+

-
+

-
+

+
+
-
+
+
+








+


















+

-
+

-
+







	}
	default {
	    #Should never reach this
	    return -code error [list {unknown comparison operator} $comp]
	}
    }
    # Do this last to minimize shimmering
    set weight [string length $extracted]
    set useful($level) [string length $extracted]

    if {$class eq {binary}} {
	set typematch($level)  1
    } else {
	set typematch($level)  0
    }

    ::fileutil::magic::rt::Debug {
	puts stderr "numeric $type: $val $t$comp $extracted / $mod - $c"
    }
    if {$testinvert} {
	set c [expr {!$c}]
	return $c 
    } else {
	return $c
    }
}


proc ::fileutil::magic::rt::S {type offset testinvert mod mand comp val} {
    variable cursor
    variable extracted
    variable fd
    variable level
    variable lfound
proc ::fileutil::magic::rt::Nv {type offset compinvert mod mand} {
    upvar #1 class class cursors cursors extracted extracted level level \
	offsets offsets useful useful
    variable typemap

    set offsets($level) $offset

    # unpack the type characteristics
    foreach {size scan} $typemap($type) break

    # fetch the numeric field from the file
    set extracted [Fetch $offset $size $scan]

    if {$compinvert && $extracted ne {}} {
	set extracted [expr ~$extracted]
    }
    if {$mod ne {} && $extracted ne {}} {
	# there's a mask to be applied
	set extracted [expr $extracted $mod $mand]
    }

    if {$class eq {binary}} {
	set typematch($level)  1
    } else {
	set typematch($level)  0
    }

    ::fileutil::magic::rt::Debug {puts stderr "NV $type $offset $mod: $extracted"}
    set useful($level) [string length $extracted]
    return $extracted
}


proc ::fileutil::magic::rt::O offset {
    # Handling of offset locations specified relative to the offset
    # last field one level up.
    upvar #1 offsets offsets level level
    upvar 0 offsets([expr {$level -1}]) base
    return [expr {$base + $offset}]
}


proc ::fileutil::magic::rt::R offset {
    # Handling of offset locations specified relative to the cursor one level
    # up.
    upvar #1 cursors cursors level level
    upvar 0 cursors([expr {$level -1}]) cursor
    return [expr {$cursor + $offset}]
}


proc ::fileutil::magic::rt::S {type offset testinvert mod mand comp val} {
    upvar #1 cursors cursors extracted extracted level level \
	lfound lfound useful useful
    variable maxstring
    variable regexdefaultlen
    variable weight

    upvar 0 cursors($level) cursor useful($level) used
    set cursor $offset

    # $compinvert is currently ignored for strings

    set weight [string length $val]

    switch $type {
	pstring {
	    set ptype B
	    set vincluded 0
	    # The last pstring type specifier wins 
	    foreach item $mod {
		if {$item eq {J}} {
		    set vincluded 1
		} else {
		    set ptype $item
		}
	    }
	    lassign [dict get {B {b 1} H {S 2} h {s 2} L {I 4} l {i 4}} $ptype] scan slength
	    set length [GetString $offset $slength]
	    set offset $cursor 
	    binary scan $length ${scan}u length
	    if {$vincluded} {
		set length [expr {$length - $slength}]
	    }
	    set extracted [GetString $offset $length]
	    set c [Smatch $val $comp $extracted $mod]
	    incr offset $slength
	    incr cursor $slength
	    set scanu ${scan}u
	    if {[binary scan $length $scanu length2]} {
		if {$vincluded} {
		    set length2 [expr {$length2 - $slength}]
		}
		set extracted [GetString $offset $length2]
		incr cursor [string length $extracted]
		    array get cursors]]
		set c [Smatch $val $comp $extracted $mod]
	    } else {
		set c 0
	    }
	}
	regex {
	    if {$mand eq {}} {
		set mand $regexdefaultlen 
	    }
	    set extracted [GetString $offset $mand]
	    if {[regexp $val $extracted match]} {
		set weight [string length $match]
	    if {[regexp -indices $val $extracted match indices]} {
		incr cursor [lindex $indices 1]
		set used [string length $match]
	        set c 1
	    } else {
	        set c 0
	    }
	}
	search {
	    set limit $mand
	    set extracted [GetString $offset $limit]
	    if {[string first $val $extracted] >= 0} {
		set weight [string length $val]
	    if {[set offset2 [string first $val $extracted]] >= 0} {
		set cursor [expr {$offset + $offset2 + [string length $val]}]
		set used [string length $val]
		set c 1
	    } else {
		set c 0
	    }
	} default {
	    # explicit "default" type, which is intended only to be used with
	    # the "x" pattern
	    set c [expr {[dict exists $lfound $level] ? ![dict get $lfound $level] : 1}]
	} default {
	    # get the string and compare it
	    switch $type bestring16 - lestring16 {
		set extracted [GetString $offset $maxstring]
		set extracted [string range $extracted 0 1]
		set extracted [GetString $offset [
		    expr {2 * [string length $val]}]]
		switch $type bestring16 {
		    binary scan $extracted Su extracted
		    binary scan $extracted Su* extracted
		} lestring16 {
		    binary scan $extracted Su extracted
		    binary scan $extracted su* extracted
		}

		foreach ordinal $extracted[set extracted {}] {
		set extracted [format %c $extracted]
		    append extracted [format %c $ordinal]
		}

	    } default {
		# If $val is 0, give [emit] something to work with .
		if {$val eq  "\0"} {
		    set extracted [GetString $offset $maxstring]
		} else {
		    set extracted [GetString $offset [string length $val]]
		}
	    }
	    incr cursor [string length $extracted]
	    set c [Smatch $val $comp $extracted $mod]
	}
    }


    ::fileutil::magic::rt::Debug {
	puts "String '$val' $comp '$extracted' - $c"
	if {$c} {
	    puts "offset $offset - $extracted"
	}
    }
    if {$testinvert} {
	return [expr {!$c}]
    } else {
	return $c
    }
}


proc ::fileutil::magic::rt::Smatch {val op string mod} {
    variable weight
    upvar #1 class class level level typematch typematch useful useful 
    if {$op eq {x}} {
	set weight 0
	set useful($level) 0
	return 1
    }

    if {![string length $string] && $op in {eq == < <=}} {
	if {$op in {eq == < <=}} {
	    # Nothing matches an empty $string.
	    return 0
531
532
533
534
535
536
537






538
539
540
541
542
543
544
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748







+
+
+
+
+
+







    }


    if {{T} in $mod} {
	set string [string trim $string[set string {}]]
	set val [string tolower $val[set val {}]]
    }

    if {$class eq {binary} || {b} in $mod} {
	set typematch($level)  0
    } else {
	set typematch($level)  1
    }

    set string [string range $string  0 [string length $val]-1]

    # The remaining code may assume that $string and $val have the same length
    # .

    set opnum [dict get {< -1 == 0 eq 0 != 0 ne 0 > 1} $op]
571
572
573
574
575
576
577


578

579
580
581
582
583
584
585
586
587
588
589
590
591
592


593
594

595
596
597
598




599
600
601
602
603

604
605
606
607
608
609
610
611
612



613
614
615

616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634



635
636
637
638

639
640
641
642
643
644
645
646
647
648
649


650
651
652
653
654
655
656
657
658
659
660
661

662
663
664
665

666
667
668
669
670
671
672
673
674
675
676
677
678
679

680
681
682
683
684
685
686
687





688
689

690
691

692
693
694
695
696
697






698
699
700
701
702
703
704
705
706
707
708
709











































710








711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726

727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774

775
776


777
778
779
780
781

782
783
784
785
786










787
788
789
790
791


792
793
794


795
796


797








798




799
800
801
802
803



804

805
806
807
808
809
810
811
812
813











814
815
816
817
818
819
820
821
822
823
824
825
826

827
828
829
830
831
832
833


834
835
836
837

838
839
840
841
842
843



844
845
846
847


848
775
776
777
778
779
780
781
782
783

784
785
786
787






788




789
790


791




792
793
794
795





796









797
798
799



800












801






802
803
804




805











806
807












808




809



810
811
812





813
814
815
816
817
818
819





820
821
822
823
824
825

826
827

828
829
830




831
832
833
834
835
836
837
838
839
840
841
842
843





844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906





907




















908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928


929



930
931


932
933
934
935
936
937
938
939





940
941
942
943
944
945
946
947
948
949


950


951
952



953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975

976
977
978
979
980
981
982








983
984
985
986
987
988
989
990
991
992
993




994
995
996
997

998
999
1000

1001

1002





1003
1004


1005

1006


1007
1008
1009
1010
1011
1012
1013
1014
1015
1016

1017
1018
1019







+
+
-
+



-
-
-
-
-
-

-
-
-
-
+
+
-
-
+
-
-
-
-
+
+
+
+
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-

-
-
-
-
-
-
+
+
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
+
-
-
-



-
-
-
-
-



+



-
-
-
-
-
+
+
+
+
+

-
+

-
+


-
-
-
-
+
+
+
+
+
+







-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+











-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-





















-
-

-
-
-

+
-
-
+
+





+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-

-
-
+
+
-
-
-
+
+


+
+

+
+
+
+
+
+
+
+

+
+
+
+



-

+
+
+

+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-




-



-
+
-

-
-
-
-
-
+
+
-
-

-
+
-
-




+
+
+



-
+
+

	}
    } else {
	set res [expr {[::string compare $string $val] == $opnum}]
    }
    if {$op in {!= ne}} {
	set res [expr {!$res}]
    }
    # use the extracted value here, not val, because in the case of
    # inequalities the extra information has weight
    set weight [string length $val]
    set useful($level) [string length $string]
    return $res
}

proc ::fileutil::magic::rt::Nvx {type offset compinvert mod mand} {
    variable typemap
    variable extracted
    variable last
    variable weight
    variable level

    # unpack the type characteristics
    foreach {size scan} $typemap($type) break
    set last($level) [expr {$offset + $size}]

proc ::fileutil::magic::rt::T {offset mod} {
    upvar #1 cursors cursors level level offsets offsets tests tests \
    set extracted [Nv $type $offset $compinvert $mod $mand]

	virtual virtual
    ::fileutil::magic::rt::Debug {puts stderr "NVx $type $offset $extracted $mod $mand"}
    return $extracted
}

    if {{r} in $mod} {
	set offset [expr {$cursors($level) + $offset}]
    }
    set newvirtual [expr {$virtual($level) + $offset}]
# Numeric - get bytes of $type at $offset and $compare to $val
# qual might be a mask
proc ::fileutil::magic::rt::Nx {
    type offset testinvert compinvert mod mand comp val} {

    >
    variable cursor
    variable typemap
    variable extracted
    variable last
    variable level
    variable weight

    set res [N $type $offset $testinvert $compinvert $mod $mand $comp $val]

	set virtual($level) $newvirtual
	{*}$tests
    <
    ::fileutil::magic::rt::Debug {
	puts stderr "Nx numeric $type: $val $comp $extracted / $qual - $c"
    }
}
    set last($level) $cursor
    return $res
}

proc ::fileutil::magic::rt::Sx {
    type offset testinvert mod mand comp val} {
    variable cursor
    variable extracted
    variable fd
    variable last
    variable level
    variable weight

    set res [S $type $offset $testinvert $mod $mand $comp $val]
    set last($level) $cursor
    return $res
}
proc ::fileutil::magic::rt::L {newlevel} {
    variable level $newlevel

proc ::fileutil::magic::rt::U {file name offset} {
    upvar #1 level level named named offsets offsets
    # Regenerate level information in the calling context.
    return
}

    set script [use $named $file $name]
proc ::fileutil::magic::rt::I {offset it ioi ioo iir io} {
    # Handling of base locations specified indirectly through the
    # contents of the inspected file.
    variable typemap
    foreach {size scan} $typemap($it) break
    if {$iir} {
	# To do:  this can't be right.
	set io [Fetch [expr $offset + $io] $size $scan]
    }
    set data [Fetch $offset $size $scan]

    set offsets($level) $offset
    >
    if {$ioi && [string is double -strict $data]} {
	set data [expr {~$data}]
    }
    if {$ioo ne {} && [string is double -strict $data]} {
	set data [expr $data $ioo $io]
    }
    if {![string is double -strict $data]} {
	set data -1
    }
    return $data
}

	::try $script
proc ::fileutil::magic::rt::R base {
    # Handling of base locations specified relative to the end of the
    # last field one level above.

    <
    variable last   ; # Remembered locations.
    variable level  ; # The level to get data from.
    return [expr {$last([expr {$level-1}]) + $base}]
}


proc ::fileutil::magic::rt::U {file name} {
    upvar named named
    set script [use $named $file $name]
    tailcall ::try $script
}

# ### ### ### ######### ######### #########
## Internal. Retrieval of the data used in comparisons.


# fetch and cache a numeric value from the file
proc ::fileutil::magic::rt::Fetch {where what scan} {
    variable cache
    variable cursor
    variable extracted
    variable strbuf
    variable fd
    upvar #1 cache cache chan chan cursors cursors extracted extracted \
	level level offsets offsets strbuf strbuf virtual virtual

    set where [expr {$virtual($level) + $where}]
    set offsets($level) $where 

    # Avoid [seek] errors
    # A negative offset means that an attempt to extract an indirect offset failed
    if {$where < 0} {
	set where 0
	return {}
    }
    # {to do} id3 length
    if {![info exists cache($where,$what,$scan)]} {
	::seek $fd $where
	set data [::read $fd $what]
	incr cursor [string length $data]
    if {[info exists cache($where,$what,$scan)]} {
	lassign $cache($where,$what,$scan) extracted cursor
    } else {
	::seek $chan $where
	set data [::read $chan $what]
	set cursor [expr {$where + [string length $data]}]
	set extracted [rtscan $data $scan]
	set cache($where,$what,$scan) [list $extracted $cursor]

	# Optimization: If we got 4 bytes, i.e. long we implicitly
	# know the short and byte data as well. Should put them into
	# the cache. -- Profile: How often does such an overlap truly
	# happen ?

    } else {
	lassign $cache($where,$what,$scan) extracted cursor
    }
    return $extracted
    }
    set cursors($level) $cursor 
    return $extracted
}


proc ::fileutil::magic::rt::GetString {offset len} {
    upvar #1 chan chan level level strbuf strbuf offsets offsets \
	virtual virtual
    # We have the first 1k of the file cached

    set offsets($level) $offset
    set offset [expr {$virtual($level) + $offset}]
    set end [expr {$offset + $len - 1}]
    if {$end < [string length $strbuf]} {
        # in the string cache, copy the requested part.
	try {
	    set string [::string range $strbuf $offset $end]
	} on error {tres topts} {
	    lassign [dict get $topts -errorcode] TCL VALUE INDEX
	    if {$TCL eq {TCL} && $VALUE eq {VALUE} && $INDEX eq {INDEX}} {
		set string {}
	    } else {
		return -options $topts $tres
	    }
	}
    } else {
	# an unusual one, move to the offset and read directly from
	# the file.
	::seek $chan $offset
	try {
	    # maybe offset is out of bounds
	    set string [::read $chan $len]
	} on error {tres topts} {
	    lassign [dict get $topts -errorcode] TCL VALUE INDEX
	    if {$TCL eq {TCL} && $VALUE eq {VALUE} && $INDEX eq {INDEX}} {
		set string {}
	    } else {
		return -options $topts $tres
	    }
	}
    }
    return $string
}


proc ::fileutil::magic::rt::me4 data {
	binary scan $data a4 chars
	set data [binary format a4 [lindex $chars 1] [
	lindex $chars 0] [lindex $chars 3] [lindex $chars 2]]
}


proc ::fileutil::magic::rt::rtscan {data scan} {
    if {$scan eq {me}} {
	set data [me4 $data]
	set scan I 
    }
    set numeric {}
    binary scan $data $scan numeric
    return $numeric
}

proc ::fileutil::magic::rt::me4 data {
	binary scan $data a4 chars
	set data [binary format a4 [lindex $chars 1] [
	lindex $chars 0] [lindex $chars 3] [lindex $chars 2]]
}


proc ::fileutil::magic::rt::GetString {offset len} {
    variable cursor
    # We have the first 1k of the file cached
    variable strbuf
    variable fd

    set end [expr {$offset + $len - 1}]
    if {$end < 4096} {
	# in the string cache, copy the requested part.
	set string [::string range $strbuf $offset $end]
    } else {
	# an unusual one, move to the offset and read directly from
	# the file.
	::seek $fd $offset
	set string [::read $fd $len]
    }
    set cursor [expr {$offset + [string length $string]}]
    return $string
}

# ### ### ### ######### ######### #########
## Internal, debugging.

if {!$::fileutil::magic::rt::debug} {
    # This procedure definition is optimized out of using code by the
    # core bcc. It knows that neither argument checks are required,
    # nor is anything done. So neither results, nor errors are
    # possible, a true no-operation.
    proc ::fileutil::magic::rt::Debug {args} {}

} else {
    proc ::fileutil::magic::rt::Debug {script} {
	# Run the commands in the debug script. This usually generates
	# some output. The uplevel is required to ensure the proper
	# resolution of all variables found in the script.
	uplevel 1 $script
	return
    }
}

# ### ### ### ######### ######### #########
## Initialize constants

namespace eval ::fileutil::magic::rt {
    # maps magic typenames to field characteristics: size (#byte),
    # binary scan format

# ### ### ### ######### ######### #########
    variable typemap
}
## Initializ package


proc ::fileutil::magic::rt::Init {} {
    variable typemap
    global tcl_platform

    # map magic typenames to field characteristics: size (#byte),
    # Set the definitions for all types which have their endianess
    # explicitly specified n their name.

    array set typemap {
	byte    {1 c}

    # Types without explicit endianess assume/use 'native' byteorder.
    # We also put in special forms for the compiler, so that it can use short
    # names for the native-endian types as well.

    # {to do} {Is ldate done correctly in the procedure?  What is its byte
    # order anyway?  Native?}
    
    foreach {type sig} {
	bedate  {4 S}
	beshort {2 S}
	leshort {2 s}
	bedouble {8 Q}
	belong  {4 I}
	lelong  {4 i}
	befloat {4 R}
	beid3 {4 n}
	bedate  {4 S}  ledate   {4 s}
	beldate {4 I}  leldate  {4 i}
	bedouble {8 Q}
	beldate {4 I}
	belong  {4 I}
	beqdate {8 W}
	beqldate {8 W}
	beqwdate {8 W}
	beqldate {8 W}
	bequad {8 W} 
	beshort {2 S}
	bestring16 {2 S}
	byte    {1 c}
	date {4 n}
	double {8 d}
	float {4 f}
	ldate {4 n}
	ledate   {4 n}
	ledouble {8 q}
	leid3 {4 nu}
	lefloat {4 f}
	leldate  {4 i}
	lelong  {4 i}
	leqdate {8 w}
	leqldate {8 w}
	lequad {8 w}
	lequad {8 w} 
	leqwdate {8 w}
	leshort {2 s}
	lestring16 {2 s}
	long  {4 n}
	medate  {4 me}
	meldate  {4 me}
	melong  {4 me}
	meldate  {4 me}
	lestring16 {2 s}
	bestring16 {2 S}

	long  {4 Q} date  {4 Q} ldate {4 Q}
	short {2 Y} quad {8 W} 
    }

	qdate {8 m}
	qdate {8 n}
	qldata {8 m}
	quad {8 m} 
	qwdate {8 m}
	short {2 t}
    } {
	set typemap($type) $sig
	lassign $sig size scan
	set typemap(u$type) [list $size ${scan}u]
    }
    # Now set the definitions for the types without explicit
    # endianess. They assume/use 'native' byteorder. We also put in
    # special forms for the compiler, so that it can use short names
    # for the native-endian types as well.

    # generate short form names
    foreach {n v} [array get typemap] {
	foreach {len scan} $v break
	#puts stderr "Adding $scan - [list $len $scan]"
	set typemap($scan) [list $len $scan]
    }

    # The special Q and Y short forms are incorrect, correct now to
    # Add the special Q and Y short forms using the proper native endianess.
    # use the proper native endianess.

    # {to do} {Is ldate done correctly in the procedure?  What is its byte
    # order anyway?  Native?}

    if {$tcl_platform(byteOrder) eq "littleEndian"} {
	array set typemap {Q {4 i} Y {2 s}
    if {$tcl_platform(byteOrder) eq {littleEndian}} {
	array set typemap {Q {4 i} Y {2 s} quad {8 w}}
	    short {2 s} long {4 i} quad {8 w}
	}
    } else {
	array set typemap {Q {4 I} Y {2 S}
	array set typemap {Q {4 I} Y {2 S} quad {8 W}}
	    short {2 S} long {4 I} quad {8 W}
	}
    }
}

::fileutil::magic::rt::Init



# ### ### ### ######### ######### #########
## Ready for use.

package provide fileutil::magic::rt 2.0
package provide fileutil::magic::rt 3.0

# EOF

Changes to modules/fumagic/tmc.

11
12
13
14
15
16
17
18

19
20
21

22
23
24

25
26
27
28
29
30
31
32
33

34
35
36
37
38
39
40
11
12
13
14
15
16
17

18
19
20

21
22
23

24
25
26
27
28
29
30
31
32

33
34
35
36
37
38
39
40







-
+


-
+


-
+








-
+







# (-)	Compilation of one or more files in magic(5) syntax into a
#	list of recognizers performing all the checks and mappings
#	encoded in them.
# 
# Command syntax
# --------------
# 
# Ad 1)	tmc namespace magic-file ?magic-file...?
# Ad 1)	tmc magic-file ?magic-file...?
#
#	Compile all magic files list of recognizers, generate a script which
#	assigns the recognizers to $namespace::tests and $namespace::named and
#	assigns the recognizers to $tests and $named and
#	write the script to stdout.
# 
# Ad 2)	tmc -merge tclfile namespace magic-file ?magic-file...?
# Ad 2)	tmc -merge tclfile magic-file ?magic-file...?
#
#	Same as (1), but does not write to stdout. Instead the part of
#	the 'tclfile' delineated by marker lines containing "BEGIN
#	GENERATED CODE" and "END GENERATED CODE" is replaced with the
#	generated code.

package require Tcl 8.5
set auto_path [linsert $auto_path 0 [file dirname [file normalize [info script]]]] ; # This directory
set auto_path [linsert $auto_path 0 [file dirname [lindex $auto_path end]]]]        ; # and the one above
set auto_path [linsert $auto_path 0 [file dirname [lindex $auto_path end]]]        ; # and the one above
#puts *\t[join $auto_path \n*\t]
package require fileutil::magic::cfront

# ### ### ### ######### ######### #########
## Internal data and status

namespace eval ::tmc {
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
73
74
75
76
77
78
79

80
81
82

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

101
102

103
104
105
106



107
108
109
110
111
112
113







-



-


















-
+

-




-
-
-







##

proc ::tmc::processCmdline {} {
    global argv

    variable output
    variable magic
    variable namespace

    set output ""
    set magic  {}
    set namespace ""

    # Process the options, perform basic validation.

    while {[llength $argv]} {
	set opt [lindex $argv 0]
	if {![string match "-*" $opt]} break
	if {$opt eq "-merge"} {
	    if {[llength $argv] < 2} Usage
	    set output [lindex $argv 1]
	    set argv   [lrange $argv 2 end]
	} else {
	    Usage
	}
    }

    # Additional validation, and extraction of the non-option
    # arguments.

    if {[llength $argv] < 2} Usage
    if {[llength $argv] < 1} Usage

    set namespace  [lindex $argv 0]
    set magic [lrange $argv 1 end]

    # Final validation across the whole configuration.

    if {$namespace eq ""} {
	ArgError "Illegal empty namespace name"
    }
    foreach m $magic {
	CheckInput $m {Magic file}
    }
    if {$output ne ""} {
	CheckTheMerge
    }
    return
128
129
130
131
132
133
134
135

136
137
138
139
140
141
142
122
123
124
125
126
127
128

129
130
131
132
133
134
135
136







-
+







# Both write their messages to stderr and then
# exit the application with status 1.
##

proc ::tmc::Usage {} {
    global argv0
    puts stderr "$argv0 wrong#args, expected:\
	    ?-merge iofile? namespace magic magic..."
	    ?-merge iofile? magic magic..."
    exit 1
}

proc ::tmc::ArgError {text} {
    global argv0
    puts stderr "$argv0: $text"
    exit 1
181
182
183
184
185
186
187




188

189

190
191
192
193
194
195
196
175
176
177
178
179
180
181
182
183
184
185

186
187
188
189
190
191
192
193
194
195







+
+
+
+
-
+

+







## Helper commands. File reading and writing.

proc ::tmc::Get {f} {
    return [read [set in [open $f r]]][close $in]
}

proc ::tmc::Write {f data} {
    while 1 {
	set tmp $f.tmc_[incr i]
	if {![file exists $tmp]} break 
    }
    puts -nonewline [set out [open $f w]] $data
    puts -nonewline [set out [open $tmp w]] $data
    close $out
    file rename -force $tmp $f
    return
}

# ### ### ### ######### ######### #########
## Configuation phase, validate command line.

::tmc::processCmdline
223
224
225
226
227
228
229
230
231
232
233
234
235

236
237
238

239
240
241
242
243
244
245
246
247
248
249
222
223
224
225
226
227
228

229
230
231
232

233

234

235
236
237
238
239
240
241
242
243
244
245
246







-




-
+
-

-
+











}

# ### ### ### ######### ######### #########
## Invoking the functionality.

if {[catch {
    # Read and process all input files.
    # Generate commands into a namespace.
    # Write the result either to stdout, or merge
    # into the specified output file.

    set tcl [eval [linsert $tmc::magic 0 \
	    fileutil::magic::cfront::generate \
	    fileutil::magic::cfront::generate compressed 0 --]]
	    $tmc::namespace]]

    if {$tmc::output eq ""} {
    if {$tmc::output eq {}} {
	puts stdout $tcl
    } else {
	::tmc::Merge $tmc::output \n${tcl}\n
    }
} msg]} {
    puts $::errorInfo
    ::tmc::ArgError $msg
}

# ### ### ### ######### ######### #########
exit

Changes to modules/htmlparse/htmlparse.man.

134
135
136
137
138
139
140
141

142
143
144
145
146
147
148
134
135
136
137
138
139
140

141
142
143
144
145
146
147
148







-
+







error if it sees incomplete HTML and has no place to store it to. This
makes sense for the normal mode. Only incomplete tags are detected,
not missing tags.  Optional, defaults to 'no variable'.

[list_end]

[list_begin definitions]
[para]

[def [emph "Interface to the command prefix"]]

In normal mode the parser will invoke the command prefix with four
arguments appended. See [cmd ::htmlparse::debugCallback] for a
description.

[para]

Changes to modules/httpd/build/build.tcl.

1
2
3




4

5
6

7
8

9
10
11

12
13
14
15
16
17
18
1
2
3
4
5
6
7

8
9
10
11
12

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



+
+
+
+
-
+


+

-
+



+







set srcdir [file dirname [file normalize [file join [pwd] [info script]]]]
set moddir [file dirname $srcdir]

if {[catch {package require clay 0.3}]} {
  source [file join $moddir .. clay build doctool.tcl]
}
::clay::doctool create AutoDoc
set version 4.2.0
set version 4.3
set tclversion 8.6
set module [file tail $moddir]
set filename $module

set fout [open [file join $moddir ${module}.tcl] w]
set fout [open [file join $moddir ${filename}.tcl] w]
dict set map %module% $module
dict set map %version% $version
dict set map %tclversion% $tclversion
dict set map %filename% $filename
dict set map {    } {} ;# strip indentation
dict set map "\t" {    } ;# reduce indentation (see cleanup)

puts $fout [string map $map {###
    # Amalgamated package for %module%
    # Do not edit directly, tweak the source in src/ and rerun
    # build.tcl
35
36
37
38
39
40
41
42
43


44

45
46
47
48
49
50
51
52
53


54

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75







41
42
43
44
45
46
47

48
49
50

51

52
53
54
55
56
57
58
59
60
61

62

63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89







-

+
+
-
+
-








+
+
-
+
-




















+
+
+
+
+
+
+
  file.tcl
  proxy.tcl
  cgi.tcl
  scgi.tcl
  websocket.tcl
} {
  lappend loaded $file
  set fin [open [file join $srcdir $file] r]
  puts $fout "###\n# START: [file tail $file]\n###"
  set content [::clay::cat [file join $srcdir $file]]
  AutoDoc scan_text $content
  puts $fout [read $fin]
  puts $fout $content
  close $fin
  puts $fout "###\n# END: [file tail $file]\n###"
}
# These files can be loaded in any order
foreach file [glob [file join $srcdir *.tcl]] {
  if {[file tail $file] in $loaded} continue
  lappend loaded $file
  set fin [open [file join $srcdir $file] r]
  puts $fout "###\n# START: [file tail $file]\n###"
  set content [::clay::cat [file join $srcdir $file]]
  AutoDoc scan_text $content
  puts $fout [read $fin]
  puts $fout $content
  close $fin
  puts $fout "###\n# END: [file tail $file]\n###"
}

# Provide some cleanup and our final package provide
puts $fout [string map $map {
    namespace eval ::%module% {
	namespace export *
    }
}]
close $fout

###
# Build our pkgIndex.tcl file
###
set fout [open [file join $moddir pkgIndex.tcl] w]
puts $fout [string map $map {
    if {![package vsatisfies [package provide Tcl] %tclversion%]} {return}
    package ifneeded %module% %version% [list source [file join $dir %module%.tcl]]
}]
close $fout

set manout [open [file join $moddir $filename.man] w]
puts $manout [AutoDoc manpage \
  header [string map $map [::clay::cat [file join $srcdir manual.txt]]] \
  footer [string map $map [::clay::cat [file join $srcdir footer.txt]]] \
]
close $manout

Changes to modules/httpd/build/cgi.tcl.

1

2
3
4
5
6
7



8
9
10
11
12
13
14

1
2
3
4



5
6
7
8
9
10
11
12
13
14
-
+



-
-
-
+
+
+







::tool::define ::httpd::content.cgi {
::clay::define ::httpd::content.cgi {
  superclass ::httpd::content.proxy

  method FileName {} {
    set uri [string trimleft [my http_info get REQUEST_URI] /]
    set path [my http_info get path]
    set prefix [my http_info get prefix]
    set uri [string trimleft [my request get REQUEST_URI] /]
    set path [my clay get path]
    set prefix [my clay get prefix]

    set fname [string range $uri [string length $prefix] end]
    if {[file exists [file join $path $fname]]} {
      return [file join $path $fname]
    }
    if {[file exists [file join $path $fname.fossil]]} {
      return [file join $path $fname.fossil]
24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38







-
+








  method proxy_channel {} {
    ###
    # When delivering static content, allow web caches to save
    ###
    set local_file [my FileName]
    if {$local_file eq {} || ![file exist $local_file]} {
      my log httpNotFound [my http_info get REQUEST_URI]
      my log httpNotFound [my request get REQUEST_URI]
      my error 404 {Not Found}
      tailcall my DoOutput
    }
    if {[file isdirectory $local_file]} {
      ###
      # Produce an index page... or error
      ###
48
49
50
51
52
53
54
55

56
57
58

59
60

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
48
49
50
51
52
53
54

55
56
57

58


59

60
61
62
63







64
65
66
67
68
69
70







-
+


-
+
-
-
+
-




-
-
-
-
-
-
-







    }
    foreach item $verbatim {
      set ::env($item) {}
    }
    foreach item [array names ::env HTTP_*] {
      set ::env($item) {}
    }
    set ::env(SCRIPT_NAME) [my http_info get REQUEST_PATH]
    set ::env(SCRIPT_NAME) [my request get REQUEST_PATH]
    set ::env(SERVER_PROTOCOL) HTTP/1.0
    set ::env(HOME) $::env(DOCUMENT_ROOT)
    foreach {f v} [my http_info dump] {
    foreach {f v} [my request dump] {
      if {$f in $verbatim} {
        set ::env($f) $v
      set ::env($f) $v
      }
    }
  	set arglist $::env(QUERY_STRING)
    set pwd [pwd]
    cd [file dirname $local_file]
    foreach {f v} [my request dump] {
      if {$f in $verbatim} {
        set ::env($f) $v
      } else {
        set ::env(HTTP_$f) $v
      }
    }
    set script_file $local_file
    if {[file extension $local_file] in {.fossil .fos}} {
      if {![file exists $local_file.cgi]} {
        set fout [open $local_file.cgi w]
        chan puts $fout "#!/usr/bin/fossil"
        chan puts $fout "repository: $local_file"
        close $fout
88
89
90
91
92
93
94
95

96
97
98
99
100
101
102

103
104
105
106

107
108
109
110
111
112
113
114
115
79
80
81
82
83
84
85

86
87
88
89
90
91
92

93
94
95

96
97
98

99
100
101
102
103
104
105







-
+






-
+


-

+

-







    cd $pwd
    return $pipe
  }

  method ProxyRequest {chana chanb} {
    chan event $chanb writable {}
    my log ProxyRequest {}
    set length [my http_info get CONTENT_LENGTH]
    set length [my request get CONTENT_LENGTH]
    if {$length} {
      chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
      chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
      ###
      # Send any POST/PUT/etc content
      ###
      chan copy $chana $chanb -size $length -command [info coroutine]
      my ChannelCopy $chana $chanb -size $length
    } else {
      chan flush $chanb
      chan event $chanb readable [info coroutine]
    }
    chan event $chanb readable [info coroutine]
    yield

  }


  method ProxyReply {chana chanb args} {
    my log ProxyReply [list args $args]
    chan event $chana readable {}
    set replyhead [my HttpHeaders $chana]
124
125
126
127
128
129
130
131
132
133
134
135
136
137





138
139
140

141
142
143
144
145
146
147
148
149
150
151
114
115
116
117
118
119
120







121
122
123
124
125



126

127
128
129
130
131
132
133
134
135
136







-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
+
-










    # a standard service reply line from a web server, but
    # otherwise spit out the rest of the headers verbatim
    ###
    set replybuffer "HTTP/1.0 [dict get $replydat Status]\n"
    append replybuffer $replyhead
    chan configure $chanb -translation {auto crlf} -blocking 0 -buffering full -buffersize 4096
    chan puts $chanb $replybuffer
    my log SendReply [list length $length]
    if {$length} {
      ###
      # Output the body
      ###
      chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
      chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
    ###
    # Output the body. With no -size flag, channel will copy until EOF
    ###
    chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
    chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
      chan copy $chana $chanb -size $length -command [namespace code [list my TransferComplete $chana $chanb]]
    } else {
      my TransferComplete $chana $chanb
    my ChannelCopy $chana $chanb -chunk 4096
    }
  }

  ###
  # For most CGI applications a directory list is vorboten
  ###
  method DirectoryListing {local_file} {
    my error 403 {Not Allowed}
    tailcall my DoOutput
  }
}

Deleted modules/httpd/build/content.man.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94






























































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
[section {Class ::httpd::content}]

The httpd module includes several ready to use implementations of content mixins
for common use cases. Options are passed in to the [cmd add_uri] method of the server.

[section {Class ::httpd::content.cgi}]

An implementation to relay requests to process which will accept post data
streamed in vie stdin, and sent a reply streamed to stdout.

[list_begin definitions]
[call method cgi_info]

Mandatory method to be replaced by the end user. If needed, activates the
process to proxy, and then returns a list of three values:

[arg exec] - The arguments to send to exec to fire off the responding process, minus the stdin/stdout redirection.

[list_end]

[section {Class ::httpd::content.file}]

An implementation to deliver files from the local file system.

[list_begin definitions]

[call option [cmd path]]

The root directory on the local file system to be exposed via http.

[call option [cmd prefix]]

The prefix of the URI portion to ignore when calculating relative file paths.
[list_end]

[section {Class ::httpd::content.proxy}]

An implementation to relay requests to another HTTP server, and relay
the results back across the request channel.

[list_begin definitions]
[call method proxy_info]

Mandatory method to be replaced by the end user. If needed, activates the
process to proxy, and then returns a list of three values:

[arg proxyhost] - The hostname where the proxy is located

[arg proxyport] - The port to connect to

[arg proxyscript] - A pre-amble block of text to send prior to the mirrored request

[list_end]

[section {Class ::httpd::content.scgi}]

An implementation to relay requests to a server listening on a socket
expecting SCGI encoded requests, and relay
the results back across the request channel.

[list_begin definitions]
[call method scgi_info]

Mandatory method to be replaced by the end user. If needed, activates the
process to proxy, and then returns a list of three values:

[arg scgihost] - The hostname where the scgi listener is located

[arg scgiport] - The port to connect to

[arg scgiscript] - The contents of the [arg SCRIPT_NAME] header to be sent

[list_end]

[section {Class ::httpd::content.websocket}]

A placeholder for a future implementation to manage requests that can expect to be
promoted to a Websocket. Currently it is an empty class.

[section {SCGI Server Functions}]

The HTTP module also provides an SCGI server implementation, as well as an HTTP
implementation. To use the SCGI functions, create an object of the [cmd http::server.scgi]
class instead of the [cmd http::server] class.

[section {Class ::httpd::reply.scgi}]

An modified [cmd http::reply] implementation that understands how to deal with
netstring encoded headers.

[section {Class ::httpd::server.scgi}]

A modified [cmd http::server] which is tailored to replying to request according to
the SCGI standard instead of the HTTP standard.

Changes to modules/httpd/build/core.tcl.

10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27
28
29
30
31









































32
33
34
35
36
37
38
10
11
12
13
14
15
16

17
18
19
20
21
22
23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78







-
+













-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







# support the SCGI module
###

package require uri
package require dns
package require cron
package require coroutine
package require tool
package require clay 0.3
package require mime
package require fileutil
package require websocket
package require Markdown
package require uuid
package require fileutil::magic::filetype

namespace eval httpd::content {}

namespace eval ::url {}
namespace eval ::httpd {}
namespace eval ::scgi {}

tool::define ::httpd::mime {
clay::define ::httpd::mime {

  method ChannelCopy {in out args} {
    set chunk 4096
    set size -1
    foreach {f v} $args {
      set [string trim $f -] $v
    }
    dict set info coroutine [info coroutine]
    if {$size>0 && $chunk>$size} {
        set chunk $size
    }
    set bytes 0
    set sofar 0
    set method [self method]
    while 1 {
      set command {}
      set error {}
      if {$size>=0} {
        incr sofar $bytes
        set remaining [expr {$size-$sofar}]
        if {$remaining <= 0} {
          break
        } elseif {$chunk > $remaining} {
          set chunk $remaining
        }
      }
      lassign [yieldto chan copy $in $out -size $chunk \
        -command [list [info coroutine] $method]] \
        command bytes error
      if {$command ne $method} {
        error "Subroutine $method interrupted"
      }
      if {[string length $error]} {
        error $error
      }
      if {[chan eof $in]} {
        break
      }
    }
  }


  method html_header {{title {}} args} {
    set result {}
    append result "<HTML><HEAD>"
    if {$title ne {}} {
      append result "<TITLE>$title</TITLE>"
105
106
107
108
109
110
111
112










113
114



115
116
117
118
119
120
121
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

164
165
166
167
168
169
170
171
172
173








+
+
+
+
+
+
+
+
+
+

-
+
+
+







  method HttpHeaders_Default {} {
    return {Status {200 OK}
Content-Size 0
Content-Type {text/html; charset=UTF-8}
Cache-Control {no-cache}
Connection close}
  }

  method HttpServerHeaders {} {
    return {
      CONTENT_LENGTH CONTENT_TYPE QUERY_STRING REMOTE_USER AUTH_TYPE
      REQUEST_METHOD REMOTE_ADDR REMOTE_HOST REQUEST_URI REQUEST_PATH
      REQUEST_VERSION  DOCUMENT_ROOT QUERY_STRING REQUEST_RAW
      GATEWAY_INTERFACE SERVER_PORT SERVER_HTTPS_PORT
      SERVER_NAME  SERVER_SOFTWARE SERVER_PROTOCOL
    }
  }

  ###
  # Minimalist MIME Header Parser
  # Converts a block of mime encoded text to a key/value list. If an exception is encountered,
  # the method will generate its own call to the [cmd error] method, and immediately invoke
  # the [cmd output] method to produce an error code and close the connection.
  ###
  method MimeParse mimetext {
    set data(mimeorder) {}
    foreach line [split $mimetext \n] {
      # This regexp picks up
      # key: value
      # MIME headers.  MIME headers may be continue with a line
193
194
195
196
197
198
199

200
201
202
203
204
205
206
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259







+







        }
      }
      dict set result $ckey $data(mime,$key)
    }
    return $result
  }

  # De-httpizes a string.
  method Url_Decode data {
    regsub -all {\+} $data " " data
    regsub -all {([][$\\])} $data {\\\1} data
    regsub -all {%([0-9a-fA-F][0-9a-fA-F])} $data  {[format %c 0x\1]} data
    return [subst $data]
  }

Changes to modules/httpd/build/dispatch.tcl.

1

2
3
4
5
6
7
8
9
10
11
12
13




14
15
16
17
18
19
20
21
22

23
24
25
26
27
28

29
30

31
32
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48
49

50
51
52
53


54
55

56
57

1
2
3
4
5
6
7
8
9




10
11
12
13
14
15
16
17
18
19
20
21

22
23
24
25
26
27

28
29

30


31


32

33
34

35
36
37
38
39
40
41
42
43

44
45
46


47
48
49

50
51
52
-
+








-
-
-
-
+
+
+
+








-
+





-
+

-
+
-
-

-
-

-


-
+








-
+


-
-
+
+

-
+


::tool::define ::httpd::content.redirect {
::clay::define ::httpd::content.redirect {

  method reset {} {
    ###
    # Inject the location into the HTTP headers
    ###
    my variable reply_body
    set reply_body {}
    my reply replace    [my HttpHeaders_Default]
    my reply set Server [my <server> cget server_string]
    set msg [my http_info get LOCATION]
    my reply set Location [my http_info get LOCATION]
    set code  [my http_info getnull REDIRECT_CODE]
    my reply set Server [my <server> clay get server/ string]
    set msg [my clay get LOCATION]
    my reply set Location [my clay get LOCATION]
    set code  [my clay get REDIRECT_CODE]
    if {$code eq {}} {
      set code 301
    }
    my reply set Status [list $code [my http_code_string $code]]
  }

  method content {} {
    set template [my <server> template redirect]
    set msg [my http_info get LOCATION]
    set msg [my clay get LOCATION]
    set HTTP_STATUS [my reply get Status]
    my puts [subst $msg]
  }
}

::tool::define ::httpd::content.cache {
::clay::define ::httpd::content.cache {

  method dispatch {newsock datastate} {
  method Dispatch {} {
    my http_info replace $datastate
    my request replace  [dict get $datastate http]
    my variable chan
    set chan $newsock
    chan event $chan readable {}
    try {
      my Log_Dispatched
      my wait writable $chan
      chan configure $chan  -translation {binary binary}
      chan puts -nonewline $chan [my http_info get CACHE_DATA]
      chan puts -nonewline $chan [my clay get cache/ data]
    } on error {err info} {
      my <server> debug [dict get $info -errorinfo]
    } finally {
      my TransferComplete $chan
    }
  }
}

::tool::define ::httpd::content.template {
::clay::define ::httpd::content.template {

  method content {} {
    if {[my http_info getnull HTTP_STATUS] ne {}} {
      my reply set Status [my http_info getnull HTTP_STATUS]
    if {[my request get HTTP_STATUS] ne {}} {
      my reply set Status [my request get HTTP_STATUS]
    }
    my puts [subst [my <server> template [my http_info get template]]]
    my puts [subst [my <server> template [my clay get template]]]
  }
}

Changes to modules/httpd/build/file.tcl.

1
2
3
4
5
6

7
8
9
10
11



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34



35
36
37
38
39
40
41
1
2
3
4
5

6
7
8



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31



32
33
34
35
36
37
38
39
40
41





-
+


-
-
-
+
+
+




















-
-
-
+
+
+







###
# Class to deliver Static content
# When utilized, this class is fed a local filename
# by the dispatcher
###
::tool::define ::httpd::content.file {
::clay::define ::httpd::content.file {

  method FileName {} {
    set uri [string trimleft [my http_info get REQUEST_URI] /]
    set path [my http_info get path]
    set prefix [my http_info get prefix]
    set uri [string trimleft [my request get REQUEST_URI] /]
    set path [my clay get path]
    set prefix [my clay get prefix]
    set fname [string range $uri [string length $prefix] end]
    if {$fname in "{} index.html index.md index"} {
      return $path
    }
    if {[file exists [file join $path $fname]]} {
      return [file join $path $fname]
    }
    if {[file exists [file join $path $fname.md]]} {
      return [file join $path $fname.md]
    }
    if {[file exists [file join $path $fname.html]]} {
      return [file join $path $fname.html]
    }
    if {[file exists [file join $path $fname.tml]]} {
      return [file join $path $fname.tml]
    }
    return {}
  }

  method DirectoryListing {local_file} {
    set uri [string trimleft [my http_info get REQUEST_URI] /]
    set path [my http_info get path]
    set prefix [my http_info get prefix]
    set uri [string trimleft [my request get REQUEST_URI] /]
    set path [my clay get path]
    set prefix [my clay get prefix]
    set fname [string range $uri [string length $prefix] end]
    my puts [my html_header "Listing of /$fname/"]
    my puts "Listing contents of /$fname/"
    my puts "<TABLE>"
    if {$prefix ni {/ {}}} {
      set updir [file dirname $prefix]
      if {$updir ne {}} {
53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
53
54
55
56
57
58
59

60
61
62
63
64
65
66
67







-
+







    my puts [my html_footer]
  }

  method content {} {
    my variable reply_file
    set local_file [my FileName]
    if {$local_file eq {} || ![file exist $local_file]} {
      my log httpNotFound [my http_info get REQUEST_URI]
      my log httpNotFound [my request get REQUEST_URI]
      my error 404 {File Not Found}
      tailcall my DoOutput
    }
    if {[file isdirectory $local_file] || [file tail $local_file] in {index index.html index.tml index.md}} {
      ###
      # Produce an index page
      ###
88
89
90
91
92
93
94
95

96
97
98
99
100
101
102
103
104
105
106
107
108
109

110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
88
89
90
91
92
93
94

95
96
97
98
99
100
101
102
103
104
105
106
107
108

109
110
111







112
113
114
115
116
117
118







-
+













-
+


-
-
-
-
-
-
-







        my reply set Content-Type {text/html; charset=UTF-8}
        set mdtxt  [::fileutil::cat $local_file]
        my puts [::Markdown::convert $mdtxt]
      }
      .tml {
        my reply set Content-Type {text/html; charset=UTF-8}
        set tmltxt  [::fileutil::cat $local_file]
        set headers [my http_info dump]
        set headers [my request dump]
        dict with headers {}
        my puts [subst $tmltxt]
      }
      default {
        ###
        # Assume we are returning a binary file
        ###
        my reply set Content-Type [::fileutil::magic::filetype $local_file]
        set reply_file $local_file
      }
    }
  }

  method dispatch {newsock datastate} {
  method Dispatch {} {
    my variable reply_body reply_file reply_chan chan
    try {
      my http_info replace $datastate
      my request replace  [dict get $datastate http]
      my Log_Dispatched
      set chan $newsock
      chan event $chan readable {}
      chan configure $chan -translation {auto crlf} -buffering line

      my reset
      # Invoke the URL implementation.
      my content
    } on error {err errdat} {
      my error 500 $err [dict get $errdat -errorinfo]
      tailcall my DoOutput
    }
136
137
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
153
154
155

156
157


158
159
160
161
129
130
131
132
133
134
135

136

137









138
139


140
141
142
143
144
145







-

-
+
-
-
-
-
-
-
-
-
-

+
-
-
+
+




      ###
      set size [file size $reply_file]
      my reply set Content-Length $size
      append result [my reply output] \n
      chan puts -nonewline $chan $result
      set reply_chan [open $reply_file r]
      my log SendReply [list length $size]
      chan configure $reply_chan  -translation {binary binary}
      ###
      # Send any POST/PUT/etc content
      # Output the file contents. With no -size flag, channel will copy until EOF
      # Note, we are terminating the coroutine at this point
      # and using the file event to wake the object back up
      #
      # We *could*:
      # chan copy $sock $chan -command [info coroutine]
      # yield
      #
      # But in the field this pegs the CPU for long transfers and locks
      # up the process
      ###
      chan configure $reply_chan -translation {binary binary} -buffersize 4096 -buffering full -blocking 0
      chan copy $reply_chan $chan -command [namespace code [list my TransferComplete $reply_chan $chan]]
    } on error {err errdat} {
      my ChannelCopy $reply_chan $chan -chunk 4096
    } finally {
      my TransferComplete $reply_chan $chan
    }
  }
}

Added modules/httpd/build/footer.txt.






1
2
3
4
5
+
+
+
+
+
[section AUTHORS]
Sean Woods

[vset CATEGORY network]
[include ../doctools2base/include/feedback.inc]

Added modules/httpd/build/manual.txt.







































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
[vset VERSION %version%]
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin %module% n [vset VERSION]]
[keywords WWW]
[copyright {2018 Sean Woods <[email protected]>}]
[moddesc   {Tcl Web Server}]
[titledesc {A TclOO and coroutine based web server}]
[category  Networking]
[keywords TclOO]
[keywords http]
[keywords httpd]
[keywords httpserver]
[keywords services]
[require Tcl 8.6]
[require httpd [opt [vset VERSION]]]
[require uuid]
[require clay]
[require coroutine]
[require fileutil]
[require fileutil::magic::filetype]
[require websocket]
[require mime]
[require cron]
[require uri]
[require Markdown]
[description]
[para]

This module implements a web server, suitable for embedding in an
application. The server is object oriented, and contains all of the
fundamentals needed for a full service website.

[para]

[section {Minimal Example}]

Starting a web service requires starting a class of type
[cmd httpd::server], and providing that server with one or more URIs
to service, and [cmd httpd::reply] derived classes to generate them.

[example {
oo::class create ::reply.hello {
  method content {} {
    my puts "<HTML><HEAD><TITLE>IRM Dispatch Server</TITLE></HEAD><BODY>"
    my puts "<h1>Hello World!</h1>"
    my puts </BODY></HTML>
  }
}
::httpd::server create HTTPD port 8015 myaddr 127.0.0.1 doc_root ~/htdocs
HTTPD plugin dispatch httpd::server::dispatch
HTTPD uri add * /hello [list mixin reply.hello]
}]

The bare module does have facilities to hose a files from a file system. Files that end in a .tml will be substituted in the style of Tclhttpd:

[example {
<!-- hello.tml -->
[my html_header {Hello World!}]
Your Server is running.
<p>
The time is now [clock format [clock seconds]]
[my html_footer]
}]

A complete example of an httpd server is in the /examples directory of Tcllib. It also show how to dispatch URIs to other processes via SCGI and HTTP proxies.

[example {
cd ~/tcl/sandbox/tcllib
tclsh examples/httpd.tcl
}]

Changes to modules/httpd/build/plugin.tcl.

1
2
3
4

5
6
7
8
9
10
11
12
13

14
15
16
17
18

19
20
21
22
23

24
25
26
27
28

29
30
31
32
33
34

35
36
37
38
39
40
41
42
43


44
45
46
47
48
49



50

51
52
53



54

55
56
57
58
59
60
61







62
63
64


65
66
67
68
69
70
71
72
73


74
75
76
77
78
79




80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105



















106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

123
124

125
126
127
128
129
130
131
1
2
3

4
5
6
7
8
9
10
11
12

13
14
15
16
17

18
19
20
21
22

23
24
25
26
27

28
29
30
31
32
33

34
35
36
37
38
39
40
41


42
43
44
45
46
47
48
49
50
51
52
53
54



55
56
57
58
59







60
61
62
63
64
65
66



67
68









69
70






71
72
73
74













75
76
77
78
79
80
81
82
83
84



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

120
121

122
123
124
125
126
127
128
129



-
+








-
+




-
+




-
+




-
+





-
+







-
-
+
+






+
+
+

+
-
-
-
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-










-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
















-
+

-
+







###
# httpd plugin template
###
tool::define ::httpd::plugin {
::clay::define ::httpd::plugin {
  ###
  # Any options will be saved to the local config file
  # to allow threads to pull up a snapshot of the object' configuration
  ###

  ###
  # Define a code snippet to run on plugin load
  ###
  meta set plugin load: {}
  clay set plugin/ load {}

  ###
  # Define a code snippet to run within the object's Headers_Process method
  ###
  meta set plugin headers: {}
  clay set plugin/ headers {}

  ###
  # Define a code snippet to run within the object's dispatch method
  ###
  meta set plugin dispatch: {}
  clay set plugin/ dispatch {}

  ###
  # Define a code snippet to run within the object's writes a local config file
  ###
  meta set plugin local_config: {}
  clay set plugin/ local_config {}

  ###
  # When after all the plugins are loaded
  # allow specially configured ones to light off a thread
  ###
  meta set plugin thread: {}
  clay set plugin/ thread {}

}

###
# A rudimentary plugin that dispatches URLs from a dict
# data structure
###
tool::define ::httpd::plugin.dict_dispatch {
  meta set plugin dispatch: {
::clay::define ::httpd::plugin.dict_dispatch {
  clay set plugin/ dispatch {
    set reply [my Dispatch_Dict $data]
    if {[dict size $reply]} {
      return $reply
    }
  }

  ###
  # Implementation of the dispatcher
  ###
  method Dispatch_Dict {data} {
    my variable url_patterns
    set vhost [lindex [split [dict get $data HTTP_HOST] :] 0]
    set uri   [dict get $data REQUEST_PATH]
    foreach {host pattern info} [my uri patterns] {
    set vhost [lindex [split [dict get $data http HTTP_HOST] :] 0]
    set uri   [dict get $data http REQUEST_PATH]
    foreach {host hostpat} $url_patterns {
      if {![string match $host $vhost]} continue
      foreach {pattern info} $hostpat {
      if {![string match $pattern $uri]} continue
      set buffer $data
      foreach {f v} $info {
        dict set buffer $f $v
      }
      return $buffer
    }
        if {![string match $pattern $uri]} continue
        set buffer $data
        foreach {f v} $info {
          dict set buffer $f $v
        }
        return $buffer
      }
    return {}
  }

    }
    return {}
  method uri::patterns {} {
    my variable url_patterns url_stream
    if {![info exists url_stream]} {
      set url_stream {}
      foreach {host hostpat} $url_patterns {
        foreach {pattern info} $hostpat {
          lappend url_stream $host $pattern $info
        }
      }
  }

    }
    return $url_stream
  }

  method uri::add args {
    my variable url_patterns url_stream
  ###
  #
  Ensemble uri::add {vhosts patterns info} {
    my variable url_patterns
    unset -nocomplain url_stream
    switch [llength $args] {
      2 {
        set vhosts *
        lassign $args patterns info
      }
      3 {
        lassign $args vhosts patterns info
      }
      default {
        error "Usage: add_url ?vhosts? prefix info"
      }
    }
    foreach vhost $vhosts {
      foreach pattern $patterns {
        set data $info
        if {![dict exists $data prefix]} {
           dict set data prefix [my PrefixNormalize $pattern]
        }
        dict set url_patterns $vhost [string trimleft $pattern /] $data
      }
    }
  }
}

tool::define ::httpd::reply.memchan {

  Ensemble uri::direct {vhosts patterns info body} {
    my variable url_patterns url_stream
    set cbody {}
    if {[dict exists $info superclass]} {
      append cbody \n "superclass {*}[dict get $info superclass]"
      dict unset info superclass
    }
    append cbody \n [list method content {} $body]

    set class [namespace current]::${vhosts}/${patterns}
    set class [string map {* %} $class]
    ::clay::define $class $cbody
    dict set info mixin content $class
    my uri add $vhosts $patterns $info
  }
}

::clay::define ::httpd::reply.memchan {
  superclass ::httpd::reply

  method output {} {
    my variable reply_body
    return $reply_body
  }

  method DoOutput {} {}

  method close {} {
    # Neuter the channel closing mechanism we need the channel to stay alive
    # until the reader sucks out the info
  }
}


tool::define ::httpd::plugin.local_memchan {
::clay::define ::httpd::plugin.local_memchan {

  meta set plugin load: {
  clay set plugin/ load {
package require tcl::chan::events
package require tcl::chan::memchan
  }

  method local_memchan {command args} {
    my variable sock_to_coro
    switch $command {
157
158
159
160
161
162
163

164
165
166
167




168
169
170

171
172
173
174
175
176
177
178







179
180
181
182
183
184
185

186
187

188
189
190
191
192
193


194
195
196
197
198
199
200
201
202
203
204
205
206



207
208
209
210
211
212
213
214
155
156
157
158
159
160
161
162




163
164
165
166
167
168

169
170







171
172
173
174
175
176
177
178
179
180
181
182
183

184
185

186
187
188
189
190


191
192
193
194
195



196
197
198
199



200
201
202
203
204
205
206
207
208
209
210







+
-
-
-
-
+
+
+
+


-
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+






-
+

-
+




-
-
+
+



-
-
-




-
-
-
+
+
+









    chan configure $sock \
      -blocking 0 \
      -translation {auto crlf} \
      -buffering line
    set ip 127.0.0.1
    dict set query UUID $uuid
    dict set query http UUID $uuid
    dict set query HTTP_HOST       localhost
    dict set query REMOTE_ADDR     127.0.0.1
    dict set query REMOTE_HOST     localhost
    dict set query LOCALHOST 1
    dict set query http HTTP_HOST       localhost
    dict set query http REMOTE_ADDR     127.0.0.1
    dict set query http REMOTE_HOST     localhost
    dict set query http LOCALHOST 1
    my counter url_hit

    dict set query REQUEST_METHOD  [lindex $args 0]
    dict set query http REQUEST_METHOD  [lindex $args 0]
    set uriinfo [::uri::split [lindex $args 1]]
    dict set query REQUEST_URI     [lindex $args 1]
    dict set query REQUEST_PATH    [dict get $uriinfo path]
    dict set query REQUEST_VERSION [lindex [split [lindex $args end] /] end]
    dict set query DOCUMENT_ROOT   [my cget doc_root]
    dict set query QUERY_STRING    [dict get $uriinfo query]
    dict set query REQUEST_RAW     $args
    dict set query SERVER_PORT     [my port_listening]
    dict set query http REQUEST_URI     [lindex $args 1]
    dict set query http REQUEST_PATH    [dict get $uriinfo path]
    dict set query http REQUEST_VERSION [lindex [split [lindex $args end] /] end]
    dict set query http DOCUMENT_ROOT   [my clay get server/ doc_root]
    dict set query http QUERY_STRING    [dict get $uriinfo query]
    dict set query http REQUEST_RAW     $args
    dict set query http SERVER_PORT     [my port_listening]
    my Headers_Process query
    set reply [my dispatch $query]

    if {[llength $reply]==0} {
      my log BadLocation $uuid $query
      my log BadLocation $uuid $query
      dict set query HTTP_STATUS 404
      dict set query http HTTP_STATUS 404
      dict set query template notfound
      dict set query mixinmap reply ::httpd::content.template
      dict set query mixin reply ::httpd::content.template
    }

    set class ::httpd::reply.memchan
    set pageobj [$class create ::httpd::object::$uuid [self]]
    if {[dict exists $reply mixinmap]} {
      set mixinmap [dict get $reply mixinmap]
    if {[dict exists $reply mixin]} {
      set mixinmap [dict get $reply mixin]
    } else {
      set mixinmap {}
    }
    if {[dict exists $reply mixin]} {
      dict set mixinmap reply [dict get $reply mixin]
    }
    foreach item [dict keys $reply MIXIN_*] {
      set slot [string range $reply 6 end]
      dict set mixinmap [string tolower $slot] [dict get $reply $item]
    }
    $pageobj mixinmap {*}$mixinmap
    if {[dict exists $reply organ]} {
      $pageobj graft {*}[dict get $reply organ]
    $pageobj clay mixinmap {*}$mixinmap
    if {[dict exists $reply delegate]} {
      $pageobj clay delegate {*}[dict get $reply delegate]
    }
    $pageobj dispatch $sock $reply
    set output [$pageobj output]
    catch {$pageobj destroy}
    return $output
  }
}

Changes to modules/httpd/build/proxy.tcl.

1

2
3
4
5
6
7
8

1
2
3
4
5
6
7
8
-
+







::tool::define ::httpd::content.exec {
::clay::define ::httpd::content.exec {
  variable exename [list tcl [info nameofexecutable] .tcl [info nameofexecutable]]

  method CgiExec {execname script arglist} {
    if { $::tcl_platform(platform) eq "windows"} {
      if {[file extension $script] eq ".exe"} {
        return [open "|[list $script] $arglist" r+]
      } else {
56
57
58
59
60
61
62
63
64


65
66
67
68
69
70


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96
97
98
99
100
101


102
103
104
105
106
107
108
109
110




111
112
113
114
115
116
117

118
119
120
121

122
123
124
125
126
127
128
129
130
131
132
133
134
135

136
137
138
139
140
141
142
143
144
145
146
147
148
149
150





151
152
153
154
155



156
157
158
159
160
161
162


163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182

183
184
185
186







56
57
58
59
60
61
62


63
64
65
66
67
68


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95
96
97
98
99


100
101
102
103
104
105
106
107



108
109
110
111
112
113
114
115
116
117

118
119
120

121
122
123
124
125
126
127
128
129
130
131





132
133
134
135
136
137
138
139
140







141
142
143
144
145





146
147
148







149
150









151
152
153
154
155
156
157
158
159
160
161
162




163
164
165
166
167
168
169







-
-
+
+




-
-
+
+

















-
+











-
-
+
+






-
-
-
+
+
+
+






-
+


-

+









-
-
-
-
-
+








-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-











+
-
-
-
-
+
+
+
+
+
+
+
        return $result
      }
    }
    if {[dict exists exename $which]} {
      return [dict get $exename $which]
    }
    if {$which eq "tcl"} {
      if {[my cget tcl_exe] ne {}} {
        dict set exename $which [my cget tcl_exe]
      if {[my clay get tcl_exe] ne {}} {
        dict set exename $which [my clay get tcl_exe]
      } else {
        dict set exename $which [info nameofexecutable]
      }
    } else {
      if {[my cget ${which}_exe] ne {}} {
        dict set exename $which [my cget ${which}_exe]
      if {[my clay get ${which}_exe] ne {}} {
        dict set exename $which [my clay get ${which}_exe]
      } elseif {"$::tcl_platform(platform)" == "windows"} {
        dict set exename $which $which.exe
      } else {
        dict set exename $which $which
      }
    }
    set result [dict get $exename $which]
    if {$ext ne {}} {
      dict set exename $ext $result
    }
    return $result
  }
}

###
# Return data from an proxy process
###
::tool::define ::httpd::content.proxy {
::clay::define ::httpd::content.proxy {
  superclass ::httpd::content.exec

  method proxy_channel {} {
    ###
    # This method returns a channel to the
    # proxied socket/stdout/etc
    ###
    error unimplemented
  }

  method proxy_path {} {
    set uri [string trimleft [my http_info get REQUEST_URI] /]
    set prefix [my http_info get prefix]
    set uri [string trimleft [my request get REQUEST_URI] /]
    set prefix [my clay get prefix]
    return /[string range $uri [string length $prefix] end]
  }

  method ProxyRequest {chana chanb} {
    chan event $chanb writable {}
    my log ProxyRequest {}
    chan puts $chanb "[my http_info get REQUEST_METHOD] [my proxy_path]"
    chan puts $chanb [my http_info get mimetxt]
    set length [my http_info get CONTENT_LENGTH]
    chan puts $chanb "[my request get REQUEST_METHOD] [my proxy_path]"
    set mimetxt [my clay get mimetxt]
    chan puts $chanb [my clay get mimetxt]
    set length [my request get CONTENT_LENGTH]
    if {$length} {
      chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
      chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
      ###
      # Send any POST/PUT/etc content
      ###
      chan copy $chana $chanb -size $length -command [info coroutine]
      my ChannelCopy $chana $chanb -size $length
    } else {
      chan flush $chanb
      chan event $chanb readable [info coroutine]
    }
    chan event $chanb readable [info coroutine]
    yield
  }

  method ProxyReply {chana chanb args} {
    my log ProxyReply [list args $args]
    chan event $chana readable {}
    set readCount [::coroutine::util::gets_safety $chana 4096 reply_status]
    set replyhead [my HttpHeaders $chana]
    set replydat  [my MimeParse $replyhead]
    if {![dict exists $replydat Content-Length]} {
      set length 0
    } else {
      set length [dict get $replydat Content-Length]
    }

    ###
    # Read the first incoming line as the HTTP reply status
    # Return the rest of the headers verbatim
    ###
    set replybuffer "$reply_status\n"
    append replybuffer $replyhead
    chan configure $chanb -translation {auto crlf} -blocking 0 -buffering full -buffersize 4096
    chan puts $chanb $replybuffer
    my log SendReply [list length $length]
    if {$length} {
      ###
      # Output the body
      ###
      chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
      chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
    ###
    # Output the body. With no -size flag, channel will copy until EOF
    ###
    chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
    chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
      chan copy $chana $chanb -size $length -command [namespace code [list my TransferComplete $chana $chanb]]
    } else {
      my TransferComplete $chana $chanb
    }
  }
    my ChannelCopy $chana $chanb -chunk 4096
  }


  method dispatch {newsock datastate} {
    try {
      my http_info replace $datastate
      my request replace  [dict get $datastate http]
      my Log_Dispatched
      my variable sock chan
  method Dispatch {} {
    my variable sock chan
      set chan $newsock
      chan configure $chan -translation {auto crlf} -buffering line
      # Initialize the reply
      my reset
      # Invoke the URL implementation.
    } on error {err errdat} {
      my error 500 $err [dict get $errdat -errorinfo]
      tailcall my DoOutput
    }
    if {[catch {my proxy_channel} sock errdat]} {
      my error 504 {Service Temporarily Unavailable} [dict get $errdat -errorinfo]
      tailcall my DoOutput
    }
    if {$sock eq {}} {
      my error 404 {Not Found}
      tailcall my DoOutput
    }
    my log HttpAccess {}
    chan event $sock writable [info coroutine]
    yield
    try {
    my ProxyRequest $chan $sock
    my ProxyReply   $sock $chan
  }
}
      my ProxyRequest $chan $sock
      my ProxyReply   $sock $chan
    } finally {
      my TransferComplete $chan $sock
    }
  }
}

Deleted modules/httpd/build/reply.man.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297









































































































































































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
[section {Class ::httpd::reply}]

A class which shephards a request through the process of generating a
reply.

The socket associated with the reply is available at all times as the [arg chan]
variable.

The process of generating a reply begins with an [cmd httpd::server] generating a
[cmd http::class] object, mixing in a set of behaviors and then invoking the reply
object's [cmd dispatch] method.

In normal operations the [cmd dispatch] method:

[list_begin enumerated]

[enum]
Invokes the [cmd reset] method for the object to populate default headers.

[enum]
Invokes the [cmd HttpHeaders] method to stream the MIME headers out of the socket

[enum]
Invokes the [cmd {request parse}] method to convert the stream of MIME headers into a
dict that can be read via the [cmd request] method.

[enum]
Stores the raw stream of MIME headers in the [arg rawrequest] variable of the object.

[enum]
Invokes the [cmd content] method for the object, generating an call to the [cmd error]
method if an exception is raised.

[enum]
Invokes the [cmd output] method for the object
[list_end]

[para]

[section {Reply Method Ensembles}]

The [cmd http::reply] class and its derivatives maintain several variables as dictionaries
internally. Access to these dictionaries is managed through a dedicated ensemble. The
ensemble implements most of the same behaviors as the [cmd dict] command.

Each ensemble implements the following methods above, beyond, or modifying standard dicts:

[list_begin definitions]

[call method [cmd ENSEMBLE::add] [arg field] [arg element]]

Add [arg element] to a list stored in [arg field], but only if it is not already present om the list.

[call method [cmd ENSEMBLE::dump]]

Return the current contents of the data structure as a key/value list.

[call method [cmd ENSEMBLE::get] [arg field]]

Return the value of the field [arg field], or an empty string if it does not exist.

[call method [cmd ENSEMBLE::reset]]

Return a key/value list of the default contents for this data structure.

[call method [cmd ENSEMBLE::remove] [arg field] [arg element]]

Remove all instances of [arg element] from the list stored in [arg field].

[call method [cmd ENSEMBLE::replace] [arg keyvaluelist]]

Replace the internal dict with the contents of [arg keyvaluelist]

[call method [cmd ENSEMBLE::reset]]

Replace the internal dict with the default state.

[call method [cmd ENSEMBLE::set] [arg field] [arg value]]

Set the value of [arg field] to [arg value].

[list_end]

[section {Reply Method Ensemble: http_info}]

Manages HTTP headers passed in by the server.

Ensemble Methods:

[list_begin definitions]

[call method [cmd http_info::netstring]]

Return the contents of this data structure as a netstring encoded block.

[list_end]

[section {Reply Method Ensemble: request}]

Managed data from MIME headers of the request.

[list_begin definitions]

[call method  [cmd request::parse] [arg string]]

Replace the contents of the data structure with information encoded in a MIME
formatted block of text ([arg string]).

[list_end]

[section {Reply Method Ensemble: reply}]

Manage the headers sent in the reply.


[list_begin definitions]

[call method [cmd reply::output]]

Return the contents of this data structure as a MIME encoded block appropriate
for an HTTP response.

[list_end]

[section {Reply Methods}]

[list_begin definitions]
[call method [cmd close]]

Terminate the transaction, and close the socket.

[call method [cmd HttpHeaders] [arg sock] [arg ?debug?]]

Stream MIME headers from the socket [arg sock], stopping at an empty line. Returns
the stream as a block of text.

[call method [cmd dispatch] [arg newsock] [arg datastate]]

Take over control of the socket [arg newsock], and store that as the [arg chan] variable
for the object. This method runs through all of the steps of reading HTTP headers, generating
content, and closing the connection. (See class writetup).

[call method [cmd error] [arg code] [arg ?message?] [arg ?errorInfo?]]

Generate an error message of the specified [arg code], and display the [arg message] as the
reason for the exception. [arg errorInfo] is passed in from calls, but how or if it should be
displayed is a prerogative of the developer.

[call method [cmd content]]

Generate the content for the reply. This method is intended to be replaced by the mixin.

Developers have the option of streaming output to a buffer via the [cmd puts] method of the
reply, or simply populating the [arg reply_body] variable of the object.
The information returned by the [cmd content] method is not interpreted in any way.

If an exception is thrown (via the [cmd error] command in Tcl, for example) the caller will
auto-generate a 500 {Internal Error} message.

A typical implementation of [cmd content] look like:

[example {

tool::define ::test::content.file {
	superclass ::httpd::content.file
	# Return a file
	# Note: this is using the content.file mixin which looks for the reply_file variable
	# and will auto-compute the Content-Type
	method content {} {
	  my reset
    set doc_root [my http_info get doc_root]
    my variable reply_file
    set reply_file [file join $doc_root index.html]
	}
}
tool::define ::test::content.time {
  # return the current system time
	method content {} {
		my variable reply_body
    my reply set Content-Type text/plain
		set reply_body [clock seconds]
	}
}
tool::define ::test::content.echo {
	method content {} {
		my variable reply_body
    my reply set Content-Type [my request get CONTENT_TYPE]
		set reply_body [my PostData [my request get CONTENT_LENGTH]]
	}
}
tool::define ::test::content.form_handler {
	method content {} {
	  set form [my FormData]
	  my reply set Content-Type {text/html; charset=UTF-8}
    my puts [my html header {My Dynamic Page}]
    my puts "<BODY>"
    my puts "You Sent<p>"
    my puts "<TABLE>"
    foreach {f v} $form {
      my puts "<TR><TH>$f</TH><TD><verbatim>$v</verbatim></TD>"
    }
    my puts "</TABLE><p>"
    my puts "Send some info:<p>"
    my puts "<FORM action=/[my http_info get REQUEST_PATH] method POST>"
    my puts "<TABLE>"
    foreach field {name rank serial_number} {
      set line "<TR><TH>$field</TH><TD><input name=\"$field\" "
      if {[dict exists $form $field]} {
        append line " value=\"[dict get $form $field]\"""
      }
      append line " /></TD></TR>"
      my puts $line
    }
    my puts "</TABLE>"
    my puts [my html footer]
	}
}

}]

[call method [cmd EncodeStatus] [arg status]]

Formulate a standard HTTP status header from he string provided.

[call method FormData]

For GET requests, converts the QUERY_DATA header into a key/value list.

For POST requests, reads the Post data and converts that information to
a key/value list for application/x-www-form-urlencoded posts. For multipart
posts, it composites all of the MIME headers of the post to a singular key/value
list, and provides MIME_* information as computed by the [cmd mime] package, including
the MIME_TOKEN, which can be fed back into the mime package to read out the contents.

[call method MimeParse [arg mimetext]]

Converts a block of mime encoded text to a key/value list. If an exception is encountered,
the method will generate its own call to the [cmd error] method, and immediately invoke
the [cmd output] method to produce an error code and close the connection.

[call method [cmd DoOutput]]

Generates the the HTTP reply, and streams that reply back across [arg chan].

[call method PostData [arg length]]

Stream [arg length] bytes from the [arg chan] socket, but only of the request is a
POST or PUSH. Returns an empty string otherwise.

[call method [cmd puts] [arg string]]

Appends the value of [arg string] to the end of [arg reply_body], as well as a trailing newline
character.

[call method [cmd reset]]

Clear the contents of the [arg reply_body] variable, and reset all headers in the [cmd reply]
structure back to the defaults for this object.

[call method [cmd timeOutCheck]]

Called from the [cmd http::server] object which spawned this reply. Checks to see
if too much time has elapsed while waiting for data or generating a reply, and issues
a timeout error to the request if it has, as well as destroy the object and close the
[arg chan] socket.

[call method [cmd timestamp]]

Return the current system time in the format: [example {%a, %d %b %Y %T %Z}]

[call method [cmd TransferComplete] [arg args]]

Intended to be invoked from [cmd {chan copy}] as a callback. This closes every channel
fed to it on the command line, and then destroys the object.

[example {
    ###
    # Output the body
    ###
    chan configure $sock -translation binary -blocking 0 -buffering full -buffersize 4096
    chan configure $chan -translation binary -blocking 0 -buffering full -buffersize 4096
    if {$length} {
      ###
      # Send any POST/PUT/etc content
      ###
      chan copy $sock $chan -size $SIZE -command [info coroutine]
      yield
    }
    catch {close $sock}
    chan flush $chan
}]

[call method [cmd Url_Decode] [arg string]]

De-httpizes a string.

[list_end]

Changes to modules/httpd/build/reply.tcl.

1

2
































































































3
4

5
6
7















8
9
10
11
12
13
14
15



16
17
18
19
20
21
22
23
24
25



26
27
28
29
30
31
32
33
34
35
36



37
38
39
40
41
42
43
44
45
46








47
48
























49
50
51
52
53
54
55
56
57



















58
59
60
61







62
63
64
65
66






67
68
69
70
71
72
73
1
2

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

100
101
102

103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154








155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188









189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207




208
209
210
211
212
213
214
215
216

217
218
219
220
221
222
223
224
225
226
227
228
229
230
231

+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+


-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
-
+
+
+










+
+
+











+
+
+


-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+


-


+
+
+
+
+
+







###
# A class which shephards a request through the process of generating a
# Define the reply class
# reply.
#
# The socket associated with the reply is available at all times as the [arg chan]
# variable.
#
# The process of generating a reply begins with an [cmd httpd::server] generating a
# [cmd http::class] object, mixing in a set of behaviors and then invoking the reply
# object's [cmd dispatch] method.
#
# In normal operations the [cmd dispatch] method:
#
# [list_begin enumerated]
# [enum]
# Invokes the [cmd reset] method for the object to populate default headers.
# [enum]
# Invokes the [cmd HttpHeaders] method to stream the MIME headers out of the socket
# [enum]
# Invokes the [cmd {request parse}] method to convert the stream of MIME headers into a
# dict that can be read via the [cmd request] method.
# [enum]
# Stores the raw stream of MIME headers in the [arg rawrequest] variable of the object.
# [enum]
# Invokes the [cmd content] method for the object, generating an call to the [cmd error]
# method if an exception is raised.
# [enum]
# Invokes the [cmd output] method for the object
# [list_end]
# [para]
#
# Developers have the option of streaming output to a buffer via the [cmd puts] method of the
# reply, or simply populating the [arg reply_body] variable of the object.
# The information returned by the [cmd content] method is not interpreted in any way.
#
# If an exception is thrown (via the [cmd error] command in Tcl, for example) the caller will
# auto-generate a 500 {Internal Error} message.
#
# A typical implementation of [cmd content] look like:
#
# [example {
#
# clay::define ::test::content.file {
# 	superclass ::httpd::content.file
# 	# Return a file
# 	# Note: this is using the content.file mixin which looks for the reply_file variable
# 	# and will auto-compute the Content-Type
# 	method content {} {
# 	  my reset
#     set doc_root [my request get DOCUMENT_ROOT]
#     my variable reply_file
#     set reply_file [file join $doc_root index.html]
# 	}
# }
# clay::define ::test::content.time {
#   # return the current system time
# 	method content {} {
# 		my variable reply_body
#     my reply set Content-Type text/plain
# 		set reply_body [clock seconds]
# 	}
# }
# clay::define ::test::content.echo {
# 	method content {} {
# 		my variable reply_body
#     my reply set Content-Type [my request get CONTENT_TYPE]
# 		set reply_body [my PostData [my request get CONTENT_LENGTH]]
# 	}
# }
# clay::define ::test::content.form_handler {
# 	method content {} {
# 	  set form [my FormData]
# 	  my reply set Content-Type {text/html; charset=UTF-8}
#     my puts [my html_header {My Dynamic Page}]
#     my puts "<BODY>"
#     my puts "You Sent<p>"
#     my puts "<TABLE>"
#     foreach {f v} $form {
#       my puts "<TR><TH>$f</TH><TD><verbatim>$v</verbatim></TD>"
#     }
#     my puts "</TABLE><p>"
#     my puts "Send some info:<p>"
#     my puts "<FORM action=/[my request get REQUEST_PATH] method POST>"
#     my puts "<TABLE>"
#     foreach field {name rank serial_number} {
#       set line "<TR><TH>$field</TH><TD><input name=\"$field\" "
#       if {[dict exists $form $field]} {
#         append line " value=\"[dict get $form $field]\"""
#       }
#       append line " /></TD></TR>"
#       my puts $line
#     }
#     my puts "</TABLE>"
#     my puts [my html footer]
# 	}
# }
#
# }]
###
::tool::define ::httpd::reply {
::clay::define ::httpd::reply {
  superclass ::httpd::mime

  variable transfer_complete 0
  Variable transfer_complete 0

  Dict reply {}

  Dict request {
    CONTENT_LENGTH 0
    COOKIE {}
    HTTP_HOST {}
    REFERER {}
    REQUEST_URI {}
    REMOTE_ADDR {}
    REMOTE_HOST {}
    USER_AGENT {}
    SESSION {}
  }

  constructor {ServerObj args} {
    my variable chan dispatched_time uuid
    set uuid [namespace tail [self]]
    set dispatched_time [clock milliseconds]
    oo::objdefine [self] forward <server> $ServerObj
    foreach {field value} [::oo::meta::args_to_options {*}$args] {
      my meta set config $field: $value
    my clay delegate <server> $ServerObj
    foreach {field value} [::clay::args_to_options {*}$args] {
      my clay set config $field: $value
    }
  }

  ###
  # clean up on exit
  ###
  destructor {
    my close
  }

  ###
  # Close channels opened by this object
  ###
  method close {} {
    my variable chan
    if {[info exists chan] && $chan ne {}} {
      catch {chan event $chan readable {}}
      catch {chan event $chan writable {}}
      catch {chan flush $chan}
      catch {chan close $chan}
      set chan {}
    }
  }

  ###
  # Record a dispatch event
  ###
  method Log_Dispatched {} {
    my log Dispatched [dict create \
     REMOTE_ADDR [my http_info get REMOTE_ADDR] \
     REMOTE_HOST [my http_info get REMOTE_HOST] \
     COOKIE [my request get COOKIE] \
     REFERER [my request get REFERER] \
     USER_AGENT [my request get USER_AGENT] \
     REQUEST_URI [my http_info get REQUEST_URI] \
     HTTP_HOST [my http_info getnull HTTP_HOST] \
     SESSION [my http_info getnull SESSION] \
     REMOTE_ADDR [my request get REMOTE_ADDR] \
     REMOTE_HOST [my request get REMOTE_HOST] \
     COOKIE [my request get HTTP_COOKIE] \
     REFERER [my request get HTTP_REFERER] \
     USER_AGENT [my request get HTTP_USER_AGENT] \
     REQUEST_URI [my request get REQUEST_URI] \
     HTTP_HOST [my request get HTTP_HOST] \
     SESSION [my request get SESSION] \
    ]
  }

  ###
  # Accept the handoff from the server object of the socket
  # [emph newsock] and feed it the state [emph datastate].
  # Fields the [emph datastate] are looking for in particular are:
  # [para]
  # * [const mixin] - A key/value list of slots and classes to be mixed into the
  # object prior to invoking [cmd Dispatch].
  # [para]
  # * [const http] - A key/value list of values to populate the object's [emph request]
  # ensemble
  # [para]
  # All other fields are passed along to the [method clay] structure of the object.
  ###
  method dispatch {newsock datastate} {
    my variable chan request
    try {
      set chan $newsock
      chan event $chan readable {}
      chan configure $chan -translation {auto crlf} -buffering line
      if {[dict exists $datastate mixin]} {
        set mixinmap [dict get $datastate mixin]
      } else {
        set mixinmap {}

  method dispatch {newsock datastate} {
    my http_info replace $datastate
    my request replace  [dict getnull $datastate http]
    my Log_Dispatched
    my variable chan
    set chan $newsock
    try {
      chan event $chan readable {}
      }
      foreach item [dict keys $datastate MIXIN_*] {
        set slot [string range $item 6 end]
        dict set mixinmap [string tolower $slot] [dict get $datastate $item]
      }
      my clay mixinmap {*}$mixinmap
      if {[dict exists $datastate delegate]} {
        my clay delegate {*}[dict get $datastate delegate]
      }
      my reset
      set request [my clay get dict/ request]
      foreach {f v} $datastate {
        if {[string index $f end] eq "/"} {
          my clay merge $f $v
        } else {
          my clay set $f $v
        }
        if {$f eq "http"} {
          foreach {ff vf} $v {
      chan configure $chan -translation {auto crlf} -buffering line
      my reset
      # Invoke the URL implementation.
      my content
            dict set request $ff $vf
          }
        }
      }
      my Session_Load
      my Log_Dispatched
      my Dispatch
    } on error {err errdat} {
      my error 500 $err [dict get $errdat -errorinfo]
    } finally {
      my DoOutput
    }
  }

  method Dispatch {} {
    # Invoke the URL implementation.
    my content
    my DoOutput
  }

  method html_css {} {
    set result "<link rel=\"stylesheet\" href=\"/style.css\">"
    append result \n {<style media="screen" type="text/css">
body {
	background:  url(images/etoyoc-circuit-tile.gif) repeat;
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

123
124

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

152
153
154
155
156
157
158



159
160
161
162
163
164
165
166
167
168
169
170

171
172
173
174
175
176


177
178
179
180
181
182
183
259
260
261
262
263
264
265













266

267
268

269
270
271
272
273
274

275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294

295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316

317
318
319
320
321


322
323
324
325
326
327
328
329
330







-
-
-
-
-
-
-
-
-
-
-
-
-

-
+

-
+





-




















-
+







+
+
+











-
+




-
-
+
+







  }

  method html_footer {args} {
    set result {</div><div id="footer">}
    append result {</div></BODY></HTML>}
  }

  dictobj http_info http_info {
    initialize {
      CONTENT_LENGTH 0
    }
    netstring {
      set result {}
      foreach {name value} $%VARNAME% {
        append result $name \x00 $value \x00
      }
      return "[string length $result]:$result,"
    }
  }

  method error {code {msg {}} {errorInfo {}}} {
    my http_info set HTTP_ERROR $code
    my clay set  HTTP_ERROR $code
    my reset
    set qheaders [my http_info dump]
    set qheaders [my clay dump]
    set HTTP_STATUS "$code [my http_code_string $code]"
    dict with qheaders {}
    my reply replace {}
    my reply set Status $HTTP_STATUS
    my reply set Content-Type {text/html; charset=UTF-8}

    switch $code {
      301 - 302 - 303 - 307 - 308 {
        my reply set Location $msg
        set template [my <server> template redirect]
      }
      404 {
        set template [my <server> template notfound]
      }
      default {
        set template [my <server> template internal_error]
      }
    }
    my puts [subst $template]
  }


  ###
  # REPLACE ME:
  # This method is the "meat" of your application.
  # It writes to the result buffer via the "puts" method
  # and can tweak the headers via "meta put header_reply"
  # and can tweak the headers via "clay put header_reply"
  ###
  method content {} {
    my puts [my html_header {Hello World!}]
    my puts "<H1>HELLO WORLD!</H1>"
    my puts [my html_footer]
  }

  ###
  # Formulate a standard HTTP status header from he string provided.
  ###
  method EncodeStatus {status} {
    return "HTTP/1.0 $status"
  }

  method log {type {info {}}} {
    my variable dispatched_time uuid
    my <server> log $type $uuid $info
  }

  method CoroName {} {
    if {[info coroutine] eq {}} {
      return ::httpd::object::[my http_info get UUID]
      return ::httpd::object::[my clay get UUID]
    }
  }

  ###
  # Output the result or error to the channel
  # and destroy this object
  # Generates the the HTTP reply, streams that reply back across [arg chan],
  # and destroys the object.
  ###
  method DoOutput {} {
    my variable reply_body chan
    if {$chan eq {}} return
    catch {
      my wait writable $chan
      chan configure $chan  -translation {binary binary}
195
196
197
198
199
200
201









202
203
204
205
206
207
208
209
210
211

212
213
214

215
216
217
218
219
220
221
222
223
224
225
226

227
228
229
230
231
232
233
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363




364

365

366
367
368
369
370
371
372
373
374
375
376
377

378
379
380
381
382
383
384
385







+
+
+
+
+
+
+
+
+






-
-
-
-
+
-

-
+











-
+







      }
      chan puts -nonewline $chan $result
      my log HttpAccess {}
    }
    my destroy
  }

  ###
  # For GET requests, converts the QUERY_DATA header into a key/value list.
  #
  # For POST requests, reads the Post data and converts that information to
  # a key/value list for application/x-www-form-urlencoded posts. For multipart
  # posts, it composites all of the MIME headers of the post to a singular key/value
  # list, and provides MIME_* information as computed by the [cmd mime] package, including
  # the MIME_TOKEN, which can be fed back into the mime package to read out the contents.
  ###
  method FormData {} {
    my variable chan formdata
    # Run this only once
    if {[info exists formdata]} {
      return $formdata
    }
    if {![my request exists CONTENT_LENGTH]} {
      set length 0
    } else {
      set length [my request get CONTENT_LENGTH]
    set length [my request get CONTENT_LENGTH]
    }
    set formdata {}
    if {[my http_info get REQUEST_METHOD] in {"POST" "PUSH"}} {
    if {[my request get REQUEST_METHOD] in {"POST" "PUSH"}} {
      set rawtype [my request get CONTENT_TYPE]
      if {[string toupper [string range $rawtype 0 8]] ne "MULTIPART"} {
        set type $rawtype
      } else {
        set type multipart
      }
      switch $type {
        multipart {
          ###
          # Ok, Multipart MIME is troublesome, farm out the parsing to a dedicated tool
          ###
          set body [my http_info get mimetxt]
          set body [my clay get mimetxt]
          append body \n [my PostData $length]
          set token [::mime::initialize -string $body]
          foreach item [::mime::getheader $token -names] {
            dict set formdata $item [::mime::getheader $token $item]
          }
          foreach item {content encoding params parts size} {
            dict set formdata MIME_[string toupper $item] [::mime::getproperty $token $item]
243
244
245
246
247
248
249
250

251
252
253
254
255
256
257
258


259
260
261
262
263
264
265
266

267
268
269
270
271
272
273
























274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290


291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308

309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326






















327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360








































































361
362


363
364
365
366

367
368
369
370
371
372
373




374
375
376
377
378
379
380
381
382
383
384
385
386
387
388

389
390
391
392
393
395
396
397
398
399
400
401

402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419

420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465



466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486


















487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508


































509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580


581
582
583
584
585

586
587
588
589
590



591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608

609
610
611
612
613
614







-
+








+
+







-
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














-
-
-
+
+


















+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+



-
+




-
-
-
+
+
+
+














-
+





            foreach {name value} [split $pair "="] {
              lappend formdata [my Url_Decode $name] [my Url_Decode $value]
            }
          }
        }
      }
    } else {
      foreach pair [split [my http_info getnull QUERY_STRING] "&"] {
      foreach pair [split [my clay get QUERY_STRING] "&"] {
        foreach {name value} [split $pair "="] {
          lappend formdata [my Url_Decode $name] [my Url_Decode $value]
        }
      }
    }
    return $formdata
  }

  # Stream [arg length] bytes from the [arg chan] socket, but only of the request is a
  # POST or PUSH. Returns an empty string otherwise.
  method PostData {length} {
    my variable postdata
    # Run this only once
    if {[info exists postdata]} {
      return $postdata
    }
    set postdata {}
    if {[my http_info get REQUEST_METHOD] in {"POST" "PUSH"}} {
    if {[my request get REQUEST_METHOD] in {"POST" "PUSH"}} {
      my variable chan
      chan configure $chan -translation binary -blocking 0 -buffering full -buffersize 4096
      set postdata [::coroutine::util::read $chan $length]
    }
    return $postdata
  }

  # Manage session data
  method Session_Load {} {}



  # Intended to be invoked from [cmd {chan copy}] as a callback. This closes every channel
  # fed to it on the command line, and then destroys the object.
  #
  # [example {
  #     ###
  #     # Output the body
  #     ###
  #     chan configure $sock -translation binary -blocking 0 -buffering full -buffersize 4096
  #     chan configure $chan -translation binary -blocking 0 -buffering full -buffersize 4096
  #     if {$length} {
  #       ###
  #       # Send any POST/PUT/etc content
  #       ###
  #       chan copy $sock $chan -size $SIZE -command [info coroutine]
  #       yield
  #     }
  #     catch {close $sock}
  #     chan flush $chan
  # }]
  method TransferComplete args {
    my variable chan transfer_complete
    set transfer_complete 1
    my log TransferComplete
    set chan {}
    foreach c $args {
      catch {chan event $c readable {}}
      catch {chan event $c writable {}}
      catch {chan flush $c}
      catch {chan close $c}
    }
    my destroy
  }

  ###
  # Append to the result buffer
  ###
  # Appends the value of [arg string] to the end of [arg reply_body], as well as a trailing newline
  # character.
  method puts line {
    my variable reply_body
    append reply_body $line \n
  }

  method RequestFind {field} {
    my variable request
    if {[dict exists $request $field]} {
      return $field
    }
    foreach item [dict keys $request] {
      if {[string tolower $item] eq [string tolower $field]} {
        return $item
      }
    }
    return $field
  }

  method request {subcommand args} {
  dictobj request request {
    field {
      tailcall my RequestFind [lindex $args 0]
    }
    get {
      set field [my RequestFind [lindex $args 0]]
      if {![dict exists $request $field]} {
        return {}
      }
      tailcall dict get $request $field
    }
    getnull {
      set field [my RequestFind [lindex $args 0]]
      if {![dict exists $request $field]} {
        return {}
      }
      tailcall dict get $request $field

    my variable request
    switch $subcommand {
      dump {
        return $request
      }
      field {
        tailcall my RequestFind [lindex $args 0]
      }
      get {
        set field [my RequestFind [lindex $args 0]]
        if {![dict exists $request $field]} {
          return {}
        }
        tailcall dict get $request $field
      }
      getnull {
        set field [my RequestFind [lindex $args 0]]
        if {![dict exists $request $field]} {
          return {}
        }
        tailcall dict get $request $field
      }
    }
    exists {
      set field [my RequestFind [lindex $args 0]]
      tailcall dict exists $request $field
    }
    parse {
      if {[catch {my MimeParse [lindex $args 0]} result]} {
        my error 400 $result
        tailcall my DoOutput
      }
      set request $result
    }
  }

  dictobj reply reply {
    output {
      set result {}
      if {![dict exists $reply Status]} {
        set status {200 OK}
      } else {
        set status [dict get $reply Status]
      }
      set result "[my EncodeStatus $status]\n"
      foreach {f v} $reply {
        if {$f in {Status}} continue
        append result "[string trimright $f :]: $v\n"
      }
      #append result \n
      return $result
    }
  }


  ###
      exists {
        set field [my RequestFind [lindex $args 0]]
        tailcall dict exists $request $field
      }
      parse {
        if {[catch {my MimeParse [lindex $args 0]} result]} {
          my error 400 $result
          tailcall my DoOutput
        }
        set request $result
      }
      replace {
        set request [lindex $args 0]
      }
      set {
        dict set request {*}$args
      }
      default {
        error "Unknown command $subcommand. Valid: field, get, getnull, exists, parse, replace, set"
      }
    }
  }

  method reply {subcommand args} {
    my variable reply
    switch $subcommand {
      dump {
        return $reply
      }
      exists {
        return [dict exists $reply {*}$args]
      }
      get -
      getnull {
        return [dict getnull $reply {*}$args]
      }
      replace {
        set reply [my HttpHeaders_Default]
        if {[llength $args]==1} {
          foreach {f v} [lindex $args 0] {
            dict set reply $f $v
          }
        } else {
          foreach {f v} $args {
            dict set reply $f $v
          }
        }
      }
      output {
        set result {}
        if {![dict exists $reply Status]} {
          set status {200 OK}
        } else {
          set status [dict get $reply Status]
        }
        set result "[my EncodeStatus $status]\n"
        foreach {f v} $reply {
          if {$f in {Status}} continue
          append result "[string trimright $f :]: $v\n"
        }
        #append result \n
        return $result
      }
      set {
        dict set reply {*}$args
      }
      default {
        error "Unknown command $subcommand. Valid: exists, get, getnull, output, replace, set"
      }
    }
  }

  # Reset the result
  ###
  # Clear the contents of the [arg reply_body] variable, and reset all headers in the [cmd reply]
  # structure back to the defaults for this object.
  method reset {} {
    my variable reply_body
    my reply replace    [my HttpHeaders_Default]
    my reply set Server [my <server> cget server_string]
    my reply set Server [my <server> clay get server/ string]
    my reply set Date [my timestamp]
    set reply_body {}
  }

  ###
  # Return true of this class as waited too long to respond
  ###
  # Called from the [cmd http::server] object which spawned this reply. Checks to see
  # if too much time has elapsed while waiting for data or generating a reply, and issues
  # a timeout error to the request if it has, as well as destroy the object and close the
  # [arg chan] socket.
  method timeOutCheck {} {
    my variable dispatched_time
    if {([clock seconds]-$dispatched_time)>120} {
      ###
      # Something has lasted over 2 minutes. Kill this
      ###
      catch {
        my error 408 {Request Timed out}
        my DoOutput
      }
    }
  }

  ###
  # Return a timestamp
  # Return the current system time in the format: [example {%a, %d %b %Y %T %Z}]
  ###
  method timestamp {} {
    return [clock format [clock seconds] -format {%a, %d %b %Y %T %Z}]
  }
}

Changes to modules/httpd/build/scgi.tcl.

1
2
3







4

5

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33
34
35
36
37
38


39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55

56
57
58
59

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88





89
90
91
92
93
94
95

96
97
98
99
100
101
102
103
104
105
106
107

108
109
110
111
112



113
114



115
116
117
118
119
120
121
122
123
124
125
126
127
128




129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

146
147
148

149
150
151


152
153

154
155
156
157
158
159
160
161
162









163
164
165
166






167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

188
189
190


191
192
193
194

195
196
197
198
199
200
201
202







203

204
205
206
1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
39
40
41
42
43
44


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

64
65
66

67
68
69
70
71
72
73
74
75
76





77
78
79
80
81
82
83
84
85







86
87
88
89
90







91





92
93
94
95
96
97

98
99
100



101
102
103
104

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119


120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137



138



139



140
141
142

143
144
145







146
147
148
149
150
151
152
153
154




155
156
157
158
159
160





















161



162
163




164








165
166
167
168
169
170
171

172
173
174
175



+
+
+
+
+
+
+
-
+

+


















-
+












-
-
+
+
















+
-
+


-

+








-
-
-
-
-









-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
+
-
-
-
-
-






-
+


-
-
-
+
+
+

-
+
+
+












-
-
+
+
+
+














-
-
-
+
-
-
-
+
-
-
-
+
+

-
+


-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
+
+
-
-
-
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+



###
# Return data from an SCGI process
###
::clay::define ::httpd::protocol.scgi {

  method EncodeStatus {status} {
    return "Status: $status"
  }
}

::tool::define ::httpd::content.scgi {
::clay::define ::httpd::content.scgi {
  superclass ::httpd::content.proxy


  method scgi_info {} {
    ###
    # This method should check if a process is launched
    # or launch it if needed, and return a list of
    # HOST PORT SCRIPT_NAME
    ###
    # return {localhost 8016 /some/path}
    error unimplemented
  }

  method proxy_channel {} {
    set sockinfo [my scgi_info]
    if {$sockinfo eq {}} {
      my error 404 {Not Found}
      tailcall my DoOutput
    }
    lassign $sockinfo scgihost scgiport scgiscript
    my http_info set SCRIPT_NAME $scgiscript
    my clay set  SCRIPT_NAME $scgiscript
    if {![string is integer $scgiport]} {
      my error 404 {Not Found}
      tailcall my DoOutput
    }
    return [::socket $scgihost $scgiport]
  }

  method ProxyRequest {chana chanb} {
    chan event $chanb writable {}
    my log ProxyRequest {}
    chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
    chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
    set info [dict create CONTENT_LENGTH 0 SCGI 1.0 SCRIPT_NAME [my http_info get SCRIPT_NAME]]
    foreach {f v} [my http_info dump] {
    set info [dict create CONTENT_LENGTH 0 SCGI 1.0 SCRIPT_NAME [my clay get SCRIPT_NAME]]
    foreach {f v} [my request dump] {
      dict set info $f $v
    }
    set length [dict get $info CONTENT_LENGTH]
    set block {}
    foreach {f v} $info {
      append block [string toupper $f] \x00 $v \x00
    }
    chan puts -nonewline $chanb "[string length $block]:$block,"
    # Light off another coroutine
    #set cmd [list coroutine [my CoroName] {*}[namespace code [list my ProxyReply $chanb $chana]]]
    if {$length} {
      chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
      chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
      ###
      # Send any POST/PUT/etc content
      ###
      my ChannelCopy $chana $chanb -size $length
      chan copy $chana $chanb -size $length -command [info coroutine]
      #chan copy $chana $chanb -size $length -command [info coroutine]
    } else {
      chan flush $chanb
      chan event $chanb readable [info coroutine]
    }
    chan event $chanb readable [info coroutine]
    yield
  }

  method ProxyReply {chana chanb args} {
    my log ProxyReply [list args $args]
    chan event $chana readable {}
    set replyhead [my HttpHeaders $chana]
    set replydat  [my MimeParse $replyhead]
    if {![dict exists $replydat Content-Length]} {
      set length 0
    } else {
      set length [dict get $replydat Content-Length]
    }
    ###
    # Convert the Status: header from the CGI process to
    # a standard service reply line from a web server, but
    # otherwise spit out the rest of the headers verbatim
    ###
    set replybuffer "HTTP/1.0 [dict get $replydat Status]\n"
    append replybuffer $replyhead
    chan configure $chanb -translation {auto crlf} -blocking 0 -buffering full -buffersize 4096
    chan puts $chanb $replybuffer
    my log SendReply [list length $length]
    if {$length} {
      ###
      # Output the body
      ###
      chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
      chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
    ###
    # Output the body. With no -size flag, channel will copy until EOF
    ###
    chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
    chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
      chan copy $chana $chanb -size $length -command [namespace code [list my TransferComplete $chana $chanb]]
    } else {
      my TransferComplete $chan $chanb
    }
  }
}

    my ChannelCopy $chana $chanb -chunk 4096
tool::define ::httpd::reply.scgi {
  superclass ::httpd::reply

  method EncodeStatus {status} {
    return "Status: $status"
  }
}

###
# Act as an  SCGI Server
###
tool::define ::httpd::server.scgi {
::clay::define ::httpd::server.scgi {
  superclass ::httpd::server

  property socket buffersize   32768
  property socket blocking     0
  property socket translation  {binary binary}
  clay set socket/ buffersize   32768
  clay set socket/ blocking     0
  clay set socket/ translation  {binary binary}

  property reply_class ::httpd::reply.scgi
  method debug args {
    puts $args
  }

  method Connect {uuid sock ip} {
    yield [info coroutine]
    chan event $sock readable {}
    chan configure $sock \
        -blocking 1 \
        -translation {binary binary} \
        -buffersize 4096 \
        -buffering none
    my counter url_hit
    try {
      # Read the SCGI request on byte at a time until we reach a ":"
      dict set query REQUEST_URI /
      dict set query REMOTE_ADDR     $ip
      dict set query http HTTP_HOST {}
      dict set query http CONTENT_LENGTH 0
      dict set query http REQUEST_URI /
      dict set query http REMOTE_ADDR $ip
      set size {}
      while 1 {
        set char [::coroutine::util::read $sock 1]
        if {[chan eof $sock]} {
          catch {close $sock}
          return
        }
        if {$char eq ":"} break
        append size $char
      }
      # With length in hand, read the netstring encoded headers
      set inbuffer [::coroutine::util::read $sock [expr {$size+1}]]
      chan configure $sock -blocking 0 -buffersize 4096 -buffering full
      foreach {f v} [lrange [split [string range $inbuffer 0 end-1] \0] 0 end-1] {
        dict set query $f $v
        if {$f in {CONTENT_LENGTH CONTENT_TYPE}} {
          dict set query http $f $v
        dict set query http $f $v
        } elseif {[string range $f 0 4] eq "HTTP_"} {
          dict set query http [string range $f 5 end] $v
        }
      }
      }
      if {![dict exists $query REQUEST_PATH]} {
        set uri [dict get $query REQUEST_URI]
      if {![dict exists $query http REQUEST_PATH]} {
        set uri [dict get $query http REQUEST_URI]
        set uriinfo [::uri::split $uri]
        dict set query REQUEST_PATH    [dict get $uriinfo path]
        dict set query http REQUEST_PATH    [dict get $uriinfo path]
      }
      set reply [my dispatch $query]
      dict with query {}
      if {[llength $reply]} {
        if {[dict exists $reply class]} {
          set class [dict get $reply class]
        } else {
          set class [my cget reply_class]
        }
    } on error {err errdat} {
      my debug [list uri: [dict getnull $query http REQUEST_URI] ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      my log BadRequest $uuid [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      catch {chan puts $sock "HTTP/1.0 400 Bad Request (The data is invalid)"}
      catch {chan event readable $sock {}}
      catch {chan event writeable $sock {}}
      catch {chan close $sock}
      return
    }
        set pageobj [$class create [namespace current]::reply$uuid [self]]
        if {[dict exists $reply mixin]} {
          oo::objdefine $pageobj mixin [dict get $reply mixin]
        }
    if {[dict size $reply]==0} {
      my log BadLocation $uuid $query
      dict set query http HTTP_STATUS 404
      dict set query template notfound
      dict set query mixin reply ::httpd::content.template
    }
        $pageobj dispatch $sock $reply
        my log HttpAccess $REQUEST_URI
      } else {
        try {
          my log HttpMissing $REQUEST_URI
          chan puts $sock "Status: 404 NOT FOUND"
          dict with query {}
          set body [subst [my template notfound]]
          chan puts $sock "Content-Length: [string length $body]"
          chan puts $sock {}
          chan puts $sock $body
        } on error {err errdat} {
          my <server> debug "FAILED ON 404: $err [dict get $errdat -errorinfo]"
        } finally {
          catch {chan event readable $sock {}}
          catch {chan event writeable $sock {}}
          catch {chan close $sock}
        }
      }
    } on error {err errdat} {
      try {
    try {
        my <server> debug [dict get $errdat -errorinfo]
        chan puts $sock "Status: 500 INTERNAL ERROR - scgi 298"
        dict with query {}
      set pageobj [::httpd::reply create ::httpd::object::$uuid [self]]
      dict set reply mixin protocol ::httpd::protocol.scgi
        set body [subst [my template internal_error]]
        chan puts $sock "Content-Length: [string length $body]"
        chan puts $sock {}
        chan puts $sock $body
      $pageobj dispatch $sock $reply
        my log HttpError [list error [my http_info get REMOTE_ADDR] errorinfo [dict get $errdat -errorinfo]]
      } on error {err errdat} {
        my log HttpFatal [list error [my http_info get REMOTE_ADDR] errorinfo [dict get $errdat -errorinfo]]
        my <server> debug "Failed on 500: [dict get $errdat -errorinfo]""
      } finally {
        catch {chan event readable $sock {}}
        catch {chan event writeable $sock {}}
        catch {chan close $sock}
    } on error {err errdat} {
      my debug [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      my log BadRequest $uuid [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      catch {$pageobj destroy}
      catch {chan event readable $sock {}}
      catch {chan event writeable $sock {}}
      catch {chan close $sock}
      }
      return
    }
  }
}

Deleted modules/httpd/build/server.man.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
[section {Class ::httpd::server}]

This class is the root object of the webserver. It is responsible
for opening the socket and providing the initial connection negotiation.

[list_begin definitions]
[call constructor ?port [opt port]? ?myaddr [opt ipaddr]|all? ?server_string [opt string]? ?server_name [opt string]?]
Build a new server object. [opt port] is the port to listen on

[call method [cmd add_uri] [arg pattern] [arg dict]]
Set the hander for a URI pattern. Information given in the [arg dict] is stored
in the data structure the [cmd dispatch] method uses. If a field called
[arg mixin] is given, that class will be mixed into the reply object immediately
after construction.

[call method [cmd connect] [arg sock] [arg ip] [arg port]]

Reply to an open socket. This method builds a coroutine to manage the remainder
of the connection. The coroutine's operations are driven by the [cmd Connect] method.

[call method [cmd Connect] [arg uuid] [arg sock] [arg ip]]

This method reads HTTP headers, and then consults the [cmd dispatch] method to
determine if the request is valid, and/or what kind of reply to generate. Under
normal cases, an object of class [cmd ::http::reply] is created.

Fields the server are looking for in particular are:

class: A class to use instead of the server's own [arg reply_class]

mixin: A class to be mixed into the new object after construction.

All other fields are passed along to the [cmd http_info] structure of the
reply object.

After the class is created and the mixin is mixed in, the server invokes the
reply objects [cmd dispatch] method. This action passes control of the socket to
the reply object. The reply object manages the rest of the transaction, including
closing the socket.

[call method [cmd counter] [arg which]]

Increment an internal counter.

[call method [cmd CheckTimeout]]

Check open connections for a time out event.

[call method [cmd dispatch] [arg header_dict]]

Given a key/value list of information, return a data structure describing how
the server should reply.

[call method [cmd log] [arg args]]

Log an event. The input for args is free form. This method is intended
to be replaced by the user, and is a noop for a stock http::server object.

[call method [cmd port_listening]]

Return the actual port that httpd is listening on.

[call method [cmd PrefixNormalize] [arg prefix]]

For the stock version, trim trailing /'s and *'s from a prefix. This
method can be replaced by the end user to perform any other transformations
needed for the application.

[call method [cmd start]]

Open the socket listener.

[call method [cmd stop]]

Shut off the socket listener, and destroy any pending replies.

[call method [cmd template] [arg page]]

Return a template for the string [arg page]

[call method [cmd TemplateSearch] [arg page]]

Perform a search for the template that best matches [arg page]. This
can include local file searches, in-memory structures, or even
database lookups. The stock implementation simply looks for files
with a .tml or .html extension in the [opt doc_root] directory.

[call method [cmd Validate_Connection] [arg sock] [arg ip]]


Given a socket and an ip address, return true if this connection should
be terminated, or false if it should be allowed to continue. The stock
implementation always returns 0. This is intended for applications to
be able to implement black lists and/or provide security based on IP
address.

[list_end]

Changes to modules/httpd/build/server.tcl.

1
2

3



4
5
6
7
8

9
10
11
12
13
14
15
16
17








18
19
20
21



22
23
24


25
26
27



















28
29
30
31
32
33
34




35
36
37
38


39
40
41
42
43
44
45
46
47
48











































49
50
51
52
53
54
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82
83
84


85
86
87

88
89
90
91
92
93
94
95
96
97
98
99


100
101
102
103



104
105
106
107
108
109
110
111

112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135

136
137
138
139
140
141
142
143



144
145
146
147
148
149
150

151
152
153
154
155
156
157
158
159
160
161
162
163
164

165

166
167




168
169
170





171
172
173
174
175

176
177
178
179
180
181
182

183
184
185
186
187


















188
189





190
191

192
193
194
195
196
197
198
199




200
201
202
203














204
205
206
207
208
209
210
211
212


213
214
215
216
217


218
219
220
221
222





223
224

225
226
227
228
229
230
231
232
233
234
235
236
237
238
239

240
241

242
243
244
245
246
247
248
249

250
251
252
253
254
255
256
257

258
259
260
261
262
263
264
265
266
267
268
269



270
271
272
273
274



275
276
277
278
279
280
281
282
283
284
285

286
287
288
289
290
291
292


293
294

295
296
297
298
299
300

301
302
303
304
305
306
307
308
309
310
311
312
313

314
315
316
317
318
319
320
321
322
323
324


325






326
327
328
329
330
331
332
333
334




335
336

337
338
339
340
341
342
343
344
345
346
347


348
349
350
351

352
353
354
355
356
357


358
359
360
361
362
363

364
365
366
367
368
369
370



371
372
373
374
375




376
377







378
379
380
381
382
383
384
385





386
387
388
389
390
391
392
393
394
395

396
397
1

2

3
4
5
6
7
8
9

10
11
12







13
14
15
16
17
18
19
20
21



22
23
24
25


26
27
28


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

119
120
121
122

123

124

125












126

127









128
129



130

131
132
133
134
135
136
137
138
139


140
141
142



143
144
145
146







147
























148








149
150
151
152
153
154
155
156
157

158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173

174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192

193
194
195
196
197
198
199

200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231

232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269


270
271
272
273
274
275

276
277
278
279
280
281
282
283
284
285
286
287
288

289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306

307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323

324
325
326
327
328
329
330
331
332
333



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361


362
363
364

365
366
367
368
369
370

371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398

399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418

419
420
421
422
423
424
425
426
427
428


429
430
431
432
433

434
435
436
437
438


439
440
441
442
443
444
445

446
447
448
449
450



451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476



477
478
479
480
481
482
483
484
485
486
487
488
489
490

491
492
493

-
+
-
+
+
+




-
+


-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
+
+
+

-
-
+
+

-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
+
+
+


-
-
+
+










+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-




-

-

-
+
-
-
-
-
-
-
-
-
-
-
-
-

-
+
-
-
-
-
-
-
-
-
-
+
+
-
-
-
+
-









-
-
+
+

-
-
-
+
+
+

-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
+
+
+






-
+














+
-
+


+
+
+
+



+
+
+
+
+




-
+






-
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+
+
+
+

-
+








+
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
-
+
+




-
+
+





+
+
+
+
+

-
+















+

-
+








+







-
+









-
-
-
+
+
+





+
+
+











+





-
-
+
+

-
+





-
+













+











+
+
-
+
+
+
+
+
+









+
+
+
+

-
+









-
-
+
+



-
+




-
-
+
+





-
+




-
-
-
+
+
+





+
+
+
+


+
+
+
+
+
+
+





-
-
-
+
+
+
+
+









-
+


###
# An httpd server with a template engine
# An httpd server with a template engine and a shim to insert URL domains.
# and a shim to insert URL domains
#
# This class is the root object of the webserver. It is responsible
# for opening the socket and providing the initial connection negotiation.
###
namespace eval ::httpd::object {}
namespace eval ::httpd::coro {}

::tool::define ::httpd::server {
::clay::define ::httpd::server {
  superclass ::httpd::mime

  option port  {default: auto}
  option myaddr {default: 127.0.0.1}
  option server_string [list default: [list TclHttpd $::httpd::version]]
  option server_name [list default: [list [info hostname]]]
  option doc_root {default {}}
  option reverse_dns {type boolean default 0}
  option configuration_file {type filename default {}}
  clay set server/ port auto
  clay set server/ myaddr 127.0.0.1
  clay set server/ string [list TclHttpd $::httpd::version]
  clay set server/ name [info hostname]
  clay set server/ doc_root {}
  clay set server/ reverse_dns 0
  clay set server/ configuration_file {}
  clay set server/ protocol {HTTP/1.1}

  property socket buffersize   32768
  property socket translation  {auto crlf}
  property reply_class ::httpd::reply
  clay set socket/ buffersize   32768
  clay set socket/ translation  {auto crlf}
  clay set reply_class ::httpd::reply

  array template
  variable url_patterns {}
  Array template
  Dict url_patterns {}

  constructor {args} {
    my configure {*}$args
  constructor {
  {args {
    port        {default auto      comment {Port to listen on}}
    myaddr      {default 127.0.0.1 comment {IP address to listen on. "all" means all}}
    string      {default auto      comment {Value for SERVER_SOFTWARE in HTTP headers}}
    name        {default auto      comment {Value for SERVER_NAME in HTTP headers. Defaults to [info hostname]}}
    doc_root    {default {}        comment {File path to serve.}}
    reverse_dns {default 0         comment {Perform reverse DNS to convert IPs into hostnames}}
    configuration_file {default {} comment {Configuration file to load into server namespace}}
    protocol    {default {HTTP/1.1} comment {Value for SERVER_PROTOCOL in HTTP headers}}
  }}} {
    if {[llength $args]==1} {
      set arglist [lindex $args 0]
    } else {
      set arglist $args
    }
    foreach {var val} $arglist {
      my clay set server/ $var $val
    }
    my start
  }

  destructor {
    my stop
  }

  ###
  # Reply to an open socket. This method builds a coroutine to manage the remainder
  # of the connection. The coroutine's operations are driven by the [cmd Connect] method.
  ###
  method connect {sock ip port} {
    ###
    # If an IP address is blocked
    # send a "go to hell" message
    # If an IP address is blocked drop the
    # connection
    ###
    if {[my Validate_Connection $sock $ip]} {
      catch {close $sock}
      return
    }
    set uuid [my Uuid_Generate]
    set coro [coroutine ::httpd::coro::$uuid {*}[namespace code [list my Connect $uuid $sock $ip]]]
    chan event $sock readable $coro
  }

  method ServerHeaders {ip http_request mimetxt} {
    set result {}
    dict set result HTTP_HOST {}
    dict set result CONTENT_LENGTH 0
    foreach {f v} [my MimeParse $mimetxt] {
      set fld [string toupper [string map {- _} $f]]
      if {$fld in {CONTENT_LENGTH CONTENT_TYPE}} {
        set qfld $fld
      } else {
        set qfld HTTP_$fld
      }
      dict set result $qfld $v
    }
    dict set result REMOTE_ADDR     $ip
    dict set result REMOTE_HOST     [my HostName $ip]
    dict set result REQUEST_METHOD  [lindex $http_request 0]
    set uriinfo [::uri::split [lindex $http_request 1]]
    dict set result uriinfo $uriinfo
    dict set result REQUEST_URI     [lindex $http_request 1]
    dict set result REQUEST_PATH    [dict get $uriinfo path]
    dict set result REQUEST_VERSION [lindex [split [lindex $http_request end] /] end]
    dict set result DOCUMENT_ROOT   [my clay get server/ doc_root]
    dict set result QUERY_STRING    [dict get $uriinfo query]
    dict set result REQUEST_RAW     $http_request
    dict set result SERVER_PORT     [my port_listening]
    dict set result SERVER_NAME     [my clay get server/ name]
    dict set result SERVER_PROTOCOL [my clay get server/ protocol]
    dict set result SERVER_SOFTWARE [my clay get server/ string]
    if {[string match 127.* $ip]} {
      dict set result LOCALHOST [expr {[lindex [split [dict getnull $result HTTP_HOST] :] 0] eq "localhost"}]
    }
    return $result
  }

  ###
  # This method reads HTTP headers, and then consults the [cmd dispatch] method to
  # determine if the request is valid, and/or what kind of reply to generate. Under
  # normal cases, an object of class [cmd ::http::reply] is created, and that class's
  # [cmd dispatch] method.
  # This action passes control of the socket to
  # the reply object. The reply object manages the rest of the transaction, including
  # closing the socket.
  ###
  method Connect {uuid sock ip} {
    yield [info coroutine]
    chan event $sock readable {}

    chan configure $sock \
      -blocking 0 \
      -translation {auto crlf} \
      -buffering line

    my counter url_hit
    set line {}
    try {
      set readCount [::coroutine::util::gets_safety $sock 4096 line]
      set readCount [::coroutine::util::gets_safety $sock 4096 http_request]
      dict set query UUID $uuid
      dict set query REMOTE_ADDR     $ip
      dict set query REMOTE_HOST     [my HostName $ip]
      dict set query REQUEST_METHOD  [lindex $line 0]
      set uriinfo [::uri::split [lindex $line 1]]
      dict set query REQUEST_URI     [lindex $line 1]
      dict set query REQUEST_PATH    [dict get $uriinfo path]
      dict set query REQUEST_VERSION [lindex [split [lindex $line end] /] end]
      dict set query DOCUMENT_ROOT   [my cget doc_root]
      dict set query QUERY_STRING    [dict get $uriinfo query]
      dict set query REQUEST_RAW     $line
      dict set query SERVER_PORT     [my port_listening]
      set mimetxt [my HttpHeaders $sock]
      dict set query mimetxt $mimetxt
      dict set query UUID $uuid
      foreach {f v} [my MimeParse $mimetxt] {
        set fld [string toupper [string map {- _} $f]]
        if {$fld in {CONTENT_LENGTH CONTENT_TYPE}} {
          set qfld $fld
        } else {
          set qfld HTTP_$fld
        }
        dict set query $qfld $v
        dict set query http $fld $v
      dict set query mimetxt $mimetxt
      dict set query mixin style [my clay get server/ style]
      }
      if {[string match 127.* $ip]} {
        dict set query LOCALHOST [expr {[lindex [split [dict getnull $query HTTP_HOST] :] 0] eq "localhost"}]
      dict set query http [my ServerHeaders $ip $http_request $mimetxt]
      }
      my Headers_Process query
      set reply [my dispatch $query]
    } on error {err errdat} {
      my debug [list uri: [dict getnull $query REQUEST_URI] ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      my log BadRequest $uuid [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      catch {chan puts $sock "HTTP/1.0 400 Bad Request (The data is invalid)"}
      catch {chan close $sock}
      return
    }
    if {[llength $reply]==0} {
      my log BadLocation $uuid $query
    if {[dict size $reply]==0} {
      set reply $query
      my log BadLocation $uuid $query
      dict set query HTTP_STATUS 404
      dict set query template notfound
      dict set query mixinmap reply ::httpd::content.template
      dict set reply http HTTP_STATUS {404 Not Found}
      dict set reply template notfound
      dict set reply mixin reply ::httpd::content.template
    }
    try {
      if {[dict exists $reply class]} {
        set class [dict get $reply class]
      } else {
        set class [my cget reply_class]
      }
      set pageobj [$class create ::httpd::object::$uuid [self]]
    set pageobj [::httpd::reply create ::httpd::object::$uuid [self]]
      if {[dict exists $reply mixinmap]} {
        set mixinmap [dict get $reply mixinmap]
      } else {
        set mixinmap {}
      }
      if {[dict exists $reply mixin]} {
        dict set mixinmap reply [dict get $reply mixin]
      }
      foreach item [dict keys $reply MIXIN_*] {
        set slot [string range $reply 6 end]
        dict set mixinmap [string tolower $slot] [dict get $reply $item]
      }
      $pageobj mixinmap {*}$mixinmap
      if {[dict exists $reply organ]} {
        $pageobj graft {*}[dict get $reply organ]
      }
    } on error {err errdat} {
      my debug [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      my log BadRequest $uuid [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      catch {$pageobj destroy}
      catch {chan close $sock}
    }
    try {
      $pageobj dispatch $sock $reply
    tailcall $pageobj dispatch $sock $reply
    } on error {err errdat} {
      my debug [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      my log BadRequest $uuid [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      catch {$pageobj destroy}
      catch {chan close $sock}
    }
  }

  }

  # Increment an internal counter.
  method counter which {
    my variable counters
    incr counters($which)
  }

  ###
  # Clean up any process that has gone out for lunch
  # Check open connections for a time out event.
  ###
  method CheckTimeout {} {
    foreach obj [info commands ::httpd::object::*] {
      try {
        $obj timeOutCheck
      } on error {} {
        catch {$obj destroy}
      }
    }
  }

  method debug args {}

  ###
  # Given a key/value list of information, return a data structure describing how
  # Route a request to the appropriate handler
  # the server should reply.
  ###
  method dispatch {data} {
    set reply [my Dispatch_Local $data]
    if {[dict size $reply]} {
      return $reply
    }
    return [my Dispatch_Default $data]
  }

  ###
  # Method dispatch method of last resort before returning a 404 NOT FOUND error.
  # The default behavior is to look for a file in [emph DOCUMENT_ROOT] which
  # matches the query.
  ###
  method Dispatch_Default {reply} {
    ###
    # Fallback to docroot handling
    ###
    set doc_root [dict get $reply DOCUMENT_ROOT]
    set doc_root [dict getnull $reply http DOCUMENT_ROOT]
    if {$doc_root ne {}} {
      ###
      # Fall back to doc_root handling
      ###
      dict set reply prefix {}
      dict set reply path $doc_root
      dict set reply mixinmap reply httpd::content.file
      dict set reply mixin reply httpd::content.file
      return $reply
    }
    return {}
  }

  ###
  # Method dispatch method invoked prior to invoking methods implemented by plugins.
  # If this method returns a non-empty dictionary, that structure will be passed to
  # the reply. The default is an empty implementation.
  ###
  method Dispatch_Local data {}

  ###
  # Introspect and possibly modify a data structure destined for a reply. This
  # method is invoked before invoking Header methods implemented by plugins.
  # The default implementation is empty.
  ###
  method Headers_Local {varname} {}

  ###
  # Introspect and possibly modify a data structure destined for a reply. This
  # method is built dynamically by the [cmd plugin] method.
  ###
  method Headers_Process varname {}

  ###
  # Convert an ip address to a host name. If the server/ reverse_dns flag
  # is false, this method simply returns the IP address back.
  # Internally, this method uses the [emph dns] module from tcllib.
  ###
  method HostName ipaddr {
    if {![my cget reverse_dns]} {
    if {![my clay get server/ reverse_dns]} {
      return $ipaddr
    }
    set t [::dns::resolve $ipaddr]
    set result [::dns::name $t]
    ::dns::cleanup $t
    return $result
  }

  ###
  # Log an event. The input for args is free form. This method is intended
  # to be replaced by the user, and is a noop for a stock http::server object.
  ###
  method log args {
    # Do nothing for now
  }

  ###
  # Incorporate behaviors from a plugin.
  # This method dynamically rebuilds the [cmd Dispatch] and [cmd Headers]
  # method. For every plugin, the server looks for the following entries in
  # [emph "clay plugin/"]:
  # [para]
  # [emph load] - A script to invoke in the server's namespace during the [cmd plugin] method invokation.
  # [para]
  # [emph dispatch] - A script to stitch into the server's [cmd Dispatch] method.
  # [para]
  # [emph headers] - A script to stitch into the server's [cmd Headers] method.
  # [para]
  # [emph thread] - A script to stitch into the server's [cmd Thread_start] method.
  ###
  method plugin {slot {class {}}} {
    if {$class eq {}} {
      set class ::httpd::plugin.$slot
    }
    if {[info command $class] eq {}} {
      error "Class $class for plugin $slot does not exist"
    }
    my mixinmap $slot $class
    my variable mixinmap
    my clay mixinmap $slot $class
    set mixinmap [my clay get mixin]

    ###
    # Perform action on load
    ###
    eval [$class meta getnull plugin load:]
    set script [$class clay search plugin/ load]
    eval $script

    ###
    # rebuild the dispatch method
    ###
    set body "\n try \{"
    append body \n {
  set reply [my Dispatch_Local $data]
  if {[dict size $reply]} {return $reply}
}

    foreach {slot class} $mixinmap {
      set script [$class meta getnull plugin dispatch:]
      set script [$class clay search plugin/ dispatch]
      if {[string length $script]} {
        append body \n "# SLOT $slot"
        append body \n $script
      }
    }
    append body \n {  return [my Dispatch_Default $data]}
    append body \n "\} on error \{err errdat\} \{"
    append body \n {  puts [list DISPATCH ERROR [dict get $errdat -errorinfo]] ; return {}}
    append body \n "\}"
    oo::objdefine [self] method dispatch data $body
    ###
    # rebuild the Headers_Process method
    ###
    set body "\n try \{"
    append body \n "  upvar 1 \$varname query"
    append body \n {  my Headers_Local query}
    foreach {slot class} $mixinmap {
      set script [$class meta getnull plugin headers:]
      set script [$class clay search plugin/ headers]
      if {[string length $script]} {
        append body \n "# SLOT $slot"
        append body \n $script
      }
    }
    append body \n "\} on error \{err errdat\} \{"
    append body \n {  puts [list HEADERS ERROR [dict get $errdat -errorinfo]] ; return {}}
    append body \n "\}"

    oo::objdefine [self] method Headers_Process varname $body

    ###
    # rebuild the Threads_Start method
    ###
    set body "\n try \{"
    foreach {slot class} $mixinmap {
      set script [$class meta getnull plugin thread:]
      set script [$class clay search plugin/ thread]
      if {[string length $script]} {
        append body \n "# SLOT $slot"
        append body \n $script
      }
    }
    append body \n "\} on error \{err errdat\} \{"
    append body \n {  puts [list THREAD START ERROR [dict get $errdat -errorinfo]] ; return {}}
    append body \n "\}"
    oo::objdefine [self] method Thread_start {} $body

  }

  }

  # Return the actual port that httpd is listening on.
  method port_listening {} {
    my variable port_listening
    return $port_listening
  }

  # For the stock version, trim trailing /'s and *'s from a prefix. This
  # method can be replaced by the end user to perform any other transformations
  # needed for the application.
  method PrefixNormalize prefix {
    set prefix [string trimright $prefix /]
    set prefix [string trimright $prefix *]
    set prefix [string trimright $prefix /]
    return $prefix
  }

  method source {filename} {
    source $filename
  }

  # Open the socket listener.
  method start {} {
    # Build a namespace to contain replies
    namespace eval [namespace current]::reply {}

    my variable socklist port_listening
    if {[my cget configuration_file] ne {}} {
      source [my cget configuration_file]
    if {[my clay get server/ configuration_file] ne {}} {
      source [my clay get server/ configuration_file]
    }
    set port [my cget port]
    set port [my clay get server/ port]
    if { $port in {auto {}} } {
      package require nettool
      set port [::nettool::allocate_port 8015]
    }
    set port_listening $port
    set myaddr [my cget myaddr]
    set myaddr [my clay get server/ myaddr]
    my debug [list [self] listening on $port $myaddr]

    if {$myaddr ni {all any * {}}} {
      foreach ip $myaddr {
        lappend socklist [socket -server [namespace code [list my connect]] -myaddr $ip $port]
      }
    } else {
      lappend socklist [socket -server [namespace code [list my connect]] $port]
    }
    ::cron::every [self] 120 [namespace code {my CheckTimeout}]
    my Thread_start
  }

  # Shut off the socket listener, and destroy any pending replies.
  method stop {} {
    my variable socklist
    if {[info exists socklist]} {
      foreach sock $socklist {
        catch {close $sock}
      }
    }
    set socklist {}
    ::cron::cancel [self]
  }

  Ensemble SubObject::db {} {
    return [namespace current]::Sqlite_db

  }
  Ensemble SubObject::default {} {
    return [namespace current]::$method
  }

  # Return a template for the string [arg page]
  method template page {
    my variable template
    if {[info exists template($page)]} {
      return $template($page)
    }
    set template($page) [my TemplateSearch $page]
    return $template($page)
  }

  # Perform a search for the template that best matches [arg page]. This
  # can include local file searches, in-memory structures, or even
  # database lookups. The stock implementation simply looks for files
  # with a .tml or .html extension in the [opt doc_root] directory.
  method TemplateSearch page {
    set doc_root [my cget doc_root]
    set doc_root [my clay get server/ doc_root]
    if {$doc_root ne {} && [file exists [file join $doc_root $page.tml]]} {
      return [::fileutil::cat [file join $doc_root $page.tml]]
    }
    if {$doc_root ne {} && [file exists [file join $doc_root $page.html]]} {
      return [::fileutil::cat [file join $doc_root $page.html]]
    }
    switch $page {
      redirect {
return {
[my html header "$HTTP_STATUS"]
The page you are looking for: <b>[my http_info get REQUEST_URI]</b> has moved.
[my html_header "$HTTP_STATUS"]
The page you are looking for: <b>[my request get REQUEST_URI]</b> has moved.
<p>
If your browser does not automatically load the new location, it is
<a href=\"$msg\">$msg</a>
[my html footer]
[my html_footer]
}
      }
      internal_error {
        return {
[my html header "$HTTP_STATUS"]
Error serving <b>[my http_info get REQUEST_URI]</b>:
[my html_header "$HTTP_STATUS"]
Error serving <b>[my request get REQUEST_URI]</b>:
<p>
The server encountered an internal server error: <pre>$msg</pre>
<pre><code>
$errorInfo
</code></pre>
[my html footer]
[my html_footer]
        }
      }
      notfound {
        return {
[my html header "$HTTP_STATUS"]
The page you are looking for: <b>[my http_info get REQUEST_URI]</b> does not exist.
[my html footer]
[my html_header "$HTTP_STATUS"]
The page you are looking for: <b>[my request get REQUEST_URI]</b> does not exist.
[my html_footer]
        }
      }
    }
  }

  ###
  # Built by the [cmd plugin] method. Called by the [cmd start] method. Intended
  # to allow plugins to spawn worker threads.
  ###
  method Thread_start {} {}

  ###
  # Generate a GUUID. Used to ensure every request has a unique ID.
  # The default implementation is:
  # [example {
  #   return [::uuid::uuid generate]
  # }]
  ###
  method Uuid_Generate {} {
    return [::uuid::uuid generate]
  }

  ###
  # Return true if this IP address is blocked
  # The socket will be closed immediately after returning
  # This handler is welcome to send a polite error message
  # Given a socket and an ip address, return true if this connection should
  # be terminated, or false if it should be allowed to continue. The stock
  # implementation always returns 0. This is intended for applications to
  # be able to implement black lists and/or provide security based on IP
  # address.
  ###
  method Validate_Connection {sock ip} {
    return 0
  }
}

###
# Provide a backward compadible alias
###
::tool::define ::httpd::server::dispatch {
::clay::define ::httpd::server::dispatch {
    superclass ::httpd::server
}

Changes to modules/httpd/build/websocket.tcl.

1
2
3
4

5
6
1
2
3

4
5
6



-
+


###
# Upgrade a connection to a websocket
###
::tool::define ::httpd::content.websocket {
::clay::define ::httpd::content.websocket {

}

Changes to modules/httpd/httpd.man.

1

2
3

4
5
6
7
8
9
10
11
12
13
14
15
16

17
18
19
20

21
22
23
24
25
26
27

1
2

3
4
5
6
7
8
9
10
11
12
13
14
15

16




17
18
19
20
21
22
23
24
-
+

-
+












-
+
-
-
-
-
+







[vset VERSION 4.1.1]
[vset VERSION 4.3]
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin tool n [vset VERSION]]
[manpage_begin httpd n [vset VERSION]]
[keywords WWW]
[copyright {2018 Sean Woods <[email protected]>}]
[moddesc   {Tcl Web Server}]
[titledesc {A TclOO and coroutine based web server}]
[category  Networking]
[keywords TclOO]
[keywords http]
[keywords httpd]
[keywords httpserver]
[keywords services]
[require Tcl 8.6]
[require httpd [opt [vset VERSION]]]
[require sha1]
[require uuid]
[require dicttool]
[require oo::meta]
[require oo::dialect]
[require tool]
[require clay]
[require coroutine]
[require fileutil]
[require fileutil::magic::filetype]
[require websocket]
[require mime]
[require cron]
[require uri]
38
39
40
41
42
43
44
45

46
47
48
49



50
51
52
53





















54
55


56
57
58





















































































































































































































































































































































































































































































































































































































































































































































































































































59
60
61
62
63
64

65

35
36
37
38
39
40
41

42
43



44
45
46
47
48


49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903







-
+

-
-
-
+
+
+


-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






+

+
[section {Minimal Example}]

Starting a web service requires starting a class of type
[cmd httpd::server], and providing that server with one or more URIs
to service, and [cmd httpd::reply] derived classes to generate them.

[example {
tool::define ::reply.hello {
oo::class create ::reply.hello {
  method content {} {
    my puts "<HTML><HEAD><TITLE>IRM Dispatch Server</TITLE></HEAD><BODY>"
    my puts "<h1>Hello World!</h1>"
    my puts </BODY></HTML>
my puts "<HTML><HEAD><TITLE>IRM Dispatch Server</TITLE></HEAD><BODY>"
my puts "<h1>Hello World!</h1>"
my puts </BODY></HTML>
  }
}
::docserver::server create HTTPD port 8015 myaddr 127.0.0.1
HTTPD add_uri /* [list mixin reply.hello]
::httpd::server create HTTPD port 8015 myaddr 127.0.0.1 doc_root ~/htdocs
HTTPD plugin dispatch httpd::server::dispatch
HTTPD uri add * /hello [list mixin reply.hello]
}]

The bare module does have facilities to hose a files from a file system. Files that end in a .tml will be substituted in the style of Tclhttpd:

[example {
<!-- hello.tml -->
[my html_header {Hello World!}]
Your Server is running.
<p>
The time is now [clock format [clock seconds]]
[my html_footer]
}]

A complete example of an httpd server is in the /examples directory of Tcllib. It also show how to dispatch URIs to other processes via SCGI and HTTP proxies.

[example {
cd ~/tcl/sandbox/tcllib
tclsh examples/httpd.tcl
}]

[section Classes]
[subsection {Class  httpd::mime}]
[include build/server.man]
[include build/reply.man]
[include build/content.man]

[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "ChannelCopy"] [arg in] [arg out] [opt "[arg args]"]]


[call method [cmd "html_header"] [opt "[arg title] [const ""]"] [opt "[arg args]"]]


[call method [cmd "html_footer"] [opt "[arg args]"]]


[call method [cmd "http_code_string"] [arg code]]


[call method [cmd "HttpHeaders"] [arg sock] [opt "[arg debug] [const ""]"]]


[call method [cmd "HttpHeaders_Default"]]


[call method [cmd "HttpServerHeaders"]]


[call method [cmd "MimeParse"] [arg mimetext]]

 Converts a block of mime encoded text to a key/value list. If an exception is encountered,
 the method will generate its own call to the [cmd error] method, and immediately invoke
 the [cmd output] method to produce an error code and close the connection.




[call method [cmd "Url_Decode"] [arg data]]
 De-httpizes a string.



[call method [cmd "Url_PathCheck"] [arg urlsuffix]]


[call method [cmd "wait"] [arg mode] [arg sock]]


[list_end]
[para]

[subsection {Class  httpd::reply}]
[emph "ancestors"]: [class httpd::mime]
[para]

 A class which shephards a request through the process of generating a
 reply.

 The socket associated with the reply is available at all times as the [arg chan]
 variable.

 The process of generating a reply begins with an [cmd httpd::server] generating a
 [cmd http::class] object, mixing in a set of behaviors and then invoking the reply
 object's [cmd dispatch] method.

 In normal operations the [cmd dispatch] method:

 [list_begin enumerated]
 [enum]
 Invokes the [cmd reset] method for the object to populate default headers.
 [enum]
 Invokes the [cmd HttpHeaders] method to stream the MIME headers out of the socket
 [enum]
 Invokes the [cmd {request parse}] method to convert the stream of MIME headers into a
 dict that can be read via the [cmd request] method.
 [enum]
 Stores the raw stream of MIME headers in the [arg rawrequest] variable of the object.
 [enum]
 Invokes the [cmd content] method for the object, generating an call to the [cmd error]
 method if an exception is raised.
 [enum]
 Invokes the [cmd output] method for the object
 [list_end]
 [para]

 Developers have the option of streaming output to a buffer via the [cmd puts] method of the
 reply, or simply populating the [arg reply_body] variable of the object.
 The information returned by the [cmd content] method is not interpreted in any way.

 If an exception is thrown (via the [cmd error] command in Tcl, for example) the caller will
 auto-generate a 500 {Internal Error} message.

 A typical implementation of [cmd content] look like:

 [example {

 clay::define ::test::content.file {
 	superclass ::httpd::content.file
 	# Return a file
 	# Note: this is using the content.file mixin which looks for the reply_file variable
 	# and will auto-compute the Content-Type
 	method content {} {
 	  my reset
     set doc_root [my request get DOCUMENT_ROOT]
     my variable reply_file
     set reply_file [file join $doc_root index.html]
 	}
 }
 clay::define ::test::content.time {
   # return the current system time
 	method content {} {
 		my variable reply_body
     my reply set Content-Type text/plain
 		set reply_body [clock seconds]
 	}
 }
 clay::define ::test::content.echo {
 	method content {} {
 		my variable reply_body
     my reply set Content-Type [my request get CONTENT_TYPE]
 		set reply_body [my PostData [my request get CONTENT_LENGTH]]
 	}
 }
 clay::define ::test::content.form_handler {
 	method content {} {
 	  set form [my FormData]
 	  my reply set Content-Type {text/html; charset=UTF-8}
     my puts [my html_header {My Dynamic Page}]
     my puts "<BODY>"
     my puts "You Sent<p>"
     my puts "<TABLE>"
     foreach {f v} $form {
       my puts "<TR><TH>$f</TH><TD><verbatim>$v</verbatim></TD>"
     }
     my puts "</TABLE><p>"
     my puts "Send some info:<p>"
     my puts "<FORM action=/[my request get REQUEST_PATH] method POST>"
     my puts "<TABLE>"
     foreach field {name rank serial_number} {
       set line "<TR><TH>$field</TH><TD><input name=\"$field\" "
       if {[dict exists $form $field]} {
         append line " value=\"[dict get $form $field]\"""
       }
       append line " /></TD></TR>"
       my puts $line
     }
     my puts "</TABLE>"
     my puts [my html footer]
 	}
 }

 }]



[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "constructor"] [arg ServerObj] [opt "[arg args]"]]


[call method [cmd "destructor"] [opt "[arg dictargs]"]]

 clean up on exit




[call method [cmd "close"]]

 Close channels opened by this object




[call method [cmd "Log_Dispatched"]]

 Record a dispatch event




[call method [cmd "dispatch"] [arg newsock] [arg datastate]]

 Accept the handoff from the server object of the socket
 [emph newsock] and feed it the state [emph datastate].
 Fields the [emph datastate] are looking for in particular are:
 [para]
 * [const mixin] - A key/value list of slots and classes to be mixed into the
 object prior to invoking [cmd Dispatch].
 [para]
 * [const http] - A key/value list of values to populate the object's [emph request]
 ensemble
 [para]
 All other fields are passed along to the [method clay] structure of the object.




[call method [cmd "Dispatch"]]


[call method [cmd "html_css"]]


[call method [cmd "html_header"] [arg title] [opt "[arg args]"]]


[call method [cmd "html_footer"] [opt "[arg args]"]]


[call method [cmd "error"] [arg code] [opt "[arg msg] [const ""]"] [opt "[arg errorInfo] [const ""]"]]


[call method [cmd "content"]]

 REPLACE ME:
 This method is the "meat" of your application.
 It writes to the result buffer via the "puts" method
 and can tweak the headers via "clay put header_reply"




[call method [cmd "EncodeStatus"] [arg status]]

 Formulate a standard HTTP status header from he string provided.




[call method [cmd "log"] [arg type] [opt "[arg info] [const ""]"]]


[call method [cmd "CoroName"]]


[call method [cmd "DoOutput"]]

 Generates the the HTTP reply, streams that reply back across [arg chan],
 and destroys the object.




[call method [cmd "FormData"]]

 For GET requests, converts the QUERY_DATA header into a key/value list.

 For POST requests, reads the Post data and converts that information to
 a key/value list for application/x-www-form-urlencoded posts. For multipart
 posts, it composites all of the MIME headers of the post to a singular key/value
 list, and provides MIME_* information as computed by the [cmd mime] package, including
 the MIME_TOKEN, which can be fed back into the mime package to read out the contents.




[call method [cmd "PostData"] [arg length]]
 Stream [arg length] bytes from the [arg chan] socket, but only of the request is a
 POST or PUSH. Returns an empty string otherwise.



[call method [cmd "Session_Load"]]
 Manage session data



[call method [cmd "TransferComplete"] [opt "[arg args]"]]
 Intended to be invoked from [cmd {chan copy}] as a callback. This closes every channel
 fed to it on the command line, and then destroys the object.

 [example {
     ###
     # Output the body
     ###
     chan configure $sock -translation binary -blocking 0 -buffering full -buffersize 4096
     chan configure $chan -translation binary -blocking 0 -buffering full -buffersize 4096
     if {$length} {
       ###
       # Send any POST/PUT/etc content
       ###
       chan copy $sock $chan -size $SIZE -command [info coroutine]
       yield
     }
     catch {close $sock}
     chan flush $chan
 }]



[call method [cmd "puts"] [arg line]]
 Appends the value of [arg string] to the end of [arg reply_body], as well as a trailing newline
 character.



[call method [cmd "RequestFind"] [arg field]]


[call method [cmd "request"] [arg subcommand] [opt "[arg args]"]]


[call method [cmd "reply"] [arg subcommand] [opt "[arg args]"]]


[call method [cmd "reset"]]
 Clear the contents of the [arg reply_body] variable, and reset all headers in the [cmd reply]
 structure back to the defaults for this object.



[call method [cmd "timeOutCheck"]]
 Called from the [cmd http::server] object which spawned this reply. Checks to see
 if too much time has elapsed while waiting for data or generating a reply, and issues
 a timeout error to the request if it has, as well as destroy the object and close the
 [arg chan] socket.



[call method [cmd "timestamp"]]

 Return the current system time in the format: [example {%a, %d %b %Y %T %Z}]




[list_end]
[para]

[subsection {Class  httpd::server}]
[emph "ancestors"]: [class httpd::mime]
[para]

[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "constructor"] [arg args] [opt "[arg port] [const "auto"]"] [opt "[arg myaddr] [const "127.0.0.1"]"] [opt "[arg string] [const "auto"]"] [opt "[arg name] [const "auto"]"] [opt "[arg doc_root] [const ""]"] [opt "[arg reverse_dns] [const "0"]"] [opt "[arg configuration_file] [const ""]"] [opt "[arg protocol] [const "HTTP/1.1"]"]]


[call method [cmd "destructor"] [opt "[arg dictargs]"]]


[call method [cmd "connect"] [arg sock] [arg ip] [arg port]]

 Reply to an open socket. This method builds a coroutine to manage the remainder
 of the connection. The coroutine's operations are driven by the [cmd Connect] method.




[call method [cmd "ServerHeaders"] [arg ip] [arg http_request] [arg mimetxt]]


[call method [cmd "Connect"] [arg uuid] [arg sock] [arg ip]]

 This method reads HTTP headers, and then consults the [cmd dispatch] method to
 determine if the request is valid, and/or what kind of reply to generate. Under
 normal cases, an object of class [cmd ::http::reply] is created, and that class's
 [cmd dispatch] method.
 This action passes control of the socket to
 the reply object. The reply object manages the rest of the transaction, including
 closing the socket.




[call method [cmd "counter"] [arg which]]
 Increment an internal counter.



[call method [cmd "CheckTimeout"]]

 Check open connections for a time out event.




[call method [cmd "debug"] [opt "[arg args]"]]


[call method [cmd "dispatch"] [arg data]]

 Given a key/value list of information, return a data structure describing how
 the server should reply.




[call method [cmd "Dispatch_Default"] [arg reply]]

 Method dispatch method of last resort before returning a 404 NOT FOUND error.
 The default behavior is to look for a file in [emph DOCUMENT_ROOT] which
 matches the query.




[call method [cmd "Dispatch_Local"] [arg data]]

 Method dispatch method invoked prior to invoking methods implemented by plugins.
 If this method returns a non-empty dictionary, that structure will be passed to
 the reply. The default is an empty implementation.




[call method [cmd "Headers_Local"] [arg varname]]

 Introspect and possibly modify a data structure destined for a reply. This
 method is invoked before invoking Header methods implemented by plugins.
 The default implementation is empty.




[call method [cmd "Headers_Process"] [arg varname]]

 Introspect and possibly modify a data structure destined for a reply. This
 method is built dynamically by the [cmd plugin] method.




[call method [cmd "HostName"] [arg ipaddr]]

 Convert an ip address to a host name. If the server/ reverse_dns flag
 is false, this method simply returns the IP address back.
 Internally, this method uses the [emph dns] module from tcllib.




[call method [cmd "log"] [opt "[arg args]"]]

 Log an event. The input for args is free form. This method is intended
 to be replaced by the user, and is a noop for a stock http::server object.




[call method [cmd "plugin"] [arg slot] [opt "[arg class] [const ""]"]]

 Incorporate behaviors from a plugin.
 This method dynamically rebuilds the [cmd Dispatch] and [cmd Headers]
 method. For every plugin, the server looks for the following entries in
 [emph "clay plugin/"]:
 [para]
 [emph load] - A script to invoke in the server's namespace during the [cmd plugin] method invokation.
 [para]
 [emph dispatch] - A script to stitch into the server's [cmd Dispatch] method.
 [para]
 [emph headers] - A script to stitch into the server's [cmd Headers] method.
 [para]
 [emph thread] - A script to stitch into the server's [cmd Thread_start] method.




[call method [cmd "port_listening"]]
 Return the actual port that httpd is listening on.



[call method [cmd "PrefixNormalize"] [arg prefix]]
 For the stock version, trim trailing /'s and *'s from a prefix. This
 method can be replaced by the end user to perform any other transformations
 needed for the application.



[call method [cmd "source"] [arg filename]]


[call method [cmd "start"]]
 Open the socket listener.



[call method [cmd "stop"]]
 Shut off the socket listener, and destroy any pending replies.



[call method [cmd "SubObject {} db"]]


[call method [cmd "SubObject {} default"]]


[call method [cmd "template"] [arg page]]
 Return a template for the string [arg page]



[call method [cmd "TemplateSearch"] [arg page]]
 Perform a search for the template that best matches [arg page]. This
 can include local file searches, in-memory structures, or even
 database lookups. The stock implementation simply looks for files
 with a .tml or .html extension in the [opt doc_root] directory.



[call method [cmd "Thread_start"]]

 Built by the [cmd plugin] method. Called by the [cmd start] method. Intended
 to allow plugins to spawn worker threads.




[call method [cmd "Uuid_Generate"]]

 Generate a GUUID. Used to ensure every request has a unique ID.
 The default implementation is:
 [example {
   return [::uuid::uuid generate]
 }]




[call method [cmd "Validate_Connection"] [arg sock] [arg ip]]

 Given a socket and an ip address, return true if this connection should
 be terminated, or false if it should be allowed to continue. The stock
 implementation always returns 0. This is intended for applications to
 be able to implement black lists and/or provide security based on IP
 address.




[list_end]
[para]

[subsection {Class  httpd::server::dispatch}]
[emph "ancestors"]: [class httpd::server]
[para]

 Provide a backward compadible alias



[para]

[subsection {Class  httpd::content.redirect}]

[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "reset"]]


[call method [cmd "content"]]


[list_end]
[para]

[subsection {Class  httpd::content.cache}]

[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "Dispatch"]]


[list_end]
[para]

[subsection {Class  httpd::content.template}]

[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "content"]]


[list_end]
[para]

[subsection {Class  httpd::content.file}]

 Class to deliver Static content
 When utilized, this class is fed a local filename
 by the dispatcher



[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "FileName"]]


[call method [cmd "DirectoryListing"] [arg local_file]]


[call method [cmd "content"]]


[call method [cmd "Dispatch"]]


[list_end]
[para]

[subsection {Class  httpd::content.exec}]

[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "CgiExec"] [arg execname] [arg script] [arg arglist]]


[call method [cmd "Cgi_Executable"] [arg script]]


[list_end]
[para]

[subsection {Class  httpd::content.proxy}]
[emph "ancestors"]: [class httpd::content.exec]
[para]

 Return data from an proxy process



[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "proxy_channel"]]


[call method [cmd "proxy_path"]]


[call method [cmd "ProxyRequest"] [arg chana] [arg chanb]]


[call method [cmd "ProxyReply"] [arg chana] [arg chanb] [opt "[arg args]"]]


[call method [cmd "Dispatch"]]


[list_end]
[para]

[subsection {Class  httpd::content.cgi}]
[emph "ancestors"]: [class httpd::content.proxy]
[para]

[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "FileName"]]


[call method [cmd "proxy_channel"]]


[call method [cmd "ProxyRequest"] [arg chana] [arg chanb]]


[call method [cmd "ProxyReply"] [arg chana] [arg chanb] [opt "[arg args]"]]


[call method [cmd "DirectoryListing"] [arg local_file]]

 For most CGI applications a directory list is vorboten




[list_end]
[para]

[subsection {Class  httpd::protocol.scgi}]

 Return data from an SCGI process



[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "EncodeStatus"] [arg status]]


[list_end]
[para]

[subsection {Class  httpd::content.scgi}]
[emph "ancestors"]: [class httpd::content.proxy]
[para]

[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "scgi_info"]]


[call method [cmd "proxy_channel"]]


[call method [cmd "ProxyRequest"] [arg chana] [arg chanb]]


[call method [cmd "ProxyReply"] [arg chana] [arg chanb] [opt "[arg args]"]]


[list_end]
[para]

[subsection {Class  httpd::server.scgi}]
[emph "ancestors"]: [class httpd::server]
[para]

 Act as an  SCGI Server



[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "debug"] [opt "[arg args]"]]


[call method [cmd "Connect"] [arg uuid] [arg sock] [arg ip]]


[list_end]
[para]

[subsection {Class  httpd::content.websocket}]

 Upgrade a connection to a websocket



[para]

[subsection {Class  httpd::plugin}]

 httpd plugin template



[para]

[subsection {Class  httpd::plugin.dict_dispatch}]

 A rudimentary plugin that dispatches URLs from a dict
 data structure



[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "Dispatch_Dict"] [arg data]]

 Implementation of the dispatcher




[call method [cmd "uri {} add"] [arg vhosts] [arg patterns] [arg info]]





[call method [cmd "uri {} direct"] [arg vhosts] [arg patterns] [arg info] [arg body]]


[list_end]
[para]

[subsection {Class  httpd::reply.memchan}]
[emph "ancestors"]: [class httpd::reply]
[para]

[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "output"]]


[call method [cmd "DoOutput"]]


[call method [cmd "close"]]


[list_end]
[para]

[subsection {Class  httpd::plugin.local_memchan}]

[para]
[class {Methods}]
[list_begin definitions]
[call method [cmd "local_memchan"] [arg command] [opt "[arg args]"]]


[call method [cmd "Connect_Local"] [arg uuid] [arg sock] [opt "[arg args]"]]

 A modified connection method that passes simple GET request to an object
 and pulls data directly from the reply_body data variable in the object

 Needed because memchan is bidirectional, and we can't seem to communicate that
 the server is one side of the link and the reply is another




[list_end]
[para]

[section AUTHORS]
Sean Woods

[vset CATEGORY network]
[include ../doctools2base/include/feedback.inc]

[manpage_end]

Changes to modules/httpd/httpd.tcl.

1
2
3
4
5
6
7

8
9

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40
41
42
43
44









































45
46
47
48
49
50
51
1
2
3
4
5
6

7
8

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91






-
+

-
+




















-
+













-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







###
# Amalgamated package for httpd
# Do not edit directly, tweak the source in src/ and rerun
# build.tcl
###
package require Tcl 8.6
package provide httpd 4.2.0
package provide httpd 4.3
namespace eval ::httpd {}
set ::httpd::version 4.2.0
set ::httpd::version 4.3

###
# START: core.tcl
###
###
# Author: Sean Woods, [email protected]
##
# Adapted from the "minihttpd.tcl" file distributed with Tclhttpd
#
# The working elements have been updated to operate as a TclOO object
# running with Tcl 8.6+. Global variables and hard coded tables are
# now resident with the object, allowing this server to be more easily
# embedded another program, as well as be adapted and extended to
# support the SCGI module
###

package require uri
package require dns
package require cron
package require coroutine
package require tool
package require clay 0.3
package require mime
package require fileutil
package require websocket
package require Markdown
package require uuid
package require fileutil::magic::filetype

namespace eval httpd::content {}

namespace eval ::url {}
namespace eval ::httpd {}
namespace eval ::scgi {}

tool::define ::httpd::mime {
clay::define ::httpd::mime {

  method ChannelCopy {in out args} {
    set chunk 4096
    set size -1
    foreach {f v} $args {
      set [string trim $f -] $v
    }
    dict set info coroutine [info coroutine]
    if {$size>0 && $chunk>$size} {
        set chunk $size
    }
    set bytes 0
    set sofar 0
    set method [self method]
    while 1 {
      set command {}
      set error {}
      if {$size>=0} {
        incr sofar $bytes
        set remaining [expr {$size-$sofar}]
        if {$remaining <= 0} {
          break
        } elseif {$chunk > $remaining} {
          set chunk $remaining
        }
      }
      lassign [yieldto chan copy $in $out -size $chunk \
        -command [list [info coroutine] $method]] \
        command bytes error
      if {$command ne $method} {
        error "Subroutine $method interrupted"
      }
      if {[string length $error]} {
        error $error
      }
      if {[chan eof $in]} {
        break
      }
    }
  }


  method html_header {{title {}} args} {
    set result {}
    append result "<HTML><HEAD>"
    if {$title ne {}} {
      append result "<TITLE>$title</TITLE>"
118
119
120
121
122
123
124
125










126
127



128
129
130
131
132
133
134
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176

177
178
179
180
181
182
183
184
185
186








+
+
+
+
+
+
+
+
+
+

-
+
+
+







  method HttpHeaders_Default {} {
    return {Status {200 OK}
Content-Size 0
Content-Type {text/html; charset=UTF-8}
Cache-Control {no-cache}
Connection close}
  }

  method HttpServerHeaders {} {
    return {
      CONTENT_LENGTH CONTENT_TYPE QUERY_STRING REMOTE_USER AUTH_TYPE
      REQUEST_METHOD REMOTE_ADDR REMOTE_HOST REQUEST_URI REQUEST_PATH
      REQUEST_VERSION  DOCUMENT_ROOT QUERY_STRING REQUEST_RAW
      GATEWAY_INTERFACE SERVER_PORT SERVER_HTTPS_PORT
      SERVER_NAME  SERVER_SOFTWARE SERVER_PROTOCOL
    }
  }

  ###
  # Minimalist MIME Header Parser
  # Converts a block of mime encoded text to a key/value list. If an exception is encountered,
  # the method will generate its own call to the [cmd error] method, and immediately invoke
  # the [cmd output] method to produce an error code and close the connection.
  ###
  method MimeParse mimetext {
    set data(mimeorder) {}
    foreach line [split $mimetext \n] {
      # This regexp picks up
      # key: value
      # MIME headers.  MIME headers may be continue with a line
206
207
208
209
210
211
212

213
214
215
216
217
218
219
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272







+







        }
      }
      dict set result $ckey $data(mime,$key)
    }
    return $result
  }

  # De-httpizes a string.
  method Url_Decode data {
    regsub -all {\+} $data " " data
    regsub -all {([][$\\])} $data {\\\1} data
    regsub -all {%([0-9a-fA-F][0-9a-fA-F])} $data  {[format %c 0x\1]} data
    return [subst $data]
  }

267
268
269
270
271
272
273

274
































































































275
276

277
278
279















280
281
282
283
284
285
286
287



288
289
290
291
292
293
294
295
296
297



298
299
300
301
302
303
304
305
306
307
308



309
310
311
312
313
314
315
316
317
318








319
320
























321
322
323
324
325
326
327
328
329



















330
331
332
333







334
335
336
337
338






339
340
341
342
343
344
345
320
321
322
323
324
325
326
327

328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424

425
426
427

428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447



448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479








480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513









514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532




533
534
535
536
537
538
539
540
541

542
543
544
545
546
547
548
549
550
551
552
553
554
555
556







+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+


-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
-
+
+
+










+
+
+











+
+
+


-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+


-


+
+
+
+
+
+







###
# END: core.tcl
###
###
# START: reply.tcl
###
###
# A class which shephards a request through the process of generating a
# Define the reply class
# reply.
#
# The socket associated with the reply is available at all times as the [arg chan]
# variable.
#
# The process of generating a reply begins with an [cmd httpd::server] generating a
# [cmd http::class] object, mixing in a set of behaviors and then invoking the reply
# object's [cmd dispatch] method.
#
# In normal operations the [cmd dispatch] method:
#
# [list_begin enumerated]
# [enum]
# Invokes the [cmd reset] method for the object to populate default headers.
# [enum]
# Invokes the [cmd HttpHeaders] method to stream the MIME headers out of the socket
# [enum]
# Invokes the [cmd {request parse}] method to convert the stream of MIME headers into a
# dict that can be read via the [cmd request] method.
# [enum]
# Stores the raw stream of MIME headers in the [arg rawrequest] variable of the object.
# [enum]
# Invokes the [cmd content] method for the object, generating an call to the [cmd error]
# method if an exception is raised.
# [enum]
# Invokes the [cmd output] method for the object
# [list_end]
# [para]
#
# Developers have the option of streaming output to a buffer via the [cmd puts] method of the
# reply, or simply populating the [arg reply_body] variable of the object.
# The information returned by the [cmd content] method is not interpreted in any way.
#
# If an exception is thrown (via the [cmd error] command in Tcl, for example) the caller will
# auto-generate a 500 {Internal Error} message.
#
# A typical implementation of [cmd content] look like:
#
# [example {
#
# clay::define ::test::content.file {
# 	superclass ::httpd::content.file
# 	# Return a file
# 	# Note: this is using the content.file mixin which looks for the reply_file variable
# 	# and will auto-compute the Content-Type
# 	method content {} {
# 	  my reset
#     set doc_root [my request get DOCUMENT_ROOT]
#     my variable reply_file
#     set reply_file [file join $doc_root index.html]
# 	}
# }
# clay::define ::test::content.time {
#   # return the current system time
# 	method content {} {
# 		my variable reply_body
#     my reply set Content-Type text/plain
# 		set reply_body [clock seconds]
# 	}
# }
# clay::define ::test::content.echo {
# 	method content {} {
# 		my variable reply_body
#     my reply set Content-Type [my request get CONTENT_TYPE]
# 		set reply_body [my PostData [my request get CONTENT_LENGTH]]
# 	}
# }
# clay::define ::test::content.form_handler {
# 	method content {} {
# 	  set form [my FormData]
# 	  my reply set Content-Type {text/html; charset=UTF-8}
#     my puts [my html_header {My Dynamic Page}]
#     my puts "<BODY>"
#     my puts "You Sent<p>"
#     my puts "<TABLE>"
#     foreach {f v} $form {
#       my puts "<TR><TH>$f</TH><TD><verbatim>$v</verbatim></TD>"
#     }
#     my puts "</TABLE><p>"
#     my puts "Send some info:<p>"
#     my puts "<FORM action=/[my request get REQUEST_PATH] method POST>"
#     my puts "<TABLE>"
#     foreach field {name rank serial_number} {
#       set line "<TR><TH>$field</TH><TD><input name=\"$field\" "
#       if {[dict exists $form $field]} {
#         append line " value=\"[dict get $form $field]\"""
#       }
#       append line " /></TD></TR>"
#       my puts $line
#     }
#     my puts "</TABLE>"
#     my puts [my html footer]
# 	}
# }
#
# }]
###
::tool::define ::httpd::reply {
::clay::define ::httpd::reply {
  superclass ::httpd::mime

  variable transfer_complete 0
  Variable transfer_complete 0

  Dict reply {}

  Dict request {
    CONTENT_LENGTH 0
    COOKIE {}
    HTTP_HOST {}
    REFERER {}
    REQUEST_URI {}
    REMOTE_ADDR {}
    REMOTE_HOST {}
    USER_AGENT {}
    SESSION {}
  }

  constructor {ServerObj args} {
    my variable chan dispatched_time uuid
    set uuid [namespace tail [self]]
    set dispatched_time [clock milliseconds]
    oo::objdefine [self] forward <server> $ServerObj
    foreach {field value} [::oo::meta::args_to_options {*}$args] {
      my meta set config $field: $value
    my clay delegate <server> $ServerObj
    foreach {field value} [::clay::args_to_options {*}$args] {
      my clay set config $field: $value
    }
  }

  ###
  # clean up on exit
  ###
  destructor {
    my close
  }

  ###
  # Close channels opened by this object
  ###
  method close {} {
    my variable chan
    if {[info exists chan] && $chan ne {}} {
      catch {chan event $chan readable {}}
      catch {chan event $chan writable {}}
      catch {chan flush $chan}
      catch {chan close $chan}
      set chan {}
    }
  }

  ###
  # Record a dispatch event
  ###
  method Log_Dispatched {} {
    my log Dispatched [dict create \
     REMOTE_ADDR [my http_info get REMOTE_ADDR] \
     REMOTE_HOST [my http_info get REMOTE_HOST] \
     COOKIE [my request get COOKIE] \
     REFERER [my request get REFERER] \
     USER_AGENT [my request get USER_AGENT] \
     REQUEST_URI [my http_info get REQUEST_URI] \
     HTTP_HOST [my http_info getnull HTTP_HOST] \
     SESSION [my http_info getnull SESSION] \
     REMOTE_ADDR [my request get REMOTE_ADDR] \
     REMOTE_HOST [my request get REMOTE_HOST] \
     COOKIE [my request get HTTP_COOKIE] \
     REFERER [my request get HTTP_REFERER] \
     USER_AGENT [my request get HTTP_USER_AGENT] \
     REQUEST_URI [my request get REQUEST_URI] \
     HTTP_HOST [my request get HTTP_HOST] \
     SESSION [my request get SESSION] \
    ]
  }

  ###
  # Accept the handoff from the server object of the socket
  # [emph newsock] and feed it the state [emph datastate].
  # Fields the [emph datastate] are looking for in particular are:
  # [para]
  # * [const mixin] - A key/value list of slots and classes to be mixed into the
  # object prior to invoking [cmd Dispatch].
  # [para]
  # * [const http] - A key/value list of values to populate the object's [emph request]
  # ensemble
  # [para]
  # All other fields are passed along to the [method clay] structure of the object.
  ###
  method dispatch {newsock datastate} {
    my variable chan request
    try {
      set chan $newsock
      chan event $chan readable {}
      chan configure $chan -translation {auto crlf} -buffering line
      if {[dict exists $datastate mixin]} {
        set mixinmap [dict get $datastate mixin]
      } else {
        set mixinmap {}

  method dispatch {newsock datastate} {
    my http_info replace $datastate
    my request replace  [dict getnull $datastate http]
    my Log_Dispatched
    my variable chan
    set chan $newsock
    try {
      chan event $chan readable {}
      }
      foreach item [dict keys $datastate MIXIN_*] {
        set slot [string range $item 6 end]
        dict set mixinmap [string tolower $slot] [dict get $datastate $item]
      }
      my clay mixinmap {*}$mixinmap
      if {[dict exists $datastate delegate]} {
        my clay delegate {*}[dict get $datastate delegate]
      }
      my reset
      set request [my clay get dict/ request]
      foreach {f v} $datastate {
        if {[string index $f end] eq "/"} {
          my clay merge $f $v
        } else {
          my clay set $f $v
        }
        if {$f eq "http"} {
          foreach {ff vf} $v {
      chan configure $chan -translation {auto crlf} -buffering line
      my reset
      # Invoke the URL implementation.
      my content
            dict set request $ff $vf
          }
        }
      }
      my Session_Load
      my Log_Dispatched
      my Dispatch
    } on error {err errdat} {
      my error 500 $err [dict get $errdat -errorinfo]
    } finally {
      my DoOutput
    }
  }

  method Dispatch {} {
    # Invoke the URL implementation.
    my content
    my DoOutput
  }

  method html_css {} {
    set result "<link rel=\"stylesheet\" href=\"/style.css\">"
    append result \n {<style media="screen" type="text/css">
body {
	background:  url(images/etoyoc-circuit-tile.gif) repeat;
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394

395
396

397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423

424
425
426
427
428
429
430



431
432
433
434
435
436
437
438
439
440
441
442

443
444
445
446
447
448


449
450
451
452
453
454
455
584
585
586
587
588
589
590













591

592
593

594
595
596
597
598
599

600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619

620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641

642
643
644
645
646


647
648
649
650
651
652
653
654
655







-
-
-
-
-
-
-
-
-
-
-
-
-

-
+

-
+





-




















-
+







+
+
+











-
+




-
-
+
+







  }

  method html_footer {args} {
    set result {</div><div id="footer">}
    append result {</div></BODY></HTML>}
  }

  dictobj http_info http_info {
    initialize {
      CONTENT_LENGTH 0
    }
    netstring {
      set result {}
      foreach {name value} $%VARNAME% {
        append result $name \x00 $value \x00
      }
      return "[string length $result]:$result,"
    }
  }

  method error {code {msg {}} {errorInfo {}}} {
    my http_info set HTTP_ERROR $code
    my clay set  HTTP_ERROR $code
    my reset
    set qheaders [my http_info dump]
    set qheaders [my clay dump]
    set HTTP_STATUS "$code [my http_code_string $code]"
    dict with qheaders {}
    my reply replace {}
    my reply set Status $HTTP_STATUS
    my reply set Content-Type {text/html; charset=UTF-8}

    switch $code {
      301 - 302 - 303 - 307 - 308 {
        my reply set Location $msg
        set template [my <server> template redirect]
      }
      404 {
        set template [my <server> template notfound]
      }
      default {
        set template [my <server> template internal_error]
      }
    }
    my puts [subst $template]
  }


  ###
  # REPLACE ME:
  # This method is the "meat" of your application.
  # It writes to the result buffer via the "puts" method
  # and can tweak the headers via "meta put header_reply"
  # and can tweak the headers via "clay put header_reply"
  ###
  method content {} {
    my puts [my html_header {Hello World!}]
    my puts "<H1>HELLO WORLD!</H1>"
    my puts [my html_footer]
  }

  ###
  # Formulate a standard HTTP status header from he string provided.
  ###
  method EncodeStatus {status} {
    return "HTTP/1.0 $status"
  }

  method log {type {info {}}} {
    my variable dispatched_time uuid
    my <server> log $type $uuid $info
  }

  method CoroName {} {
    if {[info coroutine] eq {}} {
      return ::httpd::object::[my http_info get UUID]
      return ::httpd::object::[my clay get UUID]
    }
  }

  ###
  # Output the result or error to the channel
  # and destroy this object
  # Generates the the HTTP reply, streams that reply back across [arg chan],
  # and destroys the object.
  ###
  method DoOutput {} {
    my variable reply_body chan
    if {$chan eq {}} return
    catch {
      my wait writable $chan
      chan configure $chan  -translation {binary binary}
467
468
469
470
471
472
473









474
475
476
477
478
479
480
481
482
483

484
485
486

487
488
489
490
491
492
493
494
495
496
497
498

499
500
501
502
503
504
505
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688




689

690

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

703
704
705
706
707
708
709
710







+
+
+
+
+
+
+
+
+






-
-
-
-
+
-

-
+











-
+







      }
      chan puts -nonewline $chan $result
      my log HttpAccess {}
    }
    my destroy
  }

  ###
  # For GET requests, converts the QUERY_DATA header into a key/value list.
  #
  # For POST requests, reads the Post data and converts that information to
  # a key/value list for application/x-www-form-urlencoded posts. For multipart
  # posts, it composites all of the MIME headers of the post to a singular key/value
  # list, and provides MIME_* information as computed by the [cmd mime] package, including
  # the MIME_TOKEN, which can be fed back into the mime package to read out the contents.
  ###
  method FormData {} {
    my variable chan formdata
    # Run this only once
    if {[info exists formdata]} {
      return $formdata
    }
    if {![my request exists CONTENT_LENGTH]} {
      set length 0
    } else {
      set length [my request get CONTENT_LENGTH]
    set length [my request get CONTENT_LENGTH]
    }
    set formdata {}
    if {[my http_info get REQUEST_METHOD] in {"POST" "PUSH"}} {
    if {[my request get REQUEST_METHOD] in {"POST" "PUSH"}} {
      set rawtype [my request get CONTENT_TYPE]
      if {[string toupper [string range $rawtype 0 8]] ne "MULTIPART"} {
        set type $rawtype
      } else {
        set type multipart
      }
      switch $type {
        multipart {
          ###
          # Ok, Multipart MIME is troublesome, farm out the parsing to a dedicated tool
          ###
          set body [my http_info get mimetxt]
          set body [my clay get mimetxt]
          append body \n [my PostData $length]
          set token [::mime::initialize -string $body]
          foreach item [::mime::getheader $token -names] {
            dict set formdata $item [::mime::getheader $token $item]
          }
          foreach item {content encoding params parts size} {
            dict set formdata MIME_[string toupper $item] [::mime::getproperty $token $item]
515
516
517
518
519
520
521
522

523
524
525
526
527
528
529
530


531
532
533
534
535
536
537
538

539
540
541
542
543
544
545
























546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562


563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580

581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598






















599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632








































































633
634


635
636
637
638

639
640
641
642
643
644
645




646
647
648
649
650
651
652
653
654
655
656
657
658
659
660

661
662
663
664
665
666
667
668
669
670
671
672
673
674

675



676
677
678
679
680

681
682
683
684
685
686
687
688
689








690
691
692
693



694
695
696


697
698
699



















700
701
702
703
704
705
706




707
708
709
710


711
712
713
714
715
716
717
718
719
720











































721
722
723
724
725
726
727
728
729
730
731
732
733

734
735
736
737
738
739
740
741
742
743
744
745
746
747

748
749
750
751
752
753
754
755
756


757
758
759

760
761
762
763
764
765
766
767
768
769
770
771


772
773
774
775



776
777
778
779
780
781
782
783

784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807

808
809
810
811
812
813
814
815



816
817
818
819
820
821
822

823
824
825
826
827
828
829
830
831
832
833
834
835
836

837

838
839




840
841
842





843
844
845
846
847

848
849
850
851
852
853
854

855
856
857
858
859


















860
861





862
863

864
865
866
867
868
869
870
871




872
873
874
875














876
877
878
879
880
881
882
883
884


885
886
887
888
889


890
891
892
893
894





895
896

897
898
899
900
901
902
903
904
905
906
907
908
909
910
911

912
913

914
915
916
917
918
919
920
921

922
923
924
925
926
927
928
929

930
931
932
933
934
935
936
937
938
939
940
941



942
943
944
945
946



947
948
949
950
951
952
953
954
955
956
957

958
959
960
961
962
963
964


965
966

967
968
969
970
971
972

973
974
975
976
977
978
979
980
981
982
983
984
985

986
987
988
989
990
991
992
993
994
995
996


997






998
999
1000
1001
1002
1003
1004
1005
1006




1007
1008

1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019


1020
1021
1022
1023

1024
1025
1026
1027
1028
1029


1030
1031
1032
1033
1034
1035

1036
1037
1038
1039
1040
1041
1042



1043
1044
1045
1046
1047




1048
1049







1050
1051
1052
1053
1054
1055
1056
1057





1058
1059
1060
1061
1062
1063
1064
1065
1066
1067

1068
1069
1070
1071
1072
1073
1074
1075
1076
1077

1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089




1090
1091
1092
1093
1094
1095
1096
1097
1098

1099
1100
1101
1102
1103
1104

1105
1106

1107
1108
1109
1110
1111
1112
1113
1114
1115
1116

1117
1118
1119
1120
1121
1122
1123
1124
1125

1126
1127
1128
1129


1130
1131

1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146

1147
1148
1149
1150
1151



1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174



1175
1176
1177
1178
1179
1180
1181
720
721
722
723
724
725
726

727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744

745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790



791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811


















812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833


































834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905


906
907
908
909
910

911
912
913
914
915



916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933

934
935
936
937
938
939
940
941
942
943
944
945
946
947

948

949
950
951
952
953
954
955

956
957
958







959
960
961
962
963
964
965
966
967



968
969
970
971


972
973
974


975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006


1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064

1065
1066
1067
1068

1069

1070

1071












1072

1073









1074
1075



1076

1077
1078
1079
1080
1081
1082
1083
1084
1085


1086
1087
1088



1089
1090
1091
1092







1093
























1094








1095
1096
1097
1098
1099
1100
1101
1102
1103

1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119

1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138

1139
1140
1141
1142
1143
1144
1145

1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177

1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215


1216
1217
1218
1219
1220
1221

1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234

1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252

1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269

1270
1271
1272
1273
1274
1275
1276
1277
1278
1279



1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307


1308
1309
1310

1311
1312
1313
1314
1315
1316

1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344

1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364

1365
1366
1367
1368
1369
1370
1371
1372
1373
1374


1375
1376
1377
1378
1379

1380
1381
1382
1383
1384


1385
1386
1387
1388
1389
1390
1391

1392
1393
1394
1395
1396



1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422



1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436

1437
1438
1439
1440
1441
1442
1443
1444
1445
1446

1447
1448
1449
1450
1451
1452
1453
1454
1455




1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467

1468
1469
1470
1471
1472
1473

1474
1475

1476


1477


1478

1479
1480

1481
1482
1483
1484
1485
1486
1487
1488
1489

1490
1491
1492


1493
1494
1495

1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510

1511
1512
1513



1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536



1537
1538
1539
1540
1541
1542
1543
1544
1545
1546







-
+








+
+







-
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














-
-
-
+
+


















+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+



-
+




-
-
-
+
+
+
+














-
+













-
+
-
+
+
+




-
+


-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
+
+
+

-
-
+
+

-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







+
+
+
+


-
-
+
+










+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-




-

-

-
+
-
-
-
-
-
-
-
-
-
-
-
-

-
+
-
-
-
-
-
-
-
-
-
+
+
-
-
-
+
-









-
-
+
+

-
-
-
+
+
+

-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
+
+
+






-
+














+
-
+


+
+
+
+



+
+
+
+
+




-
+






-
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
+
+
+
+

-
+








+
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
-
+
+




-
+
+





+
+
+
+
+

-
+















+

-
+








+







-
+









-
-
-
+
+
+





+
+
+











+





-
-
+
+

-
+





-
+













+











+
+
-
+
+
+
+
+
+









+
+
+
+

-
+









-
-
+
+



-
+




-
-
+
+





-
+




-
-
-
+
+
+





+
+
+
+


+
+
+
+
+
+
+





-
-
-
+
+
+
+
+









-
+









-
+








-
-
-
-
+
+
+
+








-
+





-
+

-
+
-
-

-
-

-


-
+








-
+


-
-
+
+

-
+














-
+


-
-
-
+
+
+




















-
-
-
+
+
+







            foreach {name value} [split $pair "="] {
              lappend formdata [my Url_Decode $name] [my Url_Decode $value]
            }
          }
        }
      }
    } else {
      foreach pair [split [my http_info getnull QUERY_STRING] "&"] {
      foreach pair [split [my clay get QUERY_STRING] "&"] {
        foreach {name value} [split $pair "="] {
          lappend formdata [my Url_Decode $name] [my Url_Decode $value]
        }
      }
    }
    return $formdata
  }

  # Stream [arg length] bytes from the [arg chan] socket, but only of the request is a
  # POST or PUSH. Returns an empty string otherwise.
  method PostData {length} {
    my variable postdata
    # Run this only once
    if {[info exists postdata]} {
      return $postdata
    }
    set postdata {}
    if {[my http_info get REQUEST_METHOD] in {"POST" "PUSH"}} {
    if {[my request get REQUEST_METHOD] in {"POST" "PUSH"}} {
      my variable chan
      chan configure $chan -translation binary -blocking 0 -buffering full -buffersize 4096
      set postdata [::coroutine::util::read $chan $length]
    }
    return $postdata
  }

  # Manage session data
  method Session_Load {} {}



  # Intended to be invoked from [cmd {chan copy}] as a callback. This closes every channel
  # fed to it on the command line, and then destroys the object.
  #
  # [example {
  #     ###
  #     # Output the body
  #     ###
  #     chan configure $sock -translation binary -blocking 0 -buffering full -buffersize 4096
  #     chan configure $chan -translation binary -blocking 0 -buffering full -buffersize 4096
  #     if {$length} {
  #       ###
  #       # Send any POST/PUT/etc content
  #       ###
  #       chan copy $sock $chan -size $SIZE -command [info coroutine]
  #       yield
  #     }
  #     catch {close $sock}
  #     chan flush $chan
  # }]
  method TransferComplete args {
    my variable chan transfer_complete
    set transfer_complete 1
    my log TransferComplete
    set chan {}
    foreach c $args {
      catch {chan event $c readable {}}
      catch {chan event $c writable {}}
      catch {chan flush $c}
      catch {chan close $c}
    }
    my destroy
  }

  ###
  # Append to the result buffer
  ###
  # Appends the value of [arg string] to the end of [arg reply_body], as well as a trailing newline
  # character.
  method puts line {
    my variable reply_body
    append reply_body $line \n
  }

  method RequestFind {field} {
    my variable request
    if {[dict exists $request $field]} {
      return $field
    }
    foreach item [dict keys $request] {
      if {[string tolower $item] eq [string tolower $field]} {
        return $item
      }
    }
    return $field
  }

  method request {subcommand args} {
  dictobj request request {
    field {
      tailcall my RequestFind [lindex $args 0]
    }
    get {
      set field [my RequestFind [lindex $args 0]]
      if {![dict exists $request $field]} {
        return {}
      }
      tailcall dict get $request $field
    }
    getnull {
      set field [my RequestFind [lindex $args 0]]
      if {![dict exists $request $field]} {
        return {}
      }
      tailcall dict get $request $field

    my variable request
    switch $subcommand {
      dump {
        return $request
      }
      field {
        tailcall my RequestFind [lindex $args 0]
      }
      get {
        set field [my RequestFind [lindex $args 0]]
        if {![dict exists $request $field]} {
          return {}
        }
        tailcall dict get $request $field
      }
      getnull {
        set field [my RequestFind [lindex $args 0]]
        if {![dict exists $request $field]} {
          return {}
        }
        tailcall dict get $request $field
      }
    }
    exists {
      set field [my RequestFind [lindex $args 0]]
      tailcall dict exists $request $field
    }
    parse {
      if {[catch {my MimeParse [lindex $args 0]} result]} {
        my error 400 $result
        tailcall my DoOutput
      }
      set request $result
    }
  }

  dictobj reply reply {
    output {
      set result {}
      if {![dict exists $reply Status]} {
        set status {200 OK}
      } else {
        set status [dict get $reply Status]
      }
      set result "[my EncodeStatus $status]\n"
      foreach {f v} $reply {
        if {$f in {Status}} continue
        append result "[string trimright $f :]: $v\n"
      }
      #append result \n
      return $result
    }
  }


  ###
      exists {
        set field [my RequestFind [lindex $args 0]]
        tailcall dict exists $request $field
      }
      parse {
        if {[catch {my MimeParse [lindex $args 0]} result]} {
          my error 400 $result
          tailcall my DoOutput
        }
        set request $result
      }
      replace {
        set request [lindex $args 0]
      }
      set {
        dict set request {*}$args
      }
      default {
        error "Unknown command $subcommand. Valid: field, get, getnull, exists, parse, replace, set"
      }
    }
  }

  method reply {subcommand args} {
    my variable reply
    switch $subcommand {
      dump {
        return $reply
      }
      exists {
        return [dict exists $reply {*}$args]
      }
      get -
      getnull {
        return [dict getnull $reply {*}$args]
      }
      replace {
        set reply [my HttpHeaders_Default]
        if {[llength $args]==1} {
          foreach {f v} [lindex $args 0] {
            dict set reply $f $v
          }
        } else {
          foreach {f v} $args {
            dict set reply $f $v
          }
        }
      }
      output {
        set result {}
        if {![dict exists $reply Status]} {
          set status {200 OK}
        } else {
          set status [dict get $reply Status]
        }
        set result "[my EncodeStatus $status]\n"
        foreach {f v} $reply {
          if {$f in {Status}} continue
          append result "[string trimright $f :]: $v\n"
        }
        #append result \n
        return $result
      }
      set {
        dict set reply {*}$args
      }
      default {
        error "Unknown command $subcommand. Valid: exists, get, getnull, output, replace, set"
      }
    }
  }

  # Reset the result
  ###
  # Clear the contents of the [arg reply_body] variable, and reset all headers in the [cmd reply]
  # structure back to the defaults for this object.
  method reset {} {
    my variable reply_body
    my reply replace    [my HttpHeaders_Default]
    my reply set Server [my <server> cget server_string]
    my reply set Server [my <server> clay get server/ string]
    my reply set Date [my timestamp]
    set reply_body {}
  }

  ###
  # Return true of this class as waited too long to respond
  ###
  # Called from the [cmd http::server] object which spawned this reply. Checks to see
  # if too much time has elapsed while waiting for data or generating a reply, and issues
  # a timeout error to the request if it has, as well as destroy the object and close the
  # [arg chan] socket.
  method timeOutCheck {} {
    my variable dispatched_time
    if {([clock seconds]-$dispatched_time)>120} {
      ###
      # Something has lasted over 2 minutes. Kill this
      ###
      catch {
        my error 408 {Request Timed out}
        my DoOutput
      }
    }
  }

  ###
  # Return a timestamp
  # Return the current system time in the format: [example {%a, %d %b %Y %T %Z}]
  ###
  method timestamp {} {
    return [clock format [clock seconds] -format {%a, %d %b %Y %T %Z}]
  }
}

###
# END: reply.tcl
###
###
# START: server.tcl
###
###
# An httpd server with a template engine
# An httpd server with a template engine and a shim to insert URL domains.
# and a shim to insert URL domains
#
# This class is the root object of the webserver. It is responsible
# for opening the socket and providing the initial connection negotiation.
###
namespace eval ::httpd::object {}
namespace eval ::httpd::coro {}

::tool::define ::httpd::server {
::clay::define ::httpd::server {
  superclass ::httpd::mime

  option port  {default: auto}
  option myaddr {default: 127.0.0.1}
  option server_string [list default: [list TclHttpd $::httpd::version]]
  option server_name [list default: [list [info hostname]]]
  option doc_root {default {}}
  option reverse_dns {type boolean default 0}
  option configuration_file {type filename default {}}
  clay set server/ port auto
  clay set server/ myaddr 127.0.0.1
  clay set server/ string [list TclHttpd $::httpd::version]
  clay set server/ name [info hostname]
  clay set server/ doc_root {}
  clay set server/ reverse_dns 0
  clay set server/ configuration_file {}
  clay set server/ protocol {HTTP/1.1}

  property socket buffersize   32768
  property socket translation  {auto crlf}
  property reply_class ::httpd::reply
  clay set socket/ buffersize   32768
  clay set socket/ translation  {auto crlf}
  clay set reply_class ::httpd::reply

  array template
  variable url_patterns {}
  Array template
  Dict url_patterns {}

  constructor {args} {
    my configure {*}$args
  constructor {
  {args {
    port        {default auto      comment {Port to listen on}}
    myaddr      {default 127.0.0.1 comment {IP address to listen on. "all" means all}}
    string      {default auto      comment {Value for SERVER_SOFTWARE in HTTP headers}}
    name        {default auto      comment {Value for SERVER_NAME in HTTP headers. Defaults to [info hostname]}}
    doc_root    {default {}        comment {File path to serve.}}
    reverse_dns {default 0         comment {Perform reverse DNS to convert IPs into hostnames}}
    configuration_file {default {} comment {Configuration file to load into server namespace}}
    protocol    {default {HTTP/1.1} comment {Value for SERVER_PROTOCOL in HTTP headers}}
  }}} {
    if {[llength $args]==1} {
      set arglist [lindex $args 0]
    } else {
      set arglist $args
    }
    foreach {var val} $arglist {
      my clay set server/ $var $val
    }
    my start
  }

  destructor {
    my stop
  }

  ###
  # Reply to an open socket. This method builds a coroutine to manage the remainder
  # of the connection. The coroutine's operations are driven by the [cmd Connect] method.
  ###
  method connect {sock ip port} {
    ###
    # If an IP address is blocked
    # send a "go to hell" message
    # If an IP address is blocked drop the
    # connection
    ###
    if {[my Validate_Connection $sock $ip]} {
      catch {close $sock}
      return
    }
    set uuid [my Uuid_Generate]
    set coro [coroutine ::httpd::coro::$uuid {*}[namespace code [list my Connect $uuid $sock $ip]]]
    chan event $sock readable $coro
  }

  method ServerHeaders {ip http_request mimetxt} {
    set result {}
    dict set result HTTP_HOST {}
    dict set result CONTENT_LENGTH 0
    foreach {f v} [my MimeParse $mimetxt] {
      set fld [string toupper [string map {- _} $f]]
      if {$fld in {CONTENT_LENGTH CONTENT_TYPE}} {
        set qfld $fld
      } else {
        set qfld HTTP_$fld
      }
      dict set result $qfld $v
    }
    dict set result REMOTE_ADDR     $ip
    dict set result REMOTE_HOST     [my HostName $ip]
    dict set result REQUEST_METHOD  [lindex $http_request 0]
    set uriinfo [::uri::split [lindex $http_request 1]]
    dict set result uriinfo $uriinfo
    dict set result REQUEST_URI     [lindex $http_request 1]
    dict set result REQUEST_PATH    [dict get $uriinfo path]
    dict set result REQUEST_VERSION [lindex [split [lindex $http_request end] /] end]
    dict set result DOCUMENT_ROOT   [my clay get server/ doc_root]
    dict set result QUERY_STRING    [dict get $uriinfo query]
    dict set result REQUEST_RAW     $http_request
    dict set result SERVER_PORT     [my port_listening]
    dict set result SERVER_NAME     [my clay get server/ name]
    dict set result SERVER_PROTOCOL [my clay get server/ protocol]
    dict set result SERVER_SOFTWARE [my clay get server/ string]
    if {[string match 127.* $ip]} {
      dict set result LOCALHOST [expr {[lindex [split [dict getnull $result HTTP_HOST] :] 0] eq "localhost"}]
    }
    return $result
  }

  ###
  # This method reads HTTP headers, and then consults the [cmd dispatch] method to
  # determine if the request is valid, and/or what kind of reply to generate. Under
  # normal cases, an object of class [cmd ::http::reply] is created, and that class's
  # [cmd dispatch] method.
  # This action passes control of the socket to
  # the reply object. The reply object manages the rest of the transaction, including
  # closing the socket.
  ###
  method Connect {uuid sock ip} {
    yield [info coroutine]
    chan event $sock readable {}

    chan configure $sock \
      -blocking 0 \
      -translation {auto crlf} \
      -buffering line

    my counter url_hit
    set line {}
    try {
      set readCount [::coroutine::util::gets_safety $sock 4096 line]
      set readCount [::coroutine::util::gets_safety $sock 4096 http_request]
      dict set query UUID $uuid
      dict set query REMOTE_ADDR     $ip
      dict set query REMOTE_HOST     [my HostName $ip]
      dict set query REQUEST_METHOD  [lindex $line 0]
      set uriinfo [::uri::split [lindex $line 1]]
      dict set query REQUEST_URI     [lindex $line 1]
      dict set query REQUEST_PATH    [dict get $uriinfo path]
      dict set query REQUEST_VERSION [lindex [split [lindex $line end] /] end]
      dict set query DOCUMENT_ROOT   [my cget doc_root]
      dict set query QUERY_STRING    [dict get $uriinfo query]
      dict set query REQUEST_RAW     $line
      dict set query SERVER_PORT     [my port_listening]
      set mimetxt [my HttpHeaders $sock]
      dict set query mimetxt $mimetxt
      dict set query UUID $uuid
      foreach {f v} [my MimeParse $mimetxt] {
        set fld [string toupper [string map {- _} $f]]
        if {$fld in {CONTENT_LENGTH CONTENT_TYPE}} {
          set qfld $fld
        } else {
          set qfld HTTP_$fld
        }
        dict set query $qfld $v
        dict set query http $fld $v
      dict set query mimetxt $mimetxt
      dict set query mixin style [my clay get server/ style]
      }
      if {[string match 127.* $ip]} {
        dict set query LOCALHOST [expr {[lindex [split [dict getnull $query HTTP_HOST] :] 0] eq "localhost"}]
      dict set query http [my ServerHeaders $ip $http_request $mimetxt]
      }
      my Headers_Process query
      set reply [my dispatch $query]
    } on error {err errdat} {
      my debug [list uri: [dict getnull $query REQUEST_URI] ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      my log BadRequest $uuid [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      catch {chan puts $sock "HTTP/1.0 400 Bad Request (The data is invalid)"}
      catch {chan close $sock}
      return
    }
    if {[llength $reply]==0} {
      my log BadLocation $uuid $query
    if {[dict size $reply]==0} {
      set reply $query
      my log BadLocation $uuid $query
      dict set query HTTP_STATUS 404
      dict set query template notfound
      dict set query mixinmap reply ::httpd::content.template
      dict set reply http HTTP_STATUS {404 Not Found}
      dict set reply template notfound
      dict set reply mixin reply ::httpd::content.template
    }
    try {
      if {[dict exists $reply class]} {
        set class [dict get $reply class]
      } else {
        set class [my cget reply_class]
      }
      set pageobj [$class create ::httpd::object::$uuid [self]]
    set pageobj [::httpd::reply create ::httpd::object::$uuid [self]]
      if {[dict exists $reply mixinmap]} {
        set mixinmap [dict get $reply mixinmap]
      } else {
        set mixinmap {}
      }
      if {[dict exists $reply mixin]} {
        dict set mixinmap reply [dict get $reply mixin]
      }
      foreach item [dict keys $reply MIXIN_*] {
        set slot [string range $reply 6 end]
        dict set mixinmap [string tolower $slot] [dict get $reply $item]
      }
      $pageobj mixinmap {*}$mixinmap
      if {[dict exists $reply organ]} {
        $pageobj graft {*}[dict get $reply organ]
      }
    } on error {err errdat} {
      my debug [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      my log BadRequest $uuid [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      catch {$pageobj destroy}
      catch {chan close $sock}
    }
    try {
      $pageobj dispatch $sock $reply
    tailcall $pageobj dispatch $sock $reply
    } on error {err errdat} {
      my debug [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      my log BadRequest $uuid [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      catch {$pageobj destroy}
      catch {chan close $sock}
    }
  }

  }

  # Increment an internal counter.
  method counter which {
    my variable counters
    incr counters($which)
  }

  ###
  # Clean up any process that has gone out for lunch
  # Check open connections for a time out event.
  ###
  method CheckTimeout {} {
    foreach obj [info commands ::httpd::object::*] {
      try {
        $obj timeOutCheck
      } on error {} {
        catch {$obj destroy}
      }
    }
  }

  method debug args {}

  ###
  # Given a key/value list of information, return a data structure describing how
  # Route a request to the appropriate handler
  # the server should reply.
  ###
  method dispatch {data} {
    set reply [my Dispatch_Local $data]
    if {[dict size $reply]} {
      return $reply
    }
    return [my Dispatch_Default $data]
  }

  ###
  # Method dispatch method of last resort before returning a 404 NOT FOUND error.
  # The default behavior is to look for a file in [emph DOCUMENT_ROOT] which
  # matches the query.
  ###
  method Dispatch_Default {reply} {
    ###
    # Fallback to docroot handling
    ###
    set doc_root [dict get $reply DOCUMENT_ROOT]
    set doc_root [dict getnull $reply http DOCUMENT_ROOT]
    if {$doc_root ne {}} {
      ###
      # Fall back to doc_root handling
      ###
      dict set reply prefix {}
      dict set reply path $doc_root
      dict set reply mixinmap reply httpd::content.file
      dict set reply mixin reply httpd::content.file
      return $reply
    }
    return {}
  }

  ###
  # Method dispatch method invoked prior to invoking methods implemented by plugins.
  # If this method returns a non-empty dictionary, that structure will be passed to
  # the reply. The default is an empty implementation.
  ###
  method Dispatch_Local data {}

  ###
  # Introspect and possibly modify a data structure destined for a reply. This
  # method is invoked before invoking Header methods implemented by plugins.
  # The default implementation is empty.
  ###
  method Headers_Local {varname} {}

  ###
  # Introspect and possibly modify a data structure destined for a reply. This
  # method is built dynamically by the [cmd plugin] method.
  ###
  method Headers_Process varname {}

  ###
  # Convert an ip address to a host name. If the server/ reverse_dns flag
  # is false, this method simply returns the IP address back.
  # Internally, this method uses the [emph dns] module from tcllib.
  ###
  method HostName ipaddr {
    if {![my cget reverse_dns]} {
    if {![my clay get server/ reverse_dns]} {
      return $ipaddr
    }
    set t [::dns::resolve $ipaddr]
    set result [::dns::name $t]
    ::dns::cleanup $t
    return $result
  }

  ###
  # Log an event. The input for args is free form. This method is intended
  # to be replaced by the user, and is a noop for a stock http::server object.
  ###
  method log args {
    # Do nothing for now
  }

  ###
  # Incorporate behaviors from a plugin.
  # This method dynamically rebuilds the [cmd Dispatch] and [cmd Headers]
  # method. For every plugin, the server looks for the following entries in
  # [emph "clay plugin/"]:
  # [para]
  # [emph load] - A script to invoke in the server's namespace during the [cmd plugin] method invokation.
  # [para]
  # [emph dispatch] - A script to stitch into the server's [cmd Dispatch] method.
  # [para]
  # [emph headers] - A script to stitch into the server's [cmd Headers] method.
  # [para]
  # [emph thread] - A script to stitch into the server's [cmd Thread_start] method.
  ###
  method plugin {slot {class {}}} {
    if {$class eq {}} {
      set class ::httpd::plugin.$slot
    }
    if {[info command $class] eq {}} {
      error "Class $class for plugin $slot does not exist"
    }
    my mixinmap $slot $class
    my variable mixinmap
    my clay mixinmap $slot $class
    set mixinmap [my clay get mixin]

    ###
    # Perform action on load
    ###
    eval [$class meta getnull plugin load:]
    set script [$class clay search plugin/ load]
    eval $script

    ###
    # rebuild the dispatch method
    ###
    set body "\n try \{"
    append body \n {
  set reply [my Dispatch_Local $data]
  if {[dict size $reply]} {return $reply}
}

    foreach {slot class} $mixinmap {
      set script [$class meta getnull plugin dispatch:]
      set script [$class clay search plugin/ dispatch]
      if {[string length $script]} {
        append body \n "# SLOT $slot"
        append body \n $script
      }
    }
    append body \n {  return [my Dispatch_Default $data]}
    append body \n "\} on error \{err errdat\} \{"
    append body \n {  puts [list DISPATCH ERROR [dict get $errdat -errorinfo]] ; return {}}
    append body \n "\}"
    oo::objdefine [self] method dispatch data $body
    ###
    # rebuild the Headers_Process method
    ###
    set body "\n try \{"
    append body \n "  upvar 1 \$varname query"
    append body \n {  my Headers_Local query}
    foreach {slot class} $mixinmap {
      set script [$class meta getnull plugin headers:]
      set script [$class clay search plugin/ headers]
      if {[string length $script]} {
        append body \n "# SLOT $slot"
        append body \n $script
      }
    }
    append body \n "\} on error \{err errdat\} \{"
    append body \n {  puts [list HEADERS ERROR [dict get $errdat -errorinfo]] ; return {}}
    append body \n "\}"

    oo::objdefine [self] method Headers_Process varname $body

    ###
    # rebuild the Threads_Start method
    ###
    set body "\n try \{"
    foreach {slot class} $mixinmap {
      set script [$class meta getnull plugin thread:]
      set script [$class clay search plugin/ thread]
      if {[string length $script]} {
        append body \n "# SLOT $slot"
        append body \n $script
      }
    }
    append body \n "\} on error \{err errdat\} \{"
    append body \n {  puts [list THREAD START ERROR [dict get $errdat -errorinfo]] ; return {}}
    append body \n "\}"
    oo::objdefine [self] method Thread_start {} $body

  }

  }

  # Return the actual port that httpd is listening on.
  method port_listening {} {
    my variable port_listening
    return $port_listening
  }

  # For the stock version, trim trailing /'s and *'s from a prefix. This
  # method can be replaced by the end user to perform any other transformations
  # needed for the application.
  method PrefixNormalize prefix {
    set prefix [string trimright $prefix /]
    set prefix [string trimright $prefix *]
    set prefix [string trimright $prefix /]
    return $prefix
  }

  method source {filename} {
    source $filename
  }

  # Open the socket listener.
  method start {} {
    # Build a namespace to contain replies
    namespace eval [namespace current]::reply {}

    my variable socklist port_listening
    if {[my cget configuration_file] ne {}} {
      source [my cget configuration_file]
    if {[my clay get server/ configuration_file] ne {}} {
      source [my clay get server/ configuration_file]
    }
    set port [my cget port]
    set port [my clay get server/ port]
    if { $port in {auto {}} } {
      package require nettool
      set port [::nettool::allocate_port 8015]
    }
    set port_listening $port
    set myaddr [my cget myaddr]
    set myaddr [my clay get server/ myaddr]
    my debug [list [self] listening on $port $myaddr]

    if {$myaddr ni {all any * {}}} {
      foreach ip $myaddr {
        lappend socklist [socket -server [namespace code [list my connect]] -myaddr $ip $port]
      }
    } else {
      lappend socklist [socket -server [namespace code [list my connect]] $port]
    }
    ::cron::every [self] 120 [namespace code {my CheckTimeout}]
    my Thread_start
  }

  # Shut off the socket listener, and destroy any pending replies.
  method stop {} {
    my variable socklist
    if {[info exists socklist]} {
      foreach sock $socklist {
        catch {close $sock}
      }
    }
    set socklist {}
    ::cron::cancel [self]
  }

  Ensemble SubObject::db {} {
    return [namespace current]::Sqlite_db

  }
  Ensemble SubObject::default {} {
    return [namespace current]::$method
  }

  # Return a template for the string [arg page]
  method template page {
    my variable template
    if {[info exists template($page)]} {
      return $template($page)
    }
    set template($page) [my TemplateSearch $page]
    return $template($page)
  }

  # Perform a search for the template that best matches [arg page]. This
  # can include local file searches, in-memory structures, or even
  # database lookups. The stock implementation simply looks for files
  # with a .tml or .html extension in the [opt doc_root] directory.
  method TemplateSearch page {
    set doc_root [my cget doc_root]
    set doc_root [my clay get server/ doc_root]
    if {$doc_root ne {} && [file exists [file join $doc_root $page.tml]]} {
      return [::fileutil::cat [file join $doc_root $page.tml]]
    }
    if {$doc_root ne {} && [file exists [file join $doc_root $page.html]]} {
      return [::fileutil::cat [file join $doc_root $page.html]]
    }
    switch $page {
      redirect {
return {
[my html header "$HTTP_STATUS"]
The page you are looking for: <b>[my http_info get REQUEST_URI]</b> has moved.
[my html_header "$HTTP_STATUS"]
The page you are looking for: <b>[my request get REQUEST_URI]</b> has moved.
<p>
If your browser does not automatically load the new location, it is
<a href=\"$msg\">$msg</a>
[my html footer]
[my html_footer]
}
      }
      internal_error {
        return {
[my html header "$HTTP_STATUS"]
Error serving <b>[my http_info get REQUEST_URI]</b>:
[my html_header "$HTTP_STATUS"]
Error serving <b>[my request get REQUEST_URI]</b>:
<p>
The server encountered an internal server error: <pre>$msg</pre>
<pre><code>
$errorInfo
</code></pre>
[my html footer]
[my html_footer]
        }
      }
      notfound {
        return {
[my html header "$HTTP_STATUS"]
The page you are looking for: <b>[my http_info get REQUEST_URI]</b> does not exist.
[my html footer]
[my html_header "$HTTP_STATUS"]
The page you are looking for: <b>[my request get REQUEST_URI]</b> does not exist.
[my html_footer]
        }
      }
    }
  }

  ###
  # Built by the [cmd plugin] method. Called by the [cmd start] method. Intended
  # to allow plugins to spawn worker threads.
  ###
  method Thread_start {} {}

  ###
  # Generate a GUUID. Used to ensure every request has a unique ID.
  # The default implementation is:
  # [example {
  #   return [::uuid::uuid generate]
  # }]
  ###
  method Uuid_Generate {} {
    return [::uuid::uuid generate]
  }

  ###
  # Return true if this IP address is blocked
  # The socket will be closed immediately after returning
  # This handler is welcome to send a polite error message
  # Given a socket and an ip address, return true if this connection should
  # be terminated, or false if it should be allowed to continue. The stock
  # implementation always returns 0. This is intended for applications to
  # be able to implement black lists and/or provide security based on IP
  # address.
  ###
  method Validate_Connection {sock ip} {
    return 0
  }
}

###
# Provide a backward compadible alias
###
::tool::define ::httpd::server::dispatch {
::clay::define ::httpd::server::dispatch {
    superclass ::httpd::server
}

###
# END: server.tcl
###
###
# START: dispatch.tcl
###
::tool::define ::httpd::content.redirect {
::clay::define ::httpd::content.redirect {

  method reset {} {
    ###
    # Inject the location into the HTTP headers
    ###
    my variable reply_body
    set reply_body {}
    my reply replace    [my HttpHeaders_Default]
    my reply set Server [my <server> cget server_string]
    set msg [my http_info get LOCATION]
    my reply set Location [my http_info get LOCATION]
    set code  [my http_info getnull REDIRECT_CODE]
    my reply set Server [my <server> clay get server/ string]
    set msg [my clay get LOCATION]
    my reply set Location [my clay get LOCATION]
    set code  [my clay get REDIRECT_CODE]
    if {$code eq {}} {
      set code 301
    }
    my reply set Status [list $code [my http_code_string $code]]
  }

  method content {} {
    set template [my <server> template redirect]
    set msg [my http_info get LOCATION]
    set msg [my clay get LOCATION]
    set HTTP_STATUS [my reply get Status]
    my puts [subst $msg]
  }
}

::tool::define ::httpd::content.cache {
::clay::define ::httpd::content.cache {

  method dispatch {newsock datastate} {
  method Dispatch {} {
    my http_info replace $datastate
    my request replace  [dict get $datastate http]
    my variable chan
    set chan $newsock
    chan event $chan readable {}
    try {
      my Log_Dispatched
      my wait writable $chan
      chan configure $chan  -translation {binary binary}
      chan puts -nonewline $chan [my http_info get CACHE_DATA]
      chan puts -nonewline $chan [my clay get cache/ data]
    } on error {err info} {
      my <server> debug [dict get $info -errorinfo]
    } finally {
      my TransferComplete $chan
    }
  }
}

::tool::define ::httpd::content.template {
::clay::define ::httpd::content.template {

  method content {} {
    if {[my http_info getnull HTTP_STATUS] ne {}} {
      my reply set Status [my http_info getnull HTTP_STATUS]
    if {[my request get HTTP_STATUS] ne {}} {
      my reply set Status [my request get HTTP_STATUS]
    }
    my puts [subst [my <server> template [my http_info get template]]]
    my puts [subst [my <server> template [my clay get template]]]
  }
}

###
# END: dispatch.tcl
###
###
# START: file.tcl
###
###
# Class to deliver Static content
# When utilized, this class is fed a local filename
# by the dispatcher
###
::tool::define ::httpd::content.file {
::clay::define ::httpd::content.file {

  method FileName {} {
    set uri [string trimleft [my http_info get REQUEST_URI] /]
    set path [my http_info get path]
    set prefix [my http_info get prefix]
    set uri [string trimleft [my request get REQUEST_URI] /]
    set path [my clay get path]
    set prefix [my clay get prefix]
    set fname [string range $uri [string length $prefix] end]
    if {$fname in "{} index.html index.md index"} {
      return $path
    }
    if {[file exists [file join $path $fname]]} {
      return [file join $path $fname]
    }
    if {[file exists [file join $path $fname.md]]} {
      return [file join $path $fname.md]
    }
    if {[file exists [file join $path $fname.html]]} {
      return [file join $path $fname.html]
    }
    if {[file exists [file join $path $fname.tml]]} {
      return [file join $path $fname.tml]
    }
    return {}
  }

  method DirectoryListing {local_file} {
    set uri [string trimleft [my http_info get REQUEST_URI] /]
    set path [my http_info get path]
    set prefix [my http_info get prefix]
    set uri [string trimleft [my request get REQUEST_URI] /]
    set path [my clay get path]
    set prefix [my clay get prefix]
    set fname [string range $uri [string length $prefix] end]
    my puts [my html_header "Listing of /$fname/"]
    my puts "Listing contents of /$fname/"
    my puts "<TABLE>"
    if {$prefix ni {/ {}}} {
      set updir [file dirname $prefix]
      if {$updir ne {}} {
1193
1194
1195
1196
1197
1198
1199
1200

1201
1202
1203
1204
1205
1206
1207
1558
1559
1560
1561
1562
1563
1564

1565
1566
1567
1568
1569
1570
1571
1572







-
+







    my puts [my html_footer]
  }

  method content {} {
    my variable reply_file
    set local_file [my FileName]
    if {$local_file eq {} || ![file exist $local_file]} {
      my log httpNotFound [my http_info get REQUEST_URI]
      my log httpNotFound [my request get REQUEST_URI]
      my error 404 {File Not Found}
      tailcall my DoOutput
    }
    if {[file isdirectory $local_file] || [file tail $local_file] in {index index.html index.tml index.md}} {
      ###
      # Produce an index page
      ###
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
1593
1594
1595
1596
1597
1598
1599

1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613

1614
1615
1616







1617
1618
1619
1620
1621
1622
1623







-
+













-
+


-
-
-
-
-
-
-







        my reply set Content-Type {text/html; charset=UTF-8}
        set mdtxt  [::fileutil::cat $local_file]
        my puts [::Markdown::convert $mdtxt]
      }
      .tml {
        my reply set Content-Type {text/html; charset=UTF-8}
        set tmltxt  [::fileutil::cat $local_file]
        set headers [my http_info dump]
        set headers [my request dump]
        dict with headers {}
        my puts [subst $tmltxt]
      }
      default {
        ###
        # Assume we are returning a binary file
        ###
        my reply set Content-Type [::fileutil::magic::filetype $local_file]
        set reply_file $local_file
      }
    }
  }

  method dispatch {newsock datastate} {
  method Dispatch {} {
    my variable reply_body reply_file reply_chan chan
    try {
      my http_info replace $datastate
      my request replace  [dict get $datastate http]
      my Log_Dispatched
      set chan $newsock
      chan event $chan readable {}
      chan configure $chan -translation {auto crlf} -buffering line

      my reset
      # Invoke the URL implementation.
      my content
    } on error {err errdat} {
      my error 500 $err [dict get $errdat -errorinfo]
      tailcall my DoOutput
    }
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
1634
1635
1636
1637
1638
1639
1640

1641

1642









1643
1644


1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657

1658
1659
1660
1661
1662
1663
1664
1665







-

-
+
-
-
-
-
-
-
-
-
-

+
-
-
+
+











-
+







      ###
      set size [file size $reply_file]
      my reply set Content-Length $size
      append result [my reply output] \n
      chan puts -nonewline $chan $result
      set reply_chan [open $reply_file r]
      my log SendReply [list length $size]
      chan configure $reply_chan  -translation {binary binary}
      ###
      # Send any POST/PUT/etc content
      # Output the file contents. With no -size flag, channel will copy until EOF
      # Note, we are terminating the coroutine at this point
      # and using the file event to wake the object back up
      #
      # We *could*:
      # chan copy $sock $chan -command [info coroutine]
      # yield
      #
      # But in the field this pegs the CPU for long transfers and locks
      # up the process
      ###
      chan configure $reply_chan -translation {binary binary} -buffersize 4096 -buffering full -blocking 0
      chan copy $reply_chan $chan -command [namespace code [list my TransferComplete $reply_chan $chan]]
    } on error {err errdat} {
      my ChannelCopy $reply_chan $chan -chunk 4096
    } finally {
      my TransferComplete $reply_chan $chan
    }
  }
}

###
# END: file.tcl
###
###
# START: proxy.tcl
###
::tool::define ::httpd::content.exec {
::clay::define ::httpd::content.exec {
  variable exename [list tcl [info nameofexecutable] .tcl [info nameofexecutable]]

  method CgiExec {execname script arglist} {
    if { $::tcl_platform(platform) eq "windows"} {
      if {[file extension $script] eq ".exe"} {
        return [open "|[list $script] $arglist" r+]
      } else {
1364
1365
1366
1367
1368
1369
1370
1371
1372


1373
1374
1375
1376
1377
1378


1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396

1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409


1410
1411
1412
1413
1414
1415
1416
1417
1418




1419
1420
1421
1422
1423
1424
1425

1426
1427
1428
1429

1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443

1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458





1459
1460
1461
1462
1463



1464
1465
1466
1467
1468
1469
1470


1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490

1491
1492





1493
1494
1495
1496
1497
1498
1499
1500
1501
1502

1503
1504
1505
1506
1507
1508



1509
1510
1511
1512
1513
1514
1515
1713
1714
1715
1716
1717
1718
1719


1720
1721
1722
1723
1724
1725


1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744

1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756


1757
1758
1759
1760
1761
1762
1763
1764



1765
1766
1767
1768
1769
1770
1771
1772
1773
1774

1775
1776
1777

1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788





1789
1790
1791
1792
1793
1794
1795
1796
1797







1798
1799
1800
1801
1802





1803
1804
1805







1806
1807









1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819


1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833

1834
1835
1836
1837



1838
1839
1840
1841
1842
1843
1844
1845
1846
1847







-
-
+
+




-
-
+
+

















-
+











-
-
+
+






-
-
-
+
+
+
+






-
+


-

+









-
-
-
-
-
+








-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-











+
-
-
+
+
+
+
+









-
+



-
-
-
+
+
+







        return $result
      }
    }
    if {[dict exists exename $which]} {
      return [dict get $exename $which]
    }
    if {$which eq "tcl"} {
      if {[my cget tcl_exe] ne {}} {
        dict set exename $which [my cget tcl_exe]
      if {[my clay get tcl_exe] ne {}} {
        dict set exename $which [my clay get tcl_exe]
      } else {
        dict set exename $which [info nameofexecutable]
      }
    } else {
      if {[my cget ${which}_exe] ne {}} {
        dict set exename $which [my cget ${which}_exe]
      if {[my clay get ${which}_exe] ne {}} {
        dict set exename $which [my clay get ${which}_exe]
      } elseif {"$::tcl_platform(platform)" == "windows"} {
        dict set exename $which $which.exe
      } else {
        dict set exename $which $which
      }
    }
    set result [dict get $exename $which]
    if {$ext ne {}} {
      dict set exename $ext $result
    }
    return $result
  }
}

###
# Return data from an proxy process
###
::tool::define ::httpd::content.proxy {
::clay::define ::httpd::content.proxy {
  superclass ::httpd::content.exec

  method proxy_channel {} {
    ###
    # This method returns a channel to the
    # proxied socket/stdout/etc
    ###
    error unimplemented
  }

  method proxy_path {} {
    set uri [string trimleft [my http_info get REQUEST_URI] /]
    set prefix [my http_info get prefix]
    set uri [string trimleft [my request get REQUEST_URI] /]
    set prefix [my clay get prefix]
    return /[string range $uri [string length $prefix] end]
  }

  method ProxyRequest {chana chanb} {
    chan event $chanb writable {}
    my log ProxyRequest {}
    chan puts $chanb "[my http_info get REQUEST_METHOD] [my proxy_path]"
    chan puts $chanb [my http_info get mimetxt]
    set length [my http_info get CONTENT_LENGTH]
    chan puts $chanb "[my request get REQUEST_METHOD] [my proxy_path]"
    set mimetxt [my clay get mimetxt]
    chan puts $chanb [my clay get mimetxt]
    set length [my request get CONTENT_LENGTH]
    if {$length} {
      chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
      chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
      ###
      # Send any POST/PUT/etc content
      ###
      chan copy $chana $chanb -size $length -command [info coroutine]
      my ChannelCopy $chana $chanb -size $length
    } else {
      chan flush $chanb
      chan event $chanb readable [info coroutine]
    }
    chan event $chanb readable [info coroutine]
    yield
  }

  method ProxyReply {chana chanb args} {
    my log ProxyReply [list args $args]
    chan event $chana readable {}
    set readCount [::coroutine::util::gets_safety $chana 4096 reply_status]
    set replyhead [my HttpHeaders $chana]
    set replydat  [my MimeParse $replyhead]
    if {![dict exists $replydat Content-Length]} {
      set length 0
    } else {
      set length [dict get $replydat Content-Length]
    }

    ###
    # Read the first incoming line as the HTTP reply status
    # Return the rest of the headers verbatim
    ###
    set replybuffer "$reply_status\n"
    append replybuffer $replyhead
    chan configure $chanb -translation {auto crlf} -blocking 0 -buffering full -buffersize 4096
    chan puts $chanb $replybuffer
    my log SendReply [list length $length]
    if {$length} {
      ###
      # Output the body
      ###
      chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
      chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
    ###
    # Output the body. With no -size flag, channel will copy until EOF
    ###
    chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
    chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
      chan copy $chana $chanb -size $length -command [namespace code [list my TransferComplete $chana $chanb]]
    } else {
      my TransferComplete $chana $chanb
    }
  }
    my ChannelCopy $chana $chanb -chunk 4096
  }


  method dispatch {newsock datastate} {
    try {
      my http_info replace $datastate
      my request replace  [dict get $datastate http]
      my Log_Dispatched
      my variable sock chan
  method Dispatch {} {
    my variable sock chan
      set chan $newsock
      chan configure $chan -translation {auto crlf} -buffering line
      # Initialize the reply
      my reset
      # Invoke the URL implementation.
    } on error {err errdat} {
      my error 500 $err [dict get $errdat -errorinfo]
      tailcall my DoOutput
    }
    if {[catch {my proxy_channel} sock errdat]} {
      my error 504 {Service Temporarily Unavailable} [dict get $errdat -errorinfo]
      tailcall my DoOutput
    }
    if {$sock eq {}} {
      my error 404 {Not Found}
      tailcall my DoOutput
    }
    my log HttpAccess {}
    chan event $sock writable [info coroutine]
    yield
    try {
    my ProxyRequest $chan $sock
    my ProxyReply   $sock $chan
      my ProxyRequest $chan $sock
      my ProxyReply   $sock $chan
    } finally {
      my TransferComplete $chan $sock
    }
  }
}

###
# END: proxy.tcl
###
###
# START: cgi.tcl
###
::tool::define ::httpd::content.cgi {
::clay::define ::httpd::content.cgi {
  superclass ::httpd::content.proxy

  method FileName {} {
    set uri [string trimleft [my http_info get REQUEST_URI] /]
    set path [my http_info get path]
    set prefix [my http_info get prefix]
    set uri [string trimleft [my request get REQUEST_URI] /]
    set path [my clay get path]
    set prefix [my clay get prefix]

    set fname [string range $uri [string length $prefix] end]
    if {[file exists [file join $path $fname]]} {
      return [file join $path $fname]
    }
    if {[file exists [file join $path $fname.fossil]]} {
      return [file join $path $fname.fossil]
1525
1526
1527
1528
1529
1530
1531
1532

1533
1534
1535
1536
1537
1538
1539
1857
1858
1859
1860
1861
1862
1863

1864
1865
1866
1867
1868
1869
1870
1871







-
+








  method proxy_channel {} {
    ###
    # When delivering static content, allow web caches to save
    ###
    set local_file [my FileName]
    if {$local_file eq {} || ![file exist $local_file]} {
      my log httpNotFound [my http_info get REQUEST_URI]
      my log httpNotFound [my request get REQUEST_URI]
      my error 404 {Not Found}
      tailcall my DoOutput
    }
    if {[file isdirectory $local_file]} {
      ###
      # Produce an index page... or error
      ###
1549
1550
1551
1552
1553
1554
1555
1556

1557
1558
1559

1560
1561

1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1881
1882
1883
1884
1885
1886
1887

1888
1889
1890

1891


1892

1893
1894
1895
1896







1897
1898
1899
1900
1901
1902
1903







-
+


-
+
-
-
+
-




-
-
-
-
-
-
-







    }
    foreach item $verbatim {
      set ::env($item) {}
    }
    foreach item [array names ::env HTTP_*] {
      set ::env($item) {}
    }
    set ::env(SCRIPT_NAME) [my http_info get REQUEST_PATH]
    set ::env(SCRIPT_NAME) [my request get REQUEST_PATH]
    set ::env(SERVER_PROTOCOL) HTTP/1.0
    set ::env(HOME) $::env(DOCUMENT_ROOT)
    foreach {f v} [my http_info dump] {
    foreach {f v} [my request dump] {
      if {$f in $verbatim} {
        set ::env($f) $v
      set ::env($f) $v
      }
    }
  	set arglist $::env(QUERY_STRING)
    set pwd [pwd]
    cd [file dirname $local_file]
    foreach {f v} [my request dump] {
      if {$f in $verbatim} {
        set ::env($f) $v
      } else {
        set ::env(HTTP_$f) $v
      }
    }
    set script_file $local_file
    if {[file extension $local_file] in {.fossil .fos}} {
      if {![file exists $local_file.cgi]} {
        set fout [open $local_file.cgi w]
        chan puts $fout "#!/usr/bin/fossil"
        chan puts $fout "repository: $local_file"
        close $fout
1589
1590
1591
1592
1593
1594
1595
1596

1597
1598
1599
1600
1601
1602
1603

1604
1605
1606
1607

1608
1609
1610
1611
1612
1613
1614
1615
1616
1912
1913
1914
1915
1916
1917
1918

1919
1920
1921
1922
1923
1924
1925

1926
1927
1928

1929
1930
1931

1932
1933
1934
1935
1936
1937
1938







-
+






-
+


-

+

-







    cd $pwd
    return $pipe
  }

  method ProxyRequest {chana chanb} {
    chan event $chanb writable {}
    my log ProxyRequest {}
    set length [my http_info get CONTENT_LENGTH]
    set length [my request get CONTENT_LENGTH]
    if {$length} {
      chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
      chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
      ###
      # Send any POST/PUT/etc content
      ###
      chan copy $chana $chanb -size $length -command [info coroutine]
      my ChannelCopy $chana $chanb -size $length
    } else {
      chan flush $chanb
      chan event $chanb readable [info coroutine]
    }
    chan event $chanb readable [info coroutine]
    yield

  }


  method ProxyReply {chana chanb args} {
    my log ProxyReply [list args $args]
    chan event $chana readable {}
    set replyhead [my HttpHeaders $chana]
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638





1639
1640
1641

1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662







1663

1664

1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683

1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697


1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713

1714

1715
1716
1717
1718

1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747





1748
1749
1750
1751
1752
1753
1754

1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766

1767
1768
1769
1770
1771



1772
1773



1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787




1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804

1805
1806
1807

1808
1809
1810


1811
1812

1813
1814
1815
1816
1817
1818
1819
1820
1821









1822
1823
1824
1825






1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846

1847
1848
1849


1850
1851
1852
1853

1854
1855
1856
1857
1858
1859
1860
1861







1862

1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876

1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889

1890
1891
1892
1893
1894
1895
1896
1897
1898

1899
1900
1901
1902
1903

1904
1905
1906
1907
1908

1909
1910
1911
1912
1913

1914
1915
1916
1917
1918
1919

1920
1921
1922
1923
1924
1925
1926
1927
1928


1929
1930
1931
1932
1933
1934



1935

1936
1937
1938



1939

1940
1941
1942
1943
1944
1945
1946







1947
1948
1949


1950
1951
1952
1953
1954
1955
1956
1957
1958


1959
1960
1961
1962
1963
1964




1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990



















1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007

2008
2009

2010
2011
2012
2013
2014
2015
2016
1947
1948
1949
1950
1951
1952
1953







1954
1955
1956
1957
1958



1959

1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986

1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007

2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020


2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039

2040
2041
2042

2043
2044
2045
2046
2047
2048
2049
2050
2051
2052





2053
2054
2055
2056
2057
2058
2059
2060
2061







2062
2063
2064
2065
2066







2067





2068
2069
2070
2071
2072
2073

2074
2075
2076



2077
2078
2079
2080

2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095


2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113



2114



2115



2116
2117
2118

2119
2120
2121







2122
2123
2124
2125
2126
2127
2128
2129
2130




2131
2132
2133
2134
2135
2136





















2137



2138
2139




2140








2141
2142
2143
2144
2145
2146
2147

2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161

2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174

2175
2176
2177
2178
2179
2180
2181
2182
2183

2184
2185
2186
2187
2188

2189
2190
2191
2192
2193

2194
2195
2196
2197
2198

2199
2200
2201
2202
2203
2204

2205
2206
2207
2208
2209
2210
2211
2212


2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225



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







-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
+
-




















+
+
+
+
+
+
+
-
+

+


















-
+












-
-
+
+
















+
-
+


-

+








-
-
-
-
-









-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
+
-
-
-
-
-






-
+


-
-
-
+
+
+

-
+
+
+












-
-
+
+
+
+














-
-
-
+
-
-
-
+
-
-
-
+
+

-
+


-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
+
+
-
-
-
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+













-
+












-
+








-
+




-
+




-
+




-
+





-
+







-
-
+
+






+
+
+

+
-
-
-
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-










-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
















-
+

-
+







    # a standard service reply line from a web server, but
    # otherwise spit out the rest of the headers verbatim
    ###
    set replybuffer "HTTP/1.0 [dict get $replydat Status]\n"
    append replybuffer $replyhead
    chan configure $chanb -translation {auto crlf} -blocking 0 -buffering full -buffersize 4096
    chan puts $chanb $replybuffer
    my log SendReply [list length $length]
    if {$length} {
      ###
      # Output the body
      ###
      chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
      chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
    ###
    # Output the body. With no -size flag, channel will copy until EOF
    ###
    chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
    chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
      chan copy $chana $chanb -size $length -command [namespace code [list my TransferComplete $chana $chanb]]
    } else {
      my TransferComplete $chana $chanb
    my ChannelCopy $chana $chanb -chunk 4096
    }
  }

  ###
  # For most CGI applications a directory list is vorboten
  ###
  method DirectoryListing {local_file} {
    my error 403 {Not Allowed}
    tailcall my DoOutput
  }
}

###
# END: cgi.tcl
###
###
# START: scgi.tcl
###
###
# Return data from an SCGI process
###
::clay::define ::httpd::protocol.scgi {

  method EncodeStatus {status} {
    return "Status: $status"
  }
}

::tool::define ::httpd::content.scgi {
::clay::define ::httpd::content.scgi {
  superclass ::httpd::content.proxy


  method scgi_info {} {
    ###
    # This method should check if a process is launched
    # or launch it if needed, and return a list of
    # HOST PORT SCRIPT_NAME
    ###
    # return {localhost 8016 /some/path}
    error unimplemented
  }

  method proxy_channel {} {
    set sockinfo [my scgi_info]
    if {$sockinfo eq {}} {
      my error 404 {Not Found}
      tailcall my DoOutput
    }
    lassign $sockinfo scgihost scgiport scgiscript
    my http_info set SCRIPT_NAME $scgiscript
    my clay set  SCRIPT_NAME $scgiscript
    if {![string is integer $scgiport]} {
      my error 404 {Not Found}
      tailcall my DoOutput
    }
    return [::socket $scgihost $scgiport]
  }

  method ProxyRequest {chana chanb} {
    chan event $chanb writable {}
    my log ProxyRequest {}
    chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
    chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
    set info [dict create CONTENT_LENGTH 0 SCGI 1.0 SCRIPT_NAME [my http_info get SCRIPT_NAME]]
    foreach {f v} [my http_info dump] {
    set info [dict create CONTENT_LENGTH 0 SCGI 1.0 SCRIPT_NAME [my clay get SCRIPT_NAME]]
    foreach {f v} [my request dump] {
      dict set info $f $v
    }
    set length [dict get $info CONTENT_LENGTH]
    set block {}
    foreach {f v} $info {
      append block [string toupper $f] \x00 $v \x00
    }
    chan puts -nonewline $chanb "[string length $block]:$block,"
    # Light off another coroutine
    #set cmd [list coroutine [my CoroName] {*}[namespace code [list my ProxyReply $chanb $chana]]]
    if {$length} {
      chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
      chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
      ###
      # Send any POST/PUT/etc content
      ###
      my ChannelCopy $chana $chanb -size $length
      chan copy $chana $chanb -size $length -command [info coroutine]
      #chan copy $chana $chanb -size $length -command [info coroutine]
    } else {
      chan flush $chanb
      chan event $chanb readable [info coroutine]
    }
    chan event $chanb readable [info coroutine]
    yield
  }

  method ProxyReply {chana chanb args} {
    my log ProxyReply [list args $args]
    chan event $chana readable {}
    set replyhead [my HttpHeaders $chana]
    set replydat  [my MimeParse $replyhead]
    if {![dict exists $replydat Content-Length]} {
      set length 0
    } else {
      set length [dict get $replydat Content-Length]
    }
    ###
    # Convert the Status: header from the CGI process to
    # a standard service reply line from a web server, but
    # otherwise spit out the rest of the headers verbatim
    ###
    set replybuffer "HTTP/1.0 [dict get $replydat Status]\n"
    append replybuffer $replyhead
    chan configure $chanb -translation {auto crlf} -blocking 0 -buffering full -buffersize 4096
    chan puts $chanb $replybuffer
    my log SendReply [list length $length]
    if {$length} {
      ###
      # Output the body
      ###
      chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
      chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
    ###
    # Output the body. With no -size flag, channel will copy until EOF
    ###
    chan configure $chana -translation binary -blocking 0 -buffering full -buffersize 4096
    chan configure $chanb -translation binary -blocking 0 -buffering full -buffersize 4096
      chan copy $chana $chanb -size $length -command [namespace code [list my TransferComplete $chana $chanb]]
    } else {
      my TransferComplete $chan $chanb
    }
  }
}

    my ChannelCopy $chana $chanb -chunk 4096
tool::define ::httpd::reply.scgi {
  superclass ::httpd::reply

  method EncodeStatus {status} {
    return "Status: $status"
  }
}

###
# Act as an  SCGI Server
###
tool::define ::httpd::server.scgi {
::clay::define ::httpd::server.scgi {
  superclass ::httpd::server

  property socket buffersize   32768
  property socket blocking     0
  property socket translation  {binary binary}
  clay set socket/ buffersize   32768
  clay set socket/ blocking     0
  clay set socket/ translation  {binary binary}

  property reply_class ::httpd::reply.scgi
  method debug args {
    puts $args
  }

  method Connect {uuid sock ip} {
    yield [info coroutine]
    chan event $sock readable {}
    chan configure $sock \
        -blocking 1 \
        -translation {binary binary} \
        -buffersize 4096 \
        -buffering none
    my counter url_hit
    try {
      # Read the SCGI request on byte at a time until we reach a ":"
      dict set query REQUEST_URI /
      dict set query REMOTE_ADDR     $ip
      dict set query http HTTP_HOST {}
      dict set query http CONTENT_LENGTH 0
      dict set query http REQUEST_URI /
      dict set query http REMOTE_ADDR $ip
      set size {}
      while 1 {
        set char [::coroutine::util::read $sock 1]
        if {[chan eof $sock]} {
          catch {close $sock}
          return
        }
        if {$char eq ":"} break
        append size $char
      }
      # With length in hand, read the netstring encoded headers
      set inbuffer [::coroutine::util::read $sock [expr {$size+1}]]
      chan configure $sock -blocking 0 -buffersize 4096 -buffering full
      foreach {f v} [lrange [split [string range $inbuffer 0 end-1] \0] 0 end-1] {
        dict set query $f $v
        if {$f in {CONTENT_LENGTH CONTENT_TYPE}} {
          dict set query http $f $v
        dict set query http $f $v
        } elseif {[string range $f 0 4] eq "HTTP_"} {
          dict set query http [string range $f 5 end] $v
        }
      }
      }
      if {![dict exists $query REQUEST_PATH]} {
        set uri [dict get $query REQUEST_URI]
      if {![dict exists $query http REQUEST_PATH]} {
        set uri [dict get $query http REQUEST_URI]
        set uriinfo [::uri::split $uri]
        dict set query REQUEST_PATH    [dict get $uriinfo path]
        dict set query http REQUEST_PATH    [dict get $uriinfo path]
      }
      set reply [my dispatch $query]
      dict with query {}
      if {[llength $reply]} {
        if {[dict exists $reply class]} {
          set class [dict get $reply class]
        } else {
          set class [my cget reply_class]
        }
    } on error {err errdat} {
      my debug [list uri: [dict getnull $query http REQUEST_URI] ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      my log BadRequest $uuid [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      catch {chan puts $sock "HTTP/1.0 400 Bad Request (The data is invalid)"}
      catch {chan event readable $sock {}}
      catch {chan event writeable $sock {}}
      catch {chan close $sock}
      return
    }
        set pageobj [$class create [namespace current]::reply$uuid [self]]
        if {[dict exists $reply mixin]} {
          oo::objdefine $pageobj mixin [dict get $reply mixin]
        }
    if {[dict size $reply]==0} {
      my log BadLocation $uuid $query
      dict set query http HTTP_STATUS 404
      dict set query template notfound
      dict set query mixin reply ::httpd::content.template
    }
        $pageobj dispatch $sock $reply
        my log HttpAccess $REQUEST_URI
      } else {
        try {
          my log HttpMissing $REQUEST_URI
          chan puts $sock "Status: 404 NOT FOUND"
          dict with query {}
          set body [subst [my template notfound]]
          chan puts $sock "Content-Length: [string length $body]"
          chan puts $sock {}
          chan puts $sock $body
        } on error {err errdat} {
          my <server> debug "FAILED ON 404: $err [dict get $errdat -errorinfo]"
        } finally {
          catch {chan event readable $sock {}}
          catch {chan event writeable $sock {}}
          catch {chan close $sock}
        }
      }
    } on error {err errdat} {
      try {
    try {
        my <server> debug [dict get $errdat -errorinfo]
        chan puts $sock "Status: 500 INTERNAL ERROR - scgi 298"
        dict with query {}
      set pageobj [::httpd::reply create ::httpd::object::$uuid [self]]
      dict set reply mixin protocol ::httpd::protocol.scgi
        set body [subst [my template internal_error]]
        chan puts $sock "Content-Length: [string length $body]"
        chan puts $sock {}
        chan puts $sock $body
      $pageobj dispatch $sock $reply
        my log HttpError [list error [my http_info get REMOTE_ADDR] errorinfo [dict get $errdat -errorinfo]]
      } on error {err errdat} {
        my log HttpFatal [list error [my http_info get REMOTE_ADDR] errorinfo [dict get $errdat -errorinfo]]
        my <server> debug "Failed on 500: [dict get $errdat -errorinfo]""
      } finally {
        catch {chan event readable $sock {}}
        catch {chan event writeable $sock {}}
        catch {chan close $sock}
    } on error {err errdat} {
      my debug [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      my log BadRequest $uuid [list ip: $ip error: $err errorinfo: [dict get $errdat -errorinfo]]
      catch {$pageobj destroy}
      catch {chan event readable $sock {}}
      catch {chan event writeable $sock {}}
      catch {chan close $sock}
      }
      return
    }
  }
}

###
# END: scgi.tcl
###
###
# START: websocket.tcl
###
###
# Upgrade a connection to a websocket
###
::tool::define ::httpd::content.websocket {
::clay::define ::httpd::content.websocket {

}

###
# END: websocket.tcl
###
###
# START: plugin.tcl
###
###
# httpd plugin template
###
tool::define ::httpd::plugin {
::clay::define ::httpd::plugin {
  ###
  # Any options will be saved to the local config file
  # to allow threads to pull up a snapshot of the object' configuration
  ###

  ###
  # Define a code snippet to run on plugin load
  ###
  meta set plugin load: {}
  clay set plugin/ load {}

  ###
  # Define a code snippet to run within the object's Headers_Process method
  ###
  meta set plugin headers: {}
  clay set plugin/ headers {}

  ###
  # Define a code snippet to run within the object's dispatch method
  ###
  meta set plugin dispatch: {}
  clay set plugin/ dispatch {}

  ###
  # Define a code snippet to run within the object's writes a local config file
  ###
  meta set plugin local_config: {}
  clay set plugin/ local_config {}

  ###
  # When after all the plugins are loaded
  # allow specially configured ones to light off a thread
  ###
  meta set plugin thread: {}
  clay set plugin/ thread {}

}

###
# A rudimentary plugin that dispatches URLs from a dict
# data structure
###
tool::define ::httpd::plugin.dict_dispatch {
  meta set plugin dispatch: {
::clay::define ::httpd::plugin.dict_dispatch {
  clay set plugin/ dispatch {
    set reply [my Dispatch_Dict $data]
    if {[dict size $reply]} {
      return $reply
    }
  }

  ###
  # Implementation of the dispatcher
  ###
  method Dispatch_Dict {data} {
    my variable url_patterns
    set vhost [lindex [split [dict get $data HTTP_HOST] :] 0]
    set uri   [dict get $data REQUEST_PATH]
    foreach {host pattern info} [my uri patterns] {
    set vhost [lindex [split [dict get $data http HTTP_HOST] :] 0]
    set uri   [dict get $data http REQUEST_PATH]
    foreach {host hostpat} $url_patterns {
      if {![string match $host $vhost]} continue
      foreach {pattern info} $hostpat {
      if {![string match $pattern $uri]} continue
      set buffer $data
      foreach {f v} $info {
        dict set buffer $f $v
      }
      return $buffer
    }
        if {![string match $pattern $uri]} continue
        set buffer $data
        foreach {f v} $info {
          dict set buffer $f $v
        }
        return $buffer
      }
    return {}
  }

    }
    return {}
  method uri::patterns {} {
    my variable url_patterns url_stream
    if {![info exists url_stream]} {
      set url_stream {}
      foreach {host hostpat} $url_patterns {
        foreach {pattern info} $hostpat {
          lappend url_stream $host $pattern $info
        }
      }
  }

    }
    return $url_stream
  }

  method uri::add args {
    my variable url_patterns url_stream
  ###
  #
  Ensemble uri::add {vhosts patterns info} {
    my variable url_patterns
    unset -nocomplain url_stream
    switch [llength $args] {
      2 {
        set vhosts *
        lassign $args patterns info
      }
      3 {
        lassign $args vhosts patterns info
      }
      default {
        error "Usage: add_url ?vhosts? prefix info"
      }
    }
    foreach vhost $vhosts {
      foreach pattern $patterns {
        set data $info
        if {![dict exists $data prefix]} {
           dict set data prefix [my PrefixNormalize $pattern]
        }
        dict set url_patterns $vhost [string trimleft $pattern /] $data
      }
    }
  }
}

tool::define ::httpd::reply.memchan {

  Ensemble uri::direct {vhosts patterns info body} {
    my variable url_patterns url_stream
    set cbody {}
    if {[dict exists $info superclass]} {
      append cbody \n "superclass {*}[dict get $info superclass]"
      dict unset info superclass
    }
    append cbody \n [list method content {} $body]

    set class [namespace current]::${vhosts}/${patterns}
    set class [string map {* %} $class]
    ::clay::define $class $cbody
    dict set info mixin content $class
    my uri add $vhosts $patterns $info
  }
}

::clay::define ::httpd::reply.memchan {
  superclass ::httpd::reply

  method output {} {
    my variable reply_body
    return $reply_body
  }

  method DoOutput {} {}

  method close {} {
    # Neuter the channel closing mechanism we need the channel to stay alive
    # until the reader sucks out the info
  }
}


tool::define ::httpd::plugin.local_memchan {
::clay::define ::httpd::plugin.local_memchan {

  meta set plugin load: {
  clay set plugin/ load {
package require tcl::chan::events
package require tcl::chan::memchan
  }

  method local_memchan {command args} {
    my variable sock_to_coro
    switch $command {
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
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







+
-
-
-
-
+
+
+
+


-
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+






-
+

-
+




-
-
+
+



-
-
-




-
-
-
+
+
+








    chan configure $sock \
      -blocking 0 \
      -translation {auto crlf} \
      -buffering line
    set ip 127.0.0.1
    dict set query UUID $uuid
    dict set query http UUID $uuid
    dict set query HTTP_HOST       localhost
    dict set query REMOTE_ADDR     127.0.0.1
    dict set query REMOTE_HOST     localhost
    dict set query LOCALHOST 1
    dict set query http HTTP_HOST       localhost
    dict set query http REMOTE_ADDR     127.0.0.1
    dict set query http REMOTE_HOST     localhost
    dict set query http LOCALHOST 1
    my counter url_hit

    dict set query REQUEST_METHOD  [lindex $args 0]
    dict set query http REQUEST_METHOD  [lindex $args 0]
    set uriinfo [::uri::split [lindex $args 1]]
    dict set query REQUEST_URI     [lindex $args 1]
    dict set query REQUEST_PATH    [dict get $uriinfo path]
    dict set query REQUEST_VERSION [lindex [split [lindex $args end] /] end]
    dict set query DOCUMENT_ROOT   [my cget doc_root]
    dict set query QUERY_STRING    [dict get $uriinfo query]
    dict set query REQUEST_RAW     $args
    dict set query SERVER_PORT     [my port_listening]
    dict set query http REQUEST_URI     [lindex $args 1]
    dict set query http REQUEST_PATH    [dict get $uriinfo path]
    dict set query http REQUEST_VERSION [lindex [split [lindex $args end] /] end]
    dict set query http DOCUMENT_ROOT   [my clay get server/ doc_root]
    dict set query http QUERY_STRING    [dict get $uriinfo query]
    dict set query http REQUEST_RAW     $args
    dict set query http SERVER_PORT     [my port_listening]
    my Headers_Process query
    set reply [my dispatch $query]

    if {[llength $reply]==0} {
      my log BadLocation $uuid $query
      my log BadLocation $uuid $query
      dict set query HTTP_STATUS 404
      dict set query http HTTP_STATUS 404
      dict set query template notfound
      dict set query mixinmap reply ::httpd::content.template
      dict set query mixin reply ::httpd::content.template
    }

    set class ::httpd::reply.memchan
    set pageobj [$class create ::httpd::object::$uuid [self]]
    if {[dict exists $reply mixinmap]} {
      set mixinmap [dict get $reply mixinmap]
    if {[dict exists $reply mixin]} {
      set mixinmap [dict get $reply mixin]
    } else {
      set mixinmap {}
    }
    if {[dict exists $reply mixin]} {
      dict set mixinmap reply [dict get $reply mixin]
    }
    foreach item [dict keys $reply MIXIN_*] {
      set slot [string range $reply 6 end]
      dict set mixinmap [string tolower $slot] [dict get $reply $item]
    }
    $pageobj mixinmap {*}$mixinmap
    if {[dict exists $reply organ]} {
      $pageobj graft {*}[dict get $reply organ]
    $pageobj clay mixinmap {*}$mixinmap
    if {[dict exists $reply delegate]} {
      $pageobj clay delegate {*}[dict get $reply delegate]
    }
    $pageobj dispatch $sock $reply
    set output [$pageobj output]
    catch {$pageobj destroy}
    return $output
  }
}

Changes to modules/httpd/httpd.test.

10
11
12
13
14
15
16

17
18
19
20
21
22
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47







+










-
-
-
+











+







testsNeedTcltest 2

testsNeed TclOO 1

support {
  use cmdline/cmdline.tcl cmdline
  use fileutil/fileutil.tcl fileutil
  use uuid/uuid.tcl uuid
  use sha1/sha1.tcl sha1
  use uri/uri.tcl uri
  use ncgi/ncgi.tcl ncgi

  use dns/ip.tcl ip
  use nettool/nettool.tcl nettool
  use coroutine/coroutine.tcl coroutine

  use dicttool/dicttool.tcl dicttool
  use cron/cron.tcl cron
  use oodialect/oodialect.tcl oo::dialect
  use oometa/oometa.tcl oo::meta
  use tool/tool.tcl tool
  use clay/clay.tcl clay
  use virtchannel_core/core.tcl tcl::chan::core
  use virtchannel_core/events.tcl tcl::chan::events
  use virtchannel_base/memchan.tcl tcl::chan::memchan
}

testing {
  useLocal httpd.tcl httpd
}

# Set to true for debugging and traces
set ::DEBUG 0
set ::clay::debug $::DEBUG

proc DEBUG args {
  if {$::DEBUG} {
    uplevel 1 $args
  }
}

115
116
117
118
119
120
121
122

123
124
125
126
127

128
129
130
131
132
133
134
135



136
137
138
139
140
141
142

143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

158
159
160
161
162
163
164
165
166
167
168
169
170
171
172

173
174
175

176
177
178
179
180
181
182
183
184

185
186
187
188
189
190
191

192
193
194
195

196
197
198
199
200

201
202
203
204
205
206

207
208
209
210
211

212
213
214
215
216

217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232





233
234
235

236
237
238
239
240
241
242
115
116
117
118
119
120
121

122
123
124

125

126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

160
161
162
163
164
165
166
167
168
169
170
171
172
173
174

175
176
177

178
179
180
181
182
183
184
185
186

187
188
189
190
191
192
193

194
195
196
197

198
199
200
201
202

203
204
205
206
207
208

209
210
211
212
213

214
215
216
217
218

219
220
221
222
223
224
225
226
227
228
229
230





231
232
233
234
235
236
237

238
239
240
241
242
243
244
245







-
+


-

-
+








+
+
+







+














-
+














-
+


-
+








-
+






-
+



-
+




-
+





-
+




-
+




-
+











-
-
-
-
-
+
+
+
+
+


-
+







    chan event $sock readable {}
    set [namespace current]::reply($sock) $buffer($sock)
    unset buffer($sock)
  }
}


tool::define ::httpd::server {
clay::define ::httpd::server {
  method log args {}


  method TemplateSearch page {
    set doc_root [my cget doc_root]
    set doc_root [my clay get server/ doc_root]
    if {$doc_root ne {} && [file exists [file join $doc_root $page.tml]]} {
      return [::fileutil::cat [file join $doc_root $page.tml]]
    }
    if {$doc_root ne {} && [file exists [file join $doc_root $page.html]]} {
      return [::fileutil::cat [file join $doc_root $page.html]]
    }
    switch $page {
      redirect {
        return {300 Redirect}
      }
      notfound {
        return {404 Not Found}
      }
      internal_error {
        return {500 Server Internal Error}
      }
    }
  }


  ::DEBUG method debug args {
    puts stderr $args
  }

  ::DEBUG method log args {
    puts stdout $args
  }
}


###
# Modify the reply class to return plain text
###
tool::define ::httpd::reply {
clay::define ::httpd::reply {

  method HttpHeaders_Default {} {
    return {Status {200 OK}
    Content-Type {text/plain}
		Connection close}
  }

  method reset {} {
    my variable reply_body
    my reply replace [my HttpHeaders_Default]
    set reply_body {}
  }

  method error {code {msg {}} {errorInfo {}}} {
    my http_info set HTTP_ERROR $code
    my clay set HTTP_ERROR $code
    my reset
    set errorstring [my http_code_string $code]
    set qheaders [my http_info dump]
    set qheaders [my clay dump]
    dict with qheaders {}
    my reply replace {}
    my reply set Status "$code $errorstring"
    my reply set Content-Type text/plain
    my puts "$code $errorstring"
  }
}

tool::define ::test::content.echo {
clay::define ::test::content.echo {
	method content {} {
		my variable reply_body
		set reply_body [my PostData [my request get CONTENT_LENGTH]]
		#puts [list REPLY BODY WAS $reply_body]
	}
}
tool::define ::test::content.file {
clay::define ::test::content.file {
	superclass ::httpd::content.file
	method content {} {
	  my reset
    set doc_root [my http_info get doc_root]
    set doc_root [my request get DOCUMENT_ROOT]
    my variable reply_file
    set reply_file [file join $doc_root pkgIndex.tcl]
	}
}
tool::define ::test::content.time {
clay::define ::test::content.time {
	method content {} {
		my variable reply_body
		set reply_body [clock seconds]
	}
}
tool::define ::test::content.error {
clay::define ::test::content.error {
	method content {} {
		error {The programmer asked me to die this way}
	}
}
tool::define ::test::content.cgi {
clay::define ::test::content.cgi {
	superclass ::httpd::content.cgi

}

tool::define ::httpd::test::reply {
clay::define ::httpd::test::reply {
	superclass ::httpd::reply ::test::content.echo
}

###
# Build the server
###
set DIR [file dirname [file normalize [info script]]]
set ::DEMOROOT $DIR

::httpd::server create TESTAPP port 10001
TESTAPP plugin dict_dispatch
TESTAPP uri add /     [list mixin ::test::content.echo]
TESTAPP uri add /echo [list mixin ::test::content.echo]
TESTAPP uri add /file [list mixin ::test::content.file doc_root $::DEMOROOT]
TESTAPP uri add /time [list mixin ::test::content.time]
TESTAPP uri add /error [list mixin ::test::content.error]
TESTAPP uri add * /     [list mixin {reply ::test::content.echo}]
TESTAPP uri add * /echo [list mixin {reply ::test::content.echo}]
TESTAPP uri add * /file [list mixin {reply ::test::content.file} doc_root $::DEMOROOT]
TESTAPP uri add * /time [list mixin {reply ::test::content.time}]
TESTAPP uri add * /error [list mixin {replyy ::test::content.error}]

# Catch all
#TESTAPP uri add * [list mixin httpd::content.echo]
#TESTAPP uri add * * [list mixin {reply httpd::content.echo}]

::DEBUG puts httpd-client-0001
test httpd-client-0001 {Do an echo request} {

set reply [::httpd::test::send 10001 {POST /echo HTTP/1.0} {} {THIS IS MY CODE}]
::httpd::test::compare $reply {HTTP/1.0 200 OK
Content-Type: text/plain
306
307
308
309
310
311
312












313
314
315
316
317

318
319
320
321
322

323
324
325
326
327
328

329
330
331
332
333
334





335
336
337
338
339
340
341
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331

332
333
334

335

336
337
338
339
340
341

342
343





344
345
346
347
348
349
350
351
352
353
354
355







+
+
+
+
+
+
+
+
+
+
+
+




-
+


-

-
+





-
+

-
-
-
-
-
+
+
+
+
+








::DEBUG puts httpd-client-0006
test httpd-client-0006 {Return a file} {
set reply [::httpd::test::send 10001 {GET /file HTTP/1.0} {} {}]
::httpd::test::compare $reply $checkreply
} {}

::DEBUG puts httpd-client-0007
test httpd-client-0007 {URL Generates Not Found} {

set reply [::httpd::test::send 10001 {POST /doesnotexist HTTP/1.0} {} {THIS ONE ALONE IS MINE}]

::httpd::test::compare $reply {HTTP/1.0 404 Not Found
Content-Type: text/plain
Connection: close
Content-Length: *

404 Not Found}
} {}

# -------------------------------------------------------------------------
# Test proxies

tool::define ::test::content.proxy {
clay::define ::test::content.proxy {
	superclass ::httpd::content.proxy


  method proxy_channel {} {
    return [::socket localhost [my http_info get proxy_port]]
    return [::socket localhost [my clay get proxy_port]]
  }
}


::httpd::server create TESTPROXY port 10002
TESTAPP   uri add /proxy*     [list mixin ::test::content.proxy proxy_port [TESTPROXY port_listening]]
TESTAPP   uri add * /proxy*     [list mixin {reply ::test::content.proxy} proxy_port [TESTPROXY port_listening]]
TESTPROXY plugin dict_dispatch
TESTPROXY uri add /     [list mixin ::test::content.echo]
TESTPROXY uri add /echo [list mixin ::test::content.echo]
TESTPROXY uri add /file [list mixin ::test::content.file doc_root $::DEMOROOT]
TESTPROXY uri add /time [list mixin ::test::content.time]
TESTPROXY uri add /error [list mixin ::test::content.error]
TESTPROXY uri add * /     [list mixin {reply ::test::content.echo}]
TESTPROXY uri add * /echo [list mixin {reply ::test::content.echo}]
TESTPROXY uri add * /file [list mixin {reply ::test::content.file} doc_root $::DEMOROOT]
TESTPROXY uri add * /time [list mixin {reply ::test::content.time}]
TESTPROXY uri add * /error [list mixin {reply ::test::content.error}]

::DEBUG puts httpd-proxy-0001
test httpd-proxy-0001 {Do an echo request} {

set reply [::httpd::test::send 10001 {POST /proxy/echo HTTP/1.0} {} {THIS IS MY CODE}]
::httpd::test::compare $reply {HTTP/1.0 200 OK
Content-Type: text/plain
405
406
407
408
409
410
411
412
413
414
415
416
417
418

419
420
421
422
423
424
425
419
420
421
422
423
424
425


426
427
428
429

430
431
432
433
434
435
436
437







-
-




-
+








::DEBUG puts httpd-proxy-0006
test httpd-proxy-0006 {Return a file} {
set reply [::httpd::test::send 10001 {GET /proxy/file HTTP/1.0} {} {}]
::httpd::test::compare $reply $checkreply
} {}



# -------------------------------------------------------------------------
# cgi
TESTAPP plugin local_memchan

TESTAPP uri add /cgi-bin* [list mixin ::test::content.cgi path $::DEMOROOT]
TESTAPP uri add * /cgi-bin* [list mixin {reply ::test::content.cgi} path $::DEMOROOT]

set fout [open [file join $DIR test.tcl] w]
puts $fout {#!/usr/bin/tclsh

puts stdout "Status: 200 OK"
if {$::env(CONTENT_LENGTH) > 0} {
  puts stdout "Content-Type: $::env(CONTENT_TYPE)"
543
544
545
546
547
548
549
550
551


552
553
554
555
556
557
558
559
560
561
562
563

564
565
566

567
568

569
570
571
572
573
574
575





576
577
578
579
580
581
582
583
584
585
586
587
588

589
590
591
592
593
594
595
555
556
557
558
559
560
561


562
563
564
565
566
567
568
569
570
571
572
573
574

575
576
577

578
579
580
581
582
583





584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609







-
-
+
+











-
+


-
+


+


-
-
-
-
-
+
+
+
+
+













+







namespace eval ::scgi {
  variable server_block {SCGI 1.0 SERVER_SOFTWARE {TclScgiServer/0.1}}
}

###
# Build the reply class
###
tool::class create ::scgi::test::reply {
  superclass ::httpd::reply.scgi
::clay::define ::scgi::test::reply {
  superclass ::httpd::reply

  method reset {} {
    my variable reply_body
    my reply replace [my HttpHeaders_Default]
    set reply_body {}
  }
}

###
# Build the server
###
tool::class create scgi::test::app {
::clay::define scgi::test::app {
  superclass ::httpd::server.scgi

  property reply_class ::scgi::test::reply
  clay set reply_class ::scgi::test::reply
}

puts [list ::test::content.file [info commands ::test::content.file]]
scgi::test::app create TESTSCGI port 10003
TESTSCGI plugin dict_dispatch
TESTSCGI uri add /     [list mixin ::test::content.echo]
TESTSCGI uri add /echo [list mixin ::test::content.echo]
TESTSCGI uri add /file [list mixin ::test::content.file doc_root $::DEMOROOT]
TESTSCGI uri add /time [list mixin ::test::content.time]
TESTSCGI uri add /error [list mixin ::test::content.error]
TESTSCGI uri add * /     [list mixin {reply ::test::content.echo}]
TESTSCGI uri add * /echo [list mixin {reply ::test::content.echo}]
TESTSCGI uri add * /file [list mixin {reply ::test::content.file} doc_root $::DEMOROOT]
TESTSCGI uri add * /time [list mixin {reply ::test::content.time}]
TESTSCGI uri add * /error [list mixin {reply ::test::content.error}]

::DEBUG puts scgi-client-0001
test scgi-client-0001 {Do an echo request} {

set reply [::scgi::test::send 10003 {REQUEST_METHOD POST REQUEST_URI /echo} {THIS IS MY CODE}]
set checkreply {Status: 200 OK
Content-Type: text/plain
Connection: close
Content-Length: *

THIS IS MY CODE}
::httpd::test::compare $reply $checkreply
} {}


::DEBUG puts scgi-client-0002
test scgi-client-0002 {Do another echo request} {
set reply [::scgi::test::send 10003 {REQUEST_METHOD POST REQUEST_URI /echo} {THOUGH THERE ARE MANY LIKE IT}]
set checkreply {Status: 200 OK
Content-Type: text/plain
Connection: close
654
655
656
657
658
659
660
661

662
663
664
665
666
667
668
669
668
669
670
671
672
673
674

675
676
677
678
679
680
681
682
683







-
+








Content-Length: [string length $checkfile]

$checkfile"
::httpd::test::compare $reply $checkreply
} {}

::DEBUG puts all-tests-finished

file delete [file join $DIR test.tcl]
# -------------------------------------------------------------------------

testsuiteCleanup

# Local variables:
# mode: tcl
# indent-tabs-mode: nil
# End:

Changes to modules/httpd/pkgIndex.tcl.

1
2
3

4
1
2

3
4


-
+


if {![package vsatisfies [package provide Tcl] 8.6]} {return}
package ifneeded httpd 4.2.0 [list source [file join $dir httpd.tcl]]
package ifneeded httpd 4.3 [list source [file join $dir httpd.tcl]]

Changes to modules/ldap/ldap.man.

1
2

3
4
5
6
7
8
9
1

2
3
4
5
6
7
8
9

-
+







[comment {-*- tcl -*- doctools manpage}]
[vset VERSION 1.9.2]
[vset VERSION 1.10]
[manpage_begin ldap n [vset VERSION]]
[keywords {directory access}]
[keywords internet]
[keywords ldap]
[keywords {ldap client}]
[keywords protocol]
[keywords {rfc 2251}]
26
27
28
29
30
31
32

33





























34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50




























































































51

52
53
54
55
56
57
58
59




60
61
62
63











64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

83
84
85
86
87
88





89










90
91
92
93
94
95
96
26
27
28
29
30
31
32
33

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

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




210
211
212
213

214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243







+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+








+
+
+
+




+
+
+
+
+
+
+
+
+
+
+










-
-
-
-




-
+






+
+
+
+
+

+
+
+
+
+
+
+
+
+
+







RFC 4511 ([uri http://www.rfc-editor.org/rfc/rfc4511.txt]).

It works by opening the standard (or secure) LDAP socket on the
server, and then providing a Tcl API to access the LDAP protocol
commands.  All server errors are returned as Tcl errors (thrown) which
must be caught with the Tcl [cmd catch] command.

[section {TLS Security Considerations}]
[include ../common-text/tls-security-notes.inc]

[para] This package uses the [package TLS] package to handle the
security for [const LDAPS] connections.

[para] Policy decisions like the set of protocols to support and what
ciphers to use are not the responsibility of [package TLS], nor of
this package itself however.

Such decisions are the responsibility of whichever application is
using the package, and are likely influenced by the set of servers
the application will talk to as well.

[para] For example, in light of the recent
[uri http://googleonlinesecurity.blogspot.co.uk/2014/10/this-poodle-bites-exploiting-ssl-30.html \
{POODLE attack}] discovered by Google many servers will disable support
for the SSLv3 protocol.

To handle this change the applications using [package TLS] must be
patched, and not this package, nor [package TLS] itself.

Such a patch may be as simple as generally activating [const tls1]
support, as shown in the example below.

[example {
    ldap::tlsoptions -tls1 1 -ssl2 0 -ssl3 0 ;# forcibly activate support for the TLS1 protocol

    ... your own application code ...
}]


[section COMMANDS]

[list_begin definitions]

[call [cmd ::ldap::connect] [arg host] [opt [arg port]]]

Opens a LDAPv3 connection to the specified [arg host], at the given
[arg port], and returns a token for the connection. This token is the
[arg handle] argument for all other commands. If no [arg port] is
specified it will default to [const 389].

[para]

The command blocks until the connection has been established, or
establishment definitely failed.

[call [cmd ::ldap::tlsoptions] [cmd reset]]

This command resets TLS options to default values. It returns the
set of options.
Using this command is incompatible with the obsolete
form of [cmd ::ldap::secure_connect] and [cmd ::ldap_starttls].

[call [cmd ::ldap::tlsoptions] [opt "[arg opt1] [arg val1]"] [opt "[arg opt2] [arg val2]"] ...]

This commands adds one or more options to some value, and may be used
more than one time in order to add options in several steps.  A complete
description of options may be found in the [package tls] package
documentation. Valid options and values are:

[list_begin options]
[opt_def {-cadir} directory  ]

Provide the directory containing the CA certificates.
No default.

[opt_def {-cafile} file]

Provide the CA file.
No default.

[opt_def {-cipher} string]

Provide the cipher suites to use.
No default.

[opt_def {-dhparams} file]

Provide a Diffie-Hellman parameters file.
No default.

[opt_def {-request} boolean]

Request a certificate from peer during SSL handshake.
Default: true.

[opt_def {-require} boolean]

Require a valid certificate from peer during SSL handshake. If this is
set to true then -request must also be set to true.
Default: false

[opt_def {-servername} host]

Only available if the OpenSSL library the TLS package is linked against
supports the TLS hostname extension for 'Server Name Indication'
(SNI). Use to name the logical host we are talking to and expecting a
certificate for.
No default.

[opt_def {-ssl2} bool]

Enable use of SSL v2.
Default: false

[opt_def {-ssl3} bool]

Enable use of SSL v3.
Default: false

[opt_def {-tls1} bool]

Enable use of TLS v1
Default: true

[opt_def {-tls1.1} bool]

Enable use of TLS v1.1
Default: true

[opt_def {-tls1.2} bool]

Enable use of TLS v1.2
Default: true

[list_end]
[para]

This command returns the current set of TLS options and values.
In particular, one may use this command without any arguments to get
the current set of options.

[para]

Using this command is incompatible with the obsolete
form of [cmd ::ldap::secure_connect] and [cmd ::ldap_starttls]
(see below).

[call [cmd ::ldap::secure_connect] [arg host] [opt [arg port]] [opt [arg verify_cert]] [opt [arg sni_servername]]]
[call [cmd ::ldap::secure_connect] [arg host] [opt [arg port]]]

Like [cmd ::ldap::connect], except that the created connection is
secured by SSL. The port defaults to [const 636].  This command
depends on the availability of the package [package TLS], which is a
SSL binding for Tcl. If [package TLS] is not available, then this
command will fail.

[para]

TLS options are specified with [cmd ::ldap::tlsoptions].

[para]

The command blocks until the connection has been established, or
establishment definitely failed.


[call [cmd ::ldap::secure_connect] [arg host] [opt [arg port]] [opt [arg verify_cert]] [opt [arg sni_servername]]]

Note: this form of the command is deprecated, since TLS options had
to be specified with a combination of parameters to this command
([arg verify_cert] and [arg sni_servername]) and arguments to [cmd ::tls::init]
(from package [package tls]) for example to setup defaults for trusted
certificates. Prefer the above form (without the [arg verify_cert] and
[arg sni_servername] parameters) and set TLS options with
[cmd ::ldap::tlsoptions].

[para]

If [arg verify_cert] is set to 1, the default, this checks the server certificate against
the known hosts. If [arg sni_servername] is set, the given hostname is used as the 
hostname for Server Name Indication in the TLS handshake.

[para]

Use [cmd ::tls::init] to setup defaults for trusted certificates.

[example {
    tls::init -cadir /etc/ssl/certs/ca-certificates.crt
}]

[para]

TLS supports different protocol levels. In common use are the versions 1.0, 1.1 and 1.2.
By default all those versions are offered. If you need to modify the acceptable
protocols, you can change the ::ldap::tlsProtocols list.
protocols, you can change the ::ldap::tlsProtocols list (deprecated).

[call [cmd ::ldap::disconnect] [arg handle]]

Closes the ldap connection refered to by the token
[arg handle]. Returns the empty string as its result.

[call [cmd ::ldap::starttls] [arg handle]]

Start TLS negotiation on the connection denoted by [arg handle],
with TLS parameters set with [cmd ::ldap::tlsoptions].

[call [cmd ::ldap::starttls] [arg handle] [opt [arg cafile]] [opt [arg certfile]] [opt [arg keyfile]] [opt [arg verify_cert]] [opt [arg sni_servername]]]

Note: this form of the command is deprecated, since TLS options had
to be specified with a combination of parameters to this command
([arg cafile], [arg certfile], [arg keyfile], [arg verify_cert]
and [arg sni_servername]) and arguments to [cmd ::tls::init]
(from package [package tls]).
Prefer the above form (without specific TLS arguments)
and set TLS options with [cmd ::ldap::tlsoptions].

[para]

Start TLS negotiation on the connection denoted by [arg handle].

You need to set at least the [arg cafile] argument to a file with trusted certificates, if [arg verify_cert] is 1, which is the default.
The [arg sni_servername] can be used to signal a different hostname during the TLS handshake.

The announced protocols are determined in the same way as [cmd ::ldap::secure_connect].
409
410
411
412
413
414
415







416
417
418
419
420
421
422
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576







+
+
+
+
+
+
+







This command returns all currently existing ldap connection handles.

[call [cmd ::ldap::info] [cmd tls] [arg handle] ]

This command returns 1 if the ldap connection [arg handle] used TLS/SSL for
connection via [cmd ldap::secure_connect] or completed [cmd ldap::starttls], 0 otherwise.

[call [cmd ::ldap::info] [cmd tlsstatus] [arg handle] ]

This command returns the current security status of an TLS secured
channel. The result is a list of key-value pairs describing the connected
peer (see the [package TLS] package documentation for the returned values).
If the connection is not secured with TLS, an empty list is returned.

[call [cmd ::ldap::info] [cmd saslmechanisms] [arg handle]]

Return the supported SASL mechanisms advertised by the server. Only valid in a
bound state (anonymous or other).

[call [cmd ::ldap::info] [cmd control] [arg handle] ]

499
500
501
502
503
504
505
506

507
508
509
510
511
512
513
653
654
655
656
657
658
659

660
661
662
663
664
665
666
667







-
+







    ldap::delete $handle $dn

    ldap::unbind     $handle
    ldap::disconnect $handle
}]
[para]

And a another example, a simple query, and processing the
And another example, a simple query, and processing the
results.

[para]
[example {
    package require ldap
    set handle [ldap::connect ldap.acme.com 389]
    ldap::bind $handle

Changes to modules/ldap/ldap.tcl.

40
41
42
43
44
45
46
47

48
49
50
51


52
53
54
55
56
57
58
40
41
42
43
44
45
46

47
48
49
50
51
52
53
54
55
56
57
58
59
60







-
+




+
+







#   written by Jochen Loewer
#   3 June, 1999
#
#-----------------------------------------------------------------------------

package require Tcl 8.4
package require asn 0.7
package provide ldap 1.9.2
package provide ldap 1.10

namespace eval ldap {

    namespace export    connect secure_connect  \
			starttls                \
			tlsoptions              \
                        disconnect              \
                        bind unbind             \
                        bindSASL                \
                        search                  \
                        searchInit           	\
		        searchNext	        \
		        searchEnd		\
113
114
115
116
117
118
119











































120
121
122
123
124
125
126
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







        66  notAllowedOnNonLeaf
        67  notAllowedOnRDN
        68  entryAlreadyExists
        69  objectClassModsProhibited
        80  other
    }

    # TLS options for secure_connect and starttls
    # (see tcltls documentation, function tls::import)
    variable validTLSOptions
    set validTLSOptions {
	-cadir
	-cafile
	-certfile
	-cipher
	-command
	-dhparams
	-keyfile
	-model
	-password
	-request
	-require
	-server
	-servername
	-ssl2
	-ssl3
	-tls1
	-tls1.1
	-tls1.2
    }

    # Default TLS options for secure_connect and starttls
    variable defaultTLSOptions
    array set defaultTLSOptions {
	-request 1
	-require 1
	-ssl2    no
	-ssl3    no
	-tls1	 yes
	-tls1.1	 yes
	-tls1.2	 yes
    }

    variable curTLSOptions
    array set curTLSOptions [array get defaultTLSOptions]

    # are we using the old interface (TLSMode = "compatible") or the
    # new one (TLSMode = "integrated")
    variable TLSMode
    set TLSMode "compatible"
}


#-----------------------------------------------------------------------------
#    Lookup an numerical ldap result code and return a string version
#
#-----------------------------------------------------------------------------
246
247
248
249
250
251
252
























253
254
255
256
257
258
259
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







   upvar #0 [lindex $args 0] conn
   if {![::info exists conn(tls)]} {
   	return -code error \
		"\"[lindex $args 0]\" is not a ldap connection handle"
   }
   return $conn(tls)
}

#-----------------------------------------------------------------------------
#   return the TLS connection status
#
#-----------------------------------------------------------------------------

proc ldap::info_tlsstatus {args} {
   if {[llength $args] != 1} {
   	return -code error \
	       "Wrong # of arguments. Usage: ldap::info tlsstatus handle"
   }
   CheckHandle [lindex $args 0]
   upvar #0 [lindex $args 0] conn
   if {![::info exists conn(tls)]} {
   	return -code error \
		"\"[lindex $args 0]\" is not a ldap connection handle"
   }
   if {$conn(tls)} then {
       set r [::tls::status $conn(sock)]
   } else {
       set r {}
   }
   return $r
}

proc ldap::info_saslmechanisms {args} {
   if {[llength $args] != 1} {
   	return -code error \
	       "Wrong # of arguments. Usage: ldap::info saslmechanisms handle"
   }
   return [Saslmechanisms [lindex $args 0]]
386
387
388
389
390
391
392

























393
394
395
396
397
398

399
400


401
402
403
404
405

406











407
408
409
410
411





412
413
414
415
416
417
418
419
























420
421
422
423
424
425
426
427
428
429
430


431
432

433
434
435


436
437
438
439
440
441
442



443
444
445

446


447
448
449
450
451
452
453
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491

492
493
494
495
496
497
498
499
500

501
502
503
504
505
506
507
508
509
510
511
512
513





514
515
516
517
518
519







520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546


547
548
549
550


551
552


553



554
555







556
557
558



559

560
561
562
563
564
565
566
567
568







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+


+
+




-
+

+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
-




-
-
+
+
-
-
+
-
-
-
+
+
-
-
-
-
-
-
-
+
+
+
-
-
-
+
-
+
+







    set conn(lastError) ""
    set conn(referenceVar) [namespace current]::searchReferences
    set conn(returnReferences) 0

    fileevent $sock readable [list ::ldap::MessageReceiver ::ldap::ldap$sock]
    return ::ldap::ldap$sock
}

#-----------------------------------------------------------------------------
#    tlsoptions
#
#-----------------------------------------------------------------------------
proc ldap::tlsoptions {args} {
    variable curTLSOptions
    variable validTLSOptions
    variable defaultTLSOptions
    variable TLSMode

    if {$args eq "reset"} then {
	array set curTLSOptions [array get defaultTLSOptions]
    } else {
	foreach {opt val} $args {
	    if {$opt in $validTLSOptions} then {
		set curTLSOptions($opt) $val
	    } else {
		return -code error "invalid TLS option '$opt'"
	    }
	}
    }
    set TLSMode "integrated"
    return [array get curTLSOptions]
}

#-----------------------------------------------------------------------------
#    secure_connect
#
#-----------------------------------------------------------------------------
proc ldap::secure_connect { host {port 636} {verify_cert 1} {sni_servername ""}} {
proc ldap::secure_connect { host {port 636} {verify_cert ""} {sni_servername ""}} {

    variable tlsProtocols
    variable curTLSOptions
    variable TLSMode

    package require tls

    #------------------------------------------------------------------
    #   connect via TCP/IP
    #   set options
    #------------------------------------------------------------------

    if {$TLSMode eq "compatible"} then {
	#
	# Compatible with old mode. Build a TLS socket with appropriate
	# parameters, without changing any other parameter which may
	# have been set by a previous call to tls::init (as specified
	# in the ldap.tcl manpage).
	#
	if {$verify_cert eq ""} then {
	    set verify_cert 1
	}
    set cmd [list tls::socket -request 1 -require $verify_cert \
                              -ssl2 no -ssl3 no]
    if {$sni_servername ne ""} {
	lappend cmd -servername $sni_servername
    }
	set cmd [list tls::socket -request 1 -require $verify_cert \
				  -ssl2 no -ssl3 no]
	if {$sni_servername ne ""} {
	    lappend cmd -servername $sni_servername
	}

    # The valid ones depend on the server and openssl version,
    # tls::ciphers all tells it in the error message, but offers no
    # nice introspection.
    foreach {proto active} $tlsProtocols {
	lappend cmd $proto $active
    }
    lappend cmd $host $port
	# The valid ones depend on the server and openssl version,
	# tls::ciphers all tells it in the error message, but offers no
	# nice introspection.
	foreach {proto active} $tlsProtocols {
	    lappend cmd $proto $active
	}

	lappend cmd $host $port
    } else {
	#
	# New, integrated mode. Use only parameters set with
	# ldap::tlsoptions to build the socket.
	#

	if {$verify_cert ne "" || $sni_servername ne ""} then {
	    return -code error "verify_cert/sni_servername: incompatible with the use of tlsoptions"
	}

	set cmd [list tls::socket {*}[array get curTLSOptions] $host $port]
    }

    #------------------------------------------------------------------
    #   connect via TCP/IP
    #------------------------------------------------------------------

    set sock [eval $cmd]

    fconfigure $sock -blocking no -translation binary -buffering full

    #------------------------------------------------------------------
    #   Run the TLS handshake
    #
    #------------------------------------------------------------------
    set retry 0
    while {1} {
    
    # run the handshake in synchronous I/O mode
        if {$retry > 20} {
            close $sock
    fconfigure $sock -blocking yes -translation binary -buffering full
            return -code error "too long retry to setup SSL connection"
        }
        if {[catch { tls::handshake $sock } err]} {

    if {[catch { tls::handshake $sock } err]} {
            if {[string match "*resource temporarily unavailable*" $err]} {
                after 50
                incr retry
            } else {
                close $sock
                return -code error $err
            }
	close $sock
	return -code error $err
    }
        } else {
            break
        }

    }
    # from now on, run in asynchronous I/O mode
    fconfigure $sock -blocking no -translation binary -buffering full

    #--------------------------------------
    #   initialize connection array
    #--------------------------------------
    upvar ::ldap::ldap$sock conn
    catch { unset conn }

469
470
471
472
473
474
475
476





477
478
479
480

























481
482





















483
484
485
486
487
488
489
584
585
586
587
588
589
590

591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624


625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652







-
+
+
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









#------------------------------------------------------------------------------
#    starttls -  negotiate tls on an open ldap connection
#
#------------------------------------------------------------------------------
proc ldap::starttls {handle {cafile ""} {certfile ""} {keyfile ""} \
                     {verify_cert 1} {sni_servername ""}} {
                     {verify_cert ""} {sni_servername ""}} {
    variable tlsProtocols
    variable curTLSOptions
    variable TLSMode

    CheckHandle $handle

    upvar #0 $handle conn

    #------------------------------------------------------------------
    #   set options
    #------------------------------------------------------------------

    if {$TLSMode eq "compatible"} then {
	#
	# Compatible with old mode. Build a TLS socket with appropriate
	# parameters, without changing any other parameter which may
	# have been set by a previous call to tls::init (as specified
	# in the ldap.tcl manpage).
	#
	if {$verify_cert eq ""} then {
	    set verify_cert 1
	}
	set cmd [list tls::import $conn(sock) \
		     -cafile $cafile -certfile $certfile -keyfile $keyfile \
		     -request 1 -server 0 -require $verify_cert \
		     -ssl2 no -ssl3 no ]
	if {$sni_servername ne ""} {
	    lappend cmd -servername $sni_servername
	}

	# The valid ones depend on the server and openssl version,
	# tls::ciphers all tells it in the error message, but offers no
	# nice introspection.
    variable tlsProtocols
    
	foreach {proto active} $tlsProtocols {
	    lappend cmd $proto $active
	}
    } else {
	#
	# New, integrated mode. Use only parameters set with
	# ldap::tlsoptions to build the socket.
	#

	if {$cafile ne "" || $certfile ne "" || $keyfile ne "" ||
		$verify_cert ne "" || $sni_servername ne ""} then {
	    return -code error "cafile/certfile/keyfile/verify_cert/sni_servername: incompatible with the use of tlsoptions"
	}

	set cmd [list tls::import $conn(sock) {*}[array get curTLSOptions]]
    }

    #------------------------------------------------------------------
    #   check handle
    #------------------------------------------------------------------

    if {$conn(tls)} {
        return -code error \
            "Cannot StartTLS on connection, TLS already running"
    }

    if {[ldap::waitingForMessages $handle]} {
        return -code error \
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
694
695
696
697
698
699
700











701
702
703
704
705
706
707







-
-
-
-
-
-
-
-
-
-
-







    if {$oid ne "1.3.6.1.4.1.1466.20037"} {
        set conn(tlsHandshakeInProgress) 0
        return -code error \
            "Unexpected LDAP response"
    }

    # Initiate the TLS socket setup
    set cmd [list tls::import $conn(sock) \
		 -cafile $cafile -certfile $certfile -keyfile $keyfile \
		 -request 1 -server 0 -require $verify_cert -ssl2 no -ssl3 no ]
    
    if {$sni_servername ne ""} {
	lappend cmd -servername $sni_servername
    }

    foreach {proto active} $tlsProtocols {
	lappend cmd $proto $active
    }

    eval $cmd

    set retry 0
    while {1} {
        if {$retry > 20} {
            close $sock

Changes to modules/ldap/ldap.test.

252
253
254
255
256
257
258







259
260
261
262
263
264
265
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272







+
+
+
+
+
+
+








test ldap-28.0 {check wrong num args for ldap::disconnect
} -body {
   ldap::disconnect
} -returnCodes {error} \
  -result [tcltest::wrongNumArgs {ldap::disconnect} \
	{handle} 0 ]

test ldap-29.0 {check invalid TLS option
} -body {
    ldap::tlsoptions -foo bar
} -returnCodes {error} \
    -result {invalid TLS option '-foo'}

# -------------------------------------------------------------------------
# Handling of string representation of filters (RFC 4515):
# -------------------------------------------------------------------------

proc glue args {
    join $args ""
}

Changes to modules/ldap/ldapx.man.

1

2
3
4
5
6
7
8

1
2
3
4
5
6
7
8
-
+







[vset VERSION 1.1]
[vset VERSION 1.2]
[comment {-*- tcl -*- doctools manpage}]
[comment {$Id: ldapx.man,v 1.14 2009/01/29 06:16:19 andreas_kupries Exp $}]
[manpage_begin ldapx n [vset VERSION]]
[keywords {directory access}]
[keywords internet]
[keywords ldap]
[keywords {ldap client}]
404
405
406
407
408
409
410





















411

412
413
414
415
416
417
418
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431

432
433
434
435
436
437
438
439







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+







	modified in nearly all methods). The [method error] method
	may be used to fetch this message.

[list_end]

[subsection {Ldap Options}]

Options are configured on [class ldap] instances using the [cmd configure]
method.

[para]

The first option is used for TLS parameters:

[list_begin options]
    [opt_def -tlsoptions [arg list]]

	Specify the set of TLS options to use when connecting to the
	LDAP server (see the [cmd connect] method). For the list of
	valid options, see the [package LDAP] package documentation.
	[para]
	The default is [const {-request 1 -require 1 -ssl2 no -ssl3 no -tls1 yes -tls1.1 yes -tls1.2 yes}].
	[para]
	Example:
	[para]
[example {$l configure -tlsoptions {-request yes -require yes}}]
[list_end]

A first set of options of the [class ldap] class is used during
A set of options of the [class ldap] class is used during
search operations (methods [method traverse], [method search] and
[method read], see below).

[list_begin options]

    [opt_def -scope [const base]|[const one]|[const sub]]

479
480
481
482
483
484
485

486
487
488
489
490
491
492
493
494
495
496
497

498
499
500
501
502
503
504
505














506
507
508
509
510
511
512
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518

519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548







+











-
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+








	[para]

	Default is {{.*} {}}, meaning: all attributes are converted,
	without exception.

[list_end]


[subsection {Ldap Methods}]

[list_begin definitions]
    [call [arg la] [method error] [opt [arg newmsg]]]

	This method returns the error message that occurred in the
	last call to a [class ldap] class method. If the optional
	argument [arg newmsg] is supplied, it becomes the last
	error message.

    [call [arg la] [method connect] [arg url] [opt [arg binddn]] [opt [arg bindpw]]]
    [call [arg la] [method connect] [arg url] [opt [arg binddn]] [opt [arg bindpw]] [opt [arg starttls]]]

	This method connects to the LDAP server using given URL
	(which can be of the form [uri ldap://host:port] or
	[uri ldaps://host:port]). If an optional [arg binddn]
	argument is given together with the [arg bindpw] argument,
	the [method connect] binds to the LDAP server using the
	specified DN and password.

	[para]

	If the [arg starttls] argument is given a true value ([const 1],
	[const yes], etc.) and the URL uses the [uri ldap://] scheme,
	a TLS negotiation is initiated with the newly created connection,
	before LDAP binding.

	Default value: [const no].

	[para]

	This method returns 1 if connection was successful, or 0 if an
	error occurred (use the [cmd error] method to get the message).

    [call [arg la] [method disconnect]]

	This method disconnects (and unbinds, if necessary) from
	the LDAP server.

    [call [arg la] [method traverse] [arg base] [arg filter] [arg attrs] [arg entry] [arg body]]

558
559
560
561
562
563
564
565

566
567
568

569
570

571
572
573
574
575
576
577
594
595
596
597
598
599
600

601
602
603
604
605
606

607
608
609
610
611
612
613
614







-
+



+

-
+








[subsection {Ldap Example}]

[example {
    package require ldapx

    #
    # Connects to the LDAP directory
    # Connects to the LDAP directory using StartTLS
    #

    ::ldapx::ldap create l
    l configure -tlsoptions {-cadir /etc/ssl/certs -request yes -require yes}
    set url "ldap://server.mycomp.com"
    if {! [l connect $url "cn=admin,o=mycomp" "mypasswd"]} then {
    if {! [l connect $url "cn=admin,o=mycomp" "mypasswd" yes]} then {
	puts stderr "error: [l error]"
	exit 1
    }

    #
    # Search all entries matching some criterion
    #

Changes to modules/ldap/ldapx.tcl.

1
2
3
4

5
6
7
8
9
10
11
12
13
14
15
16

17
18

19
20
21
22
23
24
25
1
2
3

4
5
6
7
8
9
10
11
12
13
14
15

16
17

18
19
20
21
22
23
24
25



-
+











-
+

-
+







#
# Extended object interface to entries in LDAP directories or LDIF files.
#
# (c) 2006-2018 Pierre David (pdav@users.sourceforge.net)
# (c) 2006-2018 Pierre David (pdagog@gmail.com)
#
# $Id: ldapx.tcl,v 1.12 2008/02/07 21:19:39 pdav Exp $
#
# History:
#   2006/08/08 : pda : design
#

package require Tcl 8.4
package require snit		;# tcllib
package require uri 1.1.5	;# tcllib
package require base64		;# tcllib
package require ldap 1.6	;# tcllib, low level code for LDAP directories
package require ldap 1.10	;# tcllib, low level code for LDAP directories

package provide ldapx 1.1
package provide ldapx 1.2

##############################################################################
# LDAPENTRY object type
##############################################################################

snit::type ::ldapx::entry {
    #########################################################################
847
848
849
850
851
852
853


854
855
856
857
858
859
860
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862







+
+








    option -scope        -default "sub"
    option -derefaliases -default "never"
    option -sizelimit	 -default 0
    option -timelimit	 -default 0
    option -attrsonly	 -default 0

    option -tlsoptions  -default {}

    component translator
    delegate option -utf8 to translator

    #
    # Channel descriptor
    #

902
903
904
905
906
907
908
909

910
911
912
913
914
915
916
917




918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933






934
935
936
937
938
939
940
941
942
943
944
945

946
947
948
949
950
951
952
904
905
906
907
908
909
910

911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956

957
958
959
960
961
962
963
964







-
+








+
+
+
+
















+
+
+
+
+
+











-
+







	    set lastError $le
	}
	return $lastError
    }

    # Connect to the LDAP directory, and binds to it if needed

    method connect {url {binddn {}} {bindpw {}}} {
    method connect {url {binddn {}} {bindpw {}} {starttls no}} {

	array set comp [::uri::split $url "ldap"]

	if {! [::info exists comp(host)]} then {
	    $self error "Invalid host in URL '$url'"
	    return 0
	}

	# use ::ldap with integrated TLS mode
	::ldap::tlsoptions reset
	::ldap::tlsoptions {*}$options(-tlsoptions)

	set scheme $comp(scheme)
	if {! [::info exists connect_defaults($scheme)]} then {
	    $self error "Unrecognized URL '$url'"
	    return 0
	}

	set defport [lindex $connect_defaults($scheme) 0]
	set fct     [lindex $connect_defaults($scheme) 1]

	if {[string equal $comp(port) ""]} then {
	    set comp(port) $defport
	}

	if {[Check $selfns {set channel [$fct $comp(host) $comp(port)]}]} then {
	    return 0
	}

	if {$starttls && [string equal $scheme "ldap"]} then {
	    if {[Check $selfns {::ldap::starttls $channel}]} then {
		return 0
	    }
	}

	if {$binddn eq ""} then {
	    set bind 0
	} else {
	    set bind 1
	    if {[Check $selfns {::ldap::bind $channel $binddn $bindpw}]} then {
		return 0
	    }
	}
	return 1
    }

    
    # Disconnect from the LDAP directory

    method disconnect {} {

	Connected $selfns

	if {$bind} {

Changes to modules/log/log.man.

132
133
134
135
136
137
138
139

140
141
142
143
144

145
146
147
148
149
150
151
132
133
134
135
136
137
138

139
140
141
142
143

144
145
146
147
148
149
150
151







-
+




-
+







Compares two levels (including unique abbreviations) with respect to
their priority. This command can be used by the -command option of
lsort. The result is one of -1, 0 or 1 or an error. A result of -1
signals that level1 is of less priority than level2. 0 signals that
both levels have the same priority. 1 signals that level1 has higher
priority than level2.

[call [cmd ::log::lvSuppress] [arg level] "{[arg suppress] 1}"]]
[call [cmd ::log::lvSuppress] [arg level] "{[arg suppress] 1}"]

(Un)suppresses the output of messages having the specified
level. Unique abbreviations for the level are allowed here too.

[call [cmd ::log::lvSuppressLE] [arg level] "{[arg suppress] 1}"]]
[call [cmd ::log::lvSuppressLE] [arg level] "{[arg suppress] 1}"]

(Un)suppresses the output of messages having the specified level or
one of lesser priority. Unique abbreviations for the level are allowed
here too.

[call [cmd ::log::lvIsSuppressed] [arg level]]

Changes to modules/math/ChangeLog.







1
2
3
4
5
6
7
1
2
3
4
5
6
7
8
9
10
11
12
13
+
+
+
+
+
+







2018-08-04  Arjen Markus <[email protected]>
	* statistics.tcl: Source stat_wasserstein.tcl and stat_logit.tcl - for new commands
	* statistics.test: Add corresponding tests
	* statistics.man: Add description of these commands
	* pkgIndex.tcl: Bump the version to 1.3.0

2018-07-22  Arjen Markus <[email protected]>
	* trig.tcl: Implementation of (additional) trigonometric functions - package math::trig
	* trig.test: Trst for the (additional) trigonometric functions
	* trig.man: Documentation for the (additional) trigonometric functions
	* pkgIndex.tcl: Add the new package

2018-07-19  Arjen Markus <[email protected]>

Changes to modules/math/TODO.

1






2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
9
10
11
12
13
14

+
+
+
+
+
+







This file records outstanding actions for the math module

dd. 4 september 2018
- Implement a "typical profile" for timeseries and determining residuals
  (Plus perhaps a notion of outliers)
- Implement detection of extreme values/periods with extreme values


dd. 17 june 2018
- Factor out the backward rotation in the intersection routines for circles
- Add a normalisation routine for vectors
- Add routines to construct a perpendicular vector and line
- Add a routine to return the perpendicular bisector of a line segment
- Add routines to deal with triangles (incircle, circumcircle)

Changes to modules/math/pkgIndex.tcl.

17
18
19
20
21
22
23

24

25
26
27
28
29
30
31
17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32







+
-
+







package ifneeded math::bignum            3.1.1 [list source [file join $dir bignum.tcl]]
package ifneeded math::bigfloat          1.2.2 [list source [file join $dir bigfloat.tcl]]
package ifneeded math::machineparameters 0.1   [list source [file join $dir machineparameters.tcl]]

if {![package vsatisfies [package provide Tcl] 8.5]} {return}
package ifneeded math::calculus          0.8.1 [list source [file join $dir calculus.tcl]]
# statistics depends on linearalgebra (for multi-variate linear regression).
# statistics depends on optimize (for logistic regression).
package ifneeded math::statistics        1.2.0 [list source [file join $dir statistics.tcl]]
package ifneeded math::statistics        1.3.0 [list source [file join $dir statistics.tcl]]
package ifneeded math::linearalgebra     1.1.6 [list source [file join $dir linalg.tcl]]
package ifneeded math::calculus::symdiff 1.0.1 [list source [file join $dir symdiff.tcl]]
package ifneeded math::bigfloat          2.0.2 [list source [file join $dir bigfloat2.tcl]]
package ifneeded math::numtheory         1.1.1 [list source [file join $dir numtheory.tcl]]
package ifneeded math::decimal           1.0.3 [list source [file join $dir decimal.tcl]]
package ifneeded math::geometry          1.3.0 [list source [file join $dir geometry.tcl]]
package ifneeded math::trig              1.0   [list source [file join $dir trig.tcl]]

Added modules/math/stat_logit.tcl.

























































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# stat_logit.tcl --
#     Logistic regression functions - part of the statistics package
#
#     Note:
#     The implementation was derived from the Wikipedia page on logistic regression,
#     (https://en.wikipedia.org/wiki/Logistic_regression) as is the test case.
#
#     TODO:
#     - Deviance to evaluate the goodness of fit
#     - Evaluate the probability
#

package require math::optimize

namespace eval ::math::statistics {
     variable xLogit {}
     variable yLogit {}
}

# logistic-model --
#     Fit 1/0 data to a logistic model
#
# Arguments:
#     xdata        Independent variables (list of lists if there are more than one)
#     ydata        Corresponding scores (0 or 1)
#
# Result:
#     Estimate of the parameters for a logistic model
#
# Note:
#     It is expected that the independent variables have roughly the same scale
#
proc ::math::statistics::logistic-model {xdata ydata} {
    variable xLogit
    variable yLogit

    set xLogit {}
    foreach coords $xdata {
        lappend xLogit [concat 1.0 $coords]
    }
    set yLogit $ydata

    #
    # Use a trivial starting point
    #
    set startx [lrepeat [llength [lindex $xLogit 0]] 0.0]

    set result [::math::optimize::nelderMead LogisticML_NM $startx]

    return [dict get $result x]
}


# LogisticML_NM --
#     Calculate the (log) maximum likelihood for the given logistic model
#     using Nelder-Mead
#
# Arguments:
#     args        Vector of the current regression coefficients
#
# Returns:
#     Log maximum likelihood
#
proc ::math::statistics::LogisticML_NM {args} {
    variable xLogit
    variable yLogit

    set loglike 0.0
    foreach coords $xLogit score $yLogit {
        set sum 0.0

        foreach c $coords v $args {
            set sum [expr {$sum + $v * $c}]
        }
        set exp [expr {exp(-$sum)}]

        if { $score == 1 } {
            set loglike [expr {$loglike - log(1.0 + $exp)}]
        } else {
            set loglike [expr {$loglike - $sum - log(1.0 + $exp)}]
        }
    }
    return [expr {-$loglike}]
}

# logistic-probability --
#     Calculate the probability of a positive score (1) given the model
#
# Arguments:
#     coeffs      Coefficients of the logistic model (for instance outcome of model fit)
#     values      Values of the independent variables
#
# Returns:
#     Probability
#
proc ::math::statistics::logistic-probability {coeffs values} {
    set sum 0.0

    foreach c $coeffs v [concat 1.0 $values] {
        set sum [expr {$sum + $c * $v}]
    }

    return [expr {1.0 / (1.0 + exp(-$sum))}]
}

# test case: from Wikipedia
if {0} {
set xdata {0.50 0.75 1.00 1.25 1.50 1.75 1.75 2.00 2.25 2.50 2.75 3.00 3.25 3.50 4.00 4.25 4.50 4.75 5.00 5.50}
set ydata {0    0    0    0    0    0    1    0    1    0    1    0    1    0    1    1    1    1    1    1   }

set coeffs [::math::statistics::logistic-model $xdata $ydata]

puts "Model fit: $coeffs"

puts "Probabilities:"

foreach x {1 2 3 4 5} {
    puts "$x - [::math::statistics::logistic-probability $coeffs $x]"
}
}

Added modules/math/stat_wasserstein.tcl.























































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# stat-wasserstein.tcl --
#     Determine the Wasserstein distance between two probability distributions
#
#     Note:
#     This is an implementation for one-dimensional distributions (or better:
#     non-negative patterns)
#
#     Note 2:
#     The lower bound of 1.0e-10 is probably not at all necessary
#

# LastNonZero --
#     Auxiliary procedure to find the last non-zero entry
#
# Arguments:
#     prob           Probability distribution
#
# Result:
#     Index in the list of the last non-zero entry
#
# Note:
#     To avoid numerical problems any value smaller than 1.0e-10 is considered to
#     be zero
#
proc ::math::statistics::LastNonZero {prob} {
    set maxidx [expr {[llength $prob] - 1}]

    for {set idx $maxidx} {$idx >= 0} {incr idx -1} {
        if { [lindex $prob $idx] > 1.0e-10 } {
            return $idx
        }
    }

    return -1 ;# No non-zero entry
}

# Normalise --
#     Auxiliary procedure to normalise the probability distribution
#
# Arguments:
#     prob           Probability distribution
#
# Result:
#     Normalised distribution (i.e. the entries sum to 1)
#
# Note:
#     To avoid numerical problems any value smaller than 1.0e-10 is set to zero
#
proc ::math::statistics::Normalise {prob} {

    set newprob {}
    set sum     0.0

    foreach p $prob {
        set sum [expr {$sum + $p}]
    }

    if { $sum == 0.0 } {
        return -code error "Probability distribution should not consist of only zeroes"
    }

    foreach p $prob {
        lappend newprob [expr {$p > 1.0e-10? ($p/$sum) : 0.0}]
    }

    return $newprob
}

# wasserstein-distance --
#     Determine the Wasserstein distance using a "greedy" algorithm.
#
# Arguments:
#     prob1          First probability distribution, interpreted as a histogram
#                    with uniform bin width
#     prob2          Second probability distribution
#
# Result:
#     Distance between the two distributions
#
proc ::math::statistics::wasserstein-distance {prob1 prob2} {
    #
    # First step: make sure the histograms have the same length and the
    # same cumulative weight.
    #
    if { [llength $prob1] != [llength $prob2] } {
        return -code error "Lengths of the probability histograms must be the same"
    }

    set prob1 [Normalise $prob1]
    set prob2 [Normalise $prob2]

    set distance 0.0

    #
    # Determine the last non-zero bin - this bin will be shifted to the second
    # distribution
    #
    while {1} {
        set idx1 [LastNonZero $prob1]
        set idx2 [LastNonZero $prob2]

        if { $idx1 < 0 } {
            break ;# We are done
        }

        set bin1 [lindex $prob1 $idx1]
        set bin2 [lindex $prob2 $idx2]

        if { $bin1 <= $bin2 } {
            lset prob1 $idx1 0.0
            lset prob2 $idx2 [expr {$bin2 - $bin1}]
            set distance [expr {$distance + abs($idx2-$idx1) * $bin1}]
        } else {
            lset prob1 $idx1 [expr {$bin1 - $bin2}]
            lset prob2 $idx2 0.0
            set distance [expr {$distance + abs($idx2-$idx1) * $bin2}]
        }
    }

    return $distance
}

# kl-divergence --
#     Calculate the Kullback-Leibler (KL) divergence for two discrete distributions
#
# Arguments:
#     prob1          First probability distribution - the divergence is calculated
#                    with this one as the basis
#     prob2          Second probability distribution - the divergence of this
#                    distribution wrt the first is calculated
#
# Notes:
#     - The KL divergence is an asymmetric measure
#     - It is actually only defined if prob2 is only zero when prob1 is too
#     - The number of elements in the two distributions must be the same and
#       bins must be the same
#
proc ::math::statistics::kl-divergence {prob1 prob2} {
    if { [llength $prob1] != [llength $prob2] } {
        return -code error "Lengths of the two probability histograms must be the same"
    }

    #
    # Normalise the probability histograms
    #
    set prob1 [Normalise $prob1]
    set prob2 [Normalise $prob2]

    #
    # Check for well-definedness while going along
    #
    set sum 0.0
    foreach p1 $prob1 p2 $prob2 {
        if { $p2 == 0.0 && $p1 != 0.0 } {
            return -code error "Second probability histogram contains unmatched zeroes"
        }

        if { $p1 != 0.0 } {
            set sum [expr {$sum - $p1 * log($p2/$p1)}]
        }
    }

    return $sum
}

if {0} {
# tests --
#

# Almost trivial
set prob1 {0.0 0.0 0.0 1.0}
set prob2 {0.0 0.0 1.0 0.0}

puts "Expected distance: 1"
puts "Calculated: [wasserstein-distance $prob1 $prob2]"
puts "Symmetric:  [wasserstein-distance $prob2 $prob1]"

# Less trivial
set prob1 {0.0 0.75 0.25 0.0}
set prob2 {0.0 0.0  1.0  0.0}

puts "Expected distance: 0.75"
puts "Calculated: [wasserstein-distance $prob1 $prob2]"
puts "Symmetric:  [wasserstein-distance $prob2 $prob1]"

# Shift trivial
set prob1 {0.0 0.1 0.2 0.4 0.2 0.1 0.0 0.0}
set prob2 {0.0 0.0 0.0 0.1 0.2 0.4 0.2 0.1}

puts "Expected distance: 2"
puts "Calculated: [wasserstein-distance $prob1 $prob2]"
puts "Symmetric:  [wasserstein-distance $prob2 $prob1]"


# KL-divergence
set prob1 {0.0 0.1 0.2 0.4 0.2 0.1 0.0 0.0}
set prob2 {0.0 0.1 0.2 0.4 0.2 0.1 0.0 0.0}

puts "KL-divergence for equal distributions: 0"
puts "KL-divergence: [kl-divergence $prob1 $prob2]"

set prob1 {0.1e-8 0.1    0.2 0.4 0.2 0.1 0.0 0.0    0.0}
set prob2 {0.1e-8 0.1e-8 0.1 0.2 0.4 0.2 0.1 0.1e-8 0.1e-8}

puts "KL-divergence for shifted distributions: ??"
puts "KL-divergence: [kl-divergence $prob1 $prob2]"

# Hm, the normalisation proc causes a slight problem with elements of 1.0e-10
set prob1 {0.1e-8 0.1  0.2  0.4 0.2  0.1  0.0     0.0}
set prob2 {0.1e-8 0.11 0.19 0.4 0.24 0.06 0.1e-8  0.1e-8}

puts "KL-divergence slightly dififerent distributions: ??"
puts "KL-divergence: [kl-divergence $prob1 $prob2]"
}

Changes to modules/math/statistics.man.

1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16








-
+







[vset VERSION 1]
[manpage_begin math::statistics n [vset VERSION]]
[keywords {data analysis}]
[keywords mathematics]
[keywords statistics]
[moddesc {Tcl Math Library}]
[titledesc {Basic statistical functions and procedures}]
[category  Mathematics]
[require Tcl 8.4]
[require Tcl 8.5]
[require math::statistics [vset VERSION]]
[description]
[para]

The [package math::statistics] package contains functions and procedures for
basic statistical data analysis, such as:

324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
324
325
326
327
328
329
330

331
332
333
334
335
336
337







-







[arg_def list args] - One or more groups of data to be checked
[list_end]
[para]

[call [cmd ::math::statistics::quantiles] [arg data] [arg confidence]]
Return the quantiles for a given set of data
[list_begin arguments]
[para]
[arg_def list data] - List of raw data values
[para]
[arg_def float confidence] - Confidence level (0.95 or 0.99 for instance) or a list of confidence levels.
[para]
[list_end]
[para]

615
616
617
618
619
620
621
















































622
623
624
625
626
627
628
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







is returned (as a list of "sampleSize" values), otherwise a list of samples is returned.

[list_begin arguments]
[arg_def list data]           List of values to chose from
[arg_def int sampleSize]      Number of values per sample
[arg_def int numberSamples]   Number of samples (default: 1)
[list_end]

[call [cmd ::math::statistics::wasserstein-distance] [arg prob1] [arg prob2]]
Compute the Wasserstein distance or earth mover's distance for two equidstantly spaced histograms
or probability densities. The histograms need not to be normalised to sum to one,
but they must have the same number of entries.
[nl]
Note: the histograms are assumed to be based on the same equidistant intervals.
As the bounds are not passed, the value is expressed in the length of the intervals.

[list_begin arguments]
[arg_def list prob1]          List of values for the first histogram/probability density
[arg_def list prob2]          List of values for the second histogram/probability density
[list_end]

[call [cmd ::math::statistics::kl-divergence] [arg prob1] [arg prob2]]
Compute the Kullback-Leibler (KL) divergence for two equidstantly spaced histograms
or probability densities. The histograms need not to be normalised to sum to one,
but they must have the same number of entries.
[nl]
Note: the histograms are assumed to be based on the same equidistant intervals.
As the bounds are not passed, the value is expressed in the length of the intervals.
[nl]
Note also that the KL divergence is not symmetric and that the second histogram
should not contain zeroes in places where the first histogram has non-zero values.

[list_begin arguments]
[arg_def list prob1]          List of values for the first histogram/probability density
[arg_def list prob2]          List of values for the second histogram/probability density
[list_end]

[call [cmd ::math::statistics::logistic-model] [arg xdata] [arg ydata]]
Estimate the coefficients of the logistic model that fits the data best. The data consist
of independent x-values and the outcome 0 or 1 for each of the x-values. The result
can be used to estimate the probability that a certain x-value gives 1.

[list_begin arguments]
[arg_def list xdata]          List of values for which the success (1) or failure (0) is known
[arg_def list ydata]          List of successes or failures corresponding to each value in [term xdata].
[list_end]

[call [cmd ::math::statistics::logistic-probability] [arg coeffs] [arg x]]
Calculate the probability of success for the value [term x] given the coefficients of the
logistic model.

[list_begin arguments]
[arg_def list coeffs]         List of coefficients as determine by the [cmd logistic-model] command
[arg_def float x]             X-value for which the probability needs to be determined
[list_end]

[list_end]

[section "MULTIVARIATE LINEAR REGRESSION"]

Besides the linear regression with a single independent variable, the
statistics package provides two procedures for doing ordinary
1531
1532
1533
1534
1535
1536
1537
1538

1539
1540
1541
1542
1543
1544
1545
1578
1579
1580
1581
1582
1583
1584

1585
1586
1587
1588
1589
1590
1591
1592







-
+







::math::statistics::plot-scale .plot1  0 100  0 20
::math::statistics::plot-scale .plot2  0 20   0 20
::math::statistics::plot-tline .plot1 $data1
::math::statistics::plot-tline .plot1 $data2
::math::statistics::plot-xydata .plot2 $data1 $data2

puts "Correlation coefficient:"
puts [lb]::math::statistics::corr $data1 $data2]
puts [lb]::math::statistics::corr $data1 $data2[rb]

pause 2
puts "Plot histograms"
.plot2 delete all
::math::statistics::plot-scale .plot2  0 20 0 100
set limits         [lb]::math::statistics::minmax-histogram-limits 7 16[rb]
set histogram_data [lb]::math::statistics::histogram $limits $data1[rb]
1555
1556
1557
1558
1559
1560
1561
1562

1563
1564
1565
1566
1567
1568
1569
1602
1603
1604
1605
1606
1607
1608

1609
1610
1611
1612
1613
1614
1615
1616







-
+







.plot2 itemconfigure d2 -fill red

puts "Second series:"
print-histogram $histogram_data $limits

puts "Autocorrelation function:"
set  autoc [lb]::math::statistics::autocorr $data1[rb]
puts [lb]::math::statistics::map $autoc {[lb]format "%.2f" $x]}[rb]
puts [lb]::math::statistics::map $autoc {[lb]format "%.2f" $x[rb]}[rb]
puts "Cross-correlation function:"
set  crossc [lb]::math::statistics::crosscorr $data1 $data2[rb]
puts [lb]::math::statistics::map $crossc {[lb]format "%.2f" $x[rb]}[rb]

::math::statistics::plot-scale .plot1  0 100 -1  4
::math::statistics::plot-tline .plot1  $autoc "autoc"
::math::statistics::plot-tline .plot1  $crossc "crossc"

Changes to modules/math/statistics.tcl.

17
18
19
20
21
22
23

24
25
26

27
28
29
30
31
32
33
17
18
19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34







+


-
+







# version 0.7:   added Kruskal-Wallis test (by Torsten Berg)
# version 0.8:   added Wilcoxon test and Spearman rank correlation
# version 0.9:   added kernel density estimation
# version 0.9.3: added histogram-alt, corrected test-normal
# version 1.0:   added test-anova-F
# version 1.0.1: correction in pdf-lognormal and cdf-lognormal
# version 1.1:   added test-Tukey-range and test-Dunnett
# version 1.3:   added wasserstein-distance, kl-divergence and logit regression

package require Tcl 8.5 ; # 8.5+ feature in test-anovo-F: **-operator
package provide math::statistics 1.2.0
package provide math::statistics 1.3.0
package require math

if {![llength [info commands ::lrepeat]]} {
    # Forward portability, emulate lrepeat
    proc ::lrepeat {n args} {
	if {$n < 1} {
	    return -code error "must have a count of at least 1"
1790
1791
1792
1793
1794
1795
1796


1797
1798
1799
1800
1801
1802
1803
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806







+
+







source [file join [file dirname [info script]] pdf_stat.tcl]
source [file join [file dirname [info script]] plotstat.tcl]
source [file join [file dirname [info script]] liststat.tcl]
source [file join [file dirname [info script]] mvlinreg.tcl]
source [file join [file dirname [info script]] kruskal.tcl]
source [file join [file dirname [info script]] wilcoxon.tcl]
source [file join [file dirname [info script]] stat_kernel.tcl]
source [file join [file dirname [info script]] stat_wasserstein.tcl]
source [file join [file dirname [info script]] stat_logit.tcl]

#
# Define the tables
#
namespace eval ::math::statistics {
    variable tukey_table_05
    variable tukey_table_01

Changes to modules/math/statistics.test.

14
15
16
17
18
19
20

21
22
23
24
25
26
27
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28







+








testsNeedTcl     8.5;# statistics,linalg!
testsNeedTcltest 2.1

support {
    useLocal math.tcl math
    useLocal linalg.tcl math::linearalgebra
    useLocal optimize.tcl math::optimize
}
testing {
    useLocal statistics.tcl math::statistics
}

# -------------------------------------------------------------------------

1194
1195
1196
1197
1198
1199
1200
1201



















































































































































1202
1203
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


            set result 0
        }
    }

    set result
} -result 1


#
# Tests for Wasserstein distance and KL divergence
#
# Tests for error conditions
#
test "Wasserstein-1.1" "Error: lengths differ" -body {
    set distance [::math::statistics::wasserstein-distance {0 1} {1 0 0 0}]
} -returnCodes 1 -result {Lengths of the probability histograms must be the same}

#
# Check the symmetry for arbitrary histograms
#
test "Wasserstein-1.2" "Test symmetry" -body {
    set okay 1
    for {set i 0} {$i < 10} {incr i} {
        set histogram1 {}
        set histogram2 {}
        for {set j 0} {$j < 3*($i+1)} {incr j} {
            lappend histogram1 [expr {rand()}]
            lappend histogram2 [expr {rand()}]
        }

        set distance1 [::math::statistics::wasserstein-distance $histogram1 $histogram2]
        set distance2 [::math::statistics::wasserstein-distance $histogram2 $histogram1]

        if { abs($distance1-$distance2) > 1.0e-6 } {
            set okay 0
        }
    }

    return $okay

} -result 1

#
# Check the non-negativity for arbitrary histograms
#
test "Wasserstein-1.3" "Test non-negativity" -body {
    set okay 1
    for {set i 0} {$i < 10} {incr i} {
        set histogram1 {}
        set histogram2 {}
        for {set j 0} {$j < 3*($i+1)} {incr j} {
            lappend histogram1 [expr {rand()}]
            lappend histogram2 [expr {rand()}]
        }

        set distance1 [::math::statistics::wasserstein-distance $histogram1 $histogram2]

        if { $distance1 < 0.0 } {
            set okay 0
        }
    }

    return $okay

} -result 1

#
# Check the non-normalised histograms
#
test "Wasserstein-1.4" "Test non-normalised histograms" -match tolerant -body {
    return [list [::math::statistics::wasserstein-distance {2 0 0} {0 0 0.5}]  \
                 [::math::statistics::wasserstein-distance {1 0 0} {0 0 0.25}] ]
} -result {2.0 2.0}

#
# Check arbitrarily extended histograms
#
test "Wasserstein-1.5" "Test extended histograms" -match tolerant -body {
    return [list [::math::statistics::wasserstein-distance {1 0 0}     {0 0 1}]     \
                 [::math::statistics::wasserstein-distance {0 0 1 0 0} {0 0 0 0 1}] \
                 [::math::statistics::wasserstein-distance {1 0 0 0 0} {0 0 1 0 0}] ]
} -result {2.0 2.0 2.0}

#
# Check numerical results
#
test "Wasserstein-1.6" "Test numerical results" -match tolerant -body {
    return [list [::math::statistics::wasserstein-distance {1 0 0}     {0 0.5 0.5}]             \
                 [::math::statistics::wasserstein-distance {1 0 0 0 0} {0 0 0 0.5 0.5}]         \
                 [::math::statistics::wasserstein-distance {1 0 0 0 0} {0 0.25 0.25 0.25 0.25}] ]
} -result {1.5 3.5 2.5}

#
# Tests for error conditions
#
test "KL-divergence-1.1" "Error: lengths differ" -body {
    set distance [::math::statistics::kl-divergence {0 1} {1 0 0 0}]
} -returnCodes 1 -result {Lengths of the two probability histograms must be the same}

test "KL-divergence-1.2" "Error: unmatched zeroes" -body {
    set distance [::math::statistics::kl-divergence {0.3 0.3 0.4} {1 0 0}]
} -returnCodes 1 -result {Second probability histogram contains unmatched zeroes}


test "KL-divergence-1.3" "Matched zeroes should be accepted" -match tolerant -body {
    set distance [::math::statistics::kl-divergence {0.3 0.3 0.4 0} {0.7 0.2 0.1 0}]
} -returnCodes 0 -result 0.42196792

#
# Tests for equal histograms (not all normalised)
#
test "KL-divergence-1.4" "Equal histograms give zero divergence" -body {
    set distance [::math::statistics::kl-divergence {0.3 0.3 0.4} {0.3 0.3 0.4}]
} -result 0.0

test "KL-divergence-1.5" "Non-normalised but equal histograms give zero divergence" -body {
    set distance [::math::statistics::kl-divergence {0.3 0.3 0.4} {0.6 0.6 0.8}]
} -result 0.0

#
# Numerical tests - note: the expected values were taken from the implementation
#                         No independent source found
#
test "KL-divergence-1.6" "Shifted histograms" -match tolerant -body {
    set distance [::math::statistics::kl-divergence {1.0e-8 0.3 0.3 0.3 0.1} {0.3 0.3 0.3 0.1 1.0e-8}]
} -result 1.9413931

test "KL-divergence-1.7" "Arbitrary histograms" -match tolerant -body {
    set distance [::math::statistics::kl-divergence {0.4 0.2 0.3 0.1} {0.1 0.1 0.7 0.1}]
} -result 0.4389578


#
# Tests for logistic regression
#
set xdata {0.50 0.75 1.00 1.25 1.50 1.75 1.75 2.00 2.25 2.50 2.75 3.00 3.25 3.50 4.00 4.25 4.50 4.75 5.00 5.50}
set ydata {0    0    0    0    0    0    1    0    1    0    1    0    1    0    1    1    1    1    1    1   }

test "Logit-regression-1.0" "Logistic regression - coefficients" -match tolerant -body {
    set coeffs [::math::statistics::logistic-model $xdata $ydata]
} -result {-4.07679035286303 1.5045982920571572}

test "Logit-regression-1.1" "Logistic regression - probabilities" -match tolerant -body {
    set coeffs [::math::statistics::logistic-model $xdata $ydata]

    set probabilities {}
    foreach x {1 2 3 4 5} {
        lappend probabilities [::math::statistics::logistic-probability $coeffs $x]
    }

    return $probabilities

} -result {0.07094967663389527 0.2558609520815849 0.6075450376084848 0.8745281239093334 0.9691176473773739}


# End of test cases
testsuiteCleanup

Changes to modules/mime/mime.man.

13
14
15
16
17
18
19
20

21
22
23
24
25
26
27

28
29
30
31
32
33
34
35

36
37
38
39
40

41
42

43
44
45
46
47
48
49
50
51
52
53
54
55
56


57
58

59
60

61
62
63
64
65
66

67
68
69
70
71

72
73
74
75
76

77
78
79
80
81

82
83
84
85
86
87
88
89
90
91

92
93
94
95
96
97

98
99

100
101

102
103
104
105

106
107

108
109
110

111
112

113
114
115
116

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

137
138

139
140
141
142
143
144


145
146

147
148
149
150

151
152

153
154

155
156
157
158

159
160
161
162
163
164
165
166
167
168

169
170

171
172
173
174
175
176
177

178
179

180
181
182

183
184
185
186

187
188
189
190

191
192

193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209

210
211
212
213
214
215
216
217
218
219

220
221
222
223
224
225
226
227


228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244

245
246

247
248

249
250

251
252

253
254
255
256
257
258

259
260
261
262
263
264
265
266
267
268
269
270

271
272

273
274
275
276
277
278

279
280
281
282

283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301

302
303
304
305
306
307
308
13
14
15
16
17
18
19

20
21
22
23
24
25
26

27

28
29
30

31


32





33


34














35
36


37


38



39


40

41



42

43



44


45


46






47



48



49


50


51


52

53


54


55



56


57

58


59




















60


61



62


63
64


65

66


67


68


69

70


71






72



73


74


75




76


77



78

79


80




81


82





83

84



85
86




87

88



89




90

91






92
93
94





95





96
97



98


99


100


101


102

103

104


105

106







107


108


109

110

111


112

113


114

115

116












117
118

119
120
121
122
123
124
125
126







-
+






-
+
-



-

-
-
+
-
-
-
-
-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
-
-
+
-
-
-

-
-
+
-

-
-
-
+
-

-
-
-
+
-
-

-
-
+
-
-
-
-
-
-

-
-
-
+
-
-
-

-
-
+
-
-
+
-
-
+
-

-
-
+
-
-
+
-
-
-
+
-
-
+
-

-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
-
-
-

-
-
+
+
-
-
+
-

-
-
+
-
-
+
-
-
+
-

-
-
+
-
-
-
-
-
-

-
-
-
+
-
-
+
-
-

-
-
-
-
+
-
-
+
-
-
-
+
-

-
-
+
-
-
-
-
+
-
-
+
-
-
-
-
-

-

-
-
-


-
-
-
-
+
-

-
-
-

-
-
-
-
+
-

-
-
-
-
-
-
+
+

-
-
-
-
-

-
-
-
-
-


-
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-

-

-
-
+
-

-
-
-
-
-
-
-

-
-
+
-
-
+
-

-

-
-
+
-

-
-
+
-

-

-
-
-
-
-
-
-
-
-
-
-
-


-
+







[keywords {rfc 822}]
[keywords {rfc 2045}]
[keywords {rfc 2046}]
[keywords {rfc 2049}]
[keywords smtp]
[copyright {1999-2000 Marshall T. Rose}]
[moddesc   {Mime}]
[titledesc {Manipulation of MIME body parts}]
[titledesc {Manipulation of Internet messages}]
[category  {Text processing}]
[require Tcl 8.5]
[require mime [opt 1.6]]
[description]
[para]

The [package mime] library package provides the commands to create and
Provides commands to create and manipulate Internet messages.
manipulate MIME body parts.

[list_begin definitions]

[call [cmd ::mime::initialize] [opt "[option -canonical] [arg type/subtype] [opt "[option -param] \{[arg {key value}]\}..."] [opt "[option -encoding] [arg value]"] [opt "[option -header] \{[arg {key value}]\}..."]"] "([option -file] [arg name] | [option -string] [arg value] | [option -parts] \{[arg token1] ... [arg tokenN]\})"]

This command creates a MIME part and returns a token representing it.

[call [cmd ::mime::initialize] [
[list_begin itemized]

[item]

If the [option -canonical] option is present, then the body is in
    opt "[option -canonical] [arg type/subtype]"] [
canonical (raw) form and is found by consulting either the

    opt "[option -params] [arg dictionary]"] [
[option -file], [option -string], or [option -parts] option.

[para]

In addition, both the [option -param] and [option -header] options may
occur zero or more times to specify [const Content-Type] parameters
(e.g., [const charset]) and header keyword/values (e.g.,

[const Content-Disposition]), respectively.

[para]

Also, [option -encoding], if present, specifies the

    opt "[option -encoding] [arg value]"] [
    opt "[option -headers] [arg dictionary]"] [
[const Content-Transfer-Encoding] when copying the body.

    opt "([option -chan] [arg name] | [option -file] [arg name] | [
[item]

	option -string] [arg value] | [option -parts] [arg parts])"]]
If the [option -canonical] option is not present, then the MIME part
contained in either the [option -file] or the [option -string] option
is parsed, dynamically generating subordinates as appropriate.

[list_end]

Parses a message and returns a token for the message.  One of [
[call [cmd ::mime::finalize] [arg token] [opt "[option -subordinates] [const all] | [const dynamic] | [const none]"]]

This command destroys the MIME part represented by [arg token]. It
returns an empty string.

    option -chan
[para]

If the [option -subordinates] option is present, it specifies which
subordinates should also be destroyed. The default value is

], [
[const dynamic], destroying all subordinates which were created by
[cmd ::mime::initialize] together with the containing body part.

[call [cmd ::mime::getproperty] [arg token] [opt "[arg property] | [option -names]"]]

    option -file
This command returns a string or a list of strings containing the
properties of a MIME part. If the command is invoked with the name of
a specific property, then the corresponding value is returned;
instead, if [option -names] is specified, a list of all properties is
returned; otherwise, a serialized array of properties and values is
returned.

[para]
The possible properties are:

], or [
[list_begin definitions]

[def [const content]]

The type/subtype describing the content

    option -string
[def [const encoding]]

    
The "Content-Transfer-Encoding"

] must be provided as the source of the input.  If [
[def [const params]]

A list of "Content-Type" parameters

    option -canonical
[def [const parts]]

    
A list of tokens for the part's subordinates.  This property is
present only if the MIME part has subordinates.

] is provided the input is the body only and is already formatted according to
[def [const size]]

the provided [
The approximate size of the content (unencoded)

[list_end]

    arg type/subtype
[call [cmd ::mime::getheader] [arg token] [opt "[arg key] | [option -names]"]]

This command returns the header of a MIME part, as a list of strings.

[para]

A header consists of zero or more key/value pairs. Each value is a
list containing one or more strings.

[para]

If this command is invoked with the name of a specific [arg key], then
a list containing the corresponding value(s) is returned; instead, if
-names is specified, a list of all keys is returned; otherwise, a
serialized array of keys and values is returned. Note that when a key
is specified (e.g., "Subject"), the list returned usually contains
exactly one string; however, some keys (e.g., "Received") often occur
more than once in the header, accordingly the list returned usually
contains more than one string.

    
[call [cmd ::mime::setheader] [arg token] [arg {key value}] [opt "[option -mode] [const write] | [const append] | [const delete]"]]

], and therefore should not be parsed.  If [
This command writes, appends to, or deletes the [arg value] associated
with a [arg key] in the header. It returns a list of strings
containing the previous value associated with the key.

[para]

    arg parts
    
The value for [option -mode] is one of:

] is provided it is a list of tokens for messages that comprise a [
[list_begin definitions]

[def [const write]]

    const multipart/mixed
The [arg key]/[arg value] is either created or overwritten (the default).

    
[def [const append]]

] message body.  [
A new [arg value] is appended for the [arg key] (creating it as necessary).

[def [const delete]]

    option -params
All values associated with the key are removed (the [arg value]
parameter is ignored).

[list_end]

[call [cmd ::mime::getbody] [arg token] [opt [option -decode]] [opt "[option -command] [arg callback] [opt "[option -blocksize] [arg octets]"]"]]

This command returns a string containing the body of the leaf MIME
part represented by [arg token] in canonical form.

] is a multidict (a dictionary where the keys may not be unique)
[para]

of parameters for the [
If the [option -command] option is present, then it is repeatedly
invoked with a fragment of the body as this:

[example {
  uplevel #0 $callback [list "data" $fragment]
}]

    const Content-Type
[para]

    
(The [option -blocksize] option, if present, specifies the maximum
size of each fragment passed to the callback.)

] header.  [
[para]

When the end of the body is reached, the callback is invoked as:

    option -headers
[example {
    uplevel #0 $callback "end"
}]

    
[para]

] is a multidict of headers.
Alternatively, if an error occurs, the callback is invoked as:

[example {
    uplevel #0 $callback [list "error" reason]
}]

[para]

Regardless, the return value of the final invocation of the callback
is propagated upwards by [cmd ::mime::getbody].

[para]

If the [option -command] option is absent, then the return value of
[cmd ::mime::getbody] is a string containing the MIME part's entire
body.

[option -encoding] sets the [const Content-Transfer-Encoding].
[para]

If the option [option -decode] is absent the return value computed
above is returned as is. This means that it will be in the charset
specified for the token and not the usual utf-8.

If the option [option -decode] is present however the command will use
the charset information associated with the token to convert the
string from its encoding into utf-8 before returning it.

[call [cmd ::mime::body] [arg token] [opt [option -decode]] [opt "[option -blocksize] [arg octets]"]"]
[call [cmd ::mime::copymessage] [arg token] [arg channel]]

This command copies the MIME represented by [arg token] part to the
specified [arg channel]. The command operates synchronously, and uses
fileevent to allow asynchronous operations to proceed
independently. It returns an empty string.

[call [cmd ::mime::buildmessage] [arg token]]
Returns as a string in canonical form the body of the message corresponding to 
[arg token].

This command returns the MIME part represented by [arg token] as a
string.  It is similar to [cmd ::mime::copymessage], only it returns
the data as a return string instead of writing to a channel.

[call [cmd ::mime::parseaddress] [arg string]]

This command takes a string containing one or more 822-style address
specifications and returns a list of serialized arrays, one element
for each address specified in the argument. If the string contains
more than one address they will be separated by commas.

[para]

Each serialized array contains the properties below. Note that one or
more of these properties may be empty.

If [option -blocksize] is provided, returns a command that itself returns up to
[list_begin definitions]

the next [arg octets] of the message each time it's called, and returns a code
[def [const address]]

of [const break] when finished, deleting itself as well.  If [arg octets] is
local@domain

the empty string, a default value is used.  Pauses the current coroutine as
[def [const comment]]

needed to wait for input.
822-style comment

[def [const domain]]

the domain part (rhs)

[para]
[def [const error]]

non-empty on a parse error

[def [const group]]

this address begins a group

[def [const friendly]]

user-friendly rendering

[option -decode] converts the message body from the character set it is encoded
[def [const local]]

in.
the local part (lhs)

[def [const memberP]]

this address belongs to a group

[call [cmd ::mime::datetime] ([arg time] | [option -now]) [arg property]]
[def [const phrase]]

the phrase part

Returns the [arg property] of [arg time], which 822-style date-time value.
[def [const proper]]

822-style address specification

[def [const route]]

822-style route specification (obsolete)

[list_end]

[call [cmd ::mime::parsedatetime] ([arg string] | [option -now]) [arg property]]

This command takes a string containing an 822-style date-time
specification and returns the specified property as a serialized
array.

[para]

The list of properties and their ranges are:
Available properties and their ranges are:

[list_begin definitions]

[def [const hour]]

0 .. 23

359
360
361
362
363
364
365
366






















































































































































































367
368
369

370
371

372
373
374
375

376
377
378
379
380
381
382
383
384
385
386
387
388

389
390
391
392
393
394
395

396
397
398
399
400
401
402
403
404
405
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368

369
370
371
372
373
374
375

376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+


+



-
+













+







+










1900 ...

[def [const zone]]

-720 .. 720 (minutes east of GMT)

[list_end]


[call [cmd ::mime::finalize] [arg token] [opt "[option -subordinates] [const all] | [const dynamic] | [const none]"]]

Destroys the message corresponding to [arg token] and
returns the empty string.


[para]

[option -subordinates] specifies which messages 
comprising the body should also be destroyed.  The default value is
[const dynamic], which destroys all component messages that were created by
[cmd ::mime::initialize].


[call [cmd ::mime::header] [cmd serialize] [arg value] [arg parameters]]

Serialize a header.


[call [cmd ::mime::header] [cmd get] [arg token] [opt "[arg key] | [option -names]"]]

Returns the header of a message as a multidict
where each value is a list containing the header value and any parameters for
that value.


[para]

If [arg name] is provided returns a list of values for that name, without
regard to case.


[para]

If [option -names] is provided, returns a list of all header names.


[call [cmd ::mime::header] [cmd set] [arg token] [arg {name value}] [ \
    opt "[arg parameters] [opt "[option -mode] [const write] | [\
	const append] | [const delete]"]"]]

If [const append] is provided, creates a new header named [arg name] with the
value of [arg value] is added.

If [const write] is provided, deletes any existing headers whose names match
[arg key] and then creates a new header named [arg key] with the value of
[arg value].

If [const delete] is provided, deletes any existing header having a name that matches
[arg key]. 

[arg parameters] is a dictionary of parameters for the header.

Returns a list of strings containing the previous value associated with the
key.


[para]

The value for [option -mode] is one of:

[list_begin definitions]

[def [const write]]

The [arg key]/[arg value] is either created or overwritten (the default).

[def [const append]]

Appends a new [arg key]/[arg value].

[def [const delete]]

Removes all values associated with the key.  [arg value] is ignored.

[list_end]


[call [cmd ::mime::property] [arg token] [opt "[arg name] | [option -names]"]]

Returns a dictionary of message properties.  If [arg name] is provided, only
the corresponding value is returned.  If [option -names] is provided, a list
of all property names is returned.


[para]
properties:

[list_begin definitions]

[def [const content]]

The type/subtype of the content

[def [const encoding]]

The "Content-Transfer-Encoding"

[def [const params]]

A list of "Content-Type" parameters

[def [const parts]]

A list of tokens for messages that should comprise a multipart body.  Only exists if
there are any such messages.

[def [const size]]

The approximate size of the unencoded content.

[list_end]


[call [cmd ::mime::serialize] [arg token] [opt "[option -chan] [arg channel]"]]

Return the serialization of the message corresponding to [arg token].  If
[option -chan] is provided, write the message to [arg channel] and return the
empty string.  Pauses the current coroutine as needed to wait for input to
become available.


[call [cmd ::mime::parseaddress] [arg addresses]]

Returns a list of describing the comma-separated 822-style [arg addresses].


[para]

Each dictionary contains the following keys, whose values may be the empty
string:

[list_begin definitions]

[def [const address]]

local@domain

[def [const comment]]

822-style comment

[def [const domain]]

the domain part (rhs)

[def [const error]]

non-empty on a parse error

[def [const group]]

this address begins a group

[def [const friendly]]

user-friendly rendering

[def [const local]]

the local part (lhs)

[def [const memberP]]

this address belongs to a group

[def [const phrase]]

the phrase part

[def [const proper]]

822-style address specification

[def [const route]]

822-style route specification (obsolete)

[list_end]


[call [cmd ::mime::mapencoding] [arg encoding_name]]

This commansd maps tcl encodings onto the proper names for their MIME
Maps Tcl encodings onto the proper names for their MIME
charset type.  This is only done for encodings whose charset types
were known.  The remaining encodings return "" for now.


[call [cmd ::mime::reversemapencoding] [arg charset_type]]

This command maps MIME charset types onto tcl encoding names.  Those
Maps MIME charset types onto tcl encoding names.  Those
that are unknown return "".

[list_end]

[section {KNOWN BUGS}]

[list_begin definitions]
[def {Tcllib Bug #447037}]

This problem affects only people which are using Tcl and Mime on a
64-bit system. The currently recommended fix for this problem is to
upgrade to Tcl version 8.4. This version has extended 64 bit support
and the bug does not appear anymore.


[para]

The problem could have been generally solved by requiring the use of
Tcl 8.4 for this package. We decided against this solution as it would
force a large number of unaffected users to upgrade their Tcl
interpreter for no reason.


[para]

See [uri {/tktview?name=447037} {Ticket 447037}] for additional information.

[list_end]

[vset CATEGORY mime]
[include ../doctools2base/include/feedback.inc]
[manpage_end]

Changes to modules/mime/mime.tcl.

1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
21
22
23
24

25








26

27
28
29
30
31
32
33
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33

34
35
36
37
38
39
40
41












-
+










-
+

+
+
+
+
+
+
+
+
-
+







# mime.tcl - MIME body parts
#
# (c) 1999-2000 Marshall T. Rose
# (c) 2000      Brent Welch
# (c) 2000      Sandeep Tamhankar
# (c) 2000      Dan Kuchler
# (c) 2000-2001 Eric Melski
# (c) 2001      Jeff Hobbs
# (c) 2001-2008 Andreas Kupries
# (c) 2002-2003 David Welton
# (c) 2003-2008 Pat Thoyts
# (c) 2005      Benjamin Riefenstahl
# (c) 2013      PoorYorick
# (c) 2013-2018 PoorYorick
#
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# Influenced by Borenstein's/Rose's safe-tcl (circa 1993) and Darren New's
# unpublished package of 1999.
#

# new string features and inline scan are used, requiring 8.3.
package require Tcl 8.5
package require Tcl 8.6

package require tcl::chan::memchan
package require tcl::chan::string
package require coroutine
namespace eval ::mime {
    namespace path ::coroutine::util {*}[namespace path]
}
package require sha256

package provide mime 1.6
package provide mime 1.7

if {[catch {package require Trf 2.0}]} {

    # Fall-back to tcl-based procedures of base64 and quoted-printable encoders
    # Warning!
    # These are a fragile emulations of the more general calling sequence
    # that appears to work with this code here.
62
63
64
65
66
67
68
69
70
71
72
73

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

93
94

95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
70
71
72
73
74
75
76


77
78

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116














117
118
119
120
121
122
123







-
-


-
+



















+


+














-
-
-
-
-
-
-
-
-
-
-
-
-
-







    unset ::major
}

#
# state variables:
#
#     canonicalP: input is in its canonical form
#     content: type/subtype
#     params: seralized array of key/value pairs (keys are lower-case)
#     encoding: transfer encoding
#     version: MIME-version
#     header: serialized array of key/value pairs (keys are lower-case)
#     header: dicttionary (keys are lower-case)
#     lowerL: list of header keys, lower-case
#     mixedL: list of header keys, mixed-case
#     value: either "file", "parts", or "string"
#
#     file: input file
#     fd: cached file-descriptor, typically for root
#     root: token for top-level part, for (distant) subordinates
#     offset: number of octets from beginning of file/string
#     count: length in octets of (encoded) content
#
#     parts: list of bodies (tokens)
#
#     string: input string
#
#     cid: last child-id assigned
#


namespace eval ::mime {

    variable mime
    array set mime {uid 0 cid 0}


    # RFC 822 lexemes
    variable addrtokenL
    lappend addrtokenL \; , < > : . ( ) @ \" \[ ] \\
    variable addrlexemeL {
        LX_SEMICOLON LX_COMMA
        LX_LBRACKET  LX_RBRACKET
        LX_COLON     LX_DOT
        LX_LPAREN    LX_RPAREN
        LX_ATSIGN    LX_QUOTE
        LX_LSQUARE   LX_RSQUARE
        LX_QUOTE
    }

    # RFC 2045 lexemes
    variable typetokenL
    lappend typetokenL \; , < > : ? ( ) @ \" \[ \] = / \\
    variable typelexemeL {
        LX_SEMICOLON LX_COMMA
        LX_LBRACKET  LX_RBRACKET
        LX_COLON     LX_QUESTION
        LX_LPAREN    LX_RPAREN
        LX_ATSIGN    LX_QUOTE
        LX_LSQUARE   LX_RSQUARE
        LX_EQUALS    LX_SOLIDUS
        LX_QUOTE
    }

    variable encList {
        ascii US-ASCII
        big5 Big5
        cp1250 Windows-1250
        cp1251 Windows-1251
        cp1252 Windows-1252
        cp1253 Windows-1253
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340










































341


















342
343
344
345
346
347
348

349

350

351
352
353
354
355
356
357
358
359
360










361
362
363
364
365
366
367
368
369
370
371
372
373
374

375

376
377
378
379
380
381

382
383
384
385
386
387
388

389
390

391
392
393
394
395
396
397
398
399
400
401
402
403
404
405

406
407
408
409
410
411
412

413
414
415


416

417
418
419
420



421
422
423
424
425
426
427
428
429
430

431

432

433
434









435
436
437







438

439
440
441
442




443
444

445
446
447



448
449



450
451
452
453
454
455
456

457
458
459

460
461

462
463
464
465


466
467
468
469
470
471




472
473
474
475
476
477
478

479
480
481
482
483
484
485
486
487

488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522

523
524
525
526
527

528
529
530
531

532
533
534
535
536

537
538


539
540
















































541
542
543
544
545
546

547
548
549
550
551
552
553
554

555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581

582
583
584
585
586
587
588
589
590
591


592
593
594
595
596
597
598
599

600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639

640
641
642
643

644
645
646
647
648
649
650


651
652
653
654
655
656
657
658
659
660
661

662
663
664
665
666
667
668
669
670
671

672
673

674
675
676
677
678

679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703

704
705
706

707
708
709

710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752

753
754
755
756
757

758
759
760
761
762
763
764
765
766
767
768

769
770
771
772
773
774
775
776
777
778
779
780
781
782

783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802

803
804
805


806
807
808


809
810
811
812
813

814
815
816
817
818
819
820
317
318
319
320
321
322
323











324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390

391
392
393

394
395
396
397
398
399
400




401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421


422
423

424


425

426

427







428


429















430





431

432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455

456
457
458

459
460
461
462
463
464
465
466
467
468
469
470



471
472
473
474
475
476
477
478
479




480
481
482
483
484

485



486
487
488
489

490
491
492
493
494
495
496
497
498
499
500
501
502

503


504




505
506






507
508
509
510
511






512


513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533

534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553

554
555
556
557
558

559
560
561
562

563
564
565
566
567

568
569
570
571
572


573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625

626
627
628
629
630
631
632
633

634
635
636
637
638
639
640
641
642
643
644
645
646









647

648
649
650

651
652
653
654
655
656





657
658
659
660






661







662


















663
664
665
666
667
668
669
670
671
672
673
674
675

676
677

678

679







680
681











682










683


684





685

























686



687



688











































689





690
691
692
693
694
695
696
697
698
699
700

701
702
703
704
705
706
707
708
709
710
711
712
713
714

715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734

735



736
737



738
739



740

741
742
743
744
745
746
747
748







-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
+

+
-
+






-
-
-
-
+
+
+
+
+
+
+
+
+
+











-
-

+
-
+
-
-

-

-
+
-
-
-
-
-
-
-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-

-
+



+
+

+




+
+
+









-
+

+
-
+


+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+

+
-
-
-
-
+
+
+
+

-
+
-
-
-
+
+
+

-
+
+
+







+


-
+
-
-
+
-
-
-
-
+
+
-
-
-
-
-
-
+
+
+
+

-
-
-
-
-
-
+
-
-







+













-




















-
+




-
+



-
+




-
+


+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+







-
+












-
-
-
-
-
-
-
-
-

-



-
+





-
-
-
-
-
+
+


-
-
-
-
-
-
+
-
-
-
-
-
-
-

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-













-
+

-

-
+
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
+
-
-
+
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+










-
+













-
+



















-
+
-
-
-
+
+
-
-
-
+
+
-
-
-

-
+







        ksc5601 KS_C_5601-1989
        ksc5601 KSC5601
        ksc5601 korean
        shiftjis MS_Kanji
        utf-8 UTF8
    }

    namespace export initialize finalize getproperty \
                     getheader setheader \
                     getbody \
                     copymessage \
                     mapencoding \
                     reversemapencoding \
                     parseaddress \
                     parsedatetime \
                     uniqueID
}

    namespace export {*}{
	datetime finalize getbody header initialize mapencoding parseaddress
	property reversemapencoding serialize setheader uniqueID
    }
}


proc ::mime::addchan {token fd} {
    variable channels
    upvar 0 $token state
    if {[info exists state(fd)]} {
	error [list {a channel is already present}]
    }
    set state(fd) $fd
    incr channels($fd)
    return
}


proc ::mime::contentid {} {
    set unique [uniqueID]
    return $unique@|
}


proc ::mime::dropchan token {
    variable channels
    upvar 0 $token state
    upvar 0 state(fd) fd

    if {[info exists fd]} {
	if {[incr channels($fd) -1] == 0} {
	    unset channels($fd)
	    if {$state(closechan)} {
		close $fd
	    }
	}
	unset state(fd)
    }
}


# ::mime::initialize --
#
#    the public interface for initializeaux

proc ::mime::initialize args {
    variable mime

    set token [namespace current]::[incr mime(uid)]
    # FRINK: nocheck
    upvar 0 $token state

    if {[catch [list mime::initializeaux $token {*}$args] result eopts]} {
        catch {mime::finalize $token -subordinates dynamic}
        return -options $eopts $result
    }
    return $token
}

# ::mime::initializeaux --
#
#    Creates a MIME part, and returnes the MIME token for that part.
#
# Arguments:
#    args   Args can be any one of the following:
#                  ?-canonical type/subtype
#                  ?-param    {key value}?...
#                  ?-params    {?key value? ...}
#                  ?-encoding value?
#                  ?-headers   {?key value? ...}
#                  ?-header   {key value}?... ?
#                  ?-http
#                  (-file name | -string value | -parts {token1 ... tokenN})
#
#       If the -canonical option is present, then the body is in
#       canonical (raw) form and is found by consulting either the -file,
#       -string, or -parts option.
#
#       In addition, both the -param and -header options may occur zero
#       or more times to specify "Content-Type" parameters (e.g.,
#       "charset") and header keyword/values (e.g.,
#       "Content-Disposition"), respectively.
#       -header
#           a dictionary of headers
#               with possibliy-redundant keys
#       -http
#           operate in http mode
#
#       -params
#           a dictionary of parameters
#           with possibly-redundant keys
#
#
#       Also, -encoding, if present, specifies the
#       "Content-Transfer-Encoding" when copying the body.
#
#       If the -canonical option is not present, then the MIME part
#       contained in either the -file or the -string option is parsed,
#       dynamically generating subordinates as appropriate.
#
# Results:
#    An initialized mime token.

proc ::mime::initialize args {
    global errorCode errorInfo

proc ::mime::initializeaux {token args} {
    variable mime
    variable channels

    set token [namespace current]::[incr mime(uid)]
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    upvar 0 state(canonicalP) canonicalP
    if {[catch {{*}[list mime::initializeaux $token {*}$args]} result eopts]} {
        catch {mime::finalize $token -subordinates dynamic}
        return -options $eopts $result
    }
    return $token
}

    upvar 0 state(params) params
# ::mime::initializeaux --
#

#    Configures the MIME token created in mime::initialize based on
#       the arguments that mime::initialize supports.
#
# Arguments:
#       token  The MIME token to configure.
#    args   Args can be any one of the following:
#                  ?-canonical type/subtype
#                  ?-param    {key value}?...
#                  ?-encoding value?
#                  ?-header   {key value}?... ?
#                  (-file name | -string value | -parts {token1 ... tokenN})
#
# Results:
#       Either configures the mime token, or throws an error.

    set params {}
proc ::mime::initializeaux {token args} {
    global errorCode errorInfo
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    array set params [set state(params) {}]
    set state(hparams) {}
    set state(encoding) {}
    set state(version) 1.0

    set state(bodyparsed) 0
    set canonicalP 0
    set state(header) {}
    set state(headerparsed) 0
    set state(lowerL) {}
    set state(mixedL) {}

    set state(cid) 0
    set state(closechan) 1

    set userparams 0

    set argc [llength $args]
    for {set argx 0} {$argx < $argc} {incr argx} {
        set option [lindex $args $argx]
        if {[incr argx] >= $argc} {
            error "missing argument to $option"
        }
        set value [lindex $args $argx]

        switch -- $option {
        switch $option {
            -canonical {
		set canonicalP 1
                set state(content) [string tolower $value]
		set type [string tolower $value]
            }

	    -chan {
		set state(file) {}
		addchan $token $value
	    }

	    -close {
		set state(closechan) [expr {!!$value}]
	    }

            -param {
                if {[llength $value] != 2} {
                    error "-param expects a key and a value, not $value"
            -params {
		if {$userparams} {
		    error [list {-params can only be provided once}]
		}
		set userparams 1
                if {[llength $value] % 2} {
		    error [list -params expects a dictionary]
                }
		foreach {mixed pvalue} $value {
                set lower [string tolower [set mixed [lindex $value 0]]]
                if {[info exists params($lower)]} {
                    error "the $mixed parameter may be specified at most once"
                }
		    set lower [string tolower $mixed]
		    if {[dict exists params $lower]} {
			error "the $mixed parameter may be specified at most once"
		    }

                set params($lower) [lindex $value 1]
		    dict set params $lower $pvalue
                set state(params) [array get params]
            }

		}
            }

            -encoding {
                switch -- [set state(encoding) [string tolower $value]] {
		set value [string tolower $value[set value {}]]

                switch $value {
                    7bit - 8bit - binary - quoted-printable - base64 {
                    }

                    default {
                        error "unknown value for -encoding $state(encoding)"
                    }
                }
                set state(encoding) [string tolower $value]
            }

            -header {
            -headers {
                if {[llength $value] != 2} {
                    error "-header expects a key and a value, not $value"
		# process headers later in order to assure that content-id and
                }
                set lower [string tolower [set mixed [lindex $value 0]]]
                if {$lower eq "content-type"} {
                    error "use -canonical instead of -header $value"
		# content-type occur first
		if {[info exists headers]} {
                }
                if {$lower eq "content-transfer-encoding"} {
                    error "use -encoding instead of -header $value"
                }
                if {$lower in {content-md5 mime-version}} {
                    error "don't go there..."
		    error [list {-headers option occurred more than once}]
		}
                if {[llength $value] % 2} {
                    error [list -headers expects a dictionary]
                }
                if {$lower ni $state(lowerL)} {
                    lappend state(lowerL) $lower
                    lappend state(mixedL) $mixed
                }

                array set header $state(header)
		set headers $value
                lappend header($lower) [lindex $value 1]
                set state(header) [array get header]
            }

            -file {
                set state(file) $value
            }

            -parts {
		set canonicalP 1
                set state(parts) $value
            }

            -string {
                set state(string) $value

                set state(lines) [split $value \n]
                set state(lines.count) [llength $state(lines)]
                set state(lines.current) 0
            }

            -root {
                # the following are internal options

                set state(root) $value
            }

            -offset {
                set state(offset) $value
            }

            -count {
                set state(count) $value
            }

            -lineslist {
                set state(lines) $value
                set state(lines.count) [llength $state(lines)]
                set state(lines.current) 0
                #state(string) is needed, but will be built when required
                set state(string) {}
            }

            default {
                error "unknown option $option"
                error [list {unknown option} $option]
            }
        }
    }

    #We only want one of -file, -parts or -string:
    #We only want one of -chan -file, -parts or -string:
    set valueN 0
    foreach value {file parts string} {
        if {[info exists state($value)]} {
            set state(value) $value
	    set state(value) $value
            incr valueN
        }
    }
    if {$valueN != 1 && ![info exists state(lines)]} {
        error "specify exactly one of -file, -parts, or -string"
        error [list {specify exactly one of} {-file -parts -string}]
    }


    if {$state(value) eq {file}} {
    if {[set state(canonicalP) [info exists state(content)]]} {
        switch -- $state(value) {
	if {[info exists state(root)]} {
	    # FRINK: nocheck
	    upvar 0 $state(root) root
	    addchan $token $root(fd)
	} else {
	    set state(root) $token
	    if {![info exists state(fd)]} {
		addchan $token [open $state(file) RDONLY]
	    }
	    set state(offset) 0
	    seek $state(fd) 0 end
	    set state(count) [tell $state(fd)]

	    fconfigure $state(fd) -translation binary
	}
    }


    if {$canonicalP} {
        if {![header exists $token content-id]} {
	    header::setinternal $token Content-ID [contentid]
        }

	if {![info exists type]} {
	    set type multipart/mixed
	}

	header setinternal $token Content-Type $type $params

	if {[info exists headers]} {
	    foreach {name hvalue} $headers {
		set lname [string tolower $name]
		if {$lname eq {content-type}} {
		    error [list {use -canonical instead of -headers} $hkey $name]
		}
		if {$lname eq {content-transfer-encoding}} {
		    error [list {use -encoding instead of -headers} $hkey $name]
		}
		if {$lname in {content-md5 mime-version}} {
		    error [list {don't go there...}]
		}
		header::setinternal $token $name $hvalue
	    }
	}

	lassign [header get $token content-type] content

        switch $state(value) {
            file {
                set state(offset) 0
            }

            parts {
                switch -glob -- $state(content) {
                switch -glob $content {
                    text/*
                        -
                    image/*
                        -
                    audio/*
                        -
                    video/* {
                        error "-canonical $state(content) and -parts do not mix"
                        error "-canonical $content and -parts do not mix"
                    }

                    default {
                        if {$state(encoding) ne {}} {
                            error "-encoding and -parts do not mix"
                        }
                    }
                }
            }
            default {# Go ahead}
        }

        if {[lsearch -exact $state(lowerL) content-id] < 0} {
            lappend state(lowerL) content-id
            lappend state(mixedL) Content-ID

            array set header $state(header)
            lappend header(content-id) [uniqueID]
            set state(header) [array get header]
        }

        set state(version) 1.0

        return
    }

    if {$state(params) ne {}} {
    if {[dict size $params]} {
        error "-param requires -canonical"
    }
    if {$state(encoding) ne {}} {
        error "-encoding requires -canonical"
    }
    if {$state(header) ne {}} {
        error "-header requires -canonical"
    }
    if {[info exists state(parts)]} {
        error "-parts requires -canonical"
    if {[info exists headers]} {
        error "-header requires -canonical"
    }

    if {[set fileP [info exists state(file)]]} {
        if {[set openP [info exists state(root)]]} {
            # FRINK: nocheck
            variable $state(root)
            upvar 0 $state(root) root

}
            set state(fd) $root(fd)
        } else {
            set state(root) $token
            set state(fd) [open $state(file) RDONLY]
            set state(offset) 0
            seek $state(fd) 0 end
            set state(count) [tell $state(fd)]

            fconfigure $state(fd) -translation binary
        }
    }

    set code [catch {mime::parsepart $token} result]
    set ecode $errorCode
    set einfo $errorInfo

    if {$fileP} {
        if {!$openP} {
            unset state(root)
            catch {close $state(fd)}
        }
        unset state(fd)
    }

    return -code $code -errorinfo $einfo -errorcode $ecode $result
}

# ::mime::parsepart --
#
#       Parses the MIME headers and attempts to break up the message
#       into its various parts, creating a MIME token for each part.
#
# Arguments:
#       token  The MIME token to parse.
#
# Results:
#       Throws an error if it has problems parsing the MIME token,
#       otherwise it just sets up the appropriate variables.

proc ::mime::parsepart {token} {
proc ::mime::parsepartaux token {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    upvar 0 state(last) last
    if {[set fileP [info exists state(file)]]} {
        seek $state(fd) [set pos $state(offset)] start
        set last [expr {$state(offset) + $state(count) - 1}]
    } else {
        set string $state(string)
    }


    header parse $token
    set vline {}
    while 1 {
        set blankP 0
        if {$fileP} {
            if {($pos > $last) || ([set x [gets $state(fd) line]] <= 0)} {
                set blankP 1
            } else {
                incr pos [expr {$x + 1}]
            }
        } else {

    if {![header exists $token content-type]} {
        if {$state(lines.current) >= $state(lines.count)} {
            set blankP 1
            set line {}
        } else {
            set line [lindex $state(lines) $state(lines.current)]
            incr state(lines.current)
            set x [string length $line]
            if {$x == 0} {set blankP 1}
        }

	header setinternal $token Content-Type text/plain [
        }

	    dict create charset us-ascii]
         if {(!$blankP) && ([string last \r $line] == {$x - 1})} {
             set line [string range $line 0 [expr {$x - 2}]]
             if {$x == 1} {
                 set blankP 1
             }
    }
         }

        if {(!$blankP) && (([
            string first { } $line] == 0) || ([
            string first \t $line] == 0))} {
            append vline \n $line
            continue
        }

        if {$vline eq {}} {
            if {$blankP} {
                break
            }

            set vline $line
            continue
        }

        if {([set x [string first : $vline]] <= 0) \
                || ([set mixed [ string trimright [
                    string range $vline 0 [expr {$x - 1}]]
                ]] eq {})
            } {
            error "improper line in header: $vline"
        }

        set value [string trim [string range $vline [expr {$x + 1}] end]]
        switch -- [set lower [string tolower $mixed]] {
            content-type {
    lassign [header get $token content-type] content params
                if {[info exists state(content)]} {
                    error "multiple Content-Type fields starting with $vline"
                }


                if {![catch {set x [parsetype $token $value]}]} {
                    set state(content) [lindex $x 0]
                    set state(params) [lindex $x 1]
                }
            }

            content-md5 {
            }

            content-transfer-encoding {
                if {($state(encoding) ne {}) \
                        && ($state(encoding) ne [
                            string tolower $value])} {
                    error "multiple Content-Transfer-Encoding fields starting with $vline"
                }

                set state(encoding) [string tolower $value]
            }

            mime-version {
                set state(version) $value
            }

            default {
                if {[lsearch -exact $state(lowerL) $lower] < 0} {
                    lappend state(lowerL) $lower
                    lappend state(mixedL) $mixed
                }

                array set header $state(header)
                lappend header($lower) $value
                set state(header) [array get header]
            }
        }

        if {$blankP} {
            break
        }
        set vline $line
    }

    if {![info exists state(content)]} {
    set fileP [info exists state(file)]
        set state(content) text/plain
        set state(params) [list charset us-ascii]
    }

    if {![string match multipart/* $state(content)]} {
    if {![string match multipart/* $content]} {
        if {$fileP} {
            set x [tell $state(fd)]
            incr state(count) [expr {$state(offset) - $x}]
            set state(offset) $x
        } else {
            # rebuild string, this is cheap and needed by other functions
            set state(string) [join [
                lrange $state(lines) $state(lines.current) end] \n]
        }

        if {[string match message/* $state(content)]} {
        if {[string match message/* $content]} {
            # FRINK: nocheck
            variable [set child $token-[incr state(cid)]]

            set state(value) parts
            set state(parts) $child
            if {$fileP} {
                mime::initializeaux $child \
                    -file $state(file) -root $state(root) \
                    -offset $state(offset) -count $state(count)
            } else {
                if {[info exists state(encoding)]} {
                    set strng [join [
                        lrange $state(lines) $state(lines.current) end] \n]
                    switch -- $state(encoding) {
                    switch $state(encoding) {
                        base64 -
                        quoted-printable {
                            set strng [$state(encoding) -mode decode -- $strng]
                        }
                        default {}
                    }
                    mime::initializeaux $child -string $strng
                } else {
                    mime::initializeaux $child -lineslist [
                        lrange $state(lines) $state(lines.current) end]
                }
            }
        }

        return
    }

    set state(value) parts

    set boundary {}
    dict update params boundary boundary {}
    foreach {k v} $state(params) {
        if {$k eq "boundary"} {
            set boundary $v
    if {![info exists boundary]} {
        error "boundary parameter is missing in $content"
            break
        }
    }
    }

    if {$boundary eq {}} {
        error "boundary parameter is missing in $state(content)"
    }
    if {[string trim $boundary] eq {}} {
        error "boundary parameter is empty in $state(content)"
        error "boundary parameter is empty in $content"
    }

    if {$fileP} {
        set pos [tell $state(fd)]
            # This variable is like 'start', for the reasons laid out
            # below, in the other branch of this conditional.
            set initialpos $pos
844
845
846
847
848
849
850
851

852
853
854
855
856
857

858
859
860
861
862
863
864
772
773
774
775
776
777
778

779
780
781
782
783
784

785
786
787
788
789
790
791
792







-
+





-
+







                # application/octet-stream regardless of header
                # information.
                set line "--$boundary--"
                set x [string length $line]
                set forceoctet 1
            } else {
                if {[set x [gets $state(fd) line]] < 0} {
                    error "end-of-file encountered while parsing $state(content)"
                    error "end-of-file encountered while parsing $content"
                }
            }
            incr pos [expr {$x + 1}]
        } else {
            if {$state(lines.current) >= $state(lines.count)} {
                error "end-of-string encountered while parsing $state(content)"
                error "end-of-string encountered while parsing $content"
            } else {
                set line [lindex $state(lines) $state(lines.current)]
                incr state(lines.current)
                set x [string length $line]
            }
            set x [string length $line]
        }
921
922
923
924
925
926
927
928
929



930
931
932
933
934
935
936
849
850
851
852
853
854
855


856
857
858
859
860
861
862
863
864
865







-
-
+
+
+







            }
        }

        # Looking for the end of the current part. We accept both a
        # terminating boundary and the starting boundary of the next
        # part as the end of the current part.

        if {[set moreP [string compare $line --$boundary--]] \
                && $line ne "--$boundary"} {
        if {[set moreP [string compare $line --$boundary--]]
	    && $line ne "--$boundary"} {

            # The current part has not ended, so we record the line
            # if we are inside a part and doing string parsing.
            if {$inP && !$fileP} {
                lappend start $line
            }
            continue
        }
946
947
948
949
950
951
952
953
954
955
956
957

958
959
960

961

962
963
964

965
966
967
968
969
970

971

972
973
974
975
976

977
978
979
980
981
982
983

984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153

1154
1155
1156
1157
1158
1159
1160
875
876
877
878
879
880
881

882
883
884
885
886
887
888
889
890

891
892
893
894
895
896
897
898
899
900
901
902

903
904
905
906
907
908
909
910
911
912
913



914
915
916
917
918
919











































































































































920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938

939

940
941
942

943
944
945
946
947
948
949
950







-




+



+
-
+



+






+
-
+





+




-
-
-
+





-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-



















-

-



-
+








        set nochild 0
        if {$fileP} {
            if {[set count [expr {$pos - ($start + $x + $crlf + 1)}]] < 0} {
                set count 0
            }
            if {$forceoctet} {
                set ::errorInfo {}
                if {[catch {
                    mime::initializeaux $child \
                        -file $state(file) -root $state(root) \
                        -offset $start -count $count
		    parsepart $child
                }]} {
                    set nochild 1
                    set state(parts) [lrange $state(parts) 0 end-1]
                }
                } } else {
	    } else {
                mime::initializeaux $child \
                    -file $state(file) -root $state(root) \
                    -offset $start -count $count
		parsepart $child
            }
            seek $state(fd) [set start $pos] start
        } else {
            if {$forceoctet} {
                if {[catch {
                    mime::initializeaux $child -lineslist $start
		    parsepart $child
                }]} {
                } cres copts]} {
                    set nochild 1
                    set state(parts) [lrange $state(parts) 0 end-1]
                }
            } else {
                mime::initializeaux $child -lineslist $start
		parsepart $child
            }
            set start {}
        }
        if {$forceoctet && !$nochild} {
            variable $child
            upvar 0  $child childstate
            set childstate(content) application/octet-stream
	    header setinternal $child Content-Type application/octet-stream
        }
        set forceoctet 0
    }
}

# ::mime::parsetype --
#
#       Parses the string passed in and identifies the content-type and
#       params strings.
#
# Arguments:
#       token  The MIME token to parse.
#       string The content-type string that should be parsed.
#
# Results:
#       Returns the content and params for the string as a two element
#       tcl list.

proc ::mime::parsetype {token string} {
    global errorCode errorInfo
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    variable typetokenL
    variable typelexemeL

    set state(input)   $string
    set state(buffer)  {}
    set state(lastC)   LX_END
    set state(comment) {}
    set state(tokenL)  $typetokenL
    set state(lexemeL) $typelexemeL

    set code [catch {mime::parsetypeaux $token $string} result]
    set ecode $errorCode
    set einfo $errorInfo

    unset state(input)   \
          state(buffer)  \
          state(lastC)   \
          state(comment) \
          state(tokenL)  \
          state(lexemeL)

    return -code $code -errorinfo $einfo -errorcode $ecode $result
}

# ::mime::parsetypeaux --
#
#       A helper function for mime::parsetype.  Parses the specified
#       string looking for the content type and params.
#
# Arguments:
#       token  The MIME token to parse.
#       string The content-type string that should be parsed.
#
# Results:
#       Returns the content and params for the string as a two element
#       tcl list.

proc ::mime::parsetypeaux {token string} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    if {[parselexeme $token] ne "LX_ATOM"} {
        error [format "expecting type (found %s)" $state(buffer)]
    }
    set type [string tolower $state(buffer)]

    switch -- [parselexeme $token] {
        LX_SOLIDUS {
        }

        LX_END {
            if {$type ne "message"} {
                error "expecting type/subtype (found $type)"
            }

            return [list message/rfc822 {}]
        }

        default {
            error [format "expecting \"/\" (found %s)" $state(buffer)]
        }
    }

    if {[parselexeme $token] ne "LX_ATOM"} {
        error [format "expecting subtype (found %s)" $state(buffer)]
    }
    append type [string tolower /$state(buffer)]

    array set params {}
    while {1} {
        switch -- [parselexeme $token] {
            LX_END {
                return [list $type [array get params]]
            }

            LX_SEMICOLON {
            }

            default {
                error [format "expecting \";\" (found %s)" $state(buffer)]
            }
        }

        switch -- [parselexeme $token] {
            LX_END {
                return [list $type [array get params]]
            }

            LX_ATOM {
            }

            default {
                error [format "expecting attribute (found %s)" $state(buffer)]
            }
        }

        set attribute [string tolower $state(buffer)]

        if {[parselexeme $token] ne "LX_EQUALS"} {
            error [format "expecting \"=\" (found %s)" $state(buffer)]
        }

        switch -- [parselexeme $token] {
            LX_ATOM {
            }

            LX_QSTRING {
                set state(buffer) [
                    string range $state(buffer) 1 [
                        expr {[string length $state(buffer)] - 2}]]
            }

            default {
                error [format "expecting value (found %s)" $state(buffer)]
            }
        }
        set params($attribute) $state(buffer)
    }
}

# ::mime::finalize --
#
#   mime::finalize destroys a MIME part.
#
#   If the -subordinates option is present, it specifies which
#   subordinates should also be destroyed. The default value is
#   "dynamic".
#
# Arguments:
#       token  The MIME token to parse.
#       args   Args can be optionally be of the following form:
#              ?-subordinates "all" | "dynamic" | "none"?
#
# Results:
#       Returns an empty string.

proc ::mime::finalize {token args} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    array set options [list -subordinates dynamic]
    array set options $args

    switch -- $options(-subordinates) {
    switch $options(-subordinates) {
        all {
            #TODO: this code path is untested
            if {$state(value) eq "parts"} {
                foreach part $state(parts) {
                    eval [linsert $args 0 mime::finalize $part]
                }
            }
1169
1170
1171
1172
1173
1174
1175
1176


1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283

1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311

1312
1313
1314
1315

1316
1317
1318
1319
1320
1321












































































































































1322

1323
1324

1325
1326
1327
1328
1329

1330
1331
1332

1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346

1347
1348
1349


1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
















1360
1361
1362
















































































































































































































































































































1363
1364
1365







1366

1367



























1368
1369
1370
1371
1372































1373

1374
1375
1376

1377
1378

1379
1380
1381
1382
1383
1384
1385
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975



















































































976
977
978
979
980
981
982
983
984
985
986
987
988

989
990

991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006

1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017

1018
1019
1020
1021

1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168

1169
1170

1171
1172
1173
1174
1175

1176
1177
1178

1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192

1193
1194

1195
1196
1197
1198
1199








1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216


1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530

1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559





1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594

1595
1596

1597
1598
1599
1600
1601
1602
1603
1604








+
+







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-













-


-
+















-











-
+



-
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+

-
+




-
+


-
+













-
+

-

+
+


-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+


-
+

-
+







        none {
        }

        default {
            error "unknown value for -subordinates $options(-subordinates)"
        }
    }

    dropchan $token

    foreach name [array names state] {
        unset state($name)
    }
    # FRINK: nocheck
    unset $token
}

# ::mime::getproperty --
#
#   mime::getproperty returns the properties of a MIME part.
#
#   The properties are:
#
#       property    value
#       ========    =====
#       content     the type/subtype describing the content
#       encoding    the "Content-Transfer-Encoding"
#       params      a list of "Content-Type" parameters
#       parts       a list of tokens for the part's subordinates
#       size        the approximate size of the content (unencoded)
#
#   The "parts" property is present only if the MIME part has
#   subordinates.
#
#   If mime::getproperty is invoked with the name of a specific
#   property, then the corresponding value is returned; instead, if
#   -names is specified, a list of all properties is returned;
#   otherwise, a serialized array of properties and values is returned.
#
# Arguments:
#       token      The MIME token to parse.
#       property   One of 'content', 'encoding', 'params', 'parts', and
#                  'size'. Defaults to returning a serialized array of
#                  properties and values.
#
# Results:
#       Returns the properties of a MIME part

proc ::mime::getproperty {token {property {}}} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    switch -- $property {
        {} {
            array set properties [list content  $state(content) \
                                       encoding $state(encoding) \
                                       params   $state(params) \
                                       size     [getsize $token]]
            if {[info exists state(parts)]} {
                set properties(parts) $state(parts)
            }

            return [array get properties]
        }

        -names {
            set names [list content encoding params]
            if {[info exists state(parts)]} {
                lappend names parts
            }

            return $names
        }

        content
            -
        encoding
            -
        params {
            return $state($property)
        }

        parts {
            if {![info exists state(parts)]} {
                error "MIME part is a leaf"
            }

            return $state(parts)
        }

        size {
            return [getsize $token]
        }

        default {
            error "unknown property $property"
        }
    }
}

# ::mime::getsize --
#
#    Determine the size (in bytes) of a MIME part/token
#
# Arguments:
#       token      The MIME token to parse.
#
# Results:
#       Returns the size in bytes of the MIME token.

proc ::mime::getsize {token} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    switch -- $state(value)/$state(canonicalP) {
    switch $state(value)/$state(canonicalP) {
        file/0 {
            set size $state(count)
        }

        file/1 {
            return [file size $state(file)]
        }

        parts/0
            -
        parts/1 {
            set size 0
            foreach part $state(parts) {
                incr size [getsize $part]
            }

            return $size
        }

        string/0 {
            set size [string length $state(string)]
        }

        string/1 {
            return [string length $state(string)]
        }
        default {
            error "Unknown combination \"$state(value)/$state(canonicalP)\""
            error [list {Unknown combination} $state(value) $state(canonicalP)]
        }
    }

    if {$state(encoding) eq "base64"} {
    if {$state(encoding) eq {base64}} {
        set size [expr {($size * 3 + 2) / 4}]
    }

    return $size
}


proc ::mime::getTransferEncoding token {
    upvar 0 $token state
    set res {}
    if {[set encoding $state(encoding)] eq {}} {
	set encoding [encoding $token]
    }
    if {$encoding ne {}} {
	set res $encoding
    }
    switch $encoding {
	base64
	    -
	quoted-printable {
	    set converter $encoding
	}
	7bit - 8bit - binary - {} {
	    # Bugfix for [#477088], also [#539952]
	    # Go ahead
	}
	default {
	    error "Can't handle content encoding \"$encoding\""
	}
    }
    return $res
}


namespace eval ::mime::header {
    namespace ensemble create -map {
	get get
	exists exists
	parse parse
	set set_
	serialize serialize
	setinternal setinternal
    }

    variable tchar
    # hypen is first for inclusion in brackets
    variable tchar_re {-!#$%&'*+.^`|~0-9A-Za-z}
    variable token_re "^(\[$tchar_re]*)\\s*(?:;|$)?"
    variable notattchar_re "\[^[string map {* {} ' {} % {}} $tchar_re]]"

    # RFC 2045 lexemes
    variable typetokenL
    lappend typetokenL \; , < > : ? ( ) @ \" \[ ] = / \\
    variable typelexemeL {
        LX_SEMICOLON LX_COMMA
        LX_LBRACKET  LX_RBRACKET
        LX_COLON     LX_QUESTION
        LX_LPAREN    LX_RPAREN
        LX_ATSIGN    LX_QUOTE
        LX_LSQUARE   LX_RSQUARE
        LX_EQUALS    LX_SOLIDUS
        LX_QUOTE
    }

    variable internal 0
}


proc ::mime::header::boundary {} {
    return [uniqueID]
}


proc ::mime::header::serialize {token name value params} {
    variable notattchar_re
    set lname [string tolower $name]

    # to do: check key for conformance
    # to do: properly quote/process $value for interpolation
    if {[regexp {[^\x21-\x39\x3b-\x7e]} $name]} {
	error [
	    list {non-printing character or colon character in header name} $name]
    }
    if {[regexp {[^\t\x20-\x7e]} $value]} {
	error [
	    list {non-printing character in header value}]
    }

    switch $lname {
	content-id - message-id {
	    set value <$value>
	}
    }

    set res "$name: $value"

    dict for {key value} $params {
	if {[regexp $notattchar_re $key]} {
	    error [list {illegal character found in attribute name}]
	}
	set len [expr {[string length $key]} + 1 + [string length $value]]
	# save one byte for the folding white space continuation space
	# and two bytes for "; "
	if {$len > 73 || ![regexp {[^-!#$%&'*+,.\w`~^@{}|]+$} $value]} {
	    # save two bytes for the quotes
	    if {$len <= 71 && ![regexp {[^\x20-\x7e]} $value]} {
		set value "[string map [list \\ \\\\ \" \\\"] $value[set value {}]]"
		append res "\n\t; $key=$value"
	    } else {
		set value [::encoding convertto utf-8 $value]

		regsub -all -- $notattchar_re $value {[format %%%02X [scan "\\&" %c]]} value
		set value [subst -novariables $value]

		set partnum 0
		set start 0
		set param $key*$partnum*=utf-8''
		while {$start < [string length $value]} {
		    # subtract one from the limit to ensure that at least one byte
		    # is included in the part value
		    if {[string length $param] > 72} {
			error [list {parameter name is too long}]
		    }
		    set end [expr {$start + 72 - [string length $param]}]
		    set part [string range $value $start $end]
		    incr start [string length $part]
		    append res "\n\t; $param$part"
		    set param $key*$partnum=
		    incr partnum
		}
	    }
	} else {
	    append res "\n\t; $key=$value"
	}
    }
    return $res
}


proc ::mime::header::exists {token name} {
    upvar 0 $token state
    set lname [string tolower $name]
    dict exists $state(header) $lname
}


# ::mime::getheader --
# ::mime::header get --
#
#    mime::getheader returns the header of a MIME part.
#    [mime::header get] returns the header of a MIME part.
#
#    A header consists of zero or more key/value pairs. Each value is a
#    list containing one or more strings.
#
#    If mime::getheader is invoked with the name of a specific key, then
#    If [mime::header get] is invoked with the name of a specific key, then
#    a list containing the corresponding value(s) is returned; instead,
#    if -names is specified, a list of all keys is returned; otherwise, a
#    serialized array of keys and values is returned. Note that when a
#    dictionary is returned. Note that when a
#    key is specified (e.g., "Subject"), the list returned usually
#    contains exactly one string; however, some keys (e.g., "Received")
#    often occur more than once in the header, accordingly the list
#    returned usually contains more than one string.
#
# Arguments:
#       token      The MIME token to parse.
#       key        Either a key or '-names'.  If it is '-names' a list
#                  of all keys is returned.
#
# Results:
#       Returns the header of a MIME part.

proc ::mime::getheader {token {key {}}} {
proc ::mime::header::get {token {key {}}} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state
    upvar 0 state(hparams) hparams
    parse $token

    array set header $state(header)
    switch -- $key {
        {} {
            set result {}
            foreach lower $state(lowerL) mixed $state(mixedL) {
                lappend result $mixed $header($lower)
            }
            return $result
        }
    switch $key {
	{} {
	    set result {}
	    lappend result MIME-Version [list $state(version) {}]
	    foreach lower $state(lowerL) mixed $state(mixedL) {
		foreach value $header($lower) hparam [
		    dict get $hparams $lower] {
		    lappend result $mixed [list $value $hparam]
		}
	    }
	    set tencoding [getTransferEncoding $token]
	    if {$tencoding ne {}} {
		lappend result Content-Transfer-Encoding [list $tencoding {}]
	    }
	    return $result
	}

        -names {
            return $state(mixedL)
	-names {
	    return $state(mixedL)
	}

	default {
	    set lower [string tolower $key]

	    switch $lower {
		content-transfer-encoding {
		    return [list [getTransferEncoding $token] {}]
		}
		mime-version {
		    return [list $state(version) {}]
		}
		default {
		    if {![info exists header($lower)]} {
			error "key $key not in header"
		    }
		    return [list $header($lower) [lindex [
			dict get $hparams $lower] end]]
		}
	    }
	}
    }
}


proc ::mime::header::parse token {
    # FRINK: nocheck
    upvar 0 $token state
    if {$state(canonicalP) || $state(headerparsed)} {
	return
    }
    set state(headerparsed) 1
    upvar 0 state(last) last

    if {[set fileP [info exists state(file)]]} {
        seek $state(fd) [set pos $state(offset)] start
        set last [expr {$state(offset) + $state(count) - 1}]
    } else {
        set string $state(string)
    }

    set vline {}
    while 1 {
	set blankP 0
	if {$fileP} {
	    if {($pos > $last) || ([set x [gets $state(fd) line]] <= 0)} {
		set blankP 1
	    } else {
		incr pos [expr {$x + 1}]
	    }
	} else {
	    if {$state(lines.current) >= $state(lines.count)} {
		set blankP 1
		set line {}
	    } else {
		set line [lindex $state(lines) $state(lines.current)]
		incr state(lines.current)
		set x [string length $line]
		if {$x == 0} {set blankP 1}
	    }
	}

	if {!$blankP && [string match *\r $line]} {
	    set line [string range $line 0 $x-2]
	    if {$x == 1} {
		set blankP 1
	    }
	}

	# there is a space and a tab between the brackets in next line
        if {!$blankP && [string match {[ 	]*} $line]} {
            append vline { } [string trimleft $line " \t"]
            continue
        }

        if {$vline eq {}} {
            if {$blankP} {
                break
            }

            set vline $line
            continue
        }

        if {
	    [set x [string first : $vline]] <= 0
	    ||
	    [set mixed [string trimright [
		string range $vline 0 [expr {$x - 1}]]]] eq {}
	} {
            error [list {improper line in header} $vline]
        }
        set value [string trim [string range $vline [expr {$x + 1}] end]]

        switch [set lower [string tolower $mixed]] {
	    content-disposition {
		set_ $token $mixed {*}[parseparts $token $value]
	    }
            content-type {
                if {[exists $token content-type]} {
                    error [list {multiple Content-Type fields starting with} \
			$vline]
                }

                set x [parsetype $token $value]
		setinternal $token Content-Type {*}$x
            }

            content-md5 {
            }

            content-transfer-encoding {
                if {
		    $state(encoding) ne {}
		    &&
		    $state(encoding) ne [string tolower $value]
		} {
                    error [list [list multiple Content-Transfer-Encoding \
			fields starting with] $vline]
                }

                set state(encoding) [string tolower $value]
            }

            mime-version {
                set state(version) $value
            }

            default {
		setinternal $token $mixed $value -mode append
            }
        }

        if {$blankP} {
            break
        }
        set vline $line
    }
}


proc ::mime::header::parseparams token {
    # FRINK: nocheck
    upvar 0 $token state
    set params {}

    while 1 {
        switch [parselexeme $token] {
            LX_END {
		return [processparams $params[set params {}]]
            }

	    LX_SEMICOLON {
		if {[dict size $params]} {
		    continue
		} else {
		    error [list {expecting attribute} not $state(buffer)]
		}
	    }

            LX_ATOM {
            }

            default {
                error [list {expecting attribute} not $state(buffer)]
            }
        }

        set attribute [string tolower $state(buffer)]

        if {[parselexeme $token] ne {LX_EQUALS}} {
            error [list expecting = found  $state(buffer)]
        }

        switch [parselexeme $token] {
            LX_ATOM {
            }

            LX_QSTRING {
                set state(buffer) [
                    string range $state(buffer) 1 [
                        expr {[string length $state(buffer)] - 2}]]
            }

            default {
                error [list expecting value found $state(buffer)]
            }
        }
        dict set params $attribute $state(buffer)
    }
}


proc ::mime::header::parseparts {token value} {
    variable token_re
    upvar 0 $token state

    if {![regexp $token_re $value match type]} {
	error [list {expected disposition-type}]
    }

    variable typetokenL
    variable typelexemeL

    set value [string range $value[set value {}] [string length $match] end]

    set state(input)   $value
    set state(buffer)  {}
    set state(lastC)   LX_END
    set state(comment) {}
    set state(tokenL)  $typetokenL
    set state(lexemeL) $typelexemeL

    set code [catch {parseparams $token} result copts]

    unset {*}{
	state(input)
	state(buffer)
	state(lastC)
	state(comment)
	state(tokenL)
	state(lexemeL)
    }

    return -options $copts [list $type $result]
}


# ::mime::header::parsetype --
#
#       Parses the string passed in and identifies the content-type and
#       params strings.
#
# Arguments:
#       token  The MIME token to parse.
#       string The content-type string that should be parsed.
#
# Results:
#       Returns the content and params for the string as a two element
#       tcl list.

proc ::mime::header::parsetype {token string} {
    # FRINK: nocheck
    upvar 0 $token state

    variable typetokenL
    variable typelexemeL

    set state(input)   $string
    set state(buffer)  {}
    set state(lastC)   LX_END
    set state(comment) {}
    set state(tokenL)  $typetokenL
    set state(lexemeL) $typelexemeL

    catch {parsetypeaux $token} result copts

    unset {*}{
	state(input)
	state(buffer)
	state(lastC)
	state(comment)
	state(tokenL)
	state(lexemeL)
    }

    return -options $copts $result
}

# ::mime::header::parsetypeaux --
#
#       A helper function for mime::parsetype.  Parses the specified
#       string looking for the content type and params.
#
# Arguments:
#       token  The MIME token to parse.
#       string The content-type string that should be parsed.
#
# Results:
#       Returns the content and params for the string as a two element
#       tcl list.

proc ::mime::header::parsetypeaux token {
    # FRINK: nocheck
    upvar 0 $token state
    set params {}

    if {[parselexeme $token] ne {LX_ATOM}} {
        error [list expecting type found $state(buffer)]
    }
    set type [string tolower $state(buffer)]

    switch [parselexeme $token] {
        LX_SOLIDUS {
        }

        LX_END {
            if {$type ne {message}} {
                error [list expecting type/subtype found $type]
            }

            return [list message/rfc822 {}]
        }

        default {
            error [list expecting / found  $state(buffer)]
        }
    }

    if {[parselexeme $token] ne {LX_ATOM}} {
        error [list expecting subtype found $state(buffer)]
    }
            set lower [string tolower [set mixed $key]]
    append type [string tolower /$state(buffer)]

    switch [parselexeme $token] {
	LX_END {
	}

	LX_SEMICOLON {
	    set params [parseparams $token]
	}

	default {
	    error [list expecting  {;  or end} found $state(buffer)]
	}
    }

    list $type $params
}


proc ::mime::header::processparams params {
    set info {}
    foreach key [lsort -dictionary [dict keys $params]] {
	set pvalue [dict get $params $key]
	# a trailing asterisk is ignored if this is not the first field in an
	# identically-named series

	# this expression can't fail
	regexp {^([^*]+?)(?:([*])([0-9]+))?([*])?$} $key -> name star1 counter star2
	dict update info $name dict1 {
            if {![info exists header($lower)]} {
                error "key $mixed not in header"
            }
            return $header($lower)
        }
	    if {![info exists dict1]} {
		set dict1 {}
	    }
	    dict update dict1 encoding encoding value value {
		if {$star1 ne {}} {
		    if {$star2 ne {} || $counter eq {}} {
			if {![regexp {^([^']*)'([^']*)'(.*)$} $pvalue \
			    -> charset lang pvalue]} {

			    error [list [list malformed language information in \
				extended parameter name]]
			}
			if {$charset ne {}} {
			    set encoding [reversemapencoding $charset]
			}
		    }
		}
		append value $pvalue
	    }
	}
    }

    set params {}
    dict for {key pinfo} $info[set info {}] {
	dict update pinfo encoding encoding value value {}
	if {[info exists encoding]} {
	    set value [string map {% {\x}} $value[set value {}]]
	    set value [subst -novariables -nocommands $value[set value {}]]
	    set value [encoding convertfrom $encoding $value]
	}
	dict set params $key $value
    }
    return $params
}

# ::mime::setheader --
# ::mime::header::set --
#
#    mime::setheader writes, appends to, or deletes the value associated
#    mime::header::set writes, appends to, or deletes the value associated
#    with a key in the header.
#
#    The value for -mode is one of:
#
#       write: the key/value is either created or overwritten (the
#       default);
#
1398
1399
1400
1401
1402
1403
1404
1405


1406
1407
1408


1409
1410
1411
1412





1413
1414
1415

1416
1417
1418
1419
1420
1421
1422
1423
1424
1425










1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444














































1445
1446

1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464









1465

1466
1467

1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485

1486
1487
1488

1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500

1501
1502
1503

1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514






1515
1516






1517




























































1518
1519
1520
1521



1522
1523
1524
1525
1526
1527
1528
1529
1530


























































1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560





























1561
1562
1563


1564
1565

1566
1567
1568
1569
1570
1571





1572
1573
1574
1575
1576
1577

1578
1579

1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591

1592
1593
1594

1595
1596
1597


1598
1599
1600
1601
1602
1603

1604
1605
1606
1607
1608
1609
1610
1611
1612
1613

1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642

1643
1644
1645
1646
1647
1648
1649
1650
1651
1652

1653
1654
1655
1656

1657
1658
1659

1660
1661
1662
1663
1664

1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677

1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691

1692
1693
1694

1695
1696
1697
1698
1699
1700
1701
1702
1703
1617
1618
1619
1620
1621
1622
1623

1624
1625
1626

1627
1628
1629
1630



1631
1632
1633
1634
1635



1636










1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660





1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713




1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732

1733
1734

1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752

1753
1754
1755

1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767

1768

1769

1770

1771
1772
1773
1774
1775
1776
1777


1778
1779
1780
1781
1782
1783
1784


1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852



1853
1854
1855
1856








1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915





























1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945


1946
1947


1948
1949





1950
1951
1952
1953
1954






1955


1956












1957



1958



1959
1960






1961










1962













1963















1964










1965




1966



1967





1968
1969












1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983

1984
1985
1986

1987
1988

1989
1990
1991
1992
1993
1994
1995







-
+
+

-

+
+

-
-
-
+
+
+
+
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+














-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+




-
-
-
-










+
+
+
+
+
+
+
+
+
-
+

-
+

















-
+


-
+











-
+
-

-
+
-







-
-

+
+
+
+
+
+
-
-
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
-
-
+

-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
+
-
-
-
+
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
+
-
-
-
+
-
-
-
-
-
+

-
-
-
-
-
-
-
-
-
-
-
-
+













-
+


-
+

-







#       value      The value for the header key to be set to.
#       args       An optional argument of the form:
#                  ?-mode "write" | "append" | "delete"?
#
# Results:
#       Returns previous value associated with the specified key.

proc ::mime::setheader {token key value args} {
proc ::mime::header::set_ {token key value args} {
    variable internal
    # FRINK: nocheck
    variable $token
    upvar 0 $token state
    upvar 0 state(hparams) hparams
    parse $token

    array set options [list -mode write]
    array set options $args

    set params {}
    switch [llength $args] {
	1 - 3 {
	    set args [lassign $args[set args {}] params]
	}
    switch -- [set lower [string tolower $key]] {
        content-md5
            -
	0 - 2 {
        content-type
            -
        content-transfer-encoding
            -
        mime-version {
            error "key $key may not be set"
        }
        default {# Skip key}
    }

	    # carry on
	}
	default {
	    error [list {wrong # args}]
	}
    }
    array set options [list -mode write]
    array set options $args

    set lower [string tolower $key]
    array set header $state(header)
    if {[set x [lsearch -exact $state(lowerL) $lower]] < 0} {
        #TODO: this code path is not tested
        if {$options(-mode) eq "delete"} {
            error "key $key not in header"
        }

        lappend state(lowerL) $lower
        lappend state(mixedL) $key

        set result {}
    } else {
        set result $header($lower)
    }
    switch -- $options(-mode) {
        append {
            lappend header($lower) $value
        }

    switch $options(-mode) {
	append - write {
	    switch $lower {
		content-md5
		    -
		content-transfer-encoding
		    -
		mime-version
		    -
		content-type {
		    if {!$internal} {
			switch $lower {
			    default {
				lassign [get $token $lower] values params1
				if {$value ni $values} {
				    error "key $key may not be set"
				}
			    }
			}
		    }
		    switch $lower {
			content-type {
			    if {[string match multipart/* $value]
				&&
				![dict exists $params boundary]
			    } {
				dict set params boundary [boundary]
			    }
			}
			default {
			    #carry on
			}
		    }
		}
	    }
	    switch $options(-mode) {
		append {
		    lappend header($lower) $value
		    dict lappend hparams $lower $params
		}
		write {
		    set header($lower) [list $value]
		    dict set hparams $lower [list $params]
		}
	    }
	}
        delete {
            unset header($lower)
	    dict unset hparams $lower
            set state(lowerL) [lreplace $state(lowerL) $x $x]
            set state(mixedL) [lreplace $state(mixedL) $x $x]
        }

        write {
            set header($lower) [list $value]
        }

        default {
            error "unknown value for -mode $options(-mode)"
        }
    }

    set state(header) [array get header]

    return $result
}


proc ::mime::header::setinternal args {
    variable internal 1
    try {
	set_ {*}$args
    } finally {
	set internal 0
    }
}
# ::mime::getbody --
# ::mime::body --
#
#    mime::getbody returns the body of a leaf MIME part in canonical form.
#    mime::body returns the body of a leaf MIME part in canonical form.
#
#    If the -command option is present, then it is repeatedly invoked
#    with a fragment of the body as this:
#
#        uplevel #0 $callback [list "data" $fragment]
#
#    (The -blocksize option, if present, specifies the maximum size of
#    each fragment passed to the callback.)
#    When the end of the body is reached, the callback is invoked as:
#
#        uplevel #0 $callback "end"
#
#    Alternatively, if an error occurs, the callback is invoked as:
#
#        uplevel #0 $callback [list "error" reason]
#
#    Regardless, the return value of the final invocation of the callback
#    is propagated upwards by mime::getbody.
#    is propagated upwards by mime::body.
#
#    If the -command option is absent, then the return value of
#    mime::getbody is a string containing the MIME part's entire body.
#    mime::body is a string containing the MIME part's entire body.
#
# Arguments:
#       token      The MIME token to parse.
#       args       Optional arguments of the form:
#                  ?-decode? ?-command callback ?-blocksize octets? ?
#
# Results:
#       Returns a string containing the MIME part's entire body, or
#       if '-command' is specified, the return value of the command
#       is returned.

proc ::mime::getbody {token args} {
proc ::mime::body {token args} {
    global errorCode errorInfo
    # FRINK: nocheck
    variable $token
    parsepart $token
    upvar 0 $token state

    set decode 0
    if {[set pos [lsearch -exact $args -decode]] >= 0} {
        set decode 1
        set args [lreplace $args $pos $pos]
    }

    array set options [list -command [
        list mime::getbodyaux $token] -blocksize 4096]
    array set options $args

    if {[info exists options(-blocksize)]} {
	set collect 1
	if {$options(-blocksize) eq {}]} {
	    set options(-blocksize) 8192
	}
    if {$options(-blocksize) < 1} {
        error "-blocksize expects a positive integer, not $options(-blocksize)"
	if {$options(-blocksize) < 1} {
	    error [list -blocksize $options(-blocksize) {not a positive integer}]
	}
    } else {
	set options(-blocksize) 8192
	set collect 0
    }

    set coro [coroutine [info cmdcount]_body body2 $token $decode [array get options]]
    set command [list ::apply [list coro {
	return {*}[yieldto $coro [info coroutine]]
    } [namespace current]] $coro]

    if {$collect} {
	return $command
    } else {
	set res {}
	while 1 {
	    append res [{*}$command]
	}
	return $res
    }
}


proc ::mime::body2 {token decode optdict} {
    set caller [yield [info coroutine]]

    set yield [list ::apply [list args {
	upvar 1 caller caller decode decode dread dread dwrite dwrite
	if {[llength $args] == 1 && [info exists decode] && $decode} {
	    lassign $args[set args {}] fragment
	    puts -nonewline $dwrite $fragment 
	    set arg {}
	    # not using a coroutine::util::read here because there isn't much
	    # data on the channel and if no characters are currently available,
	    # the current routine must continue to put more data into the
	    # channel.

	    # $dread must be nonblocking even though it is read in a tight loop
	    # here.
	    while {[set data [::read $dread]] ne {}} {
		append arg $data
	    }
	    if {$arg ne {}} {
		lappend args $arg 
		set caller [yieldto $caller {*}$args]
	    }
	} else {
	    set caller [yieldto $caller {*}$args]
	}
    } [namespace current]]]

    set return [list ::apply [list args {
	upvar 1 caller caller yield yield
	if {[info exists dread]} {
	    close $dread
	    close $dwrite
	}
	rename [info coroutine] {}
	uplevel 1 [list {*}$yield {*}$args] 
    } [namespace current]]]

    try {
	upvar 0 $token state

	array set options $optdict 

    set code 0
    set ecode {}
    set einfo {}
	set code 0
	set ecode {}
	set einfo {}

    switch -- $state(value)/$state(canonicalP) {
        file/0 {
            set fd [open $state(file) RDONLY]

            set code [catch {
                fconfigure $fd -translation binary
                seek $fd [set pos $state(offset)] start
                set last [expr {$state(offset) + $state(count) - 1}]
	switch $state(value) {
	    file {
		upvar 0 state(fd) fd
		set offset $state(offset)
		set count $state(count)
	    }
	    string {
		set offset 0
		set fd [tcl::chan::string $state(string)]
		seek $fd 0 end
		set count [tell $fd]
		seek $fd 0
	    }
	}

	switch $state(value) {
	    parts {
		error [list {MIME part isn't a leaf}]
	    }
	}


	if {$decode} {
	    lassign [chan pipe] dread dwrite
	    chan configure $dread -blocking 0
	    chan configure $dwrite -blocking 0 -encoding binary -buffering none
	    set params [mime::property $token params]

	    if {[dict exists $params charset]} {
		set charset [dict get $params charset]
	    } else {
		set charset US-ASCII
	    }

	    set enc [reversemapencoding $charset]
	    if {$enc ne {}} {
		chan configure $dread -encoding $enc
	    } else {
		{*}$return -code error [
		    list {-decode cannot reversemap charset} $charset]
	    }
	}



	if {$state(canonicalP)} {
	    seek $fd [set pos $offset] start
	    fconfigure $fd -translation binary
	    set code [catch {
		while {[string length [
		    set fragment [read $fd $options(-blocksize)]]] > 0} {
			{*}$yield $fragment
		    }
	    } result copts]
	} else {
	    set code [catch {
		seek $fd [set pos $offset] start
		set last [expr {$offset + $count - 1}]

                set fragment {}
                while {$pos <= $last} {
                    if {[set cc [
                        expr {($last - $pos) + 1}]] > $options(-blocksize)} {
                        set cc $options(-blocksize)
                    }
                    incr pos [set len [
                        string length [set chunk [read $fd $cc]]]]
                    switch -exact -- $state(encoding) {
                        base64
                            -
                        quoted-printable {
                            if {([set x [string last \n $chunk]] > 0) \
                                    && ($x + 1 != $len)} {
                                set chunk [string range $chunk 0 $x]
                                seek $fd [incr pos [expr {($x + 1) - $len}]] start
                            }
                            set chunk [
                                $state(encoding) -mode decode -- $chunk]
                        }
                        7bit - 8bit - binary - {} {
                            # Bugfix for [#477088]
                            # Go ahead, leave chunk alone
                        }
                        default {
                            error "Can't handle content encoding \"$state(encoding)\""
                        }
                    }
                    append fragment $chunk
		set fragment {}
		while {$pos <= $last} {
		    if {[set cc [
			expr {($last - $pos) + 1}]] > $options(-blocksize)} {
			set cc $options(-blocksize)
		    }
		    incr pos [set len [
			string length [set chunk [read $fd $cc]]]]
		    switch -exact $state(encoding) {
			base64
			    -
			quoted-printable {
			    if {([set x [string last \n $chunk]] > 0) \
				    && ($x + 1 != $len)} {
				set chunk [string range $chunk 0 $x]
				seek $fd [incr pos [expr {($x + 1) - $len}]] start
			    }
			    set chunk [
				$state(encoding) -mode decode -- $chunk]
			}
			7bit - 8bit - binary - {} {
			    # Bugfix for [#477088]
			    # Go ahead, leave chunk alone
			}
			default {
			    error "Can't handle content encoding \"$state(encoding)\""
			}
		    }
		    append fragment $chunk

                    set cc [expr {$options(-blocksize) - 1}]
                    while {[string length $fragment] > $options(-blocksize)} {
		    set cc [expr {$options(-blocksize) - 1}]
		    while {[string length $fragment] > $options(-blocksize)} {
                        uplevel #0 $options(-command) [
                            list data [string range $fragment 0 $cc]]
			{*}$yield [string range $fragment 0 $cc]

                        set fragment [
                            string range $fragment $options(-blocksize) end]
                    }
                }
                if {[string length $fragment] > 0} {
			set fragment [
			    string range $fragment $options(-blocksize) end]
		    }
		}
		if {[string length $fragment] > 0} {
                    uplevel #0 $options(-command) [list data $fragment]
                }
            } result]
            set ecode $errorCode
            set einfo $errorInfo

		    {*}$yield $fragment
            catch {close $fd}
        }
		}

        file/1 {
            set fd [open $state(file) RDONLY]

            set code [catch {
                fconfigure $fd -translation binary

                while {[string length [
                    set fragment [read $fd $options(-blocksize)]]] > 0} {
                        uplevel #0 $options(-command) [list data $fragment]
                    }
            } result]
	    } result copts]
            set ecode $errorCode
            set einfo $errorInfo

	}
            catch {close $fd}
        }


	if {$code} {
        parts/0
            -
        parts/1 {
            error "MIME part isn't a leaf"
        }

	    {*}$yield -options $copts $result
        string/0
            -
        string/1 {
            switch -- $state(encoding)/$state(canonicalP) {
                base64/0
                    -
                quoted-printable/0 {
                    set fragment [
                        $state(encoding) -mode decode -- $state(string)]
                }
	}

                default {
                    # Not a bugfix for [#477088], but clarification
                    # This handles no-encoding, 7bit, 8bit, and binary.
                    set fragment $state(string)
                }
            }

            set code [catch {
                set cc [expr {$options(-blocksize) -1}]
                while {[string length $fragment] > $options(-blocksize)} {
                    uplevel #0 $options(-command) [
                        list data [string range $fragment 0 $cc]]

                    set fragment [
                        string range $fragment $options(-blocksize) end]
                }
                if {[string length $fragment] > 0} {
                    uplevel #0 $options(-command) [list data $fragment]
                }
            } result]
            set ecode $errorCode
            set einfo $errorInfo
        }
        default {
            error "Unknown combination \"$state(value)/$state(canonicalP)\""
        }
    }

	{*}$return -code break
    set code [catch {
        if {$code} {
            uplevel #0 $options(-command) [list error $result]
        } else {
            uplevel #0 $options(-command) [list end]
        }
    } result]
    set ecode $errorCode
    set einfo $errorInfo

    } on error {tres topts} {
    if {$code} {
        return -code $code -errorinfo $einfo -errorcode $ecode $result
    }

	{*}$return -options $topts $tres
    if {$decode} {
        array set params [mime::getproperty $token params]

    }
        if {[info exists params(charset)]} {
            set charset $params(charset)
        } else {
            set charset US-ASCII
        }
}

        set enc [reversemapencoding $charset]
        if {$enc ne {}} {
            set result [::encoding convertfrom $enc $result]
        } else {
            return -code error "-decode failed: can't reversemap charset $charset"
        }
    }

    return $result
}

# ::mime::getbodyaux --
# ::mime::bodyaux --
#
#    Builds up the body of the message, fragment by fragment.  When
#    the entire message has been retrieved, it is returned.
#
# Arguments:
#       token      The MIME token to parse.
#       reason     One of 'data', 'end', or 'error'.
#       fragment   The section of data data fragment to extract a
#                  string from.
#
# Results:
#       Returns nothing, except when called with the 'end' argument
#       in which case it returns a string that contains all of the
#       data that 'getbodyaux' has been called with.  Will throw an
#       data that 'bodyaux' has been called with.  Will throw an
#       error if it is called with the reason of 'error'.

proc ::mime::getbodyaux {token reason {fragment {}}} {
proc ::mime::bodyaux {token reason {fragment {}}} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    switch $reason {
        data {
            append state(getbody) $fragment
            return {}
        }
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223

2224
2225
2226

2227


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
2012
2013
2014
2015
2016
2017
2018




































































































































































































































































































































































































































































































2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030

2031
2032

2033
2034
2035
2036
2037

2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056

2057
2058
2059
2060
2061
2062
2063


2064
2065
2066











2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077


2078
2079
2080
2081
2082
2083
2084
2085
2086

2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103

2104
2105
2106
2107

2108
2109
2110
2111








2112
2113
2114
2115
2116
2117
2118



2119
2120
2121
2122
2123
2124
2125







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-












-
+

-

+

+
+
-
+


















-
+

+
+
+
+
+
-
-
+
+

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+







-
+
+
+
+










+


-
+



-
+



-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-








        default {
            error "Unknown reason \"$reason\""
        }
    }
}

# ::mime::copymessage --
#
#    mime::copymessage copies the MIME part to the specified channel.
#
#    mime::copymessage operates synchronously, and uses fileevent to
#    allow asynchronous operations to proceed independently.
#
# Arguments:
#       token      The MIME token to parse.
#       channel    The channel to copy the message to.
#
# Results:
#       Returns nothing unless an error is thrown while the message
#       is being written to the channel.

proc ::mime::copymessage {token channel} {
    global errorCode errorInfo
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    set openP [info exists state(fd)]

    set code [catch {mime::copymessageaux $token $channel} result]
    set ecode $errorCode
    set einfo $errorInfo

    if {(!$openP) && ([info exists state(fd)])} {
        if {![info exists state(root)]} {
            catch {close $state(fd)}
        }
        unset state(fd)
    }

    return -code $code -errorinfo $einfo -errorcode $ecode $result
}

# ::mime::copymessageaux --
#
#    mime::copymessageaux copies the MIME part to the specified channel.
#
# Arguments:
#       token      The MIME token to parse.
#       channel    The channel to copy the message to.
#
# Results:
#       Returns nothing unless an error is thrown while the message
#       is being written to the channel.

proc ::mime::copymessageaux {token channel} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    array set header $state(header)

    if {$state(version) ne {}} {
        puts $channel "MIME-Version: $state(version)"
    }
    foreach lower $state(lowerL) mixed $state(mixedL) {
        foreach value $header($lower) {
            puts $channel "$mixed: $value"
        }
    }
    if {(!$state(canonicalP)) \
            && ([set encoding $state(encoding)] ne {})} {
        puts $channel "Content-Transfer-Encoding: $encoding"
    }

    puts -nonewline $channel "Content-Type: $state(content)"
    set boundary {}
    foreach {k v} $state(params) {
        if {$k eq "boundary"} {
            set boundary $v
        }

        puts -nonewline $channel ";\n              $k=\"$v\""
    }

    set converter {}
    set encoding {}
    if {$state(value) ne "parts"} {
        puts $channel {}

        if {$state(canonicalP)} {
            if {[set encoding $state(encoding)] eq {}} {
                set encoding [encoding $token]
            }
            if {$encoding ne {}} {
                puts $channel "Content-Transfer-Encoding: $encoding"
            }
            switch -- $encoding {
                base64
                    -
                quoted-printable {
                    set converter $encoding
                }
                7bit - 8bit - binary - {} {
                    # Bugfix for [#477088], also [#539952]
                    # Go ahead
                }
                default {
                    error "Can't handle content encoding \"$encoding\""
                }
            }
        }
    } elseif {([string match multipart/* $state(content)]) \
        && ($boundary eq {})} {
        # we're doing everything in one pass...
        set key [clock seconds]$token[info hostname][array get state]
        set seqno 8
        while {[incr seqno -1] >= 0} {
            set key [md5 -- $key]
        }
        set boundary "----- =_[string trim [base64 -mode encode -- $key]]"

        puts $channel ";\n              boundary=\"$boundary\""
    } else {
        puts $channel {}
    }

    if {[info exists state(error)]} {
        unset state(error)
    }

    switch -- $state(value) {
        file {
            set closeP 1
            if {[info exists state(root)]} {
                # FRINK: nocheck
                variable $state(root)
                upvar 0 $state(root) root

                if {[info exists root(fd)]} {
                    set fd $root(fd)
                    set closeP 0
                } else {
                    set fd [set state(fd) [open $state(file) RDONLY]]
                }
                set size $state(count)
            } else {
                set fd [set state(fd) [open $state(file) RDONLY]]
                # read until eof
                set size -1
            }
            seek $fd $state(offset) start
            if {$closeP} {
                fconfigure $fd -translation binary
            }

            puts $channel {}

            while {($size != 0) && (![eof $fd])} {
                if {$size < 0 || $size > 32766} {
                    set X [read $fd 32766]
                } else {
                    set X [read $fd $size]
                }
                if {$size > 0} {
                    set size [expr {$size - [string length $X]}]
                }
                if {$converter eq {}} {
                    puts -nonewline $channel $X
                } else {
                    puts -nonewline $channel [$converter -mode encode -- $X]
                }
            }

            if {$closeP} {
                catch {close $state(fd)}
                unset state(fd)
            }
        }

        parts {
            if {(![info exists state(root)]) \
                    && ([info exists state(file)])} {
                set state(fd) [open $state(file) RDONLY]
                fconfigure $state(fd) -translation binary
            }

            switch -glob -- $state(content) {
                message/* {
                    puts $channel {}
                    foreach part $state(parts) {
                        mime::copymessage $part $channel
                        break
                    }
                }

                default {
                    # Note RFC 2046: See buildmessageaux for details.

                    foreach part $state(parts) {
                        puts $channel \n--$boundary
                        mime::copymessage $part $channel
                    }
                    puts $channel \n--$boundary--
                }
            }

            if {[info exists state(fd)]} {
                catch {close $state(fd)}
                unset state(fd)
            }
        }

        string {
            if {[catch {fconfigure $channel -buffersize} blocksize]} {
                set blocksize 4096
            } elseif {$blocksize < 512} {
                set blocksize 512
            }
            set blocksize [expr {($blocksize / 4) * 3}]

            # [893516]
            fconfigure $channel -buffersize $blocksize

            puts $channel {}

            #TODO: tests don't cover these paths
            if {$converter eq {}} {
                puts -nonewline $channel $state(string)
            } else {
                puts -nonewline $channel [$converter -mode encode -- $state(string)]
            }
        }
        default {
            error "Unknown value \"$state(value)\""
        }
    }

    flush $channel

    if {[info exists state(error)]} {
        error $state(error)
    }
}

# ::mime::buildmessage --
#
#     The following is a clone of the copymessage code to build up the
#     result in memory, and, unfortunately, without using a memory channel.
#     I considered parameterizing the "puts" calls in copy message, but
#     the need for this procedure may go away, so I'm living with it for
#     the moment.
#
# Arguments:
#       token      The MIME token to parse.
#
# Results:
#       Returns the message that has been built up in memory.

proc ::mime::buildmessage {token} {
    global errorCode errorInfo
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    set openP [info exists state(fd)]

    set code [catch {mime::buildmessageaux $token} result]
    if {![info exists errorCode]} {
        set ecode {}
    } else {
        set ecode $errorCode
    }
    set einfo $errorInfo

    if {(!$openP) && ([info exists state(fd)])} {
        if {![info exists state(root)]} {
            catch {close $state(fd)}
        }
        unset state(fd)
    }

    return -code $code -errorinfo $einfo -errorcode $ecode $result
}

# ::mime::buildmessageaux --
#
#     The following is a clone of the copymessageaux code to build up the
#     result in memory, and, unfortunately, without using a memory channel.
#     I considered parameterizing the "puts" calls in copy message, but
#     the need for this procedure may go away, so I'm living with it for
#     the moment.
#
# Arguments:
#       token      The MIME token to parse.
#
# Results:
#       Returns the message that has been built up in memory.

proc ::mime::buildmessageaux {token} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    array set header $state(header)

    set result {}
    if {$state(version) ne {}} {
        append result "MIME-Version: $state(version)\r\n"
    }
    foreach lower $state(lowerL) mixed $state(mixedL) {
        foreach value $header($lower) {
            append result "$mixed: $value\r\n"
        }
    }
    if {(!$state(canonicalP)) \
            && ([set encoding $state(encoding)] ne {})} {
        append result "Content-Transfer-Encoding: $encoding\r\n"
    }

    append result "Content-Type: $state(content)"
    set boundary {}
    foreach {k v} $state(params) {
        if {$k eq "boundary"} {
            set boundary $v
        }

        append result ";\r\n              $k=\"$v\""
    }

    set converter {}
    set encoding {}
    if {$state(value) ne "parts"} {
        #TODO: the path is not covered by tests
        append result \r\n

        if {$state(canonicalP)} {
            if {[set encoding $state(encoding)] eq {}} {
                set encoding [encoding $token]
            }
            if {$encoding ne {}} {
                append result "Content-Transfer-Encoding: $encoding\r\n"
            }
            switch -- $encoding {
                base64
                    -
                quoted-printable {
                    set converter $encoding
                }
                7bit - 8bit - binary - {} {
                    # Bugfix for [#477088]
                    # Go ahead
                }
                default {
                    error "Can't handle content encoding \"$encoding\""
                }
            }
        }
    } elseif {([string match multipart/* $state(content)]) \
                    && ($boundary eq {})} {
        # we're doing everything in one pass...
        set key [clock seconds]$token[info hostname][array get state]
        set seqno 8
        while {[incr seqno -1] >= 0} {
            set key [md5 -- $key]
        }
        set boundary "----- =_[string trim [base64 -mode encode -- $key]]"

        append result ";\r\n              boundary=\"$boundary\"\r\n"
    } else {
        append result \r\n
    }

    if {[info exists state(error)]} {
        unset state(error)
    }

    switch -- $state(value) {
        file {
            set closeP 1
            if {[info exists state(root)]} {
                # FRINK: nocheck
                variable $state(root)
                upvar 0 $state(root) root

                if {[info exists root(fd)]} {
                    set fd $root(fd)
                    set closeP 0
                } else {
                    set fd [set state(fd) [open $state(file) RDONLY]]
                }
                set size $state(count)
            } else {
                set fd [set state(fd) [open $state(file) RDONLY]]
                set size -1 ;# Read until EOF
            }
            seek $fd $state(offset) start
            if {$closeP} {
                fconfigure $fd -translation binary
            }

            append result \r\n

            while {($size != 0) && (![eof $fd])} {
                if {$size < 0 || $size > 32766} {
                    set X [read $fd 32766]
                } else {
                    set X [read $fd $size]
                }
                if {$size > 0} {
                    set size [expr {$size - [string length $X]}]
                }
                if {$converter ne {}} {
                    append result [$converter -mode encode -- $X]
                } else {
                    append result $X
                }
            }

            if {$closeP} {
                catch {close $state(fd)}
                unset state(fd)
            }
        }

        parts {
            if {(![info exists state(root)]) \
                    && ([info exists state(file)])} {
                set state(fd) [open $state(file) RDONLY]
                fconfigure $state(fd) -translation binary
            }

            switch -glob -- $state(content) {
                message/* {
                    append result "\r\n"
                    foreach part $state(parts) {
                        append result [buildmessage $part]
                        break
                    }
                }

                default {
                    # Note RFC 2046:
                    #
                    # The boundary delimiter MUST occur at the
                    # beginning of a line, i.e., following a CRLF, and
                    # the initial CRLF is considered to be attached to
                    # the boundary delimiter line rather than part of
                    # the preceding part.
                    #
                    # - The above means that the CRLF before $boundary
                    #   is needed per the RFC, and the parts must not
                    #   have a closing CRLF of their own. See Tcllib bug
                    #   1213527, and patch 1254934 for the problems when
                    #   both file/string brnaches added CRLF after the
                    #   body parts.

                    foreach part $state(parts) {
                        append result "\r\n--$boundary\r\n"
                        append result [buildmessage $part]
                    }
                    append result "\r\n--$boundary--\r\n"
                }
            }

            if {[info exists state(fd)]} {
                catch {close $state(fd)}
                unset state(fd)
            }
        }

        string {
            append result "\r\n"

            if {$converter ne {}} {
                append result [$converter -mode encode -- $state(string)]
            } else {
                append result $state(string)
            }
        }
        default {
            error "Unknown value \"$state(value)\""
        }
    }

    if {[info exists state(error)]} {
        error $state(error)
    }
    return $result
}

# ::mime::encoding --
#
#     Determines how a token is encoded.
#
# Arguments:
#       token      The MIME token to parse.
#
# Results:
#       Returns the encoding of the message (the null string, base64,
#       or quoted-printable).

proc ::mime::encoding {token} {
proc ::mime::encoding token {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state
    upvar 0 state(params) params

    lassign [header get $token content-type] content

    switch -glob -- $state(content) {
    switch -glob $content {
        audio/*
            -
        image/*
            -
        video/* {
            return base64
        }

        message/*
            -
        multipart/* {
            return {}
        }
        default {# Skip}
    }

    set asciiP 1
    set lineP 1
    switch -- $state(value) {
    switch $state(value) {
        file {
	    if {$state(file) eq {}} {
		# choose a workable all-purpose encoding 
		set asciiP 0
		set lineP 0
	    } else {
            set fd [open $state(file) RDONLY]
            fconfigure $fd -translation binary
		set fd [open $state(file) RDONLY]
		fconfigure $fd -translation binary

            while {[gets $fd line] >= 0} {
                if {$asciiP} {
                    set asciiP [encodingasciiP $line]
                }
                if {$lineP} {
                    set lineP [encodinglineP $line]
                }
                if {(!$asciiP) && (!$lineP)} {
                    break
                }
            }
		while {[gets $fd line] >= 0} {
		    if {$asciiP} {
			set asciiP [encodingasciiP $line]
		    }
		    if {$lineP} {
			set lineP [encodinglineP $line]
		    }
		    if {(!$asciiP) && (!$lineP)} {
			break
		    }
		}

            catch {close $fd}
		catch {close $fd}
	    }
        }

        parts {
            return {}
        }

        string {
            foreach line [split $state(string) "\n"] {
	    set saved $state(lines.current)
	    while {$state(lines.current) < $state(lines.count)} {
		set line [lindex $state(lines) $state(lines.current)]
		incr state(lines.current)
                if {$asciiP} {
                    set asciiP [encodingasciiP $line]
                }
                if {$lineP} {
                    set lineP [encodinglineP $line]
                }
                if {(!$asciiP) && (!$lineP)} {
                    break
                }
            }
	    set state(lines.current) $saved
        }
        default {
            error "Unknown value \"$state(value)\""
            error [list {Unknown value} $state(value)]
        }
    }

    switch -glob -- $state(content) {
    switch -glob $content {
        text/* {
            if {!$asciiP} {
                #TODO: this path is not covered by tests
                foreach {k v} $state(params) {
                    if {$k eq "charset"} {
                        set v [string tolower $v]
                        if {($v ne "us-ascii") \
                                && (![string match {iso-8859-[1-8]} $v])} {
                            return base64
                        }

		if {[dict exists $params charset]} {
		    set v [string tolower [dict get $params $charset]]
		    if {($v ne "us-ascii") \
			    && (![string match {iso-8859-[1-8]} $v])} {
			return base64
		    }
		}
                        break
                    }
                }
            }

            if {!$lineP} {
                return quoted-printable
            }
        }

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
2142
2143
2144
2145
2146
2147
2148

2149
2150

2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163


2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185

2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196


2197



















2198


















2199

2200




2201
2202















2203


2204
2205


2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225



2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240






2241
2242
2243
2244
2245
2246
2247
2248
2249







-
+

-
+











+
-
-
+
+
+
+


















-
+










-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
+
-
-
-
-
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
-
-
+



















-
-
-
+
+
+
+
+
+
+
+







-
-
-
-
-
-
+
+







# Arguments:
#       line    The line to check.
#
# Results:
#       Returns 1 if \r only occurs at the end of lines, and if all
#       characters in the line are between the ASCII codes of 32 and 126.

proc ::mime::encodingasciiP {line} {
proc ::mime::encodingasciiP line {
    foreach c [split $line {}] {
        switch -- $c {
        switch $c {
            { } - \t - \r - \n {
            }

            default {
                binary scan $c c c
                if {($c < 32) || ($c > 126)} {
                    return 0
                }
            }
        }
    }
    if {
    if {([set r [string first \r $line]] < 0) \
            || ($r == {[string length $line] - 1})} {
	[set r [string first \r $line]] < 0
	||
	$r == {[string length $line] - 1}
    } {
        return 1
    }

    return 0
}

# ::mime::encodinglineP --
#
#     Checks if a string is a line is valid to be processed.
#
# Arguments:
#       line    The line to check.
#
# Results:
#       Returns 1 the line is less than 76 characters long, the line
#       contains more characters than just whitespace, the line does
#       not start with a '.', and the line does not start with 'From '.

proc ::mime::encodinglineP {line} {
proc ::mime::encodinglineP line {
    if {([string length $line] > 76) \
            || ($line ne [string trimright $line]) \
            || ([string first . $line] == 0) \
            || ([string first {From } $line] == 0)} {
        return 0
    }

    return 1
}

# ::mime::fcopy --
#

#    Appears to be unused.
#
# Arguments:
#
# Results:
#

proc ::mime::fcopy {token count {error {}}} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    if {$error ne {}} {
        set state(error) $error
    }
    set state(doneP) 1
}

# ::mime::scopy --
proc ::mime::parsepart token {
#
#    Copy a portion of the contents of a mime token to a channel.
#
# Arguments:
#    token     The token containing the data to copy.
#       channel   The channel to write the data to.
#       offset    The location in the string to start copying
#                 from.
#       len       The amount of data to write.
#       blocksize The block size for the write operation.
#
# Results:
#    The specified portion of the string in the mime token is
#       copied to the specified channel.

proc ::mime::scopy {token channel offset len blocksize} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    if {$state(canonicalP) || $state(bodyparsed)} {
    if {$len <= 0} {
        set state(doneP) 1
        fileevent $channel writable {}
        return
	return
    }

    if {[set cc $len] > $blocksize} {
        set cc $blocksize
    }

    if {[catch {
        puts -nonewline $channel [
            string range $state(string) $offset [expr {$offset + $cc - 1}]]
        fileevent $channel writable [
            list mime::scopy $token $channel [
                incr offset $cc] [incr len -$cc] $blocksize]
              } result]} {

        set state(error) $result
        set state(doneP) 1
    set state(bodyparsed) 1
        fileevent $channel writable {}
    }
    parsepartaux $token
}
    return
}


# ::mime::qp_encode --
#
#    Tcl version of quote-printable encode
#
# Arguments:
#    string        The string to quote.
#       encoded_word  Boolean value to determine whether or not encoded words
#                     (RFC 2047) should be handled or not. (optional)
#
# Results:
#    The properly quoted string is returned.

proc ::mime::qp_encode {string {encoded_word 0} {no_softbreak 0}} {
    # 8.1+ improved string manipulation routines used.
    # Replace outlying characters, characters that would normally
    # be munged by EBCDIC gateways, and special Tcl characters "[\]{}
    # with =xx sequence

    regsub -all -- \
        {[\x00-\x08\x0B-\x1E\x21-\x24\x3D\x40\x5B-\x5E\x60\x7B-\xFF]} \
        $string {[format =%02X [scan "\\&" %c]]} string
    if {$encoded_word} {
        # Special processing for encoded words (RFC 2047)
        set regexp {[\x00-\x08\x0B-\x1E\x21-\x24\x3D\x40\x5B-\x5E\x60\x7B-\xFF\x09\x5F\x3F]}
	lappend mapChars { } _
    } else {
        set regexp {[\x00-\x08\x0B-\x1E\x21-\x24\x3D\x40\x5B-\x5E\x60\x7B-\xFF]}
    }
    regsub -all -- $regexp $string {[format =%02X [scan "\\&" %c]]} string

    # Replace the format commands with their result

    set string [subst -novariables $string]

    # soft/hard newlines and other
    # Funky cases for SMTP compatibility
    set mapChars [
        list " \n" =20\n \t\n =09\n \n\.\n \=2E\n "\nFrom " "\n=46rom "]
    if {$encoded_word} {
        # Special processing for encoded words (RFC 2047)
        lappend mapChars { } _
    }
    lappend mapChars " \n" =20\n \t\n =09\n \n\.\n =2E\n "\nFrom " "\n=46rom "

    set string [string map $mapChars $string]

    # Break long lines - ugh

    # Implementation of FR #503336
    if {$no_softbreak} {
        set result $string
2517
2518
2519
2520
2521
2522
2523

2524
2525
2526
2527
2528
2529
2530
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295







+







        set result [string replace $result end end =20]
    } elseif {$lastChar eq "\t"} {
        set result [string replace $result end end =09]
    }

    return $result
}


# ::mime::qp_decode --
#
#    Tcl version of quote-printable decode
#
# Arguments:
#    string        The quoted-prinatble string to decode.
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
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







+








-
-
+
+

-
+








    regsub -all -nocase {=([a-f0-9][a-f0-9])} $string {\\u00\1} string

    # process \u unicode mapped chars

    return [subst -novariables -nocommands $string]
}


# ::mime::parseaddress --
#
#       This was originally written circa 1982 in C. we're still using it
#       because it recognizes virtually every buggy address syntax ever
#       generated!
#
#       mime::parseaddress takes a string containing one or more 822-style
#       address specifications and returns a list of serialized arrays, one
#       element for each address specified in the argument.
#       address specifications and returns a list of dictionaries, for each
#       address specified in the argument.
#
#    Each serialized array contains these properties:
#    Each dictionary contains these properties:
#
#       property    value
#       ========    =====
#       address     local@domain
#       comment     822-style comment
#       domain      the domain part (rhs)
#       error       non-empty on a parse error
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
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







-
+


-
+
-
-

-


-


+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-







-
+

+









-
+


















-
+



-
-
+
+



-

















+
-
+
+
+







#
#    Note that one or more of these properties may be empty.
#
# Arguments:
#    string        The address string to parse
#
# Results:
#    Returns a list of serialized arrays, one element for each address
#    Returns a list of dictionaries, one element for each address
#       specified in the argument.

proc ::mime::parseaddress {string} {
proc ::mime::parseaddress {string args} {
    global errorCode errorInfo

    variable mime

    set token [namespace current]::[incr mime(uid)]
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

	if {[llength $args]} {
		set string2 [lindex $args end]
		set args [list $string {*}[lrange $args 0 end-1]]
		set string $string2
	}
	dict for {opt val} $args {
		switch $opt {
			hostname {
				set state(default_host) $val
			}
		}
	}

    set code [catch {mime::parseaddressaux $token $string} result]
    catch {mime::parseaddressaux $token $string} result copts
    set ecode $errorCode
    set einfo $errorInfo

    foreach name [array names state] {
        unset state($name)
    }
    # FRINK: nocheck
    catch {unset $token}

    return -code $code -errorinfo $einfo -errorcode $ecode $result
    return -options $copts $result
}


# ::mime::parseaddressaux --
#
#       This was originally written circa 1982 in C. we're still using it
#       because it recognizes virtually every buggy address syntax ever
#       generated!
#
#       mime::parseaddressaux does the actually parsing for mime::parseaddress
#
#    Each serialized array contains these properties:
#    Each dictionary contains these properties:
#
#       property    value
#       ========    =====
#       address     local@domain
#       comment     822-style comment
#       domain      the domain part (rhs)
#       error       non-empty on a parse error
#       group       this address begins a group
#       friendly    user-friendly rendering
#       local       the local part (lhs)
#       memberP     this address belongs to a group
#       phrase      the phrase part
#       proper      822-style address specification
#       route       822-style route specification (obsolete)
#
#    Note that one or more of these properties may be empty.
#
# Arguments:
#       token         The MIME token to work from.
#    token         The MIME token to work from.
#    string        The address string to parse
#
# Results:
#    Returns a list of serialized arrays, one element for each address
#       specified in the argument.
#    Returns a list of dictionaries, one for each address specified in the
#    argument.

proc ::mime::parseaddressaux {token string} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    variable addrtokenL
    variable addrlexemeL

    set state(input)   $string
    set state(glevel)  0
    set state(buffer)  {}
    set state(lastC)   LX_END
    set state(tokenL)  $addrtokenL
    set state(lexemeL) $addrlexemeL

    set result {}
    while {[addr_next $token]} {
        if {[set tail $state(domain)] ne {}} {
            set tail @$state(domain)
        } else {
			if {![info exists state(default_host)]} {
            set tail @[info hostname]
				set state(default_host) [info hostname]
			}
            set tail @$state(default_host)
        }
        if {[set address $state(local)] ne {}} {
            #TODO: this path is not covered by tests
            append address $tail
        }

        if {$state(phrase) ne {}} {
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
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







+
-
-
+
+
+




-
-
-
+
+
+
+
+
+






+
-
-
+
+
+
+








        if {[set friendly $state(phrase)] eq {}} {
            #TODO: this path is not covered by tests
            if {[set note $state(comment)] ne {}} {
                if {[string first ( $note] == 0} {
                    set note [string trimleft [string range $note 1 end]]
                }
                if {
                if {[string last ) $note] \
                        == [set len [expr {[string length $note] - 1}]]} {
		    [string last ) $note]
                        == [set len [expr {[string length $note] - 1}]]
		} {
                    set note [string range $note 0 [expr {$len - 1}]]
                }
                set friendly $note
            }
            
            if {($friendly eq {}) \
                    && ([set mbox $state(local)] ne {})} {

            if {
		$friendly eq {}
		&&
		[set mbox $state(local)] ne {}
	    } {
                #TODO: this path is not covered by tests
                set mbox [string trim $mbox \"]

                if {[string first / $mbox] != 0} {
                    set friendly $mbox
                } elseif {[set friendly [addr_x400 $mbox PN]] ne {}} {
                } elseif {
                } elseif {([set friendly [addr_x400 $mbox S]] ne {}) \
                    && ([set g [addr_x400 $mbox G]] ne {})} {
		    [set friendly [addr_x400 $mbox S]] ne {}
                    &&
		    [set g [addr_x400 $mbox G]] ne {}
		} {
                    set friendly "$g $friendly"
                }

                if {$friendly eq {}} {
                    set friendly $mbox
                }
            }
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
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







+
-
-
-
-
-
-
+
+
+
+
+
+
+



+











-
+
-

-






-
+



-
+





-
+







-
+








-
-
+
+







-
+






-
-
-
+
-











+















-



-
+







                             memberP  $state(memberP) \
                             phrase   $state(phrase)  \
                             proper   $proper         \
                             route    $state(route)]

    }

    unset {*}{
    unset state(input)   \
          state(glevel)  \
          state(buffer)  \
          state(lastC)   \
          state(tokenL)  \
          state(lexemeL)
	state(input)
	state(glevel)
	state(buffer)
	state(lastC)
	state(tokenL)
	state(lexemeL)
    }

    return $result
}


# ::mime::addr_next --
#
#       Locate the next address in a mime token.
#
# Arguments:
#       token         The MIME token to work from.
#
# Results:
#    Returns 1 if there is another address, and 0 if there is not.

proc ::mime::addr_next {token} {
proc ::mime::addr_next token {
    global errorCode errorInfo
    # FRINK: nocheck
    variable $token
    upvar 0 $token state
    set nocomplain [package vsatisfies [package provide Tcl] 8.4]
    foreach prop {comment domain error group local memberP phrase route} {
        if {$nocomplain} {
            unset -nocomplain state($prop)
        } else {
            if {[catch {unset state($prop)}]} {set ::errorInfo {}}
            catch {unset state($prop)}
        }
    }

    switch -- [set code [catch {mime::addr_specification $token} result]] {
    switch [set code [catch {mime::addr_specification $token} result copts]] {
        0 {
            if {!$result} {
                return 0
            }

            switch -- $state(lastC) {
            switch $state(lastC) {
                LX_COMMA
                    -
                LX_END {
                }
                default {
                    # catch trailing comments...
                    set lookahead $state(input)
                    mime::parselexeme $token
                    parselexeme $token
                    set state(input) $lookahead
                }
            }
        }

        7 {
            set state(error) $result

            while {1} {
                switch -- $state(lastC) {
            while 1 {
                switch $state(lastC) {
                    LX_COMMA
                        -
                    LX_END {
                        break
                    }

                    default {
                        mime::parselexeme $token
                        parselexeme $token
                    }
                }
            }
        }

        default {
            set ecode $errorCode
            set einfo $errorInfo

            return -options $copts $result
            return -code $code -errorinfo $einfo -errorcode $ecode $result
        }
    }

    foreach prop {comment domain error group local memberP phrase route} {
        if {![info exists state($prop)]} {
            set state($prop) {}
        }
    }

    return 1
}


# ::mime::addr_specification --
#
#   Uses lookahead parsing to determine whether there is another
#   valid e-mail address or not.  Throws errors if unrecognized
#   or invalid e-mail address syntax is used.
#
# Arguments:
#       token         The MIME token to work from.
#
# Results:
#    Returns 1 if there is another address, and 0 if there is not.

proc ::mime::addr_specification {token} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    set lookahead $state(input)
    switch -- [parselexeme $token] {
    switch [parselexeme $token] {
        LX_ATOM
            -
        LX_QSTRING {
            set state(phrase) $state(buffer)
        }

        LX_SEMICOLON {
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894



2895
2896
2897
2898

2899
2900
2901
2902
2903
2904
2905
2667
2668
2669
2670
2671
2672
2673



2674
2675
2676
2677
2678
2679

2680
2681
2682
2683
2684
2685
2686
2687







-
-
-
+
+
+



-
+








        LX_ATSIGN {
            set state(input) $lookahead
            return [addr_routeaddr $token 0]
        }

        default {
            return -code 7 \
                   [format "unexpected character at beginning (found %s)" \
                           $state(buffer)]
            return -code 7 [
		format "unexpected character at beginning (found %s)" \
		   $state(buffer)]
        }
    }

    switch -- [parselexeme $token] {
    switch [parselexeme $token] {
        LX_ATOM
            -
        LX_QSTRING {
            append state(phrase) " " $state(buffer)

            return [addr_phrase $token]
        }
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
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







+
-
-
+
+
+
+
















+














-












-
+








        LX_SEMICOLON
            -
        LX_COMMA
            -
        LX_END {
            set state(memberP) $state(glevel)
            if {
            if {($state(lastC) eq "LX_SEMICOLON") \
                    && ([incr state(glevel) -1] < 0)} {
		$state(lastC) eq "LX_SEMICOLON"
		&&
		([incr state(glevel) -1] < 0)
	    } {
                #TODO: this path is not covered by tests
                return -code 7 "extraneous semi-colon"
            }

            set state(local) $state(phrase)
            unset state(phrase)
        }

        default {
            return -code 7 [
                format "expecting mailbox (found %s)" $state(buffer)]
        }
    }

    return 1
}


# ::mime::addr_routeaddr --
#
#       Parses the domain portion of an e-mail address.  Finds the '@'
#       sign and then calls mime::addr_route to verify the domain.
#
# Arguments:
#       token         The MIME token to work from.
#
# Results:
#    Returns 1 if there is another address, and 0 if there is not.

proc ::mime::addr_routeaddr {token {checkP 1}} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    set lookahead $state(input)
    if {[parselexeme $token] eq "LX_ATSIGN"} {
        #TODO: this path is not covered by tests
        mime::addr_route $token
    } else {
        set state(input) $lookahead
    }

    mime::addr_local $token

    switch -- $state(lastC) {
    switch $state(lastC) {
        LX_ATSIGN {
            mime::addr_domain $token
        }

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







+















-




-
-
+
+













-
+


-
-
+
+







    if {($checkP) && ($state(lastC) ne "LX_RBRACKET")} {
        return -code 7 [
            format "expecting right-bracket (found %s)" $state(buffer)]
    }

    return 1
}


# ::mime::addr_route --
#
#    Attempts to parse the portion of the e-mail address after the @.
#    Tries to verify that the domain definition has a valid form.
#
# Arguments:
#       token         The MIME token to work from.
#
# Results:
#    Returns nothing if successful, and throws an error if invalid
#       syntax is found.

proc ::mime::addr_route {token} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    set state(route) @

    while {1} {
        switch -- [parselexeme $token] {
    while 1 {
        switch [parselexeme $token] {
            LX_ATOM
                -
            LX_DLITERAL {
                append state(route) $state(buffer)
            }

            default {
                return -code 7 \
                       [format "expecting sub-route in route-part (found %s)" \
                               $state(buffer)]
            }
        }

        switch -- [parselexeme $token] {
        switch [parselexeme $token] {
            LX_COMMA {
                append state(route) $state(buffer)
                while {1} {
                    switch -- [parselexeme $token] {
                while 1 {
                    switch [parselexeme $token] {
                        LX_COMMA {
                        }

                        LX_ATSIGN {
                            append state(route) $state(buffer)
                            break
                        }
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
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







-
-
-
+
+
+




+













-
+

-


-
-
+
+







-
-
-
+
+
+



-
+















+













-




-
-
+
+













-
+










+














-


-
-
+
+












-
+














-
-
-
+
+
+



+













-



-
-
+
+






-
-
+
+

















+













-


-
+












-
-
+
+



+








            LX_COLON {
                append state(route) $state(buffer)
                return
            }

            default {
                return -code 7 \
                       [format "expecting colon to terminate route (found %s)" \
                               $state(buffer)]
                return -code 7 [
		    format "expecting colon to terminate route (found %s)" \
			$state(buffer)]
            }
        }
    }
}


# ::mime::addr_domain --
#
#    Attempts to parse the portion of the e-mail address after the @.
#    Tries to verify that the domain definition has a valid form.
#
# Arguments:
#       token         The MIME token to work from.
#
# Results:
#    Returns nothing if successful, and throws an error if invalid
#       syntax is found.

proc ::mime::addr_domain {token} {
proc ::mime::addr_domain token {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    while {1} {
        switch -- [parselexeme $token] {
    while 1 {
        switch [parselexeme $token] {
            LX_ATOM
                -
            LX_DLITERAL {
                append state(domain) $state(buffer)
            }

            default {
                return -code 7 \
                       [format "expecting sub-domain in domain-part (found %s)" \
                               $state(buffer)]
                return -code 7 [
		    format "expecting sub-domain in domain-part (found %s)" \
			$state(buffer)]
            }
        }

        switch -- [parselexeme $token] {
        switch [parselexeme $token] {
            LX_DOT {
                append state(domain) $state(buffer)
            }

            LX_ATSIGN {
                append state(local) % $state(domain)
                unset state(domain)
            }

            default {
                return
            }
        }
    }
}


# ::mime::addr_local --
#
#
# Arguments:
#       token         The MIME token to work from.
#
# Results:
#    Returns nothing if successful, and throws an error if invalid
#       syntax is found.

proc ::mime::addr_local {token} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    set state(memberP) $state(glevel)

    while {1} {
        switch -- [parselexeme $token] {
    while 1 {
        switch [parselexeme $token] {
            LX_ATOM
                -
            LX_QSTRING {
                append state(local) $state(buffer)
            }

            default {
                return -code 7 \
                       [format "expecting mailbox in local-part (found %s)" \
                               $state(buffer)]
            }
        }

        switch -- [parselexeme $token] {
        switch [parselexeme $token] {
            LX_DOT {
                append state(local) $state(buffer)
            }

            default {
                return
            }
        }
    }
}


# ::mime::addr_phrase --
#
#
# Arguments:
#       token         The MIME token to work from.
#
# Results:
#    Returns nothing if successful, and throws an error if invalid
#       syntax is found.


proc ::mime::addr_phrase {token} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    while {1} {
        switch -- [parselexeme $token] {
    while 1 {
        switch [parselexeme $token] {
            LX_ATOM
                -
            LX_QSTRING {
                append state(phrase) " " $state(buffer)
            }

            default {
                break
            }
        }
    }

    switch -- $state(lastC) {
    switch $state(lastC) {
        LX_LBRACKET {
            return [addr_routeaddr $token]
        }

        LX_COLON {
            return [addr_group $token]
        }

        LX_DOT {
            append state(phrase) $state(buffer)
            return [addr_phrase $token]
        }

        default {
            return -code 7 \
                   [format "found phrase instead of mailbox (%s%s)" \
                           $state(phrase) $state(buffer)]
            return -code 7 [
		format "found phrase instead of mailbox (%s%s)" \
		    $state(phrase) $state(buffer)]
        }
    }
}


# ::mime::addr_group --
#
#
# Arguments:
#       token         The MIME token to work from.
#
# Results:
#    Returns nothing if successful, and throws an error if invalid
#       syntax is found.

proc ::mime::addr_group {token} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    if {[incr state(glevel)] > 1} {
        return -code 7 [format "nested groups not allowed (found %s)" \
                               $state(phrase)]
        return -code 7 [
	    format "nested groups not allowed (found %s)" $state(phrase)]
    }

    set state(group) $state(phrase)
    unset state(phrase)

    set lookahead $state(input)
    while {1} {
        switch -- [parselexeme $token] {
    while 1 {
        switch [parselexeme $token] {
            LX_SEMICOLON
                -
            LX_END {
                set state(glevel) 0
                return 1
            }

            LX_COMMA {
            }

            default {
                set state(input) $lookahead
                return [addr_specification $token]
            }
        }
    }
}


# ::mime::addr_end --
#
#
# Arguments:
#       token         The MIME token to work from.
#
# Results:
#    Returns nothing if successful, and throws an error if invalid
#       syntax is found.

proc ::mime::addr_end {token} {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    switch -- $state(lastC) {
    switch $state(lastC) {
        LX_SEMICOLON {
            if {[incr state(glevel) -1] < 0} {
                return -code 7 "extraneous semi-colon"
            }
        }

        LX_COMMA
            -
        LX_END {
        }

        default {
            return -code 7 [format "junk after local@domain (found %s)" \
                                   $state(buffer)]
            return -code 7 [
		format "junk after local@domain (found %s)" $state(buffer)]
        }
    }
}


# ::mime::addr_x400 --
#
#
# Arguments:
#       token         The MIME token to work from.
#
3325
3326
3327
3328
3329
3330
3331

3332

3333
3334
3335
3336
3337

3338
3339
3340
3341
3342
3343
3344
3111
3112
3113
3114
3115
3116
3117
3118

3119
3120
3121
3122
3123

3124
3125
3126
3127
3128
3129
3130
3131







+
-
+




-
+







    if {[set x [string first / $mbox]] > 0} {
        set mbox [string range $mbox 0 [expr {$x - 1}]]
    }

    return [string trim $mbox \"]
}


# ::mime::parsedatetime --
# ::mime::datetime --
#
#    Fortunately the clock command in the Tcl 8.x core does all the heavy
#    lifting for us (except for timezone calculations).
#
#    mime::parsedatetime takes a string containing an 822-style date-time
#    mime::datetime takes a string containing an 822-style date-time
#    specification and returns the specified property.
#
#    The list of properties and their ranges are:
#
#       property     range
#       ========     =====
#       clock        raw result of "clock scan"
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
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







-
+



-
+
+














-
+







        variable MONTHS_SHORT [list {} \
                                    Jan Feb Mar Apr May Jun \
                                    Jul Aug Sep Oct Nov Dec]
        variable MONTHS_LONG  [list {} \
                                    January February March April May June July \
                                    August Sepember October November December]
}
proc ::mime::parsedatetime {value property} {
proc ::mime::datetime {value property} {
    if {$value eq "-now"} {
        set clock [clock seconds]
    } elseif {[regexp {^(.*) ([+-])([0-9][0-9])([0-9][0-9])$} $value \
            -> value zone_sign zone_hour zone_min]} {
	-> value zone_sign zone_hour zone_min]
    } {
        set clock [clock scan $value -gmt 1]
        if {[info exists zone_min]} {
            set zone_min [scan $zone_min %d]
            set zone_hour [scan $zone_hour %d]
            set zone [expr {60 * ($zone_min + 60 * $zone_hour)}]
            if {$zone_sign eq "+"} {
                set zone -$zone
            }
            incr clock $zone
        }
    } else {
        set clock [clock scan $value]
    }

    switch -- $property {
    switch $property {
        clock {
            return $clock
        }

        hour {
            set value [clock format $clock -format %H]
        }
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
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







-
-
+
+



-
+
-












-
-
+
+

-
-
+
+








        mon {
            set value [clock format $clock -format %m]
        }

        month {
            variable MONTHS_SHORT
            return [lindex $MONTHS_SHORT \
                            [scan [clock format $clock -format %m] %d]]
            return [lindex $MONTHS_SHORT [
		scan [clock format $clock -format %m] %d]]
        }

        proper {
            set gmt [clock format $clock -format "%Y-%m-%d %H:%M:%S" \
            set gmt [clock format $clock -format "%Y-%m-%d %H:%M:%S" -gmt true]
                           -gmt true]
            if {[set diff [expr {($clock-[clock scan $gmt]) / 60}]] < 0} {
                set s -
                set diff [expr {-($diff)}]
            } else {
                set s +
            }
            set zone [format %s%02d%02d $s [
                expr {$diff / 60}] [expr {$diff % 60}]]

            variable WDAYS_SHORT
            set wday [lindex $WDAYS_SHORT [clock format $clock -format %w]]
            variable MONTHS_SHORT
            set mon [lindex $MONTHS_SHORT \
                [scan [clock format $clock -format %m] %d]]
            set mon [lindex $MONTHS_SHORT [
		scan [clock format $clock -format %m] %d]]

            return [clock format $clock \
                -format "$wday, %d $mon %Y %H:%M:%S $zone"]
            return [
		clock format $clock -format "$wday, %d $mon %Y %H:%M:%S $zone"]
        }

        rclock {
            #TODO: these paths are not covered by tests
            if {$value eq "-now"} {
                return 0
            } else {
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
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







-
+






+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+








        zone {
            set value [string trim [string map [list \t { }] $value]]
            if {[set x [string last { } $value]] < 0} {
                return 0
            }
            set value [string range $value [expr {$x + 1}] end]
            switch -- [set s [string index $value 0]] {
            switch [set s [string index $value 0]] {
                + - - {
                    if {$s eq "+"} {
                        #TODO: This path is not covered by tests
                        set s {}
                    }
                    set value [string trim [string range $value 1 end]]
                    if {(
                    if {([string length $value] != 4) \
                            || ([scan $value %2d%2d h m] != 2) \
                            || ($h > 12) \
                            || ($m > 59) \
                            || (($h == 12) && ($m > 0))} {
			    [string length $value] != 4)
			||
			    [scan $value %2d%2d h m] != 2
			||
			    $h > 12
			||
			    $m > 59
			||
			    ($h == 12 && $m > 0)
		    } {
                        error "malformed timezone-specification: $value"
                    }
                    set value $s[expr {$h * 60 + $m}]
                }

                default {
                    set value [string toupper $value]
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
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







+














-
-
+
+
+
+
-
-
+











-
+

-








    if {[set value [string trimleft $value 0]] eq {}} {
        #TODO: this path is not covered by tests
        set value 0
    }
    return $value
}


# ::mime::uniqueID --
#
#    Used to generate a 'globally unique identifier' for the content-id.
#    The id is built from the pid, the current time, the hostname, and
#    a counter that is incremented each time a message is sent.
#
# Arguments:
#
# Results:
#    Returns the a string that contains the globally unique identifier
#       that should be used for the Content-ID of an e-mail message.

proc ::mime::uniqueID {} {
    variable mime

    set id [base64 -mode encode -- [
	sha2::sha256 -bin [expr {rand()}][pid][clock clicks][array get state]]]
    return $id
}
    return "<[pid].[clock seconds].[incr mime(cid)]@[info hostname]>"
}


# ::mime::parselexeme --
#
#    Used to implement a lookahead parser.
#
# Arguments:
#       token    The MIME token to operate on.
#
# Results:
#    Returns the next token found by the parser.

proc ::mime::parselexeme {token} {
proc ::mime::parselexeme token {
    # FRINK: nocheck
    variable $token
    upvar 0 $token state

    set state(input) [string trimleft $state(input)]

    set state(buffer) {}
    if {$state(input) eq {}} {
        set state(buffer) end-of-input
3597
3598
3599
3600
3601
3602
3603
3604

3605
3606
3607
3608
3609
3610
3611
3391
3392
3393
3394
3395
3396
3397

3398
3399
3400
3401
3402
3403
3404
3405







-
+







        set noteP 0
        set quoteP 0

        while 1 {
            append state(buffer) $c

            #TODO: some of these paths are not covered by tests
            switch -- $c/$quoteP {
            switch $c/$quoteP {
                (/0 {
                    incr noteP
                }

                \\/0 {
                    set quoteP 1
                }
3637
3638
3639
3640
3641
3642
3643
3644

3645
3646
3647
3648
3649
3650
3651
3431
3432
3433
3434
3435
3436
3437

3438
3439
3440
3441
3442
3443
3444
3445







-
+







    if {$c eq "\""} {
        set firstP 1
        set quoteP 0

        while 1 {
            append state(buffer) $c

            switch -- $c/$quoteP {
            switch $c/$quoteP {
                "\\/0" {
                    set quoteP 1
                }

                "\"/0" {
                    if {!$firstP} {
                        return [set state(lastC) LX_QSTRING]
3668
3669
3670
3671
3672
3673
3674
3675

3676
3677
3678
3679
3680
3681
3682
3462
3463
3464
3465
3466
3467
3468

3469
3470
3471
3472
3473
3474
3475
3476







-
+








    if {$c eq {[}} {
        set quoteP 0

        while 1 {
            append state(buffer) $c

            switch -- $c/$quoteP {
            switch $c/$quoteP {
                \\/0 {
                    set quoteP 1
                }

                ]/0 {
                    return [set state(lastC) LX_DLITERAL]
                }
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
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







-



-
+


-
+
















+







            }
            set state(input) [string range $state(input) 1 end]
        }
    }

    if {[set x [lsearch -exact $state(tokenL) $c]] >= 0} {
        append state(buffer) $c

        return [set state(lastC) [lindex $state(lexemeL) $x]]
    }

    while {1} {
    while 1 {
        append state(buffer) $c

        switch -- [set c [string index $state(input) 0]] {
        switch [set c [string index $state(input) 0]] {
            {} - " " - "\t" - "\n" {
                break
            }

            default {
                if {[lsearch -exact $state(tokenL) $c] >= 0} {
                    break
                }
            }
        }

        set state(input) [string range $state(input) 1 end]
    }

    return [set state(lastC) LX_ATOM]
}


# ::mime::mapencoding --
#
#    mime::mapencodings maps tcl encodings onto the proper names for their
#    MIME charset type.  This is only done for encodings whose charset types
#    were known.  The remaining encodings return {} for now.
#
3739
3740
3741
3742
3743
3744
3745



























































































3746
3747
3748
3749
3750
3751
3752
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    variable encodings

    if {[info exists encodings($enc)]} {
        return $encodings($enc)
    }
    return {}
}


# ::mime::property --
#
#   mime::property returns the properties of a MIME part.
#
#   The properties are:
#
#       property    value
#       ========    =====
#       content     the type/subtype describing the content
#       encoding    the "Content-Transfer-Encoding"
#       params      a list of "Content-Type" parameters
#       parts       a list of tokens for the part's subordinates
#       size        the approximate size of the content (unencoded)
#
#   The "parts" property is present only if the MIME part has
#   subordinates.
#
#   If mime::property is invoked with the name of a specific
#   property, then the corresponding value is returned; instead, if
#   -names is specified, a list of all properties is returned;
#   otherwise, a dictionary of properties is returned.
#
# Arguments:
#       token      The MIME token to parse.
#       property   One of 'content', 'encoding', 'params', 'parts', and
#                  'size'. Defaults to returning a dictionary of
#                  properties.
#
# Results:
#       Returns the properties of a MIME part

proc ::mime::property {token {property {}}} {
    # FRINK: nocheck
    upvar 0 $token state
    parsepart $token


    lassign [header get $token content-type] content params

    switch $property {
        {} {
            array set properties [list content  $content \
                                       encoding $state(encoding) \
                                       params   $params \
                                       size     [getsize $token]]
            if {[info exists state(parts)]} {
                set properties(parts) $state(parts)
            }

            return [array get properties]
        }

        -names {
            set names [list content encoding params]
            if {[info exists state(parts)]} {
                lappend names parts
            }
	    lappend nams size

            return $names
        }

        content
            -
        params {
	    return [set $property]
        }

        encoding {
            return $state($property)
	}
        parts {
            if {![info exists state(parts)]} {
                error [list not a multipart message]
            }

            return $state(parts)
        }

        size {
            return [getsize $token]
        }

        default {
            error [list {unknown property} $property]
        }
    }
}


# ::mime::reversemapencoding --
#
#    mime::reversemapencodings maps MIME charset types onto tcl encoding names.
#    Those that are unknown return {}.
#
# Arguments:
3763
3764
3765
3766
3767
3768
3769





































































































































































































3770
3771
3772
3773
3774
3775
3776
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    set lmimeType [string tolower $mimeType]
    if {[info exists reversemap($lmimeType)]} {
        return $reversemap($lmimeType)
    }
    return {}
}


# ::mime::serialize --
#
#    Serializes a message to a value or a channel.
#
# Arguments:
#       token      The MIME token to parse.
#       channel    The channel to copy the message to.
#
# Results:
#       Returns nothing unless an error is thrown while the message
#       is being written to the channel.


proc ::mime::serialize {token args} {
    dict for {arg val} $args {
	switch $arg {
	    -chan {
		return [serialize_chan $token $val]
	    }
	    default {
		error [list {unknown option} $arg]
	    }
	}
    }

    # FRINK: nocheck
    upvar 0 $token state

    set openP [info exists state(fd)]

    set code [catch {mime::serialize_value $token} result copts]

    if {!$openP && [info exists state(fd)]} {
        if {![info exists state(root)]} {
            catch {close $state(fd)}
        }
        unset state(fd)
    }
    return -options $copts $result
}


proc ::mime::serialize_chan {token channel} {
    # FRINK: nocheck
    upvar 0 $token state
    upvar 0 state(fd) fd
    parsepart $token

    set result {}
    foreach {mixed value} [header get $token] {
	puts $channel [header serialize $token $mixed {*}$value]
    }

    set converter {}
    set encoding {}
    if {$state(value) ne "parts"} {
        if {$state(canonicalP)} {
            if {[set encoding $state(encoding)] eq {}} {
                set encoding [encoding $token]
            }
            if {$encoding ne {}} {
                puts $channel "Content-Transfer-Encoding: $encoding"
            }
            switch $encoding {
                base64
                    -
                quoted-printable {
                    set converter $encoding
                }
                7bit - 8bit - binary - {} {
                    # Bugfix for [#477088], also [#539952]
                    # Go ahead
                }
                default {
                    error "Can't handle content encoding \"$encoding\""
                }
            }
        }
    }

    if {[info exists state(error)]} {
        unset state(error)
    }

    switch $state(value) {
        file {
            if {[info exists state(root)]} {
                set size $state(count)
            } else {
                # read until eof
                set size -1
            }
            seek $fd $state(offset) start

            puts $channel {}

            while {$size != 0 && ![eof $fd]} {
                if {$size < 0 || $size > 32766} {
                    set X [read $fd 32766]
                } else {
                    set X [read $fd $size]
                }
                if {$size > 0} {
                    set size [expr {$size - [string length $X]}]
                }
                if {$converter eq {}} {
                    puts -nonewline $channel $X
                } else {
                    puts -nonewline $channel [$converter -mode encode -- $X]
                }
            }
        }

        parts {
	    lassign [header get $token content-type] content params
	    set boundary [dict get $params boundary]

            switch -glob $content {
                message/* {
                    puts $channel {}
                    foreach part $state(parts) {
                        mime::serialize_chan $part $channel
                        break
                    }
                }

                default {
                    # Note RFC 2046: See serialize_value for details.
                    #
                    # The boundary delimiter MUST occur at the
                    # beginning of a line, i.e., following a CRLF, and
                    # the initial CRLF is considered to be attached to
                    # the boundary delimiter line rather than part of
                    # the preceding part.
                    #
                    # - The above means that the CRLF before $boundary
                    #   is needed per the RFC, and the parts must not
                    #   have a closing CRLF of their own. See Tcllib bug
                    #   1213527, and patch 1254934 for the problems when
                    #   both file/string branches added CRLF after the
                    #   body parts.


                    foreach part $state(parts) {
                        puts $channel \n--$boundary
                        mime::serialize_chan $part $channel
                    }
                    puts $channel \n--$boundary--
                }
            }
        }

        string {
            if {[catch {fconfigure $channel -buffersize} blocksize]} {
                set blocksize 4096
            } elseif {$blocksize < 512} {
                set blocksize 512
            }
            set blocksize [expr {($blocksize / 4) * 3}]

            # [893516]
            fconfigure $channel -buffersize $blocksize

            puts $channel {}

            #TODO: tests don't cover these paths
            if {$converter eq {}} {
                puts -nonewline $channel $state(string)
            } else {
                puts -nonewline $channel [$converter -mode encode -- $state(string)]
            }
        }
        default {
            error "Unknown value \"$state(value)\""
        }
    }

    flush $channel

    if {[info exists state(error)]} {
        error $state(error)
    }
}


proc ::mime::serialize_value token {
    set chan [tcl::chan::memchan]
    chan configure $chan -translation crlf
    serialize_chan $token $chan
    seek $chan 0
    chan configure $chan -translation binary
    set res [read $chan]
    close $chan
    return $res
}

# ::mime::word_encode --
#
#    Word encodes strings as per RFC 2047.
#
# Arguments:
#       charset   The character set to encode the message to.
#       method    The encoding method (base64 or quoted-printable).
3784
3785
3786
3787
3788
3789
3790
3791

3792
3793
3794
3795

3796
3797
3798

3799

3800
3801
3802
3803
3804
3805
3806
3866
3867
3868
3869
3870
3871
3872

3873
3874
3875
3876

3877
3878
3879
3880
3881

3882
3883
3884
3885
3886
3887
3888
3889







-
+



-
+



+
-
+







#    Returns a word encoded string.

proc ::mime::word_encode {charset method string {args}} {

    variable encodings

    if {![info exists encodings($charset)]} {
        error "unknown charset '$charset'"
        error [list {unknown charset} $charset]
    }

    if {$encodings($charset) eq {}} {
        error "invalid charset '$charset'"
        error [list {invalid charset} $charset]
    }

    if {$method ne "base64" && $method ne "quoted-printable"} {
        error [list {unknown method} $method {must be one of} \
        error "unknown method '$method', must be base64 or quoted-printable"
	    {base64 quoted-printable}]
    }

    # default to encoded and a length that won't make the Subject header to long
    array set options [list -charset_encoded 1 -maxlength 66]
    array set options $args

    if {$options(-charset_encoded)} {
3815
3816
3817
3818
3819
3820
3821
3822

3823
3824

3825

3826
3827
3828
3829
3830
3831
3832

3833
3834
3835

3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853

3854

3855
3856
3857
3858
3859
3860

3861
3862
3863
3864
3865

3866

3867
3868
3869
3870



3871
3872
3873
3874
3875
3876
3877
3898
3899
3900
3901
3902
3903
3904

3905
3906
3907
3908

3909
3910
3911
3912
3913
3914
3915

3916
3917
3918

3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938

3939
3940
3941
3942
3943
3944

3945
3946
3947
3948
3949
3950
3951

3952
3953



3954
3955
3956
3957
3958
3959
3960
3961
3962
3963







-
+


+
-
+






-
+


-
+


















+
-
+





-
+





+
-
+

-
-
-
+
+
+







        return {}
    }

    set string_bytelength [string bytelength $unencoded_string]

    # the 7 is for =?, ?Q?, ?= delimiters of the encoded word
    set maxlength [expr {$options(-maxlength) - [string length $encodings($charset)] - 7}]
    switch -exact -- $method {
    switch -exact $method {
        base64 {
            if {$maxlength < 4} {
                error [list maxlength $options(-maxlength) \
                error "maxlength $options(-maxlength) too short for chosen charset and encoding"
		    {too short for chosen charset and encoding}]
            }
            set count 0
            set maxlength [expr {($maxlength / 4) * 3}]
            while {$count < $string_length} {
                set length 0
                set enc_string {}
                while {($length < $maxlength) && ($count < $string_length)} {
                while {$length < $maxlength && $count < $string_length} {
                    set char [string range $unencoded_string $count $count]
                    set enc_char [::encoding convertto $charset $char]
                    if {($length + [string length $enc_char]) > $maxlength} {
                    if {$length + [string length $enc_char] > $maxlength} {
                        set length $maxlength
                    } else {
                        append enc_string $enc_char
                        incr count
                        incr length [string length $enc_char]
                    }
                }
                set encoded_word [string map [
                    list \n {}] [base64 -mode encode -- $enc_string]]
                append result "=?$encodings($charset)?B?$encoded_word?=\n "
            }
            # Trim off last "\n ", since the above code has the side-effect
            # of adding an extra "\n " to the encoded string.

            set result [string range $result 0 end-2]
        }
        quoted-printable {
            if {$maxlength < 1} {
                error [list maxlength $options(-maxlength) \
                error "maxlength $options(-maxlength) too short for chosen charset and encoding"
		    {too short for chosen charset and encoding}]
            }
            set count 0
            while {$count < $string_length} {
                set length 0
                set encoded_word {}
                while {($length < $maxlength) && ($count < $string_length)} {
                while {$length < $maxlength && $count < $string_length} {
                    set char [string range $unencoded_string $count $count]
                    set enc_char [::encoding convertto $charset $char]
                    set qp_enc_char [qp_encode $enc_char 1]
                    set qp_enc_char_length [string length $qp_enc_char]
                    if {$qp_enc_char_length > $maxlength} {
                        error [list maxlength $options(-maxlength) \
                        error "maxlength $options(-maxlength) too short for chosen charset and encoding"
			    {too short for chosen charset and encoding}]
                    }
                    if {($length + [
                        string length $qp_enc_char]) > $maxlength} {

                    if {
			$length + [string length $qp_enc_char] > $maxlength
		    } {
                        set length $maxlength
                    } else {
                        append encoded_word $qp_enc_char
                        incr count
                        incr length [string length $qp_enc_char]
                    }
                }
3887
3888
3889
3890
3891
3892
3893

3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910


3911
3912
3913
3914
3915
3916
3917
3918
3919

3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933

3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949

3950
3951
3952
3953
3954
3955
3956
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996

3997
3998
3999
4000
4001
4002
4003
4004
4005
4006

4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020

4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045







+
















-
+
+








-
+













-
+
















+







        }
        default {
            error "Can't handle content encoding \"$method\""
        }
    }
    return $result
}


# ::mime::word_decode --
#
#    Word decodes strings that have been word encoded as per RFC 2047.
#
# Arguments:
#       encoded   The word encoded string to decode.
#
# Results:
#    Returns the string that has been decoded from the encoded message.

proc ::mime::word_decode {encoded} {

    variable reversemap

    if {[regexp -- {=\?([^?]+)\?(.)\?([^?]*)\?=} $encoded \
                - charset method string] != 1} {
	- charset method string] != 1
    } {
        error "malformed word-encoded expression '$encoded'"
    }

    set enc [reversemapencoding $charset]
    if {$enc eq {}} {
        error "unknown charset '$charset'"
    }

    switch -exact -- $method {
    switch -exact $method {
        b -
        B {
            set method base64
        }
        q -
        Q {
            set method quoted-printable
        }
        default {
            error "unknown method '$method', must be B or Q"
        }
    }

    switch -exact -- $method {
    switch -exact $method {
        base64 {
            set result [base64 -mode decode -- $string]
        }
        quoted-printable {
            set result [qp_decode $string 1]
        }
        {} {
            # Go ahead
        }
        default {
            error "Can't handle content encoding \"$method\""
        }
    }

    return [list $enc $method $result]
}


# ::mime::field_decode --
#
#    Word decodes strings that have been word encoded as per RFC 2047
#    and converts the string from the original encoding/charset to UTF.
#
# Arguments:
3967
3968
3969
3970
3971
3972
3973
3974



3975
3976

3977
3978
3979
3980
3981
3982
3983
3984
3985
3986

3987
3988
3989



















3990
3991
3992
3993
3994
3995
3996
4056
4057
4058
4059
4060
4061
4062

4063
4064
4065
4066

4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107







-
+
+
+

-
+










+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    # version with unencoded equivalents.

    # Sorry about the grotesque regexp.  Most of it is sensible.  One
    # notable fudge: the final $ is needed because of an apparent bug
    # in the regexp engine where the preceding .* otherwise becomes
    # non-greedy - perhaps because of the earlier ".*?", sigh.

    while {[regexp {(.*?)(=\?(?:[^?]+)\?(?:.)\?(?:[^?]*)\?=)(.*)$} $field ignore prefix encoded field]} {
    while {[regexp {(.*?)(=\?(?:[^?]+)\?(?:.)\?(?:[^?]*)\?=)(.*)$} $field \
	ignore prefix encoded field]
    } {
        # don't allow whitespace between encoded words per RFC 2047
        if {{} != $prefix} {
        if {{} ne $prefix} {
            if {![string is space $prefix]} {
                append result $prefix
            }
        }

        set decoded [word_decode $encoded]
        foreach {charset - string} $decoded break

        append result [::encoding convertfrom $charset $string]
    }

    append result $field
    return $result
}

namespace eval ::mime::header {
    ::apply [list {} {
	set saved [namespace eval [namespace parent] {
	    namespace export
	}]
	namespace eval [namespace parent] {
	    namespace export *
	}
	namespace import [namespace parent]::getTransferEncoding
	namespace import [namespace parent]::parselexeme
	namespace import [namespace parent]::reversemapencoding
	namespace import [namespace parent]::uniqueID
	namespace eval [namespace parent] [
	    list namespace export -clear {*}$saved
	]
    } [namespace current]]
}


## One-Shot Initialization

::apply {{} {
    variable encList
    variable encAliasList
    variable reversemap

Changes to modules/mime/mime.test.

1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16


17
18
19

20
21
22
23
24


25
26
27
28
29
30
31
32
33
34
35









































36

37
38
39

40

41
42
43





44
45
46
47
48
49
50

51
















52
53
54



55
56

57
58

59
60

61

62




63



64


























65
66
67




68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83


84

85

86
87
88
89
90
91
92


93

94

95
96
97
98
99
100
101
1
2
3
4

5
6
7
8
9
10
11
12
13
14


15
16
17
18

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

79
80
81

82
83
84



85
86
87
88
89
90
91
92
93
94
95

96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113



114
115
116
117

118


119
120

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177


178
179
180
181

182
183
184
185
186
187


188
189
190
191

192
193
194
195
196
197
198
199




-
+









-
-
+
+


-
+





+
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+


-
+

+
-
-
-
+
+
+
+
+






-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+

-
+
-
-
+

-
+

+

+
+
+
+

+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+














-
-
+
+

+
-
+





-
-
+
+

+
-
+







# mime.test - Test suite for TclMIME                     -*- tcl -*-
#
# This file contains a collection of tests for one or more of the Tcl
# built-in commands.  Sourcing this file into Tcl runs the tests and
# generates output for errors.  No output means no errors were found.
# genere totes output for errors.  No output means no errors were found.
#
# Copyright (c) 2000 by Ajuba Solutions
# All rights reserved.
#
# RCS: @(#) $Id: mime.test,v 1.31 2012/02/23 17:35:17 andreas_kupries Exp $

# -------------------------------------------------------------------------

source [file join \
	[file dirname [file dirname [file join [pwd] [info script]]]] \
	devtools testutilities.tcl]
	[file dirname [file dirname [file dirname [
	    file normalize [info script]/...]]]]/devtools/testutilities.tcl]

testsNeedTcl     8.5
testsNeedTcltest 1.0
testsNeedTcltest 1.0-

support {
    # This code loads md5x, i.e. md5 v2. Proper testing should do one
    # run using md5 v1, aka md5.tcl as well.
    use md5/md5x.tcl md5

    use namespacex/namespacex.tcl namespacex
}
testing {
    useLocal mime.tcl mime
}

# -------------------------------------------------------------------------

namespace import mime::*

# -------------------------------------------------------------------------



proc channamescmp names {
    expr {[llength $names] == [llength [chan names]]}
}

proc cleanly script {
    set ns [info cmdcount]
    namespace eval $ns {
	namespace path [namespace parent]
    }
    catch {namespace eval $ns $script} cres copts
    namespace delete $ns
    return -options $copts $cres
}

proc setup1 {} {
    uplevel 1 {
	set channames [chan names]
    }
}

proc with.chan {name args} {
    set body [lindex $args end]
    set args [lrange $args 0 end-1]
    set chan [open $name]
    uplevel 1 [list set tok [::mime::initialize {*}$args -chan $chan]]
    uplevel 1 $body
}

proc with.file {name args} {
    set body [lindex $args end]
    set args [lrange $args 0 end-1]
    uplevel 1 [list set tok [::mime::initialize {*}$args -file $name]]
    uplevel 1 $body
}

proc main {} {
variable encoded
variable name
variable n
test mime-1.1 {initialize with no args} {
test mime-1.1 {initialize with no args} {cleanly {
    catch {initialize} res
    subst $res
} {specify exactly one of -file, -parts, or -string}
}} {{specify exactly one of} {-file -parts -string}}


test mime-2.1 {Generate a MIME message} {
    set tok [initialize -canonical "Text/plain" -string "jack and jill"]
    set msg [mime::buildmessage $tok]
test mime-2.1 {Generate a MIME message} {cleanly {
    
    set tok [initialize -canonical Text/plain -string {jack and jill}]
    set msg [mime::serialize $tok]

    # The generated message is predictable except for the Content-ID
    regexp "MIME-Version: 1.0\r
Content-ID: \[^\n]+\r
Content-Type: text/plain\r
\r
jack and jill" $msg
} 1
}} 1

foreach name {file chan} {
    test mime-2.1.1.$name {Generate a MIME message} {cleanly {
	setup1
	with.$name [makeFile {jack and jill} input.txt] -canonical Text/plain {
	    set msg [mime::body $tok]
	    mime::finalize $tok

	    # The generated message is predictable except for the Content-ID
	    lappend res $msg
	    lappend res [channamescmp $channames]
	    return $res
	}
    }} [list "jack and jill\n" 1]
}


test mime-2.2 {Generate a multi-part MIME message} {
    set tok1 [initialize -canonical "Text/plain" -string "jack and jill"]
    set tok2 [initialize -canonical "Text/plain" -string "james"]
test mime-2.2 {Generate a multi-part MIME message} {cleanly {
    set tok1 [initialize -canonical Text/plain -string {jack and jill}]
    set tok2 [initialize -canonical Text/plain -string james]
    set bigTok [mime::initialize -canonical Multipart/MyType \
	    -param [list MyParam foo] \
	    -params [list MyParam foo boundary bndry] \
	    -param [list boundary bndry] \
	    -header [list Content-Description "Test Multipart"] \
	    -headers [list Content-Description {Test Multipart}] \
	    -parts [list $tok1 $tok2]]
    set msg [mime::buildmessage $bigTok]
    set msg [mime::serialize $bigTok]
    # The generated message is predictable except for the Content-ID

    list [regexp "MIME-Version: 1.0\r
Content-ID: \[^\n]+\r
Content-Type: multipart/mytype\r
\t; \[^\n]+\r
\t; \[^\n]+\r
Content-Description: Test Multipart\r
\r
--bndry\r
MIME-Version: 1.0\r
Content-ID: \[^\n]+\r
Content-Type: text/plain\r
\r
jack and jill\r
--bndry\r
MIME-Version: 1.0\r
Content-ID: \[^\n]+\r
Content-Type: text/plain\r
\r
james\r
--bndry--\r
" $msg] [regexp boundary=bndry $msg] [regexp myparam=foo $msg]
}} {1 1 1}



test mime-2.3 {Generate a multi-part MIME message} {cleanly {
    set tok1 [initialize -canonical Text/plain -string {jack and jill}]
    set tok2 [initialize -canonical Text/plain -string james]
    set bigTok [mime::initialize \
	    -params [list MyParam foo boundary bndry] \
	    -headers [list Content-Description {Test Multipart}] \
	    -parts [list $tok1 $tok2]]
    set msg [mime::serialize $bigTok]
    # The generated message is predictable except for the Content-ID
    list [regexp "MIME-Version: 1.0\r
Content-ID: \[^\n]+\r
Content-Type: multipart/mytype;\r
              \[^\n]+;\r
              \[^\n]+\r
Content-Type: multipart/mixed\r
	; \[^\n]+\r
	; \[^\n]+\r
Content-Description: Test Multipart\r
\r
--bndry\r
MIME-Version: 1.0\r
Content-ID: \[^\n]+\r
Content-Type: text/plain\r
\r
jack and jill\r
--bndry\r
MIME-Version: 1.0\r
Content-ID: \[^\n]+\r
Content-Type: text/plain\r
\r
james\r
--bndry--\r
" $msg] [regexp "boundary=\"bndry\"" $msg] [regexp "myparam=\"foo\"" $msg]
} {1 1 1}
" $msg] [regexp boundary=bndry $msg] [regexp myparam=foo $msg]
}} {1 1 1}


test mime-3.1 {Parse a MIME message} {
test mime-3.1 {Parse a MIME message} {cleanly {
    set msg {MIME-Version: 1.0
Content-Type: Text/plain

I'm the message.}
    set tok [mime::initialize -string $msg]
    mime::getbody $tok
} "I'm the message."
    mime::body $tok
}} {I'm the message.}


test mime-3.2 {Parse a multi-part MIME message} {
test mime-3.2 {Parse a multi-part MIME message} {cleanly {
    set msg {MIME-Version: 1.0
Content-Type: Multipart/foo; boundary="bar"

--bar
MIME-Version: 1.0
Content-Type: Text/plain

110
111
112
113
114
115
116
117

118
119

120
121

122
123
124

125

126
127


128
129

130

131

132
133
134
135
136
137
138
139


140
141

142
143
144
145

146

147
148
149
150
151


152

153

154
155

156
157
158
159
160
161
162
163
164


165
166

167
168
169
170


171

172
173

174
175
176
177
178
179
180
181








182
183

184
185
186
187
188
189
190
191
192
193
194















195
196
197
198
199
200
201
202


203
204

205

206
207
208
209
210
211
212
213


214

215

216
217
218
219
220
221
222
223


224

225

226
227
228
229
230
231
232

233
234

235

236

237
238
239
240
241
242
243
244


245

246

247
248
249

250

251

252
253
254
255
256

257

258

259
260
261

262

263
264


265
266

267
268
269

270
271
272

273
274

275

276






277
278



279



280



281
282
283
284
285




286
287
288
289




290
291
292
293
294
295
296
297
298
299



















300

301
302
303




304
305
306
307





308
309
310


311
312
313


314













315
316
317



318
319
320

321

322
323

324
325
326
327
328
329



330
331
332
333

334

335
336

337
338
339
340
341
342



343
344
345
346
347
348
349
350



351
352
353
354
355
356
357



358
359
360
361
362
363
364
365



366
367
368
369

370
371
372



373

374
375
376



377

378
379
380



381

382
383
384



385

386
387
388



389

390
391
392



393

394
395


396
397

398

399
400


401
402

403

404
405


406

407

408
409
410


411
412

413

414
415


416
417

418

419
420


421
422

423
424
425
426


427
428

429

430
431


432
433

434

435

436
437

438

439

440
441

442

443

444
445
446

447
448
449
450
451
452
453
454
455
456








457
458
459
460
461
462
463
464
465
466
467
468










469

470
471

472
473

474
475
476
477

478
479

480
481

482
483

484
485


486
487

488

489
490


491
492

493
494

495
496
497



498
499

500
501
502
503
504




505
506
507
508



509
510
511
512
513
514
515
516










517
518
519
520
521
522
523
524
525
526
527
528
529
530
531





532
533
534
535
536

537
538
539
540
541
542
543
208
209
210
211
212
213
214

215
216

217
218

219
220
221

222
223
224
225

226
227
228

229
230
231

232
233
234
235
236
237
238


239
240
241

242
243
244
245
246
247

248
249
250
251
252

253
254
255
256

257
258

259
260
261
262
263
264
265
266


267
268
269

270
271
272
273
274
275
276

277


278








279
280
281
282
283
284
285
286
287

288











289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309


310
311
312
313
314

315
316
317
318
319
320
321


322
323
324
325

326
327
328
329
330
331
332


333
334
335
336

337
338
339
340
341
342
343

344
345

346
347
348

349
350
351
352
353
354
355


356
357
358
359

360
361
362

363
364
365

366
367
368
369
370

371
372
373

374
375
376

377
378
379


380
381
382

383
384
385

386
387
388

389
390
391
392

393
394
395
396
397
398
399
400


401
402
403
404
405
406
407
408
409
410
411
412
413



414
415
416
417
418



419
420
421
422
423









424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444



445
446
447
448
449



450
451
452
453
454
455


456
457



458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473



474
475
476
477
478
479
480

481
482

483
484
485
486



487
488
489
490
491
492
493
494

495
496

497
498
499
500



501
502
503
504
505
506
507
508



509
510
511
512
513
514
515



516
517
518
519
520
521
522
523



524
525
526
527
528
529
530
531



532
533
534
535
536



537
538
539
540
541



542
543
544
545
546



547
548
549
550
551



552
553
554
555
556



557
558
559
560
561


562
563
564

565
566
567


568
569
570

571
572
573


574
575
576
577

578
579


580
581
582

583
584
585


586
587
588

589
590
591


592
593
594

595
596
597


598
599
600

601
602
603


604
605
606

607
608
609

610
611

612
613
614

615
616

617
618
619

620
621
622

623
624
625








626
627
628
629
630
631
632
633
634
635










636
637
638
639
640
641
642
643
644
645

646
647

648
649

650
651
652
653

654
655

656
657

658
659
660
661


662
663
664

665
666
667


668
669
670

671
672
673
674



675
676
677
678

679





680
681
682
683
684



685
686
687
688







689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708





709
710
711
712
713
714
715
716
717

718
719
720
721
722
723
724
725







-
+

-
+

-
+


-
+

+

-
+
+

-
+

+
-
+






-
-
+
+

-
+




+
-
+




-
+
+

+
-
+

-
+







-
-
+
+

-
+




+
+
-
+
-
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
-
+
+


+
-
+






-
-
+
+

+
-
+






-
-
+
+

+
-
+






-
+

-
+

+
-
+






-
-
+
+

+
-
+


-
+

+
-
+




-
+

+
-
+


-
+

+
-
-
+
+

-
+


-
+


-
+


+
-
+

+
+
+
+
+
+
-
-
+
+
+

+
+
+

+
+
+


-
-
-
+
+
+
+

-
-
-
+
+
+
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
-
-
+
+
+
+

-
-
-
+
+
+
+
+

-
-
+
+
-
-
-
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+



+
-
+

-
+



-
-
-
+
+
+




+
-
+

-
+



-
-
-
+
+
+





-
-
-
+
+
+




-
-
-
+
+
+





-
-
-
+
+
+




+
-
-
-
+
+
+

+
-
-
-
+
+
+

+
-
-
-
+
+
+

+
-
-
-
+
+
+

+
-
-
-
+
+
+

+
-
-
-
+
+
+

+
-
-
+
+

-
+

+
-
-
+
+

-
+

+
-
-
+
+

+
-
+

-
-
+
+

-
+

+
-
-
+
+

-
+

+
-
-
+
+

-
+


-
-
+
+

-
+

+
-
-
+
+

-
+

+
-
+

-
+

+
-
+

-
+

+
-
+


-
+


-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+


-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
+

-
+

-
+



-
+

-
+

-
+


+
-
-
+
+

-
+

+
-
-
+
+

-
+


+
-
-
-
+
+
+

-
+
-
-
-
-
-
+
+
+
+

-
-
-
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+










-
-
-
-
-
+
+
+
+
+




-
+







Content-Type: Text/plain

part3
--bar--
}

    set tok [mime::initialize -string $msg]
    set partToks [mime::getproperty $tok parts]
    set partToks [mime::property $tok parts]

    set res ""
    set res {} 
    foreach childTok $partToks {
	lappend res [mime::getbody $childTok]
	lappend res [mime::body $childTok]
    }
    set res
} {part1 part2 part3}
}} {part1 part2 part3}


test mime-3.3 {Try to parse a totally invalid message} {
    catch {mime::initialize -string "blah"} err0
	set token [mime::initialize -string blah]
    catch {mime::header get $token} err0
    set err0
} {improper line in header: blah}
} {{improper line in header} blah}


test mime-3.4 {Try to parse a MIME message with an invalid version} {
test mime-3.4 {Try to parse a MIME message with an invalid version} {cleanly {
    set msg1 {MIME-Version: 2.0
Content-Type: text/plain

msg1}

    set tok [mime::initialize -string $msg1]
    catch {mime::getbody $tok} err1
    catch {mime::buildmessage $tok} err1a
    catch {mime::body $tok} err1
    catch {mime::serialize $tok} err1a
    list $err1 $err1a
} "msg1 {MIME-Version: 2.0\r
}} "msg1 {MIME-Version: 2.0\r
Content-Type: text/plain\r
\r
msg1}"


test mime-3.5 {Try to parse a MIME message with no newline between headers and data} {
test mime-3.5 {Try to parse a MIME message with no newline between headers and data} {cleanly {
    set msg2 {MIME-Version: 1.0
Content-Type: foobar
data without newline}

    catch {mime::initialize -string $msg2} err2
    set token [mime::initialize -string $msg2]
    catch {mime::header get $token} err2
    set err2
}} {expecting type/subtype found foobar}
} {improper line in header: data without newline}


test mime-3.6 {Try to parse a MIME message with no MIME version and generate a new message from it} {
test mime-3.6 {Try to parse a MIME message with no MIME version and generate a new message from it} {cleanly {

    # No MIME version
    set msg3 {Content-Type: text/plain

foo}

    set tok [mime::initialize -string $msg3]
    catch {mime::getbody $tok} err3
    catch {mime::buildmessage $tok} err3a
    catch {mime::body $tok} err3
    catch {mime::serialize $tok} err3a
    list $err3 $err3a
} "foo {MIME-Version: 1.0\r
}} "foo {MIME-Version: 1.0\r
Content-Type: text/plain\r
\r
foo}"


foreach name {file chan} {
test mime-3.7 {Test mime with a bad email [SF Bug 631314 ]} {
    test mime-3.7.$name {Test mime with a bad email [SF Bug 631314 ]} {cleanly {
    set tok [mime::initialize -file \
		 [file join $tcltest::testsDirectory badmail1.txt]]
	with.$name $tcltest::testsDirectory/badmail1.txt {

    set res {}
    set ctok [lindex [mime::getproperty $tok parts] 0]
    lappend res [dictsort [mime::getproperty $tok]]
    lappend res [dictsort [mime::getproperty $ctok]]
    mime::finalize $tok
    string map [list $ctok CHILD] $res
} {{content multipart/mixed encoding {} params {boundary ----------CSFNU9QKPGZL79} parts CHILD size 0} {content application/octet-stream encoding {} params {charset us-ascii} size 0}}
	    set res {}
	    set ctok [lindex [mime::property $tok parts] 0]
	    lappend res [dictsort [mime::property $tok]]
	    lappend res [dictsort [mime::property $ctok]]
	    mime::finalize $tok
	    string map [list $ctok CHILD] $res
	}
    }} {{content multipart/mixed encoding {} params {boundary ----------CSFNU9QKPGZL79} parts CHILD size 0} {content application/octet-stream encoding {} params {} size 0}}

test mime-3.8 {Test mime with another bad email [SF Bug 631314 ]} {
    test mime-3.8 {Test mime with another bad email [SF Bug 631314 ]} {cleanly {
    set tok [mime::initialize -file \
		 [file join $tcltest::testsDirectory badmail2.txt]]
    set res {}
    set ctok [lindex [mime::getproperty $tok parts] 0]
    lappend res [dictsort [mime::getproperty $tok]]
    lappend res [dictsort [mime::getproperty $ctok]]
    mime::finalize $tok
    string map [list $ctok CHILD] $res
} {{content multipart/related encoding {} params {boundary ----=_NextPart_000_0000_2CBA2CBA.150C56D2} parts CHILD size 659} {content application/octet-stream encoding base64 params {} size 659}}

test mime-3.9 {Parse a MIME message with a charset encoded body and use getbody -decode to get it back} {
	with.$name $tcltest::testsDirectory/badmail2.txt {
	    set res {}
	    set ctok [lindex [mime::property $tok parts] 0]
	    lappend res [dictsort [mime::property $tok]]
	    lappend res [dictsort [mime::property $ctok]]
	    mime::finalize $tok
	    string map [list $ctok CHILD] $res
	}
    }} {{content multipart/related encoding {} params {boundary ----=_NextPart_000_0000_2CBA2CBA.150C56D2} parts CHILD size 659} {content application/octet-stream encoding base64 params {} size 659}}
}




test mime-3.9 {Parse a MIME message with a charset encoded body and use [body] -decode to get it back} {cleanly {
    set msg {MIME-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1

Fran\xE7ois
}
    set tok [mime::initialize -string $msg]
    mime::getbody $tok -decode
} {Fran\xE7ois
    mime::body $tok -decode
}} {Fran\xE7ois
}


test mime-3.10 {Parse a MIME message with a charset encoded body and use getbody -decode to get it back (example from encoding man page)} {
test mime-3.10 {Parse a MIME message with a charset encoded body and use [body] -decode to get it back (example from encoding man page)} {cleanly {
    set msg {MIME-Version: 1.0
Content-Type: text/plain; charset=EUC-JP
Content-Transfer-Encoding: quoted-printable

=A4=CF}
    set tok [mime::initialize -string $msg]
    mime::getbody $tok -decode
} "\u306F"
    mime::body $tok -decode
}} \u306F


test mime-3.11 {Parse a MIME message without a charset encoded body and use getbody -decode to get it back} {
test mime-3.11 {Parse a MIME message without a charset encoded body and use [body] -decode to get it back} {cleanly {
    set msg {MIME-Version: 1.0
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

A plain text message.}
    set tok [mime::initialize -string $msg]
    mime::getbody $tok -decode
} "A plain text message."
    mime::body $tok -decode
}} {A plain text message.}


test mime-3.12 {Parse a MIME message with a charset encoded body in an unrecognised charset and use getbody -decode to attempt to get it back} {
test mime-3.12 {Parse a MIME message with a charset encoded body in an unrecognised charset and use [body] -decode to attempt to get it back} {cleanly {
    set msg {MIME-Version: 1.0
Content-Type: text/plain; charset=SCRIBBLE
Content-Transfer-Encoding: quoted-printable

This is a message in the scribble charset that tcl does not recognise.}
    set tok [mime::initialize -string $msg]
    catch {mime::getbody $tok -decode} errmsg
    catch {mime::body $tok -decode} errmsg
    set errmsg
} "-decode failed: can't reversemap charset SCRIBBLE"
}} {{-decode cannot reversemap charset} SCRIBBLE}


test mime-3.13 {Parse a MIME message with a charset encoded body in an unrecognised charset but don't use -decode so we get it back raw} {
test mime-3.13 {Parse a MIME message with a charset encoded body in an unrecognised charset but don't use -decode so we get it back raw} {cleanly {
    set msg {MIME-Version: 1.0
Content-Type: text/plain; charset=SCRIBBLE
Content-Transfer-Encoding: quoted-printable

This is a message in the scribble charset that tcl does not recognise.}
    set tok [mime::initialize -string $msg]
    mime::getbody $tok
} "This is a message in the scribble charset that tcl does not recognise."
    mime::body $tok
}} {This is a message in the scribble charset that tcl does not recognise.}


test mime-4.1 {Test qp_encode with a > 76 character string containing special chars.} {
test mime-4.1 {Test qp_encode with a > 76 character string containing special chars.} {cleanly {
    set str1 "foo!\"\t barbaz \$ ` \{ # jack and jill went up a hill to fetch a pail of water. Jack fell down and said !\"\#\$@\[\\\]^`\{\|\}\~  \nJill said, \"Oh my\""
    mime::qp_encode $str1
} "foo=21=22\t barbaz =24 =60 =7B =23 jack and jill went up a hill to fetch a=\n pail of water. Jack fell down and said =21=22=23=24=40=5B=5C=5D=5E=60=7B=\n=7C=7D=7E =20\nJill said, =22Oh my=22"
}} "foo=21=22\t barbaz =24 =60 =7B =23 jack and jill went up a hill to fetch a=\n pail of water. Jack fell down and said =21=22=23=24=40=5B=5C=5D=5E=60=7B=\n=7C=7D=7E =20\nJill said, =22Oh my=22"


test mime-4.2 {Check that encode/decode yields original string} {
test mime-4.2 {Check that encode/decode yields original string} {cleanly {
    set str1 "foo!\"\t barbaz \$ ` \{ # jack and jill went up a hill to fetch a pail of water. Jack fell down and said !\"\#\$@\[\\\]^`\{\|\}\~  \nJill said, \"Oh my\"  "
    set enc [mime::qp_encode $str1]
    set dec [mime::qp_decode $enc]
    string equal $dec $str1
} {1}
}} 1


test mime-4.3 {mime::decode data that might come from an MUA} {
test mime-4.3 {mime::decode data that might come from an MUA} {cleanly {
    set enc "I'm the =22 message =\nwith some new lines=  \n but with some extra space, too.   "
    mime::qp_decode $enc
} "I'm the \" message with some new lines but with some extra space, too."
}} "I'm the \" message with some new lines but with some extra space, too."


test mime-4.4 {Test qp_encode with non-US_ASCCI characters.} {
    set str1 "Test de caractres accentus :  � � � et quelques contrles \"\[|\]()\""
test mime-4.4 {Test qp_encode with non-US_ASCCI characters.} {cleanly {
    set str1 "Test de caractères accentués : â î é ç et quelques contrôles \"\[|\]()\""
    mime::qp_encode $str1
} "Test de caract=E8res accentu=E9s : =E2 =EE =E9 =E7 et quelques contr=F4le=\ns =22=5B=7C=5D()=22"
}} "Test de caract=E8res accentu=E9s : =E2 =EE =E9 =E7 et quelques contr=F4le=\ns =22=5B=7C=5D()=22"


test mime-4.5 {Test qp_encode with softbreak} {
test mime-4.5 {Test qp_encode with softbreak} {cleanly {
    set str1 [string repeat abc 40]
    mime::qp_encode $str1
} "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca=
}} "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca=
bcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc"


test mime-4.6 {Test qp_encode with softbreak} {
test mime-4.6 {Test qp_encode with softbreak} {cleanly {
    set str1 [string repeat abc 40]
    mime::qp_encode $str1 0 1
}} [string repeat abc 40]


test mime-4.7 {Test qp_encode/decode in encoded_word mode} {cleanly {
    set enc [mime::qp_encode {jack and jill went up the hill} 1]
    mime::qp_encode $str1 0 1
} [string repeat abc 40]
    mime::qp_decode $enc 1
}} {jack and jill went up the hill}


test mime-4.8 {Test qp_encode in encoded_word mode with equal signs} {cleanly {
    mime::qp_encode 1and1=2 1
}} 1and1=3D2

test mime-4.9 {Test qp_encode in encoded_word mode with tabs and spaces} {cleanly {
    mime::qp_encode "1 and 1 =\t2" 1
}} 1_and_1_=3D=092


test mime-5.1 {Test word_encode with quoted-printable method} {
    mime::word_encode iso8859-1 quoted-printable "Test de contr�le effectu�"
} "=?ISO-8859-1?Q?Test_de_contr=F4le_effectu=E9?="
test mime-4.10 {Test qp_encode in encoded_word mode with underscores} {cleanly {
    mime::qp_encode 2003_06_30 1
}} 2003=5F06=5F30


test mime-5.2 {Test word_encode with base64 method} {
    mime::word_encode iso8859-1 base64 "Test de contr�le effectu�"
} "=?ISO-8859-1?B?VGVzdCBkZSBjb250cvRsZSBlZmZlY3R16Q==?="
test mime-4.11 {Test qp_encode in encoded_word mode with underscores and spaces} {cleanly {
    mime::qp_encode {2003_06_30 is 30 June 2003} 1
}} 2003=5F06=5F30_is_30_June_2003


test mime-5.3 {Test encode+decode with quoted-printable method} {
    set enc [mime::word_encode iso8859-1 quoted-printable "Test de contr�le effectu�"]
    mime::word_decode $enc
} {iso8859-1 quoted-printable {Test de contr�le effectu�}}

test mime-5.4 {Test encode+decode with base64 method} {
    set enc [mime::word_encode iso8859-1 base64 "Test de contrle effectu�"]
    mime::word_decode $enc
} {iso8859-1 base64 {Test de contrle effectu�}}
test mime-4.12 {Test qp_encode in encoded_word mode with question marks} {cleanly {
    mime::qp_encode {How long is a piece of string ?} 1
}} How_long_is_a_piece_of_string_=3F


test mime-4.13 {Test qp_encode in no_softbreak mode} {cleanly {
    mime::qp_encode {This is a very long string into which we do not want inserted softbreaks as we want one very long line returned even though that's probably not how we whould be doing it (see RFC2047) but we don't want to break backward compatibility} 0 1
}} {This is a very long string into which we do not want inserted softbreaks as we want one very long line returned even though that's probably not how we whould be doing it (see RFC2047) but we don't want to break backward compatibility}
 


test mime-5.1 {Test word_encode with quoted-printable method} {cleanly {
    mime::word_encode iso8859-1 quoted-printable {Test de contrôle effectué}
}} =?ISO-8859-1?Q?Test_de_contr=F4le_effectu=E9?=


test mime-5.2 {Test word_encode with base64 method} {cleanly {
    mime::word_encode iso8859-1 base64 {Test de contrôle effectué}
}} =?ISO-8859-1?B?VGVzdCBkZSBjb250cvRsZSBlZmZlY3R16Q==?=


test mime-5.5 {Test decode with lowercase quoted-printable method} {
	mime::word_decode "=?ISO-8859-1?q?Test_lowercase_q?="
} {iso8859-1 quoted-printable {Test lowercase q}}
test mime-5.3 {Test encode+decode with quoted-printable method} {cleanly {
    set enc [mime::word_encode iso8859-1 quoted-printable {Test de contrôle effectué}]
    mime::word_decode $enc
}} {iso8859-1 quoted-printable {Test de contrôle effectué}}

test mime-5.6 {Test decode with lowercase base64 method} {
	mime::word_decode "=?ISO-8859-1?b?VGVzdCBsb3dlcmNhc2UgYg==?="
} {iso8859-1 base64 {Test lowercase b}}

test mime-5.4 {Test encode+decode with base64 method} {cleanly {
    set enc [mime::word_encode iso8859-1 base64 {Test de contrôle effectué}]
    mime::word_decode $enc
}} {iso8859-1 base64 {Test de contrôle effectué}}

test mime-5.7 {Test word_encode with quoted-printable method across encoded word boundaries} {
    mime::word_encode iso8859-1 quoted-printable "Test de contr�le effectu�" -maxlength 31

test mime-5.5 {Test decode with lowercase quoted-printable method} {cleanly {
} "=?ISO-8859-1?Q?Test_de_contr?=
 =?ISO-8859-1?Q?=F4le_effectu?=
 =?ISO-8859-1?Q?=E9?="
	mime::word_decode =?ISO-8859-1?q?Test_lowercase_q?=
}} {iso8859-1 quoted-printable {Test lowercase q}}


test mime-5.6 {Test decode with lowercase base64 method} {cleanly {
	mime::word_decode =?ISO-8859-1?b?VGVzdCBsb3dlcmNhc2UgYg==?=
}} {iso8859-1 base64 {Test lowercase b}}


test mime-5.7 {Test word_encode with quoted-printable method across encoded word boundaries} {cleanly {
    mime::word_encode iso8859-1 quoted-printable {Test de contrôle effectué} -maxlength 31
}} "=?ISO-8859-1?Q?Test_de_contr?=
 =?ISO-8859-1?Q?=F4le_effectu?=
 =?ISO-8859-1?Q?=E9?="


test mime-5.8 {Test word_encode with quoted-printable method across encoded word boundaries} {
    mime::word_encode iso8859-1 quoted-printable "Test de contrle effectu�" -maxlength 32
} "=?ISO-8859-1?Q?Test_de_contr?=
test mime-5.8 {Test word_encode with quoted-printable method across encoded word boundaries} {cleanly {
    mime::word_encode iso8859-1 quoted-printable {Test de contrôle effectué} -maxlength 32
}} "=?ISO-8859-1?Q?Test_de_contr?=
 =?ISO-8859-1?Q?=F4le_effectu?=
 =?ISO-8859-1?Q?=E9?="


test mime-5.9 {Test word_encode with quoted-printable method and multibyte character} {
test mime-5.9 {Test word_encode with quoted-printable method and multibyte character} {cleanly {
    mime::word_encode euc-jp quoted-printable "Following me is a multibyte character \xA4\xCF"
} "=?EUC-JP?Q?Following_me_is_a_multibyte_character_=A4=CF?="
}} =?EUC-JP?Q?Following_me_is_a_multibyte_character_=A4=CF?=

set n 10
while {$n < 14} {
    test mime-5.$n {Test word_encode with quoted-printable method and multibyte character across encoded word boundary} {
        mime::word_encode euc-jp quoted-printable "Following me is a multibyte character \xA4\xCF" -maxlength [expr 42 + $n]
    } "=?EUC-JP?Q?Following_me_is_a_multibyte_character_?=
    test mime-5.$n {Test word_encode with quoted-printable method and multibyte character across encoded word boundary} {cleanly {
	mime::word_encode euc-jp quoted-printable "Following me is a multibyte character \xA4\xCF" -maxlength [expr 42 + $n]
    }} "=?EUC-JP?Q?Following_me_is_a_multibyte_character_?=
 =?EUC-JP?Q?=A4=CF?="
    incr n
}


test mime-5.14 {Test word_encode with quoted-printable method and multibyte character (triple)} {
test mime-5.14 {Test word_encode with quoted-printable method and multibyte character (triple)} {cleanly {
    mime::word_encode utf-8 quoted-printable "Here is a triple byte encoded character \xE3\x81\xAF"
} "=?UTF-8?Q?Here_is_a_triple_byte_encoded_character_=E3=81=AF?="
}} =?UTF-8?Q?Here_is_a_triple_byte_encoded_character_=E3=81=AF?=

set n 15
while {$n < 23} {
    test mime-5.$n {Test word_encode with quoted-printable method and triple byte character across encoded word boundary} {
        mime::word_encode utf-8 quoted-printable "Here is a triple byte encoded character \xE3\x81\xAF" -maxlength [expr 38 + $n]
    } "=?UTF-8?Q?Here_is_a_triple_byte_encoded_character_?=
    test mime-5.$n {Test word_encode with quoted-printable method and triple byte character across encoded word boundary} {cleanly {
	mime::word_encode utf-8 quoted-printable "Here is a triple byte encoded character \xE3\x81\xAF" -maxlength [expr 38 + $n]
    }} "=?UTF-8?Q?Here_is_a_triple_byte_encoded_character_?=
 =?UTF-8?Q?=E3=81=AF?="
    incr n
}

while {$n < 25} {
    test mime-5.$n {Test word_encode with quoted-printable method and triple byte character across encoded word boundary} {
        mime::word_encode utf-8 quoted-printable "Here is a triple byte encoded character \xE3\x81\xAF" -maxlength [expr 38 + $n]
    } "=?UTF-8?Q?Here_is_a_triple_byte_encoded_character_=E3=81=AF?="
    test mime-5.$n {Test word_encode with quoted-printable method and triple byte character across encoded word boundary} {cleanly {
	mime::word_encode utf-8 quoted-printable "Here is a triple byte encoded character \xE3\x81\xAF" -maxlength [expr 38 + $n]
    }} =?UTF-8?Q?Here_is_a_triple_byte_encoded_character_=E3=81=AF?=
    incr n
}

while {$n < 29} {
    test mime-5.$n {Test word_encode with base64 method across encoded word boundaries} {
        mime::word_encode euc-jp base64 "There is a multibyte character \xA4\xCF" -maxlength [expr 28 + $n]
    } "=?EUC-JP?B?VGhlcmUgaXMgYSBtdWx0aWJ5dGUgY2hhcmFjdGVy?=
    test mime-5.$n {Test word_encode with base64 method across encoded word boundaries} {cleanly {
	mime::word_encode euc-jp base64 "There is a multibyte character \xA4\xCF" -maxlength [expr 28 + $n]
    }} "=?EUC-JP?B?VGhlcmUgaXMgYSBtdWx0aWJ5dGUgY2hhcmFjdGVy?=
 =?EUC-JP?B?IKTP?="
    incr n
}

while {$n < 33} {
    test mime-5.$n {Test word_encode with base64 method and triple byte character across encoded word boundary} {
        mime::word_encode utf-8 base64 "Here is a multibyte character \xE3\x81\xAF" -maxlength [expr 23 + $n]
    } "=?UTF-8?B?SGVyZSBpcyBhIG11bHRpYnl0ZSBjaGFyYWN0ZXIg?=
    test mime-5.$n {Test word_encode with base64 method and triple byte character across encoded word boundary} {cleanly {
	mime::word_encode utf-8 base64 "Here is a multibyte character \xE3\x81\xAF" -maxlength [expr 23 + $n]
    }} "=?UTF-8?B?SGVyZSBpcyBhIG11bHRpYnl0ZSBjaGFyYWN0ZXIg?=
 =?UTF-8?B?44Gv?="
    incr n
}


test mime-5.33 {Test word_encode with quoted-printable method and -maxlength set to same length as will the result} {
    mime::word_encode iso8859-1 quoted-printable "123" -maxlength 20
} "=?ISO-8859-1?Q?123?="
test mime-5.33 {Test word_encode with quoted-printable method and -maxlength set to same length as will the result} {cleanly {
    mime::word_encode iso8859-1 quoted-printable 123 -maxlength 20
}} =?ISO-8859-1?Q?123?=


test mime-5.34 {Test word_encode with base64 method and -maxlength set to same length as will the result} {
    mime::word_encode iso8859-1 base64 "123" -maxlength 21
} "=?ISO-8859-1?B?MTIz?="
test mime-5.34 {Test word_encode with base64 method and -maxlength set to same length as will the result} {cleanly {
    mime::word_encode iso8859-1 base64 123 -maxlength 21
}} =?ISO-8859-1?B?MTIz?=


test mime-5.35 {Test word_encode with quoted-printable method and non charset encoded string} {
    mime::word_encode utf-8 quoted-printable "\u306F" -charset_encoded 0
} "=?UTF-8?Q?=E3=81=AF?="
test mime-5.35 {Test word_encode with quoted-printable method and non charset encoded string} {cleanly {
    mime::word_encode utf-8 quoted-printable \u306F -charset_encoded 0
}} =?UTF-8?Q?=E3=81=AF?=


test mime-5.36 {Test word_encode with base64 method and non charset encoded string} {
    mime::word_encode utf-8 base64 "\u306F" -charset_encoded 0
} "=?UTF-8?B?44Gv?="
test mime-5.36 {Test word_encode with base64 method and non charset encoded string} {cleanly {
    mime::word_encode utf-8 base64 \u306F -charset_encoded 0
}} =?UTF-8?B?44Gv?=


test mime-5.36 {Test word_encode with base64 method and one byte} {
    mime::word_encode iso8859-1 base64 "a"
} "=?ISO-8859-1?B?YQ==?="
test mime-5.36 {Test word_encode with base64 method and one byte} {cleanly {
    mime::word_encode iso8859-1 base64 a
}} =?ISO-8859-1?B?YQ==?=


test mime-5.37 {Test word_encode with base64 method and two bytes} {
    mime::word_encode euc-jp base64 "\xA4\xCF"
} "=?EUC-JP?B?pM8=?="
test mime-5.37 {Test word_encode with base64 method and two bytes} {cleanly {
    mime::word_encode euc-jp base64 \xA4\xCF
}} =?EUC-JP?B?pM8=?=


test mime-5.38 {Test word_encode with unknown charset} {
    catch {mime::word_encode scribble  quoted-printable "scribble is an unknown charset"} errmsg
test mime-5.38 {Test word_encode with unknown charset} {cleanly {
    catch {mime::word_encode scribble  quoted-printable {scribble is an unknown charset}} errmsg
    set errmsg
} "unknown charset 'scribble'"
}} {{unknown charset} scribble}


test mime-5.39 {Test word_encode with invalid charset} {
    catch {mime::word_encode unicode quoted-printable "unicode is not a valid charset"} errmsg
test mime-5.39 {Test word_encode with invalid charset} {cleanly {
    catch {mime::word_encode unicode quoted-printable {unicode is not a valid charset}} errmsg
    set errmsg
} "invalid charset 'unicode'"
}} {{invalid charset} unicode}


test mime-5.40 {Test word_encode with invalid method} {
    catch {mime::word_encode iso8859-1 tea-leaf "tea-leaf is not a valid method"} errmsg
test mime-5.40 {Test word_encode with invalid method} {cleanly {
    catch {mime::word_encode iso8859-1 tea-leaf {tea-leaf is not a valid method}} errmsg
    set errmsg
}} {{unknown method} tea-leaf {must be one of} {base64 quoted-printable}}
} "unknown method 'tea-leaf', must be base64 or quoted-printable"


test mime-5.41 {Test word_encode with maxlength to short for method quoted-printable} {
    catch {mime::word_encode iso8859-1 quoted-printable "1" -maxlength 17} errmsg
test mime-5.41 {Test word_encode with maxlength to short for method quoted-printable} {cleanly {
    catch {mime::word_encode iso8859-1 quoted-printable 1 -maxlength 17} errmsg
    set errmsg
} "maxlength 17 too short for chosen charset and encoding"
}} {maxlength 17 {too short for chosen charset and encoding}}


test mime-5.42 {Test word_encode with maxlength on the limit for quoted_printable and an unquoted character} {
   catch {mime::word_encode iso8859-1 quoted-printable "_" -maxlength 18} errmsg
test mime-5.42 {Test word_encode with maxlength on the limit for quoted_printable and an unquoted character} {cleanly {
   catch {mime::word_encode iso8859-1 quoted-printable _ -maxlength 20} errmsg
   set errmsg
} "=?ISO-8859-1?Q?_?="
}} =?ISO-8859-1?Q?=5F?=


test mime-5.43 {Test word_encode with maxlength to short for method quoted_printable and a character to be quoted} {
   catch {mime::word_encode iso8859-1 quoted-printable "=" -maxlength 18} errmsg
test mime-5.43 {Test word_encode with maxlength to short for method quoted_printable and a character to be quoted} {cleanly {
   catch {mime::word_encode iso8859-1 quoted-printable = -maxlength 18} errmsg
   set errmsg
} "maxlength 18 too short for chosen charset and encoding"
}} {maxlength 18 {too short for chosen charset and encoding}}


test mime-5.44 {Test word_encode with maxlength to short for method quoted-printable and multibyte character} {
    catch {mime::word_encode euc-jp quoted-printable "\xA4\xCF" -maxlength 17} errmsg
test mime-5.44 {Test word_encode with maxlength to short for method quoted-printable and multibyte character} {cleanly {
    catch {mime::word_encode euc-jp quoted-printable \xA4\xCF -maxlength 17} errmsg
    set errmsg
} "maxlength 17 too short for chosen charset and encoding"
}} {maxlength 17 {too short for chosen charset and encoding}}


test mime-5.45 {Test word_encode with maxlength to short for method base64} {
    catch {mime::word_encode iso8859-1 base64 "1" -maxlength 20} errmsg
test mime-5.45 {Test word_encode with maxlength to short for method base64} {cleanly {
    catch {mime::word_encode iso8859-1 base64 1 -maxlength 20} errmsg
    set errmsg
} "maxlength 20 too short for chosen charset and encoding"
}} {maxlength 20 {too short for chosen charset and encoding}}


test mime-6.1 {Test field_decode (from RFC 2047, part 8)} {
test mime-6.1 {Test field_decode (from RFC 2047, part 8)} {cleanly {
    mime::field_decode {=?US-ASCII?Q?Keith_Moore?= <[email protected]>}
} {Keith Moore <[email protected]>}
}} {Keith Moore <[email protected]>}


test mime-6.2 {Test field_decode (from RFC 2047, part 8)} {
test mime-6.2 {Test field_decode (from RFC 2047, part 8)} {cleanly {
    mime::field_decode {=?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <[email protected]>}
} {Patrik Fltstrm <[email protected]>}
}} {Patrik Fältström <[email protected]>}


test mime-6.3 {Test field_decode (from RFC 2047, part 8)} {
test mime-6.3 {Test field_decode (from RFC 2047, part 8)} {cleanly {
    mime::field_decode {=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=
			=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=}
} {If you can read this you understand the example.}
}} {If you can read this you understand the example.}

foreach {n encoded expected} {
    4 "(=?ISO-8859-1?Q?a?=)"
    "(a)"
    5 "(=?ISO-8859-1?Q?a?= b)"
    "(a b)"
    6 "(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)"
    "(ab)"
    7 "(=?ISO-8859-1?Q?a?=  =?ISO-8859-1?Q?b?=)"
    "(ab)"
    4 (=?ISO-8859-1?Q?a?=)
    (a)
    5 {(=?ISO-8859-1?Q?a?= b)}
    {(a b)}
    6 {(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)}
    (ab)
    7 {(=?ISO-8859-1?Q?a?=  =?ISO-8859-1?Q?b?=)}
    (ab)
    8 "(=?ISO-8859-1?Q?a?=
    =?ISO-8859-1?Q?b?=)"
    "(ab)"
    9 "(=?ISO-8859-1?Q?a_b?=)"
    "(a b)"
    10 "(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)"
    "(a b)"
    11 "(=?ISO-8859-1?Q?a?=x=?ISO-8859-2?Q?_b?=)"
    "(ax b)"
    12 "a         b         c"
    "a         b         c"
    13 ""
    (ab)
    9 (=?ISO-8859-1?Q?a_b?=)
    {(a b)}
    10 {(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)}
    {(a b)}
    11 {(=?ISO-8859-1?Q?a?=x=?ISO-8859-2?Q?_b?=)}
    {(ax b)}
    12 {a         b         c}
    {a         b         c}
    13 {} 
    ""
    {}
} {
    test mime-6.$n {Test field_decode (from RFC 2047, part 8)} {
    test mime-6.$n {Test field_decode (from RFC 2047, part 8)} {cleanly {
	mime::field_decode $encoded
    } $expected ; # {}
    }} $expected ; # {}
}

foreach {bug n encoded expected} {
    764702 1 "(=?utf-8?Q?H=C3=BCrz?=)" "(Hrz)"
    764702 1 {(=?utf-8?Q?H=C3=BCrz?=)} {(Hürz)}
} {
    test mime-7.$n "Test field_decode (from SF Tcllib bug $bug)" {
    test mime-7.$n "Test field_decode (from SF Tcllib bug $bug)" {cleanly {
	mime::field_decode $encoded
    } $expected ; # {}
    }} $expected ; # {}
}


test mime-8.1 {Test reversemapencoding+mapencoding with preferred name} {
    set charset [mime::reversemapencoding "US-ASCII"]
test mime-8.1 {Test reversemapencoding+mapencoding with preferred name} {cleanly {
    set charset [mime::reversemapencoding US-ASCII]
    mime::mapencoding $charset
} {US-ASCII}
}} US-ASCII


test mime-8.2 {Test reversemapencoding+mapencoding with alias} {
    set charset [mime::reversemapencoding "UTF8"]
test mime-8.2 {Test reversemapencoding+mapencoding with alias} {cleanly {
    set charset [mime::reversemapencoding UTF8]
    mime::mapencoding $charset
} {UTF-8}
}} UTF-8


foreach name {file chan} {
test mime-9.0 {Test chunk handling of copymessage and helpers} {
    set in [makeFile [set data [string repeat [string repeat "123456789 " 10]\n 350]] input.txt]
    set mi [makeFile {} mime.txt]
    test mime-9.0.$name {Test chunk handling of serialize and helpers} {cleanly {
	set in [makeFile [set data [string repeat [string repeat {123456789 } 10]\n 350]] input.txt]
	set mi [makeFile {} mime.txt]

    set token [mime::initialize -canonical text/plain -file $in]
	with.$name $in -canonical text/plain {

    set f [open $mi w]
    fconfigure $f -translation binary
    mime::copymessage $token $f
    close $f
	    set f [open $mi w]
	    fconfigure $f -translation binary
	    mime::serialize $tok -chan $f
	    close $f

    set token [mime::initialize -file $mi]
    set newdata [mime::getbody $token]
    set res [string compare $data $newdata]
	    with.$name $mi {
		set newdata [mime::body $tok]
		set res [string compare $data $newdata]

    removeFile input.txt
    removeFile mime.txt
    unset data newdata token f in mi
    set res
} 0

set ::env(TZ) "UTC0"
		removeFile input.txt
		removeFile mime.txt
		unset data newdata tok f in mi
		set res
	    }
	}
    }} 0
}

set ::env(TZ) UTC0
set epoch [clock scan 2000-01-01]
foreach {n stamp date} {
    1     86340 {Sat, 01 Jan 2000 23:59:00 +0000}
    2   5176620 {Tue, 29 Feb 2000 21:57:00 +0000}
    3  31610520 {Sun, 31 Dec 2000 20:42:00 +0000}
    4  31708740 {Mon, 01 Jan 2001 23:59:00 +0000}
    5  68248620 {Thu, 28 Feb 2002 21:57:00 +0000}
    6 126218520 {Wed, 31 Dec 2003 20:42:00 +0000}
} {
    test mime-10.$n "Test formatting dates (RFC 822)" {
        # To verify that clock scan gets the expected value.
        set stamp_test [expr {[mime::parsedatetime $date clock] - $epoch}]
        # Parse and re-format should get us the original.
        set parsed_test [mime::parsedatetime $date proper]
        list $stamp_test $parsed_test
	# To verify that clock scan gets the expected value.
	set stamp_test [expr {[mime::datetime $date clock] - $epoch}]
	# Parse and re-format should get us the original.
	set parsed_test [mime::datetime $date proper]
	list $stamp_test $parsed_test
    } [list $stamp $date]
}


test mime-11.0 {Bug 1825092} {
test mime-11.0 {Bug 1825092} {cleanly {
    set in [makeFile {From [email protected]  Sat Oct 20 17:58:49 2007
Return-Path: <[email protected]>
Message-ID: <[email protected]>
From: Somwhere <[email protected]>
MIME-Version: 1.0
To: Here <[email protected]>
Subject: test
559
560
561
562
563
564
565
566

567
568
569
570
571
572

573
574
575
576
577
578
579
580



581
582
583



584
585
586
587
588
589

590

591
592
593
594
595
596
597
598
599
600
601
602
603
604




605
606
607





































































































608

609






741
742
743
744
745
746
747

748
749
750
751
752
753

754
755
756
757
758
759



760
761
762



763
764
765
766
767
768
769
770
771
772

773
774
775
776
777
778
779
780
781
782
783
784



785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901







-
+





-
+





-
-
-
+
+
+
-
-
-
+
+
+






+
-
+











-
-
-
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+

+
+
+
+
+
+
 name="a0036.dss"

BGRzcwEAAQABAAAAYQAAAAAAAAAAAAAAAAAAACQAAAD+//7/+/8wNzA2MTYwODE1MjQwNzA2
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ
--------------090305080603000703000106--
} mail_part]
    set token [mime::initialize -file $in]
    set allparts [mime::getproperty $token parts]
    set allparts [mime::property $token parts]
    set attachment [lindex $allparts 1]

    set out [makeFile {} mail_att]
    set ofh [open $out w]
    fconfigure $ofh -translation binary
    mime::copymessage $attachment $ofh
    mime::serialize $attachment -chan $ofh
    close $ofh

    set data [viewFile $out]
    file delete $in $out
    set data
} {MIME-Version: 1.0
Content-Disposition: attachment;
 filename="a0036.dss"
}} {MIME-Version: 1.0
Content-Disposition: attachment
	; filename=a0036.dss
Content-Transfer-Encoding: base64
Content-Type: application/octet-stream;
              name="a0036.dss"
Content-Type: application/octet-stream
	; name=a0036.dss
Content-Transfer-Encoding: base64

BGRzcwEAAQABAAAAYQAAAAAAAAAAAAAAAAAAACQAAAD+//7/+/8wNzA2MTYwODE1MjQwNzA2
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ}

# -------------------------------------------------------------------------


test mime-12.0 {Bug 3483716} {
test mime-12.0 {Bug 3483716} {cleanly {
    set token [mime::initialize -string {Content-Type: message/delivery-status; name="deliverystatus.txt"
Content-Disposition: attachment; filename="deliverystatus.txt"; size=138;
creation-date="Thu, 02 Feb 2012 13:50:05 GMT";
modification-date="Thu, 02 Feb 2012 13:50:05 GMT"
Content-Description: deliverystatus.txt
Content-Transfer-Encoding: base64

T3JpZ2luYWwtUmVjaXBpZW50OiA8L2ZheD1ibHViYkBndW1taS5ib290PgpBY3Rpb246IGZhaWxl
ZApEaWFnbm9zdGljLUNvZGU6IHNtdHA7IDU1MCAjNS4xLjAgQWRkcmVzcyByZWplY3RlZC4KUmVt
b3RlLU1UQTogNTMuMjQuMjgyLjE1MA==
}]
    set parts [mime::getproperty $token parts]
    mime::getheader [lindex $parts end] Remote-MTA
} 53.24.282.150
    set parts [mime::property $token parts]
    lassign [mime::header get [lindex $parts end] Remote-MTA] result
    return $result
}} 53.24.282.150

# -------------------------------------------------------------------------


test mime-13.0 {cleanly {
    issue a16b1095974e071d
}} {
    set msg "MIME-Version: 1.0
Content-Type: text/plain\r
\r
so plain
"

    set tok [mime::initialize -string $msg]
	mime::body $tok
} "so plain\n"

# -------------------------------------------------------------------------

test mime-14.0 {cleanly {
	hostname argument to parseaddress
}} {
	set parsed [mime::parseaddress hostname fakedomain.fake {Here <h>}] 
    list [llength $parsed] [lindex $parsed 0]
} [list 1 [list address [email protected] comment {} domain {} error {} \
	friendly Here group {} local h memberP 0 phrase Here \
	proper {Here <[email protected]>} route {}]]



test mime-14.1 {cleanly {
	special characters in local part
}} {
	set parsed [mime::parseaddress hostname fakedomain.fake foo<>[email protected]]
    list [llength $parsed] [lindex $parsed 0]
} [list 1 [list address {} comment {} domain {} \
	error {expecting mailbox in local-part (found >)} friendly foo group {} \
	local {} memberP 0 phrase foo proper {foo <>} route {}]]


# -------------------------------------------------------------------------


test mime-15.0 {cleanly {
	a multipart/mixed message with an invalid body
}} {
    set msg "MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=\"something\"\r
\r
so plain
"

    set tok [mime::initialize -string $msg]
    mime::header get $tok
} [list MIME-Version {1.0 {}} Content-Type \
    {multipart/mixed {boundary something}}]


# -------------------------------------------------------------------------


test mime-16.0 {cleanly {
}} {
    set msg "MIME-Version: 1.0
Content-Type: text/plain\r
Content-Disposition: attachment
    ; param1=\"a parameter value\";  param2*1=\"another param\";
	param2*2=\"eter value\" \r
\r
so plain
"

    set tok [mime::initialize -string $msg]
	mime::header get $tok
} [list MIME-Version {1.0 {}} Content-Type {text/plain {}} Content-Disposition [
    list attachment [list param1 {a parameter value} \
	param2 {another parameter value}]]]


set char [encoding convertfrom utf-8 \xE3\x81\xAF]
test mime-16.1 {cleanly {
}} {
    set res {}
    set mime [mime::initialize -canonical text/plain -string {dawg one}]
    mime::header set $mime Content-Disposition attachment [list param1 $char]
    set msg [mime::serialize $mime]
    regsub {(Content-ID: <).*(>)} $msg {\1\2} msg
    lappend res $msg
    set mime2 [mime::initialize -string $msg]
    lappend res [mime::header get $mime2]
    return $res

} [list "MIME-Version: 1.0\r
Content-ID: <>\r
Content-Type: text/plain\r
Content-Disposition: attachment\r
	; param1*0*=utf-8''%E3%81%AF\r
\r
dawg one" \
[list MIME-Version {1.0 {}} Content-ID {<> {}} Content-Type {text/plain {}} \
    Content-Disposition [list attachment [list param1 $char]]]

]

testsuiteCleanup
set [namespace current]::done 1
return
}

after 0 [list ::coroutine [info cmdcount]_main [namespace current]::main]
vwait [namespace current]::done
return

Changes to modules/mime/pkgIndex.tcl.

1
2
3
4

1
2
3

4



-
+
if {![package vsatisfies [package provide Tcl] 8.3]} {return}
package ifneeded smtp 1.5 [list source [file join $dir smtp.tcl]]
if {![package vsatisfies [package provide Tcl] 8.5]} {return}
package ifneeded mime 1.6 [list source [file join $dir mime.tcl]]
package ifneeded mime 1.7 [list source [file join $dir mime.tcl]]

Changes to modules/mime/smtp.tcl.

1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
18










-
+







# smtp.tcl - SMTP client
#
# Copyright (c) 1999-2000 Marshall T. Rose
# Copyright (c) 2003-2006 Pat Thoyts
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#

package require Tcl 8.3
package require mime 1.4.1
package require mime 1.7-

catch {
    package require SASL 1.0;           # tcllib 1.8
    package require SASL::NTLM 1.0;     # tcllib 1.8
}

#
382
383
384
385
386
387
388
389

390
391
392
393
394
395
396

397
398
399
400
401
402
403
404
405


406
407
408
409
410

411
412
413
414
415
416
417
382
383
384
385
386
387
388

389
390
391
392
393
394
395

396
397
398
399
400
401
402
403
404

405
406
407
408
409
410

411
412
413
414
415
416
417
418







-
+






-
+








-
+
+




-
+







        lappend vrecipients $aprops(address)
    }

    # If there's no date header, get the date from the mime message.  Same for
    # the message-id.

    if {([lsearch -exact $lowerL $dateL] < 0) \
            && ([catch { ::mime::getheader $part $dateL }])} {
            && ([catch {::mime::header get $part $dateL}])} {
        lappend lowerL $dateL
        lappend mixedL $dateM
        lappend header($dateL) [::mime::parsedatetime -now proper]
    }

    if {([lsearch -exact $lowerL ${message-idL}] < 0) \
            && ([catch { ::mime::getheader $part ${message-idL} }])} {
            && ([catch {::mime::header get $part ${message-idL}}])} {
        lappend lowerL ${message-idL}
        lappend mixedL ${message-idM}
        lappend header(${message-idL}) [::mime::uniqueID]

    }

    # Get all the headers from the MIME object and save them so that they can
    # later be restored.
    set savedH [::mime::getheader $part]
    set savedH [::mime::header get $part]
    puts [list crackle $savedH]

    # Take all the headers defined earlier and add them to the MIME message.
    foreach lower $lowerL mixed $mixedL {
        foreach value $header($lower) {
            ::mime::setheader $part $mixed $value -mode append
            ::mime::header set $part $mixed $value -mode append
        }
    }

    if {[string length $client] < 1} {
        if {![string compare $servers localhost]} {
            set client localhost
        } else {
440
441
442
443
444
445
446
447
448


449
450
451
452
453
454
455
456
457

458
459
460
461
462
463






464
465
466


467
468
469
470
471
472
473
441
442
443
444
445
446
447


448
449
450
451
452
453
454
455
456
457

458






459
460
461
462
463
464



465
466
467
468
469
470
471
472
473







-
-
+
+








-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
+
+







    set ecode $errorCode
    set einfo $errorInfo

    # Send the message to bcc recipients as a MIME attachment.

    if {($code == 0) && ($bccP)} {
        set inner [::mime::initialize -canonical message/rfc822 \
                                    -header [list Content-Description \
                                                  "Original Message"] \
                                    -headers [list Content-Description \
                                                  {Original Message}] \
                                    -parts [list $part]]

        set subject "\[$bccM\]"
        if {[info exists header(subject)]} {
            append subject " " [lindex $header(subject) 0] 
        }

        set outer [::mime::initialize \
                         -canonical multipart/digest \
	    -canonical multipart/digest \
                         -header [list From $originator] \
                         -header [list Bcc ""] \
                         -header [list Date \
                                       [::mime::parsedatetime -now proper]] \
                         -header [list Subject $subject] \
                         -header [list Message-ID [::mime::uniqueID]] \
	    -headers [list \
		From [list $originator {}] \
		Bcc 
		Date [::mime::parsedatetime -now proper] \
		Subject $subject \
		Message-ID [::mime::uniqueID] \
                         -header [list Content-Description \
                                       "Blind Carbon Copy"] \
                         -parts [list $inner]]
		Content-Description {Blind Carbon Copy} \
	     -parts [list $inner]]


        set code [catch { sendmessageaux $token $outer \
                                               $sender $brecipients \
                                               $aloP } result2]
        set ecode $errorCode
        set einfo $errorInfo
500
501
502
503
504
505
506
507

508
509
510
511
512


513
514

515
516
517
518
519




520
521
522
523
524
525
526
527
500
501
502
503
504
505
506

507
508
509
510


511
512
513

514





515
516
517
518

519
520
521
522
523
524
525







-
+



-
-
+
+

-
+
-
-
-
-
-
+
+
+
+
-







        default {
            set status abort
        }
    }

    # Destroy SMTP token 'cause we're done with it.
    
    catch { finalize $token -close $status }
    catch { finalize $token -close $status } copts cres

    # Restore provided MIME object to original state (without the SMTP headers).
    
    foreach key [::mime::getheader $part -names] {
        mime::setheader $part $key "" -mode delete
    foreach key [::mime::header get $part -names] {
        mime::header set $part $key "" -mode delete
    }
    foreach {key values} $savedH {
    foreach {key value} $savedH {
        foreach value $values {
            ::mime::setheader $part $key $value -mode append
        }
    }

	::mime::header set $part $key {*}$value -mode append
    }

    retuern -options $copts $cres
    return -code $code -errorinfo $einfo -errorcode $ecode $result
}

# ::smtp::sendmessageaux --
#
#	Sends a mime object (containing a message) to some recipients using an
#       existing SMTP token.
#

Changes to modules/namespacex/namespacex.man.

26
27
28
29
30
31
32
33

34
35
36
37
38
39

40
41
42
43
44
45

46
47





48
49
50

51
52
53
54
55
56
57
58
59

60
61
62
63
64
65
66
67

68
69
70
71







72
73
26
27
28
29
30
31
32

33
34
35
36
37
38

39
40
41
42
43
44

45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
63

64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79
80
81
82
83
84
85







-
+





-
+





-
+


+
+
+
+
+


-
+








-
+







-
+




+
+
+
+
+
+
+


[call [cmd {::namespacex hook add}]  [opt [arg namespace]] [arg cmdprefix]]
[call [cmd {::namespacex hook proc}] [opt [arg namespace]] [arg arguments] [arg body]]
[call [cmd {::namespacex hook on}]   [opt [arg namespace]] [arg guardcmdprefix] [arg actioncmdprefix]]
[call [cmd {::namespacex hook next}] [arg arg]...]

[call [cmd {::namespacex info allchildren}] [arg namespace]]

This command returns a list containing the names of all child
Returns a list containing the names of all child
namespaces in the specified [arg namespace] and its children. The
names are all fully qualified.

[call [cmd {::namespacex info allvars}] [arg namespace]]

This command returns a list containing the names of all variables in
Returns a list containing the names of all variables in
the specified [arg namespace] and its children. The names are all
relative to [arg namespace], and [emph not] fully qualified.

[call [cmd {::namespacex info vars}] [arg namespace] [opt [arg pattern]]]

This command returns a list containing the names of all variables in
Returns a list containing the names of all variables in
the specified [arg namespace].

[call [cmd {::namespacex normalize}] [arg namespace]]

Returns the absolute name of [arg namespace], which is resolved relative
to the namespace of the caller, with all unneeded colon characters removed.

[call [cmd {::namespacex state get}] [arg namespace]]

This command returns a dictionary holding the names and values of all
Returns a dictionary holding the names and values of all
variables in the specified [arg namespace] and its child namespaces.

[para]
Note that the names are all relative to [arg namespace],
and [emph not] fully qualified.

[call [cmd {::namespacex state set}] [arg namespace] [arg dict]]

This command takes a dictionary holding the names and values for a set
Takes a dictionary holding the names and values for a set
of variables and replaces the current state of the specified
[arg namespace] and its child namespaces with this state.

The result of the command is the empty string.

[call [cmd {::namespacex state drop}] [arg namespace]]

This command unsets all variables in the specified [arg namespace] and
Unsets all variables in the specified [arg namespace] and
its child namespaces.

The result of the command is the empty string.


[call [cmd {::namespacex strip}] [arg namespace] [arg namespaces]]

Each item in [arg namespaces] must be the absolute normalized name of a child
namespace of [arg prefix].  Returns the corresponding list of relative names of
child namespaces.

[list_end]
[manpage_end]

Changes to modules/namespacex/namespacex.tcl.

11
12
13
14
15
16
17
18

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

18
19
20
21
22
23
24
25







-
+








# # ## ### ##### ######## ############# ######################
## Requisites

package require Tcl 8.5  ; # namespace ensembles, {*}

namespace eval ::namespacex {
    namespace export add hook info state
    namespace export add hook info normalize strip state
    namespace ensemble create

    namespace eval hook {
	namespace export add proc on next
	namespace ensemble create

	# add - hook a command prefix into the chain of unknown handlers for a
179
180
181
182
183
184
185

186
187


188
189
190
191
192

193
194

195
196


197
198
199
200
201
202
203
204

205

206

207
208


209
210
211
212
213















214
215
216
217
218
219
220
221
222


223
224
225
226
227
228


229
230
231
232
233
234


235
236
237
238
239
240
241
242
243
244

245
246
247
248
249

250
251
252
253
254
179
180
181
182
183
184
185
186


187
188
189
190
191
192

193
194
195
196


197
198
199
200
201
202
203
204
205
206
207
208
209

210
211
212
213
214





215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244


245
246
247
248
249
250


251
252
253
254
255
256
257
258
259
260
261

262
263
264
265
266
267
268
269
270
271
272
273







+
-
-
+
+




-
+


+
-
-
+
+








+

+
-
+


+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









+
+




-
-
+
+




-
-
+
+









-
+





+





        return -code $rc $result
    }
}

# # ## ### ##### ######## ############# ######################
## Implementation :: Info - Visible API


proc ::namespacex::info::allvars {ns} {
    if {![string match {::*} $ns]} { set ns ::$ns }
proc ::namespacex::info::allvars ns {
    set ns [uplevel 1 [list [namespace parent] normalize $ns]]
    ::set result [::info vars ${ns}::*]
    foreach cns [allchildren $ns] {
	lappend result {*}[::info vars ${cns}::*]
    }
    return [Strip $ns $result]
    return [[namespace parent] strip $ns $result]
}


proc ::namespacex::info::allchildren {ns} {
    if {![string match {::*} $ns]} { set ns ::$ns }
proc ::namespacex::info::allchildren ns {
    set ns [uplevel 1 [list [namespace parent] normalize $ns]]
    ::set result [list]
    foreach cns [::namespace children $ns] {
	lappend result {*}[allchildren $cns]
	lappend result $cns
    }
    return $result
}


proc ::namespacex::info::vars {ns {pattern *}} {
    set ns [uplevel 1 [list [namespace parent] normalize $ns]]
    return [Strip $ns [::info vars ${ns}::$pattern]]
    return [[namespace parent] strip $ns [::info vars ${ns}::$pattern]]
}


# this implementation avoids string operations
proc ::namespacex::info::Strip {ns itemlist} {
    set n [string length $ns]
    if {![string match {::*} $ns]} {
	incr n 4
    } else {
proc ::namespacex::normalize ns {
    if {[uplevel 1 [list ::namespace exists $ns]]} {
	return [uplevel 1 [list namespace eval $ns {::namespace current}]]
    }
    if {![string match ::* $ns]} {
	set ns [uplevel 1 {::namespace current}]::$ns
    }
    regsub {::+} $ns :: ns
    return $ns
}


proc ::namespacex::strip {ns itemlist} {
    set n [string length $ns]
    if {$ns ne {::}} {
	incr n 2
    }

    set result {}
    foreach i $itemlist {
	lappend result [string range $i $n end]
    }
    return $result
}



# # ## ### ##### ######## ############# ######################
## Implementation :: State - Visible API

proc ::namespacex::state::drop {ns} {
    if {![string match {::*} $ns]} { ::set ns ::$ns }
proc ::namespacex::state::drop ns {
    ::set ns [uplevel 1 [list [namespace parent] normalize $ns]]
    namespace eval $ns [list ::unset {*}[::namespacex info allvars $ns]]
    return
}

proc ::namespacex::state::get {ns} {
    if {![string match {::*} $ns]} { ::set ns ::$ns }
proc ::namespacex::state::get ns {
    ::set ns [uplevel 1 [list [namespace parent] normalize $ns]]
    ::set result {}
    foreach v [::namespacex info allvars $ns] {
	namespace upvar $ns $v value
	lappend result $v $value
    }
    return $result
}

proc ::namespacex::state::set {ns state} {
    if {![string match {::*} $ns]} { ::set ns ::$ns }
    ::set ns [uplevel 1 [list [namespace parent] normalize $ns]]
    # Inlined 'state drop'.
    namespace eval $ns [list ::unset  {*}[::namespacex info allvars $ns]]
    namespace eval $ns [list variable {*}$state]
    return
}


# # ## ### ##### ######## ############# ######################
## Ready

package provide namespacex 0.1

Changes to modules/namespacex/namespacex.test.

12
13
14
15
16
17
18


19
20
21
22
23
24
25
26
27









28
29
30
31
32
33
34
35
36
37









38
39
40
41
42
43
44
45







46
47

48

49
50
51



52

53
54
55



56

57
58
59
60
61
62
63








64
65
66
67
68
69
70
71







72
73


74
75
76
77



78

79
80
81



82

83
84
85
86
87
88
89







90

91
92
93
94
95
96
97







98

99
100
101
102
103
104
105







106
107


108
109
110
111



112

113
114
115



116

117
118
119
120
121
122
123







124

125
126
127
128
129
130
131







132

133
134
135
136
137
138
139







140

141
142
143
144
145
146
147







148

149
150
151
152
153
154
155








156
157
158
159
160
161
162
163







164
165

166

167
168
169



170

171
172
173



174

175
176
177
178
179
180
181







182

183
184
185
186
187
188
189








190
191
192
193
194
195
196
197







198

199
200
201
202
203
204
205







206

207
208
209
210
211
212
213








214
215
216
217
218
219
220
221







222
223

224

225
226
227



228

229
230
231



232

233
234
235
236
237
238
239
240









241
242
243
244
245
246
247
248
249








250

251
252
253
254
255
256
257
258








259

260
261
262
263
264
265
266
267








268

269
270
271
272
273
274
275
276









277
278
279
280
281
282
283
284
285








286
287

288

289
290
291



292

293
294
295




296
297
298
299



300

301
302
303
304
305
306
307
308
309
310










311

312
313
314
315
316
317
318
319
320
321










322

323
324
325
326
327
328
329
330
331
332










333

334
335
336
337
338
339
340
341
342
343










344
345
346


347
348
349
350
351





12
13
14
15
16
17
18
19
20









21
22
23
24
25
26
27
28
29
30









31
32
33
34
35
36
37
38
39
40







41
42
43
44
45
46
47
48

49
50
51



52
53
54
55
56



57
58
59
60
61







62
63
64
65
66
67
68
69
70







71
72
73
74
75
76
77
78

79
80
81



82
83
84
85
86



87
88
89
90
91







92
93
94
95
96
97
98
99
100







101
102
103
104
105
106
107
108
109







110
111
112
113
114
115
116
117

118
119
120



121
122
123
124
125



126
127
128
129
130







131
132
133
134
135
136
137
138
139







140
141
142
143
144
145
146
147
148







149
150
151
152
153
154
155
156
157







158
159
160
161
162
163
164
165
166







167
168
169
170
171
172
173
174
175







176
177
178
179
180
181
182
183

184
185
186



187
188
189
190
191



192
193
194
195
196







197
198
199
200
201
202
203
204
205







206
207
208
209
210
211
212
213
214







215
216
217
218
219
220
221
222
223







224
225
226
227
228
229
230
231
232







233
234
235
236
237
238
239
240
241







242
243
244
245
246
247
248
249

250
251
252



253
254
255
256
257



258
259
260
261
262








263
264
265
266
267
268
269
270
271
272








273
274
275
276
277
278
279
280
281
282








283
284
285
286
287
288
289
290
291
292








293
294
295
296
297
298
299
300
301
302








303
304
305
306
307
308
309
310
311
312








313
314
315
316
317
318
319
320
321

322
323
324



325
326
327
328
329



330
331
332
333
334



335
336
337
338
339










340
341
342
343
344
345
346
347
348
349
350
351










352
353
354
355
356
357
358
359
360
361
362
363










364
365
366
367
368
369
370
371
372
373
374
375










376
377
378
379
380
381
382
383
384
385
386


387
388
389




390
391
392
393
394







+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+

-
+

+
-
-
-
+
+
+

+
-
-
-
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+

-
+
+

-
-
-
+
+
+

+
-
-
-
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+

-
+
+

-
-
-
+
+
+

+
-
-
-
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+

-
+

+
-
-
-
+
+
+

+
-
-
-
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+

-
+

+
-
-
-
+
+
+

+
-
-
-
+
+
+

+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
+

+
-
-
-
+
+
+

+
-
-
-
+
+
+
+

-
-
-
+
+
+

+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
-
+
+

-
-
-
-
+
+
+
+
+

testing {
    useLocal namespacex.tcl namespacex
}

# -------------------------------------------------------------------------


namespace eval Q {
proc ns_setup {} {
    namespace eval ::X {
        namespace eval A {}
        namespace eval B {
            namespace eval D {}
        }
        namespace eval C {}
    }
}
    proc ns_setup {} {
	namespace eval X {
	    namespace eval A {}
	    namespace eval B {
		namespace eval D {}
	    }
	    namespace eval C {}
	}
    }

proc ns2_setup {} {
    namespace eval ::X {
        variable vXa 1
        variable vXb aleph
        namespace eval B {
            variable vB 3
        }
    }
}
    proc ns2_setup {} {
	namespace eval X::Y {
	    variable vXa 1
	    variable vXb aleph
	    namespace eval B {
		variable vB 3
	    }
	}
    }

proc ns3_setup {} {
    namespace eval ::X {
        namespace eval B {
            variable vB mjolnir
        }
    }
}
    proc ns3_setup {} {
	namespace eval X {
	    namespace eval B {
		variable vB mjolnir
	    }
	}
    }

# -------------------------------------------------------------------------
    # -------------------------------------------------------------------------


test namespacex-info-allchildren-1.0 {namespacex info allchildren, wrong\#args} -body {
    namespacex info allchildren
} -returnCodes error -result {wrong # args: should be "namespacex info allchildren ns"}
    test namespacex-info-allchildren-1.0 {namespacex info allchildren, wrong\#args} -body {
	namespacex info allchildren
    } -returnCodes error -result {wrong # args: should be "namespacex info allchildren ns"}


test namespacex-info-allchildren-1.1 {namespacex info allchildren, wrong\#args} -body {
    namespacex info allchildren N X
} -returnCodes error -result {wrong # args: should be "namespacex info allchildren ns"}
    test namespacex-info-allchildren-1.1 {namespacex info allchildren, wrong\#args} -body {
	namespacex info allchildren N X
    } -returnCodes error -result {wrong # args: should be "namespacex info allchildren ns"}


test namespacex-info-allchildren-2.0.0 {namespacex info allchildren} -setup {
    ns_setup
} -body {
    lsort -dict [namespacex info allchildren ::X]
} -cleanup {
    namespace delete ::X
} -result {::X::A ::X::B ::X::B::D ::X::C}
    test namespacex-info-allchildren-2.0.0 {namespacex info allchildren} -setup {
	ns_setup
    } -body {
	lsort -dict [namespacex strip [namespace current] [namespacex info allchildren X]]
    } -cleanup {
	namespace delete X
    } -result {X::A X::B X::B::D X::C}


test namespacex-info-allchildren-2.0.1 {namespacex info allchildren} -setup {
    ns_setup
} -body {
    lsort -dict [namespacex info allchildren X]
} -cleanup {
    namespace delete ::X
} -result {::X::A ::X::B ::X::B::D ::X::C}
    test namespacex-info-allchildren-2.0.1 {namespacex info allchildren} -setup {
	ns_setup
    } -body {
	lsort -dict [namespacex strip [namespace current] [namespacex info allchildren X]]
    } -cleanup {
	namespace delete X
    } -result {X::A X::B X::B::D X::C}

# -------------------------------------------------------------------------
    # -------------------------------------------------------------------------


test namespacex-info-vars-1.0 {namespacex info vars, wrong\#args} -body {
    namespacex info vars
} -returnCodes error -result {wrong # args: should be "namespacex info vars ns ?pattern?"}
    test namespacex-info-vars-1.0 {namespacex info vars, wrong\#args} -body {
	namespacex info vars
    } -returnCodes error -result {wrong # args: should be "namespacex info vars ns ?pattern?"}


test namespacex-info-vars-1.1 {namespacex info vars, wrong\#args} -body {
    namespacex info vars N P X
} -returnCodes error -result {wrong # args: should be "namespacex info vars ns ?pattern?"}
    test namespacex-info-vars-1.1 {namespacex info vars, wrong\#args} -body {
	namespacex info vars N P X
    } -returnCodes error -result {wrong # args: should be "namespacex info vars ns ?pattern?"}


test namespacex-info-vars-2.0 {namespacex info vars} -setup {
    ns2_setup
} -body {
    lsort -dict [namespacex info vars ::X]
} -cleanup {
    namespace delete ::X
} -result {vXa vXb}
    test namespacex-info-vars-2.0 {namespacex info vars} -setup {
	ns2_setup
    } -body {
	lsort -dict [namespacex info vars X::::Y]
    } -cleanup {
	namespace delete X
    } -result {vXa vXb}


test namespacex-info-vars-2.1 {namespacex info vars} -setup {
    namespace eval ::X {}
} -body {
    lsort -dict [namespacex info vars ::X]
} -cleanup {
    namespace delete ::X
} -result {}
    test namespacex-info-vars-2.1 {namespacex info vars} -setup {
	namespace eval X {}
    } -body {
	lsort -dict [namespacex info vars X]
    } -cleanup {
	namespace delete X
    } -result {}


test namespacex-info-vars-2.2 {namespacex info vars} -setup {
    ns3_setup
} -body {
    lsort -dict [namespacex info vars ::X]
} -cleanup {
    namespace delete ::X
} -result {}
    test namespacex-info-vars-2.2 {namespacex info vars} -setup {
	ns3_setup
    } -body {
	lsort -dict [namespacex info vars X]
    } -cleanup {
	namespace delete X
    } -result {}

# -------------------------------------------------------------------------
    # -------------------------------------------------------------------------


test namespacex-info-allvars-1.0 {namespacex info allvars, wrong\#args} -body {
    namespacex info allvars
} -returnCodes error -result {wrong # args: should be "namespacex info allvars ns"}
    test namespacex-info-allvars-1.0 {namespacex info allvars, wrong\#args} -body {
	namespacex info allvars
    } -returnCodes error -result {wrong # args: should be "namespacex info allvars ns"}


test namespacex-info-allvars-1.1 {namespacex info allvars, wrong\#args} -body {
    namespacex info allvars N X
} -returnCodes error -result {wrong # args: should be "namespacex info allvars ns"}
    test namespacex-info-allvars-1.1 {namespacex info allvars, wrong\#args} -body {
	namespacex info allvars N X
    } -returnCodes error -result {wrong # args: should be "namespacex info allvars ns"}


test namespacex-info-allvars-2.0.0 {namespacex info allvars} -setup {
    ns2_setup
} -body {
    lsort -dict [namespacex info allvars ::X]
} -cleanup {
    namespace delete ::X
} -result {B::vB vXa vXb}
    test namespacex-info-allvars-2.0.0 {namespacex info allvars} -setup {
	ns2_setup
    } -body {
	lsort -dict [namespacex info allvars X::::Y]
    } -cleanup {
	namespace delete X
    } -result {B::vB vXa vXb}


test namespacex-info-allvars-2.0.1 {namespacex info allvars} -setup {
    ns2_setup
} -body {
    lsort -dict [namespacex info allvars X]
} -cleanup {
    namespace delete ::X
} -result {B::vB vXa vXb}
    test namespacex-info-allvars-2.0.1 {namespacex info allvars} -setup {
	ns2_setup
    } -body {
	lsort -dict [namespacex info allvars X::Y]
    } -cleanup {
	namespace delete X
    } -result {B::vB vXa vXb}


test namespacex-info-allvars-2.1.0 {namespacex info allvars} -setup {
    namespace eval ::X {}
} -body {
    lsort -dict [namespacex info allvars ::X]
} -cleanup {
    namespace delete ::X
} -result {}
    test namespacex-info-allvars-2.1.0 {namespacex info allvars} -setup {
	namespace eval X {}
    } -body {
	lsort -dict [namespacex info allvars X]
    } -cleanup {
	namespace delete X
    } -result {}


test namespacex-info-allvars-2.1.1 {namespacex info allvars} -setup {
    namespace eval ::X {}
} -body {
    lsort -dict [namespacex info allvars X]
} -cleanup {
    namespace delete ::X
} -result {}
    test namespacex-info-allvars-2.1.1 {namespacex info allvars} -setup {
	namespace eval X {}
    } -body {
	lsort -dict [namespacex info allvars X]
    } -cleanup {
	namespace delete X
    } -result {}


test namespacex-info-allvars-2.2.0 {namespacex info allvars} -setup {
    ns3_setup
} -body {
    lsort -dict [namespacex info allvars ::X]
} -cleanup {
    namespace delete ::X
} -result {B::vB}
    test namespacex-info-allvars-2.2.0 {namespacex info allvars} -setup {
	ns3_setup
    } -body {
	lsort -dict [namespacex info allvars X]
    } -cleanup {
	namespace delete X
    } -result {B::vB}


test namespacex-info-allvars-2.2.1 {namespacex info allvars} -setup {
    ns3_setup
} -body {
    lsort -dict [namespacex info allvars X]
} -cleanup {
    namespace delete ::X
} -result {B::vB}
    test namespacex-info-allvars-2.2.1 {namespacex info allvars} -setup {
	ns3_setup
    } -body {
	lsort -dict [namespacex info allvars X]
    } -cleanup {
	namespace delete X
    } -result {B::vB}

# -------------------------------------------------------------------------
    # -------------------------------------------------------------------------


test namespacex-state-get-1.0 {namespacex state get, wrong\#args} -body {
    namespacex state get
} -returnCodes error -result {wrong # args: should be "namespacex state get ns"}
    test namespacex-state-get-1.0 {namespacex state get, wrong\#args} -body {
	namespacex state get
    } -returnCodes error -result {wrong # args: should be "namespacex state get ns"}


test namespacex-state-get-1.1 {namespacex state get, wrong\#args} -body {
    namespacex state get N X
} -returnCodes error -result {wrong # args: should be "namespacex state get ns"}
    test namespacex-state-get-1.1 {namespacex state get, wrong\#args} -body {
	namespacex state get N X
    } -returnCodes error -result {wrong # args: should be "namespacex state get ns"}


test namespacex-state-get-2.0.0 {namespacex state get} -setup {
    ns2_setup
} -body {
    dictsort [namespacex state get ::X]
} -cleanup {
    namespace delete ::X
} -result {B::vB 3 vXa 1 vXb aleph}
    test namespacex-state-get-2.0.0 {namespacex state get} -setup {
	ns2_setup
    } -body {
	dictsort [namespacex state get X::Y]
    } -cleanup {
	namespace delete X
    } -result {B::vB 3 vXa 1 vXb aleph}


test namespacex-state-get-2.0.1 {namespacex state get} -setup {
    ns2_setup
} -body {
    dictsort [namespacex state get X]
} -cleanup {
    namespace delete ::X
} -result {B::vB 3 vXa 1 vXb aleph}
    test namespacex-state-get-2.0.1 {namespacex state get} -setup {
	ns2_setup
    } -body {
	dictsort [namespacex state get X::Y]
    } -cleanup {
	namespace delete X
    } -result {B::vB 3 vXa 1 vXb aleph}


test namespacex-state-get-2.1.0 {namespacex state get} -setup {
    namespace eval ::X {}
} -body {
    dictsort [namespacex state get ::X]
} -cleanup {
    namespace delete ::X
} -result {}
    test namespacex-state-get-2.1.0 {namespacex state get} -setup {
	namespace eval ::X {}
    } -body {
	dictsort [namespacex state get ::X]
    } -cleanup {
	namespace delete ::X
    } -result {}


test namespacex-state-get-2.1.1 {namespacex state get} -setup {
    namespace eval ::X {}
} -body {
    dictsort [namespacex state get X]
} -cleanup {
    namespace delete ::X
} -result {}
    test namespacex-state-get-2.1.1 {namespacex state get} -setup {
	namespace eval X {}
    } -body {
	dictsort [namespacex state get X]
    } -cleanup {
	namespace delete X
    } -result {}


test namespacex-state-get-2.2.0 {namespacex state get} -setup {
    ns3_setup
} -body {
    dictsort [namespacex state get ::X]
} -cleanup {
    namespace delete ::X
} -result {B::vB mjolnir}
    test namespacex-state-get-2.2.0 {namespacex state get} -setup {
	ns3_setup
    } -body {
	dictsort [namespacex state get X]
    } -cleanup {
	namespace delete X
    } -result {B::vB mjolnir}


test namespacex-state-get-2.2.1 {namespacex state get} -setup {
    ns3_setup
} -body {
    dictsort [namespacex state get X]
} -cleanup {
    namespace delete ::X
} -result {B::vB mjolnir}
    test namespacex-state-get-2.2.1 {namespacex state get} -setup {
	ns3_setup
    } -body {
	dictsort [namespacex state get X]
    } -cleanup {
	namespace delete X
    } -result {B::vB mjolnir}

# -------------------------------------------------------------------------
    # -------------------------------------------------------------------------


test namespacex-state-drop-1.0 {namespacex state drop, wrong\#args} -body {
    namespacex state drop
} -returnCodes error -result {wrong # args: should be "namespacex state drop ns"}
    test namespacex-state-drop-1.0 {namespacex state drop, wrong\#args} -body {
	namespacex state drop
    } -returnCodes error -result {wrong # args: should be "namespacex state drop ns"}


test namespacex-state-drop-1.1 {namespacex state drop, wrong\#args} -body {
    namespacex state drop N X
} -returnCodes error -result {wrong # args: should be "namespacex state drop ns"}
    test namespacex-state-drop-1.1 {namespacex state drop, wrong\#args} -body {
	namespacex state drop N X
    } -returnCodes error -result {wrong # args: should be "namespacex state drop ns"}


test namespacex-state-drop-2.0.0 {namespacex state drop} -setup {
    ns2_setup
} -body {
    namespacex state drop ::X
    dictsort [namespacex state get ::X]
} -cleanup {
    namespace delete ::X
} -result {}
    test namespacex-state-drop-2.0.0 {namespacex state drop} -setup {
	ns2_setup
    } -body {
	namespacex state drop X
	dictsort [namespacex state get X]
    } -cleanup {
	namespace delete X
    } -result {}


test namespacex-state-drop-2.0.1 {namespacex state drop} -setup {
    ns2_setup
} -body {
    namespacex state drop X
    dictsort [namespacex state get X]
} -cleanup {
    namespace delete ::X
} -result {}
    test namespacex-state-drop-2.0.1 {namespacex state drop} -setup {
	ns2_setup
    } -body {
	namespacex state drop X
	dictsort [namespacex state get X]
    } -cleanup {
	namespace delete X
    } -result {}


test namespacex-state-drop-2.1.0 {namespacex state drop} -setup {
    namespace eval ::X {}
} -body {
    namespacex state drop X
    dictsort [namespacex state get ::X]
} -cleanup {
    namespace delete ::X
} -result {}
    test namespacex-state-drop-2.1.0 {namespacex state drop} -setup {
	namespace eval X {}
    } -body {
	namespacex state drop X
	dictsort [namespacex state get X]
    } -cleanup {
	namespace delete X
    } -result {}


test namespacex-state-drop-2.1.1 {namespacex state drop} -setup {
    namespace eval ::X {}
} -body {
    namespacex state drop X
    dictsort [namespacex state get X]
} -cleanup {
    namespace delete ::X
} -result {}
    test namespacex-state-drop-2.1.1 {namespacex state drop} -setup {
	namespace eval X {}
    } -body {
	namespacex state drop X
	dictsort [namespacex state get X]
    } -cleanup {
	namespace delete X
    } -result {}


test namespacex-state-drop-2.2.0 {namespacex state drop} -setup {
    ns3_setup
} -body {
    namespacex state drop X
    dictsort [namespacex state get ::X]
} -cleanup {
    namespace delete ::X
} -result {}
    test namespacex-state-drop-2.2.0 {namespacex state drop} -setup {
	ns3_setup
    } -body {
	namespacex state drop X
	dictsort [namespacex state get X]
    } -cleanup {
	namespace delete X
    } -result {}


test namespacex-state-drop-2.2.1 {namespacex state drop} -setup {
    ns3_setup
} -body {
    namespacex state drop X
    dictsort [namespacex state get X]
} -cleanup {
    namespace delete ::X
} -result {}
    test namespacex-state-drop-2.2.1 {namespacex state drop} -setup {
	ns3_setup
    } -body {
	namespacex state drop X
	dictsort [namespacex state get X]
    } -cleanup {
	namespace delete X
    } -result {}

# -------------------------------------------------------------------------
    # -------------------------------------------------------------------------


test namespacex-state-set-1.0 {namespacex state set, wrong\#args} -body {
    namespacex state set
} -returnCodes error -result {wrong # args: should be "namespacex state set ns state"}
    test namespacex-state-set-1.0 {namespacex state set, wrong\#args} -body {
	namespacex state set
    } -returnCodes error -result {wrong # args: should be "namespacex state set ns state"}


test namespacex-state-set-1.1 {namespacex state set, wrong\#args} -body {
    namespacex state set N
} -returnCodes error -result {wrong # args: should be "namespacex state set ns state"}
    test namespacex-state-set-1.1 {namespacex state set, wrong\#args} -body {
	namespacex state set N
    } -returnCodes error -result {wrong # args: should be "namespacex state set ns state"}


test namespacex-state-set-1.2 {namespacex state set, wrong\#args} -body {
    namespacex state set N S X
} -returnCodes error -result {wrong # args: should be "namespacex state set ns state"}
    test namespacex-state-set-1.2 {namespacex state set, wrong\#args} -body {
	namespacex state set N S X
    } -returnCodes error -result {wrong # args: should be "namespacex state set ns state"}


test namespacex-state-set-2.0.0 {namespacex state set} -setup {
    ns2_setup
    set ST [namespacex state get ::X]
    ns3_setup
} -body {
    namespacex state set ::X $ST
    dictsort [namespacex state get ::X]
} -cleanup {
    namespace delete ::X
} -result {B::vB 3 vXa 1 vXb aleph}
    test namespacex-state-set-2.0.0 {namespacex state set} -setup {
	ns2_setup
	set ST [namespacex state get X::Y]
	ns3_setup
    } -body {
	namespacex state set X::Y $ST
	dictsort [namespacex state get X::Y]
    } -cleanup {
	namespace delete X
    } -result {B::vB 3 vXa 1 vXb aleph}


test namespacex-state-set-2.0.1 {namespacex state set} -setup {
    ns2_setup
    set ST [namespacex state get ::X]
    ns3_setup
} -body {
    namespacex state set X $ST
    dictsort [namespacex state get X]
} -cleanup {
    namespace delete ::X
} -result {B::vB 3 vXa 1 vXb aleph}
    test namespacex-state-set-2.0.1 {namespacex state set} -setup {
	ns2_setup
	set ST [namespacex state get X::Y]
	ns3_setup
    } -body {
	namespacex state set X::Y $ST
	dictsort [namespacex state get X::Y]
    } -cleanup {
	namespace delete X::Y
    } -result {B::vB 3 vXa 1 vXb aleph}


test namespacex-state-set-2.1.0 {namespacex state set} -setup {
    ns3_setup
    set ST [namespacex state get ::X]
    ns2_setup
} -body {
    namespacex state set ::X $ST
    dictsort [namespacex state get ::X]
} -cleanup {
    namespace delete ::X
} -result {B::vB mjolnir}
    test namespacex-state-set-2.1.0 {namespacex state set} -setup {
	ns3_setup
	set ST [namespacex state get X]
	ns2_setup
    } -body {
	namespacex state set X $ST
	dictsort [namespacex state get X]
    } -cleanup {
	namespace delete X
    } -result {B::vB mjolnir}


test namespacex-state-set-2.1.1 {namespacex state set} -setup {
    ns3_setup
    set ST [namespacex state get ::X]
    ns2_setup
} -body {
    namespacex state set X $ST
    dictsort [namespacex state get X]
} -cleanup {
    namespace delete ::X
} -result {B::vB mjolnir}
    test namespacex-state-set-2.1.1 {namespacex state set} -setup {
	ns3_setup
	set ST [namespacex state get X]
	ns2_setup
    } -body {
	namespacex state set X $ST
	dictsort [namespacex state get X]
    } -cleanup {
	namespace delete X
    } -result {B::vB mjolnir}

# -------------------------------------------------------------------------
testsuiteCleanup
    # -------------------------------------------------------------------------
    testsuiteCleanup

# Local variables:
# mode: tcl
# indent-tabs-mode: nil
# End:
    # Local variables:
    # mode: tcl
    # indent-tabs-mode: nil
    # End:
}

Changes to modules/ncgi/ncgi.tcl.

811
812
813
814
815
816
817
818

819
820
821
822
823
824
825
826
827
828
829
830

831
832
833



834
835
836
837
838
839
840
841
811
812
813
814
815
816
817

818
819
820
821
822
823
824
825
826
827
828
829

830



831
832
833

834
835
836
837
838
839
840







-
+











-
+
-
-
-
+
+
+
-







#
# Results:
#	A two-element list, the first is the primary value,
#	the second is in turn a name-value list corresponding to the
#	parameters.  Given the above example, the return value is
#	{
#		value
#		{param value param2 value param3 value3}
#		{param value param2 value2 param3 value3}
#	}

proc ::ncgi::parseMimeValue {value} {
    set parts [split $value \;]
    set results [list [string trim [lindex $parts 0]]]
    set paramList [list]
    foreach sub [lrange $parts 1 end] {
	if {[regexp -- {([^=]+)=(.+)} $sub match key val]} {
            set key [string trim [string tolower $key]]
            set val [string trim $val]
            # Allow single as well as double quotes
            if {[regexp -- {^["']} $val quote]} { ;# need a " for balance
            if {[regexp -- {^(['"])(.*)\1} $val x quote val2]} { ; # need a " for balance
                if {[regexp -- ^${quote}(\[^$quote\]*)$quote $val x val2]} {
                    # Trim quotes and any extra crap after close quote
                    set val $val2
               # Trim quotes and any extra crap after close quote
               # remove quoted quotation marks
               set val [string map {\\" "\"" \\' "\'"} $val2]
                }
            }
            lappend paramList $key $val
	}
    }
    if {[llength $paramList]} {
	lappend results $paramList
    }

Changes to modules/ncgi/ncgi.test.

573
574
575
576
577
578
579








580
581
582
583
584
585
586
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594







+
+
+
+
+
+
+
+







test ncgi-13.5 {ncgi::parseMimeValue} {
    ncgi::parseMimeValue "text/html; charset=\"iso-8859-1\"; ignored"
} {text/html {charset iso-8859-1}}

test ncgi-13.6 {ncgi::parseMimeValue} {
    ncgi::parseMimeValue "text/html; charset=\"iso-8859-1\"morecrap"
} {text/html {charset iso-8859-1}}

test ncgi-13.7 {ncgi::parseMimeValue} {
    ncgi::parseMimeValue {test/test; foo="bar\"baz\""}
} {test/test {foo bar\"baz\"}}

test ncgi-13.8 {ncgi::parseMimeValue} {
    ncgi::parseMimeValue {test/test; foo=""}
} {test/test {foo {}}}


test ncgi-14.1 {ncgi::multipart} {
    catch {ncgi::multipart "application/x-www-urlencoded" name=val+ue} err
    set err
} {Not a multipart Content-Type: application/x-www-urlencoded}

Changes to modules/nns/nns_client.man.

132
133
134
135
136
137
138
139

140
141
142
143
144
145
146
132
133
134
135
136
137
138

139
140
141
142
143
144
145
146







-
+







In this form the command returns a dictionary of all supported
options, and their current values. The list of supported options and
their meaning can be found in section [sectref OPTIONS].

[call [cmd ::nameserv::configure] [option -option]]

In this form the command is an alias for
"[cmd ::nameserv::cget] [option -option]]".
"[cmd ::nameserv::cget] [option -option]".

The list of supported options and their meaning can be found in
section [sectref OPTIONS].

[call [cmd ::nameserv::configure] "[option -option] [arg value]..."]

In this form the command is used to configure one or more of the

Changes to modules/nns/nns_server.man.

76
77
78
79
80
81
82
83

84
85
86
87
88
89
90
76
77
78
79
80
81
82

83
84
85
86
87
88
89
90







-
+







In this form the command returns a dictionary of all supported
options, and their current values. The list of supported options and
their meaning can be found in section [sectref OPTIONS].

[call [cmd ::nameserv::server::configure] [option -option]]

In this form the command is an alias for
"[cmd ::nameserv::server::cget] [option -option]]".
"[cmd ::nameserv::server::cget] [option -option]".

The list of supported options and their meaning can be found in
section [sectref OPTIONS].

[call [cmd ::nameserv::server::configure] "[option -option] [arg value]..."]

In this form the command is used to configure one or more of the

Changes to modules/oodialect/oodialect.tcl.

1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17
18
19
20
21
22



23
24
25
26
27
28
29









-
+












-
-
-







###
# oodialect.tcl
#
# Copyright (c) 2015-2018 Sean Woods
# Copyright (c) 2015 Donald K Fellows
#
# BSD License
###
# @@ Meta Begin
# Package oo::dialect 0.3.3
# Package oo::dialect 0.3.4
# Meta platform     tcl
# Meta summary      A utility for defining a domain specific language for TclOO systems
# Meta description  This package allows developers to generate
# Meta description  domain specific languages to describe TclOO
# Meta description  classes and objects.
# Meta category     TclOO
# Meta subject      oodialect
# Meta require      {Tcl 8.6}
# Meta author       Sean Woods
# Meta author       Donald K. Fellows
# Meta license      BSD
# @@ Meta End

package require TclOO

namespace eval ::oo::dialect {
  namespace export create
}

# A stack of class names
proc ::oo::dialect::Push {class} {
  ::variable class_stack
75
76
77
78
79
80
81
82

83
84
85
86
87
88

89
90
91
92
93
94
95
96
97
98

99
100
101
102
103
104
105
72
73
74
75
76
77
78

79
80
81
82
83
84

85
86
87
88
89
90
91
92
93
94

95
96
97
98
99
100
101
102







-
+





-
+









-
+







  	# We should begin with that
  	###
  	set pnspace [NSNormalize [uplevel 1 {namespace current}] $parent]
    apply [list parent {
  	  ::namespace export dynamic_methods
  	  ::namespace import -force ${parent}::dynamic_methods
  	} $NSPACE] $pnspace
	

    apply [list parent {
  	  ::namespace import -force ${parent}::define::*
  	  ::namespace export *
  	} ${NSPACE}::define] $pnspace
      set ANCESTORS [list ${pnspace}::object]
  }  
  }
  ###
  # Build our dialect template functions
  ###
  proc ${NSPACE}::define {oclass args} [string map [list %NSPACE% $NSPACE] {
	###
	# To facilitate library reloading, allow
	# a dialect to create a class from DEFINE
	###
  set class [::oo::dialect::NSNormalize [uplevel 1 {namespace current}] $oclass]
    if {[info commands $class] eq {}} {      
    if {[info commands $class] eq {}} {
	    %NSPACE%::class create $class {*}${args}
    } else {
	    ::oo::dialect::Define %NSPACE% $class {*}${args}
    }
}]
  interp alias {} ${NSPACE}::define::current_class {} \
    ::oo::dialect::Peek
129
130
131
132
133
134
135
136
137
138
139
140
141





142
143
144
145
146
147
148
149
126
127
128
129
130
131
132






133
134
135
136
137

138
139
140
141
142
143
144







-
-
-
-
-
-
+
+
+
+
+
-







  ###
  uplevel #0 [string map [list %NSPACE% [list $NSPACE] %name% [list $name] %ANCESTORS% $ANCESTORS] {
    %NSPACE%::class create %NSPACE%::object {
     superclass %ANCESTORS%
      # Put MOACish stuff in here
    }
  }]
  if {[info exists ::oo::meta::core_classes]} {
    if { "${NSPACE}::class" ni $::oo::meta::core_classes } {
      lappend ::oo::meta::core_classes "${NSPACE}::class"
    }
    if { "${NSPACE}::object" ni $::oo::meta::core_classes } {
      lappend ::oo::meta::core_classes "${NSPACE}::object"
  if { "${NSPACE}::class" ni $::oo::dialect::core_classes } {
    lappend ::oo::dialect::core_classes "${NSPACE}::class"
  }
  if { "${NSPACE}::object" ni $::oo::dialect::core_classes } {
    lappend ::oo::dialect::core_classes "${NSPACE}::object"
    }
  }
}

# Support commands; not intended to be called directly.
proc ::oo::dialect::NSNormalize {namespace qualname} {
  if {![string match ::* $qualname]} {
    set qualname ${namespace}::$qualname
258
259
260
261
262
263
264




265

253
254
255
256
257
258
259
260
261
262
263

264







+
+
+
+
-
+
  method aliases {} {
    if {[info exists ::oo::dialect::aliases([self])]} {
      return $::oo::dialect::aliases([self])
    }
  }
}

namespace eval ::oo::dialect {
  variable core_classes {::oo::class ::oo::object}
}

package provide oo::dialect 0.3.3
package provide oo::dialect 0.3.4

Changes to modules/oodialect/pkgIndex.tcl.

1


1
-
+
package ifneeded oo::dialect 0.3.3 [list source [file join $dir oodialect.tcl]]
package ifneeded oo::dialect 0.3.4 [list source [file join $dir oodialect.tcl]]

Changes to modules/oometa/oometa.man.

136
137
138
139
140
141
142
143

144
145
146
147
148
149
150
136
137
138
139
140
141
142

143
144
145
146
147
148
149
150







-
+







produce a local picture of metadata.

This method provides the following additional commands:

[call [cmd {oo::object method meta cget}] [opt [arg field]] [opt [arg ...]] [arg field]]

Attempts to locate a singlar leaf, and return its value. For single option lookups, this
is faster than [cmd {my meta getnull}] [opt [arg field]] [opt [arg ...]] [arg field]], because
is faster than [cmd {my meta getnull}] [opt [arg field]] [opt [arg ...]] [arg field], because
it performs a search instead directly instead of producing the recursive merge product
between the class metadata, the local [emph meta] variable, and THEN performing the search.

[list_end]

[vset CATEGORY tcloo]
[include ../doctools2base/include/feedback.inc]

Changes to modules/oometa/oometa.tcl.

1
2
3
4
5
6
7

8

9
10
11

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

43
44
45
46
47
48
49
1
2
3
4
5
6
7
8

9
10
11

12

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48







+
-
+


-
+
-


















-










-
+







###
# Author: Sean Woods, [email protected]
##
# TclOO routines to implement property tracking by class and object
###
package require Tcl 8.6 ;# tailcall
package require dicttool
package require oo::dialect
package provide oo::meta 0.7.1
package provide oo::meta 0.7.2

namespace eval ::oo::meta {
  variable dirty_classes {}
  set dirty_classes {}
  variable core_classes {::oo::class ::oo::object}
}

proc ::oo::meta::args_to_dict args {
  if {[llength $args]==1} {
    return [lindex $args 0]
  }
  return $args
}

proc ::oo::meta::args_to_options args {
  set result {}
  foreach {var val} [args_to_dict {*}$args] {
    lappend result [string trimleft $var -] $val
  }
  return $result
}

proc ::oo::meta::ancestors class {
  variable core_classes
  set class [::oo::meta::normalize $class]
  set core_result {}
  set queue $class
  set result {}
  # Rig things such that that the top superclasses
  # are evaluated first
  while {[llength $queue]} {
    set tqueue $queue
    set queue {}
    foreach qclass $tqueue {
      if {$qclass in $core_classes} {
      if {$qclass in $::oo::dialect::core_classes} {
        if {$qclass ni $core_result} {
          lappend core_result $qclass
        }
        continue
      }
      foreach aclass [::info class superclasses $qclass] {
        if { $aclass in $result } continue
178
179
180
181
182
183
184
185

186
187
188
189
190
191
192
177
178
179
180
181
182
183

184
185
186
187
188
189
190
191







-
+







    }
    dump {
      set info [metadata $class]
      return $info
    }
    default {
      set info [metadata $class]
      return [::dict $submethod $info {*}$args] 
      return [::dict $submethod $info {*}$args]
    }
  }
}

proc ::oo::meta::localdata {class args} {
  if {![::info exists ::oo::meta::local_property($class)]} {
    return {}
216
217
218
219
220
221
222
223
224


225
226
227
228
229
230
231
215
216
217
218
219
220
221


222
223
224
225
226
227
228
229
230







-
-
+
+







        if {$dclass in $cancestors} {
          unset -nocomplain ::oo::meta::cached_property($cclass)
          unset -nocomplain ::oo::meta::cached_hierarchy($cclass)
        }
      }
      if {![::info exists ::oo::meta::local_property($dclass)]} continue
      if {[dict getnull $::oo::meta::local_property($dclass) classinfo type:] eq "core"} {
        if {$dclass ni $::oo::meta::core_classes} {
          lappend ::oo::meta::core_classes $dclass
        if {$dclass ni $::oo::dialect::core_classes} {
          lappend ::oo::dialect::core_classes $dclass
        }
      }
    }
    set dirty_classes {}
  }

  ###
492
493
494
495
496
497
498
499

500
501
502
503
491
492
493
494
495
496
497

498
499
500
501
502







-
+




        }
      }
      default {
        foreach mclass $classlist {
          lappend mdata [::oo::meta::metadata $mclass]
        }
        set info [dict rmerge {*}$mdata $meta]
        return [dict $submethod $info {*}$args] 
        return [dict $submethod $info {*}$args]
      }
    }
  }
}

Changes to modules/oometa/pkgIndex.tcl.

1
2
3
4
5
6
7

8
1
2
3
4
5
6

7
8






-
+

#checker -scope global exclude warnUndefinedVar
# var in question is 'dir'.
if {![package vsatisfies [package provide Tcl] 8.6]} {
    # PRAGMA: returnok
    return
}
package ifneeded oo::meta   0.7.1 [list source [file join $dir oometa.tcl]]
package ifneeded oo::meta   0.7.2 [list source [file join $dir oometa.tcl]]
package ifneeded oo::option 0.3.1 [list source [file join $dir oooption.tcl]]

Changes to modules/pop3d/pop3d.man.

201
202
203
204
205
206
207
208

209
210
211
212
213
214
215
201
202
203
204
205
206
207

208
209
210
211
212
213
214
215







-
+







callback so that pop3 servers following the interface of this module
are able to use it. The [arg mbox] argument is the storage reference
as returned by the [method lookup] method of the authentication
command, see section [sectref Authentication].

[list_begin definitions]

[call [arg storageCmd] [method dele] [arg mbox] [arg msgList]]]
[call [arg storageCmd] [method dele] [arg mbox] [arg msgList]]

Deletes the messages whose numeric ids are contained in the
[arg msgList] from the mailbox specified via [arg mbox].

[call [arg storageCmd] [method lock] [arg mbox]]

This method locks the specified mailbox for use by a single connection

Changes to modules/practcl/build/build.tcl.

1
2


3
4
5





6

7
8

9
10
11
12
13
14


15
16
17
18
19
20
21
22
23







24
25
26
27
28
29
30
31
32
33
34

35
36
37
38
39


40

41
42
43
44
45
46
47

48
49
50
51
52
53
54
1
2
3
4



5
6
7
8
9
10
11
12

13
14
15
16
17


18
19
20
21







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

44
45
46

47

48
49
50
51
52
53
54
55
56
57
58
59
60
61


+
+
-
-
-
+
+
+
+
+

+

-
+




-
-
+
+


-
-
-
-
-
-
-
+
+
+
+
+
+
+











+



-

+
+
-
+
-






+







set srcdir [file dirname [file normalize [file join [pwd] [info script]]]]
set moddir [file dirname $srcdir]
if {[catch {package require clay 0.3}]} {
  source [file join $$moddir .. clay build doctool.tcl]

set version 0.11.1
set tclversion 8.5
}
::clay::doctool create AutoDoc

set version 0.13
set tclversion 8.6
set module [file tail $moddir]
set filename $module

set fout [open [file join $moddir [file tail $module].tcl] w]
set fout [open [file join $moddir $filename.tcl] w]
fconfigure $fout -translation lf
dict set map %module% $module
dict set map %version% $version
dict set map %tclversion% $tclversion
dict set map {    } {}
dict set map "\t" {    }
#dict set map {    } {}
#dict set map "\t" {    }

puts $fout [string map $map {###
    # Amalgamated package for %module%
    # Do not edit directly, tweak the source in src/ and rerun
    # build.tcl
    ###
    package require Tcl %tclversion%
    package provide %module% %version%
    namespace eval ::%module% {}
# Amalgamated package for %module%
# Do not edit directly, tweak the source in src/ and rerun
# build.tcl
###
package require Tcl %tclversion%
package provide %module% %version%
namespace eval ::%module% {}
}]

# Track what files we have included so far
set loaded {}
# These files must be loaded in a particular order

###
# Load other module code that this module will need
###
foreach {omod files} {
  httpwget wget.tcl
  clay {build/procs.tcl build/class.tcl build/object.tcl build/doctool.tcl}
} {
  foreach fname $files {
    set file [file join $moddir .. $omod $fname]
    set fin [open $file r]
    puts $fout "###\n# START: [file join $omod $fname]\n###"
    set content [::clay::cat [file join $moddir .. $omod $fname]]
    #AutoDoc scan_text $content
    puts $fout [read $fin]
    puts $fout $content
    close $fin
    puts $fout "###\n# END: [file join $omod $fname]\n###"
  }
}

foreach file {
  setup.tcl
  docbuild.tcl
  buildutil.tcl
  fileutil.tcl
  installutil.tcl
  makeutil.tcl
  {class metaclass.tcl}

  {class toolset baseclass.tcl}
74
75
76
77
78
79
80
81
82


83

84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106







81
82
83
84
85
86
87

88
89
90

91

92
93

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







-

+
+
-
+
-


-



















+
+
+
+
+
+
+
  {class subproject binary.tcl}
  {class subproject core.tcl}

  {class tool.tcl}

} {
  lappend loaded $file
  set fin [open [file join $srcdir {*}$file] r]
  puts $fout "###\n# START: [file join $file]\n###"
  set content [::clay::cat [file join $srcdir {*}$file]]
  AutoDoc scan_text $content
  puts $fout [read $fin]
  puts $fout $content
  close $fin
  puts $fout "###\n# END: [file join $file]\n###"
}


# Provide some cleanup and our final package provide
puts $fout [string map $map {
namespace eval ::%module% {
  namespace export *
}
}]
close $fout

###
# Build our pkgIndex.tcl file
###
set fout [open [file join $moddir pkgIndex.tcl] w]
fconfigure $fout -translation lf
puts $fout [string map $map {###
    if {![package vsatisfies [package provide Tcl] %tclversion%]} {return}
    package ifneeded %module% %version% [list source [file join $dir %module%.tcl]]
}]
close $fout

set manout [open [file join $moddir $filename.man] w]
puts $manout [AutoDoc manpage \
  header [string map $map [::clay::cat [file join $srcdir manual.txt]]] \
  footer [string map $map [::clay::cat [file join $srcdir footer.txt]]] \
]
close $manout

Changes to modules/practcl/build/buildutil.tcl.

1
2
3








4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


20

21
22
23
24
25
26
27



+
+
+
+
+
+
+
+








-
-
+
-







###
# Build utility functions
###

###
# Generate a proc if no command already exists by that name
###
proc Proc {name arglist body} {
  if {[info command $name] ne {}} return
  proc $name $arglist $body
}

###
# A command to do nothing. A handy way of
# negating an instruction without
# having to comment it completely out.
# It's also a handy attachment point for
# an object to be named later
###
if {[info command ::noop] eq {}} {
  proc ::noop args {}
Proc ::noop args {}
}

proc ::practcl::debug args {
  #puts $args
  ::practcl::cputs ::DEBUG_INFO $args
}

###
97
98
99
100
101
102
103















104
105
106
107
108
109
110
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  return $result
}

proc ::practcl::os {} {
  return [${::practcl::MAIN} define get TEACUP_OS]
}

###
# Build a zipfile. On tcl8.6 this invokes the native Zip implementation
# on older interpreters this invokes zip via exec
###
proc ::practcl::mkzip {exename barekit vfspath} {
  ::practcl::tcllib_require zipfile::mkzip
  ::zipfile::mkzip::mkzip $exename -runtime $barekit -directory $vfspath
}
###
# Dictionary sort a key/value list. Needed because pre tcl8.6
# does not have [emph {lsort -stride 2}]
###
proc ::practcl::sort_dict list {
  return [::lsort -stride 2 -dictionary $list]
}
if {[::package vcompare $::tcl_version 8.6] < 0} {
  # Approximate ::zipfile::mkzip with exec calls
  proc ::practcl::mkzip {exename barekit vfspath} {
    set path [file dirname [file normalize $exename]]
    set zipfile [file join $path [file rootname $exename].zip]
    file copy -force $barekit $exename
    set pwd [pwd]
123
124
125
126
127
128
129
130
131
132
133
134

135
136
137
138


139
140
141
142
143
144
145
144
145
146
147
148
149
150





151




152
153
154
155
156
157
158
159
160







-
-
-
-
-
+
-
-
-
-
+
+







  proc ::practcl::sort_dict list {
    set result {}
    foreach key [lsort -dictionary [dict keys $list]] {
      dict set result $key [dict get $list $key]
    }
    return $result
  }
} else {
  proc ::practcl::mkzip {exename barekit vfspath} {
    ::practcl::tcllib_require zipfile::mkzip
    ::zipfile::mkzip::mkzip $exename -runtime $barekit -directory $vfspath
  }
}
  proc ::practcl::sort_dict list {
    return [::lsort -stride 2 -dictionary $list]
  }
}



proc ::practcl::local_os {} {
  # If we have already run this command, return
  # a cached copy of the data
  if {[info exists ::practcl::LOCAL_INFO]} {
    return $::practcl::LOCAL_INFO
  }

Changes to modules/practcl/build/class/metaclass.tcl.

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

154
155
156
157
158
159
160
64
65
66
67
68
69
70







































































71











72
73
74
75
76
77
78
79







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
-
-
-
-
-
-
-
-
-
-
+







      }
      default {
        array $submethod define {*}$args
      }
    }
  }


  method meta {submethod args} {
    my variable meta
    if {![info exists meta]} {
      set meta {}
    }
    switch $submethod {
      dump {
        return $meta
      }
      add {
        set field [lindex $args 0]
        if {![dict exists $meta $field]} {
          dict set meta $field {}
        }
        foreach arg [lrange $args 1 end] {
          if {$arg ni [dict get $meta $field]} {
            dict lappend meta $field $arg
          }
        }
        return [dict get $meta $field]
      }
      remove {
        set field [lindex $args 0]
        if {![dict exists meta $field]} {
          return
        }
        set rlist [lrange $args 1 end]
        set olist [dict get $meta $field]
        set nlist {}
        foreach arg $olist {
          if {$arg in $rlist} continue
          lappend nlist $arg
        }
        dict set meta $field $nlist
        return $nlist
      }
      exists {
        return [dict exists $meta {*}$args]
      }
      getnull -
      get {
        if {[dict exists $meta {*}$args]} {
          return [dict get $meta {*}$args]
        }
        return {}
      }
      cget {
        set field [lindex $args 0]
        if {[dict exists $meta $field]} {
          return [dict get $meta $field]
        }
        return [lindex $args 1]
      }
      set {
        if {[llength $args]==1} {
          foreach {field value} $args {
            dict set meta [string trimright $field :]: $value
          }
        } else {
          set field [lindex $args end-1]
          set value [lindex $args end]
          dict set meta {*}[lrange $args 0 end-2] [string trimright $field :]: $value
        }
      }
      default {
        error "Valid: add cget dump exists get getnull remove set"
      }
    }
  }
  
  method graft args {
    my variable organs
    if {[llength $args] == 1} {
      error "Need two arguments"
    }
    set object {}
    foreach {stub object} $args {
      dict set organs $stub $object
      oo::objdefine [self] forward <${stub}> $object
      oo::objdefine [self] export <${stub}>
    }
    return $object
    return [my clay delegate {*}$args]
  }

  method initialize {} {}


  method link {command args} {
    my variable links
275
276
277
278
279
280
281
282

283
284
285

286
287
288
289
290
291
292
293
294
295
296
297
298
299
194
195
196
197
198
199
200

201



202







203
204
205
206
207
208
209







-
+
-
-
-
+
-
-
-
-
-
-
-







    foreach {s c} $mixinslot {
      if {$c eq {}} continue
      lappend mixins $c
    }
    oo::objdefine [self] mixin {*}$mixins
  }

  method organ {{stub all}} {
  method organ args {
    my variable organs
    if {![info exists organs]} {
      return {}
    return [my clay delegate {*}$args]
    }
    if { $stub eq "all" } {
      return $organs
    }
    if {[dict exists $organs $stub]} {
      return [dict get $organs $stub]
    }
  }

  method script script {
    eval $script
  }

  method select {} {

Changes to modules/practcl/build/class/module.tcl.

1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
21
22
23


24
25

26
27
28
29
30
31
32
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
20
21


22
23
24

25
26
27
28
29
30
31
32












-
+








-
-
+
+

-
+








###
# In the end, all C code must be loaded into a module
# This will either be a dynamically loaded library implementing
# a tcl extension, or a compiled in segment of a custom shell/app
###
::oo::class create ::practcl::module {
  superclass ::practcl::object ::practcl::product.dynamic

  method _MorphPatterns {} {
    return {{@name@} {::practcl::module.@name@} ::practcl::module}
  }
  

  method add args {
    my variable links
    set object [::practcl::object new [self] {*}$args]
    foreach linktype [$object linktype] {
      lappend links($linktype) $object
    }
    return $object
  }
  
  


  method install-headers args {}
  

  ###
  # Target handling
  ###
  method make {command args} {
    my variable make_object
    if {![info exists make_object]} {
      set make_object {}
119
120
121
122
123
124
125
126

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

126
127
128
129
130
131
132
133







-
+







      }
      task -
      target -
      add {
        set name [lindex $args 0]
        set info [uplevel #0 [list subst [lindex $args 1]]]
        set body [lindex $args 2]
        

        set nspace [namespace current]
        if {[dict exist $make_object $name]} {
          set obj [dict get $$make_object $name]
        } else {
          set obj [::practcl::make_obj new [self] $name $info $body]
          dict set make_object $name $obj
          dict set target_make $name 0
143
144
145
146
147
148
149
150

151
152
153
154
155
156
157
158
159
160
161
162

163
164

165
166
167
168
169
170
171
143
144
145
146
147
148
149

150
151
152
153
154
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
170
171
172







-
+











-
+


+







        return $obj
      }
      todo {
         foreach {name obj} $make_object {
          if {[$obj do]} {
            lappend result $name
          }
        }       
        }
      }
      do {
        global CWD SRCDIR project SANDBOX
        foreach {name obj} $make_object {
          if {[$obj do]} {
            eval [$obj define get action]
          }
        }
      }
    }
  }
  

  method child which {
    switch $which {
      delegate -
      organs {
        return [list project [my define get project] module [self]]
      }
    }
  }

 ###
335
336
337
338
339
340
341
342

343
344
345
346
347
348
349
336
337
338
339
340
341
342

343
344
345
346
347
348
349
350







-
+







          lappend errs [dict get $errdat -errorinfo]
        } else {
          lappend errs $errdat
        }
      }
    }
    if {[llength $errs]} {
      set logfile [file join $::CWD practcl.log]      
      set logfile [file join $::CWD practcl.log]
      ::practcl::log $logfile "*** ERRORS ***"
      foreach {item trace} $errs {
        ::practcl::log $logfile "###\n# ERROR\n###\n$item"
       ::practcl::log $logfile "###\n# TRACE\n###\n$trace"
      }
      ::practcl::log $logfile "*** DEBUG INFO ***"
      ::practcl::log $logfile $::DEBUG_INFO

Changes to modules/practcl/build/class/object.tcl.




1
2
3
4
5
6
7

8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17
+
+
+






-
+







###
# A generic Practcl object
###
::oo::class create ::practcl::object {
  superclass ::practcl::metaclass

  constructor {parent args} {
    my variable links define
    set organs [$parent child organs]
    my graft {*}$organs
    my clay delegate {*}$organs
    array set define $organs
    array set define [$parent child define]
    array set links {}
    if {[llength $args]==1 && [file exists [lindex $args 0]]} {
      my define set filename [lindex $args 0]
      ::practcl::product select [self]
    } elseif {[llength $args] == 1} {

Changes to modules/practcl/build/class/product.tcl.

1



2
3
4
5
6
7
8
9
10

1
2
3
4

5
6
7
8
9
10
11
-
+
+
+

-








###
# A deliverable for the build system
###
::oo::class create ::practcl::product {


  method code {section body} {
    my variable code
    ::practcl::cputs code($section) $body
  }

  method Collate_Source CWD {}

Changes to modules/practcl/build/class/project/baseclass.tcl.

120
121
122
123
124
125
126

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







+







    }
    $tkobj define set config_opts $tk_config_opts
    $tkobj compile
  }

  method child which {
    switch $which {
      delegate -
      organs {
	# A library can be a project, it can be a module. Any
	# subordinate modules will indicate their existance
        return [list project [self] module [self]]
      }
    }
  }

Changes to modules/practcl/build/class/subproject/baseclass.tcl.

8
9
10
11
12
13
14

15
16
17
18
19
20
21
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22







+








  method BuildDir {PWD} {
    return [my define get srcdir]
  }

  method child which {
    switch $which {
      delegate -
      organs {
	# A library can be a project, it can be a module. Any
	# subordinate modules will indicate their existance
        return [list project [self] module [self]]
      }
    }
  }

Changes to modules/practcl/build/fileutil.tcl.

1
2
3
4
5
6
7

8
9
10
11
12
13




14
15
16
17
18
19
20
1
2
3




4






5
6
7
8
9
10
11
12
13
14
15



-
-
-
-
+
-
-
-
-
-
-
+
+
+
+







###
# Bits stolen from fileutil
###
proc ::practcl::cat fname {
    if {![file exists $fname]} {
       return
    }

    set fin [open $fname r]
    set data [read $fin]
    close $fin
    return $data
}


###
# grep
###
proc ::practcl::grep {pattern {files {}}} {
    set result [list]
    if {[llength $files] == 0} {
	      # read from stdin
    	  set lnum 0
	      while {[gets stdin line] >= 0} {
	          incr lnum

Added modules/practcl/build/footer.txt.



1
2
+
+
[vset CATEGORY practcl]
[include ../doctools2base/include/feedback.inc]

Added modules/practcl/build/manual.txt.














1
2
3
4
5
6
7
8
9
10
11
12
13
+
+
+
+
+
+
+
+
+
+
+
+
+
[comment {-*- %module% -*-}]
[vset VERSION %version%]
[manpage_begin practcl n [vset VERSION]]
[keywords practcl]
[copyright {2016-2018 Sean Woods <[email protected]>}]
[moddesc {The The Proper Rational API for C to Tool Command Language Module}]
[titledesc {The Practcl Module}]
[category {TclOO}]
[require TclOO 1.0]
[require practcl [vset VERSION]]
[description]
The Practcl module is a tool for integrating large modules for C API
Tcl code that requires custom Tcl types and TclOO objects.

Changes to modules/practcl/pkgIndex.tcl.

1
2
3


4
1


2
3
4

-
-
+
+

###
if {![package vsatisfies [package provide Tcl] 8.5]} {return}
package ifneeded practcl 0.11.1 [list source [file join $dir practcl.tcl]]
if {![package vsatisfies [package provide Tcl] 8.6]} {return}
package ifneeded practcl 0.13 [list source [file join $dir practcl.tcl]]

Changes to modules/practcl/practcl.man.

1
2

3
4
5
6
7
8
9
10
11
12
13
14














































































































































































































15




























































































































































































































































16
17

18
19
20




21

22
23



24






25

26
27
28



29
30
31
32

33



34


35
36









































































































































































































































37
38

39

40




















































































































41

42

43
44





















































































































45
46























































































































47


48

49
50




















































































































51

52


53
54



















































































































55
56


57
58









59











































































































60
61
62

63

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220

221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475



476
477
478
479
480
481


482
483
484
485
486
487
488
489
490
491
492
493
494


495
496
497
498
499
500

501
502
503
504
505

506
507
508

509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742

743
744
745

746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863

864
865

866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983

984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105

1106
1107

1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225

1226
1227
1228

1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344

1345
1346
1347

1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470

-
+












+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+
-
-
-
+
+
+
+

+
-
-
+
+
+

+
+
+
+
+
+

+

-
-
+
+
+



-
+

+
+
+
-
+
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+

+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
-
+
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
+

-
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+

+
[comment {-*- practcl -*-}]
[vset VERSION 0.11]
[vset VERSION 0.13]
[manpage_begin practcl n [vset VERSION]]
[keywords practcl]
[copyright {2016-2018 Sean Woods <[email protected]>}]
[moddesc {The The Proper Rational API for C to Tool Command Language Module}]
[titledesc {The Practcl Module}]
[category {TclOO}]
[require TclOO 1.0]
[require practcl [vset VERSION]]
[description]
The Practcl module is a tool for integrating large modules for C API
Tcl code that requires custom Tcl types and TclOO objects.

[section {Commands}]
[list_begin definitions]

[call proc [cmd practcl::cat] [arg fname]]


[call proc [cmd putb] [arg buffername] [opt "[arg args]"]]


[call proc [cmd Proc] [arg name] [arg arglist] [arg body]]

Generate a proc if no command already exists by that name




[call proc [cmd noop] [opt "[arg args]"]]

A command to do nothing. A handy way of
negating an instruction without
having to comment it completely out.
It's also a handy attachment point for
an object to be named later




[call proc [cmd practcl::debug] [opt "[arg args]"]]


[call proc [cmd practcl::doexec] [opt "[arg args]"]]

Drop in a static copy of Tcl




[call proc [cmd practcl::doexec_in] [arg path] [opt "[arg args]"]]


[call proc [cmd practcl::dotclexec] [opt "[arg args]"]]


[call proc [cmd practcl::domake] [arg path] [opt "[arg args]"]]


[call proc [cmd practcl::domake.tcl] [arg path] [opt "[arg args]"]]


[call proc [cmd practcl::fossil] [arg path] [opt "[arg args]"]]


[call proc [cmd practcl::fossil_status] [arg dir]]


[call proc [cmd practcl::os]]


[call proc [cmd practcl::mkzip] [arg exename] [arg barekit] [arg vfspath]]

Build a zipfile. On tcl8.6 this invokes the native Zip implementation
on older interpreters this invokes zip via exec




[call proc [cmd practcl::sort_dict] [arg list]]

Dictionary sort a key/value list. Needed because pre tcl8.6
does not have [emph {lsort -stride 2}]




[call proc [cmd practcl::local_os]]


[call proc [cmd practcl::config.tcl] [arg path]]

Detect local platform




[call proc [cmd practcl::read_configuration] [arg path]]


[call proc [cmd practcl::tcllib_require] [arg pkg] [opt "[arg args]"]]
Try to load  a package, and failing that
retrieve tcllib



[call proc [cmd practcl::platform::tcl_core_options] [arg os]]


[call proc [cmd practcl::platform::tk_core_options] [arg os]]


[call proc [cmd practcl::read_rc_file] [arg filename] [opt "[arg localdat] [const ""]"]]

Read a stylized key/value list stored in a file




[call proc [cmd practcl::read_sh_subst] [arg line] [arg info]]



[call proc [cmd practcl::read_sh_file] [arg filename] [opt "[arg localdat] [const ""]"]]



[call proc [cmd practcl::read_Config.sh] [arg filename]]

A simpler form of read_sh_file tailored
to pulling data from (tcl|tk)Config.sh




[call proc [cmd practcl::read_Makefile] [arg filename]]

A simpler form of read_sh_file tailored
to pulling data from a Makefile




[call proc [cmd practcl::cputs] [arg varname] [opt "[arg args]"]]
Append arguments to a buffer
The command works like puts in that each call will also insert
a line feed. Unlike puts, blank links in the interstitial are
suppressed



[call proc [cmd practcl::tcl_to_c] [arg body]]


[call proc [cmd practcl::_tagblock] [arg text] [opt "[arg style] [const "tcl"]"] [opt "[arg note] [const ""]"]]


[call proc [cmd practcl::de_shell] [arg data]]


[call proc [cmd practcl::grep] [arg pattern] [opt "[arg files] [const ""]"]]

grep




[call proc [cmd practcl::file_lexnormalize] [arg sp]]


[call proc [cmd practcl::file_relative] [arg base] [arg dst]]


[call proc [cmd practcl::log] [arg fname] [arg comment]]


[call proc [cmd practcl::_isdirectory] [arg name]]

Installer tools




[call proc [cmd practcl::_pkgindex_directory] [arg path]]

Return true if the pkgindex file contains
any statement other than "package ifneeded"
and/or if any package ifneeded loads a DLL




[call proc [cmd practcl::_pkgindex_path_subdir] [arg path]]


[call proc [cmd practcl::pkgindex_path] [opt "[arg args]"]]

Index all paths given as though they will end up in the same
virtual file system




[call proc [cmd practcl::installDir] [arg d1] [arg d2]]


[call proc [cmd practcl::copyDir] [arg d1] [arg d2] [opt "[arg toplevel] [const "1"]"]]


[call proc [cmd practcl::trigger] [opt "[arg args]"]]


[call proc [cmd practcl::depends] [opt "[arg args]"]]


[call proc [cmd practcl::target] [arg name] [arg info] [opt "[arg action] [const ""]"]]

[list_end]

[section COMMANDS]
[section Classes]
[subsection {Class  clay::doctool}]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd constructor]]


[call method [cmd arglist] [arg arglist]]


[call method [cmd comment] [arg block]]


[call method [cmd keyword.Class] [arg resultvar] [arg commentblock] [arg name] [arg body]]

Process an oo::objdefine call that modifies the class object
itself




[call method [cmd keyword.class] [arg resultvar] [arg commentblock] [arg name] [arg body]]


[call method [cmd keyword.class_method] [arg resultvar] [arg commentblock] [arg name] [opt "[arg args]"]]


[call method [cmd keyword.method] [arg resultvar] [arg commentblock] [arg name] [opt "[arg args]"]]


[call method [cmd keyword.proc] [arg commentblock] [arg name] [arg arglist] [arg body]]


[call method [cmd reset]]


[call method [cmd section.command] [arg procinfo]]


[call method [cmd section.class] [arg class_name] [arg class_info]]


[call method [cmd manpage] [opt "[arg args]"]]


[call method [cmd scan_text] [arg text]]


[call method [cmd scan_file] [arg filename]]

[list_end]
[para]

[subsection {Class  practcl::metaclass}]
[emph "ancestors"]: [class oo::object]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd _MorphPatterns]]


[call method [cmd define] [arg submethod] [opt "[arg args]"]]


[call method [cmd graft] [opt "[arg args]"]]


[call method [cmd initialize]]


[call method [cmd link] [arg command] [opt "[arg args]"]]


[call method [cmd morph] [arg classname]]


[call method [cmd mixin] [arg slot] [arg classname]]


[call method [cmd organ] [opt "[arg args]"]]


[call method [cmd script] [arg script]]


[call method [cmd select]]


[call method [cmd source] [arg filename]]

[list_end]
[para]

[subsection {Class  practcl::toolset}]

Ancestor-less class intended to be a mixin
which defines a family of build related behaviors
that are modified when targetting either gcc or msvc



[para]
[class {Class Methods}]
[list_begin definitions]

[call method [cmd select] [arg object]]

[list_end]
[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd config.sh]]

find or fake a key/value list describing this project




[call method [cmd BuildDir] [arg PWD]]


[call method [cmd MakeDir] [arg srcdir]]


[call method [cmd read_configuration]]


[call method [cmd build-cflags] [arg PROJECT] [arg DEFS] [arg namevar] [arg versionvar] [arg defsvar]]
method DEFS
This method populates 4 variables:
name - The name of the package
version - The version of the package
defs - C flags passed to the compiler
includedir - A list of paths to feed to the compiler for finding headers




[call method [cmd critcl] [opt "[arg args]"]]


[call method [cmd make-autodetect]]

[list_end]
[para]

[subsection {Class  practcl::toolset.gcc}]
[emph "ancestors"]: [class practcl::toolset]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd Autoconf]]


[call method [cmd BuildDir] [arg PWD]]


[call method [cmd ConfigureOpts]]


[call method [cmd MakeDir] [arg srcdir]]
Detect what directory contains the Makefile template



[call method [cmd make-autodetect]]


[call method [cmd make-clean]]


[call method [cmd make-compile]]


[call method [cmd make-install] [arg DEST]]


[call method [cmd build-compile-sources] [arg PROJECT] [arg COMPILE] [arg CPPCOMPILE] [arg INCLUDES]]


[call method [cmd build-Makefile] [arg path] [arg PROJECT]]


[call method [cmd build-library] [arg outfile] [arg PROJECT]]

Produce a static or dynamic library




[call method [cmd build-tclsh] [arg outfile] [arg PROJECT]]

Produce a static executable



[list_end]
[para]

[subsection {Class  practcl::toolset.msvc}]
[emph "ancestors"]: [class practcl::toolset]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd BuildDir] [arg PWD]]
MSVC always builds in the source directory



[call method [cmd make-autodetect]]
Do nothing



[call method [cmd make-clean]]


[call method [cmd make-compile]]


[call method [cmd make-install] [arg DEST]]


[call method [cmd MakeDir] [arg srcdir]]
Detect what directory contains the Makefile template



[call method [cmd NmakeOpts]]

[list_end]
[para]

[subsection {Class  practcl::make_obj}]
[emph "ancestors"]: [class practcl::metaclass]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd constructor] [arg module_object] [arg name] [arg info] [opt "[arg action_body] [const ""]"]]
[call [cmd CPUTS] [arg varname] [arg body] [opt [arg body]...]]
Appends blocks of text to a buffer. This command tries to reduce the number
of line breaks between bodies.


[call method [cmd do]]


[call method [cmd check]]
[call [cmd practcl::_isdirectory] [arg path]]
Returns true if [arg path] is a directory, using the test 


[call method [cmd output]]


[call method [cmd reset]]


[call method [cmd triggers]]

[list_end]
[para]

[list_begin definitions]
[call [cmd practcl::object] [arg "parent"] [opt [arg "keyvaluelist"]]]
[subsection {Class  practcl::object}]
[emph "ancestors"]: [class practcl::metaclass]
[para]

A generic Practcl object

[call [cmd practcl::library] [opt [arg "keyvaluelist"]]]


[para]
[class {Methods}]
[list_begin definitions]
A Practcl object representing a library container

[call method [cmd constructor] [arg parent] [opt "[arg args]"]]

[call [cmd practcl::exe] [opt [arg "keyvaluelist"]]]

[call method [cmd child] [arg method]]


[call method [cmd go]]

[list_end]
[para]

[subsection {Class  practcl::dynamic}]

Dynamic blocks do not generate their own .c files,
instead the contribute to the amalgamation
of the main library file



[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd cstructure] [arg name] [arg definition] [opt "[arg argdat] [const ""]"]]

Parser functions




[call method [cmd include] [arg header]]


[call method [cmd include_dir] [opt "[arg args]"]]


[call method [cmd include_directory] [opt "[arg args]"]]


[call method [cmd c_header] [arg body]]


[call method [cmd c_code] [arg body]]


[call method [cmd c_function] [arg header] [arg body] [opt "[arg info] [const ""]"]]


[call method [cmd c_tcloomethod] [arg name] [arg body] [opt "[arg arginfo] [const ""]"]]


[call method [cmd cmethod] [arg name] [arg body] [opt "[arg arginfo] [const ""]"]]
Alias to classic name



[call method [cmd c_tclproc_nspace] [arg nspace]]


[call method [cmd c_tclcmd] [arg name] [arg body] [opt "[arg arginfo] [const ""]"]]


[call method [cmd c_tclproc_raw] [arg name] [arg body] [opt "[arg arginfo] [const ""]"]]
Alias to classic name



[call method [cmd tcltype] [arg name] [arg argdat]]


[call method [cmd project-compile-products]]

Module interactions




[call method [cmd implement] [arg path]]


[call method [cmd initialize]]

Practcl internals




[call method [cmd linktype]]


[call method [cmd generate-cfile-constant]]


[call method [cmd generate-cfile-header]]


[call method [cmd generate-cfile-tclapi]]

Generate code that provides implements Tcl API
calls




[call method [cmd generate-loader-module]]

Generate code that runs when the package/module is
initialized into the interpreter




[call method [cmd Collate_Source] [arg CWD]]


[call method [cmd select]]
Once an object marks itself as some
flavor of dynamic, stop trying to morph
it into something else


[list_end]
[para]

[subsection {Class  practcl::product}]

A deliverable for the build system



[para]
[class {Class Methods}]
[list_begin definitions]

[call method [cmd select] [arg object]]

[list_end]
[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd code] [arg section] [arg body]]


[call method [cmd Collate_Source] [arg CWD]]


[call method [cmd project-compile-products]]


[call method [cmd generate-debug] [opt "[arg spaces] [const ""]"]]


[call method [cmd generate-cfile-constant]]


[call method [cmd generate-cfile-public-structure]]

Populate const static data structures




[call method [cmd generate-cfile-header]]


[call method [cmd generate-cfile-global]]


[call method [cmd generate-cfile-private-typedef]]


[call method [cmd generate-cfile-private-structure]]


[call method [cmd generate-cfile-functions]]

Generate code that provides subroutines called by
Tcl API methods




[call method [cmd generate-cfile-tclapi]]

Generate code that provides implements Tcl API
calls




[call method [cmd generate-hfile-public-define]]


[call method [cmd generate-hfile-public-macro]]


[call method [cmd generate-hfile-public-typedef]]


[call method [cmd generate-hfile-public-structure]]


[call method [cmd generate-hfile-public-headers]]


[call method [cmd generate-hfile-public-function]]


[call method [cmd generate-hfile-public-includes]]


[call method [cmd generate-hfile-public-verbatim]]


[call method [cmd generate-loader-external]]


[call method [cmd generate-loader-module]]


[call method [cmd generate-stub-function]]


[call method [cmd IncludeAdd] [arg headervar] [opt "[arg args]"]]


[call method [cmd generate-tcl-loader]]


[call method [cmd generate-tcl-pre]]

This methods generates any Tcl script file
which is required to pre-initialize the C library


A Practcl object representing a wrapped executable


[call method [cmd generate-tcl-post]]
[call [cmd practcl::product] [arg "parent"] [opt [arg "keyvaluelist"]]]


[call method [cmd linktype]]


[call method [cmd Ofile] [arg filename]]


[call method [cmd project-static-packages]]

Methods called by the master project




[call method [cmd toolset-include-directory]]

Methods called by the toolset




[call method [cmd target] [arg method] [opt "[arg args]"]]

[list_end]
[para]

[subsection {Class  practcl::product.cheader}]
[emph "ancestors"]: [class practcl::product]
[para]

Flesh out several trivial varieties of product



[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd project-compile-products]]


[call method [cmd generate-loader-module]]

[list_end]
[para]

[subsection {Class  practcl::product.csource}]
[emph "ancestors"]: [class practcl::product]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd project-compile-products]]

[list_end]
[para]

[subsection {Class  practcl::product.clibrary}]
[emph "ancestors"]: [class practcl::product]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd linker-products] [arg configdict]]

[list_end]
[para]

[subsection {Class  practcl::product.dynamic}]
[emph "ancestors"]: [class practcl::dynamic] [class practcl::product]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd initialize]]

[list_end]
[para]

[subsection {Class  practcl::product.critcl}]
[emph "ancestors"]: [class practcl::dynamic] [class practcl::product]
[para]

[para]

[subsection {Class  practcl::module}]
[emph "ancestors"]: [class practcl::object] [class practcl::product.dynamic]
[para]

In the end, all C code must be loaded into a module
This will either be a dynamically loaded library implementing
a tcl extension, or a compiled in segment of a custom shell/app



[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd _MorphPatterns]]


[call method [cmd add] [opt "[arg args]"]]


[call method [cmd install-headers] [opt "[arg args]"]]


[call method [cmd make] [arg command] [opt "[arg args]"]]

Target handling
A Practcl object representing a compiled product


[call [cmd practcl::cheader] [arg "parent"] [opt [arg "keyvaluelist"]]]


[call method [cmd child] [arg which]]


[call method [cmd generate-c]]

This methods generates the contents of an amalgamated .c file
which implements the loader for a batch of tools




[call method [cmd generate-h]]

This methods generates the contents of an amalgamated .h file
which describes the public API of this module




[call method [cmd generate-loader]]


[call method [cmd initialize]]


[call method [cmd implement] [arg path]]


[call method [cmd linktype]]

[list_end]
[para]

[subsection {Class  practcl::project}]
[emph "ancestors"]: [class practcl::module]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd _MorphPatterns]]


[call method [cmd constructor] [opt "[arg args]"]]


[call method [cmd add_object] [arg object]]


[call method [cmd add_project] [arg pkg] [arg info] [opt "[arg oodefine] [const ""]"]]


[call method [cmd add_tool] [arg pkg] [arg info] [opt "[arg oodefine] [const ""]"]]


[call method [cmd build-tclcore]]


[call method [cmd child] [arg which]]


[call method [cmd linktype]]


[call method [cmd project] [arg pkg] [opt "[arg args]"]]
Exercise the methods of a sub-object



[call method [cmd tclcore]]


[call method [cmd tkcore]]


[call method [cmd tool] [arg pkg] [opt "[arg args]"]]

[list_end]
[para]

[subsection {Class  practcl::library}]
[emph "ancestors"]: [class practcl::project]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd clean] [arg PATH]]


[call method [cmd project-compile-products]]


[call method [cmd go]]


[call method [cmd generate-decls] [arg pkgname] [arg path]]


[call method [cmd implement] [arg path]]


[call method [cmd generate-make] [arg path]]
Backward compadible call



[call method [cmd linktype]]


[call method [cmd package-ifneeded] [opt "[arg args]"]]
Create a "package ifneeded"
Args are a list of aliases for which this package will answer to

A Practcl object representing an externally generated c header


[call method [cmd shared_library] [opt "[arg filename] [const ""]"]]


[call method [cmd static_library] [opt "[arg filename] [const ""]"]]

[list_end]
[para]

[subsection {Class  practcl::tclkit}]
[emph "ancestors"]: [class practcl::library]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd build-tclkit_main] [arg PROJECT] [arg PKG_OBJS]]


[call method [cmd Collate_Source] [arg CWD]]


[call method [cmd wrap] [arg PWD] [arg exename] [arg vfspath] [opt "[arg args]"]]
Wrap an executable



[list_end]
[para]

[subsection {Class  practcl::distribution}]

Standalone class to manage code distribution
This class is intended to be mixed into another class
(Thus the lack of ancestors)



[para]
[class {Class Methods}]
[list_begin definitions]

[call method [cmd Sandbox] [arg object]]


[call method [cmd select] [arg object]]


[call method [cmd claim_path] [arg path]]


[call method [cmd claim_object] [arg object]]

[list_end]
[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd scm_info]]


[call method [cmd DistroMixIn]]


[call method [cmd Sandbox]]


[call method [cmd SrcDir]]


[call method [cmd ScmTag]]


[call method [cmd ScmClone]]


[call method [cmd ScmUnpack]]


[call method [cmd ScmUpdate]]


[call method [cmd Unpack]]

[list_end]
[para]

[subsection {Class  practcl::distribution.snapshot}]
[emph "ancestors"]: [class practcl::distribution]
[para]

[para]
[class {Class Methods}]
[list_begin definitions]

[call method [cmd claim_path] [arg path]]


[call method [cmd claim_object] [arg object]]

[list_end]
[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd ScmUnpack]]

[list_end]
[para]

[subsection {Class  practcl::distribution.fossil}]
[emph "ancestors"]: [class practcl::distribution]
[para]

[para]
[class {Class Methods}]
[list_begin definitions]

[call method [cmd claim_path] [arg path]]
Check for markers in the source root
[call [cmd practcl::csource] [arg "parent"] [opt [arg "keyvaluelist"]]]


A Practcl object representing an externally generated c source file

[call method [cmd claim_object] [arg obj]]
Check for markers in the metadata


[list_end]
[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd scm_info]]


[call method [cmd ScmClone]]
Clone the source



[call method [cmd ScmTag]]


[call method [cmd ScmUnpack]]


[call method [cmd ScmUpdate]]

[list_end]
[para]

[subsection {Class  practcl::distribution.git}]
[emph "ancestors"]: [class practcl::distribution]
[para]

[para]
[class {Class Methods}]
[list_begin definitions]

[call method [cmd claim_path] [arg path]]


[call method [cmd claim_object] [arg obj]]

[list_end]
[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd ScmTag]]


[call method [cmd ScmUnpack]]


[call method [cmd ScmUpdate]]

[list_end]
[para]

[subsection {Class  practcl::subproject}]
[emph "ancestors"]: [class practcl::module]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd _MorphPatterns]]


[call method [cmd BuildDir] [arg PWD]]


[call method [cmd child] [arg which]]


[call method [cmd compile]]


[call method [cmd go]]


[call method [cmd install] [opt "[arg args]"]]
Install project into the local build system



[call method [cmd linktype]]


[call method [cmd linker-products] [arg configdict]]


[call method [cmd linker-external] [arg configdict]]


[call method [cmd linker-extra] [arg configdict]]


[call method [cmd env-bootstrap]]

Methods for packages/tools that can be downloaded
possibly built and used internally by this Practcl
process


Load the facility into the interpreter




[call method [cmd env-exec]]

Return a file path that exec can call




[call method [cmd env-install]]
[call [cmd practcl::module] [arg "parent"] [opt [arg "keyvaluelist"]]]

Install the tool into the local environment

A Practcl object representing a dynamically generated C/H/Tcl suite



[call method [cmd env-load]]

Do whatever is necessary to get the tool
into the local environment




[call method [cmd env-present]]

Check if tool is available for load/already loaded




[call method [cmd sources]]


[call method [cmd update]]


[call method [cmd unpack]]

[list_end]
[para]

[subsection {Class  practcl::subproject.source}]
[emph "ancestors"]: [class practcl::subproject] [class practcl::library]
[para]

A project which the kit compiles and integrates
the source for itself



[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd env-bootstrap]]


[call method [cmd env-present]]


[call method [cmd linktype]]

[list_end]
[para]

[subsection {Class  practcl::subproject.teapot}]
[emph "ancestors"]: [class practcl::subproject]
[para]
a copy from the teapot


[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd env-bootstrap]]


[call method [cmd env-install]]


[call method [cmd env-present]]


[call method [cmd install] [arg DEST]]

[list_end]
[para]

[subsection {Class  practcl::subproject.kettle}]
[emph "ancestors"]: [class practcl::subproject]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd kettle] [arg path] [opt "[arg args]"]]


[call method [cmd install] [arg DEST]]

[list_end]
[para]

[subsection {Class  practcl::subproject.critcl}]
[emph "ancestors"]: [class practcl::subproject]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd install] [arg DEST]]

[list_end]
[para]

[subsection {Class  practcl::subproject.sak}]
[emph "ancestors"]: [class practcl::subproject]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd env-bootstrap]]

[call [cmd practcl::submodule] [arg "parent"] [opt [arg "keyvaluelist"]]]

[call method [cmd env-install]]

A Practcl object representing a dynamically generated C/H/Tcl suite, subordinate to a module

[call method [cmd env-present]]


[call method [cmd install] [arg DEST]]


[call method [cmd install-module] [arg DEST] [opt "[arg args]"]]

[list_end]
[para]

[subsection {Class  practcl::subproject.binary}]
[emph "ancestors"]: [class practcl::subproject]
[para]

A binary package



[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd clean]]


[call method [cmd env-install]]


[call method [cmd project-compile-products]]


[call method [cmd ComputeInstall]]


[call method [cmd go]]


[call method [cmd linker-products] [arg configdict]]


[call method [cmd project-static-packages]]


[call method [cmd BuildDir] [arg PWD]]


[call method [cmd compile]]


[call method [cmd Configure]]


[call method [cmd install] [arg DEST]]

[list_end]
[para]

[subsection {Class  practcl::subproject.tea}]
[emph "ancestors"]: [class practcl::subproject.binary]
[para]

[para]

[subsection {Class  practcl::subproject.library}]
[emph "ancestors"]: [class practcl::subproject.binary] [class practcl::library]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd install] [arg DEST]]

[list_end]
[para]

[subsection {Class  practcl::subproject.external}]
[emph "ancestors"]: [class practcl::subproject.binary]
[para]
An external library


[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd install] [arg DEST]]

[list_end]
[para]

[subsection {Class  practcl::subproject.core}]
[emph "ancestors"]: [class practcl::subproject.binary]
[para]

[para]
[class {Methods}]
[list_begin definitions]

[call method [cmd env-bootstrap]]


[call method [cmd env-present]]


[call method [cmd env-install]]


[call method [cmd go]]


[call method [cmd linktype]]

[list_end]
[para]

[vset CATEGORY practcl]
[include ../doctools2base/include/feedback.inc]

[manpage_end]

Changes to modules/practcl/practcl.tcl.

1
2
3
4
5
6
7


8
9
10
11
12
13
14
1
2
3
4
5


6
7
8
9
10
11
12
13
14





-
-
+
+







###
# Amalgamated package for practcl
# Do not edit directly, tweak the source in src/ and rerun
# build.tcl
###
package require Tcl 8.5
package provide practcl 0.11.1
package require Tcl 8.6
package provide practcl 0.13
namespace eval ::practcl {}

###
# START: httpwget/wget.tcl
###
###
# Tool to download file from the web
64
65
66
67
68
69
70































































































































































































































































































































































































































































































































































































































































































































































































































































71
72
73
74
75
76
77
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    close $tmpchan
}


###
# END: httpwget/wget.tcl
###
###
# START: clay/build/procs.tcl
###
###
# Global utilities
###
if {[info commands ::ladd] eq {}} {
  proc ladd {varname args} {
    upvar 1 $varname var
    if ![info exists var] {
        set var {}
    }
    foreach item $args {
      if {$item in $var} continue
      lappend var $item
    }
    return $var
  }
}

if {[info command ::ldelete] eq {}} {
  proc ::ldelete {varname args} {
    upvar 1 $varname var
    if ![info exists var] {
        return
    }
    foreach item [lsort -unique $args] {
      while {[set i [lsearch $var $item]]>=0} {
        set var [lreplace $var $i $i]
      }
    }
    return $var
  }
}

if {[info command ::lrandom] eq {}} {
  proc ::lrandom list {
    set len [llength $list]
    set idx [expr int(rand()*$len)]
    return [lindex $list $idx]
  }
}

if {[::info commands ::tcl::dict::getnull] eq {}} {
  proc ::tcl::dict::getnull {dictionary args} {
    if {[exists $dictionary {*}$args]} {
      get $dictionary {*}$args
    }
  }
  namespace ensemble configure dict -map [dict replace\
      [namespace ensemble configure dict -map] getnull ::tcl::dict::getnull]
}

proc ::putb {buffername args} {
  upvar 1 $buffername buffer
  switch [llength $args] {
    1 {
      append buffer [lindex $args 0] \n
    }
    2 {
      append buffer [string map {*}$args] \n
    }
    default {
      error "usage: putb buffername ?map? string"
    }
  }
}
namespace eval ::clay {}
set ::clay::trace 0

proc ::clay::ancestors args {
  set result {}
  set queue {}
  foreach class [lreverse $args] {
    lappend queue $class
  }

  # Rig things such that that the top superclasses
  # are evaluated first
  while {[llength $queue]} {
    set tqueue $queue
    set queue {}
    foreach qclass $tqueue {
      foreach aclass [::info class superclasses $qclass] {
        if { $aclass in $result } continue
        if { $aclass in $queue } continue
        lappend queue $aclass
      }
    }
    foreach item $tqueue {
      if { $item ni $result } {
        lappend result $item
      }
    }
  }
  return $result
}

proc ::clay::args_to_dict args {
  if {[llength $args]==1} {
    return [lindex $args 0]
  }
  return $args
}

proc ::clay::args_to_options args {
  set result {}
  foreach {var val} [args_to_dict {*}$args] {
    lappend result [string trim $var -:] $val
  }
  return $result
}

proc ::clay::dictmerge {varname args} {
  upvar 1 $varname result
  if {![info exists result]} {
    set result {}
  }
  switch [llength $args] {
    0 {
      return
    }
    1 {
      set result [_dictmerge $result [lindex $args 0]]
      return $result
    }
    2 {
      lassign $args path value
    }
    default {
      # Merge b into a, and handle nested dicts appropriately
      set value [lindex $args end]
      set path  [lrange $args 0 end-1]
    }
  }
  if {![dict exists $result {*}$path]} {
    dict set result {*}$path $value
    return $result
  }
  if {[string index [lindex $path end] end] ne "/"} {
    dict set result {*}$path $value
    return $result
  }
  ::dict for { k v } $value {
    # Element names that end in "/" are assumed to be branches
    if {[string index $k end] eq "/" && [::dict exists $result {*}$path $k]} {
      # key exists in a and b?  let's see if both values are dicts
      # both are dicts, so merge the dicts
      set dvalue [::dict get $result {*}$path $k]
      if { [is_dict $dvalue] && [is_dict $v] } {
        ::dict set result {*}$path $k [_dictmerge $dvalue $v]
      } else {
        ::dict set result {*}$path $k $v
      }
    } else {
      ::dict set result {*}$path $k $v
    }
  }
  return $result
}

proc ::clay::_dictmerge {a b} {
  ::set result $a
  # Merge b into a, and handle nested dicts appropriately
  ::dict for { k v } $b {
    if {[string index $k end] ne "/"} {
      # Element names that do not end in "/" are assumed to be literals
      # or dict trees we intend to replace wholly
      ::dict set result $k $v
    } elseif { [::dict exists $result $k] } {
      # key exists in a and b?  let's see if both values are dicts
      # both are dicts, so merge the dicts
      if { [is_dict [::dict get $result $k]] && [is_dict $v] } {
        ::dict set result $k [_dictmerge [::dict get $result $k] $v]
      } else {
        ::dict set result $k $v
      }
    } else {
      ::dict set result $k $v
    }
  }
  return $result
}

proc ::clay::dictputb {dict} {
  set result {}
  set level -1
  _dictputb 0 $level result $dict
  return $result
}

proc ::clay::_dictputb {leaf level varname dict} {
  upvar 1 $varname result
  incr level
  foreach {field value} $dict {
    if {[string index $field end] eq "/"} {
      putb result "[string repeat "  " $level]$field \{"
      _dictputb 0 $level result $value
      putb result "[string repeat "  " $level]\}"
    } else {
      putb result "[string repeat "  " $level][list $field $value]"
    }
  }
}

###
# topic: 4969d897a83d91a230a17f166dbcaede
###
proc ::clay::dynamic_arguments {ensemble method arglist args} {
  set idx 0
  set len [llength $args]
  if {$len > [llength $arglist]} {
    ###
    # Catch if the user supplies too many arguments
    ###
    set dargs 0
    if {[lindex $arglist end] ni {args dictargs}} {
      return -code error -level 2 "Usage: $ensemble $method [string trim [dynamic_wrongargs_message $arglist]]"
    }
  }
  foreach argdef $arglist {
    if {$argdef eq "args"} {
      ###
      # Perform args processing in the style of tcl
      ###
      uplevel 1 [list set args [lrange $args $idx end]]
      break
    }
    if {$argdef eq "dictargs"} {
      ###
      # Perform args processing in the style of tcl
      ###
      uplevel 1 [list set args [lrange $args $idx end]]
      ###
      # Perform args processing in the style of clay
      ###
      set dictargs [::clay::args_to_options {*}[lrange $args $idx end]]
      uplevel 1 [list set dictargs $dictargs]
      break
    }
    if {$idx > $len} {
      ###
      # Catch if the user supplies too few arguments
      ###
      if {[llength $argdef]==1} {
        return -code error -level 2 "Usage: $ensemble $method [string trim [dynamic_wrongargs_message $arglist]]"
      } else {
        uplevel 1 [list set [lindex $argdef 0] [lindex $argdef 1]]
      }
    } else {
      uplevel 1 [list set [lindex $argdef 0] [lindex $args $idx]]
    }
    incr idx
  }
}

###
# topic: 53ab28ac5c6ee601fe1fe07b073be88e
###
proc ::clay::dynamic_wrongargs_message {arglist} {
  set result ""
  set dargs 0
  foreach argdef $arglist {
    if {$argdef in {args dictargs}} {
      set dargs 1
      break
    }
    if {[llength $argdef]==1} {
      append result " $argdef"
    } else {
      append result " ?[lindex $argdef 0]?"
    }
  }
  if { $dargs } {
    append result " ?option value?..."
  }
  return $result
}

proc ::clay::is_dict { d } {
  # is it a dict, or can it be treated like one?
  if {[catch {::dict size $d} err]} {
    #::set ::errorInfo {}
    return 0
  }
  return 1
}

proc ::clay::is_null value {
  return [expr {$value in {{} NULL}}]
}

proc ::clay::leaf args {
  set marker [string index [lindex $args end] end]
  set result [path {*}${args}]
  if {$marker eq "/"} {
    return $result
  }
  return [list {*}[lrange $result 0 end-1] [string trim [string trim [lindex $result end]] /]]
}

proc ::clay::path args {
  set result {}
  foreach item $args {
    set item [string trim $item :./]
    foreach subitem [split $item /] {
      lappend result [string trim ${subitem}]/
    }
  }
  return $result
}

proc ::clay::script_path {} {
  set path [file dirname [file join [pwd] [info script]]]
  return $path
}

proc ::clay::NSNormalize qualname {
  if {![string match ::* $qualname]} {
    set qualname ::clay::classes::$qualname
  }
  regsub -all {::+} $qualname "::"
}

proc ::clay::uuid_generate args {
  return [uuid::uuid generate]
}

namespace eval ::clay {
  variable option_class {}
  variable core_classes {::oo::class ::oo::object}
}

###
# END: clay/build/procs.tcl
###
###
# START: clay/build/class.tcl
###
oo::define oo::class {
  method clay {submethod args} {
    my variable clay
    if {![info exists clay]} {
      set clay {}
    }
    switch $submethod {
      ancestors {
        tailcall ::clay::ancestors [self]
      }
      exists {
        set path [::clay::leaf {*}$args]
        if {![info exists clay]} {
          return 0
        }
        return [dict exists $clay {*}$path]
      }
      dump {
        return $clay
      }
      getnull -
      get {
        set path $args
        set leaf [expr {[string index [lindex $path end] end] ne "/"}]
        set clayorder [::clay::ancestors [self]]
        #puts [list [self] clay get {*}$path (leaf: $leaf)]
        if {$leaf} {
          #puts [list EXISTS: (clay) [dict exists $clay {*}$path]]
          if {[dict exists $clay {*}$path]} {
            return [dict get $clay {*}$path]
          }
          #puts [list Search in the in our list of classes for an answer]
          foreach class $clayorder {
            if {$class eq [self]} continue
            if {[$class clay exists {*}$path]} {
              set value [$class clay get {*}$path]
              return $value
            }
          }
        } else {
          set result {}
          # Leaf searches return one data field at a time
          # Search in our local dict
          # Search in the in our list of classes for an answer
          foreach class [lreverse $clayorder] {
            if {$class eq [self]} continue
            ::clay::dictmerge result [$class clay get {*}$path]
          }
          if {[dict exists $clay {*}$path]} {
            ::clay::dictmerge result [dict get $clay {*}$path]
          }
          return $result
        }
      }
      merge {
        foreach arg $args {
          ::clay::dictmerge clay {*}$arg
        }
      }
      search {
        foreach aclass [::clay::ancestors [self]] {
          if {[$aclass clay exists {*}$args]} {
            return [$aclass clay get {*}$args]
          }
        }
      }
      set {
        #puts [list [self] clay SET {*}$args]
        set value [lindex $args end]
        set path [::clay::leaf {*}[lrange $args 0 end-1]]
        ::clay::dictmerge clay {*}$path $value
      }
      default {
        dict $submethod clay {*}$args
      }
    }
  }
}

###
# END: clay/build/class.tcl
###
###
# START: clay/build/object.tcl
###
oo::define oo::object {

  ###
  # title: Provide access to clay data
  # format: markdown
  # description:
  # The *clay* method allows an object access
  # to a combination of its own clay data as
  # well as to that of its class
  ###
  method clay {submethod args} {
    my variable clay claycache clayorder config option_canonical
    if {![info exists clay]} {set clay {}}
    if {![info exists claycache]} {set claycache {}}
    if {![info exists config]} {set config {}}
    if {![info exists clayorder] || [llength $clayorder]==0} {
      set clayorder [::clay::ancestors [info object class [self]] {*}[info object mixins [self]]]
    }
    switch $submethod {
      ancestors {
        return $clayorder
      }
      cget {
        # Leaf searches return one data field at a time
        # Search in our local dict
        if {[llength $args]==1} {
          set field [string trim [lindex $args 0] -:/]
          if {[info exists option_canonical($field)]} {
            set field $option_canonical($field)
          }
          if {[dict exists $config $field]} {
            return [dict get $config $field]
          }
        }
        if {[dict exists $clay {*}$args]} {
          return [dict get $clay {*}$args]
        }
        # Search in our local cache
        if {[dict exists $claycache {*}$args]} {
          return [dict get $claycache {*}$args]
        }
        # Search in the in our list of classes for an answer
        foreach class $clayorder {
          if {[$class clay exists {*}$args]} {
            set value [$class clay get {*}$args]
            dict set claycache {*}$args $value
            return $value
          }
          if {[$class clay exists const/ {*}$args]} {
            set value [$class clay get const/ {*}$args]
            dict set claycache {*}$args $value
            return $value
          }
        }
        return {}
      }
      delegate {
        if {![dict exists $clay delegate/ <class>]} {
          dict set clay delegate/ <class> [info object class [self]]
        }
        if {[llength $args]==0} {
          return [dict get $clay delegate/]
        }
        if {[llength $args]==1} {
          set stub <[string trim [lindex $args 0] <>]>
          if {![dict exists $clay delegate/ $stub]} {
            return {}
          }
          return [dict get $clay delegate/ $stub]
        }
        if {([llength $args] % 2)} {
          error "Usage: delegate
    OR
    delegate stub
    OR
    delegate stub OBJECT ?stub OBJECT? ..."
        }
        foreach {stub object} $args {
          set stub <[string trim $stub <>]>
          dict set clay delegate/ $stub $object
          oo::objdefine [self] forward ${stub} $object
          oo::objdefine [self] export ${stub}
        }
      }
      dump {
        # Do a full dump of clay data
        set result $clay
        # Search in the in our list of classes for an answer
        foreach class $clayorder {
          ::clay::dictmerge result [$class clay dump]
        }
        ::clay::dictmerge result $clay
        return $result
      }
      ensemble_map {
        set ensemble [lindex $args 0]
        my variable claycache
        set mensemble [string trim $ensemble :/]/
        if {[dict exists $claycache method_ensemble/ $mensemble]} {
          return [dict get $claycache method_ensemble/ $mensemble]
        }
        set emap [my clay get method_ensemble/ $mensemble]
        dict set claycache method_ensemble/ $mensemble $emap
        return $emap
      }
      eval {
        set script [lindex $args 0]
        set buffer {}
        set thisline {}
        foreach line [split $script \n] {
          append thisline $line
          if {![info complete $thisline]} {
            append thisline \n
            continue
          }
          set thisline [string trim $thisline]
          if {[string index $thisline 0] eq "#"} continue
          if {[string length $thisline]==0} continue
          if {[lindex $thisline 0] eq "my"} {
            # Line already calls out "my", accept verbatim
            append buffer $thisline \n
          } elseif {[string range $thisline 0 2] eq "::"} {
            # Fully qualified commands accepted verbatim
            append buffer $thisline \n
          } elseif {
            append buffer "my $thisline" \n
          }
          set thisline {}
        }
        eval $buffer
      }
      evolve -
      initialize {
        my InitializePublic
      }
      exists {
        # Leaf searches return one data field at a time
        # Search in our local dict
        if {[dict exists $clay {*}$args]} {
          return 1
        }
        # Search in our local cache
        if {[dict exists $claycache {*}$args]} {
          return 2
        }
        set count 2
        # Search in the in our list of classes for an answer
        foreach class $clayorder {
          incr count
          if {[$class clay exists {*}$args]} {
            return $count
          }
        }
        return 0
      }
      flush {
        set claycache {}
        set clayorder [::clay::ancestors [info object class [self]] {*}[info object mixins [self]]]
      }
      forward {
        oo::objdefine [self] forward {*}$args
      }
      getnull -
      get {
        set leaf [expr {[string index [lindex $args end] end] ne "/"}]
        #puts [list [self] clay get {*}$args (leaf: $leaf)]
        if {$leaf} {
          #puts [list EXISTS: (clay) [dict exists $clay {*}$args]]
          if {[dict exists $clay {*}$args]} {
            return [dict get $clay {*}$args]
          }
          # Search in our local cache
          #puts [list EXISTS: (claycache) [dict exists $claycache {*}$args]]
          if {[dict exists $claycache {*}$args]} {
            return [dict get $claycache {*}$args]
          }
          # Search in the in our list of classes for an answer
          foreach class $clayorder {
            if {[$class clay exists {*}$args]} {
              set value [$class clay get {*}$args]
              dict set claycache {*}$args $value
              return $value
            }
          }
        } else {
          set result {}
          # Leaf searches return one data field at a time
          # Search in our local dict

          # Search in the in our list of classes for an answer
          foreach class [lreverse $clayorder] {
            ::clay::dictmerge result [$class clay get {*}$args]
          }
          if {[dict exists $clay {*}$args]} {
            ::clay::dictmerge result [dict get $clay {*}$args]
          }
          return $result
        }
      }
      leaf {
        # Leaf searches return one data field at a time
        # Search in our local dict
        if {[dict exists $clay {*}$args]} {
          return [dict get $clay {*}$args]
        }
        # Search in our local cache
        if {[dict exists $claycache {*}$args]} {
          return [dict get $claycache {*}$args]
        }
        # Search in the in our list of classes for an answer
        foreach class $clayorder {
          if {[$class clay exists {*}$args]} {
            set value [$class clay get {*}$args]
            dict set claycache {*}$args $value
            return $value
          }
        }
      }
      merge {
        foreach arg $args {
          ::clay::dictmerge clay {*}$arg
        }
      }
      mixin {
        ###
        # Mix in the class
        ###
        set prior  [info object mixins [self]]
        set newmixin {}
        foreach item $args {
          lappend newmixin ::[string trimleft $item :]
        }
        set newmap $args
        foreach class $prior {
          if {$class ni $newmixin} {
            set script [$class clay get mixin/ unmap-script]
            if {[string length $script]} {
              if {[catch $script err errdat]} {
                puts stderr "[self] MIXIN ERROR POPPING $class:\n[dict get $errdat -errorinfo]"
              }
            }
          }
        }
        ::oo::objdefine [self] mixin {*}$args
        ###
        # Build a compsite map of all ensembles defined by the object's current
        # class as well as all of the classes being mixed in
        ###
        my InitializePublic
        foreach class $newmixin {
          if {$class ni $prior} {
            set script [$class clay get mixin/ map-script]
            if {[string length $script]} {
              if {[catch $script err errdat]} {
                puts stderr "[self] MIXIN ERROR PUSHING $class:\n[dict get $errdat -errorinfo]"
              }
            }
          }
        }
        foreach class $newmixin {
          set script [$class clay search mixin/ react-script]
          if {[string length $script]} {
            if {[catch $script err errdat]} {
              puts stderr "[self] MIXIN ERROR PEEKING $class:\n[dict get $errdat -errorinfo]"
            }
            break
          }
        }
      }
      mixinmap {
        my variable clay
        if {![dict exists $clay mixin]} {
          dict set clay mixin {}
        }
        if {[llength $args]==0} {
          return [dict get $clay mixin]
        } elseif {[llength $args]==1} {
          return [dict getnull $clay mixin [lindex $args 0]]
        } else {
          foreach {slot classes} $args {
            dict set clay mixin $slot $classes
          }
          set claycache {}
          set classlist {}
          foreach {item class} [dict get $clay mixin] {
            if {$class ne {}} {
              lappend classlist $class
            }
          }
          my clay mixin {*}$classlist
        }
      }
      provenance {
        if {[dict exists $clay {*}$args]} {
          return self
        }
        foreach class $clayorder {
          if {[$class clay exists {*}$args]} {
            return $class
          }
        }
        return {}
      }
      replace {
        set clay [lindex $args 0]
      }
      source {
        source [lindex $args 0]
      }
      set {
        #puts [list [self] clay SET {*}$args]
        set claycache {}
        ::clay::dictmerge clay {*}$args
      }
      default {
        dict $submethod clay {*}$args
      }
    }
  }

  ###
  # React to a mixin
  ###
  method InitializePublic {} {
    my variable clayorder clay claycache config option_canonical
    set claycache {}
    set clayorder [::clay::ancestors [info object class [self]] {*}[info object mixins [self]]]
    if {![info exists config]} {
      set config {}
    }
    foreach {var value} [my clay get variable/] {
      set var [string trim $var :/]
      if { $var in {clay} } continue
      my variable $var
      if {![info exists $var]} {
        if {$::clay::trace>2} {puts [list initialize variable $var $value]}
        set $var $value
      }
    }
    foreach {var value} [my clay get dict/] {
      set var [string trim $var :/]
      my variable $var
      if {![info exists $var]} {
        set $var {}
      }
      foreach {f v} $value {
        if {![dict exists ${var} $f]} {
          if {$::clay::trace>2} {puts [list initialize dict $var $f $v]}
          dict set ${var} $f $v
        }
      }
    }
    foreach {var value} [my clay get dict/] {
      set var [string trim $var :/]
      foreach {f v} [my clay get $var/] {
        if {![dict exists ${var} $f]} {
          if {$::clay::trace>2} {puts [list initialize dict (from const) $var $f $v]}
          dict set ${var} $f $v
        }
      }
    }
    foreach {var value} [my clay get array/] {
      set var [string trim $var :/]
      if { $var eq {clay} } continue
      my variable $var
      if {![info exists $var]} { array set $var {} }
      foreach {f v} $value {
        if {![array exists ${var}($f)]} {
          if {$::clay::trace>2} {puts [list initialize array $var\($f\) $v]}
          set ${var}($f) $v
        }
      }
    }
    foreach {var value} [my clay get array/] {
      set var [string trim $var :/]
      foreach {f v} [my clay get $var/] {
        if {![array exists ${var}($f)]} {
          if {$::clay::trace>2} {puts [list initialize array (from const) $var\($f\) $v]}
          set ${var}($f) $v
        }
      }
    }
    foreach {field info} [my clay get option/] {
      set field [string trim $field -/:]
      foreach alias [dict getnull $info aliases] {
        set option_canonical($alias) $field
      }
      if {[dict exists $config $field]} continue
      set getcmd [dict getnull $info default-command]
      if {$getcmd ne {}} {
        set value [{*}[string map [list %field% $field %self% [namespace which my]] $getcmd]]
      } else {
        set value [dict getnull $info default]
      }
      dict set config $field $value
      set setcmd [dict getnull $info set-command]
      if {$setcmd ne {}} {
        {*}[string map [list %field% [list $field] %value% [list $value] %self% [namespace which my]] $setcmd]
      }
    }
  }
}


###
# END: clay/build/object.tcl
###
###
# START: setup.tcl
###
###
# Practcl
# An object oriented templating system for stamping out Tcl API calls to C
###
91
92
93
94
95
96
97
































































































































































































































































































































































































































































































































98
99
100
101
102
103








104
105
106
107
108
109
110
111
112
113

114
115
116
117
118
119
120
121
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462


1463

1464
1465
1466
1467
1468
1469
1470







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






+
+
+
+
+
+
+
+








-
-
+
-







}
namespace eval ::practcl {}
namespace eval ::practcl::OBJECT {}

###
# END: setup.tcl
###
###
# START: docbuild.tcl
###
###
# Tool for build scripts to dynamically generate manual files from comments
# in source code files
###
namespace eval ::practcl {}

proc ::practcl::cat fname {
    if {![file exists $fname]} {
       return
    }
    set fin [open $fname r]
    set data [read $fin]
    close $fin
    return $data
}

proc ::putb {buffername args} {
  upvar 1 $buffername buffer
  switch [llength $args] {
    1 {
      append buffer [lindex $args 0] \n
    }
    2 {
      append buffer [string map {*}$args] \n
    }
    default {
      error "usage: putb buffername ?map? string"
    }
  }
}

oo::class create ::clay::doctool {
  constructor {} {
    my variable coro
    set coro [info object namespace [self]]::coro
    oo::objdefine [self] forward coro $coro
    coroutine $coro {*}[namespace code {my reset}]
  }

  method arglist {arglist} {
    set result [dict create]
    foreach arg $arglist {
      set name [lindex $arg 0]
      dict set result $name positional 1
      dict set result $name mandatory  1
      if {$name in {args dictargs}} {
        switch [llength $arg] {
          1 {
            dict set result $name mandatory 0
          }
          2 {
            dict for {optname optinfo} [lindex $arg 1] {
              set optname [string trim $optname -:]
              dict set result $optname {positional 1 mandatory 0}
              dict for {f v} $optinfo {
                dict set result $optname [string trim $f -:] $v
              }
            }
          }
          default {
            error "Bad argument"
          }
        }
      } else {
        switch [llength $arg] {
          1 {
            dict set result $name mandatory 1
          }
          2 {
            dict set result $name mandatory 0
            dict set result $name default   [lindex $arg 1]
          }
          default {
            error "Bad argument"
          }
        }
      }
    }
    return $result
  }

  method comment block {
    set count 0
    set field description
    set result [dict create description {}]
    foreach line [split $block \n] {
      set line [string trim $line]
      set fwidx [string first " " $line]
      set firstword [string range $line 0 [expr {$fwidx-1}]]
      if {[string index $firstword end] eq ":"} {
        set field [string trim $firstword -]
        switch $field {
          desc {
            set field description
          }
        }
        set line [string range $line [expr {$fwidx+1}] end]
      }
      dict append result $field "$line\n"
    }
    return $result
  }

  ###
  # Process an oo::objdefine call that modifies the class object
  # itself
  ####
  method keyword.Class {resultvar commentblock name body} {
    upvar 1 $resultvar result
    set name [string trim $name :]
    if {[dict exists $result class $name]} {
      set info [dict get $result class $name]
    } else {
      set info [my comment $commentblock]
    }
    set commentblock {}
    foreach line [split $body \n] {
      append thisline $line \n
      if {![info complete $thisline]} continue
      set thisline [string trim $thisline]
      if {[string index $thisline 0] eq "#"} {
        append commentblock [string trimleft $thisline #] \n
        set thisline {}
        continue
      }
      set cmd [string trim [lindex $thisline 0] ":"]
      switch $cmd {
        method -
        Ensemble {
          my keyword.class_method info $commentblock  {*}[lrange $thisline 1 end-1]
          set commentblock {}
        }
      }
      set thisline {}
    }
    dict set result class $name $info
  }

  method keyword.class {resultvar commentblock name body} {
    upvar 1 $resultvar result
    set name [string trim $name :]
    if {[dict exists $result class $name]} {
      set info [dict get $result class $name]
    } else {
      set info [my comment $commentblock]
    }
    set commentblock {}
    foreach line [split $body \n] {
      append thisline $line \n
      if {![info complete $thisline]} continue
      set thisline [string trim $thisline]
      if {[string index $thisline 0] eq "#"} {
        append commentblock [string trimleft $thisline #] \n
        set thisline {}
        continue
      }
      set cmd [string trim [lindex $thisline 0] ":"]
      switch $cmd {
        superclass {
          dict set info ancestors [lrange $thisline 1 end]
          set commentblock {}
        }
        class_method {
          my keyword.class_method info $commentblock  {*}[lrange $thisline 1 end-1]
          set commentblock {}
        }
        destructor -
        constructor {
          my keyword.method info $commentblock {*}[lrange $thisline 0 end-1]
          set commentblock {}
        }
        method -
        Ensemble {
          my keyword.method info $commentblock  {*}[lrange $thisline 1 end-1]
          set commentblock {}
        }
      }
      set thisline {}
    }
    dict set result class $name $info
  }

  method keyword.class_method {resultvar commentblock name args} {
    upvar 1 $resultvar result
    set info [my comment $commentblock]
    switch [llength $args] {
      1 {
        set arglist [lindex $args 0]
      }
      0 {
        set arglist dictargs
        #set body [lindex $args 0]
      }
      default {error "could not interpret method $name {*}$args"}
    }
    if {![dict exists $info arglist]} {
      dict set info arglist [my arglist $arglist]
    }
    dict set result class_method [string trim $name :] $info
  }

  method keyword.method {resultvar commentblock name args} {
    upvar 1 $resultvar result
    set info [my comment $commentblock]
    switch [llength $args] {
      1 {
        set arglist [lindex $args 0]
      }
      0 {
        set arglist dictargs
        #set body [lindex $args 0]
      }
      default {error "could not interpret method $name {*}$args"}
    }
    if {![dict exists $info arglist]} {
      dict set info arglist [my arglist $arglist]
    }
    dict set result method [string trim $name :] $info
  }

  method keyword.proc {commentblock name arglist body} {
    set info [my comment $commentblock]
    if {![dict exists $info arglist]} {
      dict set info arglist [my arglist $arglist]
    }
    return $info
  }

  method reset {} {
    my variable info
    set info [dict create]
    yield [info coroutine]
    set thisline {}
    set commentblock {}
    set linec 0
    while 1 {
      set line [yield]
      append thisline $line \n
      if {![info complete $thisline]} continue
      set thisline [string trim $thisline]
      if {[string index $thisline 0] eq "#"} {
        append commentblock [string trimleft $thisline #] \n
        set thisline {}
        continue
      }
      set cmd [string trim [lindex $thisline 0] ":"]
      switch $cmd {
        Proc -
        proc {
          set procinfo [my keyword.proc $commentblock {*}[lrange $thisline 1 end]]
          dict set info proc [string trim [lindex $thisline 1] :] $procinfo
          set commentblock {}
        }
        oo::objdefine {
          if {[llength $thisline]==3} {
            lassign $thisline tcmd name body
            my keyword.Class info $commentblock $name $body
          } else {
            puts "Warning: bare oo::define in library"
          }
        }
        oo::define {
          if {[llength $thisline]==3} {
            lassign $thisline tcmd name body
            my keyword.class info $commentblock $name $body
          } else {
            puts "Warning: bare oo::define in library"
          }
        }
        tao::define -
        clay::define -
        tool::define {
          lassign $thisline tcmd name body
          my keyword.class info $commentblock $name $body
          set commentblock {}
        }
        oo::class {
          lassign $thisline tcmd mthd name body
          my keyword.class info $commentblock $name $body
          set commentblock {}
        }
        default {
          if {[lindex [split $cmd ::] end] eq "define"} {
            lassign $thisline tcmd name body
            my keyword.class info $commentblock $name $body
            set commentblock {}
          }
          set commentblock {}
        }
      }
      set thisline {}
    }
  }

  method section.command {procinfo} {
    set result {}
    putb result "\[section \{Commands\}\]"
    putb result {[list_begin definitions]}
    dict for {method minfo} $procinfo {
      putb result {}
      set line "\[call proc \[cmd $method\]"
      if {[dict exists $minfo arglist]} {
        dict for {argname arginfo} [dict get $minfo arglist] {
          set positional 1
          set mandatory  1
          dict with arginfo {}
          if {$mandatory==0} {
            append line " \[opt \""
          } else {
            append line " "
          }
          if {$positional} {
            append line "\[arg $argname"
          } else {
            append line "\[option $argname"
            if {[dict exists $arginfo type]} {
              append line " \[cmd [dict get $arginfo type]\]"
            } else {
              append line " \[cmd $argname\]"
            }
          }
          append line "\]"
          if {$mandatory==0} {
            if {[dict exists $arginfo default]} {
              append line " \[const \"[dict get $arginfo default]\"\]"
            }
            append line "\"\]"
          }
        }
      }
      append line \]
      putb result $line
      if {[dict exists $minfo description]} {
        putb result [dict get $minfo description]
      }
    }
    putb result {[list_end]}
    return $result
  }

  method section.class {class_name class_info} {
    set result {}
    putb result "\[subsection \{Class  $class_name\}\]"
    if {[dict exists $class_info ancestors]} {
      set line "\[emph \"ancestors\"\]:"
      foreach {c} [dict get $class_info ancestors] {
        append line " \[class [string trim $c :]\]"
      }
      putb result $line
      putb result {[para]}
    }
    dict for {f v} $class_info {
      if {$f in {class_method method description ancestors}} continue
      putb result "\[emph \"$f\"\]: $v"
      putb result {[para]}
    }
    if {[dict exists $class_info description]} {
      putb result [dict get $class_info description]
      putb result {[para]}
    }
    if {[dict exists $class_info class_method]} {
      putb result "\[class \{Class Methods\}\]"
      #putb result "Methods on the class object itself."
      putb result {[list_begin definitions]}
      dict for {method minfo} [dict get $class_info class_method] {
        putb result {}
        set line "\[call method \[cmd $method\]"
        if {[dict exists $minfo arglist]} {
          dict for {argname arginfo} [dict get $minfo arglist] {
            set positional 1
            set mandatory  1
            dict with arginfo {}
            if {$mandatory==0} {
              append line " \[opt \""
            } else {
              append line " "
            }
            if {$positional} {
              append line "\[arg $argname"
            } else {
              append line "\[option $argname"
              if {[dict exists $arginfo type]} {
                append line " \[method [dict get $arginfo type]\]"
              } else {
                append line " \[method $argname\]"
              }
            }
            append line "\]"
            if {$mandatory==0} {
              if {[dict exists $arginfo default]} {
                append line " \[const \"[dict get $arginfo default]\"\]"
              }
              append line "\"\]"
            }
          }
        }
        append line \]
        putb result $line
        if {[dict exists $minfo description]} {
          putb result [dict get $minfo description]
        }
      }
      putb result {[list_end]}
      putb result {[para]}
    }
    if {[dict exists $class_info method]} {
      putb result "\[class {Methods}\]"
      putb result {[list_begin definitions]}
      dict for {method minfo} [dict get $class_info method] {
        putb result {}
        set line "\[call method \[cmd $method\]"
        if {[dict exists $minfo arglist]} {
          dict for {argname arginfo} [dict get $minfo arglist] {
            set positional 1
            set mandatory  1
            dict with arginfo {}
            if {$mandatory==0} {
              append line " \[opt \""
            } else {
              append line " "
            }
            if {$positional} {
              append line "\[arg $argname"
            } else {
              append line "\[opt $argname"
              if {[dict exists $arginfo type]} {
                append line " \[method [dict get $arginfo type]\]"
              } else {
                append line " \[method $argname\]"
              }
            }
            append line "\]"
            if {$mandatory==0} {
              if {[dict exists $arginfo default]} {
                append line " \[const \"[dict get $arginfo default]\"\]"
              }
              append line "\"\]"
            }
          }
        }
        append line \]
        putb result $line
        if {[dict exists $minfo description]} {
          putb result [dict get $minfo description]
        }
      }
      putb result {[list_end]}
      putb result {[para]}
    }
    return $result
  }

  method manpage args {
    my variable info map
    set result {}
    set header {}
    set footer {}
    dict with args {}
    putb result $header
    dict for {sec_type sec_info} $info {
      switch $sec_type {
        proc {
          putb result [my section.command $sec_info]
        }
        class {
          putb result "\[section Classes\]"
          dict for {class_name class_info} $sec_info {
            putb result [my section.class $class_name $class_info]
          }
        }
        default {
          putb result "\[section [list $sec_type $sec_name]\]"
          if {[dict exists $sec_info description]} {
            putb result [dict get $sec_info description]
          }
        }
      }
    }
    putb result $footer
    putb result {[manpage_end]}
    return $result
  }

  method scan_text {text} {
    my variable linecount coro
    set linecount 0
    foreach line [split $text \n] {
      incr linecount
      $coro $line
    }
  }

  method scan_file {filename} {
    my variable linecount coro
    set fin [open $filename r]
    set linecount 0
    while {[gets $fin line]>=0} {
      incr linecount
      $coro $line
    }
    close $fin
  }
}



###
# END: docbuild.tcl
###
###
# START: buildutil.tcl
###
###
# Build utility functions
###

###
# Generate a proc if no command already exists by that name
###
proc Proc {name arglist body} {
  if {[info command $name] ne {}} return
  proc $name $arglist $body
}

###
# A command to do nothing. A handy way of
# negating an instruction without
# having to comment it completely out.
# It's also a handy attachment point for
# an object to be named later
###
if {[info command ::noop] eq {}} {
  proc ::noop args {}
Proc ::noop args {}
}

proc ::practcl::debug args {
  #puts $args
  ::practcl::cputs ::DEBUG_INFO $args
}

###
197
198
199
200
201
202
203















204
205
206
207
208
209
210
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  return $result
}

proc ::practcl::os {} {
  return [${::practcl::MAIN} define get TEACUP_OS]
}

###
# Build a zipfile. On tcl8.6 this invokes the native Zip implementation
# on older interpreters this invokes zip via exec
###
proc ::practcl::mkzip {exename barekit vfspath} {
  ::practcl::tcllib_require zipfile::mkzip
  ::zipfile::mkzip::mkzip $exename -runtime $barekit -directory $vfspath
}
###
# Dictionary sort a key/value list. Needed because pre tcl8.6
# does not have [emph {lsort -stride 2}]
###
proc ::practcl::sort_dict list {
  return [::lsort -stride 2 -dictionary $list]
}
if {[::package vcompare $::tcl_version 8.6] < 0} {
  # Approximate ::zipfile::mkzip with exec calls
  proc ::practcl::mkzip {exename barekit vfspath} {
    set path [file dirname [file normalize $exename]]
    set zipfile [file join $path [file rootname $exename].zip]
    file copy -force $barekit $exename
    set pwd [pwd]
223
224
225
226
227
228
229
230
231
232
233
234

235
236
237
238


239
240
241
242
243
244
245
1587
1588
1589
1590
1591
1592
1593





1594




1595
1596
1597
1598
1599
1600
1601
1602
1603







-
-
-
-
-
+
-
-
-
-
+
+







  proc ::practcl::sort_dict list {
    set result {}
    foreach key [lsort -dictionary [dict keys $list]] {
      dict set result $key [dict get $list $key]
    }
    return $result
  }
} else {
  proc ::practcl::mkzip {exename barekit vfspath} {
    ::practcl::tcllib_require zipfile::mkzip
    ::zipfile::mkzip::mkzip $exename -runtime $barekit -directory $vfspath
  }
}
  proc ::practcl::sort_dict list {
    return [::lsort -stride 2 -dictionary $list]
  }
}



proc ::practcl::local_os {} {
  # If we have already run this command, return
  # a cached copy of the data
  if {[info exists ::practcl::LOCAL_INFO]} {
    return $::practcl::LOCAL_INFO
  }
802
803
804
805
806
807
808
809
810
811
812

813
814
815
816
817
818




819
820
821
822
823
824
825
2160
2161
2162
2163
2164
2165
2166




2167






2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178







-
-
-
-
+
-
-
-
-
-
-
+
+
+
+







###
###
# START: fileutil.tcl
###
###
# Bits stolen from fileutil
###
proc ::practcl::cat fname {
    if {![file exists $fname]} {
       return
    }

    set fin [open $fname r]
    set data [read $fin]
    close $fin
    return $data
}


###
# grep
###
proc ::practcl::grep {pattern {files {}}} {
    set result [list]
    if {[llength $files] == 0} {
	      # read from stdin
    	  set lnum 0
	      while {[gets stdin line] >= 0} {
	          incr lnum
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400

1401
1402
1403
1404
1405
1406
1407
2664
2665
2666
2667
2668
2669
2670







































































2671











2672
2673
2674
2675
2676
2677
2678
2679







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
-
-
-
-
-
-
-
-
-
-
+







      }
      default {
        array $submethod define {*}$args
      }
    }
  }


  method meta {submethod args} {
    my variable meta
    if {![info exists meta]} {
      set meta {}
    }
    switch $submethod {
      dump {
        return $meta
      }
      add {
        set field [lindex $args 0]
        if {![dict exists $meta $field]} {
          dict set meta $field {}
        }
        foreach arg [lrange $args 1 end] {
          if {$arg ni [dict get $meta $field]} {
            dict lappend meta $field $arg
          }
        }
        return [dict get $meta $field]
      }
      remove {
        set field [lindex $args 0]
        if {![dict exists meta $field]} {
          return
        }
        set rlist [lrange $args 1 end]
        set olist [dict get $meta $field]
        set nlist {}
        foreach arg $olist {
          if {$arg in $rlist} continue
          lappend nlist $arg
        }
        dict set meta $field $nlist
        return $nlist
      }
      exists {
        return [dict exists $meta {*}$args]
      }
      getnull -
      get {
        if {[dict exists $meta {*}$args]} {
          return [dict get $meta {*}$args]
        }
        return {}
      }
      cget {
        set field [lindex $args 0]
        if {[dict exists $meta $field]} {
          return [dict get $meta $field]
        }
        return [lindex $args 1]
      }
      set {
        if {[llength $args]==1} {
          foreach {field value} $args {
            dict set meta [string trimright $field :]: $value
          }
        } else {
          set field [lindex $args end-1]
          set value [lindex $args end]
          dict set meta {*}[lrange $args 0 end-2] [string trimright $field :]: $value
        }
      }
      default {
        error "Valid: add cget dump exists get getnull remove set"
      }
    }
  }
  
  method graft args {
    my variable organs
    if {[llength $args] == 1} {
      error "Need two arguments"
    }
    set object {}
    foreach {stub object} $args {
      dict set organs $stub $object
      oo::objdefine [self] forward <${stub}> $object
      oo::objdefine [self] export <${stub}>
    }
    return $object
    return [my clay delegate {*}$args]
  }

  method initialize {} {}


  method link {command args} {
    my variable links
1522
1523
1524
1525
1526
1527
1528
1529

1530
1531
1532

1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
2794
2795
2796
2797
2798
2799
2800

2801



2802







2803
2804
2805
2806
2807
2808
2809







-
+
-
-
-
+
-
-
-
-
-
-
-







    foreach {s c} $mixinslot {
      if {$c eq {}} continue
      lappend mixins $c
    }
    oo::objdefine [self] mixin {*}$mixins
  }

  method organ {{stub all}} {
  method organ args {
    my variable organs
    if {![info exists organs]} {
      return {}
    return [my clay delegate {*}$args]
    }
    if { $stub eq "all" } {
      return $organs
    }
    if {[dict exists $organs $stub]} {
      return [dict get $organs $stub]
    }
  }

  method script script {
    eval $script
  }

  method select {} {
1574
1575
1576
1577
1578
1579
1580
1581

1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594

1595
1596
1597
1598

1599
1600
1601
1602
1603
1604
1605
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







-
+












-
+



-
+







oo::class create ::practcl::toolset {
  ###
  # find or fake a key/value list describing this project
  ###
  method config.sh {} {
    return [my read_configuration]
  }
  

  method BuildDir {PWD} {
    set name [my define get name]
    set debug [my define get debug 0]
    if {[my <project> define get LOCAL 0]} {
      return [my define get builddir [file join $PWD local $name]]
    }
    if {$debug} {
      return [my define get builddir [file join $PWD debug $name]]
    } else {
      return [my define get builddir [file join $PWD pkg $name]]
    }
  }
  

  method MakeDir {srcdir} {
    return $srcdir
  }
  

  method read_configuration {} {
    my variable conf_result
    if {[info exists conf_result]} {
      return $conf_result
    }
    set result {}
    set name [my define get name]
1700
1701
1702
1703
1704
1705
1706
1707

1708
1709
1710
1711
1712
1713
1714
2963
2964
2965
2966
2967
2968
2969

2970
2971
2972
2973
2974
2975
2976
2977







-
+







    }
    set srcdir [my SourceRoot]
    set PWD [pwd]
    cd $srcdir
    ::practcl::dotclexec $critcl {*}$args
    cd $PWD
  }
  

  method make-autodetect {} {}
}


oo::objdefine ::practcl::toolset {


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
3750
3751
3752
3753
3754
3755
3756

3757
3758
3759
3760

3761
3762
3763
3764
3765
3766
3767
3768

3769
3770
3771
3772
3773
3774
3775
3776







-
+



-
+







-
+








  # MSVC always builds in the source directory
  method BuildDir {PWD} {
    set srcdir [my define get srcdir]
    return $srcdir
  }

  

  # Do nothing
  method make-autodetect {} {
  }
  

  method make-clean {} {
    set PWD [pwd]
    set srcdir [my define get srcdir]
    cd $srcdir
    catch {::practcl::doexec nmake -f makefile.vc clean}
    cd $PWD
  }
  

  method make-compile {} {
    set srcdir [my define get srcdir]
    if {[my define get static 1]} {
      puts "BUILDING Static $name $srcdir"
    } else {
      puts "BUILDING Dynamic $name $srcdir"
    }
2525
2526
2527
2528
2529
2530
2531
2532

2533
2534
2535
2536
2537
2538
2539
3788
3789
3790
3791
3792
3793
3794

3795
3796
3797
3798
3799
3800
3801
3802







-
+







        cd [file join $srcdir win]
        ::practcl::doexec nmake -f makefile.vc INSTALLDIR=[my <project> define get installdir]  {*}[my NmakeOpts] release
      } else {
        error "No make.tcl or makefile.vc found for project $name"
      }
    }
  }
  

  method make-install DEST {
    set PWD [pwd]
    set srcdir [my define get srcdir]
    cd $srcdir
    if {$DEST eq {}} {
      error "No destination given"
    }
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
3817
3818
3819
3820
3821
3822
3823

3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838

3839
3840
3841
3842
3843
3844
3845
3846







-
+














-
+







      } else {
        puts "[self] VFS INSTALL $DEST"
        ::practcl::doexec nmake -f makefile.vc INSTALLDIR=$DEST {*}[my NmakeOpts] install
      }
    }
    cd $PWD
  }
  

  # Detect what directory contains the Makefile template
  method MakeDir {srcdir} {
    set localsrcdir $srcdir
    if {[file exists [file join $srcdir generic]]} {
      my define add include_dir [file join $srcdir generic]
    }
    if {[file exists [file join $srcdir win]]} {
       my define add include_dir [file join $srcdir win]
    }
    if {[file exists [file join $srcdir makefile.vc]]} {
      set localsrcdir [file join $srcdir win]
    }
    return $localsrcdir
  }
  

  method NmakeOpts {} {
    set opts {}
    set builddir [file normalize [my define get builddir]]

    if {[my <project> define exists tclsrcdir]} {
      ###
      # On Windows we are probably running under MSYS, which doesn't deal with
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
3919
3920
3921
3922
3923
3924
3925

3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946

3947
3948
3949
3950
3951
3952
3953
3954







-
+




















-
+







        if {$filename ne {} && ![file exists $filename]} {
          set needs_make 1
        }
      }
    }
    return $needs_make
  }
  

  method output {} {
    set result {}
    set filename [my define get filename]
    if {$filename ne {}} {
      lappend result $filename
    }
    foreach filename [my define get files] {
      if {$filename ne {}} {
        lappend result $filename
      }
    }
    return $result
  }

  method reset {} {
    my variable triggered domake needs_make
    set triggerd 0
    set domake 0
    set needs_make 0
  }
  

  method triggers {} {
    my variable triggered domake define
    if {$triggered} {
      return $domake
    }
    set triggered 1
    set make_objects [my <module> make objects]
2710
2711
2712
2713
2714
2715
2716



2717
2718
2719
2720
2721
2722
2723

2724
2725
2726
2727
2728
2729
2730
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988

3989
3990
3991
3992
3993
3994
3995
3996







+
+
+






-
+








###
# END: class target.tcl
###
###
# START: class object.tcl
###
###
# A generic Practcl object
###
::oo::class create ::practcl::object {
  superclass ::practcl::metaclass

  constructor {parent args} {
    my variable links define
    set organs [$parent child organs]
    my graft {*}$organs
    my clay delegate {*}$organs
    array set define $organs
    array set define [$parent child define]
    array set links {}
    if {[llength $args]==1 && [file exists [lindex $args 0]]} {
      my define set filename [lindex $args 0]
      ::practcl::product select [self]
    } elseif {[llength $args] == 1} {
2779
2780
2781
2782
2783
2784
2785
2786

2787
2788
2789
2790
2791
2792
2793
4045
4046
4047
4048
4049
4050
4051

4052
4053
4054
4055
4056
4057
4058
4059







-
+







    foreach {f v} $argdat {
      dict set cstruct $name $f $v
    }
    if {![dict exists $cstruct $name public]} {
      dict set cstruct $name public 1
    }
  }
  

  method include header {
    my define add include $header
  }

  method include_dir args {
    my define add include_dir {*}$args
  }
3362
3363
3364
3365
3366
3367
3368
3369



3370
3371
3372
3373
3374
3375
3376
3377
3378
4628
4629
4630
4631
4632
4633
4634

4635
4636
4637
4638

4639
4640
4641
4642
4643
4644
4645







-
+
+
+

-








###
# END: class dynamic.tcl
###
###
# START: class product.tcl
###

###
# A deliverable for the build system
###
::oo::class create ::practcl::product {


  method code {section body} {
    my variable code
    ::practcl::cputs code($section) $body
  }

  method Collate_Source CWD {}
4053
4054
4055
4056
4057
4058
4059
4060

4061
4062
4063
4064
4065
4066
4067
4068
4069
4070


4071
4072

4073
4074
4075
4076
4077
4078
4079
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







-
+








-
-
+
+

-
+







###
::oo::class create ::practcl::module {
  superclass ::practcl::object ::practcl::product.dynamic

  method _MorphPatterns {} {
    return {{@name@} {::practcl::module.@name@} ::practcl::module}
  }
  

  method add args {
    my variable links
    set object [::practcl::object new [self] {*}$args]
    foreach linktype [$object linktype] {
      lappend links($linktype) $object
    }
    return $object
  }
  
  


  method install-headers args {}
  

  ###
  # Target handling
  ###
  method make {command args} {
    my variable make_object
    if {![info exists make_object]} {
      set make_object {}
4166
4167
4168
4169
4170
4171
4172
4173

4174
4175
4176
4177
4178
4179
4180
5433
5434
5435
5436
5437
5438
5439

5440
5441
5442
5443
5444
5445
5446
5447







-
+







      }
      task -
      target -
      add {
        set name [lindex $args 0]
        set info [uplevel #0 [list subst [lindex $args 1]]]
        set body [lindex $args 2]
        

        set nspace [namespace current]
        if {[dict exist $make_object $name]} {
          set obj [dict get $$make_object $name]
        } else {
          set obj [::practcl::make_obj new [self] $name $info $body]
          dict set make_object $name $obj
          dict set target_make $name 0
4190
4191
4192
4193
4194
4195
4196
4197

4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209

4210
4211

4212
4213
4214
4215
4216
4217
4218
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







-
+











-
+


+







        return $obj
      }
      todo {
         foreach {name obj} $make_object {
          if {[$obj do]} {
            lappend result $name
          }
        }       
        }
      }
      do {
        global CWD SRCDIR project SANDBOX
        foreach {name obj} $make_object {
          if {[$obj do]} {
            eval [$obj define get action]
          }
        }
      }
    }
  }
  

  method child which {
    switch $which {
      delegate -
      organs {
        return [list project [my define get project] module [self]]
      }
    }
  }

 ###
4382
4383
4384
4385
4386
4387
4388
4389

4390
4391
4392
4393
4394
4395
4396
5650
5651
5652
5653
5654
5655
5656

5657
5658
5659
5660
5661
5662
5663
5664







-
+







          lappend errs [dict get $errdat -errorinfo]
        } else {
          lappend errs $errdat
        }
      }
    }
    if {[llength $errs]} {
      set logfile [file join $::CWD practcl.log]      
      set logfile [file join $::CWD practcl.log]
      ::practcl::log $logfile "*** ERRORS ***"
      foreach {item trace} $errs {
        ::practcl::log $logfile "###\n# ERROR\n###\n$item"
       ::practcl::log $logfile "###\n# TRACE\n###\n$trace"
      }
      ::practcl::log $logfile "*** DEBUG INFO ***"
      ::practcl::log $logfile $::DEBUG_INFO
4547
4548
4549
4550
4551
4552
4553

4554
4555
4556
4557
4558
4559
4560
5815
5816
5817
5818
5819
5820
5821
5822
5823
5824
5825
5826
5827
5828
5829







+







    }
    $tkobj define set config_opts $tk_config_opts
    $tkobj compile
  }

  method child which {
    switch $which {
      delegate -
      organs {
	# A library can be a project, it can be a module. Any
	# subordinate modules will indicate their existance
        return [list project [self] module [self]]
      }
    }
  }
5347
5348
5349
5350
5351
5352
5353
5354

5355
5356
5357
5358
5359
5360
5361
6616
6617
6618
6619
6620
6621
6622

6623
6624
6625
6626
6627
6628
6629
6630







-
+







      scm  None
      hash {}
      maxdate {}
      tags {}
      isodate {}
    }
  }
  

  method DistroMixIn {} {
    my define set scm none
  }

  method Sandbox {} {
    if {[my define exists sandbox]} {
      return [my define get sandbox]
5548
5549
5550
5551
5552
5553
5554
5555

5556
5557
5558
5559
5560
5561
5562
6817
6818
6819
6820
6821
6822
6823

6824
6825
6826
6827
6828
6829
6830
6831







-
+







    set info [next]
    dict set info scm fossil
    foreach {field value} [::practcl::fossil_status [my define get srcdir]] {
      dict set info $field $value
    }
    return $info
  }
  

  # Clone the source
  method ScmClone  {} {
    set srcdir [my SrcDir]
    if {[file exists [file join $srcdir .fslckout]]} {
      return
    }
    if {[file exists [file join $srcdir _FOSSIL_]]} {
5765
5766
5767
5768
5769
5770
5771

5772
5773
5774
5775
5776
5777
5778
7034
7035
7036
7037
7038
7039
7040
7041
7042
7043
7044
7045
7046
7047
7048







+








  method BuildDir {PWD} {
    return [my define get srcdir]
  }

  method child which {
    switch $which {
      delegate -
      organs {
	# A library can be a project, it can be a module. Any
	# subordinate modules will indicate their existance
        return [list project [self] module [self]]
      }
    }
  }

Changes to modules/pt/pkgIndex.tcl.

17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31







-
+







package ifneeded pt::pe        1.0.2 [list source [file join $dir pt_pexpression.tcl]]
package ifneeded pt::pe::op    1.0.1 [list source [file join $dir pt_pexpr_op.tcl]]

# Parsing Expression Grammar support.
package ifneeded pt::peg                1 [list source [file join $dir pt_pegrammar.tcl]]
package ifneeded pt::peg::container     1 [list source [file join $dir pt_peg_container.tcl]]
package ifneeded pt::peg::interp    1.0.1 [list source [file join $dir pt_peg_interp.tcl]]
package ifneeded pt::peg::op        1.0.2 [list source [file join $dir pt_peg_op.tcl]]
package ifneeded pt::peg::op        1.1.0 [list source [file join $dir pt_peg_op.tcl]]
package ifneeded pt::parse::peg     1.0.1 [list source [file join $dir pt_parse_peg.tcl]]


# Export/import managers. Assumes an untrusted environment.
package ifneeded pt::peg::export            1 [list source [file join $dir pt_peg_export.tcl]]
package ifneeded pt::peg::import            1 [list source [file join $dir pt_peg_import.tcl]]

Changes to modules/pt/pt_peg_op.man.

1
2

3
4
5
6
7
8
9
1

2
3
4
5
6
7
8
9

-
+







[comment {-*- text -*- doctools manpage}]
[vset VERSION 1.0.2]
[vset VERSION 1.1.0]
[manpage_begin pt_peg_op i [vset VERSION]]
[include include/module.inc]
[titledesc {Parser Tools PE Grammar Utility Operations}]
[require pt::peg::op [opt [vset VERSION]]]
[description]
[include include/ref_intro.inc]

Changes to modules/pt/pt_peg_op.tcl.

1
2

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

21
22
23
24
25
26
27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28


+

















-
+







# -*- tcl -*-
# Copyright (c) 2009-2018 Andreas Kupries <[email protected]>
# Copyright (c) 2018 Stefan Sobernig <[email protected]>

# Utility commands operating on parsing expressions.

# # ## ### ##### ######## ############# #####################
## Requirements

package require Tcl 8.5        ; # Required runtime.
package require pt::pe         ; # PE basics
package require pt::pe::op     ; # PE transforms
package require struct::set    ; # Set operations (symbol sets)

# # ## ### ##### ######## ############# #####################
##

namespace eval ::pt::peg::op {
    namespace export \
	flatten called reachable realizable \
	dechain drop modeopt minimize
	drop modeopt minimize dechain

    namespace ensemble create

    namespace eval ::pt::peg::op::drop {
	namespace export \
	    unreachable unrealizable

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86




87
88

89
90
91
92
93
94
95
96









97
98
99
100
101
102
103
104
105
















106
107
108
109






110
111
112
113





114
115
116
117
118
119
120
59
60
61
62
63
64
65




















66
67
68
69
70
71
72

73








74
75
76
77
78
79
80
81
82









83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98




99
100
101
102
103
104




105
106
107
108
109
110
111
112
113
114
115
116







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-


+
+
+
+

-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+







    }

    return $dict
}

proc ::pt::peg::op::dechain {container} {

    # Simplify all symbols which just chain to a different symbol by
    # inlining the called symbol in its callers. This works if and
    # only the modes match properly.

    # X     Z      dechain notes
    # value value| yes    | value is passed
    # value leaf | yes    | value is passed
    # value void | yes    | X is implied void
    # leaf  value| no     | generated value was discarded, inlined doesn't. Z may be implied void
    # leaf  leaf | no     | s.a.
    # leaf  void | no     | s.a.
    # void  value| no     | X drops value, inline doesn't
    # void  leaf | no     | s.a.
    # void  void | yes    |

    array set caller [Invert [called $container]]
    # caller = array (x -> list(caller-of-x))
    array set mode [$container modes]
    # mode = array (x -> mode-of-x)

    set changed 1
    while {$changed} {
	
	set chainPairs [dict create]
	set rules [$container rules]
	array set modes [$container modes]
	set changed 0
	foreach {symbol rule} [$container rules] {
	foreach {caller rule} $rules {
	    # Ignore regular operators and terminals
	    if {[lindex $rule 0] ne "n"} continue
	    set called [lindex $rule 1]

	    # Ignore chains where mode changes form a barrier.
	    if {
		($mode($symbol) ne "value") &&
		(($mode($symbol) ne "void") ||
	    lassign $rule op called
	    if {$op ne "n"} continue
	    dict set chainPairs $called $caller
	}

	set ends [struct::set difference \
			[dict keys $chainPairs] \
			[dict values $chainPairs]]

		 ($mode($called) ne "void"))
	    } continue

	    # We have the chain symbol -> called.
	    # Replace all users of 'symbol' with 'called'

	    foreach user $caller($symbol) {
		$container rule $user \
		    [pt::pe::op rename $symbol $called \
	if {[struct::set empty $ends]} {
	    # stop, given a cycle
	    break
	}

	set chainPairs [dict remove $chainPairs {*}$ends]
	set changed [dict size $chainPairs]
	
	if {$changed} {
	    
	    dict for {called caller} $chainPairs {

		if {$called in [$container nonterminals]
		    && !(($modes($caller) ne "value") &&
		    (($modes($caller) ne "void") ||
		     ![info exists modes($called)] ||
			 [$container rule $user]]
	    }

	    set changed 1
		     ($modes($called) ne "void")))} {
		    
       		    $container rule $caller [$container rule $called]
		    
		} else {
		    incr changed -1
	    array set caller [Invert [called $container]]
	}
    }

		}
	    }
	}
    }
    
    return
}

# # ## ### ##### ######## #############

proc ::pt::peg::op::modeopt {container} {

152
153
154
155
156
157
158
159

160
161
162
163
164
165
166
148
149
150
151
152
153
154

155
156
157
158
159
160
161
162







-
+







	    }

	    # Rule 2
	    set callmode [CallMode $caller($sym) mode]
	    if {($callmode eq "void") &&
		($mode($sym) ne "void")} {

puts (2)$sym
		#puts (2)$sym
		set mode($sym) void

		# This change may change calling context and this call
		# mode of the symbols we call, so put them back up for
		# consideration.
		struct::set add changed $calls($sym)
	    }
186
187
188
189
190
191
192


193
194
195
196
197


198
199
200
201
202
203
204
182
183
184
185
186
187
188
189
190
191
192



193
194
195
196
197
198
199
200
201







+
+


-
-
-
+
+







    }
}

# # ## ### ##### ######## #############

proc ::pt::peg::op::minimize {container} {
    flatten           $container
    modeopt           $container; # for dechaining
    dechain           $container
    drop unrealizable $container
    drop unreachable  $container
    flatten           $container
    modeopt           $container
    dechain           $container
    modeopt           $container;
    flatten           $container
    return
}

# # ## ### ##### ######## #############

proc ::pt::peg::op::reachable {container} {

370
371
372
373
374
375
376
377

378
367
368
369
370
371
372
373

374
375







-
+

## State / Configuration :: n/a

namespace eval ::pt::peg::op {}

# # ## ### ##### ######## ############# #####################
## Ready

package provide pt::peg::op 1.0.2
package provide pt::peg::op 1.1.0
return

Changes to modules/pt/tests/pt_peg_op.tests.

1
2

3
4
5
6
7
8
9
10
11
12





13
14
15
16
17
18
19
20
21
1
2
3
4
5
6
7
8





9
10
11
12
13


14
15
16
17
18
19
20


+





-
-
-
-
-
+
+
+
+
+
-
-







# -*- tcl -*-
# Testsuite for pt::peg::op.
# Copyright (c) 2018 Stefan Sobernig <[email protected]>

# [ok] drop unreachable
# [ok] drop unrealizable
# [ok] flatten
# [ok] minimize

# TODO
# [..] called
# [..] dechain
# [..] modeopt
# [ok] called
# [ok] realizable
# [ok] reachable
# [ok] dechain
# [ok] modeopt
# [..] reachable
# [..] realizable

# -------------------------------------------------------------------------
# Basic syntax

foreach op {
    called
    dechain
51
52
53
54
55
56
57
58
59


60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79









































80
81
82
83
84
85
86
50
51
52
53
54
55
56


57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126







-
-
+
+




















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








proc TestTransformation {op data setImpl} {
    # Convert operation and data table into series of test cases
    set debug 0
    # Note, the `op` changes the container (here ::In) in-place.
    append bodyScript [list {*}::pt::peg::op::$op ::In] \;
    if {$debug} {
	append bodyScript "puts stderr \[::In       serialize\]" \;
	append bodyScript "puts stderr \[::Expected serialize\]" \;
	append bodyScript "puts stderr \"ASIS \[::In       serialize\]\"" \;
	append bodyScript "puts stderr \"TOBE \[::Expected serialize\]\"" \;
    }
    # After the op, when all is well, the content of ::In should be
    # the same as ::Expected.
    append bodyScript "pt::peg equal \[::In serialize\] \[::Expected serialize\]" \;
    set n 1
    foreach {inStart inRulesSet outStart outRulesSet} [sl $data] {
	set testLabel "pt-peg-op-set:${setImpl}-[join $op -]-$n"
	if {$debug} {
	    puts stderr >>>>$testLabel<<<<
	}
	test $testLabel "OP '$op' vs. expected" -setup {
	    pt::peg::container ::In       deserialize [g $inStart  $inRulesSet]
	    pt::peg::container ::Expected deserialize [g $outStart $outRulesSet]
	} -body $bodyScript -result 1 -cleanup {
	    ::In       destroy
	    ::Expected destroy
	}
	incr n
    }
}

# -------------------------------------------------------------------------
# op: called

set n 0
foreach {inStart inRulesSet expectedSym} [sl {
    # --- 
    epsilon {}
    {{} {}}
    # ---
    {n S} {
	S {is {x {t A} {* {n SYM}} {t B} {n OTHER} {n SYM}} mode value}
	A {is {n A} mode value}
    }
    {{} S A A S {SYM OTHER}}
    # --- 
    {n S} {
	S {is {x {t A} {t B}} mode value}
	A {is {t a} mode value}
	B {is {t b} mode value}
    }
    {{} S A {} B {} S {}}
    # --- 
    {n S} {
	S {is {epsilon} mode value}
    }
    {{} S S {}}
}] {
    test pt-peg-op-set:${setimpl}-called.$n {op called} -setup {
	pt::peg::container ::In       deserialize [g $inStart  $inRulesSet]
    } -body {
	set r [pt::peg::op called ::In]
	dict filter $r script {key val} {
	    ::tcl::mathop::in $key [lsort [dict keys $r]]
	}
    } -cleanup {
	::In destroy
    } -result $expectedSym
    incr n
}
unset n

# -------------------------------------------------------------------------
# op: flatten

TestTransformation flatten {
    # --- stays as-is #1
    epsilon {}
127
128
129
130
131
132
133














































































134
135
136
137
138
139
140
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	A {is {n A} mode value}
    }
    {n S} {
	S {is {/ {n A} {n A} {n A}} mode value}
	A {is {n A} mode value}
    }
} $setimpl


# -------------------------------------------------------------------------
# op: realizable

set n 0
foreach {inStart inRulesSet expectedSym} [sl {
    # --- just start expression
    epsilon {}
    {{}}
    # -- all realizable, incl. start expression
    {n S} {
	S {is {n X} mode value}
	X {is {t x} mode leaf}
    }
    {{} S X}
    # -- not even start expression
    {n S} {
	S {is {n X} mode value}
	X {is {n X} mode value}
    }
    {}
    # -- not even start expression
    {n S} {
	S {is {n X} mode value}
	X {is {n X} mode value}
    }
    {}
    # -- X is unrealizable
    {n S} {
	S {is {? {n X}} mode value}
	X {is {n X} mode value}
    }
    {{} S}
    # -- X is unrealizable
    {n S} {
	S {is {/ {n X} {t y}} mode value}
	X {is {n X} mode value}
    }
    {{} S}
    # --  X <- 'A' 'B' X / 'C' X 'A'; X is unrealizable
    {n S} {
	S {is {/ {n X} {t y}} mode value}
	X {is {/ {x {t A} {t B} {n X}} {x {t C} {n X} {t A}}} mode value}
    }
    {{} S}
    # --  X <- 'A' 'B' X / 'C' X 'A' / 'x'; X *is* realizable
    {n S} {
	S {is {/ {n X} {t y}} mode value}
	X {is {/ {x {t A} {t B} {n X}} {x {t C} {n X} {t A}} {t x}} mode value}
    }
    {{} S X}
    # -- E is unrealizable
    {n S} {
	S {is {/ {x {n B} {t b}} {x {n C} {t c}} {x {n E} {t e}}} mode value}
	B {is {/ {x {n B} {t b}} {t b}} mode value}
	C {is {/ {x {n C} {t c}} {t c}} mode value}
	E {is {x {n E} {t e}} mode value}
    }
    {{} B C S}
    # -- S remains realizable (*)
    {n S} {
	S {is {* {n X}} mode value}
	X {is {n X} mode value}
    }
    {{} S}
}] {
    test pt-peg-op-set:${setimpl}-realizable.$n {op realizable} -setup {
	pt::peg::container ::In deserialize [g $inStart  $inRulesSet]
    } -body {
	lsort [pt::peg::op realizable ::In]
    } -cleanup {
	::In destroy
    } -result $expectedSym
    incr n
}
unset n


# -------------------------------------------------------------------------
# op: drop unrealizable

TestTransformation "drop unrealizable" {
    # (1) stays as-is
    epsilon {}
173
174
175
176
177
178
179

180




































181

182
183
184
185
186
187
188
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335

336
337
338
339
340
341
342
343







+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+







	X {is {n X} mode value}
    }
    {n S} {
	S {is {/ {t y}} mode value}
    }
} $setimpl


# -------------------------------------------------------------------------
# op: reachable

set n 0
foreach {inStart inRulesSet expectedSym} [sl {
    # --- none
    epsilon {}
    {}
    # -- D is not reachable
    {n S} {
	S {is {/ {x {n B} {t b}} {x {n C} {t c}} {x {n E} {t e}}} mode value}
	B {is {/ {x {n B} {t b}} {t b}} mode value}
	C {is {/ {x {n C} {t c}} {t c}} mode value}
	D {is {/ {x {n B} {t d}} {x {n C} {t d}} {t d}} mode value}
	E {is {x {n E} {t e}} mode value}
    }
    {B C E S}
    # -- all reachable
    {n S} {
     	S {is {/ {x {n A} {n B}} {t a}} mode value}
     	A {is {x {t a} {n A}} mode value}
	B {is {t a} mode leaf}
    }
    {A B S}
}] {
    test pt-peg-op-set:${setimpl}-reachable.$n {op reachable} -setup {
	pt::peg::container ::In deserialize [g $inStart  $inRulesSet]
    } -body {
	lsort [pt::peg::op reachable ::In]
    } -cleanup {
	::In destroy
    } -result $expectedSym
    incr n
}
unset n

# -------------------------------------------------------------------------
# op: drop unrealizable
# op: drop unreachable

TestTransformation "drop unreachable" {
    # (1) stays as-is
    epsilon {}
    epsilon {}
    # S <-- a; A <-- a ==> S <-- a (A not reachable, dropped)
    {n S} {
198
199
200
201
202
203
204

















































































































































































































































































































































































































































205
206
207
208
209
210
211
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







     	A {is {n B} mode void}
     	B {is {t a} mode void}
    }
    {n S} {
     	S {is {t a} mode leaf}
    }
} $setimpl

# -------------------------------------------------------------------------
# op: dechain

TestTransformation dechain {
    # --- stays as-is
    epsilon {}
    epsilon {}
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode value}
	B {is {t b} mode value}
    }
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode value}
	B {is {t b} mode value}
    }
    # --- basic chain:  A <- B <- C (leaf) <- c |> A <- C (leaf) <- c
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode value}
	B {is {n C} mode value}
	C {is {t c} mode value}
    }
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n C} mode value}
	B {is {n C} mode value}
	C {is {t c} mode value}
    }
    # --- longer chain
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	E {is {t e} mode leaf}
	A {is {n B} mode value}
	D {is {n E} mode value}
	B {is {n C} mode value}
	C {is {n D} mode value}
    }
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n E} mode value}
	B {is {n D} mode value}
	C {is {n E} mode value}
	D {is {n E} mode value}
	E {is {t e} mode leaf}
    }
    # --- basic cycle:  A <- B <- A
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode value}
	B {is {n A} mode value}
    }
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode value}
	B {is {n A} mode value}
    }
    # --- basic (indirect) cycle:  A <- B <- C <- A
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode value}
	B {is {n C} mode value}
	C {is {n A} mode value}
    }
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode value}
	B {is {n C} mode value}
	C {is {n A} mode value}
    }
    # --- basic chain plus renaming for self-recursive leaves
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode value}
	B {is {n C} mode value}
	C {is {x {n C} {t c}} mode value}
    }
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n C} mode value}
	B {is {n C} mode value}
	C {is {x {n C} {t c}} mode value}
    }
    # {n S} {
    # 	S {is {x {n A} {t s}} mode value}
    # 	A {is {x {n A} {t c}} mode value}
    # 	B {is {x {n B} {t c}} mode value}
    # 	C {is {x {n C} {t c}} mode value}
    # }
    # --- start expression: {} <- Z <- S <- s |> {} <- Z <- s
    {n Z} {
	Z {is {n S} mode value}
    	S {is {t s} mode value}
    }
    {n Z} {
	Z {is {n S} mode value}
    	S {is {t s} mode value}
    }
    # --- TODO: start expression: {} <- Z <- S (leaf) <- s |> {} <- S <- s
    # {n Z} {
    # 	Z {is {n S} mode value}
    # 	S {is {t s} mode value}
    # }
    # {n S} {
    # 	S {is {t s} mode value}
    # }
    # --- broken chain #1 (undefined leaves?)
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode value}
    }
    {n S} {
	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode value}
    }
    # --- broken chain #2 (undefined leaves?)
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode value}
	B {is {n C} mode value}
    }
    {n S} {
	S {is {x {n A} {t s}} mode value}
	A {is {n C} mode value}
	B {is {n C} mode value}
    }
    # --- intermittents
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode value}
	B {is {n C} mode value}
	C {is {x {n X} {t c}} mode value}
	X {is {n Y} mode value}
	Y {is {n Z} mode value}
	Z {is {x {n Z} {t z}} mode value}
    }
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n C} mode value}
	B {is {n C} mode value}
	C {is {x {n X} {t c}} mode value}
	X {is {n Z} mode value}
	Y {is {n Z} mode value}
	Z {is {x {n Z} {t z}} mode value}
    }
    # {n S} {
    # 	S {is {x {n A} {t s}} mode value}
    # 	A {is {x {n X} {t c}} mode value}
    # 	B {is {x {n X} {t c}} mode value}
    # 	C {is {x {n X} {t c}} mode value}
    # 	X {is {x {n X} {t z}} mode value}
    # 	Y {is {x {n Y} {t z}} mode value}
    # 	Z {is {x {n Z} {t z}} mode value}
    # }
    # --- incompat modes #1a
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode void}
	B {is {n C} mode void}
	C {is {t c} mode value}
    }
    {n S} {
	S {is {x {n A} {t s}} mode value}
	A {is {n C} mode void}
	B {is {n C} mode void}
	C {is {t c} mode value}
    }
    # --- incompat modes #1b
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode void}
	B {is {n C} mode value}
	C {is {t c} mode value}
    }
    {n S} {
	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode void}
	B {is {n C} mode value}
	C {is {t c} mode value}
    }
    # X <- Z
    #
    # X     Z      dechain notes
    # value value| yes    | value is passed
    {n S} {
	S {is {n X} mode value}
	X {is {n Z} mode value}
	Z {is {t z} mode value}
    }
    {n S} {
	S {is {n Z} mode value}
	X {is {n Z} mode value}
	Z {is {t z} mode value}
    }
    # X     Z      dechain notes
    # leaf  value| no     | generated value was discarded, inlined doesn't. Z may be implied void
    {n S} {
	S {is {n X} mode leaf}
	X {is {n Z} mode value}
	Z {is {t z} mode value}
    }
    {n S} {
	S {is {n X} mode leaf}
	X {is {n Z} mode value}
	Z {is {t z} mode value}
    }
    # value leaf | yes    | value is passed
    {n S} {
	S {is {n X} mode value}
	X {is {n Z} mode leaf}
	Z {is {t z} mode value}
    }
    {n S} {
	S {is {n Z} mode value}
	X {is {n Z} mode leaf}
	Z {is {t z} mode value}
    }
    # value void | yes    | X is implied void
    {n S} {
	S {is {n X} mode value}
	X {is {n Z} mode void}
	Z {is {t z} mode value}
    }
    {n S} {
	S {is {n Z} mode value}
	X {is {n Z} mode void}
	Z {is {t z} mode value}
    }
    # leaf  leaf | no     | s.a.
    {n S} {
	S {is {n X} mode leaf}
	X {is {n Z} mode leaf}
	Z {is {t z} mode value}
    }
    {n S} {
	S {is {n X} mode leaf}
	X {is {n Z} mode leaf}
	Z {is {t z} mode value}
    }
    # leaf  void | no     | s.a.
    {n S} {
	S {is {n X} mode leaf}
	X {is {n Z} mode void}
	Z {is {t z} mode value}
    }
    {n S} {
	S {is {n X} mode leaf}
	X {is {n Z} mode void}
	Z {is {t z} mode value}
    }
    # void  value| no     | X drops value, inline doesn't
    {n S} {
	S {is {n X} mode void}
	X {is {n Z} mode value}
	Z {is {t z} mode value}
    }
    {n S} {
	S {is {n X} mode void}
	X {is {n Z} mode value}
	Z {is {t z} mode value}
    }
    # void  leaf | no     | s.a.
    {n S} {
	S {is {n X} mode void}
	X {is {n Z} mode leaf}
	Z {is {t z} mode value}
    }
    {n S} {
	S {is {n X} mode void}
	X {is {n Z} mode leaf}
	Z {is {t z} mode value}
    }
    # void  void | yes    |
    {n S} {
	S {is {n X} mode void}
	X {is {n Z} mode void}
	Z {is {t z} mode value}
    }
    {n S} {
	S {is {n Z} mode void}
	X {is {n Z} mode void}
	Z {is {t z} mode value}
    }
    {n S} {
	S {is {n S} mode value}
    }
    {n S} {
	S {is {n S} mode value}
    }
} $setimpl

# -------------------------------------------------------------------------
# op: modeopt

TestTransformation modeopt {
    # --- stays as-is
    epsilon {}
    epsilon {}
    # --- cycle
    # S <-- A; A <-- A
    {n S} {
	S {is {n A} mode value}
	A {is {n A} mode value}
    }
    {n S} {
	S {is {n A} mode value}
	A {is {n A} mode value}
    }
    # -- undefined (deferred) symbol: B
    {n S} {
	S {is {n A} mode value}
	A {is {n B} mode value}
    }
    {n S} {
	S {is {n A} mode value}
	A {is {n B} mode value}
    }
    # --- rule 1
    {n S} {
	S {is {n A} mode value}
	A {is {t A} mode value}
    }
    {n S} {
	S {is {n A} mode value}
	A {is {t A} mode leaf}
    }
    # --- rule 1
    {n S} {
	S {is {n A} mode value}
	A {is {x {t A} {n B}} mode value}
	B {is {t b} mode value}
    }
    {n S} {
	S {is {n A} mode value}
	A {is {x {t A} {n B}} mode value}
	B {is {t b} mode leaf}
    }
    # --- rule 2 (no opt)
    {n S} {
	S {is {x {n A} {n B}} mode value}
	A {is {n C} mode value}
	B {is {n C} mode value}
	C {is {t c} mode leaf}
    }
    {n S} {
	S {is {x {n A} {n B}} mode value}
	A {is {n C} mode value}
	B {is {n C} mode value}
	C {is {t c} mode leaf}
    }
    # --- rule 2 (void)
    {n S} {
	S {is {x {n A} {n B}} mode value}
	A {is {n C} mode void}
	B {is {n C} mode void}
	C {is {t c} mode leaf}
    }
    {n S} {
	S {is {x {n A} {n B}} mode value}
	A {is {n C} mode void}
	B {is {n C} mode void}
	C {is {t c} mode void}
    }
    # --- rule 2 (leaf)
    {n S} {
	S {is {x {n A} {n B}} mode value}
	A {is {n C} mode leaf}
	B {is {n C} mode leaf}
	C {is {t c} mode leaf}
    }
    {n S} {
	S {is {x {n A} {n B}} mode value}
	A {is {n C} mode leaf}
	B {is {n C} mode leaf}
	C {is {t c} mode void}
    }
    # --- rule 2 (mixed)
    {n S} {
	S {is {x {n A} {n B}} mode value}
	A {is {n C} mode leaf}
	B {is {n C} mode void}
	C {is {t c} mode leaf}
    }
    {n S} {
	S {is {x {n A} {n B}} mode value}
	A {is {n C} mode leaf}
	B {is {n C} mode void}
	C {is {t c} mode void}
    }
    # --- rule 2 (mixed, no opt)
    {n S} {
	S {is {x {n A} {n B}} mode value}
	A {is {n C} mode leaf}
	B {is {n C} mode value}
	C {is {t c} mode leaf}
    }
    {n S} {
	S {is {x {n A} {n B}} mode value}
	A {is {n C} mode leaf}
	B {is {n C} mode value}
	C {is {t c} mode leaf}
    }
    # --- rule 1: applies, rule 2: n/a
    {n S} {
	S {is {x {n A} {n B}} mode value}
	A {is {n C} mode leaf}
	B {is {n C} mode value}
	C {is {t c} mode value}
    }
    {n S} {
	S {is {x {n A} {n B}} mode value}
	A {is {n C} mode leaf}
	B {is {n C} mode value}
	C {is {t c} mode leaf}
    }
    # --- rule 1: applies, rule 2: applies
    {n S} {
	S {is {x {n A} {n B}} mode value}
	A {is {n C} mode leaf}
	B {is {n C} mode leaf}
	C {is {t c} mode value}
    }
    {n S} {
	S {is {x {n A} {n B}} mode value}
	A {is {n C} mode leaf}
	B {is {n C} mode leaf}
	C {is {t c} mode void}
    }
} $setimpl



# -------------------------------------------------------------------------
# op: minimize

TestTransformation minimize {
    # --- stays as-is
    epsilon {}
232
233
234
235
236
237
238


































239
240
241
242
243
244
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






     	S {is {/ {x {n A} {n B}} {t a}} mode value}
     	A {is {x {t a} {n A}} mode value}
	B {is {t a} mode leaf}
    }
    {n S} {
	S {is {t a} mode leaf}
    }
    # --- direct cycle
    {n A} {
	A {is {n A} mode value}
    }
    epsilon {}
    # --- indirect cycle
    {n A} {
	A {is {n B} mode value}
	B {is {n C} mode value}
	C {is {n A} mode value}
    }
    epsilon {}
    # --- dechaining creates unreachable and unrealisable rules; here: B, Y
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n B} mode value}
	B {is {n C} mode value}
	C {is {x {n X} {t c}} mode value}
	X {is {n Y} mode value}
	Y {is {n Z} mode value}
	Z {is {x {t z}} mode leaf}
    }
    {n S} {
    	S {is {x {n A} {t s}} mode value}
	A {is {n C} mode value}
	C {is {x {n X} {t c}} mode value}
	X {is {n Z} mode value}
	Z {is {t z} mode leaf}
    }
    # {n S} {
    # 	S {is {x {n A} {t s}} mode value}
    # 	A {is {x {n X} {t c}} mode value}
    # 	X {is {t z} mode leaf}
    # }
} $setimpl

# -------------------------------------------------------------------------
rename sl {}
rename g {}
rename TestTransformation {}

Changes to modules/smtpd/smtpd.man.

218
219
220
221
222
223
224
225

226
227
228
229
230
231
232
218
219
220
221
222
223
224

225
226
227
228
229
230
231
232







-
+







[def "[cmd validate_recipient] callback"]

The validate_recipient callback is similar to the validate_sender
callback and permits you to verify a local mailbox and accept mail for
a local user address during RCPT command handling. To reject mail,
throw an error as above. The error message is ignored.

[def "[cmd deliverMIME] callback"]]
[def "[cmd deliverMIME] callback"]

The deliverMIME callback is called once a mail message has been
successfully passed to the server. A mime token is constructed from
the sender, recipients and data and the users procedure it called with
this single argument. When the call returns, the mime token is cleaned
up so if the user wishes to preserve the data she must make a copy.

Changes to modules/stooop/switched.man.

261
262
263
264
265
266
267
268

269
270
271

272
273

274
275
276
277
278
279
280
261
262
263
264
265
266
267

268
269
270

271
272

273
274
275
276
277
278
279
280







-
+


-
+

-
+







the validity of the value passed to the [method set-[option option]]
procedure, which should throw an error (for example by using the Tcl
error command) if the value is invalid.

[para] The switched layer also keeps track of the options current
values, so that a [method set-[option option]] procedure is called
only when the corresponding option value passed as parameter is
different from the current value (see [variable -option] data members
different from the current value (see [var -option] data members
description).

[def [variable -option]]
[def [var -option]]

[para] The [variable -option] data member is an options current value.
[para] The [var -option] data member is an options current value.

There is one for each option listed in the options procedure. It is a
read-only value which the switched layer checks against when an option
is changed.

It is rarely used at the layer derived from switched, except in the
few cases, such as in the following example:
297
298
299
300
301
302
303
304

305
306
307


308
309
310
311
312
313
314
297
298
299
300
301
302
303

304
305


306
307
308
309
310
311
312
313
314







-
+

-
-
+
+







}
}]

[para] In this case, the manufacturer's name is stored at the switched
layer level (this is why the set-manufacturer procedure has nothing to
do) and later retrieved in the printData procedure.

[def [variable complete]]
[def [var complete]]

[para] The [variable complete] data member (not to be confused with
the [method complete] procedure) is a boolean.
[para] The [var complete] data member (not to be confused with the
[method complete] procedure) is a boolean.

Its initial value is [const false] and it is set to [const true] at
the very end of the switched [method complete] procedure.

It becomes useful when some options should be set at construction time
only and not dynamically, as the following example shows:

Changes to modules/tepam/tepam_doc_gen.man.

223
224
225
226
227
228
229
230

231
232
233
234
235
236
237
223
224
225
226
227
228
229

230
231
232
233
234
235
236
237







-
+







   Generates the part of the command line or the synopsis that is specific to an argument. The generated string has to indicate if an argument is optional, named and if it is a flag.
   [para]
   The following parameters are provided to this procedure:
   [list_begin definitions]
   [def [arg "Name"]]
      Name of the argument
   [def [arg "IsOptional"]]
      If true (=[const 1]) the argument is optional which should be indicated by the generated string (for example by putting the argument into brackets {[]} or into question marks '?'):
      If true (=[const 1]) the argument is optional which should be indicated by the generated string (for example by putting the argument into brackets {[lb][rb]} or into question marks '?'):
      [example_begin]gen(TXT,ArgumentString) mtype 1 0 string -> [emph {"[mtype]"}][example_end]
   [def [arg "IsNamed"]]
      If true (=[const 1]) an argument is a named argument (option). The generated string should in this case contain the argument/option name, followed by the argument itself:
      [example_begin]gen(TXT,ArgumentString) mtype 0 1 string -> [emph {"-mtype <mtype>"}][example_end]
      Named arguments can also be optional:
      [example_begin]gen(TXT,ArgumentString) mtype 1 1 string -> [emph {"[-mtype <mtype>]"}][example_end]
   [def [arg "Type"]]

Changes to modules/tepam/tepam_procedure.man.

772
773
774
775
776
777
778
779

780
781
782
783
784
785
786
772
773
774
775
776
777
778

779
780
781
782
783
784
785
786







-
+








The name of the first unnamed argument has therefore not to start with the '-' character. The unnamed argument is otherwise considered as name of another named argument. This is especially important if the first unnamed argument is given by a variable that can contain any character strings:

[example_begin]my_proc [cmd {-n1 N1 -n2 N2 "->" "<-"}]
[emph {-> my_proc: Argument '->' not known}]

set U1 "->"
my_proc -n1 N1 -n2 N2 $U1 U2}]
my_proc [cmd {-n1 N1 -n2 N2 $U1 U2}]
my_proc: Argument '->' not known[example_end]

The '--' flag allows separating unambiguously the unnamed arguments from the named arguments. All data after the '--' flag will be considered as unnamed argument:

[example_begin]my_proc [cmd {-n1 N1 -n2 N2 -- "->" "<-"}]
[emph {-> n1:'N1', n2:'N2', u1:'->', u2:'<-'}]

Changes to modules/textutil/adjust.man.

125
126
127
128
129
130
131
132

133
134
135
136
137
138
139
125
126
127
128
129
130
131

132
133
134
135
136
137
138
139







-
+








[opt_def  -length [arg integer]]

Set the length of the [emph logical] line in the string to
[arg integer].  [arg integer] must be a positive integer
value. Defaults to [const 72].

[opt_def -strictlength] [arg boolean]]
[opt_def -strictlength [arg boolean]]

If set to [const false] (default), a line can exceed the specified
[option -length] if a single word is longer than [option -length]. If
set to [const true], words that are longer than [option -length] are
split so that no line exceeds the specified [option -length].

[list_end]

Changes to modules/tool/build/option.tcl.

Changes to modules/tool/build/organ.tcl.

Changes to modules/tool/build/pipeline.tcl.

Changes to modules/tool/build/script.tcl.

Changes to modules/tool/module.shed.

Changes to modules/tool/tool.md.

Changes to modules/tool/tool_dict_ensemble.man.

11
12
13
14
15
16
17
18

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

18
19
20
21
22
23
24
25







-
+







[description]
[para]
The [cmd dict_ensemble] command is a keyword added by [package tool]. It defines
a public variable (stored as a dict), and an access function to manipulated and
access the values stored in that dict.
[list_begin definitions]

[call [emph object] [arg ensemble] [cmd add] [arg field]]] [arg value] [arg {value ...}]]
[call [emph object] [arg ensemble] [cmd add] [arg field] [arg value] [arg {value ...}]]

Adds elements to a list maintained with the [arg field] leaf of the dict maintained
my this ensemble.


Declares a variable [arg name] which will be initialized as an array, populated with [arg contents] for objects of this class, as well as any
objects for classes which are descendents of this class.

Changes to modules/uuid/pkgIndex.tcl.

1
2

1

2

-
+
if {![package vsatisfies [package provide Tcl] 8.5]} {return}
package ifneeded uuid 1.0.6 [list source [file join $dir uuid.tcl]]
package ifneeded uuid 1.0.7 [list source [file join $dir uuid.tcl]]

Changes to modules/uuid/uuid.tcl.

44
45
46
47
48
49
50
51

52
53
54
55
56
57
58
44
45
46
47
48
49
50

51
52
53
54
55
56
57
58







-
+







  lappend machinfo [array get ::tcl_platform]

  ###
  # If we have /dev/urandom just stream 128 bits from that
  ###
  if {[file exists /dev/urandom]} {
    set fin [open /dev/urandom r]
    set machinfo [read $fin 128]
    binary scan [read $fin 128] H* machinfo
    close $fin
  } elseif {[catch {package require nettool}]} {
    # More spatial information -- better than hostname.
    # bug 1150714: opening a server socket may raise a warning messagebox
    #   with WinXP firewall, using ipconfig will return all IP addresses
    #   including ipv6 ones if available. ipconfig is OK on win98+
    if {[string equal $::tcl_platform(platform) "windows"]} {
232
233
234
235
236
237
238
239

240
241
242
243
244
245
232
233
234
235
236
237
238

239
240
241
242
243
244
245







-
+






    variable e {}
    foreach e {critcl} {
        if {[LoadAccelerator $e]} break
    }
    unset e
}

package provide uuid 1.0.6
package provide uuid 1.0.7

# -------------------------------------------------------------------------
# Local variables:
#   mode: tcl
#   indent-tabs-mode: nil
# End:

Changes to modules/uuid/uuid.test.

1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19











-
+







# uuid.test:  tests for the uuid package                       -*- tcl -*-
#
# $Id: uuid.test,v 1.6 2006/10/09 21:41:42 andreas_kupries Exp $

# -------------------------------------------------------------------------

source [file join \
	[file dirname [file dirname [file join [pwd] [info script]]]] \
	devtools testutilities.tcl]

testsNeedTcl     8.5
testsNeedTcltest 1.0
testsNeedTcltest 2.0

testing {
    useLocal uuid.tcl uuid
}

# -------------------------------------------------------------------------
# Handle multiple implementation testing
57
58
59
60
61
62
63
64

65
66
67
68

69
70
71
72
73
74
75

76
77
78
79
80
81
82
83
84

85
86
87
88
89
90
91
92
93
94
95
96
57
58
59
60
61
62
63

64
65
66
67

68
69
70
71
72
73
74

75
76
77
78
79
80
81
82
83

84
85
86
87
88
89
90
91
92
93
94
95
96







-
+



-
+






-
+








-
+













foreach impl [implementations] {
    select_implementation $impl

    test uuid-1.0-$impl "uuid requires args" {
        list [catch {uuid::uuid} msg]
    } {1}
    

    test uuid-1.1-$impl "uuid generate should create a 36 char string uuid" {
        list [catch {string length [uuid::uuid generate]} msg] $msg
    } {0 36}
    

    test uuid-1.2-$impl "uuid comparison of uuid with self should be true" {
        list [catch {
            set a [uuid::uuid generate]
            uuid::uuid equal $a $a
        } msg] $msg
    } {0 1}
    

    test uuid-1.3-$impl "uuid comparison of two different\
        uuids should be false" {
        list [catch {
            set a [uuid::uuid generate]
            set b [uuid::uuid generate]
            uuid::uuid equal $a $b
        } msg] $msg
    } {0 0}
    

    reset_implementation
}

# -------------------------------------------------------------------------

testsuiteCleanup

# -------------------------------------------------------------------------
# Local Variables:
#   mode: tcl
#   indent-tabs-mode: nil
# End:

Changes to modules/websocket/websocket.man.

201
202
203
204
205
206
207
208

209
210
211
212
213
214
215
201
202
203
204
205
206
207

208
209
210
211
212
213
214
215







-
+







identifier fo an HTTP server that is capable of doing WebSockets.
Paths onto which this server will listen for incoming connections
should be declared using [cmd ::websocket::live].

[call [cmd ::websocket::live] [arg sock] [arg path] [arg cb] [opt [arg proto]]]

This procedure registers callbacks that will be performed on a
WebSocket compliant server registered with [cmd ::websocket::server]]
WebSocket compliant server registered with [cmd ::websocket::server]
whenever a client connects to a matching path and protocol. 
[arg sock] is the listening socket of the websocket compliant server
declared using [cmd ::websocket::server].  [arg path] is a glob-style
path to match in client request, whenever this will occur.  [arg cb]
is the command to callback (see Callbacks).  [arg proto] is a
glob-style protocol name matcher.

Changes to support/devel/sak/doc/kwic.txt.

1863
1864
1865
1866
1867
1868
1869

1870
1871
1872
1873
1874
1875
1876
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877







+







[manpage modules/math/interpolate.man             math::interpolate]
[manpage modules/math/linalg.man                  math::linearalgebra]
[manpage modules/math/optimize.man                math::optimize]
[manpage modules/math/pca.man                     math::PCA]
[manpage modules/math/polynomials.man             math::polynomials]
[manpage modules/math/rational_funcs.man          math::rationalfunctions]
[manpage modules/math/special.man                 math::special]
[manpage modules/math/trig.man                    math::trig]
[manpage modules/simulation/annealing.man         simulation::annealing]
[manpage modules/simulation/montecarlo.man        simulation::montecarlo]
[manpage modules/simulation/simulation_random.man simulation::random]
[key mathematics]
[manpage modules/math/fourier.man    math::fourier]
[manpage modules/math/statistics.man math::statistics]
[key matrices]
3574
3575
3576
3577
3578
3579
3580


3581
3582
3583
3584
3585
3586
3587
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590







+
+







[manpage modules/treeql/treeql.man treeql]
[key {tree walking}]
[manpage modules/page/page_util_flow.man       page_util_flow]
[manpage modules/page/page_util_norm_lemon.man page_util_norm_lemon]
[manpage modules/page/page_util_norm_peg.man   page_util_norm_peg]
[key TreeQL]
[manpage modules/treeql/treeql.man treeql]
[key trigonometry]
[manpage modules/math/trig.man math::trig]
[key trimming]
[manpage modules/textutil/textutil.man textutil]
[manpage modules/textutil/trim.man     textutil::trim]
[key twitter]
[manpage modules/oauth/oauth.man oauth]
[key type]
[manpage modules/fileutil/fileutil.man fileutil]

Changes to support/devel/sak/doc/manpages.txt.

199
200
201
202
203
204
205

206
207
208
209
210
211
212
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213







+







modules/math/qcomplex.man
modules/math/rational_funcs.man
modules/math/roman.man
modules/math/romberg.man
modules/math/special.man
modules/math/statistics.man
modules/math/symdiff.man
modules/math/trig.man
modules/md4/md4.man
modules/md5/md5.man
modules/md5crypt/md5crypt.man
modules/mime/mime.man
modules/mime/smtp.man
modules/multiplexer/multiplexer.man
modules/namespacex/namespacex.man

Changes to support/devel/sak/doc/toc.txt.

211
212
213
214
215
216
217

218
219
220
221
222
223
224
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225







+







[item modules/math/optimize.man                math::optimize          {Optimisation routines}]
[item modules/math/pca.man                     math::PCA               {Package for Principal Component Analysis}]
[item modules/math/polynomials.man             math::polynomials       {Polynomial functions}]
[item modules/math/rational_funcs.man          math::rationalfunctions {Polynomial functions}]
[item modules/math/roman.man                   math::roman             {Tools for creating and manipulating roman numerals}]
[item modules/math/special.man                 math::special           {Special mathematical functions}]
[item modules/math/statistics.man              math::statistics        {Basic statistical functions and procedures}]
[item modules/math/trig.man                    math::trig              {Trigonometric anf hyperbolic functions}]
[item modules/simulation/annealing.man         simulation::annealing   {Simulated annealing}]
[item modules/simulation/montecarlo.man        simulation::montecarlo  {Monte Carlo simulations}]
[item modules/simulation/simulation_random.man simulation::random      {Pseudo-random number generators}]
[division_end]
[division_start Networking]
[item modules/asn/asn.man               asn                {ASN.1 BER encoder/decoder}]
[item modules/http/autoproxy.man        autoproxy          {Automatic HTTP proxy usage and authentication}]
807
808
809
810
811
812
813

814
815
816
817
818
819
820
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822







+







[item modules/math/optimize.man          math::optimize           {Optimisation routines}]
[item modules/math/pca.man               math::PCA                {Package for Principal Component Analysis}]
[item modules/math/polynomials.man       math::polynomials        {Polynomial functions}]
[item modules/math/rational_funcs.man    math::rationalfunctions  {Polynomial functions}]
[item modules/math/roman.man             math::roman              {Tools for creating and manipulating roman numerals}]
[item modules/math/special.man           math::special            {Special mathematical functions}]
[item modules/math/statistics.man        math::statistics         {Basic statistical functions and procedures}]
[item modules/math/trig.man              math::trig               {Trigonometric anf hyperbolic functions}]
[item modules/math/machineparameters.man tclrep/machineparameters {Compute double precision machine parameters.}]
[division_end]
[division_start md4]
[item modules/md4/md4.man md4 {MD4 Message-Digest Algorithm}]
[division_end]
[division_start md5]
[item modules/md5/md5.man md5 {MD5 Message-Digest Algorithm}]

Changes to support/devel/sak/doc/toc_cats.txt.

211
212
213
214
215
216
217

218
219
220
221
222
223
224
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225







+







[item modules/math/optimize.man                math::optimize          {Optimisation routines}]
[item modules/math/pca.man                     math::PCA               {Package for Principal Component Analysis}]
[item modules/math/polynomials.man             math::polynomials       {Polynomial functions}]
[item modules/math/rational_funcs.man          math::rationalfunctions {Polynomial functions}]
[item modules/math/roman.man                   math::roman             {Tools for creating and manipulating roman numerals}]
[item modules/math/special.man                 math::special           {Special mathematical functions}]
[item modules/math/statistics.man              math::statistics        {Basic statistical functions and procedures}]
[item modules/math/trig.man                    math::trig              {Trigonometric anf hyperbolic functions}]
[item modules/simulation/annealing.man         simulation::annealing   {Simulated annealing}]
[item modules/simulation/montecarlo.man        simulation::montecarlo  {Monte Carlo simulations}]
[item modules/simulation/simulation_random.man simulation::random      {Pseudo-random number generators}]
[division_end]
[division_start Networking]
[item modules/asn/asn.man               asn                {ASN.1 BER encoder/decoder}]
[item modules/http/autoproxy.man        autoproxy          {Automatic HTTP proxy usage and authentication}]

Changes to support/devel/sak/doc/toc_mods.txt.

312
313
314
315
316
317
318

319
320
321
322
323
324
325
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326







+







[item modules/math/optimize.man          math::optimize           {Optimisation routines}]
[item modules/math/pca.man               math::PCA                {Package for Principal Component Analysis}]
[item modules/math/polynomials.man       math::polynomials        {Polynomial functions}]
[item modules/math/rational_funcs.man    math::rationalfunctions  {Polynomial functions}]
[item modules/math/roman.man             math::roman              {Tools for creating and manipulating roman numerals}]
[item modules/math/special.man           math::special            {Special mathematical functions}]
[item modules/math/statistics.man        math::statistics         {Basic statistical functions and procedures}]
[item modules/math/trig.man              math::trig               {Trigonometric anf hyperbolic functions}]
[item modules/math/machineparameters.man tclrep/machineparameters {Compute double precision machine parameters.}]
[division_end]
[division_start md4]
[item modules/md4/md4.man md4 {MD4 Message-Digest Algorithm}]
[division_end]
[division_start md5]
[item modules/md5/md5.man md5 {MD5 Message-Digest Algorithm}]

Changes to support/installation/modules.tcl.

41
42
43
44
45
46
47

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







+







Module  base64      _tcl  _man  _null
Module  bee         _tcl  _man  _null
Module  bench       _tcl _null  _null
Module  bibtex      _tcl  _man  _exa
Module  blowfish    _tcl  _man  _null
Module  cache       _tcl  _man  _null
Module  calendar     _tci _man  _null
Module  clay         _tcl  _man _null
Module  clock       _tcl  _man _null
Module  cmdline     _tcl  _man  _null
Module  comm        _tcl  _man  _null
Module  control      _tci _man  _null
Module  coroutine   _tcl _null  _null
Module  counter     _tcl  _man  _null
Module  crc         _tcl  _man  _null