Check-in [33ec70d3ba]
Not logged in

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

Overview
Comment:merge trunk
Timelines: family | ancestors | descendants | both | tip-634
Files: files | file ages | folders
SHA3-256: 33ec70d3baae23b7aaac9201c5702fe16b1572d8bd705e670c062e6a9caa040b
User & Date: dgp 2022-09-21 14:09:14.672
Context
2022-09-21
14:59
Reduce chances for test conflicts. check-in: f066ed868c user: dgp tags: tip-634
14:09
merge trunk check-in: 33ec70d3ba user: dgp tags: tip-634
13:10
Merge 8.7 check-in: c83b5c1986 user: jan.nijtmans tags: trunk, main
2022-08-31
11:06
Implement modification of the 'name2' trace callback argument. check-in: f5cc609f18 user: sbron tags: tip-634
Changes
Unified Diff Ignore Whitespace Patch
Changes to .gitignore.
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
config.status
config.status.lineno
html
manifest.uuid
_FOSSIL_
*/tclConfig.sh
*/tclsh*
*/tcltest*
*/versions.vc
*/version.vc
*/libtcl.vfs
*/libtcl*.zip
*/tclUuid.h
libtommath/bn.ilg
libtommath/bn.ind







|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
config.status
config.status.lineno
html
manifest.uuid
_FOSSIL_
*/tclConfig.sh
*/tclsh*
*/tcltest
*/versions.vc
*/version.vc
*/libtcl.vfs
*/libtcl*.zip
*/tclUuid.h
libtommath/bn.ilg
libtommath/bn.ind
Changes to doc/ByteArrObj.3.
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
.AP Tcl_Obj *objPtr in/out
For \fBTcl_SetByteArrayObj\fR, this points to an unshared value to be
overwritten by a byte-array value.  For \fBTcl_GetBytesFromObj\fR,
\fBTcl_GetByteArrayFromObj\fR and \fBTcl_SetByteArrayLength\fR, this points
to the value from which to extract an array of bytes.
.AP Tcl_Interp *interp in
Interpreter to use for error reporting.
.AP "size_t | int" *numBytesPtr out
Points to space where the number of bytes in the array may be written.
Caller may pass NULL when it does not need this information.
.BE

.SH DESCRIPTION
.PP
These routines are used to create, modify, store, transfer, and retrieve







|







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
.AP Tcl_Obj *objPtr in/out
For \fBTcl_SetByteArrayObj\fR, this points to an unshared value to be
overwritten by a byte-array value.  For \fBTcl_GetBytesFromObj\fR,
\fBTcl_GetByteArrayFromObj\fR and \fBTcl_SetByteArrayLength\fR, this points
to the value from which to extract an array of bytes.
.AP Tcl_Interp *interp in
Interpreter to use for error reporting.
.AP "size_t \&| int" *numBytesPtr out
Points to space where the number of bytes in the array may be written.
Caller may pass NULL when it does not need this information.
.BE

.SH DESCRIPTION
.PP
These routines are used to create, modify, store, transfer, and retrieve
Changes to doc/Class.3.
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
The name of the namespace to create for the object's private use, or NULL if a
new unused name is to be automatically selected. The namespace must not
already exist.
.AP size_t objc in
The number of elements in the \fIobjv\fR array.
.AP "Tcl_Obj *const" *objv in
The arguments to the command to create the instance of the class.
.AP int skip in
The number of arguments at the start of the argument array, \fIobjv\fR, that
are not arguments to any constructors. This allows the generation of correct
error messages even when complicated calling patterns are used (e.g., via the
\fBnext\fR command).
.AP Tcl_ObjectMetadataType *metaTypePtr in
The type of \fImetadata\fR being set with \fBTcl_ClassSetMetadata\fR or
retrieved with \fBTcl_ClassGetMetadata\fR.







|







81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
The name of the namespace to create for the object's private use, or NULL if a
new unused name is to be automatically selected. The namespace must not
already exist.
.AP size_t objc in
The number of elements in the \fIobjv\fR array.
.AP "Tcl_Obj *const" *objv in
The arguments to the command to create the instance of the class.
.AP size_t skip in
The number of arguments at the start of the argument array, \fIobjv\fR, that
are not arguments to any constructors. This allows the generation of correct
error messages even when complicated calling patterns are used (e.g., via the
\fBnext\fR command).
.AP Tcl_ObjectMetadataType *metaTypePtr in
The type of \fImetadata\fR being set with \fBTcl_ClassSetMetadata\fR or
retrieved with \fBTcl_ClassGetMetadata\fR.
Changes to doc/CrtAlias.3.
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
'\"
'\" Copyright (c) 1995-1996 Sun Microsystems, Inc.
'\"
'\" See the file "license.terms" for information on usage and redistribution
'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
'\"
.TH Tcl_CreateAlias 3 7.6 Tcl "Tcl Library Procedures"
.so man.macros
.BS
.SH NAME
Tcl_IsSafe, Tcl_MakeSafe, Tcl_CreateChild, Tcl_GetChild, Tcl_GetParent, Tcl_GetInterpPath, Tcl_CreateAlias, Tcl_CreateAliasObj, Tcl_GetAlias, Tcl_GetAliasObj, Tcl_ExposeCommand, Tcl_HideCommand \- manage multiple Tcl interpreters, aliases and hidden commands
.SH SYNOPSIS
.nf
\fB#include <tcl.h>\fR
.sp
int
\fBTcl_IsSafe\fR(\fIinterp\fR)
.sp
int
\fBTcl_MakeSafe\fR(\fIinterp\fR)
.sp
Tcl_Interp *
\fBTcl_CreateChild\fR(\fIinterp, name, isSafe\fR)
.sp
Tcl_Interp *
\fBTcl_GetChild\fR(\fIinterp, name\fR)
.sp
Tcl_Interp *










|







<
<
<







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
'\"
'\" Copyright (c) 1995-1996 Sun Microsystems, Inc.
'\"
'\" See the file "license.terms" for information on usage and redistribution
'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
'\"
.TH Tcl_CreateAlias 3 7.6 Tcl "Tcl Library Procedures"
.so man.macros
.BS
.SH NAME
Tcl_IsSafe, Tcl_CreateChild, Tcl_GetChild, Tcl_GetParent, Tcl_GetInterpPath, Tcl_CreateAlias, Tcl_CreateAliasObj, Tcl_GetAlias, Tcl_GetAliasObj, Tcl_ExposeCommand, Tcl_HideCommand \- manage multiple Tcl interpreters, aliases and hidden commands
.SH SYNOPSIS
.nf
\fB#include <tcl.h>\fR
.sp
int
\fBTcl_IsSafe\fR(\fIinterp\fR)
.sp



Tcl_Interp *
\fBTcl_CreateChild\fR(\fIinterp, name, isSafe\fR)
.sp
Tcl_Interp *
\fBTcl_GetChild\fR(\fIinterp, name\fR)
.sp
Tcl_Interp *
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
If the creation of the new child interpreter failed, \fBNULL\fR is returned.
.PP
\fBTcl_IsSafe\fR returns \fB1\fR if \fIinterp\fR is
.QW safe
(was created with the \fBTCL_SAFE_INTERPRETER\fR flag specified),
\fB0\fR otherwise.
.PP
\fBTcl_MakeSafe\fR marks \fIinterp\fR as
.QW safe ,
so that future
calls to \fBTcl_IsSafe\fR will return 1.  It also removes all known
potentially-unsafe core functionality (both commands and variables)
from \fIinterp\fR.  However, it cannot know what parts of an extension
or application are safe and does not make any attempt to remove those
parts, so safety is not guaranteed after calling \fBTcl_MakeSafe\fR.
Callers will want to take care with their use of \fBTcl_MakeSafe\fR
to avoid false claims of safety.  For many situations, \fBTcl_CreateChild\fR
may be a better choice, since it creates interpreters in a known-safe state.
.PP
\fBTcl_GetChild\fR returns a pointer to a child interpreter of
\fIinterp\fR. The child interpreter is identified by \fIname\fR.
If no such child interpreter exists, \fBNULL\fR is returned.
.PP
\fBTcl_GetParent\fR returns a pointer to the parent interpreter of
\fIinterp\fR. If \fIinterp\fR has no parent (it is a
top-level interpreter) then \fBNULL\fR is returned.







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







131
132
133
134
135
136
137












138
139
140
141
142
143
144
If the creation of the new child interpreter failed, \fBNULL\fR is returned.
.PP
\fBTcl_IsSafe\fR returns \fB1\fR if \fIinterp\fR is
.QW safe
(was created with the \fBTCL_SAFE_INTERPRETER\fR flag specified),
\fB0\fR otherwise.
.PP












\fBTcl_GetChild\fR returns a pointer to a child interpreter of
\fIinterp\fR. The child interpreter is identified by \fIname\fR.
If no such child interpreter exists, \fBNULL\fR is returned.
.PP
\fBTcl_GetParent\fR returns a pointer to the parent interpreter of
\fIinterp\fR. If \fIinterp\fR has no parent (it is a
top-level interpreter) then \fBNULL\fR is returned.
Changes to doc/CrtChannel.3.
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
.PP
The \fIwideSeekProc\fR field contains the address of a function called by the
generic layer to move the access point at which subsequent input or output
operations will be applied. \fIWideSeekProc\fR must match the following
prototype:
.PP
.CS
typedef Tcl_WideInt \fBTcl_DriverWideSeekProc\fR(
        void *\fIinstanceData\fR,
        Tcl_WideInt \fIoffset\fR,
        int \fIseekMode\fR,
        int *\fIerrorCodePtr\fR);
.CE
.PP
The \fIinstanceData\fR argument is the same as the value given to
\fBTcl_CreateChannel\fR when this channel was created.  \fIOffset\fR and
\fIseekMode\fR have the same meaning as for the \fBTcl_Seek\fR







|

|







526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
.PP
The \fIwideSeekProc\fR field contains the address of a function called by the
generic layer to move the access point at which subsequent input or output
operations will be applied. \fIWideSeekProc\fR must match the following
prototype:
.PP
.CS
typedef long long \fBTcl_DriverWideSeekProc\fR(
        void *\fIinstanceData\fR,
        long long \fIoffset\fR,
        int \fIseekMode\fR,
        int *\fIerrorCodePtr\fR);
.CE
.PP
The \fIinstanceData\fR argument is the same as the value given to
\fBTcl_CreateChannel\fR when this channel was created.  \fIOffset\fR and
\fIseekMode\fR have the same meaning as for the \fBTcl_Seek\fR
Changes to doc/CrtChnlHdlr.3.
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
.AS Tcl_ChannelProc clientData
.AP Tcl_Channel channel in
Tcl channel such as returned by \fBTcl_CreateChannel\fR.
.AP int mask in
Conditions under which \fIproc\fR should be called: OR-ed combination of
\fBTCL_READABLE\fR, \fBTCL_WRITABLE\fR and \fBTCL_EXCEPTION\fR. Specify
a zero value to temporarily disable an existing handler.
.AP Tcl_FileProc *proc in
Procedure to invoke whenever the channel indicated by \fIchannel\fR meets
the conditions specified by \fImask\fR.
.AP void *clientData in
Arbitrary one-word value to pass to \fIproc\fR.
.BE
.SH DESCRIPTION
.PP







|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
.AS Tcl_ChannelProc clientData
.AP Tcl_Channel channel in
Tcl channel such as returned by \fBTcl_CreateChannel\fR.
.AP int mask in
Conditions under which \fIproc\fR should be called: OR-ed combination of
\fBTCL_READABLE\fR, \fBTCL_WRITABLE\fR and \fBTCL_EXCEPTION\fR. Specify
a zero value to temporarily disable an existing handler.
.AP Tcl_ChannelProc *proc in
Procedure to invoke whenever the channel indicated by \fIchannel\fR meets
the conditions specified by \fImask\fR.
.AP void *clientData in
Arbitrary one-word value to pass to \fIproc\fR.
.BE
.SH DESCRIPTION
.PP
Changes to doc/DictObj.3.
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
dictionary value (or sub-value, in the case of
\fBTcl_DictObjPutKeyList\fR.)
.AP Tcl_Obj **valuePtrPtr out
Points to a variable that will have the value from a key/value pair
placed within it.  For \fBTcl_DictObjFirst\fR and
\fBTcl_DictObjNext\fR, this may be NULL to indicate that the caller is
not interested in the value.
.AP size_t | int *sizePtr out
Points to a variable that will have the number of key/value pairs
contained within the dictionary placed within it.
.AP Tcl_DictSearch *searchPtr in/out
Pointer to record to use to keep track of progress in enumerating all
key/value pairs in a dictionary.  The contents of the record will be
initialized by the call to \fBTcl_DictObjFirst\fR.  If the enumerating
is to be terminated before all values in the dictionary have been







|







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
dictionary value (or sub-value, in the case of
\fBTcl_DictObjPutKeyList\fR.)
.AP Tcl_Obj **valuePtrPtr out
Points to a variable that will have the value from a key/value pair
placed within it.  For \fBTcl_DictObjFirst\fR and
\fBTcl_DictObjNext\fR, this may be NULL to indicate that the caller is
not interested in the value.
.AP "size_t \&| int" *sizePtr out
Points to a variable that will have the number of key/value pairs
contained within the dictionary placed within it.
.AP Tcl_DictSearch *searchPtr in/out
Pointer to record to use to keep track of progress in enumerating all
key/value pairs in a dictionary.  The contents of the record will be
initialized by the call to \fBTcl_DictObjFirst\fR.  If the enumerating
is to be terminated before all values in the dictionary have been
Changes to doc/Eval.3.
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
A Tcl value containing the script to execute.
.AP int flags in
ORed combination of flag bits that specify additional options.
\fBTCL_EVAL_GLOBAL\fR and \fBTCL_EVAL_DIRECT\fR are currently supported.
.AP "const char" *fileName in
Name of a file containing a Tcl script.
.AP size_t objc in
The number of values in the array pointed to by \fIobjPtr\fR;
this is also the number of words in the command.
.AP Tcl_Obj **objv in
Points to an array of pointers to values; each value holds the
value of a single word in the command to execute.
.AP int numBytes in
The number of bytes in \fIscript\fR, not including any
null terminating character.  If \-1, then all characters up to the







|







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
A Tcl value containing the script to execute.
.AP int flags in
ORed combination of flag bits that specify additional options.
\fBTCL_EVAL_GLOBAL\fR and \fBTCL_EVAL_DIRECT\fR are currently supported.
.AP "const char" *fileName in
Name of a file containing a Tcl script.
.AP size_t objc in
The number of values in the array pointed to by \fIobjv\fR;
this is also the number of words in the command.
.AP Tcl_Obj **objv in
Points to an array of pointers to values; each value holds the
value of a single word in the command to execute.
.AP int numBytes in
The number of bytes in \fIscript\fR, not including any
null terminating character.  If \-1, then all characters up to the
Changes to doc/FileSystem.3.
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
used to set those values for a given file.
.AP "const char" *modeString in
Specifies how the file is to be accessed. May have any of the values
allowed for the \fImode\fR argument to the Tcl \fBopen\fR command.
.AP int permissions in
POSIX-style permission flags such as 0644. If a new file is created, these
permissions will be set on the created file.
.AP size_t | int *lenPtr out
If non-NULL, filled with the number of elements in the split path.
.AP Tcl_Obj *basePtr in
The base path on to which to join the given elements. May be NULL.
.AP size_t objc in
The number of elements in \fIobjv\fR.
.AP "Tcl_Obj *const" objv[] in
The elements to join to the given base path.







|







265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
used to set those values for a given file.
.AP "const char" *modeString in
Specifies how the file is to be accessed. May have any of the values
allowed for the \fImode\fR argument to the Tcl \fBopen\fR command.
.AP int permissions in
POSIX-style permission flags such as 0644. If a new file is created, these
permissions will be set on the created file.
.AP "size_t \&| int" *lenPtr out
If non-NULL, filled with the number of elements in the split path.
.AP Tcl_Obj *basePtr in
The base path on to which to join the given elements. May be NULL.
.AP size_t objc in
The number of elements in \fIobjv\fR.
.AP "Tcl_Obj *const" objv[] in
The elements to join to the given base path.
Changes to doc/GetVersion.3.
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Tcl_GetVersion \- get the version of the library at runtime
.SH SYNOPSIS
.nf
\fB#include <tcl.h>\fR
.sp
\fBTcl_GetVersion\fR(\fImajor, minor, patchLevel, type\fR)
.SH ARGUMENTS
.AS Tcl_ReleaseType *patchLevel out
.AP int *major out
Major version number of the Tcl library.
.AP int *minor out
Minor version number of the Tcl library.
.AP int *patchLevel out
The patch level of the Tcl library (or alpha or beta number).
.AP Tcl_ReleaseType *type out
The type of release, also indicates the type of patch level. Can be
one of \fBTCL_ALPHA_RELEASE\fR, \fBTCL_BETA_RELEASE\fR, or
\fBTCL_FINAL_RELEASE\fR.
.BE

.SH DESCRIPTION
.PP







<






|







11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27
28
29
30
31
Tcl_GetVersion \- get the version of the library at runtime
.SH SYNOPSIS
.nf
\fB#include <tcl.h>\fR
.sp
\fBTcl_GetVersion\fR(\fImajor, minor, patchLevel, type\fR)
.SH ARGUMENTS

.AP int *major out
Major version number of the Tcl library.
.AP int *minor out
Minor version number of the Tcl library.
.AP int *patchLevel out
The patch level of the Tcl library (or alpha or beta number).
.AP int *type out
The type of release, also indicates the type of patch level. Can be
one of \fBTCL_ALPHA_RELEASE\fR, \fBTCL_BETA_RELEASE\fR, or
\fBTCL_FINAL_RELEASE\fR.
.BE

.SH DESCRIPTION
.PP
Changes to doc/ListObj.3.
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
an attempt will be made to convert it to one.
.AP Tcl_Obj *objPtr in
For \fBTcl_ListObjAppendElement\fR,
points to the Tcl value that will be appended to \fIlistPtr\fR.
For \fBTcl_SetListObj\fR,
this points to the Tcl value that will be converted to a list value
containing the \fIobjc\fR elements of the array referenced by \fIobjv\fR.
.AP size_t | int *objcPtr in
Points to location where \fBTcl_ListObjGetElements\fR
stores the number of element values in \fIlistPtr\fR.
.AP Tcl_Obj ***objvPtr out
A location where \fBTcl_ListObjGetElements\fR stores a pointer to an array
of pointers to the element values of \fIlistPtr\fR.
.AP size_t objc in
The number of Tcl values that \fBTcl_NewListObj\fR
will insert into a new list value,
and \fBTcl_ListObjReplace\fR will insert into \fIlistPtr\fR.
For \fBTcl_SetListObj\fR,
the number of Tcl values to insert into \fIobjPtr\fR.
.AP "Tcl_Obj *const" objv[] in
An array of pointers to values.
\fBTcl_NewListObj\fR will insert these values into a new list value
and \fBTcl_ListObjReplace\fR will insert them into an existing \fIlistPtr\fR.
Each value will become a separate list element.
.AP size_t | int *lengthPtr out
Points to location where \fBTcl_ListObjLength\fR
stores the length of the list.
.AP size_t index in
Index of the list element that \fBTcl_ListObjIndex\fR
is to return.
The first element has index 0.
.AP Tcl_Obj **objPtrPtr out







|
















|







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
an attempt will be made to convert it to one.
.AP Tcl_Obj *objPtr in
For \fBTcl_ListObjAppendElement\fR,
points to the Tcl value that will be appended to \fIlistPtr\fR.
For \fBTcl_SetListObj\fR,
this points to the Tcl value that will be converted to a list value
containing the \fIobjc\fR elements of the array referenced by \fIobjv\fR.
.AP "size_t \&| int" *objcPtr in
Points to location where \fBTcl_ListObjGetElements\fR
stores the number of element values in \fIlistPtr\fR.
.AP Tcl_Obj ***objvPtr out
A location where \fBTcl_ListObjGetElements\fR stores a pointer to an array
of pointers to the element values of \fIlistPtr\fR.
.AP size_t objc in
The number of Tcl values that \fBTcl_NewListObj\fR
will insert into a new list value,
and \fBTcl_ListObjReplace\fR will insert into \fIlistPtr\fR.
For \fBTcl_SetListObj\fR,
the number of Tcl values to insert into \fIobjPtr\fR.
.AP "Tcl_Obj *const" objv[] in
An array of pointers to values.
\fBTcl_NewListObj\fR will insert these values into a new list value
and \fBTcl_ListObjReplace\fR will insert them into an existing \fIlistPtr\fR.
Each value will become a separate list element.
.AP "size_t \&| int" *lengthPtr out
Points to location where \fBTcl_ListObjLength\fR
stores the length of the list.
.AP size_t index in
Index of the list element that \fBTcl_ListObjIndex\fR
is to return.
The first element has index 0.
.AP Tcl_Obj **objPtrPtr out
Changes to doc/Method.3.
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
.AP Tcl_ObjectContext context in
A reference to a method-call context. Note that client code \fImust not\fR
retain a reference to a context.
.AP size_t objc in
The number of arguments to pass to the method implementation.
.AP "Tcl_Obj *const" *objv in
An array of arguments to pass to the method implementation.
.AP int skip in
The number of arguments passed to the method implementation that do not
represent "real" arguments.
.BE
.SH DESCRIPTION
.PP
A method is an operation carried out on an object that is associated with the
object. Every method must be attached to either an object or a class; methods







|







95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
.AP Tcl_ObjectContext context in
A reference to a method-call context. Note that client code \fImust not\fR
retain a reference to a context.
.AP size_t objc in
The number of arguments to pass to the method implementation.
.AP "Tcl_Obj *const" *objv in
An array of arguments to pass to the method implementation.
.AP size_t skip in
The number of arguments passed to the method implementation that do not
represent "real" arguments.
.BE
.SH DESCRIPTION
.PP
A method is an operation carried out on an object that is associated with the
object. Every method must be attached to either an object or a class; methods
Changes to doc/ParseArgs.3.
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
\fBTcl_ParseArgsObjv\fR(\fIinterp, argTable, objcPtr, objv, remObjv\fR)
.SH ARGUMENTS
.AS "const Tcl_ArgvInfo" ***remObjv in/out
.AP Tcl_Interp *interp out
Where to store error messages.
.AP "const Tcl_ArgvInfo" *argTable in
Pointer to array of option descriptors.
.AP size_t | int *objcPtr in/out
A pointer to variable holding number of arguments in \fIobjv\fR. Will be
modified to hold number of arguments left in the unprocessed argument list
stored in \fIremObjv\fR.
.AP "Tcl_Obj *const" *objv in
The array of arguments to be parsed.
.AP Tcl_Obj ***remObjv out
Pointer to a variable that will hold the array of unprocessed arguments.







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
\fBTcl_ParseArgsObjv\fR(\fIinterp, argTable, objcPtr, objv, remObjv\fR)
.SH ARGUMENTS
.AS "const Tcl_ArgvInfo" ***remObjv in/out
.AP Tcl_Interp *interp out
Where to store error messages.
.AP "const Tcl_ArgvInfo" *argTable in
Pointer to array of option descriptors.
.AP "size_t \&| int" *objcPtr in/out
A pointer to variable holding number of arguments in \fIobjv\fR. Will be
modified to hold number of arguments left in the unprocessed argument list
stored in \fIremObjv\fR.
.AP "Tcl_Obj *const" *objv in
The array of arguments to be parsed.
.AP Tcl_Obj ***remObjv out
Pointer to a variable that will hold the array of unprocessed arguments.
Changes to doc/SplitList.3.
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
.SH ARGUMENTS
.AS "const char *const" ***argvPtr out
.AP Tcl_Interp *interp out
Interpreter to use for error reporting.  If NULL, then no error message
is left.
.AP "const char" *list in
Pointer to a string with proper list structure.
.AP size_t | int *argcPtr out
Filled in with number of elements in \fIlist\fR.
.AP "const char" ***argvPtr out
\fI*argvPtr\fR will be filled in with the address of an array of
pointers to the strings that are the extracted elements of \fIlist\fR.
There will be \fI*argcPtr\fR valid entries in the array, followed by
a NULL entry.
.AP size_t argc in







|







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
.SH ARGUMENTS
.AS "const char *const" ***argvPtr out
.AP Tcl_Interp *interp out
Interpreter to use for error reporting.  If NULL, then no error message
is left.
.AP "const char" *list in
Pointer to a string with proper list structure.
.AP "size_t \&| int" *argcPtr out
Filled in with number of elements in \fIlist\fR.
.AP "const char" ***argvPtr out
\fI*argvPtr\fR will be filled in with the address of an array of
pointers to the strings that are the extracted elements of \fIlist\fR.
There will be \fI*argcPtr\fR valid entries in the array, followed by
a NULL entry.
.AP size_t argc in
Changes to doc/SplitPath.3.
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Tcl_PathType
\fBTcl_GetPathType\fR(\fIpath\fR)
.SH ARGUMENTS
.AS "const char *const" ***argvPtr in/out
.AP "const char" *path in
File path in a form appropriate for the current platform (see the
\fBfilename\fR manual entry for acceptable forms for path names).
.AP size_t | int *argcPtr out
Filled in with number of path elements in \fIpath\fR.
.AP "const char" ***argvPtr out
\fI*argvPtr\fR will be filled in with the address of an array of
pointers to the strings that are the extracted elements of \fIpath\fR.
There will be \fI*argcPtr\fR valid entries in the array, followed by
a NULL entry.
.AP size_t argc in







|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Tcl_PathType
\fBTcl_GetPathType\fR(\fIpath\fR)
.SH ARGUMENTS
.AS "const char *const" ***argvPtr in/out
.AP "const char" *path in
File path in a form appropriate for the current platform (see the
\fBfilename\fR manual entry for acceptable forms for path names).
.AP "size_t \&| int" *argcPtr out
Filled in with number of path elements in \fIpath\fR.
.AP "const char" ***argvPtr out
\fI*argvPtr\fR will be filled in with the address of an array of
pointers to the strings that are the extracted elements of \fIpath\fR.
There will be \fI*argcPtr\fR valid entries in the array, followed by
a NULL entry.
.AP size_t argc in
Changes to doc/StringObj.3.
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
The index of the last Unicode character in the Unicode range to be
returned as a new value. If \fBTCL_INDEX_NONE\fR, take all characters up to
the last one available.
.AP Tcl_Obj *objPtr in/out
Points to a value to manipulate.
.AP Tcl_Obj *appendObjPtr in
The value to append to \fIobjPtr\fR in \fBTcl_AppendObjToObj\fR.
.AP size_t | int *lengthPtr out
The location where \fBTcl_GetStringFromObj\fR will store the length
of a value's string representation. May be (int *)NULL when not used.
.AP "const char" *string in
Null-terminated string value to append to \fIobjPtr\fR.
.AP va_list argList in
An argument list which must have been initialized using
\fBva_start\fR, and cleared using \fBva_end\fR.







|







114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
The index of the last Unicode character in the Unicode range to be
returned as a new value. If \fBTCL_INDEX_NONE\fR, take all characters up to
the last one available.
.AP Tcl_Obj *objPtr in/out
Points to a value to manipulate.
.AP Tcl_Obj *appendObjPtr in
The value to append to \fIobjPtr\fR in \fBTcl_AppendObjToObj\fR.
.AP "size_t \&| int" *lengthPtr out
The location where \fBTcl_GetStringFromObj\fR will store the length
of a value's string representation. May be (int *)NULL when not used.
.AP "const char" *string in
Null-terminated string value to append to \fIobjPtr\fR.
.AP va_list argList in
An argument list which must have been initialized using
\fBva_start\fR, and cleared using \fBva_end\fR.
Changes to doc/TraceVar.3.
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
It should have arguments and result that match the type
\fBTcl_VarTraceProc\fR:
.PP
.CS
typedef char *\fBTcl_VarTraceProc\fR(
        void *\fIclientData\fR,
        Tcl_Interp *\fIinterp\fR,
        char *\fIname1\fR,
        char *\fIname2\fR,
        int \fIflags\fR);
.CE
.PP
The \fIclientData\fR and \fIinterp\fR parameters will
have the same values as those passed to \fBTcl_TraceVar\fR when the
trace was created.
\fIclientData\fR typically points to an application-specific







|
|







122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
It should have arguments and result that match the type
\fBTcl_VarTraceProc\fR:
.PP
.CS
typedef char *\fBTcl_VarTraceProc\fR(
        void *\fIclientData\fR,
        Tcl_Interp *\fIinterp\fR,
        const char *\fIname1\fR,
        const char *\fIname2\fR,
        int \fIflags\fR);
.CE
.PP
The \fIclientData\fR and \fIinterp\fR parameters will
have the same values as those passed to \fBTcl_TraceVar\fR when the
trace was created.
\fIclientData\fR typically points to an application-specific
Changes to doc/Translate.3.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
'\"
'\" Copyright (c) 1989-1993 The Regents of the University of California.
'\" Copyright (c) 1994-1998 Sun Microsystems, Inc.
'\"
'\" See the file "license.terms" for information on usage and redistribution
'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
'\"
.TH Tcl_TranslateFileName 3 8.1 Tcl "Tcl Library Procedures"
.so man.macros
.BS
.SH NAME
Tcl_TranslateFileName \- convert file name to native form and replace tilde with home directory
.SH SYNOPSIS
.nf
\fB#include <tcl.h>\fR
.sp
char *
\fBTcl_TranslateFileName\fR(\fIinterp\fR, \fIname\fR, \fIbufferPtr\fR)
.SH ARGUMENTS











|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
'\"
'\" Copyright (c) 1989-1993 The Regents of the University of California.
'\" Copyright (c) 1994-1998 Sun Microsystems, Inc.
'\"
'\" See the file "license.terms" for information on usage and redistribution
'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
'\"
.TH Tcl_TranslateFileName 3 8.1 Tcl "Tcl Library Procedures"
.so man.macros
.BS
.SH NAME
Tcl_TranslateFileName \- convert file name to native form
.SH SYNOPSIS
.nf
\fB#include <tcl.h>\fR
.sp
char *
\fBTcl_TranslateFileName\fR(\fIinterp\fR, \fIname\fR, \fIbufferPtr\fR)
.SH ARGUMENTS
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
anything stored here.
.BE
.SH DESCRIPTION
.PP
This utility procedure translates a file name to a platform-specific form
which, after being converted to the appropriate encoding, is suitable for
passing to the local operating system.  In particular, it converts
network names into native form and does tilde substitution.
.PP
However, with the advent of the newer \fBTcl_FSGetNormalizedPath\fR and
\fBTcl_FSGetNativePath\fR, there is no longer any need to use this
procedure.  In particular, \fBTcl_FSGetNativePath\fR performs all the
necessary translation and encoding conversion, is virtual-filesystem
aware, and caches the native result for faster repeated calls.
Finally \fBTcl_FSGetNativePath\fR does not require you to free anything
afterwards.
.PP
If
\fBTcl_TranslateFileName\fR has to do tilde substitution or translate
the name then it uses
the dynamic string at \fI*bufferPtr\fR to hold the new string it
generates.
After \fBTcl_TranslateFileName\fR returns a non-NULL result, the caller must
eventually invoke \fBTcl_DStringFree\fR to free any information
placed in \fI*bufferPtr\fR.  The caller need not know whether or
not \fBTcl_TranslateFileName\fR actually used the string;  \fBTcl_TranslateFileName\fR







|










|







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
anything stored here.
.BE
.SH DESCRIPTION
.PP
This utility procedure translates a file name to a platform-specific form
which, after being converted to the appropriate encoding, is suitable for
passing to the local operating system.  In particular, it converts
network names into native form.
.PP
However, with the advent of the newer \fBTcl_FSGetNormalizedPath\fR and
\fBTcl_FSGetNativePath\fR, there is no longer any need to use this
procedure.  In particular, \fBTcl_FSGetNativePath\fR performs all the
necessary translation and encoding conversion, is virtual-filesystem
aware, and caches the native result for faster repeated calls.
Finally \fBTcl_FSGetNativePath\fR does not require you to free anything
afterwards.
.PP
If
\fBTcl_TranslateFileName\fR has to translate
the name then it uses
the dynamic string at \fI*bufferPtr\fR to hold the new string it
generates.
After \fBTcl_TranslateFileName\fR returns a non-NULL result, the caller must
eventually invoke \fBTcl_DStringFree\fR to free any information
placed in \fI*bufferPtr\fR.  The caller need not know whether or
not \fBTcl_TranslateFileName\fR actually used the string;  \fBTcl_TranslateFileName\fR
64
65
66
67
68
69
70
71
\fBTcl_DStringFree\fR.
.PP
The caller is responsible for making sure that the interpreter's result
has its default empty value when \fBTcl_TranslateFileName\fR is invoked.
.SH "SEE ALSO"
filename(n)
.SH KEYWORDS
file name, home directory, tilde, translate, user







|
64
65
66
67
68
69
70
71
\fBTcl_DStringFree\fR.
.PP
The caller is responsible for making sure that the interpreter's result
has its default empty value when \fBTcl_TranslateFileName\fR is invoked.
.SH "SEE ALSO"
filename(n)
.SH KEYWORDS
file name, home directory, translate, user
Changes to doc/after.n.
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
This command is used to delay execution of the program or to execute
a command in background sometime in the future.  It has several forms,
depending on the first argument to the command:
.TP
\fBafter \fIms\fR
.
\fIMs\fR must be an integer giving a time in milliseconds.

The command sleeps for \fIms\fR milliseconds and then returns.
While the command is sleeping the application does not respond to
events.
.TP
\fBafter \fIms \fR?\fIscript script script ...\fR?
.
In this form the command returns immediately, but it arranges
for a Tcl command to be executed \fIms\fR milliseconds later as an
event handler.
The command will be executed exactly once, at the given time.
The delayed command is formed by concatenating all the \fIscript\fR
arguments in the same fashion as the \fBconcat\fR command.
The command will be executed at global level (outside the context
of any Tcl procedure).
If an error occurs while executing the delayed command then
the background error will be reported by the command
registered with \fBinterp bgerror\fR.
The \fBafter\fR command returns an identifier that can be used
to cancel the delayed command using \fBafter cancel\fR.



.TP
\fBafter cancel \fIid\fR
.
Cancels the execution of a delayed command that
was previously scheduled.
\fIId\fR indicates which command should be canceled;  it must have
been the return value from a previous \fBafter\fR command.







>



















>
>
>







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
This command is used to delay execution of the program or to execute
a command in background sometime in the future.  It has several forms,
depending on the first argument to the command:
.TP
\fBafter \fIms\fR
.
\fIMs\fR must be an integer giving a time in milliseconds.
A negative number is treated as 0.
The command sleeps for \fIms\fR milliseconds and then returns.
While the command is sleeping the application does not respond to
events.
.TP
\fBafter \fIms \fR?\fIscript script script ...\fR?
.
In this form the command returns immediately, but it arranges
for a Tcl command to be executed \fIms\fR milliseconds later as an
event handler.
The command will be executed exactly once, at the given time.
The delayed command is formed by concatenating all the \fIscript\fR
arguments in the same fashion as the \fBconcat\fR command.
The command will be executed at global level (outside the context
of any Tcl procedure).
If an error occurs while executing the delayed command then
the background error will be reported by the command
registered with \fBinterp bgerror\fR.
The \fBafter\fR command returns an identifier that can be used
to cancel the delayed command using \fBafter cancel\fR.
A \fIms\fR value of 0 (or negative) queues the event immediately with
priority over other event types (if not installed withn an event proc,
which will wait for next round of events).
.TP
\fBafter cancel \fIid\fR
.
Cancels the execution of a delayed command that
was previously scheduled.
\fIId\fR indicates which command should be canceled;  it must have
been the return value from a previous \fBafter\fR command.
Changes to doc/exec.n.
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
The standard output from the last command in the pipeline will
go to the application's standard output if it has not been
redirected, and error output from all of
the commands in the pipeline will go to the application's
standard error file unless redirected.
.PP
The first word in each command is taken as the command name;
tilde-substitution is performed on it, and if the result contains
no slashes then the directories
in the PATH environment variable are searched for
an executable by the given name.
If the name contains a slash then it must refer to an executable
reachable from the current directory.
No
.QW glob







|







194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
The standard output from the last command in the pipeline will
go to the application's standard output if it has not been
redirected, and error output from all of
the commands in the pipeline will go to the application's
standard error file unless redirected.
.PP
The first word in each command is taken as the command name;
if the result contains
no slashes then the directories
in the PATH environment variable are searched for
an executable by the given name.
If the name contains a slash then it must refer to an executable
reachable from the current directory.
No
.QW glob
Changes to doc/file.n.
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
.SH NAME
file \- Manipulate file names and attributes
.SH SYNOPSIS
\fBfile \fIoption\fR \fIname\fR ?\fIarg arg ...\fR?
.BE
.SH DESCRIPTION
.PP
This command provides several operations on a file's name or attributes.
\fIName\fR is the name of a file; if it starts with a tilde, then tilde
substitution is done before executing the command (see the manual entry for
\fBfilename\fR for details).  \fIOption\fR indicates what to do with the
file name.  Any unique abbreviation for \fIoption\fR is acceptable.  The
valid options are:
.TP
\fBfile atime \fIname\fR ?\fItime\fR?
.
Returns a decimal string giving the time at which file \fIname\fR was last
accessed.  If \fItime\fR is specified, it is an access time to set
for the file.  The time is measured in the standard POSIX fashion as
seconds from a fixed starting time (often January 1, 1970).  If the file







|
|
<
<
|
|







12
13
14
15
16
17
18
19
20


21
22
23
24
25
26
27
28
29
.SH NAME
file \- Manipulate file names and attributes
.SH SYNOPSIS
\fBfile \fIoption\fR \fIname\fR ?\fIarg arg ...\fR?
.BE
.SH DESCRIPTION
.PP
This command provides several operations on a file's name or attributes.  The
\fIname\fR argument is the name of a file in most cases. The \fIoption\fR


argument indicates what to do with the file name.  Any unique abbreviation for
\fIoption\fR is acceptable.  The valid options are:
.TP
\fBfile atime \fIname\fR ?\fItime\fR?
.
Returns a decimal string giving the time at which file \fIname\fR was last
accessed.  If \fItime\fR is specified, it is an access time to set
for the file.  The time is measured in the standard POSIX fashion as
seconds from a fixed starting time (often January 1, 1970).  If the file
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
.RS
.PP
.CS
\fBfile dirname\fR c:/
.CE
.PP
returns \fBc:/\fR.
.PP
Note that tilde substitution will only be
performed if it is necessary to complete the command. For example,
.PP
.CS
\fBfile dirname\fR ~/src/foo.c
.CE
.PP
returns \fB~/src\fR, whereas
.PP
.CS
\fBfile dirname\fR ~
.CE
.PP
returns \fB/home\fR (or something similar).
.RE
.TP
\fBfile executable \fIname\fR
.
Returns \fB1\fR if file \fIname\fR is executable by the current user,
\fB0\fR otherwise. On Windows, which does not have an executable attribute,
the command treats all directories and any files with extensions
\fBexe\fR, \fBcom\fR, \fBcmd\fR or \fBbat\fR as executable.
.TP
\fBfile exists \fIname\fR
.
Returns \fB1\fR if file \fIname\fR exists and the current user has
search privileges for the directories leading to it, \fB0\fR otherwise.
.TP
\fBfile extension \fIname\fR
.
Returns all of the characters in \fIname\fR after and including the last
dot in the last element of \fIname\fR.  If there is no dot in the last
element of \fIname\fR then returns the empty string.


















.TP
\fBfile isdirectory \fIname\fR
.
Returns \fB1\fR if file \fIname\fR is a directory, \fB0\fR otherwise.
.TP
\fBfile isfile \fIname\fR
.







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



















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







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
.RS
.PP
.CS
\fBfile dirname\fR c:/
.CE
.PP
returns \fBc:/\fR.















.RE
.TP
\fBfile executable \fIname\fR
.
Returns \fB1\fR if file \fIname\fR is executable by the current user,
\fB0\fR otherwise. On Windows, which does not have an executable attribute,
the command treats all directories and any files with extensions
\fBexe\fR, \fBcom\fR, \fBcmd\fR or \fBbat\fR as executable.
.TP
\fBfile exists \fIname\fR
.
Returns \fB1\fR if file \fIname\fR exists and the current user has
search privileges for the directories leading to it, \fB0\fR otherwise.
.TP
\fBfile extension \fIname\fR
.
Returns all of the characters in \fIname\fR after and including the last
dot in the last element of \fIname\fR.  If there is no dot in the last
element of \fIname\fR then returns the empty string.
.TP
\fBfile home ?\fIusername\fR?
.VS "8.7, TIP 602"
If no argument is specified, the command returns the home directory
of the current user. This is generally the value of the \fB$HOME\fR
environment variable except that on Windows platforms backslashes
in the path are replaced by forward slashes. An error is raised if
the \fB$HOME\fR environment variable is not set.
.RS
.PP
If \fIusername\fR is specified, the command returns the home directory
configured in the system for the specified user. Note this may be
different than the value of the \fB$HOME\fR environment variable
even when \fIusername\fR corresponds to the current user. An error is
raised if the \fIusername\fR does not correspond to a user account
on the system.
.RE
.VE "8.7, TIP 602"
.TP
\fBfile isdirectory \fIname\fR
.
Returns \fB1\fR if file \fIname\fR is a directory, \fB0\fR otherwise.
.TP
\fBfile isfile \fIname\fR
.
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
.TP
\fBfile split \fIname\fR
.
Returns a list whose elements are the path components in \fIname\fR.  The
first element of the list will have the same path type as \fIname\fR.
All other elements will be relative.  Path separators will be discarded
unless they are needed to ensure that an element is unambiguously relative.
For example, under Unix
.RS
.PP
.CS
\fBfile split\fR /foo/~bar/baz
.CE
.PP
returns
.QW \fB/\0\0foo\0\0./~bar\0\0baz\fR
to ensure that later commands
that use the third component do not attempt to perform tilde
substitution.
.RE
.TP
\fBfile stat \fIname varName\fR
.
Invokes the \fBstat\fR kernel call on \fIname\fR, and uses the variable
given by \fIvarName\fR to hold information returned from the kernel call.
\fIVarName\fR is treated as an array variable, and the following elements
of that variable are set: \fBatime\fR, \fBctime\fR, \fBdev\fR, \fBgid\fR,







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







376
377
378
379
380
381
382













383
384
385
386
387
388
389
.TP
\fBfile split \fIname\fR
.
Returns a list whose elements are the path components in \fIname\fR.  The
first element of the list will have the same path type as \fIname\fR.
All other elements will be relative.  Path separators will be discarded
unless they are needed to ensure that an element is unambiguously relative.













.TP
\fBfile stat \fIname varName\fR
.
Invokes the \fBstat\fR kernel call on \fIname\fR, and uses the variable
given by \fIvarName\fR to hold information returned from the kernel call.
\fIVarName\fR is treated as an array variable, and the following elements
of that variable are set: \fBatime\fR, \fBctime\fR, \fBdev\fR, \fBgid\fR,
478
479
480
481
482
483
484
















485
486
487
488
489
490
491
default instead.
.RS
.PP
Note that temporary files are \fIonly\fR ever created on the native
filesystem. As such, they can be relied upon to be used with operating-system
native APIs and external programs that require a filename.
.RE
















.TP
\fBfile type \fIname\fR
.
Returns a string giving the type of file \fIname\fR, which will be one of
\fBfile\fR, \fBdirectory\fR, \fBcharacterSpecial\fR, \fBblockSpecial\fR,
\fBfifo\fR, \fBlink\fR, or \fBsocket\fR.
.TP







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







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
default instead.
.RS
.PP
Note that temporary files are \fIonly\fR ever created on the native
filesystem. As such, they can be relied upon to be used with operating-system
native APIs and external programs that require a filename.
.RE
.TP
\fBfile tildeexpand \fIname\fR
.VS "8.7, TIP 602"
Returns the result of performing tilde substitution on \fIname\fR. If the name
begins with a tilde, then the file name will be interpreted as if the first
element is replaced with the location of the home directory for the given user.
If the tilde is followed immediately by a path separator, the \fB$HOME\fR
environment variable is substituted.  Otherwise the characters between the
tilde and the next separator are taken as a user name, which is used to
retrieve the user's home directory for substitution.  An error is raised if the
\fB$HOME\fR environment variable or user does not exist.
.RS
.PP
If the file name does not begin with a tilde, it is returned unmodified.
.RE
.VE "8.7, TIP 602"
.TP
\fBfile type \fIname\fR
.
Returns a string giving the type of file \fIname\fR, which will be one of
\fBfile\fR, \fBdirectory\fR, \fBcharacterSpecial\fR, \fBblockSpecial\fR,
\fBfifo\fR, \fBlink\fR, or \fBsocket\fR.
.TP
Changes to doc/filename.n.
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
volume.
.TP 15
\fB\&\e\efoo\fR
Volume-relative path to a file \fBfoo\fR in the root directory of the current
volume.  This is not a valid UNC path, so the assumption is that the
extra backslashes are superfluous.
.RE
.SH "TILDE SUBSTITUTION"
.PP
In addition to the file name rules described above, Tcl also supports
\fIcsh\fR-style tilde substitution.  If a file name starts with a tilde,
then the file name will be interpreted as if the first element is
replaced with the location of the home directory for the given user.  If
the tilde is followed immediately by a separator, then the \fB$HOME\fR
environment variable is substituted.  Otherwise the characters between
the tilde and the next separator are taken as a user name, which is used
to retrieve the user's home directory for substitution.  This works on
Unix, MacOS X and Windows (except very old releases).
.PP
Old Windows platforms do not support tilde substitution when a user name
follows the tilde.  On these platforms, attempts to use a tilde followed
by a user name will generate an error that the user does not exist when
Tcl attempts to interpret that part of the path or otherwise access the
file.  The behaviour of these paths when not trying to interpret them is
the same as on Unix.  File names that have a tilde without a user name
will be correctly substituted using the \fB$HOME\fR environment
variable, just like for Unix.
.SH "PORTABILITY ISSUES"
.PP
Not all file systems are case sensitive, so scripts should avoid code
that depends on the case of characters in a file name.  In addition,
the character sets allowed on different devices may differ, so scripts
should choose file names that do not contain special characters like:
\fB<>:?"/\e|\fR.







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







114
115
116
117
118
119
120




















121
122
123
124
125
126
127
volume.
.TP 15
\fB\&\e\efoo\fR
Volume-relative path to a file \fBfoo\fR in the root directory of the current
volume.  This is not a valid UNC path, so the assumption is that the
extra backslashes are superfluous.
.RE




















.SH "PORTABILITY ISSUES"
.PP
Not all file systems are case sensitive, so scripts should avoid code
that depends on the case of characters in a file name.  In addition,
the character sets allowed on different devices may differ, so scripts
should choose file names that do not contain special characters like:
\fB<>:?"/\e|\fR.
Changes to doc/glob.n.
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
.PP
The \fBglob\fR command differs from csh globbing in two ways.
First, it does not sort its result list (use the \fBlsort\fR
command if you want the list sorted).
Second, \fBglob\fR only returns the names of files that actually
exist; in csh no check for existence is made unless a pattern
contains a ?, *, or [] construct.
.LP
When the \fBglob\fR command returns relative paths whose filenames
start with a tilde
.QW ~
(for example through \fBglob *\fR or \fBglob \-tails\fR, the returned
list will not quote the tilde with
.QW ./ .
This means care must be taken if those names are later to
be used with \fBfile join\fR, to avoid them being interpreted as
absolute paths pointing to a given user's home directory.
.SH "WINDOWS PORTABILITY ISSUES"
.PP
For Windows UNC names, the servername and sharename components of the path
may not contain ?, *, or [] constructs. On Windows NT, if \fIpattern\fR is
of the form
.QW \fB~\fIusername\fB@\fIdomain\fR ,
it refers to the home







<
<
<
<
<
<
<
<
<
<







181
182
183
184
185
186
187










188
189
190
191
192
193
194
.PP
The \fBglob\fR command differs from csh globbing in two ways.
First, it does not sort its result list (use the \fBlsort\fR
command if you want the list sorted).
Second, \fBglob\fR only returns the names of files that actually
exist; in csh no check for existence is made unless a pattern
contains a ?, *, or [] construct.










.SH "WINDOWS PORTABILITY ISSUES"
.PP
For Windows UNC names, the servername and sharename components of the path
may not contain ?, *, or [] constructs. On Windows NT, if \fIpattern\fR is
of the form
.QW \fB~\fIusername\fB@\fIdomain\fR ,
it refers to the home
Changes to doc/http.n.
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
.TH "http" n 2.10 http "Tcl Bundled Packages"
.so man.macros
.BS
'\" Note:  do not modify the .SH NAME line immediately below!
.SH NAME
http \- Client-side implementation of the HTTP/1.1 protocol
.SH SYNOPSIS
\fBpackage require http\fI ?\fB2.10\fR?
.\" See Also -useragent option documentation in body!
.sp
\fB::http::config\fR ?\fI\-option value\fR ...?
.sp
\fB::http::geturl \fIurl\fR ?\fI\-option value\fR ...?
.sp
\fB::http::formatQuery\fR \fIkey value\fR ?\fIkey value\fR ...?
.sp
\fB::http::quoteString\fR \fIvalue\fR
.sp
\fB::http::reset\fR \fItoken\fR ?\fIwhy\fR?
.sp
\fB::http::wait \fItoken\fR
.sp
\fB::http::status \fItoken\fR
.sp
\fB::http::size \fItoken\fR
.sp
\fB::http::code \fItoken\fR
.sp
\fB::http::ncode \fItoken\fR
.sp
\fB::http::meta \fItoken\fR
.sp
\fB::http::data \fItoken\fR
.sp
\fB::http::error \fItoken\fR
.sp
\fB::http::cleanup \fItoken\fR
.sp




















\fB::http::register \fIproto port command\fR
.sp
\fB::http::registerError \fIport\fR ?\fImessage\fR?
.sp
\fB::http::unregister \fIproto\fR










.SH "EXPORTED COMMANDS"
.PP
Namespace \fBhttp\fR exports the commands \fBconfig\fR, \fBformatQuery\fR,

\fBgeturl\fR, \fBquoteString\fR, \fBregister\fR, \fBregisterError\fR,




\fBreset\fR, \fBunregister\fR, and \fBwait\fR.
.PP
It does not export the commands \fBcleanup\fR, \fBcode\fR, \fBdata\fR,
\fBerror\fR, \fBmeta\fR, \fBncode\fR, \fBsize\fR, or \fBstatus\fR.

.BE
.SH DESCRIPTION
.PP
The \fBhttp\fR package provides the client side of the HTTP/1.1
protocol, as defined in RFC 7230 to RFC 7235, which supersede RFC 2616.

The package implements the GET, POST, and HEAD operations
of HTTP/1.1.  It allows configuration of a proxy host to get through
firewalls.  The package is compatible with the \fBSafesock\fR security
policy, so it can be used by untrusted applets to do URL fetching from
a restricted set of hosts. This package can be extended to support
additional HTTP transport protocols, such as HTTPS, by providing
a custom \fBsocket\fR command, via \fB::http::register\fR.
.PP
The \fB::http::geturl\fR procedure does a HTTP transaction.
Its \fIoptions \fR determine whether a GET, POST, or HEAD transaction
is performed.
The return value of \fB::http::geturl\fR is a token for the transaction.
The value is also the name of an array in the ::http namespace
that contains state information about the transaction.  The elements
of this array are described in the \fBSTATE ARRAY\fR section.
.PP
If the \fB\-command\fR option is specified, then
the HTTP operation is done in the background.
\fB::http::geturl\fR returns immediately after generating the
HTTP request and the callback is invoked
when the transaction completes.  For this to work, the Tcl event loop
must be active.  In Tk applications this is always true.  For pure-Tcl
applications, the caller can use \fB::http::wait\fR after calling
\fB::http::geturl\fR to start the event loop.
.PP
\fBNote:\fR The event queue is even used without the \fB\-command\fR option.
As a side effect, arbitrary commands may be processed while \fBhttp::geturl\fR
is running.









.SH COMMANDS
.TP
\fB::http::config\fR ?\fIoptions\fR?
.
The \fB::http::config\fR command is used to set and query the name of the
proxy server and port, and the User-Agent name used in the HTTP
requests.  If no options are specified, then the current configuration







|


















|

<
<
<
<
<
<
|



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





>
>
>
>
>
>
>
>
>
>



>
|
>
>
>
>



|
>




|
>












|
|
<




|








>
>
>
>
>
>
>
>
>







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
.TH "http" n 2.10 http "Tcl Bundled Packages"
.so man.macros
.BS
'\" Note:  do not modify the .SH NAME line immediately below!
.SH NAME
http \- Client-side implementation of the HTTP/1.1 protocol
.SH SYNOPSIS
\fBpackage require http\fR ?\fB2.10\fR?
.\" See Also -useragent option documentation in body!
.sp
\fB::http::config\fR ?\fI\-option value\fR ...?
.sp
\fB::http::geturl \fIurl\fR ?\fI\-option value\fR ...?
.sp
\fB::http::formatQuery\fR \fIkey value\fR ?\fIkey value\fR ...?
.sp
\fB::http::quoteString\fR \fIvalue\fR
.sp
\fB::http::reset\fR \fItoken\fR ?\fIwhy\fR?
.sp
\fB::http::wait \fItoken\fR
.sp
\fB::http::status \fItoken\fR
.sp
\fB::http::size \fItoken\fR
.sp
\fB::http::error \fItoken\fR
.sp






\fB::http::postError \fItoken\fR
.sp
\fB::http::cleanup \fItoken\fR
.sp
\fB::http::requestLine\fR \fItoken\fR
.sp
\fB::http::requestHeaders\fR \fItoken\fR ?\fIheaderName\fR?
.sp
\fB::http::requestHeaderValue\fR \fItoken\fR \fIheaderName\fR
.sp
\fB::http::responseLine\fR \fItoken\fR
.sp
\fB::http::responseCode\fR \fItoken\fR
.sp
\fB::http::reasonPhrase\fR \fIcode\fR
.sp
\fB::http::responseHeaders\fR \fItoken\fR ?\fIheaderName\fR?
.sp
\fB::http::responseHeaderValue\fR \fItoken\fR \fIheaderName\fR
.sp
\fB::http::responseInfo\fR \fItoken\fR
.sp
\fB::http::responseBody\fR \fItoken\fR
.sp
\fB::http::register \fIproto port command\fR
.sp
\fB::http::registerError \fIport\fR ?\fImessage\fR?
.sp
\fB::http::unregister \fIproto\fR
.sp
\fB::http::code \fItoken\fR
.sp
\fB::http::data \fItoken\fR
.sp
\fB::http::meta \fItoken\fR ?\fIheaderName\fR?
.sp
\fB::http::metaValue\fR \fItoken\fR \fIheaderName\fR
.sp
\fB::http::ncode \fItoken\fR
.SH "EXPORTED COMMANDS"
.PP
Namespace \fBhttp\fR exports the commands \fBconfig\fR, \fBformatQuery\fR,
\fBgeturl\fR, \fBpostError\fR, \fBquoteString\fR, \fBreasonPhrase\fR,
\fBregister\fR,
\fBregisterError\fR, \fBrequestHeaders\fR, \fBrequestHeaderValue\fR,
\fBrequestLine\fR, \fBresponseBody\fR, \fBresponseCode\fR,
\fBresponseHeaders\fR, \fBresponseHeaderValue\fR, \fBresponseInfo\fR,
\fBresponseLine\fR,
\fBreset\fR, \fBunregister\fR, and \fBwait\fR.
.PP
It does not export the commands \fBcleanup\fR, \fBcode\fR, \fBdata\fR,
\fBerror\fR, \fBmeta\fR, \fBmetaValue\fR, \fBncode\fR,
\fBsize\fR, or \fBstatus\fR.
.BE
.SH DESCRIPTION
.PP
The \fBhttp\fR package provides the client side of the HTTP/1.1
protocol, as defined in RFC 9110 to 9112, which supersede RFC 7230
to RFC 7235, which in turn supersede RFC 2616.
The package implements the GET, POST, and HEAD operations
of HTTP/1.1.  It allows configuration of a proxy host to get through
firewalls.  The package is compatible with the \fBSafesock\fR security
policy, so it can be used by untrusted applets to do URL fetching from
a restricted set of hosts. This package can be extended to support
additional HTTP transport protocols, such as HTTPS, by providing
a custom \fBsocket\fR command, via \fB::http::register\fR.
.PP
The \fB::http::geturl\fR procedure does a HTTP transaction.
Its \fIoptions \fR determine whether a GET, POST, or HEAD transaction
is performed.
The return value of \fB::http::geturl\fR is a token for the transaction.
The token can be supplied as an argument to other commands, to manage the
transaction and examine its results.

.PP
If the \fB\-command\fR option is specified, then
the HTTP operation is done in the background.
\fB::http::geturl\fR returns immediately after generating the
HTTP request and the \fB\-command\fR callback is invoked
when the transaction completes.  For this to work, the Tcl event loop
must be active.  In Tk applications this is always true.  For pure-Tcl
applications, the caller can use \fB::http::wait\fR after calling
\fB::http::geturl\fR to start the event loop.
.PP
\fBNote:\fR The event queue is even used without the \fB\-command\fR option.
As a side effect, arbitrary commands may be processed while \fBhttp::geturl\fR
is running.
.PP
When the HTTP server has replied to the request, call the command
\fB::http::responseInfo\fR, which
returns a \fBdict\fR of metadata that is essential for identifying a
successful transaction and making use of the response.  See
section \fBMETADATA\fR for details of the information returned.
The response itself is returned by command \fB::http::responseBody\fR,
unless it has been redirected to a file by the \fI\-channel\fR option
of \fB::http::geturl\fR.
.SH COMMANDS
.TP
\fB::http::config\fR ?\fIoptions\fR?
.
The \fB::http::config\fR command is used to set and query the name of the
proxy server and port, and the User-Agent name used in the HTTP
requests.  If no options are specified, then the current configuration
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
request
will be automatically retried; if boolean \fBfalse\fR it will not, and the
application
that uses \fBhttp::geturl\fR is expected to seek user confirmation before
retrying the POST.  The value \fBtrue\fR should be used only under certain
conditions. See the \fBPERSISTENT SOCKETS\fR section for details. The
default is 0.


















.TP
\fB\-urlencoding\fR \fIencoding\fR
.
The \fIencoding\fR used for creating the x-url-encoded URLs with
\fB::http::formatQuery\fR and \fB::http::quoteString\fR.
The default is \fButf-8\fR, as specified by RFC 2718.
.TP
\fB\-useragent\fR \fIstring\fR
.
The value of the User-Agent header in the HTTP request.  In an unsafe
interpreter, the default value depends upon the operating system, and
the version numbers of \fBhttp\fR and \fBTcl\fR, and is (for example)
.QW "\fBMozilla/5.0 (Windows; U; Windows NT 10.0) http/2.9.0 Tcl/8.6.9\fR" .
A safe interpreter cannot determine its operating system, and so the default
in a safe interpreter is to use a Windows 10 value with the current version
numbers of \fBhttp\fR and \fBTcl\fR.
.TP
\fB\-zip\fR \fIboolean\fR
.
If the value is boolean \fBtrue\fR, then by default requests will send a header
.QW "\fBAccept-Encoding: gzip,deflate,compress\fR" .
If the value is boolean \fBfalse\fR, then by default this header will not be

sent.  In either case the default can be overridden for an individual request by
supplying a custom \fBAccept-Encoding\fR header in the \fB\-headers\fR option
of \fBhttp::geturl\fR. The default is 1.
.RE
.TP
\fB::http::geturl\fR \fIurl\fR ?\fIoptions\fR?
.
The \fB::http::geturl\fR command is the main procedure in the package.
The \fB\-query\fR option causes a POST operation and
the \fB\-validate\fR option causes a HEAD operation;
otherwise, a GET operation is performed.  The \fB::http::geturl\fR command
returns a \fItoken\fR value that can be used to get
information about the transaction.  See the \fBSTATE ARRAY\fR and
\fBERRORS\fR section for
details.  The \fB::http::geturl\fR command blocks until the operation
completes, unless the \fB\-command\fR option specifies a callback
that is invoked when the HTTP transaction completes.
\fB::http::geturl\fR takes several options:
.RS
.TP
\fB\-binary\fR \fIboolean\fR
.
Specifies whether to force interpreting the URL data as binary.  Normally
this is auto-detected (anything not beginning with a \fBtext\fR content
type or whose content encoding is \fBgzip\fR or \fBcompress\fR is
considered binary data).
.TP
\fB\-blocksize\fR \fIsize\fR
.
The block size used when reading the URL.
At most \fIsize\fR bytes are read at once.  After each block, a call to the
\fB\-progress\fR callback is made (if that option is specified).
.TP
\fB\-channel\fR \fIname\fR
.
Copy the URL contents to channel \fIname\fR instead of saving it in
\fBstate(body)\fR.
.TP
\fB\-command\fR \fIcallback\fR
.
Invoke \fIcallback\fR after the HTTP transaction completes.
This option causes \fB::http::geturl\fR to return immediately.


The \fIcallback\fR gets an additional argument that is the \fItoken\fR returned
from \fB::http::geturl\fR. This token is the name of an array that is
described in the \fBSTATE ARRAY\fR section.  Here is a template for the
callback:
.RS
.PP
.CS
proc httpCallback {token} {
    upvar #0 $token state
    # Access state as a Tcl array


}
.CE
.PP
The \fB::http::geturl\fR command runs the \fB\-command\fR callback inside
a \fBcatch\fR command.  Therefore an error in the callback command does
not call the \fBbgerror\fR handler.  See the \fBERRORS\fR section for
details.
.RE
.TP














\fB\-handler\fR \fIcallback\fR
.




Invoke \fIcallback\fR whenever HTTP data is available; if present, nothing

else will be done with the HTTP data.  This procedure gets two additional
arguments: the socket for the HTTP data and the \fItoken\fR returned from
\fB::http::geturl\fR.  The token is the name of a global array that is
described in the \fBSTATE ARRAY\fR section.  The procedure is expected
to return the number of bytes read from the socket.  Here is a
template for the callback:
.RS
.PP
.CS
proc httpHandlerCallback {socket token} {
    upvar #0 $token state
    # Access socket, and state as a Tcl array
    # For example...
    ...
    set data [read $socket 1000]
    set nbytes [string length $data]
    ...
    return $nbytes
}
.CE
.PP
The \fBhttp::geturl\fR code for the \fB\-handler\fR option is not compatible
with either compression or chunked transfer-encoding.  If \fB\-handler\fR is
specified, then to work around these issues \fBhttp::geturl\fR will reduce the
HTTP protocol to 1.0, and override the \fB\-zip\fR option (i.e. it will not

send the header "\fBAccept-Encoding: gzip,deflate,compress\fR").
.PP
If options \fB\-handler\fR and \fB\-channel\fR are used together, the handler
is responsible for copying the data from the HTTP socket to the specified
channel.  The name of the channel is available to the handler as element
\fB\-channel\fR of the token array.
.PP
The \fB::http::geturl\fR command runs the \fB\-handler\fR callback inside







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




















|
|
>
|

|





|


|
|











|











|



<
|
>
>
|







|
|
>
>









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


>
>
>
>
|
>
|
|








|
|












|
>
|







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
request
will be automatically retried; if boolean \fBfalse\fR it will not, and the
application
that uses \fBhttp::geturl\fR is expected to seek user confirmation before
retrying the POST.  The value \fBtrue\fR should be used only under certain
conditions. See the \fBPERSISTENT SOCKETS\fR section for details. The
default is 0.
.TP
\fB\-threadlevel\fR \fIlevel\fR
.
Specifies whether and how to use the \fBThread\fR package.  Possible values
of \fIlevel\fR are 0, 1 or 2.
.RS
.PP
.DS
0 - (the default) do not use Thread
1 - use Thread if it is available, do not use it if it is unavailable
2 - use Thread if it is available, raise an error if it is unavailable
.DE
The Tcl \fBsocket -async\fR command can block in adverse cases (e.g. a slow
DNS lookup).  Using the Thread package works around this problem, for both
HTTP and HTTPS transactions.  Values of \fIlevel\fR other than 0 are
available only to the main interpreter in each thread.  See
section \fBTHREADS\fR for more information.
.RE
.TP
\fB\-urlencoding\fR \fIencoding\fR
.
The \fIencoding\fR used for creating the x-url-encoded URLs with
\fB::http::formatQuery\fR and \fB::http::quoteString\fR.
The default is \fButf-8\fR, as specified by RFC 2718.
.TP
\fB\-useragent\fR \fIstring\fR
.
The value of the User-Agent header in the HTTP request.  In an unsafe
interpreter, the default value depends upon the operating system, and
the version numbers of \fBhttp\fR and \fBTcl\fR, and is (for example)
.QW "\fBMozilla/5.0 (Windows; U; Windows NT 10.0) http/2.9.0 Tcl/8.6.9\fR" .
A safe interpreter cannot determine its operating system, and so the default
in a safe interpreter is to use a Windows 10 value with the current version
numbers of \fBhttp\fR and \fBTcl\fR.
.TP
\fB\-zip\fR \fIboolean\fR
.
If the value is boolean \fBtrue\fR, then by default requests will send a header
.QW "\fBAccept-Encoding: gzip,deflate\fR" .
If the value is boolean \fBfalse\fR, then by default requests will send a header
.QW "\fBAccept-Encoding: identity\fR" .
In either case the default can be overridden for an individual request by
supplying a custom \fBAccept-Encoding\fR header in the \fB\-headers\fR option
of \fBhttp::geturl\fR. The default value is 1.
.RE
.TP
\fB::http::geturl\fR \fIurl\fR ?\fIoptions\fR?
.
The \fB::http::geturl\fR command is the main procedure in the package.
The \fB\-query\fR or \fB\-querychannel\fR option causes a POST operation and
the \fB\-validate\fR option causes a HEAD operation;
otherwise, a GET operation is performed.  The \fB::http::geturl\fR command
returns a \fItoken\fR value that can be passed as an argument to other commands
to get information about the transaction.  See the \fBMETADATA\fR and
\fBERRORS\fR section for
details.  The \fB::http::geturl\fR command blocks until the operation
completes, unless the \fB\-command\fR option specifies a callback
that is invoked when the HTTP transaction completes.
\fB::http::geturl\fR takes several options:
.RS
.TP
\fB\-binary\fR \fIboolean\fR
.
Specifies whether to force interpreting the URL data as binary.  Normally
this is auto-detected (anything not beginning with a \fBtext\fR content
type or whose content encoding is \fBgzip\fR or \fBdeflate\fR is
considered binary data).
.TP
\fB\-blocksize\fR \fIsize\fR
.
The block size used when reading the URL.
At most \fIsize\fR bytes are read at once.  After each block, a call to the
\fB\-progress\fR callback is made (if that option is specified).
.TP
\fB\-channel\fR \fIname\fR
.
Copy the URL contents to channel \fIname\fR instead of saving it in
a Tcl variable for retrieval by \fB::http::responseBody\fR.
.TP
\fB\-command\fR \fIcallback\fR
.

The presence of this option causes \fB::http::geturl\fR to return immediately.
After the HTTP transaction completes, the value of \fIcallback\fR is expanded,
an additional argument is added, and the resulting command is evaluated.
The additional argument is the \fItoken\fR returned
from \fB::http::geturl\fR. This token is the name of an array that is
described in the \fBSTATE ARRAY\fR section.  Here is a template for the
callback:
.RS
.PP
.CS
proc httpCallback {token} {
    upvar 0 $token state
    # Access state as a Tcl array defined in this proc
    ...
    return
}
.CE
.PP
The \fB::http::geturl\fR command runs the \fB\-command\fR callback inside
a \fBcatch\fR command.  Therefore an error in the callback command does
not call the \fBbgerror\fR handler.  See the \fBERRORS\fR section for
details.
.RE
.TP
\fB\-guesstype\fR \fIboolean\fR
.
Attempt to guess the \fBContent-Type\fR and character set when a misconfigured
server provides no information.  The default value is \fIfalse\fR (do
nothing).  If boolean \fItrue\fR then, if the server does not send a
\fBContent-Type\fR header, or if it sends the value "application/octet-stream",
\fBhttp::geturl\fR will attempt to guess appropriate values.  This is not
intended to become a general-purpose tool, and currently it is limited to
detecting XML documents that begin with an XML declaration.  In this case
the \fBContent-Type\fR is changed to "application/xml", the binary flag
state(binary) is changed to 0, and the character set is changed to
the one specified by the "encoding" tag of the XML line, or to utf-8 if no
encoding is specified.  Not used if a \fI\-channel\fR is specified.
.TP
\fB\-handler\fR \fIcallback\fR
.
If this option is absent, \fBhttp::geturl\fR processes incoming data itself,
either appending it to the state(body) variable or writing it to the -channel.
But if the \fB\-handler\fR option is present, \fBhttp::geturl\fR does not do
this processing and instead calls \fIcallback\fR.
Whenever HTTP data is available, the value of \fIcallback\fR is expanded, an
additional two arguments are added, and the resulting command is evaluated.
The two additional
arguments are: the socket for the HTTP data and the \fItoken\fR returned from
\fB::http::geturl\fR.  The token is the name of a global array that is
described in the \fBSTATE ARRAY\fR section.  The procedure is expected
to return the number of bytes read from the socket.  Here is a
template for the callback:
.RS
.PP
.CS
proc httpHandlerCallback {socket token} {
    upvar 0 $token state
    # Access socket, and state as a Tcl array defined in this proc
    # For example...
    ...
    set data [read $socket 1000]
    set nbytes [string length $data]
    ...
    return $nbytes
}
.CE
.PP
The \fBhttp::geturl\fR code for the \fB\-handler\fR option is not compatible
with either compression or chunked transfer-encoding.  If \fB\-handler\fR is
specified, then to work around these issues \fBhttp::geturl\fR will reduce the
HTTP protocol to 1.0, and override the \fB\-zip\fR option (i.e. it will
send the header \fBAccept-Encoding: identity\fR instead
of \fBAccept-Encoding: gzip,deflate\fR).
.PP
If options \fB\-handler\fR and \fB\-channel\fR are used together, the handler
is responsible for copying the data from the HTTP socket to the specified
channel.  The name of the channel is available to the handler as element
\fB\-channel\fR of the token array.
.PP
The \fB::http::geturl\fR command runs the \fB\-handler\fR callback inside
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
that selection and enables choices like PUT and DELETE for WebDAV support.
.RS
.PP
It is the caller's responsibility to ensure that the headers and request body
(if any) conform to the requirements of the request method.  For example, if
using \fB\-method\fR \fIPOST\fR to send a POST with an empty request body, the
caller must also supply the option


.QW "\-headers {Content-Length 0}" .

.RE
.TP
\fB\-myaddr\fR \fIaddress\fR
.
Pass an specific local address to the underlying \fBsocket\fR call in case
multiple interfaces are available.
.TP
\fB\-progress\fR \fIcallback\fR
.

The \fIcallback\fR is made after each transfer of data from the URL.


The callback gets three additional arguments: the \fItoken\fR from
\fB::http::geturl\fR, the expected total size of the contents from the
\fBContent-Length\fR meta-data, and the current number of bytes


transferred so far.  The expected total size may be unknown, in which
case zero is passed to the callback.  Here is a template for the
progress callback:
.RS
.PP
.CS
proc httpProgress {token total current} {
    upvar #0 $token state



}
.CE
.RE
.TP
\fB\-protocol\fR \fIversion\fR
.
Select the HTTP protocol version to use. This should be 1.0 or 1.1 (the







>
>
|
>









>
|
>
>
|

|
>
>
|






|
>
>
>







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
that selection and enables choices like PUT and DELETE for WebDAV support.
.RS
.PP
It is the caller's responsibility to ensure that the headers and request body
(if any) conform to the requirements of the request method.  For example, if
using \fB\-method\fR \fIPOST\fR to send a POST with an empty request body, the
caller must also supply the option
.PP
.CS
\-headers {Content-Length 0}
.CE
.RE
.TP
\fB\-myaddr\fR \fIaddress\fR
.
Pass an specific local address to the underlying \fBsocket\fR call in case
multiple interfaces are available.
.TP
\fB\-progress\fR \fIcallback\fR
.
If the \fB\-progress\fR option is present,
then the \fIcallback\fR is made after each transfer of data from the URL.
The value of \fIcallback\fR is expanded, an additional three arguments are
added, and the resulting command is evaluated.
The three additional arguments are: the \fItoken\fR returned from
\fB::http::geturl\fR, the expected total size of the contents from the
\fBContent-Length\fR response header, and the current number of bytes
transferred so far.  The token is the name of a global array that is
described in the \fBSTATE ARRAY\fR section.  The expected total size may
be unknown, in which
case zero is passed to the callback.  Here is a template for the
progress callback:
.RS
.PP
.CS
proc httpProgress {token total current} {
    upvar 0 $token state
    # Access state as a Tcl array defined in this proc
    ...
    return
}
.CE
.RE
.TP
\fB\-protocol\fR \fIversion\fR
.
Select the HTTP protocol version to use. This should be 1.0 or 1.1 (the
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
.TP
\fB\-querychannel\fR \fIchannelID\fR
.
This flag causes \fB::http::geturl\fR to do a POST request that passes the
data contained in \fIchannelID\fR to the server. The data contained in
\fIchannelID\fR must be an x-url-encoding
formatted query unless the \fB\-type\fR option below is used.
If a Content-Length header is not specified via the \fB\-headers\fR options,
\fB::http::geturl\fR attempts to determine the size of the post data
in order to create that header.  If it is
unable to determine the size, it returns an error.
.TP
\fB\-queryprogress\fR \fIcallback\fR
.

The \fIcallback\fR is made after each transfer of data to the URL

(i.e. POST) and acts exactly like the \fB\-progress\fR option (the
callback format is the same).
.TP
\fB\-strict\fR \fIboolean\fR
.


Whether to enforce RFC 3986 URL validation on the request.  Default is 1.
.TP
\fB\-timeout\fR \fImilliseconds\fR
.
If \fImilliseconds\fR is non-zero, then \fB::http::geturl\fR sets up a timeout
to occur after the specified number of milliseconds.
A timeout results in a call to \fB::http::reset\fR and to
the \fB\-command\fR callback, if specified.
The return value of \fB::http::status\fR is \fBtimeout\fR

after a timeout has occurred.
.TP
\fB\-type\fR \fImime-type\fR
.
Use \fImime-type\fR as the \fBContent-Type\fR value, instead of the
default value (\fBapplication/x-www-form-urlencoded\fR) during a
POST operation.
.TP
\fB\-validate\fR \fIboolean\fR
.
If \fIboolean\fR is non-zero, then \fB::http::geturl\fR does an HTTP HEAD
request.  This request returns meta information about the URL, but the

contents are not returned.  The meta information is available in the
\fBstate(meta) \fR variable after the transaction.  See the
\fBSTATE ARRAY\fR section for details.
.RE
.TP
\fB::http::formatQuery\fR \fIkey value\fR ?\fIkey value\fR ...?
.
This procedure does x-url-encoding of query data.  It takes an even
number of arguments that are the keys and values of the query.  It
encodes the keys and values, and generates one string that has the







|
|





>
|
>
|
|



>
>
|







|
>











|
>
|
|
|







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
.TP
\fB\-querychannel\fR \fIchannelID\fR
.
This flag causes \fB::http::geturl\fR to do a POST request that passes the
data contained in \fIchannelID\fR to the server. The data contained in
\fIchannelID\fR must be an x-url-encoding
formatted query unless the \fB\-type\fR option below is used.
If a \fBContent-Length\fR header is not specified via the \fB\-headers\fR
options, \fB::http::geturl\fR attempts to determine the size of the post data
in order to create that header.  If it is
unable to determine the size, it returns an error.
.TP
\fB\-queryprogress\fR \fIcallback\fR
.
If the \fB\-queryprogress\fR option is present,
then the \fIcallback\fR is made after each transfer of data to the URL
in a POST request (i.e. a call to \fB::http::geturl\fR with
option \fB\-query\fR or \fB\-querychannel\fR) and acts exactly like
the \fB\-progress\fR option (the callback format is the same).
.TP
\fB\-strict\fR \fIboolean\fR
.
If true then the command will test that the URL complies with RFC 3986, i.e.
that it has no characters that should be "x-url-encoded" (e.g. a space should
be encoded to "%20").  Default value is 1.
.TP
\fB\-timeout\fR \fImilliseconds\fR
.
If \fImilliseconds\fR is non-zero, then \fB::http::geturl\fR sets up a timeout
to occur after the specified number of milliseconds.
A timeout results in a call to \fB::http::reset\fR and to
the \fB\-command\fR callback, if specified.
The return value of \fB::http::status\fR (and the value of the \fIstatus\fR key
in the dictionary returned by \fB::http::responseInfo\fR) is \fBtimeout\fR
after a timeout has occurred.
.TP
\fB\-type\fR \fImime-type\fR
.
Use \fImime-type\fR as the \fBContent-Type\fR value, instead of the
default value (\fBapplication/x-www-form-urlencoded\fR) during a
POST operation.
.TP
\fB\-validate\fR \fIboolean\fR
.
If \fIboolean\fR is non-zero, then \fB::http::geturl\fR does an HTTP HEAD
request.  This server returns the same status line and response headers as it
would for a HTTP GET request, but omits the response entity
(the URL "contents").  The response headers are available after the
transaction using command \fB::http::responseHeaders\fR or, for selected
information, \fB::http::responseInfo\fR.
.RE
.TP
\fB::http::formatQuery\fR \fIkey value\fR ?\fIkey value\fR ...?
.
This procedure does x-url-encoding of query data.  It takes an even
number of arguments that are the keys and values of the query.  It
encodes the keys and values, and generates one string that has the
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
.
This command resets the HTTP transaction identified by \fItoken\fR, if any.
This sets the \fBstate(status)\fR value to \fIwhy\fR, which defaults to
\fBreset\fR, and then calls the registered \fB\-command\fR callback.
.TP
\fB::http::wait\fR \fItoken\fR
.
This is a convenience procedure that blocks and waits for the
transaction to complete.  This only works in trusted code because it
uses \fBvwait\fR.  Also, it is not useful for the case where
\fB::http::geturl\fR is called \fIwithout\fR the \fB\-command\fR option
because in this case the \fB::http::geturl\fR call does not return
until the HTTP transaction is complete, and thus there is nothing to
wait for.
.TP
\fB::http::data\fR \fItoken\fR
.
This is a convenience procedure that returns the \fBbody\fR element
(i.e., the URL data) of the state array.



.TP
\fB::http::error\fR \fItoken\fR
.
This is a convenience procedure that returns the \fBerror\fR element
of the state array.
.TP
\fB::http::status\fR \fItoken\fR
.
This is a convenience procedure that returns the \fBstatus\fR element of
the state array.
.TP
\fB::http::code\fR \fItoken\fR
.
This is a convenience procedure that returns the \fBhttp\fR element of the
state array.

.TP
\fB::http::ncode\fR \fItoken\fR
.
This is a convenience procedure that returns just the numeric return
code (200, 404, etc.) from the \fBhttp\fR element of the state array.


.TP
\fB::http::size\fR \fItoken\fR
.
This is a convenience procedure that returns the \fBcurrentsize\fR
element of the state array, which represents the number of bytes
received from the URL in the \fB::http::geturl\fR call.
.TP

\fB::http::meta\fR \fItoken\fR
.



This is a convenience procedure that returns the \fBmeta\fR
element of the state array which contains the HTTP response
headers. See below for an explanation of this element.
.TP
\fB::http::cleanup\fR \fItoken\fR
.
This procedure cleans up the state associated with the connection
identified by \fItoken\fR.  After this call, the procedures
like \fB::http::data\fR cannot be used to get information
about the operation.  It is \fIstrongly\fR recommended that you call
this function after you are done with a given HTTP request.  Not doing
so will result in memory not being freed, and if your app calls
\fB::http::geturl\fR enough times, the memory leak could cause a
performance hit...or worse.






























































































































































.TP
\fB::http::register\fR \fIproto port command\fR
.
This procedure allows one to provide custom HTTP transport types
such as HTTPS, by registering a prefix, the default port, and the
command to execute to create the Tcl \fBchannel\fR. E.g.:
.RS







|







|

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

|

|
<
>

|

|
<
>
>

|

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





|





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







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
.
This command resets the HTTP transaction identified by \fItoken\fR, if any.
This sets the \fBstate(status)\fR value to \fIwhy\fR, which defaults to
\fBreset\fR, and then calls the registered \fB\-command\fR callback.
.TP
\fB::http::wait\fR \fItoken\fR
.
This command blocks and waits for the
transaction to complete.  This only works in trusted code because it
uses \fBvwait\fR.  Also, it is not useful for the case where
\fB::http::geturl\fR is called \fIwithout\fR the \fB\-command\fR option
because in this case the \fB::http::geturl\fR call does not return
until the HTTP transaction is complete, and thus there is nothing to
wait for.
.TP
\fB::http::status\fR \fItoken\fR
.
This command returns a description of the status of the HTTP transaction.
The return value is the empty string until the HTTP transaction is
completed; after completion it has one of the values ok, eof, error,
timeout, and reset.  The meaning of these values is described in the
section \fBERRORS\fR (below).
.PP

.RS

The name "status" is not related to the terms "status line" and

"status code" that are defined for a HTTP response.
.RE


.TP
\fB::http::size\fR \fItoken\fR
.
This command returns the number of bytes

received so far from the URL in the \fB::http::geturl\fR call.
.TP
\fB::http::error\fR \fItoken\fR
.
This command returns the error information if the HTTP transaction failed,

or the empty string if there was no error.  The information is a Tcl list of
the error message, stack trace, and error code.
.TP
\fB::http::postError\fR \fItoken\fR
.


A POST request is a call to \fB::http::geturl\fR with either

the \fB\-query\fR or \fB\-querychannel\fR option.
The \fB::http::postError\fR command returns the error information generated

when a HTTP POST request sends its request-body to the server; or the empty
string if there was no error.  The information is a Tcl list of the error
message, stack trace, and error code.  When this type of error occurs,
the \fB::http::geturl\fR command continues the transaction and attempts to
receive a response from the server.

.TP
\fB::http::cleanup\fR \fItoken\fR
.
This procedure cleans up the state associated with the connection
identified by \fItoken\fR.  After this call, the procedures
like \fB::http::responseBody\fR cannot be used to get information
about the operation.  It is \fIstrongly\fR recommended that you call
this function after you are done with a given HTTP request.  Not doing
so will result in memory not being freed, and if your app calls
\fB::http::geturl\fR enough times, the memory leak could cause a
performance hit...or worse.
.TP
\fB::http::requestLine\fR \fItoken\fR
.
This command returns the "request line" sent to the server.
The "request line" is the first line of a HTTP client request, and has three
elements separated by spaces: the HTTP method, the URL relative to the server,
and the HTTP version. Examples:
.PP
.DS
.RS
GET / HTTP/1.1
GET /introduction.html?subject=plumbing HTTP/1.1
POST /forms/order.html HTTP/1.1
.RE
.DE
.TP
\fB::http::requestHeaders\fR \fItoken\fR ?\fIheaderName\fR?
.
This command returns the HTTP request header names and values, in the
order that they were sent to the server, as a Tcl list of the form
?name value ...?  Header names are case-insensitive and are converted to lower
case.  The return value is not a \fBdict\fR because some header names may occur
more than once.  If one argument is supplied, all request headers
are returned.  If two arguments are supplied, the
second provides the value of a header name.  Only headers with the requested
name (converted to lower case) are returned.  If no such headers are found,
an empty list is returned.
.TP
\fB::http::requestHeaderValue\fR \fItoken\fR \fIheaderName\fR
.
This command returns the value of the HTTP request header named
\fIheaderName\fR.  Header names are case-insensitive and are converted to
lower case.  If no such header exists, the return value is the empty string.
If there are multiple headers named \fIheaderName\fR, the result is obtained
by joining the individual values with the string ", " (comma and space),
preserving their order.
.TP
\fB::http::responseLine\fR \fItoken\fR
.
This command returns the first line of the server response: the
HTTP "status line".  The "status line" has three
elements separated by spaces: the HTTP version, a three-digit numerical
"status code", and a "reason phrase".  Only the reason phrase may contain
spaces.  Examples:
.PP
.DS
.RS
HTTP/1.1 200 OK
HTTP/1.0 404 Not Found
.RE
.DE
.RS
The "status code" is a three-digit number in the range 100 to 599.
A value of 200 is the normal return from a GET request, and its matching
"reason phrase" is "OK".  Codes beginning with 4 or 5 indicate errors.
Codes beginning with 3 are redirection errors.  In this case the
\fBLocation\fR response header specifies a new URL that contains the
requested information.
.PP
The "reason phrase" is a textual description of the "status code": it may
vary from server to server,
and can be changed without affecting the HTTP protocol.  The recommended
values (RFC 7231 and IANA assignments) for each code are provided by the
command \fB::http::reasonPhrase\fR.
.RE
.TP
\fB::http::responseCode\fR \fItoken\fR
.
This command returns the "status code" (200, 404, etc.) of the server
"status line".  If a three-digit code cannot be found, the full status
line is returned.  See command \fB::http::responseLine\fR for more information
on the "status line".
.TP
\fB::http::reasonPhrase\fR \fIcode\fR
.
This command returns the IANA recommended "reason phrase" for a particular
"status code" returned by a HTTP server.  The argument \fIcode\fR is a valid
status code, and therefore is an integer in the range 100 to 599 inclusive.
For numbers in this range with no assigned meaning, the command returns the
value "Unassigned".  Several status codes are used only in response to the
methods defined by HTTP extensions such as WebDAV, and not in response to a
HEAD, GET, or POST request method.
.PP
.RS
The "reason phrase" returned by a HTTP server may differ from the recommended
value, without affecting the HTTP protocol.  The value returned by
\fB::http::geturl\fR can be obtained by calling either command
\fB::http::responseLine\fR (which returns the full status line) or command
\fB::http::responseInfo\fR (which returns a dictionary, with
the "reason phrase" stored in key \fIreasonPhrase\fR).
.PP
A registry of valid status codes is maintained at
https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
.RE
.TP
\fB::http::responseHeaders\fR \fItoken\fR ?\fIheaderName\fR?
.
The response from a HTTP server includes metadata headers that describe the
response body and the transaction itself.
This command returns the HTTP response header names and values, in the
order that they were received from the server, as a Tcl list of the form
?name value ...?  Header names are case-insensitive and are converted to lower
case.  The return value is not a \fBdict\fR because some header names may occur
more than once, notably \fBSet-Cookie\fR.  If the second argument is not
supplied, all response headers are returned.  If the second argument is
supplied, it provides the value of a header name.  Only headers with the
requested name (converted to lower case) are returned.  If no such headers
are found, an empty list is returned.  See section \fBMETADATA\fR for more
information.
.TP
\fB::http::responseHeaderValue\fR \fItoken\fR \fIheaderName\fR
.
This command returns the value of the HTTP response header named
\fIheaderName\fR.  Header names are case-insensitive and are converted to
lower case.  If no such header exists, the return value is the empty string.
If there are multiple headers named \fIheaderName\fR, the result is obtained
by joining the individual values with the string ", " (comma and space),
preserving their order.  Multiple headers with the same name may be processed
in this manner, except \fBSet-Cookie\fR which does not conform to the
comma-separated-list syntax and cannot be combined into a single value.
Each \fBSet-Cookie\fR header must be treated individually, e.g. by processing
the return value of \fB::http::responseHeaders\fR \fItoken\fR \fBSet-Cookie\fR.
.TP
\fB::http::responseInfo\fR \fItoken\fR
.
This command returns a \fBdict\fR of selected response metadata that are
essential for identifying a successful transaction and making use of the
response, along with other metadata that are informational.  The keys of
the \fBdict\fR are \fIstage\fR, \fIstatus\fR, \fIresponseCode\fR,
\fIreasonPhrase\fR, \fIcontentType\fR, \fIbinary\fR, \fIredirection\fR,
\fIupgrade\fR, \fIerror\fR, \fIpostError\fR, \fImethod\fR, \fIcharset\fR,
\fIcompression\fR, \fIhttpRequest\fR, \fIhttpResponse\fR, \fIurl\fR,
\fIconnectionRequest\fR, \fIconnectionResponse\fR, \fIconnectionActual\fR,
\fItransferEncoding\fR, \fItotalPost\fR, \fIcurrentPost\fR, \fItotalSize\fR,
and \fIcurrentSize\fR.  The meaning of these keys is described in the
section \fBMETADATA\fR below.
.RS
.PP
It is always worth checking the value of \fIbinary\fR after a HTTP transaction,
to determine whether a misconfigured server has caused http to interpret a
text resource as a binary, or vice versa.
.PP
After a POST transaction, check the value of \fIpostError\fR to verify that
the request body was uploaded without error.
.RE
.TP
\fB::http::responseBody\fR \fItoken\fR
.
This command returns the entity sent by the HTTP server (unless
\fI-channel\fR was used, in which case the entity was delivered to the
channel, and the command returns the empty string).
.RS
.PP
Other terms for
"entity", with varying precision, include "representation of resource",
"resource", "response body after decoding", "payload",
"message body after decoding", "content(s)", and "file".
.RE
.TP
\fB::http::register\fR \fIproto port command\fR
.
This procedure allows one to provide custom HTTP transport types
such as HTTPS, by registering a prefix, the default port, and the
command to execute to create the Tcl \fBchannel\fR. E.g.:
.RS
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
\fB::http::unregister\fR \fIproto\fR
.
This procedure unregisters a protocol handler that was previously
registered via \fB::http::register\fR, returning a two-item list of
the default port and handler command that was previously installed
(via \fB::http::register\fR) if there was such a handler, and an error if
there was no such handler.
















.SH ERRORS
The \fB::http::geturl\fR procedure will raise errors in the following cases:
invalid command line options,
an invalid URL,
a URL on a non-existent host,
or a URL at a bad port on an existing host.
These errors mean that it
cannot even start the network transaction.
It will also raise an error if it gets an I/O error while
writing out the HTTP request header.
For synchronous \fB::http::geturl\fR calls (where \fB\-command\fR is
not specified), it will raise an error if it gets an I/O error while




reading the HTTP reply headers or data.  Because \fB::http::geturl\fR
does not return a token in these cases, it does all the required
cleanup and there is no issue of your app having to call
\fB::http::cleanup\fR.
.PP
For asynchronous \fB::http::geturl\fR calls, all of the above error
situations apply, except that if there is any error while reading the
HTTP reply headers or data, no exception is thrown.  This is because
after writing the HTTP headers, \fB::http::geturl\fR returns, and the
rest of the HTTP transaction occurs in the background.  The command
callback can check if any error occurred during the read by calling
\fB::http::status\fR to check the status and if its \fIerror\fR,
calling \fB::http::error\fR to get the error message.
.PP
Alternatively, if the main program flow reaches a point where it needs
to know the result of the asynchronous HTTP request, it can call
\fB::http::wait\fR and then check status and error, just as the
callback does.
.PP
The \fB::http::geturl\fR command runs the \fB\-command\fR, \fB\-handler\fR,
and \fB\-proxyfilter\fR callbacks inside a \fBcatch\fR command.  Therefore
an error in the callback command does not call the \fBbgerror\fR handler.
When debugging one of these
callbacks, it may be convenient to report errors by using a
\fBcatch\fR command within the callback command itself, e.g. to write
an error message to stdout.
.PP
In any case, you must still call
\fB::http::cleanup\fR to delete the state array when you are done.
.PP
There are other possible results of the HTTP transaction
determined by examining the status from \fB::http::status\fR.


These are described below.
.TP
\fBok\fR
.
If the HTTP transaction completes entirely, then status will be \fBok\fR.
However, you should still check the \fB::http::code\fR value to get
the HTTP status.  The \fB::http::ncode\fR procedure provides just
the numeric error (e.g., 200, 404 or 500) while the \fB::http::code\fR
procedure returns a value like
.QW "HTTP 404 File not found" .
.TP
\fBeof\fR
.
If the server closes the socket without replying, then no error
is raised, but the status of the transaction will be \fBeof\fR.
.TP
\fBerror\fR
.
The error message will also be stored in the \fBerror\fR status
array element, accessible via \fB::http::error\fR.

.TP
\fBtimeout\fR
.
A timeout occurred before the transaction could complete
.TP
\fBreset\fR
.


































































































































































































































user-reset


































































.PP
Another error possibility is that \fB::http::geturl\fR is unable to
write all the post query data to the server before the server
responds and closes the socket.
The error message is saved in the \fBposterror\fR status array
element and then  \fB::http::geturl\fR attempts to complete the
transaction.
If it can read the server's response
it will end up with an \fBok\fR status, otherwise it will have
an \fBeof\fR status.
.SH "STATE ARRAY"
The \fB::http::geturl\fR procedure returns a \fItoken\fR that can be used to

get to the state of the HTTP transaction in the form of a Tcl array.






Use this construct to create an easy-to-use array variable:
.PP
.CS
upvar #0 $token state
.CE
.PP
Once the data associated with the URL is no longer needed, the state
array should be unset to free up storage.
The \fB::http::cleanup\fR procedure is provided for that purpose.

The following elements of
the array are supported:



.RS
.TP
\fBbinary\fR
.
This is boolean \fBtrue\fR if (after decoding any compression specified
by the
.QW "Content-Encoding"
response header) the HTTP response is binary.  It is boolean \fBfalse\fR
if the HTTP response is text.
.TP
\fBbody\fR
.
The contents of the URL.  This will be empty if the \fB\-channel\fR
option has been specified.  This value is returned by the \fB::http::data\fR
command.
.TP
\fBcharset\fR
.
The value of the charset attribute from the \fBContent-Type\fR meta-data
value.  If none was specified, this defaults to the RFC standard
\fBiso8859-1\fR, or the value of \fB$::http::defaultCharset\fR.  Incoming
text data will be automatically converted from this charset to utf-8.
.TP
\fBcoding\fR
.
A copy of the \fBContent-Encoding\fR meta-data value.




.TP
\fBcurrentsize\fR
.
The current number of bytes fetched from the URL.
This value is returned by the \fB::http::size\fR command.
.TP
\fBerror\fR
.
If defined, this is the error string seen when the HTTP transaction
was aborted.
.TP
\fBhttp\fR
.
The HTTP status reply from the server.  This value
is returned by the \fB::http::code\fR command.  The format of this value is:
.RS
.PP
.CS
\fIHTTP/1.1 code string\fR
.CE
.PP
The \fIcode\fR is a three-digit number defined in the HTTP standard.
A code of 200 is OK.  Codes beginning with 4 or 5 indicate errors.
Codes beginning with 3 are redirection errors.  In this case the
\fBLocation\fR meta-data specifies a new URL that contains the
requested information.
.RE
.TP
\fBmeta\fR
.
The HTTP protocol returns meta-data that describes the URL contents.
The \fBmeta\fR element of the state array is a list of the keys and
values of the meta-data.  This is in a format useful for initializing
an array that just contains the meta-data:
.RS
.PP
.CS
array set meta $state(meta)
.CE
.PP
Some of the meta-data keys are listed below, but the HTTP standard defines
more, and servers are free to add their own.

.TP
\fBContent-Type\fR
.
The type of the URL contents.  Examples include \fBtext/html\fR,
\fBimage/gif,\fR \fBapplication/postscript\fR and

\fBapplication/x-tcl\fR.
.TP
\fBContent-Length\fR
.
The advertised size of the contents.  The actual size obtained by
\fB::http::geturl\fR is available as \fBstate(currentsize)\fR.
.TP
\fBLocation\fR
.
An alternate URL that contains the requested data.
.RE
.TP
\fBposterror\fR
.


The error, if any, that occurred while writing







the post query data to the server.


.TP




















\fBstatus\fR
.
See description in the chapter \fBERRORS\fR above for a


list and description of \fBstatus\fR.
During the transaction this value is the empty string.


.TP
\fBtotalsize\fR
.
A copy of the \fBContent-Length\fR meta-data value.




.TP
\fBtype\fR
.
A copy of the \fBContent-Type\fR meta-data value.




.TP
\fBurl\fR
.
The requested URL.
.RE
.SH "PERSISTENT CONNECTIONS"
.PP
.SS "BASICS"
.PP
See RFC 7230 Sec 6, which supersedes RFC 2616 Sec 8.1.
.PP







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



|
<
<


<
<

|
>
>
>
>











|
<




|













|
>
>





|
|
|










|
|
>



|



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

|
|
<
<
<
|
<
<
<

|
>
|
>
>
>
>
>
>
|


|





>
|
<
>
>
>




<
<
<
|
<



<
<
|



<
<
|
<



|
>
>
>
>



<
|



|
<



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

|

<
<
<
<
<
<
<
<
<
<
<
<
>

|

<
<
>
|

|

<
<
<
|
<
<
<



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

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

<
>
>
|
<
>
>



|
>
>
>
>



|
>
>
>
>



|







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
\fB::http::unregister\fR \fIproto\fR
.
This procedure unregisters a protocol handler that was previously
registered via \fB::http::register\fR, returning a two-item list of
the default port and handler command that was previously installed
(via \fB::http::register\fR) if there was such a handler, and an error if
there was no such handler.
.TP
\fB::http::code\fR \fItoken\fR
.
An alternative name for the command \fB::http::responseLine\fR
.TP
\fB::http::data\fR \fItoken\fR
.
An alternative name for the command \fB::http::responseBody\fR.
.TP
\fB::http::meta\fR \fItoken\fR ?\fIheaderName\fR?
.
An alternative name for the command \fB::http::responseHeaders\fR
.TP
\fB::http::ncode\fR \fItoken\fR
.
An alternative name for the command \fB::http::responseCode\fR
.SH ERRORS
The \fB::http::geturl\fR procedure will raise errors in the following cases:
invalid command line options,
or an invalid URL.


These errors mean that it
cannot even start the network transaction.


For synchronous \fB::http::geturl\fR calls (where \fB\-command\fR is
not specified), it will raise an error if
the URL is on a non-existent host
or at a bad port on an existing host.
It will also raise an error for any I/O errors while
writing out the HTTP request line and headers, or
reading the HTTP reply headers or data.  Because \fB::http::geturl\fR
does not return a token in these cases, it does all the required
cleanup and there is no issue of your app having to call
\fB::http::cleanup\fR.
.PP
For asynchronous \fB::http::geturl\fR calls, all of the above error
situations apply, except that if there is any error while reading the
HTTP reply headers or data, no exception is thrown.  This is because
after writing the HTTP headers, \fB::http::geturl\fR returns, and the
rest of the HTTP transaction occurs in the background.  The command
callback can check if any error occurred during the read by calling
\fB::http::responseInfo\fR to check the transaction status.

.PP
Alternatively, if the main program flow reaches a point where it needs
to know the result of the asynchronous HTTP request, it can call
\fB::http::wait\fR and then check status and error, just as the
synchronous call does.
.PP
The \fB::http::geturl\fR command runs the \fB\-command\fR, \fB\-handler\fR,
and \fB\-proxyfilter\fR callbacks inside a \fBcatch\fR command.  Therefore
an error in the callback command does not call the \fBbgerror\fR handler.
When debugging one of these
callbacks, it may be convenient to report errors by using a
\fBcatch\fR command within the callback command itself, e.g. to write
an error message to stdout.
.PP
In any case, you must still call
\fB::http::cleanup\fR to delete the state array when you are done.
.PP
There are other possible results of the HTTP transaction
determined by examining the status from \fB::http::status\fR (or the value
of the \fIstatus\fR key in the dictionary returned
by \fB::http::responseInfo\fR).
These are described below.
.TP
\fBok\fR
.
If the HTTP transaction completes entirely, then status will be \fBok\fR.
However, you should still check the \fB::http::responseLine\fR value to get
the HTTP status.  The \fB::http::responseCode\fR procedure provides just
the numeric error (e.g., 200, 404 or 500) while the \fB::http::responseLine\fR
procedure returns a value like
.QW "HTTP 404 File not found" .
.TP
\fBeof\fR
.
If the server closes the socket without replying, then no error
is raised, but the status of the transaction will be \fBeof\fR.
.TP
\fBerror\fR
.
The error message, stack trace, and error code are accessible
via \fB::http::error\fR.  The error message is also provided by the value of
the \fIerror\fR key in the dictionary returned by \fB::http::responseInfo\fR.
.TP
\fBtimeout\fR
.
A timeout occurred before the transaction could complete.
.TP
\fBreset\fR
.
The user has called \fB::http::reset\fR.
.TP
\fB""\fR
.
(empty string) The transaction has not yet finished.
.PP
Another error possibility is that \fB::http::geturl\fR failed to
write the whole of the POST request body (\fB-query\fR or \fB-querychannel\fR
data) to the server.  \fB::http::geturl\fR stores the error message for later
retrieval by the \fB::http::postError\fR or \fB::http::responseInfo\fR
commands, and then attempts to complete the transaction.
If it can read the server's response the status will be \fBok\fR, but it is
important to call \fB::http::postError\fR or \fB::http::responseInfo\fR after
every POST to check that the data was sent in full.
If the server has closed the connection the status will be \fBeof\fR.
.SH "METADATA"
.PP
.SS "MOST USEFUL METADATA"
When a HTTP server responds to a request, it supplies not only the entity
requested, but also metadata.  This is provided by the first line (the
"status line") of the response, and by a number of HTTP headers.  Further
metadata relates to how \fB::http::geturl\fR has processed the response
from the server.
.PP
The most important metadata can be accessed with the command
\fB::http::responseInfo\fR.
This command returns a \fBdict\fR of metadata that are essential for
identifying a successful transaction and making use of the response,
along with other metadata that are informational.  The keys of
the \fBdict\fR are:
.PP
.RS
.RS
\fB===== Essential Values =====\fR
.RE
.RE
.TP
\fBstage\fR
.
This value, set by \fB::http::geturl\fR, describes the stage that the
transaction has reached. Values, in order of the transaction lifecycle,
are: "created", "connecting", "header", "body", and "complete".  The
other \fBdict\fR keys will not be available until the value of \fBstage\fR
is "body" or "complete".  The key \fBcurrentSize\fR has its final value only
when \fBstage\fR is "complete".
.TP
\fBstatus\fR
.
This value, set by \fB::http::geturl\fR, is "ok" for a successful transaction;
"eof", "error", "timeout", or "reset" for an unsuccessful transaction; or ""
if the transaction is still in progress.  The value is the same as that
returned by command \fB::http::status\fR. The meaning of these values is
described in the section \fBERRORS\fR (above).
.TP
\fBresponseCode\fR
.
The "HTTP status code" sent by the server in the first line (the "status line")
of the response.  If the value cannot be extracted from the status line, the
full status line is returned.
.TP
\fBreasonPhrase\fR
.
The "reason phrase" sent by the server as a description of the HTTP status code.
If the value cannot be extracted from the status line, the full status
line is returned.
.TP
\fBcontentType\fR
.
The value of the \fBContent-Type\fR response header or, if the header was not
supplied, the default value "application/octet-stream".
.TP
\fBbinary\fR
.
This boolean value, set by \fB::http::geturl\fR, describes how the command
has interpreted the entity returned by the server (after decoding any
compression specified by the \fBContent-Encoding\fR response header).
This decoded entity is accessible as the return value of the
command \fB::http::responseBody\fR.
.PP
.RS
The value is \fBtrue\fR if http has interpreted the decoded entity as binary.
The value returned by \fB::http::responseBody\fR is a Tcl binary string.
This is a suitable format for image data, zip files, etc.
\fB::http::geturl\fR chooses this value if the user has requested a binary
interpretation by passing the option \fI\-binary\fR to the command, or if the
server has supplied a binary content type in a \fBContent-Type\fR response
header, or if the server has not supplied any \fBContent-Type\fR header.
.PP
The value is \fBfalse\fR in other cases, and this means that http has
interpreted the decoded entity as text. The text has been converted, from the
character set notified by the server, into Tcl's internal Unicode format;
the value returned by \fB::http::responseBody\fR is an ordinary Tcl string.
.PP
It is always worth checking the value of "binary" after a HTTP transaction,
to determine whether a misconfigured server has caused http to interpret a
text resource as a binary, or vice versa.
.RE
.TP
\fBredirection\fR
.
The URL that is the redirection target. The value is that of the \fBLocation\fR
response header.  This header is sent when a response has status code
3XX (redirection).
.TP
\fBupgrade\fR
.
If not empty, the value indicates the protocol(s) to which the server will
switch after completion of this transaction, while continuing to use the
same connection.  When the server intends to switch protocols, it will also
send the value "101" as the status code (the \fBresponseCode\fR key), and the
word "upgrade" as an element of the \fBConnection\fR response header (the
\fBconnectionResponse\fR key), and it will not send a response body.
See the section \fBPROTOCOL UPGRADES\fR for more information.
.TP
\fBerror\fR
.
The error message, if there is one.  Further information, including a stack
trace and error code, are available from command \fB::http::error\fR.
.TP
\fBpostError\fR
.
The error message (if any) generated when a HTTP POST request sends its
request-body to the server.  Further information, including a stack trace
and error code, are available from command \fB::http::postError\fR.  A POST
transaction may appear complete, according to the
keys \fBstage\fR, \fBstatus\fR, and \fBresponseCode\fR, but it is important
to check this \fBpostError\fR key in case an error occurred when uploading
the request-body.
.PP
.RS
.RS
\fB===== Informational Values =====\fR
.RE
.RE
.TP
\fBmethod\fR
.
The HTTP method used in the request.
.TP
\fBcharset\fR
.
The value of the charset attribute of the \fBContent-Type\fR response header.
The charset value is used only for a text resource.  If the server did not
specify a charset, the value defaults to that of the
variable \fB::http::defaultCharset\fR, which unless it has been deliberately
modified by the caller is \fBiso8859-1\fR.  Incoming text data is automatically
converted from the character set defined by \fBcharset\fR to Tcl's internal
Unicode representation, i.e. to a Tcl string.
.TP
\fBcompression\fR
.
A copy of the \fBContent-Encoding\fR response-header value.
.TP
\fBhttpRequest\fR
.
The version of HTTP specified in the request (i.e. sent in the request line).
The value is that of the option \fB\-protocol\fR supplied
to \fB::http::geturl\fR (default value "1.1"), unless the command reduced the
value to "1.0" because it was passed the \fB\-handler\fR option.
.TP
\fBhttpResponse\fR
.
The version of HTTP used by the server (obtained from the response
"status line").  The server uses this version of HTTP in its response, but
ensures that this response is compatible with the HTTP version specified in the
client's request.  If the value cannot be extracted from the status line, the
full status line is returned.
.TP
\fBurl\fR
.
The requested URL, typically the URL supplied as an argument
to \fB::http::geturl\fR but without its "fragment" (the final part of the URL
beginning with "#").
.TP
\fBconnectionRequest\fR
.
The value, if any, sent to the server in \fBConnection\fR request header(s).
.TP
\fBconnectionResponse\fR
.
The value, if any, received from the server in \fBConnection\fR response
header(s).
.TP
\fBconnectionActual\fR
.
This value, set by \fB::http::geturl\fR, reports whether the connection was
closed after the transaction (value "close"), or left open (value "keep-alive").
.TP
\fBtransferEncoding\fR
.
The value of the Transfer-Encoding response header, if it is present.
The value is either "chunked" (indicating HTTP/1.1 "chunked encoding") or
the empty string.
.TP
\fBtotalPost\fR
.
The total length of the request body in a POST request.
.TP
\fBcurrentPost\fR
.
The number of bytes of the POST request body sent to the server so far.
The value is the same as that returned by command \fB::http::size\fR.
.TP
\fBtotalSize\fR
.
A copy of the \fBContent-Length\fR response-header value.
The number of bytes specified in a \fBContent-Length\fR header, if one
was sent.  If none was sent, the value is 0.  A correctly configured server
omits this header if the transfer-encoding is "chunked", or (for older
servers) if the server closes the connection when it reaches the end of
the resource.
.TP
\fBcurrentSize\fR
.
The number of bytes fetched from the server so far.
.PP
.SS "MORE METADATA"
The dictionary returned by \fB::http::responseInfo\fR is the most useful
subset of the available metadata.  Other metadata include:
.PP
1. The full "status line" of the response, available as the return value
of command \fB::http::responseLine\fR.
.PP
2. The full response headers, available as the return value of
command \fB::http::responseHeaders\fR.  This return value is a list of the
response-header names and values, in the order that they were received from
the server.
.PP
The return value is not a \fBdict\fR because some header names may
occur more than once, notably \fBSet-Cookie\fR. If the value is read
into a \fBdict\fR or into an array (using array set), only the last header
with each name will be preserved.
.PP
.RS
Some of the header names (metadata keys) are listed below, but the HTTP
standard defines several more, and servers are free to add their own.
When a dictionary key is mentioned below, this refers to the \fBdict\fR
value returned by command \fB::http::responseInfo\fR.
.TP
\fBContent-Type\fR
.
The content type of the URL contents.  Examples include \fBtext/html\fR,
\fBimage/gif,\fR \fBapplication/postscript\fR and
\fBapplication/x-tcl\fR.  Text values typically specify a character set, e.g.
\fBtext/html; charset=UTF-8\fR.  Dictionary key \fIcontentType\fR.
.TP
\fBContent-Length\fR
.
The advertised size in bytes of the contents, available as dictionary
key \fItotalSize\fR.  The actual number of bytes read by \fB::http::geturl\fR
so far is available as dictionary key \fBcurrentSize\fR.
.TP
\fBContent-Encoding\fR
.
The compression algorithm used for the contents.
Examples include \fBgzip\fR, \fBdeflate\fR.
Dictionary key \fIcontent\fR.
.TP
\fBLocation\fR
.
This header is sent when a response has status code 3XX (redirection).
It provides the URL that is the redirection target.
Dictionary key \fIredirection\fR.
.TP
\fBSet-Cookie\fR
.
This header is sent to offer a cookie to the client.  Cookie management is
done by the \fB::http::config\fR option \fI\-cookiejar\fR, and so
the \fBSet-Cookie\fR headers need not be parsed by user scripts.
See section \fBCOOKIE JAR PROTOCOL\fR.
.TP
\fBConnection\fR
.
The value can be supplied as a comma-separated list, or by multiple headers.
The list often has only one element, either "close" or "keep-alive".
The value "upgrade" indicates a successful upgrade request and is typically
combined with the status code 101, an \fBUpgrade\fR response header, and no
response body.  Dictionary key \fIconnectionResponse\fR.
.TP
\fBUpgrade\fR
.
The value indicates the protocol(s) to which the server will switch
immediately after the empty line that terminates the 101 response headers.
Dictionary key \fIupgrade\fR.
.RE
.PP
.SS "EVEN MORE METADATA"
.PP
1. Details of the HTTP request.  The request is determined by the options
supplied to \fB::http::geturl\fR and \fB::http::config\fR.  However, it is
sometimes helpful to examine what \fB::http::geturl\fR actually sent to the
server, and this information is available through
commands \fB::http::requestHeaders\fR and \fB::http::requestLine\fR.
.PP
2. The state array: the internal variables of \fB::http::geturl\fR.
It may sometimes be helpful to examine this array.



Details are given in the next section.



.SH "STATE ARRAY"
The \fB::http::geturl\fR procedure returns a \fItoken\fR that can be used
as an argument to other \fB::http::*\fR commands, which examine and manage
the state of the HTTP transaction.  For most purposes these commands are
sufficient.  The \fItoken\fR can also be used to access
the internal state of the transaction, which is stored in a Tcl array.
This facility is most useful when writing callback commands for the
options \fB\-command\fR, \fB\-handler\fR, \fB\-progress\fR,
or \fB\-queryprogress\fR.
Use the following command inside the proc to define an easy-to-use
array \fIstate\fR as a local variable within the proc
.PP
.CS
upvar 0 $token state
.CE
.PP
Once the data associated with the URL is no longer needed, the state
array should be unset to free up storage.
The \fB::http::cleanup\fR procedure is provided for that purpose.
.PP
The following elements of the array are supported, and are the origin of the

values returned by commands as described below.  When a dictionary key is
mentioned below, this refers to the \fBdict\fR value returned by
command \fB::http::responseInfo\fR.
.RS
.TP
\fBbinary\fR
.



For dictionary key \fIbinary\fR.

.TP
\fBbody\fR
.


For command \fB::http::responseBody\fR.
.TP
\fBcharset\fR
.


For dictionary key \fIcharset\fR.

.TP
\fBcoding\fR
.
For dictionary key \fIcompression\fR.
.TP
\fBconnection\fR
.
For dictionary key \fIconnectionActual\fR.
.TP
\fBcurrentsize\fR
.

For command \fB::http::size\fR; and for dictionary key \fIcurrentSize\fR.
.TP
\fBerror\fR
.
For command \fB::http::error\fR; part is used in dictionary key \fIerror\fR.

.TP
\fBhttp\fR
.

For command \fB::http::responseLine\fR.












.TP
\fBhttpResponse\fR
.












For dictionary key \fIhttpResponse\fR.
.TP
\fBmeta\fR
.


For command \fB::http::responseHeaders\fR. Further discussion above in the
section \fBMORE METADATA\fR.
.TP
\fBmethod\fR
.



For dictionary key \fImethod\fR.



.TP
\fBposterror\fR
.
For dictionary key \fIpostError\fR.
.TP
\fBpostErrorFull\fR
.
For command \fB::http::postError\fR.
.TP
\fB\-protocol\fR
.
For dictionary key \fIhttpRequest\fR.
.TP
\fBquerylength\fR
.
For dictionary key \fItotalPost\fR.
.TP
\fBqueryoffset\fR
.
For dictionary key \fIcurrentPost\fR.
.TP
\fBreasonPhrase\fR
.
For dictionary key \fIreasonPhrase\fR.
.TP
\fBrequestHeaders\fR
.
For command \fB::http::requestHeaders\fR.
.TP
\fBrequestLine\fR
.
For command \fB::http::requestLine\fR.
.TP
\fBresponseCode\fR
.
For dictionary key \fIresponseCode\fR.
.TP
\fBstate\fR
.

For dictionary key \fIstage\fR.
.TP
\fBstatus\fR

.
For command \fB::http::status\fR; and for dictionary key \fIstatus\fR.
.TP
\fBtotalsize\fR
.
For dictionary key \fItotalSize\fR.
.TP
\fBtransfer\fR
.
For dictionary key \fItransferEncoding\fR.
.TP
\fBtype\fR
.
For dictionary key \fIcontentType\fR.
.TP
\fBupgrade\fR
.
For dictionary key \fIupgrade\fR.
.TP
\fBurl\fR
.
For dictionary key \fIurl\fR.
.RE
.SH "PERSISTENT CONNECTIONS"
.PP
.SS "BASICS"
.PP
See RFC 7230 Sec 6, which supersedes RFC 2616 Sec 8.1.
.PP
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
for a POST request.
.PP
Option \fB\-repost\fR, if \fBtrue\fR, permits automatic retry of a POST request
that fails because it uses a persistent connection that the server has
half-closed (an
.QW "asynchronous close event" ).
Subsequent GET and HEAD requests in a failed pipeline will also be retried.
\fIThe \-repost option should be used only if the application understands
that the retry is appropriate\fR - specifically, the application must know
that if the failed POST successfully modified the state of the server, a repeat
POST would have no adverse effect.
.VS TIP406
.SH "COOKIE JAR PROTOCOL"
.PP
Cookies are short key-value pairs used to implement sessions within the







|







1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
for a POST request.
.PP
Option \fB\-repost\fR, if \fBtrue\fR, permits automatic retry of a POST request
that fails because it uses a persistent connection that the server has
half-closed (an
.QW "asynchronous close event" ).
Subsequent GET and HEAD requests in a failed pipeline will also be retried.
\fIThe \fB\-repost\fI option should be used only if the application understands
that the retry is appropriate\fR - specifically, the application must know
that if the failed POST successfully modified the state of the server, a repeat
POST would have no adverse effect.
.VS TIP406
.SH "COOKIE JAR PROTOCOL"
.PP
Cookies are short key-value pairs used to implement sessions within the
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
request.
.PP
Other keys may always be ignored; they have no meaning in this protocol.
.RE
.VE TIP406
.SH "PROTOCOL UPGRADES"
.PP
The HTTP/1.1 \fBConnection\fR and \fBUpgrade\fR client headers inform the server
that the client wishes to change the protocol used over the existing connection

(RFC 7230).  This mechanism can be used to request a WebSocket (RFC 6455), a
higher version of the HTTP protocol (HTTP 2), or TLS encryption.  If the
server accepts the upgrade request, its response code will be 101.
.PP
To request a protocol upgrade when calling \fBhttp::geturl\fR, the \fB\-headers\fR
option must supply appropriate values for \fBConnection\fR and \fBUpgrade\fR, and

the \fB\-command\fR option must supply a command that implements the requested
protocol and can also handle the server response if the server refuses the
protocol upgrade.  For upgrade requests \fBhttp::geturl\fR ignores the value of
option \fB\-keepalive\fR, and always uses the value \fB0\fR so that the upgrade
request is not made over a connection that is intended for multiple HTTP requests.

.PP
The Tcllib library \fBwebsocket\fR implements WebSockets, and makes the necessary
calls to commands in the \fBhttp\fR package.
.PP
There is currently no native Tcl client library for HTTP/2.
.PP
The \fBUpgrade\fR mechanism is not used to request TLS in web browsers, because
\fBhttp\fR and \fBhttps\fR are served over different ports.  It is used by
protocols such as Internet Printing Protocol (IPP) that are built on top of
\fBhttp(s)\fR and use the same TCP port number for both secure and insecure
traffic.
.PP
In browsers, opportunistic encryption is instead implemented by the
\fBUpgrade-Insecure-Requests\fR client header.  If a secure service is available,
the server response code is a 307 redirect, and the response header
\fBLocation\fR specifies the target URL.  The browser must call \fBhttp::geturl\fR
again in order to fetch this URL.
See https://w3c.github.io/webappsec-upgrade-insecure-requests/
.PP











































.SH EXAMPLE
.PP
This example creates a procedure to copy a URL to a file while printing a
progress meter, and prints the meta-data associated with the URL.
.PP
.CS
proc httpcopy { url file {chunk 4096} } {
    set out [open $file w]
    set token [\fB::http::geturl\fR $url -channel $out \e
            -progress httpCopyProgress -blocksize $chunk]
    close $out

    # This ends the line started by httpCopyProgress
    puts stderr ""

    upvar #0 $token state
    set max 0
    foreach {name value} $state(meta) {
        if {[string length $name] > $max} {
            set max [string length $name]
        }
        if {[regexp -nocase ^location$ $name]} {
            # Handle URL redirects







|
|
>
|



|
|
>




|
>

|
|










|
|
|
|


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



|











|







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
request.
.PP
Other keys may always be ignored; they have no meaning in this protocol.
.RE
.VE TIP406
.SH "PROTOCOL UPGRADES"
.PP
The HTTP/1.1 \fBConnection\fR and \fBUpgrade\fR request headers inform the
server that the client wishes to change the protocol used over the existing
connection (RFC 7230).
This mechanism can be used to request a WebSocket (RFC 6455), a
higher version of the HTTP protocol (HTTP 2), or TLS encryption.  If the
server accepts the upgrade request, its response code will be 101.
.PP
To request a protocol upgrade when calling \fBhttp::geturl\fR,
the \fB\-headers\fR option must supply appropriate values for \fBConnection\fR
and \fBUpgrade\fR, and
the \fB\-command\fR option must supply a command that implements the requested
protocol and can also handle the server response if the server refuses the
protocol upgrade.  For upgrade requests \fBhttp::geturl\fR ignores the value of
option \fB\-keepalive\fR, and always uses the value \fB0\fR so that the upgrade
request is not made over a connection that is intended for multiple HTTP
requests.
.PP
The Tcllib library \fBwebsocket\fR implements WebSockets, and makes the
necessary calls to commands in the \fBhttp\fR package.
.PP
There is currently no native Tcl client library for HTTP/2.
.PP
The \fBUpgrade\fR mechanism is not used to request TLS in web browsers, because
\fBhttp\fR and \fBhttps\fR are served over different ports.  It is used by
protocols such as Internet Printing Protocol (IPP) that are built on top of
\fBhttp(s)\fR and use the same TCP port number for both secure and insecure
traffic.
.PP
In browsers, opportunistic encryption is instead implemented by the
\fBUpgrade-Insecure-Requests\fR client header.  If a secure service is
available, the server response code is a 307 redirect, and the response header
\fBLocation\fR specifies the target URL.  The browser must
call \fBhttp::geturl\fR again in order to fetch this URL.
See https://w3c.github.io/webappsec-upgrade-insecure-requests/
.PP
.SH THREADS
.PP
.SS "PURPOSE"
.PP
Command \fB::http::geturl\fR uses the Tcl \fB::socket\fR command with
the \fI\-async\fR option to connect to a remote server, but the return from
this command can be delayed in adverse cases (e.g. a slow DNS lookup),
preventing the event loop from processing other events.
This delay is avoided if the \fB::socket\fR command is evaluated in another
thread.  The Thread package is not part of Tcl but is provided in
"Batteries Included" distributions.  Instead of the \fB::socket\fR command,
the http package uses \fB::http::socket\fR which makes connections in the
manner specified by the value of \fI\-threadlevel\fR and the availability
of package Thread.
.PP
.SS "WITH TLS (HTTPS)"
.PP
The same \fI\-threadlevel\fR configuration applies to both HTTP and HTTPS
connections.
HTTPS is enabled by using the \fBhttp::register\fR command, typically by
specifying the \fB::tls::socket\fR command of the tls package to handle TLS
cryptography.  The \fB::tls::socket\fR command connects to the remote server by
using the command specified by the value of variable \fB::tls::socketCmd\fR, and
this value defaults to "::socket".  If http::geturl finds
that \fB::tls::socketCmd\fR has this value, it replaces it with the value
"::http::socket".  If \fB::tls::socketCmd\fR has a value other than "::socket",
i.e. if the script or the Tcl installation has replaced the value "::socket"
with the name of a different command, then http does not change the value.
The script or installation that modified \fB::tls::socketCmd\fR is responsible
for integrating \fR::http::socket\fR into its own replacement command.
.PP
.SS "WITH A CHILD INTERPRETER"
.PP
The peer thread can transfer the socket only to the main interpreter of the
script's thread.  Therefore the thread-based \fB::http::socket\fR works with
non-zero \fI\-threadlevel\fR values only if the script runs in the main
interpreter.  A child interpreter must use \fI\-threadlevel 0\fR unless the
parent interpreter has provided alternative facilities.  The main parent
interpreter may grant full \fI\-threadlevel\fR facilities to a child
interpreter, for example by aliasing, to \fB::http::socket\fR in the child,
a command that runs \fBhttp::socket\fR in the parent, and then transfers
the socket to the child.
.PP
.SH EXAMPLE
.PP
This example creates a procedure to copy a URL to a file while printing a
progress meter, and prints the response headers associated with the URL.
.PP
.CS
proc httpcopy { url file {chunk 4096} } {
    set out [open $file w]
    set token [\fB::http::geturl\fR $url -channel $out \e
            -progress httpCopyProgress -blocksize $chunk]
    close $out

    # This ends the line started by httpCopyProgress
    puts stderr ""

    upvar 0 $token state
    set max 0
    foreach {name value} $state(meta) {
        if {[string length $name] > $max} {
            set max [string length $name]
        }
        if {[regexp -nocase ^location$ $name]} {
            # Handle URL redirects
Added doc/lseq.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
'\"
'\" Copyright (c) 2022 Eric Taylor.  All rights reserved.
'\"
'\" See the file "license.terms" for information on usage and redistribution
'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
'\"
.TH lseq n 8.7 Tcl "Tcl Built-In Commands"
.so man.macros
.BS
'\" Note:  do not modify the .SH NAME line immediately below!
.SH NAME
lseq \- Build a numeric sequence returned as a list
.SH SYNOPSIS
\fBlseq \fIStart \fR?(\fB..\fR|\fBto\fR)? \fIEnd\fR ??\fBby\fR? \fIStep\fR?

\fBlseq \fIStart \fBcount\fR \fICount\fR ??\fBby\fR? \fIStep\fR?

\fBlseq \fICount\fR ?\fBby \fIStep\fR?
.BE
.SH DESCRIPTION
.PP
The \fBlseq\fR command creates a sequence of numeric values using the given
parameters \fIStart\fR, \fIEnd\fR, and \fIStep\fR. The \fIoperation\fR
argument ".." or "to" defines an inclusive range. The "count" option is used
to define a count of the number of elements in the list. The short form with a
single count value will create a range from 0 to count-1.

The numeric arguments, \fIStart\fR, \fIEnd\fR, \fIStep\fR, and \fICount\fR,
can also be a valid expression. the lseq command will evaluate the expression
and use the numeric result, or return an error as with any invalid argument
value. A valid expression is a valid [expr] expression, however, the result
must be numeric; a non-numeric string will result in an error.

.SH EXAMPLES
.CS
.\"

 lseq 3
 \(-> 0 1 2

 lseq 3 0
 \(-> 3 2 1 0

 lseq 10 .. 1 by -2
 \(-> 10 8 6 4 2

 set l [lseq 0 -5]
 \(-> 0 -1 -2 -3 -4 -5

 foreach i [lseq [llength $l]] {
   puts l($i)=[lindex $l $i]
 }
 \(-> l(0)=0
    l(1)=-1
    l(2)=-2
    l(3)=-3
    l(4)=-4
    l(5)=-5

 foreach i [lseq [llength $l]-1 0] {
    puts l($i)=[lindex $l $i]
 }
 \(-> l(5)=-5
    l(4)=-4
    l(3)=-3
    l(2)=-2
    l(1)=-1
    l(0)=0

 set i 17
 \(-> 17
 if {$i in [lseq 0 50]} { # equivalent to: (0 <= $i && $i < 50)
     puts "Ok"
 } else {
     puts "outside :("
 }
 \(-> Ok

 set sqrs [lmap i [lseq 1 10] {expr $i*$i}]
 \(-> 1 4 9 16 25 36 49 64 81 100
.\"
.CE
.SH "SEE ALSO"
foreach(n), list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n),
lmap(n), lpop(n), lrange(n), lremove(n), lreplace(n),
lreverse(n), lsearch(n), lset(n), lsort(n)
.SH KEYWORDS
element, index, list
'\" Local Variables:
'\" mode: nroff
'\" fill-column: 78
'\" End:
Changes to doc/vwait.n.
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
.so man.macros
.BS
'\" Note:  do not modify the .SH NAME line immediately below!
.SH NAME
vwait \- Process events until a variable is written
.SH SYNOPSIS
\fBvwait\fR \fIvarName\fR


.BE
.SH DESCRIPTION
.PP
This command enters the Tcl event loop to process events, blocking
the application if no events are ready.  It continues processing
events until some event handler sets the value of the global variable
\fIvarName\fR.  Once \fIvarName\fR has been set, the \fBvwait\fR
command will return as soon as the event handler that modified
\fIvarName\fR completes.  The \fIvarName\fR argument is always interpreted as
a variable name with respect to the global namespace, but can refer to any
namespace's variables if the fully-qualified name is given.
.PP



































































In some cases the \fBvwait\fR command may not return immediately
after \fIvarName\fR is set.  This happens if the event handler
that sets \fIvarName\fR does not complete immediately.  For example,
if an event handler sets \fIvarName\fR and then itself calls
\fBvwait\fR to wait for a different variable, then it may not return
for a long time.  During this time the top-level \fBvwait\fR is
blocked waiting for the event handler to complete, so it cannot
return either. (See the \fBNESTED VWAITS BY EXAMPLE\fR below.)
.PP







>
>












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

|







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
.so man.macros
.BS
'\" Note:  do not modify the .SH NAME line immediately below!
.SH NAME
vwait \- Process events until a variable is written
.SH SYNOPSIS
\fBvwait\fR \fIvarName\fR
.PP
\fBvwait\fR ?\Ioptions\fR? ?\fIvarName ...\fR?
.BE
.SH DESCRIPTION
.PP
This command enters the Tcl event loop to process events, blocking
the application if no events are ready.  It continues processing
events until some event handler sets the value of the global variable
\fIvarName\fR.  Once \fIvarName\fR has been set, the \fBvwait\fR
command will return as soon as the event handler that modified
\fIvarName\fR completes.  The \fIvarName\fR argument is always interpreted as
a variable name with respect to the global namespace, but can refer to any
namespace's variables if the fully-qualified name is given.
.PP
In the second more complex command form \fIoptions\fR allow for finer
control of the wait operation and to deal with multiple event sources.
\fIOptions\fR can be made up of
.TP
\fB\-\-\fR
.
Marks the end of options. All following arguments are handled as
variable names.
.TP
\fB\-all\fR
.
All conditions for the wait operation must be met to complete the
wait operation. Otherwise (the default) the first event completes
the wait.
.TP
\fB\-extended\fR
.
An extended result in list form is returned, see below for explanation.
.TP
\fB\-nofileevents\fR
.
File events are not handled in the wait operation.
.TP
\fB\-noidleevents\fR
.
Idle handlers are not invoked during the wait operation.
.TP
\fB\-notimerevents\fR
.
Timer handlers are not serviced during the wait operation.
.TP
\fB\-nowindowevents\fR
.
Events of the windowing system are not handled during the wait operation.
.TP
\fB\-readable\fR \fIchannel\fR
.
\fIChannel\fR must name a Tcl channel open for reading. If \fIchannel\fR
is or becomes readable the wait operation completes.
.TP
\fB\-timeout\fR milliseconds\fR
.
The wait operation is constrained to \fImilliseconds\fR.
.TP
\fB\-variable\fR \fIvarName\fR
.
\fIVarName\fR must be the name of a global variable. Writing or
unsetting this variable completes the wait operation.
.TP
\fB\-writable\fR \fIchannel\fR
.
\fIChannel\fR must name a Tcl channel open for writing. If \fIchannel\fR
is or becomes writable the wait operation completes.
.PP
The result returned by \fBvwait\fR is for the simple form an empty
string. If the \fI\-timeout\fR option is specified, the result is the
number of milliseconds remaining when the wait condition has been
met, or -1 if the wait operation timed out.
.PP
If the \fI\-extended\fR option is specified, the result is made up
of a Tcl list with an even number of elements. Odd elements
take the values \fBreadable\fR, \fBtimeleft\fR, \fBvariable\fR,
and \fBwritable\fR. Even elements are the corresponding variable
and channel names or the remaining number of milliseconds.
The list is ordered by the occurrences of the event(s) with the
exception of \fBtimeleft\fR which always comes last.
.PP
In some cases the \fBvwait\fR command may not return immediately
after \fIvarName\fR et.al. is set. This happens if the event handler
that sets \fIvarName\fR does not complete immediately.  For example,
if an event handler sets \fIvarName\fR and then itself calls
\fBvwait\fR to wait for a different variable, then it may not return
for a long time.  During this time the top-level \fBvwait\fR is
blocked waiting for the event handler to complete, so it cannot
return either. (See the \fBNESTED VWAITS BY EXAMPLE\fR below.)
.PP
Changes to generic/regc_locale.c.
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
    {0x1135D, 0x11361}, {0x11400, 0x11434}, {0x11447, 0x1144A}, {0x1145F, 0x11461},
    {0x11480, 0x114AF}, {0x11580, 0x115AE}, {0x115D8, 0x115DB}, {0x11600, 0x1162F},
    {0x11680, 0x116AA}, {0x11700, 0x1171A}, {0x11740, 0x11746}, {0x11800, 0x1182B},
    {0x118A0, 0x118DF}, {0x118FF, 0x11906}, {0x1190C, 0x11913}, {0x11918, 0x1192F},
    {0x119A0, 0x119A7}, {0x119AA, 0x119D0}, {0x11A0B, 0x11A32}, {0x11A5C, 0x11A89},
    {0x11AB0, 0x11AF8}, {0x11C00, 0x11C08}, {0x11C0A, 0x11C2E}, {0x11C72, 0x11C8F},
    {0x11D00, 0x11D06}, {0x11D0B, 0x11D30}, {0x11D60, 0x11D65}, {0x11D6A, 0x11D89},

    {0x11EE0, 0x11EF2}, {0x12000, 0x12399}, {0x12480, 0x12543}, {0x12F90, 0x12FF0},
    {0x13000, 0x1342E}, {0x14400, 0x14646}, {0x16800, 0x16A38}, {0x16A40, 0x16A5E},
    {0x16A70, 0x16ABE}, {0x16AD0, 0x16AED}, {0x16B00, 0x16B2F}, {0x16B40, 0x16B43},
    {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16E40, 0x16E7F}, {0x16F00, 0x16F4A},
    {0x16F93, 0x16F9F}, {0x17000, 0x187F7}, {0x18800, 0x18CD5}, {0x18D00, 0x18D08},
    {0x1AFF0, 0x1AFF3}, {0x1AFF5, 0x1AFFB}, {0x1B000, 0x1B122}, {0x1B150, 0x1B152},
    {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C},
    {0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1D400, 0x1D454}, {0x1D456, 0x1D49C},
    {0x1D4A9, 0x1D4AC}, {0x1D4AE, 0x1D4B9}, {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505},
    {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514}, {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539},
    {0x1D53B, 0x1D53E}, {0x1D540, 0x1D544}, {0x1D54A, 0x1D550}, {0x1D552, 0x1D6A5},
    {0x1D6A8, 0x1D6C0}, {0x1D6C2, 0x1D6DA}, {0x1D6DC, 0x1D6FA}, {0x1D6FC, 0x1D714},
    {0x1D716, 0x1D734}, {0x1D736, 0x1D74E}, {0x1D750, 0x1D76E}, {0x1D770, 0x1D788},
    {0x1D78A, 0x1D7A8}, {0x1D7AA, 0x1D7C2}, {0x1D7C4, 0x1D7CB}, {0x1DF00, 0x1DF1E},
    {0x1E100, 0x1E12C}, {0x1E137, 0x1E13D}, {0x1E290, 0x1E2AD}, {0x1E2C0, 0x1E2EB},
    {0x1E7E0, 0x1E7E6}, {0x1E7E8, 0x1E7EB}, {0x1E7F0, 0x1E7FE}, {0x1E800, 0x1E8C4},
    {0x1E900, 0x1E943}, {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F}, {0x1EE29, 0x1EE32},
    {0x1EE34, 0x1EE37}, {0x1EE4D, 0x1EE4F}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72},
    {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B},
    {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x20000, 0x2A6DF},
    {0x2A700, 0x2B738}, {0x2B740, 0x2B81D}, {0x2B820, 0x2CEA1}, {0x2CEB0, 0x2EBE0},
    {0x2F800, 0x2FA1D}, {0x30000, 0x3134A}

#endif
};

#define NUM_ALPHA_RANGE (sizeof(alphaRangeTable)/sizeof(crange))

static const chr alphaCharTable[] = {
    0xAA, 0xB5, 0xBA, 0x2EC, 0x2EE, 0x376, 0x377, 0x37F, 0x386,







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







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
    {0x1135D, 0x11361}, {0x11400, 0x11434}, {0x11447, 0x1144A}, {0x1145F, 0x11461},
    {0x11480, 0x114AF}, {0x11580, 0x115AE}, {0x115D8, 0x115DB}, {0x11600, 0x1162F},
    {0x11680, 0x116AA}, {0x11700, 0x1171A}, {0x11740, 0x11746}, {0x11800, 0x1182B},
    {0x118A0, 0x118DF}, {0x118FF, 0x11906}, {0x1190C, 0x11913}, {0x11918, 0x1192F},
    {0x119A0, 0x119A7}, {0x119AA, 0x119D0}, {0x11A0B, 0x11A32}, {0x11A5C, 0x11A89},
    {0x11AB0, 0x11AF8}, {0x11C00, 0x11C08}, {0x11C0A, 0x11C2E}, {0x11C72, 0x11C8F},
    {0x11D00, 0x11D06}, {0x11D0B, 0x11D30}, {0x11D60, 0x11D65}, {0x11D6A, 0x11D89},
    {0x11EE0, 0x11EF2}, {0x11F04, 0x11F10}, {0x11F12, 0x11F33}, {0x12000, 0x12399},
    {0x12480, 0x12543}, {0x12F90, 0x12FF0}, {0x13000, 0x1342F}, {0x13441, 0x13446},
    {0x14400, 0x14646}, {0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A70, 0x16ABE},
    {0x16AD0, 0x16AED}, {0x16B00, 0x16B2F}, {0x16B40, 0x16B43}, {0x16B63, 0x16B77},
    {0x16B7D, 0x16B8F}, {0x16E40, 0x16E7F}, {0x16F00, 0x16F4A}, {0x16F93, 0x16F9F},
    {0x17000, 0x187F7}, {0x18800, 0x18CD5}, {0x18D00, 0x18D08}, {0x1AFF0, 0x1AFF3},
    {0x1AFF5, 0x1AFFB}, {0x1B000, 0x1B122}, {0x1B150, 0x1B152}, {0x1B164, 0x1B167},
    {0x1B170, 0x1B2FB}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88},
    {0x1BC90, 0x1BC99}, {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, {0x1D4A9, 0x1D4AC},
    {0x1D4AE, 0x1D4B9}, {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A},
    {0x1D50D, 0x1D514}, {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E},
    {0x1D540, 0x1D544}, {0x1D54A, 0x1D550}, {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D6C0},
    {0x1D6C2, 0x1D6DA}, {0x1D6DC, 0x1D6FA}, {0x1D6FC, 0x1D714}, {0x1D716, 0x1D734},
    {0x1D736, 0x1D74E}, {0x1D750, 0x1D76E}, {0x1D770, 0x1D788}, {0x1D78A, 0x1D7A8},
    {0x1D7AA, 0x1D7C2}, {0x1D7C4, 0x1D7CB}, {0x1DF00, 0x1DF1E}, {0x1DF25, 0x1DF2A},
    {0x1E030, 0x1E06D}, {0x1E100, 0x1E12C}, {0x1E137, 0x1E13D}, {0x1E290, 0x1E2AD},
    {0x1E2C0, 0x1E2EB}, {0x1E4D0, 0x1E4EB}, {0x1E7E0, 0x1E7E6}, {0x1E7E8, 0x1E7EB},
    {0x1E7F0, 0x1E7FE}, {0x1E800, 0x1E8C4}, {0x1E900, 0x1E943}, {0x1EE00, 0x1EE03},
    {0x1EE05, 0x1EE1F}, {0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37}, {0x1EE4D, 0x1EE4F},
    {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72}, {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C},
    {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9},
    {0x1EEAB, 0x1EEBB}, {0x20000, 0x2A6DF}, {0x2A700, 0x2B739}, {0x2B740, 0x2B81D},
    {0x2B820, 0x2CEA1}, {0x2CEB0, 0x2EBE0}, {0x2F800, 0x2FA1D}, {0x30000, 0x3134A},
    {0x31350, 0x323AF}
#endif
};

#define NUM_ALPHA_RANGE (sizeof(alphaRangeTable)/sizeof(crange))

static const chr alphaCharTable[] = {
    0xAA, 0xB5, 0xBA, 0x2EC, 0x2EE, 0x376, 0x377, 0x37F, 0x386,
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
    0x2126, 0x2128, 0x214E, 0x2183, 0x2184, 0x2CF2, 0x2CF3, 0x2D27, 0x2D2D,
    0x2D6F, 0x2E2F, 0x3005, 0x3006, 0x303B, 0x303C, 0xA62A, 0xA62B, 0xA7D0,
    0xA7D1, 0xA7D3, 0xA8FB, 0xA8FD, 0xA8FE, 0xA9CF, 0xAA7A, 0xAAB1, 0xAAB5,
    0xAAB6, 0xAAC0, 0xAAC2, 0xFB1D, 0xFB3E, 0xFB40, 0xFB41, 0xFB43, 0xFB44
#if CHRBITS > 16
    ,0x1003C, 0x1003D, 0x10594, 0x10595, 0x105BB, 0x105BC, 0x10808, 0x10837, 0x10838,
    0x1083C, 0x108F4, 0x108F5, 0x109BE, 0x109BF, 0x10A00, 0x10EB0, 0x10EB1, 0x10F27,
    0x11071, 0x11072, 0x11075, 0x11144, 0x11147, 0x11176, 0x111DA, 0x111DC, 0x11288,
    0x1130F, 0x11310, 0x11332, 0x11333, 0x1133D, 0x11350, 0x114C4, 0x114C5, 0x114C7,
    0x11644, 0x116B8, 0x11909, 0x11915, 0x11916, 0x1193F, 0x11941, 0x119E1, 0x119E3,
    0x11A00, 0x11A3A, 0x11A50, 0x11A9D, 0x11C40, 0x11D08, 0x11D09, 0x11D46, 0x11D67,
    0x11D68, 0x11D98, 0x11FB0, 0x16F50, 0x16FE0, 0x16FE1, 0x16FE3, 0x1AFFD, 0x1AFFE,

    0x1D49E, 0x1D49F, 0x1D4A2, 0x1D4A5, 0x1D4A6, 0x1D4BB, 0x1D546, 0x1E14E, 0x1E7ED,
    0x1E7EE, 0x1E94B, 0x1EE21, 0x1EE22, 0x1EE24, 0x1EE27, 0x1EE39, 0x1EE3B, 0x1EE42,
    0x1EE47, 0x1EE49, 0x1EE4B, 0x1EE51, 0x1EE52, 0x1EE54, 0x1EE57, 0x1EE59, 0x1EE5B,
    0x1EE5D, 0x1EE5F, 0x1EE61, 0x1EE62, 0x1EE64, 0x1EE7E
#endif
};

#define NUM_ALPHA_CHAR (sizeof(alphaCharTable)/sizeof(chr))

/*
 * Unicode: control characters.
 */

static const crange controlRangeTable[] = {
    {0x0, 0x1F}, {0x7F, 0x9F}, {0x600, 0x605}, {0x200B, 0x200F},
    {0x202A, 0x202E}, {0x2060, 0x2064}, {0x2066, 0x206F}, {0xE000, 0xF8FF},
    {0xFFF9, 0xFFFB}
#if CHRBITS > 16
    ,{0x13430, 0x13438}, {0x1BCA0, 0x1BCA3}, {0x1D173, 0x1D17A}, {0xE0020, 0xE007F},
    {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}
#endif
};

#define NUM_CONTROL_RANGE (sizeof(controlRangeTable)/sizeof(crange))

static const chr controlCharTable[] = {







|
|
|
|
|
>
|
|
|
|














|







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
    0x2126, 0x2128, 0x214E, 0x2183, 0x2184, 0x2CF2, 0x2CF3, 0x2D27, 0x2D2D,
    0x2D6F, 0x2E2F, 0x3005, 0x3006, 0x303B, 0x303C, 0xA62A, 0xA62B, 0xA7D0,
    0xA7D1, 0xA7D3, 0xA8FB, 0xA8FD, 0xA8FE, 0xA9CF, 0xAA7A, 0xAAB1, 0xAAB5,
    0xAAB6, 0xAAC0, 0xAAC2, 0xFB1D, 0xFB3E, 0xFB40, 0xFB41, 0xFB43, 0xFB44
#if CHRBITS > 16
    ,0x1003C, 0x1003D, 0x10594, 0x10595, 0x105BB, 0x105BC, 0x10808, 0x10837, 0x10838,
    0x1083C, 0x108F4, 0x108F5, 0x109BE, 0x109BF, 0x10A00, 0x10EB0, 0x10EB1, 0x10F27,
    0x11071, 0x11072, 0x11075, 0x11144, 0x11147, 0x11176, 0x111DA, 0x111DC, 0x1123F,
    0x11240, 0x11288, 0x1130F, 0x11310, 0x11332, 0x11333, 0x1133D, 0x11350, 0x114C4,
    0x114C5, 0x114C7, 0x11644, 0x116B8, 0x11909, 0x11915, 0x11916, 0x1193F, 0x11941,
    0x119E1, 0x119E3, 0x11A00, 0x11A3A, 0x11A50, 0x11A9D, 0x11C40, 0x11D08, 0x11D09,
    0x11D46, 0x11D67, 0x11D68, 0x11D98, 0x11F02, 0x11FB0, 0x16F50, 0x16FE0, 0x16FE1,
    0x16FE3, 0x1AFFD, 0x1AFFE, 0x1B132, 0x1B155, 0x1D49E, 0x1D49F, 0x1D4A2, 0x1D4A5,
    0x1D4A6, 0x1D4BB, 0x1D546, 0x1E14E, 0x1E7ED, 0x1E7EE, 0x1E94B, 0x1EE21, 0x1EE22,
    0x1EE24, 0x1EE27, 0x1EE39, 0x1EE3B, 0x1EE42, 0x1EE47, 0x1EE49, 0x1EE4B, 0x1EE51,
    0x1EE52, 0x1EE54, 0x1EE57, 0x1EE59, 0x1EE5B, 0x1EE5D, 0x1EE5F, 0x1EE61, 0x1EE62,
    0x1EE64, 0x1EE7E
#endif
};

#define NUM_ALPHA_CHAR (sizeof(alphaCharTable)/sizeof(chr))

/*
 * Unicode: control characters.
 */

static const crange controlRangeTable[] = {
    {0x0, 0x1F}, {0x7F, 0x9F}, {0x600, 0x605}, {0x200B, 0x200F},
    {0x202A, 0x202E}, {0x2060, 0x2064}, {0x2066, 0x206F}, {0xE000, 0xF8FF},
    {0xFFF9, 0xFFFB}
#if CHRBITS > 16
    ,{0x13430, 0x1343F}, {0x1BCA0, 0x1BCA3}, {0x1D173, 0x1D17A}, {0xE0020, 0xE007F},
    {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}
#endif
};

#define NUM_CONTROL_RANGE (sizeof(controlRangeTable)/sizeof(crange))

static const chr controlCharTable[] = {
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
    {0xA9D0, 0xA9D9}, {0xA9F0, 0xA9F9}, {0xAA50, 0xAA59}, {0xABF0, 0xABF9},
    {0xFF10, 0xFF19}
#if CHRBITS > 16
    ,{0x104A0, 0x104A9}, {0x10D30, 0x10D39}, {0x11066, 0x1106F}, {0x110F0, 0x110F9},
    {0x11136, 0x1113F}, {0x111D0, 0x111D9}, {0x112F0, 0x112F9}, {0x11450, 0x11459},
    {0x114D0, 0x114D9}, {0x11650, 0x11659}, {0x116C0, 0x116C9}, {0x11730, 0x11739},
    {0x118E0, 0x118E9}, {0x11950, 0x11959}, {0x11C50, 0x11C59}, {0x11D50, 0x11D59},
    {0x11DA0, 0x11DA9}, {0x16A60, 0x16A69}, {0x16AC0, 0x16AC9}, {0x16B50, 0x16B59},
    {0x1D7CE, 0x1D7FF}, {0x1E140, 0x1E149}, {0x1E2F0, 0x1E2F9}, {0x1E950, 0x1E959},
    {0x1FBF0, 0x1FBF9}
#endif
};

#define NUM_DIGIT_RANGE (sizeof(digitRangeTable)/sizeof(crange))

/*
 * no singletons of digit characters.







|
|
|







334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
    {0xA9D0, 0xA9D9}, {0xA9F0, 0xA9F9}, {0xAA50, 0xAA59}, {0xABF0, 0xABF9},
    {0xFF10, 0xFF19}
#if CHRBITS > 16
    ,{0x104A0, 0x104A9}, {0x10D30, 0x10D39}, {0x11066, 0x1106F}, {0x110F0, 0x110F9},
    {0x11136, 0x1113F}, {0x111D0, 0x111D9}, {0x112F0, 0x112F9}, {0x11450, 0x11459},
    {0x114D0, 0x114D9}, {0x11650, 0x11659}, {0x116C0, 0x116C9}, {0x11730, 0x11739},
    {0x118E0, 0x118E9}, {0x11950, 0x11959}, {0x11C50, 0x11C59}, {0x11D50, 0x11D59},
    {0x11DA0, 0x11DA9}, {0x11F50, 0x11F59}, {0x16A60, 0x16A69}, {0x16AC0, 0x16AC9},
    {0x16B50, 0x16B59}, {0x1D7CE, 0x1D7FF}, {0x1E140, 0x1E149}, {0x1E2F0, 0x1E2F9},
    {0x1E4F0, 0x1E4F9}, {0x1E950, 0x1E959}, {0x1FBF0, 0x1FBF9}
#endif
};

#define NUM_DIGIT_RANGE (sizeof(digitRangeTable)/sizeof(crange))

/*
 * no singletons of digit characters.
368
369
370
371
372
373
374
375
376

377
378
379
380
381
382
383
    {0xFF0C, 0xFF0F}, {0xFF3B, 0xFF3D}, {0xFF5F, 0xFF65}
#if CHRBITS > 16
    ,{0x10100, 0x10102}, {0x10A50, 0x10A58}, {0x10AF0, 0x10AF6}, {0x10B39, 0x10B3F},
    {0x10B99, 0x10B9C}, {0x10F55, 0x10F59}, {0x10F86, 0x10F89}, {0x11047, 0x1104D},
    {0x110BE, 0x110C1}, {0x11140, 0x11143}, {0x111C5, 0x111C8}, {0x111DD, 0x111DF},
    {0x11238, 0x1123D}, {0x1144B, 0x1144F}, {0x115C1, 0x115D7}, {0x11641, 0x11643},
    {0x11660, 0x1166C}, {0x1173C, 0x1173E}, {0x11944, 0x11946}, {0x11A3F, 0x11A46},
    {0x11A9A, 0x11A9C}, {0x11A9E, 0x11AA2}, {0x11C41, 0x11C45}, {0x12470, 0x12474},
    {0x16B37, 0x16B3B}, {0x16E97, 0x16E9A}, {0x1DA87, 0x1DA8B}

#endif
};

#define NUM_PUNCT_RANGE (sizeof(punctRangeTable)/sizeof(crange))

static const chr punctCharTable[] = {
    0x3A, 0x3B, 0x3F, 0x40, 0x5F, 0x7B, 0x7D, 0xA1, 0xA7,







|
|
>







371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
    {0xFF0C, 0xFF0F}, {0xFF3B, 0xFF3D}, {0xFF5F, 0xFF65}
#if CHRBITS > 16
    ,{0x10100, 0x10102}, {0x10A50, 0x10A58}, {0x10AF0, 0x10AF6}, {0x10B39, 0x10B3F},
    {0x10B99, 0x10B9C}, {0x10F55, 0x10F59}, {0x10F86, 0x10F89}, {0x11047, 0x1104D},
    {0x110BE, 0x110C1}, {0x11140, 0x11143}, {0x111C5, 0x111C8}, {0x111DD, 0x111DF},
    {0x11238, 0x1123D}, {0x1144B, 0x1144F}, {0x115C1, 0x115D7}, {0x11641, 0x11643},
    {0x11660, 0x1166C}, {0x1173C, 0x1173E}, {0x11944, 0x11946}, {0x11A3F, 0x11A46},
    {0x11A9A, 0x11A9C}, {0x11A9E, 0x11AA2}, {0x11B00, 0x11B09}, {0x11C41, 0x11C45},
    {0x11F43, 0x11F4F}, {0x12470, 0x12474}, {0x16B37, 0x16B3B}, {0x16E97, 0x16E9A},
    {0x1DA87, 0x1DA8B}
#endif
};

#define NUM_PUNCT_RANGE (sizeof(punctRangeTable)/sizeof(crange))

static const chr punctCharTable[] = {
    0x3A, 0x3B, 0x3F, 0x40, 0x5F, 0x7B, 0x7D, 0xA1, 0xA7,
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
    {0x1D41A, 0x1D433}, {0x1D44E, 0x1D454}, {0x1D456, 0x1D467}, {0x1D482, 0x1D49B},
    {0x1D4B6, 0x1D4B9}, {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D4CF}, {0x1D4EA, 0x1D503},
    {0x1D51E, 0x1D537}, {0x1D552, 0x1D56B}, {0x1D586, 0x1D59F}, {0x1D5BA, 0x1D5D3},
    {0x1D5EE, 0x1D607}, {0x1D622, 0x1D63B}, {0x1D656, 0x1D66F}, {0x1D68A, 0x1D6A5},
    {0x1D6C2, 0x1D6DA}, {0x1D6DC, 0x1D6E1}, {0x1D6FC, 0x1D714}, {0x1D716, 0x1D71B},
    {0x1D736, 0x1D74E}, {0x1D750, 0x1D755}, {0x1D770, 0x1D788}, {0x1D78A, 0x1D78F},
    {0x1D7AA, 0x1D7C2}, {0x1D7C4, 0x1D7C9}, {0x1DF00, 0x1DF09}, {0x1DF0B, 0x1DF1E},
    {0x1E922, 0x1E943}
#endif
};

#define NUM_LOWER_RANGE (sizeof(lowerRangeTable)/sizeof(crange))

static const chr lowerCharTable[] = {
    0xB5, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10B, 0x10D, 0x10F,







|







449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
    {0x1D41A, 0x1D433}, {0x1D44E, 0x1D454}, {0x1D456, 0x1D467}, {0x1D482, 0x1D49B},
    {0x1D4B6, 0x1D4B9}, {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D4CF}, {0x1D4EA, 0x1D503},
    {0x1D51E, 0x1D537}, {0x1D552, 0x1D56B}, {0x1D586, 0x1D59F}, {0x1D5BA, 0x1D5D3},
    {0x1D5EE, 0x1D607}, {0x1D622, 0x1D63B}, {0x1D656, 0x1D66F}, {0x1D68A, 0x1D6A5},
    {0x1D6C2, 0x1D6DA}, {0x1D6DC, 0x1D6E1}, {0x1D6FC, 0x1D714}, {0x1D716, 0x1D71B},
    {0x1D736, 0x1D74E}, {0x1D750, 0x1D755}, {0x1D770, 0x1D788}, {0x1D78A, 0x1D78F},
    {0x1D7AA, 0x1D7C2}, {0x1D7C4, 0x1D7C9}, {0x1DF00, 0x1DF09}, {0x1DF0B, 0x1DF1E},
    {0x1DF25, 0x1DF2A}, {0x1E922, 0x1E943}
#endif
};

#define NUM_LOWER_RANGE (sizeof(lowerRangeTable)/sizeof(crange))

static const chr lowerCharTable[] = {
    0xB5, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10B, 0x10D, 0x10F,
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
    {0xB85, 0xB8A}, {0xB8E, 0xB90}, {0xB92, 0xB95}, {0xBA8, 0xBAA},
    {0xBAE, 0xBB9}, {0xBBE, 0xBC2}, {0xBC6, 0xBC8}, {0xBCA, 0xBCD},
    {0xBE6, 0xBFA}, {0xC00, 0xC0C}, {0xC0E, 0xC10}, {0xC12, 0xC28},
    {0xC2A, 0xC39}, {0xC3C, 0xC44}, {0xC46, 0xC48}, {0xC4A, 0xC4D},
    {0xC58, 0xC5A}, {0xC60, 0xC63}, {0xC66, 0xC6F}, {0xC77, 0xC8C},
    {0xC8E, 0xC90}, {0xC92, 0xCA8}, {0xCAA, 0xCB3}, {0xCB5, 0xCB9},
    {0xCBC, 0xCC4}, {0xCC6, 0xCC8}, {0xCCA, 0xCCD}, {0xCE0, 0xCE3},
    {0xCE6, 0xCEF}, {0xD00, 0xD0C}, {0xD0E, 0xD10}, {0xD12, 0xD44},
    {0xD46, 0xD48}, {0xD4A, 0xD4F}, {0xD54, 0xD63}, {0xD66, 0xD7F},
    {0xD81, 0xD83}, {0xD85, 0xD96}, {0xD9A, 0xDB1}, {0xDB3, 0xDBB},
    {0xDC0, 0xDC6}, {0xDCF, 0xDD4}, {0xDD8, 0xDDF}, {0xDE6, 0xDEF},
    {0xDF2, 0xDF4}, {0xE01, 0xE3A}, {0xE3F, 0xE5B}, {0xE86, 0xE8A},
    {0xE8C, 0xEA3}, {0xEA7, 0xEBD}, {0xEC0, 0xEC4}, {0xEC8, 0xECD},
    {0xED0, 0xED9}, {0xEDC, 0xEDF}, {0xF00, 0xF47}, {0xF49, 0xF6C},
    {0xF71, 0xF97}, {0xF99, 0xFBC}, {0xFBE, 0xFCC}, {0xFCE, 0xFDA},
    {0x1000, 0x10C5}, {0x10D0, 0x1248}, {0x124A, 0x124D}, {0x1250, 0x1256},
    {0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D}, {0x1290, 0x12B0},
    {0x12B2, 0x12B5}, {0x12B8, 0x12BE}, {0x12C2, 0x12C5}, {0x12C8, 0x12D6},
    {0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A}, {0x135D, 0x137C},
    {0x1380, 0x1399}, {0x13A0, 0x13F5}, {0x13F8, 0x13FD}, {0x1400, 0x167F},
    {0x1681, 0x169C}, {0x16A0, 0x16F8}, {0x1700, 0x1715}, {0x171F, 0x1736},
    {0x1740, 0x1753}, {0x1760, 0x176C}, {0x176E, 0x1770}, {0x1780, 0x17DD},
    {0x17E0, 0x17E9}, {0x17F0, 0x17F9}, {0x1800, 0x180D}, {0x180F, 0x1819},
    {0x1820, 0x1878}, {0x1880, 0x18AA}, {0x18B0, 0x18F5}, {0x1900, 0x191E},
    {0x1920, 0x192B}, {0x1930, 0x193B}, {0x1944, 0x196D}, {0x1970, 0x1974},
    {0x1980, 0x19AB}, {0x19B0, 0x19C9}, {0x19D0, 0x19DA}, {0x19DE, 0x1A1B},
    {0x1A1E, 0x1A5E}, {0x1A60, 0x1A7C}, {0x1A7F, 0x1A89}, {0x1A90, 0x1A99},
    {0x1AA0, 0x1AAD}, {0x1AB0, 0x1ACE}, {0x1B00, 0x1B4C}, {0x1B50, 0x1B7E},
    {0x1B80, 0x1BF3}, {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49}, {0x1C4D, 0x1C88},
    {0x1C90, 0x1CBA}, {0x1CBD, 0x1CC7}, {0x1CD0, 0x1CFA}, {0x1D00, 0x1F15},
    {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, {0x1F50, 0x1F57},
    {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, {0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3},
    {0x1FD6, 0x1FDB}, {0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFE},
    {0x2010, 0x2027}, {0x2030, 0x205E}, {0x2074, 0x208E}, {0x2090, 0x209C},
    {0x20A0, 0x20C0}, {0x20D0, 0x20F0}, {0x2100, 0x218B}, {0x2190, 0x2426},
    {0x2440, 0x244A}, {0x2460, 0x2B73}, {0x2B76, 0x2B95}, {0x2B97, 0x2CF3},
    {0x2CF9, 0x2D25}, {0x2D30, 0x2D67}, {0x2D7F, 0x2D96}, {0x2DA0, 0x2DA6},
    {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6},
    {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE}, {0x2DE0, 0x2E5D},
    {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB},
    {0x3001, 0x303F}, {0x3041, 0x3096}, {0x3099, 0x30FF}, {0x3105, 0x312F},
    {0x3131, 0x318E}, {0x3190, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0xA48C},
    {0xA490, 0xA4C6}, {0xA4D0, 0xA62B}, {0xA640, 0xA6F7}, {0xA700, 0xA7CA},
    {0xA7D5, 0xA7D9}, {0xA7F2, 0xA82C}, {0xA830, 0xA839}, {0xA840, 0xA877},
    {0xA880, 0xA8C5}, {0xA8CE, 0xA8D9}, {0xA8E0, 0xA953}, {0xA95F, 0xA97C},
    {0xA980, 0xA9CD}, {0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, {0xAA00, 0xAA36},
    {0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, {0xAA5C, 0xAAC2}, {0xAADB, 0xAAF6},
    {0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, {0xAB11, 0xAB16}, {0xAB20, 0xAB26},
    {0xAB28, 0xAB2E}, {0xAB30, 0xAB6B}, {0xAB70, 0xABED}, {0xABF0, 0xABF9},
    {0xAC00, 0xD7A3}, {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xF900, 0xFA6D},
    {0xFA70, 0xFAD9}, {0xFB00, 0xFB06}, {0xFB13, 0xFB17}, {0xFB1D, 0xFB36},
    {0xFB38, 0xFB3C}, {0xFB46, 0xFBC2}, {0xFBD3, 0xFD8F}, {0xFD92, 0xFDC7},
    {0xFDF0, 0xFE19}, {0xFE20, 0xFE52}, {0xFE54, 0xFE66}, {0xFE68, 0xFE6B},
    {0xFE70, 0xFE74}, {0xFE76, 0xFEFC}, {0xFF01, 0xFFBE}, {0xFFC2, 0xFFC7},
    {0xFFCA, 0xFFCF}, {0xFFD2, 0xFFD7}, {0xFFDA, 0xFFDC}, {0xFFE0, 0xFFE6},
    {0xFFE8, 0xFFEE}
#if CHRBITS > 16
    ,{0x10000, 0x1000B}, {0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003F, 0x1004D},
    {0x10050, 0x1005D}, {0x10080, 0x100FA}, {0x10100, 0x10102}, {0x10107, 0x10133},
    {0x10137, 0x1018E}, {0x10190, 0x1019C}, {0x101D0, 0x101FD}, {0x10280, 0x1029C},
    {0x102A0, 0x102D0}, {0x102E0, 0x102FB}, {0x10300, 0x10323}, {0x1032D, 0x1034A},
    {0x10350, 0x1037A}, {0x10380, 0x1039D}, {0x1039F, 0x103C3}, {0x103C8, 0x103D5},
    {0x10400, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3}, {0x104D8, 0x104FB},
    {0x10500, 0x10527}, {0x10530, 0x10563}, {0x1056F, 0x1057A}, {0x1057C, 0x1058A},
    {0x1058C, 0x10592}, {0x10597, 0x105A1}, {0x105A3, 0x105B1}, {0x105B3, 0x105B9},
    {0x10600, 0x10736}, {0x10740, 0x10755}, {0x10760, 0x10767}, {0x10780, 0x10785},
    {0x10787, 0x107B0}, {0x107B2, 0x107BA}, {0x10800, 0x10805}, {0x1080A, 0x10835},
    {0x1083F, 0x10855}, {0x10857, 0x1089E}, {0x108A7, 0x108AF}, {0x108E0, 0x108F2},
    {0x108FB, 0x1091B}, {0x1091F, 0x10939}, {0x10980, 0x109B7}, {0x109BC, 0x109CF},
    {0x109D2, 0x10A03}, {0x10A0C, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A35},
    {0x10A38, 0x10A3A}, {0x10A3F, 0x10A48}, {0x10A50, 0x10A58}, {0x10A60, 0x10A9F},
    {0x10AC0, 0x10AE6}, {0x10AEB, 0x10AF6}, {0x10B00, 0x10B35}, {0x10B39, 0x10B55},
    {0x10B58, 0x10B72}, {0x10B78, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF},
    {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2}, {0x10CFA, 0x10D27},
    {0x10D30, 0x10D39}, {0x10E60, 0x10E7E}, {0x10E80, 0x10EA9}, {0x10EAB, 0x10EAD},
    {0x10F00, 0x10F27}, {0x10F30, 0x10F59}, {0x10F70, 0x10F89}, {0x10FB0, 0x10FCB},
    {0x10FE0, 0x10FF6}, {0x11000, 0x1104D}, {0x11052, 0x11075}, {0x1107F, 0x110BC},
    {0x110BE, 0x110C2}, {0x110D0, 0x110E8}, {0x110F0, 0x110F9}, {0x11100, 0x11134},
    {0x11136, 0x11147}, {0x11150, 0x11176}, {0x11180, 0x111DF}, {0x111E1, 0x111F4},
    {0x11200, 0x11211}, {0x11213, 0x1123E}, {0x11280, 0x11286}, {0x1128A, 0x1128D},
    {0x1128F, 0x1129D}, {0x1129F, 0x112A9}, {0x112B0, 0x112EA}, {0x112F0, 0x112F9},
    {0x11300, 0x11303}, {0x11305, 0x1130C}, {0x11313, 0x11328}, {0x1132A, 0x11330},
    {0x11335, 0x11339}, {0x1133B, 0x11344}, {0x1134B, 0x1134D}, {0x1135D, 0x11363},
    {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x11400, 0x1145B}, {0x1145D, 0x11461},
    {0x11480, 0x114C7}, {0x114D0, 0x114D9}, {0x11580, 0x115B5}, {0x115B8, 0x115DD},
    {0x11600, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C}, {0x11680, 0x116B9},
    {0x116C0, 0x116C9}, {0x11700, 0x1171A}, {0x1171D, 0x1172B}, {0x11730, 0x11746},
    {0x11800, 0x1183B}, {0x118A0, 0x118F2}, {0x118FF, 0x11906}, {0x1190C, 0x11913},
    {0x11918, 0x11935}, {0x1193B, 0x11946}, {0x11950, 0x11959}, {0x119A0, 0x119A7},
    {0x119AA, 0x119D7}, {0x119DA, 0x119E4}, {0x11A00, 0x11A47}, {0x11A50, 0x11AA2},
    {0x11AB0, 0x11AF8}, {0x11C00, 0x11C08}, {0x11C0A, 0x11C36}, {0x11C38, 0x11C45},
    {0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6},
    {0x11D00, 0x11D06}, {0x11D0B, 0x11D36}, {0x11D3F, 0x11D47}, {0x11D50, 0x11D59},
    {0x11D60, 0x11D65}, {0x11D6A, 0x11D8E}, {0x11D93, 0x11D98}, {0x11DA0, 0x11DA9},

    {0x11EE0, 0x11EF8}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399}, {0x12400, 0x1246E},
    {0x12470, 0x12474}, {0x12480, 0x12543}, {0x12F90, 0x12FF2}, {0x13000, 0x1342E},
    {0x14400, 0x14646}, {0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A60, 0x16A69},
    {0x16A6E, 0x16ABE}, {0x16AC0, 0x16AC9}, {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF5},
    {0x16B00, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, {0x16B63, 0x16B77},
    {0x16B7D, 0x16B8F}, {0x16E40, 0x16E9A}, {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87},
    {0x16F8F, 0x16F9F}, {0x16FE0, 0x16FE4}, {0x17000, 0x187F7}, {0x18800, 0x18CD5},
    {0x18D00, 0x18D08}, {0x1AFF0, 0x1AFF3}, {0x1AFF5, 0x1AFFB}, {0x1B000, 0x1B122},
    {0x1B150, 0x1B152}, {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, {0x1BC00, 0x1BC6A},
    {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BC9F},
    {0x1CF00, 0x1CF2D}, {0x1CF30, 0x1CF46}, {0x1CF50, 0x1CFC3}, {0x1D000, 0x1D0F5},
    {0x1D100, 0x1D126}, {0x1D129, 0x1D172}, {0x1D17B, 0x1D1EA}, {0x1D200, 0x1D245},
    {0x1D2E0, 0x1D2F3}, {0x1D300, 0x1D356}, {0x1D360, 0x1D378}, {0x1D400, 0x1D454},
    {0x1D456, 0x1D49C}, {0x1D4A9, 0x1D4AC}, {0x1D4AE, 0x1D4B9}, {0x1D4BD, 0x1D4C3},
    {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514}, {0x1D516, 0x1D51C},
    {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E}, {0x1D540, 0x1D544}, {0x1D54A, 0x1D550},
    {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D7CB}, {0x1D7CE, 0x1DA8B}, {0x1DA9B, 0x1DA9F},

    {0x1DAA1, 0x1DAAF}, {0x1DF00, 0x1DF1E}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018},
    {0x1E01B, 0x1E021}, {0x1E026, 0x1E02A}, {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D},
    {0x1E140, 0x1E149}, {0x1E290, 0x1E2AE}, {0x1E2C0, 0x1E2F9}, {0x1E7E0, 0x1E7E6},
    {0x1E7E8, 0x1E7EB}, {0x1E7F0, 0x1E7FE}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6},
    {0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, {0x1EC71, 0x1ECB4}, {0x1ED01, 0x1ED3D},
    {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F}, {0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37},
    {0x1EE4D, 0x1EE4F}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72}, {0x1EE74, 0x1EE77},
    {0x1EE79, 0x1EE7C}, {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3},
    {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x1F000, 0x1F02B}, {0x1F030, 0x1F093},
    {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CF}, {0x1F0D1, 0x1F0F5},
    {0x1F100, 0x1F1AD}, {0x1F1E6, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248},
    {0x1F260, 0x1F265}, {0x1F300, 0x1F6D7}, {0x1F6DD, 0x1F6EC}, {0x1F6F0, 0x1F6FC},
    {0x1F700, 0x1F773}, {0x1F780, 0x1F7D8}, {0x1F7E0, 0x1F7EB}, {0x1F800, 0x1F80B},
    {0x1F810, 0x1F847}, {0x1F850, 0x1F859}, {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD},
    {0x1F900, 0x1FA53}, {0x1FA60, 0x1FA6D}, {0x1FA70, 0x1FA74}, {0x1FA78, 0x1FA7C},
    {0x1FA80, 0x1FA86}, {0x1FA90, 0x1FAAC}, {0x1FAB0, 0x1FABA}, {0x1FAC0, 0x1FAC5},
    {0x1FAD0, 0x1FAD9}, {0x1FAE0, 0x1FAE7}, {0x1FAF0, 0x1FAF6}, {0x1FB00, 0x1FB92},
    {0x1FB94, 0x1FBCA}, {0x1FBF0, 0x1FBF9}, {0x20000, 0x2A6DF}, {0x2A700, 0x2B738},
    {0x2B740, 0x2B81D}, {0x2B820, 0x2CEA1}, {0x2CEB0, 0x2EBE0}, {0x2F800, 0x2FA1D},
    {0x30000, 0x3134A}, {0xE0100, 0xE01EF}
#endif
};

#define NUM_GRAPH_RANGE (sizeof(graphRangeTable)/sizeof(crange))

static const chr graphCharTable[] = {
    0x38C, 0x85E, 0x98F, 0x990, 0x9B2, 0x9C7, 0x9C8, 0x9D7, 0x9DC,
    0x9DD, 0xA0F, 0xA10, 0xA32, 0xA33, 0xA35, 0xA36, 0xA38, 0xA39,
    0xA3C, 0xA47, 0xA48, 0xA51, 0xA5E, 0xAB2, 0xAB3, 0xAD0, 0xB0F,
    0xB10, 0xB32, 0xB33, 0xB47, 0xB48, 0xB5C, 0xB5D, 0xB82, 0xB83,
    0xB99, 0xB9A, 0xB9C, 0xB9E, 0xB9F, 0xBA3, 0xBA4, 0xBD0, 0xBD7,
    0xC55, 0xC56, 0xC5D, 0xCD5, 0xCD6, 0xCDD, 0xCDE, 0xCF1, 0xCF2,
    0xDBD, 0xDCA, 0xDD6, 0xE81, 0xE82, 0xE84, 0xEA5, 0xEC6, 0x10C7,
    0x10CD, 0x1258, 0x12C0, 0x1772, 0x1773, 0x1940, 0x1F59, 0x1F5B, 0x1F5D,
    0x2070, 0x2071, 0x2D27, 0x2D2D, 0x2D6F, 0x2D70, 0xA7D0, 0xA7D1, 0xA7D3,
    0xFB3E, 0xFB40, 0xFB41, 0xFB43, 0xFB44, 0xFDCF, 0xFFFC, 0xFFFD
#if CHRBITS > 16
    ,0x1003C, 0x1003D, 0x101A0, 0x10594, 0x10595, 0x105BB, 0x105BC, 0x10808, 0x10837,
    0x10838, 0x1083C, 0x108F4, 0x108F5, 0x1093F, 0x10A05, 0x10A06, 0x10EB0, 0x10EB1,
    0x11288, 0x1130F, 0x11310, 0x11332, 0x11333, 0x11347, 0x11348, 0x11350, 0x11357,
    0x11909, 0x11915, 0x11916, 0x11937, 0x11938, 0x11D08, 0x11D09, 0x11D3A, 0x11D3C,
    0x11D3D, 0x11D67, 0x11D68, 0x11D90, 0x11D91, 0x11FB0, 0x16FF0, 0x16FF1, 0x1AFFD,
    0x1AFFE, 0x1D49E, 0x1D49F, 0x1D4A2, 0x1D4A5, 0x1D4A6, 0x1D4BB, 0x1D546, 0x1E023,
    0x1E024, 0x1E14E, 0x1E14F, 0x1E2FF, 0x1E7ED, 0x1E7EE, 0x1E95E, 0x1E95F, 0x1EE21,
    0x1EE22, 0x1EE24, 0x1EE27, 0x1EE39, 0x1EE3B, 0x1EE42, 0x1EE47, 0x1EE49, 0x1EE4B,
    0x1EE51, 0x1EE52, 0x1EE54, 0x1EE57, 0x1EE59, 0x1EE5B, 0x1EE5D, 0x1EE5F, 0x1EE61,
    0x1EE62, 0x1EE64, 0x1EE7E, 0x1EEF0, 0x1EEF1, 0x1F250, 0x1F251, 0x1F7F0, 0x1F8B0,
    0x1F8B1
#endif
};

#define NUM_GRAPH_CHAR (sizeof(graphCharTable)/sizeof(chr))

/*
 *	End of auto-generated Unicode character ranges declarations.







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



















|



|










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











|
|
|
|
|






|
|
|
|
|
|







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
    {0xB85, 0xB8A}, {0xB8E, 0xB90}, {0xB92, 0xB95}, {0xBA8, 0xBAA},
    {0xBAE, 0xBB9}, {0xBBE, 0xBC2}, {0xBC6, 0xBC8}, {0xBCA, 0xBCD},
    {0xBE6, 0xBFA}, {0xC00, 0xC0C}, {0xC0E, 0xC10}, {0xC12, 0xC28},
    {0xC2A, 0xC39}, {0xC3C, 0xC44}, {0xC46, 0xC48}, {0xC4A, 0xC4D},
    {0xC58, 0xC5A}, {0xC60, 0xC63}, {0xC66, 0xC6F}, {0xC77, 0xC8C},
    {0xC8E, 0xC90}, {0xC92, 0xCA8}, {0xCAA, 0xCB3}, {0xCB5, 0xCB9},
    {0xCBC, 0xCC4}, {0xCC6, 0xCC8}, {0xCCA, 0xCCD}, {0xCE0, 0xCE3},
    {0xCE6, 0xCEF}, {0xCF1, 0xCF3}, {0xD00, 0xD0C}, {0xD0E, 0xD10},
    {0xD12, 0xD44}, {0xD46, 0xD48}, {0xD4A, 0xD4F}, {0xD54, 0xD63},
    {0xD66, 0xD7F}, {0xD81, 0xD83}, {0xD85, 0xD96}, {0xD9A, 0xDB1},
    {0xDB3, 0xDBB}, {0xDC0, 0xDC6}, {0xDCF, 0xDD4}, {0xDD8, 0xDDF},
    {0xDE6, 0xDEF}, {0xDF2, 0xDF4}, {0xE01, 0xE3A}, {0xE3F, 0xE5B},
    {0xE86, 0xE8A}, {0xE8C, 0xEA3}, {0xEA7, 0xEBD}, {0xEC0, 0xEC4},
    {0xEC8, 0xECE}, {0xED0, 0xED9}, {0xEDC, 0xEDF}, {0xF00, 0xF47},
    {0xF49, 0xF6C}, {0xF71, 0xF97}, {0xF99, 0xFBC}, {0xFBE, 0xFCC},
    {0xFCE, 0xFDA}, {0x1000, 0x10C5}, {0x10D0, 0x1248}, {0x124A, 0x124D},
    {0x1250, 0x1256}, {0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D},
    {0x1290, 0x12B0}, {0x12B2, 0x12B5}, {0x12B8, 0x12BE}, {0x12C2, 0x12C5},
    {0x12C8, 0x12D6}, {0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A},
    {0x135D, 0x137C}, {0x1380, 0x1399}, {0x13A0, 0x13F5}, {0x13F8, 0x13FD},
    {0x1400, 0x167F}, {0x1681, 0x169C}, {0x16A0, 0x16F8}, {0x1700, 0x1715},
    {0x171F, 0x1736}, {0x1740, 0x1753}, {0x1760, 0x176C}, {0x176E, 0x1770},
    {0x1780, 0x17DD}, {0x17E0, 0x17E9}, {0x17F0, 0x17F9}, {0x1800, 0x180D},
    {0x180F, 0x1819}, {0x1820, 0x1878}, {0x1880, 0x18AA}, {0x18B0, 0x18F5},
    {0x1900, 0x191E}, {0x1920, 0x192B}, {0x1930, 0x193B}, {0x1944, 0x196D},
    {0x1970, 0x1974}, {0x1980, 0x19AB}, {0x19B0, 0x19C9}, {0x19D0, 0x19DA},
    {0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E}, {0x1A60, 0x1A7C}, {0x1A7F, 0x1A89},
    {0x1A90, 0x1A99}, {0x1AA0, 0x1AAD}, {0x1AB0, 0x1ACE}, {0x1B00, 0x1B4C},
    {0x1B50, 0x1B7E}, {0x1B80, 0x1BF3}, {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49},
    {0x1C4D, 0x1C88}, {0x1C90, 0x1CBA}, {0x1CBD, 0x1CC7}, {0x1CD0, 0x1CFA},
    {0x1D00, 0x1F15}, {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D},
    {0x1F50, 0x1F57}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, {0x1FB6, 0x1FC4},
    {0x1FC6, 0x1FD3}, {0x1FD6, 0x1FDB}, {0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4},
    {0x1FF6, 0x1FFE}, {0x2010, 0x2027}, {0x2030, 0x205E}, {0x2074, 0x208E},
    {0x2090, 0x209C}, {0x20A0, 0x20C0}, {0x20D0, 0x20F0}, {0x2100, 0x218B},
    {0x2190, 0x2426}, {0x2440, 0x244A}, {0x2460, 0x2B73}, {0x2B76, 0x2B95},
    {0x2B97, 0x2CF3}, {0x2CF9, 0x2D25}, {0x2D30, 0x2D67}, {0x2D7F, 0x2D96},
    {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE},
    {0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE},
    {0x2DE0, 0x2E5D}, {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5},
    {0x2FF0, 0x2FFB}, {0x3001, 0x303F}, {0x3041, 0x3096}, {0x3099, 0x30FF},
    {0x3105, 0x312F}, {0x3131, 0x318E}, {0x3190, 0x31E3}, {0x31F0, 0x321E},
    {0x3220, 0xA48C}, {0xA490, 0xA4C6}, {0xA4D0, 0xA62B}, {0xA640, 0xA6F7},
    {0xA700, 0xA7CA}, {0xA7D5, 0xA7D9}, {0xA7F2, 0xA82C}, {0xA830, 0xA839},
    {0xA840, 0xA877}, {0xA880, 0xA8C5}, {0xA8CE, 0xA8D9}, {0xA8E0, 0xA953},
    {0xA95F, 0xA97C}, {0xA980, 0xA9CD}, {0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE},
    {0xAA00, 0xAA36}, {0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, {0xAA5C, 0xAAC2},
    {0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, {0xAB11, 0xAB16},
    {0xAB20, 0xAB26}, {0xAB28, 0xAB2E}, {0xAB30, 0xAB6B}, {0xAB70, 0xABED},
    {0xABF0, 0xABF9}, {0xAC00, 0xD7A3}, {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB},
    {0xF900, 0xFA6D}, {0xFA70, 0xFAD9}, {0xFB00, 0xFB06}, {0xFB13, 0xFB17},
    {0xFB1D, 0xFB36}, {0xFB38, 0xFB3C}, {0xFB46, 0xFBC2}, {0xFBD3, 0xFD8F},
    {0xFD92, 0xFDC7}, {0xFDF0, 0xFE19}, {0xFE20, 0xFE52}, {0xFE54, 0xFE66},
    {0xFE68, 0xFE6B}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC}, {0xFF01, 0xFFBE},
    {0xFFC2, 0xFFC7}, {0xFFCA, 0xFFCF}, {0xFFD2, 0xFFD7}, {0xFFDA, 0xFFDC},
    {0xFFE0, 0xFFE6}, {0xFFE8, 0xFFEE}
#if CHRBITS > 16
    ,{0x10000, 0x1000B}, {0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003F, 0x1004D},
    {0x10050, 0x1005D}, {0x10080, 0x100FA}, {0x10100, 0x10102}, {0x10107, 0x10133},
    {0x10137, 0x1018E}, {0x10190, 0x1019C}, {0x101D0, 0x101FD}, {0x10280, 0x1029C},
    {0x102A0, 0x102D0}, {0x102E0, 0x102FB}, {0x10300, 0x10323}, {0x1032D, 0x1034A},
    {0x10350, 0x1037A}, {0x10380, 0x1039D}, {0x1039F, 0x103C3}, {0x103C8, 0x103D5},
    {0x10400, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3}, {0x104D8, 0x104FB},
    {0x10500, 0x10527}, {0x10530, 0x10563}, {0x1056F, 0x1057A}, {0x1057C, 0x1058A},
    {0x1058C, 0x10592}, {0x10597, 0x105A1}, {0x105A3, 0x105B1}, {0x105B3, 0x105B9},
    {0x10600, 0x10736}, {0x10740, 0x10755}, {0x10760, 0x10767}, {0x10780, 0x10785},
    {0x10787, 0x107B0}, {0x107B2, 0x107BA}, {0x10800, 0x10805}, {0x1080A, 0x10835},
    {0x1083F, 0x10855}, {0x10857, 0x1089E}, {0x108A7, 0x108AF}, {0x108E0, 0x108F2},
    {0x108FB, 0x1091B}, {0x1091F, 0x10939}, {0x10980, 0x109B7}, {0x109BC, 0x109CF},
    {0x109D2, 0x10A03}, {0x10A0C, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A35},
    {0x10A38, 0x10A3A}, {0x10A3F, 0x10A48}, {0x10A50, 0x10A58}, {0x10A60, 0x10A9F},
    {0x10AC0, 0x10AE6}, {0x10AEB, 0x10AF6}, {0x10B00, 0x10B35}, {0x10B39, 0x10B55},
    {0x10B58, 0x10B72}, {0x10B78, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF},
    {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2}, {0x10CFA, 0x10D27},
    {0x10D30, 0x10D39}, {0x10E60, 0x10E7E}, {0x10E80, 0x10EA9}, {0x10EAB, 0x10EAD},
    {0x10EFD, 0x10F27}, {0x10F30, 0x10F59}, {0x10F70, 0x10F89}, {0x10FB0, 0x10FCB},
    {0x10FE0, 0x10FF6}, {0x11000, 0x1104D}, {0x11052, 0x11075}, {0x1107F, 0x110BC},
    {0x110BE, 0x110C2}, {0x110D0, 0x110E8}, {0x110F0, 0x110F9}, {0x11100, 0x11134},
    {0x11136, 0x11147}, {0x11150, 0x11176}, {0x11180, 0x111DF}, {0x111E1, 0x111F4},
    {0x11200, 0x11211}, {0x11213, 0x11241}, {0x11280, 0x11286}, {0x1128A, 0x1128D},
    {0x1128F, 0x1129D}, {0x1129F, 0x112A9}, {0x112B0, 0x112EA}, {0x112F0, 0x112F9},
    {0x11300, 0x11303}, {0x11305, 0x1130C}, {0x11313, 0x11328}, {0x1132A, 0x11330},
    {0x11335, 0x11339}, {0x1133B, 0x11344}, {0x1134B, 0x1134D}, {0x1135D, 0x11363},
    {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x11400, 0x1145B}, {0x1145D, 0x11461},
    {0x11480, 0x114C7}, {0x114D0, 0x114D9}, {0x11580, 0x115B5}, {0x115B8, 0x115DD},
    {0x11600, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C}, {0x11680, 0x116B9},
    {0x116C0, 0x116C9}, {0x11700, 0x1171A}, {0x1171D, 0x1172B}, {0x11730, 0x11746},
    {0x11800, 0x1183B}, {0x118A0, 0x118F2}, {0x118FF, 0x11906}, {0x1190C, 0x11913},
    {0x11918, 0x11935}, {0x1193B, 0x11946}, {0x11950, 0x11959}, {0x119A0, 0x119A7},
    {0x119AA, 0x119D7}, {0x119DA, 0x119E4}, {0x11A00, 0x11A47}, {0x11A50, 0x11AA2},
    {0x11AB0, 0x11AF8}, {0x11B00, 0x11B09}, {0x11C00, 0x11C08}, {0x11C0A, 0x11C36},
    {0x11C38, 0x11C45}, {0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, {0x11C92, 0x11CA7},
    {0x11CA9, 0x11CB6}, {0x11D00, 0x11D06}, {0x11D0B, 0x11D36}, {0x11D3F, 0x11D47},
    {0x11D50, 0x11D59}, {0x11D60, 0x11D65}, {0x11D6A, 0x11D8E}, {0x11D93, 0x11D98},
    {0x11DA0, 0x11DA9}, {0x11EE0, 0x11EF8}, {0x11F00, 0x11F10}, {0x11F12, 0x11F3A},
    {0x11F3E, 0x11F59}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399}, {0x12400, 0x1246E},
    {0x12470, 0x12474}, {0x12480, 0x12543}, {0x12F90, 0x12FF2}, {0x13000, 0x1342F},
    {0x13440, 0x13455}, {0x14400, 0x14646}, {0x16800, 0x16A38}, {0x16A40, 0x16A5E},
    {0x16A60, 0x16A69}, {0x16A6E, 0x16ABE}, {0x16AC0, 0x16AC9}, {0x16AD0, 0x16AED},
    {0x16AF0, 0x16AF5}, {0x16B00, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61},
    {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16E40, 0x16E9A}, {0x16F00, 0x16F4A},
    {0x16F4F, 0x16F87}, {0x16F8F, 0x16F9F}, {0x16FE0, 0x16FE4}, {0x17000, 0x187F7},
    {0x18800, 0x18CD5}, {0x18D00, 0x18D08}, {0x1AFF0, 0x1AFF3}, {0x1AFF5, 0x1AFFB},
    {0x1B000, 0x1B122}, {0x1B150, 0x1B152}, {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB},
    {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99},
    {0x1BC9C, 0x1BC9F}, {0x1CF00, 0x1CF2D}, {0x1CF30, 0x1CF46}, {0x1CF50, 0x1CFC3},
    {0x1D000, 0x1D0F5}, {0x1D100, 0x1D126}, {0x1D129, 0x1D172}, {0x1D17B, 0x1D1EA},
    {0x1D200, 0x1D245}, {0x1D2C0, 0x1D2D3}, {0x1D2E0, 0x1D2F3}, {0x1D300, 0x1D356},
    {0x1D360, 0x1D378}, {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, {0x1D4A9, 0x1D4AC},
    {0x1D4AE, 0x1D4B9}, {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A},
    {0x1D50D, 0x1D514}, {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E},
    {0x1D540, 0x1D544}, {0x1D54A, 0x1D550}, {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D7CB},
    {0x1D7CE, 0x1DA8B}, {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1DF00, 0x1DF1E},
    {0x1DF25, 0x1DF2A}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, {0x1E01B, 0x1E021},
    {0x1E026, 0x1E02A}, {0x1E030, 0x1E06D}, {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D},
    {0x1E140, 0x1E149}, {0x1E290, 0x1E2AE}, {0x1E2C0, 0x1E2F9}, {0x1E4D0, 0x1E4F9},
    {0x1E7E0, 0x1E7E6}, {0x1E7E8, 0x1E7EB}, {0x1E7F0, 0x1E7FE}, {0x1E800, 0x1E8C4},
    {0x1E8C7, 0x1E8D6}, {0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, {0x1EC71, 0x1ECB4},
    {0x1ED01, 0x1ED3D}, {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F}, {0x1EE29, 0x1EE32},
    {0x1EE34, 0x1EE37}, {0x1EE4D, 0x1EE4F}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72},
    {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B},
    {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x1F000, 0x1F02B},
    {0x1F030, 0x1F093}, {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CF},
    {0x1F0D1, 0x1F0F5}, {0x1F100, 0x1F1AD}, {0x1F1E6, 0x1F202}, {0x1F210, 0x1F23B},
    {0x1F240, 0x1F248}, {0x1F260, 0x1F265}, {0x1F300, 0x1F6D7}, {0x1F6DC, 0x1F6EC},
    {0x1F6F0, 0x1F6FC}, {0x1F700, 0x1F776}, {0x1F77B, 0x1F7D9}, {0x1F7E0, 0x1F7EB},
    {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, {0x1F850, 0x1F859}, {0x1F860, 0x1F887},
    {0x1F890, 0x1F8AD}, {0x1F900, 0x1FA53}, {0x1FA60, 0x1FA6D}, {0x1FA70, 0x1FA7C},
    {0x1FA80, 0x1FA88}, {0x1FA90, 0x1FABD}, {0x1FABF, 0x1FAC5}, {0x1FACE, 0x1FADB},
    {0x1FAE0, 0x1FAE8}, {0x1FAF0, 0x1FAF8}, {0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBCA},
    {0x1FBF0, 0x1FBF9}, {0x20000, 0x2A6DF}, {0x2A700, 0x2B739}, {0x2B740, 0x2B81D},
    {0x2B820, 0x2CEA1}, {0x2CEB0, 0x2EBE0}, {0x2F800, 0x2FA1D}, {0x30000, 0x3134A},
    {0x31350, 0x323AF}, {0xE0100, 0xE01EF}
#endif
};

#define NUM_GRAPH_RANGE (sizeof(graphRangeTable)/sizeof(crange))

static const chr graphCharTable[] = {
    0x38C, 0x85E, 0x98F, 0x990, 0x9B2, 0x9C7, 0x9C8, 0x9D7, 0x9DC,
    0x9DD, 0xA0F, 0xA10, 0xA32, 0xA33, 0xA35, 0xA36, 0xA38, 0xA39,
    0xA3C, 0xA47, 0xA48, 0xA51, 0xA5E, 0xAB2, 0xAB3, 0xAD0, 0xB0F,
    0xB10, 0xB32, 0xB33, 0xB47, 0xB48, 0xB5C, 0xB5D, 0xB82, 0xB83,
    0xB99, 0xB9A, 0xB9C, 0xB9E, 0xB9F, 0xBA3, 0xBA4, 0xBD0, 0xBD7,
    0xC55, 0xC56, 0xC5D, 0xCD5, 0xCD6, 0xCDD, 0xCDE, 0xDBD, 0xDCA,
    0xDD6, 0xE81, 0xE82, 0xE84, 0xEA5, 0xEC6, 0x10C7, 0x10CD, 0x1258,
    0x12C0, 0x1772, 0x1773, 0x1940, 0x1F59, 0x1F5B, 0x1F5D, 0x2070, 0x2071,
    0x2D27, 0x2D2D, 0x2D6F, 0x2D70, 0xA7D0, 0xA7D1, 0xA7D3, 0xFB3E, 0xFB40,
    0xFB41, 0xFB43, 0xFB44, 0xFDCF, 0xFFFC, 0xFFFD
#if CHRBITS > 16
    ,0x1003C, 0x1003D, 0x101A0, 0x10594, 0x10595, 0x105BB, 0x105BC, 0x10808, 0x10837,
    0x10838, 0x1083C, 0x108F4, 0x108F5, 0x1093F, 0x10A05, 0x10A06, 0x10EB0, 0x10EB1,
    0x11288, 0x1130F, 0x11310, 0x11332, 0x11333, 0x11347, 0x11348, 0x11350, 0x11357,
    0x11909, 0x11915, 0x11916, 0x11937, 0x11938, 0x11D08, 0x11D09, 0x11D3A, 0x11D3C,
    0x11D3D, 0x11D67, 0x11D68, 0x11D90, 0x11D91, 0x11FB0, 0x16FF0, 0x16FF1, 0x1AFFD,
    0x1AFFE, 0x1B132, 0x1B155, 0x1D49E, 0x1D49F, 0x1D4A2, 0x1D4A5, 0x1D4A6, 0x1D4BB,
    0x1D546, 0x1E023, 0x1E024, 0x1E08F, 0x1E14E, 0x1E14F, 0x1E2FF, 0x1E7ED, 0x1E7EE,
    0x1E95E, 0x1E95F, 0x1EE21, 0x1EE22, 0x1EE24, 0x1EE27, 0x1EE39, 0x1EE3B, 0x1EE42,
    0x1EE47, 0x1EE49, 0x1EE4B, 0x1EE51, 0x1EE52, 0x1EE54, 0x1EE57, 0x1EE59, 0x1EE5B,
    0x1EE5D, 0x1EE5F, 0x1EE61, 0x1EE62, 0x1EE64, 0x1EE7E, 0x1EEF0, 0x1EEF1, 0x1F250,
    0x1F251, 0x1F7F0, 0x1F8B0, 0x1F8B1
#endif
};

#define NUM_GRAPH_CHAR (sizeof(graphCharTable)/sizeof(chr))

/*
 *	End of auto-generated Unicode character ranges declarations.
Changes to generic/tcl.decls.
698
699
700
701
702
703
704

705
706
707

708
709
710
711
712
713
714
#  declare 188 {
#	Tcl_MainLoop
#  }

declare 189 {
    Tcl_Channel Tcl_MakeFileChannel(void *handle, int mode)
}

declare 190 {
    int Tcl_MakeSafe(Tcl_Interp *interp)
}

declare 191 {
    Tcl_Channel Tcl_MakeTcpClientChannel(void *tcpSocket)
}
declare 192 {
    char *Tcl_Merge(size_t argc, const char *const *argv)
}
declare 193 {







>
|
|
<
>







698
699
700
701
702
703
704
705
706
707

708
709
710
711
712
713
714
715
#  declare 188 {
#	Tcl_MainLoop
#  }

declare 189 {
    Tcl_Channel Tcl_MakeFileChannel(void *handle, int mode)
}
# Removed in 9.0
#declare 190 {
#    int Tcl_MakeSafe(Tcl_Interp *interp)

#}
declare 191 {
    Tcl_Channel Tcl_MakeTcpClientChannel(void *tcpSocket)
}
declare 192 {
    char *Tcl_Merge(size_t argc, const char *const *argv)
}
declare 193 {
Changes to generic/tcl.h.
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
#else
    typedef struct stat Tcl_StatBuf;
#endif

/*
 *----------------------------------------------------------------------------
 * Data structures defined opaquely in this module. The definitions below just
 * provide dummy types. A few fields are made visible in Tcl_Interp
 * structures, namely those used for returning a string result from commands.
 * Direct access to the result field is discouraged in Tcl 8.0. The
 * interpreter result is either an object or a string, and the two values are
 * kept consistent unless some C code sets interp->result directly.
 * Programmers should use either the function Tcl_GetObjResult() or
 * Tcl_GetStringResult() to read the interpreter's result. See the SetResult
 * man page for details.
 *
 * Note: any change to the Tcl_Interp definition below must be mirrored in the
 * "real" definition in tclInt.h.
 *
 * Note: Tcl_ObjCmdProc functions do not directly set result and freeProc.
 * Instead, they set a Tcl_Obj member in the "real" structure that can be
 * accessed with Tcl_GetObjResult() and Tcl_SetObjResult().
 */

typedef struct Tcl_Interp Tcl_Interp;

typedef struct Tcl_AsyncHandler_ *Tcl_AsyncHandler;
typedef struct Tcl_Channel_ *Tcl_Channel;
typedef struct Tcl_ChannelTypeVersion_ *Tcl_ChannelTypeVersion;
typedef struct Tcl_Command_ *Tcl_Command;
typedef struct Tcl_Condition_ *Tcl_Condition;
typedef struct Tcl_Dict_ *Tcl_Dict;
typedef struct Tcl_EncodingState_ *Tcl_EncodingState;
typedef struct Tcl_Encoding_ *Tcl_Encoding;
typedef struct Tcl_Event Tcl_Event;

typedef struct Tcl_InterpState_ *Tcl_InterpState;
typedef struct Tcl_LoadHandle_ *Tcl_LoadHandle;
typedef struct Tcl_Mutex_ *Tcl_Mutex;
typedef struct Tcl_Pid_ *Tcl_Pid;
typedef struct Tcl_RegExp_ *Tcl_RegExp;
typedef struct Tcl_ThreadDataKey_ *Tcl_ThreadDataKey;
typedef struct Tcl_ThreadId_ *Tcl_ThreadId;







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


<
<









>







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
#else
    typedef struct stat Tcl_StatBuf;
#endif

/*
 *----------------------------------------------------------------------------
 * Data structures defined opaquely in this module. The definitions below just
 * provide dummy types. 














 */



typedef struct Tcl_AsyncHandler_ *Tcl_AsyncHandler;
typedef struct Tcl_Channel_ *Tcl_Channel;
typedef struct Tcl_ChannelTypeVersion_ *Tcl_ChannelTypeVersion;
typedef struct Tcl_Command_ *Tcl_Command;
typedef struct Tcl_Condition_ *Tcl_Condition;
typedef struct Tcl_Dict_ *Tcl_Dict;
typedef struct Tcl_EncodingState_ *Tcl_EncodingState;
typedef struct Tcl_Encoding_ *Tcl_Encoding;
typedef struct Tcl_Event Tcl_Event;
typedef struct Tcl_Interp Tcl_Interp;
typedef struct Tcl_InterpState_ *Tcl_InterpState;
typedef struct Tcl_LoadHandle_ *Tcl_LoadHandle;
typedef struct Tcl_Mutex_ *Tcl_Mutex;
typedef struct Tcl_Pid_ *Tcl_Pid;
typedef struct Tcl_RegExp_ *Tcl_RegExp;
typedef struct Tcl_ThreadDataKey_ *Tcl_ThreadDataKey;
typedef struct Tcl_ThreadId_ *Tcl_ThreadId;
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
	int level, char *command, Tcl_CmdProc *proc,
	void *cmdClientData, int argc, const char *argv[]);
typedef int (Tcl_CmdObjTraceProc) (void *clientData, Tcl_Interp *interp,
	int level, const char *command, Tcl_Command commandInfo, int objc,
	struct Tcl_Obj *const *objv);
typedef int (Tcl_CmdObjTraceProc2) (void *clientData, Tcl_Interp *interp,
	int level, const char *command, Tcl_Command commandInfo, size_t objc,
	struct Tcl_Obj *const objv[]);
typedef void (Tcl_CmdObjTraceDeleteProc) (void *clientData);
typedef void (Tcl_DupInternalRepProc) (struct Tcl_Obj *srcPtr,
	struct Tcl_Obj *dupPtr);
typedef int (Tcl_EncodingConvertProc) (void *clientData, const char *src,
	int srcLen, int flags, Tcl_EncodingState *statePtr, char *dst,
	int dstLen, int *srcReadPtr, int *dstWrotePtr, int *dstCharsPtr);
#define Tcl_EncodingFreeProc Tcl_FreeProc







|







538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
	int level, char *command, Tcl_CmdProc *proc,
	void *cmdClientData, int argc, const char *argv[]);
typedef int (Tcl_CmdObjTraceProc) (void *clientData, Tcl_Interp *interp,
	int level, const char *command, Tcl_Command commandInfo, int objc,
	struct Tcl_Obj *const *objv);
typedef int (Tcl_CmdObjTraceProc2) (void *clientData, Tcl_Interp *interp,
	int level, const char *command, Tcl_Command commandInfo, size_t objc,
	struct Tcl_Obj *const *objv);
typedef void (Tcl_CmdObjTraceDeleteProc) (void *clientData);
typedef void (Tcl_DupInternalRepProc) (struct Tcl_Obj *srcPtr,
	struct Tcl_Obj *dupPtr);
typedef int (Tcl_EncodingConvertProc) (void *clientData, const char *src,
	int srcLen, int flags, Tcl_EncodingState *statePtr, char *dst,
	int dstLen, int *srcReadPtr, int *dstWrotePtr, int *dstCharsPtr);
#define Tcl_EncodingFreeProc Tcl_FreeProc
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
 * Tcl_CreateCommand. The other function is typically set to a compatibility
 * wrapper that does string-to-object or object-to-string argument conversions
 * then calls the other function.
 */

typedef struct Tcl_CmdInfo {
    int isNativeObjectProc;	/* 1 if objProc was registered by a call to
				 * Tcl_CreateObjCommand; 0 otherwise.

				 * Tcl_SetCmdInfo does not modify this
				 * field. */
    Tcl_ObjCmdProc *objProc;	/* Command's object-based function. */
    void *objClientData;	/* ClientData for object proc. */
    Tcl_CmdProc *proc;		/* Command's string-based function. */
    void *clientData;	/* ClientData for string proc. */
    Tcl_CmdDeleteProc *deleteProc;
				/* Function to call when command is
				 * deleted. */
    void *deleteData;	/* Value to pass to deleteProc (usually the
				 * same as clientData). */
    Tcl_Namespace *namespacePtr;/* Points to the namespace that contains this
				 * command. Note that Tcl_SetCmdInfo will not
				 * change a command's namespace; use
				 * TclRenameCommand or Tcl_Eval (of 'rename')
				 * to do that. */
    Tcl_ObjCmdProc2 *objProc2;	/* Not used in Tcl 8.7. */
    void *objClientData2;	/* Not used in Tcl 8.7. */
} Tcl_CmdInfo;

/*
 *----------------------------------------------------------------------------
 * The structure defined below is used to hold dynamic strings. The only
 * fields that clients should use are string and length, accessible via the
 * macros Tcl_DStringValue and Tcl_DStringLength.







|
>
|
<














|
|







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
 * Tcl_CreateCommand. The other function is typically set to a compatibility
 * wrapper that does string-to-object or object-to-string argument conversions
 * then calls the other function.
 */

typedef struct Tcl_CmdInfo {
    int isNativeObjectProc;	/* 1 if objProc was registered by a call to
				 * Tcl_CreateObjCommand; 2 if objProc was registered by
				 * a call to Tcl_CreateObjCommand2; 0 otherwise.
				 * Tcl_SetCmdInfo does not modify this field. */

    Tcl_ObjCmdProc *objProc;	/* Command's object-based function. */
    void *objClientData;	/* ClientData for object proc. */
    Tcl_CmdProc *proc;		/* Command's string-based function. */
    void *clientData;	/* ClientData for string proc. */
    Tcl_CmdDeleteProc *deleteProc;
				/* Function to call when command is
				 * deleted. */
    void *deleteData;	/* Value to pass to deleteProc (usually the
				 * same as clientData). */
    Tcl_Namespace *namespacePtr;/* Points to the namespace that contains this
				 * command. Note that Tcl_SetCmdInfo will not
				 * change a command's namespace; use
				 * TclRenameCommand or Tcl_Eval (of 'rename')
				 * to do that. */
    Tcl_ObjCmdProc2 *objProc2;	/* Command's object2-based function. */
    void *objClientData2;	/* ClientData for object2 proc. */
} Tcl_CmdInfo;

/*
 *----------------------------------------------------------------------------
 * The structure defined below is used to hold dynamic strings. The only
 * fields that clients should use are string and length, accessible via the
 * macros Tcl_DStringValue and Tcl_DStringLength.
Added generic/tclArithSeries.c.
















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































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

#include "tclInt.h"
#include "tclArithSeries.h"
#include <assert.h>

/* -------------------------- ArithSeries object ---------------------------- */


#define ArithSeriesRepPtr(arithSeriesObjPtr) \
    (ArithSeries *) ((arithSeriesObjPtr)->internalRep.twoPtrValue.ptr1)

#define ArithSeriesIndexM(arithSeriesRepPtr, index) \
    ((arithSeriesRepPtr)->isDouble ?					\
     (((ArithSeriesDbl*)(arithSeriesRepPtr))->start+((index) * ((ArithSeriesDbl*)(arithSeriesRepPtr))->step)) \
     :									\
     ((arithSeriesRepPtr)->start+((index) * arithSeriesRepPtr->step)))

#define ArithSeriesGetInternalRep(objPtr, arithRepPtr)		\
    do {								\
	const Tcl_ObjInternalRep *irPtr;				\
	irPtr = TclFetchInternalRep((objPtr), &tclArithSeriesType);	\
	(arithRepPtr) = irPtr ? (ArithSeries *)irPtr->twoPtrValue.ptr1 : NULL;	\
    } while (0)


/*
 * Prototypes for procedures defined later in this file:
 */

static void		DupArithSeriesInternalRep (Tcl_Obj *srcPtr, Tcl_Obj *copyPtr);
static void		FreeArithSeriesInternalRep (Tcl_Obj *listPtr);
static int		SetArithSeriesFromAny (Tcl_Interp *interp, Tcl_Obj *objPtr);
static void		UpdateStringOfArithSeries (Tcl_Obj *listPtr);

/*
 * The structure below defines the arithmetic series Tcl object type by
 * means of procedures that can be invoked by generic object code.
 *
 * The arithmetic series object is a special case of Tcl list representing
 * an interval of an arithmetic series in constant space.
 *
 * The arithmetic series is internally represented with three integers,
 * *start*, *end*, and *step*, Where the length is calculated with
 * the following algorithm:
 *
 * if RANGE == 0 THEN
 *   ERROR
 * if RANGE > 0
 *   LEN is (((END-START)-1)/STEP) + 1
 * else if RANGE < 0
 *   LEN is (((END-START)-1)/STEP) - 1
 *
 * And where the equivalent's list I-th element is calculated
 * as:
 *
 * LIST[i] = START+(STEP*i)
 *
 * Zero elements ranges, like in the case of START=10 END=10 STEP=1
 * are valid and will be equivalent to the empty list.
 */

const Tcl_ObjType tclArithSeriesType = {
    "arithseries",			/* name */
    FreeArithSeriesInternalRep,		/* freeIntRepProc */
    DupArithSeriesInternalRep,		/* dupIntRepProc */
    UpdateStringOfArithSeries,		/* updateStringProc */
    SetArithSeriesFromAny		/* setFromAnyProc */
};

/*
 *----------------------------------------------------------------------
 *
 * ArithSeriesLen --
 *
 * 	Compute the length of the equivalent list where
 * 	every element is generated starting from *start*,
 * 	and adding *step* to generate every successive element
 * 	that's < *end* for positive steps, or > *end* for negative
 * 	steps.
 *
 * Results:
 *
 * 	The length of the list generated by the given range,
 * 	that may be zero.
 * 	The function returns -1 if the list is of length infinite.
 *
 * Side effects:
 *
 * 	None.
 *
 *----------------------------------------------------------------------
 */
static Tcl_WideInt
ArithSeriesLen(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step)
{
    Tcl_WideInt len;

    if (step == 0) return 0;
    len = (step ? (1 + (((end-start))/step)) : 0);
    return (len < 0) ? -1 : len;
}

/*
 *----------------------------------------------------------------------
 *
 * TclNewArithSeriesInt --
 *
 *	Creates a new ArithSeries object. The returned object has
 *	refcount = 0.
 *
 * Results:
 *
 * 	A Tcl_Obj pointer to the created ArithSeries object.
 * 	A NULL pointer of the range is invalid.
 *
 * Side Effects:
 *
 * 	None.
 *----------------------------------------------------------------------
 */
Tcl_Obj *
TclNewArithSeriesInt(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step, Tcl_WideInt len)
{
    Tcl_WideInt length = (len>=0 ? len : ArithSeriesLen(start, end, step));
    Tcl_Obj *arithSeriesPtr;
    ArithSeries *arithSeriesRepPtr;

    TclNewObj(arithSeriesPtr);

    if (length <= 0) {
	return arithSeriesPtr;
    }

    arithSeriesRepPtr = (ArithSeries*) Tcl_Alloc(sizeof (ArithSeries));
    arithSeriesRepPtr->isDouble = 0;
    arithSeriesRepPtr->start = start;
    arithSeriesRepPtr->end = end;
    arithSeriesRepPtr->step = step;
    arithSeriesRepPtr->len = length;
    arithSeriesRepPtr->elements = NULL;
    arithSeriesPtr->internalRep.twoPtrValue.ptr1 = arithSeriesRepPtr;
    arithSeriesPtr->internalRep.twoPtrValue.ptr2 = NULL;
    arithSeriesPtr->typePtr = &tclArithSeriesType;
    if (length > 0)
    	Tcl_InvalidateStringRep(arithSeriesPtr);

    return arithSeriesPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TclNewArithSeriesDbl --
 *
 *	Creates a new ArithSeries object with doubles. The returned object has
 *	refcount = 0.
 *
 * Results:
 *
 * 	A Tcl_Obj pointer to the created ArithSeries object.
 * 	A NULL pointer of the range is invalid.
 *
 * Side Effects:
 *
 * 	None.
 *----------------------------------------------------------------------
 */
Tcl_Obj *
TclNewArithSeriesDbl(double start, double end, double step, Tcl_WideInt len)
{
    Tcl_WideInt length = (len>=0 ? len : ArithSeriesLen(start, end, step));
    Tcl_Obj *arithSeriesPtr;
    ArithSeriesDbl *arithSeriesRepPtr;

    TclNewObj(arithSeriesPtr);

    if (length <= 0) {
	return arithSeriesPtr;
    }

    arithSeriesRepPtr = (ArithSeriesDbl*) Tcl_Alloc(sizeof (ArithSeriesDbl));
    arithSeriesRepPtr->isDouble = 1;
    arithSeriesRepPtr->start = start;
    arithSeriesRepPtr->end = end;
    arithSeriesRepPtr->step = step;
    arithSeriesRepPtr->len = length;
    arithSeriesRepPtr->elements = NULL;
    arithSeriesPtr->internalRep.twoPtrValue.ptr1 = arithSeriesRepPtr;
    arithSeriesPtr->internalRep.twoPtrValue.ptr2 = NULL;
    arithSeriesPtr->typePtr = &tclArithSeriesType;
    if (length > 0)
    	Tcl_InvalidateStringRep(arithSeriesPtr);

    return arithSeriesPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * assignNumber --
 *
 *	Create the appropriate Tcl_Obj value for the given numeric values.
 *      Used locally only for decoding [lseq] numeric arguments.
 *	refcount = 0.
 *
 * Results:
 *
 * 	A Tcl_Obj pointer.
 *      No assignment on error.
 *
 * Side Effects:
 *
 * 	None.
 *----------------------------------------------------------------------
 */
static void
assignNumber(int useDoubles, Tcl_WideInt *intNumberPtr, double *dblNumberPtr, Tcl_Obj *numberObj)
{
    union {
	double d;
	Tcl_WideInt i;
    } *number;
    int tcl_number_type;

    if (TclGetNumberFromObj(NULL, numberObj, (ClientData*)&number, &tcl_number_type) != TCL_OK) {
	return;
    }
    if (useDoubles) {
	if (tcl_number_type == TCL_NUMBER_DOUBLE) {
	    *dblNumberPtr = number->d;
	} else {
	    *dblNumberPtr = (double)number->i;
	}
    } else {
	if (tcl_number_type == TCL_NUMBER_INT) {
	    *intNumberPtr = number->i;
	} else {
	    *intNumberPtr = (Tcl_WideInt)number->d;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TclNewArithSeriesObj --
 *
 *	Creates a new ArithSeries object. Some arguments may be NULL and will
 *	be computed based on the other given arguments.
 *      refcount = 0.
 *
 * Results:
 *
 * 	A Tcl_Obj pointer to the created ArithSeries object.
 * 	An empty Tcl_Obj if the range is invalid.
 *
 * Side Effects:
 *
 * 	None.
 *----------------------------------------------------------------------
 */
Tcl_Obj *
TclNewArithSeriesObj(int useDoubles, Tcl_Obj *startObj, Tcl_Obj *endObj, Tcl_Obj *stepObj, Tcl_Obj *lenObj)
{
    double dstart, dend, dstep;
    Tcl_WideInt start, end, step, len;

    if (startObj) {
	assignNumber(useDoubles, &start, &dstart, startObj);
    } else {
	start = 0;
	dstart = start;
    }
    if (stepObj) {
	assignNumber(useDoubles, &step, &dstep, stepObj);
	if (useDoubles) {
	    step = dstep;
	} else {
	    dstep = step;
	}
	if (dstep == 0) {
	    return Tcl_NewObj();
	}
    }
    if (endObj) {
	assignNumber(useDoubles, &end, &dend, endObj);
    }
    if (lenObj) {
	Tcl_GetWideIntFromObj(NULL, lenObj, &len);
    }

    if (startObj && endObj) {
	if (!stepObj) {
	    if (useDoubles) {
		dstep = (dstart < dend) ? 1.0 : -1.0;
		step = dstep;
	    } else {
		step = (start < end) ? 1 : -1;
		dstep = step;
	    }
	}
	assert(dstep!=0);
	if (!lenObj) {
	    if (useDoubles) {
		len = (dend - dstart + dstep)/dstep;
	    } else {
		len = (end - start + step)/step;
	    }
	}
    }

    if (!endObj) {
	if (useDoubles) {
	    dend = dstart + (dstep * (len-1));
	    end = dend;
	} else {
	    end = start + (step * (len-1));
	    dend = end;
	}
    }

    if (useDoubles) {
	return TclNewArithSeriesDbl(dstart, dend, dstep, len);
    } else {
	return TclNewArithSeriesInt(start, end, step, len);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TclArithSeriesObjStep --
 *
 *	Return a Tcl_Obj with the step value from the give ArithSeries Obj.
 *	refcount = 0.
 *
 * Results:
 *
 * 	A Tcl_Obj pointer to the created ArithSeries object.
 * 	A NULL pointer of the range is invalid.
 *
 * Side Effects:
 *
 * 	None.
 *----------------------------------------------------------------------
 */
/*
 * TclArithSeriesObjStep --
 */
int
TclArithSeriesObjStep(
    Tcl_Obj *arithSeriesPtr,
    Tcl_Obj **stepObj)
{
    ArithSeries *arithSeriesRepPtr;

    if (arithSeriesPtr->typePtr != &tclArithSeriesType) {
        Tcl_Panic("TclArithSeriesObjIndex called with a not ArithSeries Obj.");
    }
    arithSeriesRepPtr = ArithSeriesRepPtr(arithSeriesPtr);
    if (arithSeriesRepPtr->isDouble) {
	*stepObj = Tcl_NewDoubleObj(((ArithSeriesDbl*)(arithSeriesRepPtr))->step);
    } else {
	*stepObj = Tcl_NewWideIntObj(arithSeriesRepPtr->step);
    }
    return TCL_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * TclArithSeriesObjIndex --
 *
 *	Returns the element with the specified index in the list
 *	represented by the specified Arithmetic Sequence object.
 *	If the index is out of range, TCL_ERROR is returned,
 *	otherwise TCL_OK is returned and the integer value of the
 *	element is stored in *element.
 *
 * Results:
 *
 * 	TCL_OK on success, TCL_ERROR on index out of range.
 *
 * Side Effects:
 *
 * 	On success, the integer pointed by *element is modified.
 *
 *----------------------------------------------------------------------
 */

int
TclArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr, Tcl_WideInt index, Tcl_Obj **elementObj)
{
    ArithSeries *arithSeriesRepPtr;

    if (arithSeriesPtr->typePtr != &tclArithSeriesType) {
	Tcl_Panic("TclArithSeriesObjIndex called with a not ArithSeries Obj.");
    }
    arithSeriesRepPtr = ArithSeriesRepPtr(arithSeriesPtr);
    if (index < 0 || index >= arithSeriesRepPtr->len) {
	return TCL_ERROR;
    }
    /* List[i] = Start + (Step * index) */
    if (arithSeriesRepPtr->isDouble) {
	*elementObj = Tcl_NewDoubleObj(ArithSeriesIndexM(arithSeriesRepPtr, index));
    } else {
	*elementObj = Tcl_NewWideIntObj(ArithSeriesIndexM(arithSeriesRepPtr, index));
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TclArithSeriesObjLength
 *
 *	Returns the length of the arithmetic series.
 *
 * Results:
 *
 * 	The length of the series as Tcl_WideInt.
 *
 * Side Effects:
 *
 * 	None.
 *
 *----------------------------------------------------------------------
 */
Tcl_WideInt TclArithSeriesObjLength(Tcl_Obj *arithSeriesPtr)
{
    ArithSeries *arithSeriesRepPtr = (ArithSeries*)
	    arithSeriesPtr->internalRep.twoPtrValue.ptr1;
    return arithSeriesRepPtr->len;
}

/*
 *----------------------------------------------------------------------
 *
 * FreeArithSeriesInternalRep --
 *
 *	Deallocate the storage associated with an arithseries object's
 *	internal representation.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Frees arithSeriesPtr's ArithSeries* internal representation and
 *	sets listPtr's	internalRep.twoPtrValue.ptr1 to NULL.
 *
 *----------------------------------------------------------------------
 */

static void
FreeArithSeriesInternalRep(Tcl_Obj *arithSeriesPtr)
{
    ArithSeries *arithSeriesRepPtr =
	    (ArithSeries *) arithSeriesPtr->internalRep.twoPtrValue.ptr1;
    if (arithSeriesRepPtr->elements) {
	Tcl_WideInt i;
	Tcl_Obj**elmts = arithSeriesRepPtr->elements;
	for(i=0; i<arithSeriesRepPtr->len; i++) {
	    if (elmts[i]) {
		Tcl_DecrRefCount(elmts[i]);
	    }
	}
	Tcl_Free((char *) arithSeriesRepPtr->elements);
    }
    Tcl_Free((char *) arithSeriesRepPtr);
    arithSeriesPtr->internalRep.twoPtrValue.ptr1 = NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * DupArithSeriesInternalRep --
 *
 *	Initialize the internal representation of a arithseries Tcl_Obj to a
 *	copy of the internal representation of an existing arithseries object.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	We set "copyPtr"s internal rep to a pointer to a
 *	newly allocated ArithSeries structure.
 *----------------------------------------------------------------------
 */

static void
DupArithSeriesInternalRep(
    Tcl_Obj *srcPtr,		/* Object with internal rep to copy. */
    Tcl_Obj *copyPtr)		/* Object with internal rep to set. */
{
    ArithSeries *srcArithSeriesRepPtr =
	    (ArithSeries *) srcPtr->internalRep.twoPtrValue.ptr1;
    ArithSeries *copyArithSeriesRepPtr;

    /*
     * Allocate a new ArithSeries structure. */

    copyArithSeriesRepPtr = (ArithSeries*) Tcl_Alloc(sizeof(ArithSeries));
    *copyArithSeriesRepPtr = *srcArithSeriesRepPtr;
    copyArithSeriesRepPtr->elements = NULL;
    copyPtr->internalRep.twoPtrValue.ptr1 = copyArithSeriesRepPtr;
    copyPtr->internalRep.twoPtrValue.ptr2 = NULL;
    copyPtr->typePtr = &tclArithSeriesType;
}

/*
 *----------------------------------------------------------------------
 *
 * UpdateStringOfArithSeries --
 *
 *	Update the string representation for an arithseries object.
 *	Note: This procedure does not invalidate an existing old string rep
 *	so storage will be lost if this has not already been done.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The object's string is set to a valid string that results from
 *	the list-to-string conversion. This string will be empty if the
 *	list has no elements. The list internal representation
 *	should not be NULL and we assume it is not NULL.
 *
 * Notes:
 * 	At the cost of overallocation it's possible to estimate
 * 	the length of the string representation and make this procedure
 * 	much faster. Because the programmer shouldn't expect the
 * 	string conversion of a big arithmetic sequence to be fast
 * 	this version takes more care of space than time.
 *
 *----------------------------------------------------------------------
 */

static void
UpdateStringOfArithSeries(Tcl_Obj *arithSeriesPtr)
{
    ArithSeries *arithSeriesRepPtr =
	    (ArithSeries*) arithSeriesPtr->internalRep.twoPtrValue.ptr1;
    char *elem, *p;
    Tcl_Obj *elemObj;
    Tcl_WideInt i;
    Tcl_WideInt length = 0;
    int slen;

    /*
     * Pass 1: estimate space.
     */
    for (i = 0; i < arithSeriesRepPtr->len; i++) {
	TclArithSeriesObjIndex(arithSeriesPtr, i, &elemObj);
	elem = TclGetStringFromObj(elemObj, &slen);
	Tcl_DecrRefCount(elemObj);
	slen += 1; /* + 1 is for the space or the nul-term */
	length += slen;
    }

    /*
     * Pass 2: generate the string repr.
     */

    p = Tcl_InitStringRep(arithSeriesPtr, NULL, length);
    for (i = 0; i < arithSeriesRepPtr->len; i++) {
	TclArithSeriesObjIndex(arithSeriesPtr, i, &elemObj);
	elem = TclGetStringFromObj(elemObj, &slen);
	strcpy(p, elem);
	p[slen] = ' ';
	p += slen+1;
	Tcl_DecrRefCount(elemObj);
    }
    if (length > 0) arithSeriesPtr->bytes[length-1] = '\0';
    arithSeriesPtr->length = length-1;
}

/*
 *----------------------------------------------------------------------
 *
 * SetArithSeriesFromAny --
 *
 * 	The Arithmetic Series object is just an way to optimize
 * 	Lists space complexity, so no one should try to convert
 * 	a string to an Arithmetic Series object.
 *
 * 	This function is here just to populate the Type structure.
 *
 * Results:
 *
 * 	The result is always TCL_ERROR. But see Side Effects.
 *
 * Side effects:
 *
 * 	Tcl Panic if called.
 *
 *----------------------------------------------------------------------
 */

static int
SetArithSeriesFromAny(
    TCL_UNUSED(Tcl_Interp *),		/* Used for error reporting if not NULL. */
    TCL_UNUSED(Tcl_Obj *))		/* The object to convert. */
{
    Tcl_Panic("SetArithSeriesFromAny: should never be called");
    return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *
 * TclArithSeriesObjCopy --
 *
 *	Makes a "pure arithSeries" copy of an ArithSeries value. This provides for the C
 *	level a counterpart of the [lrange $list 0 end] command, while using
 *	internals details to be as efficient as possible.
 *
 * Results:
 *
 *	Normally returns a pointer to a new Tcl_Obj, that contains the same
 *	arithSeries value as *arithSeriesPtr does. The returned Tcl_Obj has a
 *	refCount of zero. If *arithSeriesPtr does not hold an arithSeries,
 *	NULL is returned, and if interp is non-NULL, an error message is
 *	recorded there.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclArithSeriesObjCopy(
    Tcl_Interp *interp,		/* Used to report errors if not NULL. */
    Tcl_Obj *arithSeriesPtr)	/* List object for which an element array is
				 * to be returned. */
{
    Tcl_Obj *copyPtr;
    ArithSeries *arithSeriesRepPtr;

    ArithSeriesGetInternalRep(arithSeriesPtr, arithSeriesRepPtr);
    if (NULL == arithSeriesRepPtr) {
	if (SetArithSeriesFromAny(interp, arithSeriesPtr) != TCL_OK) {
	    /* We know this is going to panic, but it's the message we want */
	    return NULL;
	}
    }

    TclNewObj(copyPtr);
    TclInvalidateStringRep(copyPtr);
    DupArithSeriesInternalRep(arithSeriesPtr, copyPtr);
    return copyPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TclArithSeriesObjRange --
 *
 *	Makes a slice of an ArithSeries value.
 *      *arithSeriesPtr must be known to be a valid list.
 *
 * Results:
 *	Returns a pointer to the sliced series.
 *      This may be a new object or the same object if not shared.
 *
 * Side effects:
 *	?The possible conversion of the object referenced by listPtr?
 *	?to a list object.?
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclArithSeriesObjRange(
    Tcl_Obj *arithSeriesPtr,	/* List object to take a range from. */
    int fromIdx,		/* Index of first element to include. */
    int toIdx)			/* Index of last element to include. */
{
    ArithSeries *arithSeriesRepPtr;
    Tcl_Obj *startObj, *endObj, *stepObj;

    ArithSeriesGetInternalRep(arithSeriesPtr, arithSeriesRepPtr);

    if (fromIdx < 0) {
	fromIdx = 0;
    }
    if (fromIdx > toIdx) {
	Tcl_Obj *obj;
	TclNewObj(obj);
	return obj;
    }

    TclArithSeriesObjIndex(arithSeriesPtr, fromIdx, &startObj);
    Tcl_IncrRefCount(startObj);
    TclArithSeriesObjIndex(arithSeriesPtr, toIdx, &endObj);
    Tcl_IncrRefCount(endObj);
    TclArithSeriesObjStep(arithSeriesPtr, &stepObj);
    Tcl_IncrRefCount(stepObj);

    if (Tcl_IsShared(arithSeriesPtr) ||
	    ((arithSeriesPtr->refCount > 1))) {
	Tcl_Obj *newSlicePtr = TclNewArithSeriesObj(arithSeriesRepPtr->isDouble,
	           startObj, endObj, stepObj, NULL);
	Tcl_DecrRefCount(startObj);
	Tcl_DecrRefCount(endObj);
	Tcl_DecrRefCount(stepObj);
	return newSlicePtr;
    }

    /*
     * In-place is possible.
     */

    /*
     * Even if nothing below causes any changes, we still want the
     * string-canonizing effect of [lrange 0 end].
     */

    TclInvalidateStringRep(arithSeriesPtr);

    if (arithSeriesRepPtr->isDouble) {
	ArithSeriesDbl *arithSeriesDblRepPtr = (ArithSeriesDbl*)arithSeriesPtr;
	double start, end, step;
	Tcl_GetDoubleFromObj(NULL, startObj, &start);
	Tcl_GetDoubleFromObj(NULL, endObj, &end);
	Tcl_GetDoubleFromObj(NULL, stepObj, &step);
	arithSeriesDblRepPtr->start = start;
	arithSeriesDblRepPtr->end = end;
	arithSeriesDblRepPtr->step = step;
	arithSeriesDblRepPtr->len = (end-start+step)/step;
	arithSeriesDblRepPtr->elements = NULL;

    } else {
	Tcl_WideInt start, end, step;
	Tcl_GetWideIntFromObj(NULL, startObj, &start);
	Tcl_GetWideIntFromObj(NULL, endObj, &end);
	Tcl_GetWideIntFromObj(NULL, stepObj, &step);
	arithSeriesRepPtr->start = start;
	arithSeriesRepPtr->end = end;
	arithSeriesRepPtr->step = step;
	arithSeriesRepPtr->len = (end-start+step)/step;
	arithSeriesRepPtr->elements = NULL;
    }

    Tcl_DecrRefCount(startObj);
    Tcl_DecrRefCount(endObj);
    Tcl_DecrRefCount(stepObj);

    return arithSeriesPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TclArithSeriesGetElements --
 *
 *	This function returns an (objc,objv) array of the elements in a list
 *	object.
 *
 * Results:
 *	The return value is normally TCL_OK; in this case *objcPtr is set to
 *	the count of list elements and *objvPtr is set to a pointer to an
 *	array of (*objcPtr) pointers to each list element. If listPtr does not
 *	refer to an Abstract List object and the object can not be converted
 *	to one, TCL_ERROR is returned and an error message will be left in the
 *	interpreter's result if interp is not NULL.
 *
 *	The objects referenced by the returned array should be treated as
 *	readonly and their ref counts are _not_ incremented; the caller must
 *	do that if it holds on to a reference. Furthermore, the pointer and
 *	length returned by this function may change as soon as any function is
 *	called on the list object; be careful about retaining the pointer in a
 *	local data structure.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TclArithSeriesGetElements(
    Tcl_Interp *interp,		/* Used to report errors if not NULL. */
    Tcl_Obj *objPtr,		/* AbstractList object for which an element
				 * array is to be returned. */
    size_t *objcPtr,		/* Where to store the count of objects
				 * referenced by objv. */
    Tcl_Obj ***objvPtr)		/* Where to store the pointer to an array of
				 * pointers to the list's objects. */
{
    if (TclHasInternalRep(objPtr,&tclArithSeriesType)) {
	ArithSeries *arithSeriesRepPtr;
	Tcl_Obj **objv;
	int i, objc;

	ArithSeriesGetInternalRep(objPtr, arithSeriesRepPtr);
	objc = arithSeriesRepPtr->len;
	if (objc > 0) {
	    if (arithSeriesRepPtr->elements) {
		/* If this exists, it has already been populated */
		objv = arithSeriesRepPtr->elements;
	    } else {
		/* Construct the elements array */
		objv = (Tcl_Obj **)Tcl_Alloc(sizeof(Tcl_Obj*) * objc);
		if (objv == NULL) {
		    if (interp) {
			Tcl_SetObjResult(
			    interp,
			    Tcl_NewStringObj("max length of a Tcl list exceeded", -1));
			Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL);
		    }
		    return TCL_ERROR;
		}
		arithSeriesRepPtr->elements = objv;
		for (i = 0; i < objc; i++) {
		    if (TclArithSeriesObjIndex(objPtr, i, &objv[i]) != TCL_OK) {
			if (interp) {
			    Tcl_SetObjResult(
				interp,
				Tcl_NewStringObj("indexing error", -1));
			    Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL);
			}
			return TCL_ERROR;
		    }
		    Tcl_IncrRefCount(objv[i]);
		}
	    }
	} else {
	    objv = NULL;
	}
	*objvPtr = objv;
	*objcPtr = objc;
    } else {
	if (interp != NULL) {
	    Tcl_SetObjResult(
		interp,
		Tcl_ObjPrintf("value is not an arithseries"));
	    Tcl_SetErrorCode(interp, "TCL", "VALUE", "UNKNOWN", NULL);
	}
	return TCL_ERROR;
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TclArithSeriesObjReverse --
 *
 *	Reverse the order of the ArithSeries value.
 *      *arithSeriesPtr must be known to be a valid list.
 *
 * Results:
 *	Returns a pointer to the reordered series.
 *      This may be a new object or the same object if not shared.
 *
 * Side effects:
 *	?The possible conversion of the object referenced by listPtr?
 *	?to a list object.?
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclArithSeriesObjReverse(
    Tcl_Obj *arithSeriesPtr)	/* List object to reverse. */
{
    ArithSeries *arithSeriesRepPtr;
    Tcl_Obj *startObj, *endObj, *stepObj;
    Tcl_Obj *resultObj;
    Tcl_WideInt start, end, step, len;
    double dstart, dend, dstep;
    int isDouble;

    ArithSeriesGetInternalRep(arithSeriesPtr, arithSeriesRepPtr);

    isDouble = arithSeriesRepPtr->isDouble;
    len = arithSeriesRepPtr->len;

    TclArithSeriesObjIndex(arithSeriesPtr, (len-1), &startObj);
    TclArithSeriesObjIndex(arithSeriesPtr, 0, &endObj);
    TclArithSeriesObjStep(arithSeriesPtr, &stepObj);

    if (isDouble) {
	Tcl_GetDoubleFromObj(NULL, startObj, &dstart);
	Tcl_GetDoubleFromObj(NULL, endObj, &dend);
	Tcl_GetDoubleFromObj(NULL, stepObj, &dstep);
	dstep = -dstep;
	TclSetDoubleObj(stepObj, dstep);
    } else {
	Tcl_GetWideIntFromObj(NULL, startObj, &start);
	Tcl_GetWideIntFromObj(NULL, endObj, &end);
	Tcl_GetWideIntFromObj(NULL, stepObj, &step);
	step = -step;
	TclSetIntObj(stepObj, step);
    }

    if (Tcl_IsShared(arithSeriesPtr) ||
	    ((arithSeriesPtr->refCount > 1))) {
	Tcl_Obj *lenObj = Tcl_NewWideIntObj(len);
	resultObj = TclNewArithSeriesObj(isDouble,
		        startObj, endObj, stepObj, lenObj);
	Tcl_DecrRefCount(lenObj);
    } else {

	/*
	 * In-place is possible.
	 */

	TclInvalidateStringRep(arithSeriesPtr);

	if (isDouble) {
	    ArithSeriesDbl *arithSeriesDblRepPtr =
		(ArithSeriesDbl*)arithSeriesRepPtr;
	    arithSeriesDblRepPtr->start = dstart;
	    arithSeriesDblRepPtr->end = dend;
	    arithSeriesDblRepPtr->step = dstep;
	} else {
	    arithSeriesRepPtr->start = start;
	    arithSeriesRepPtr->end = end;
	    arithSeriesRepPtr->step = step;
	}
	if (arithSeriesRepPtr->elements) {
	    Tcl_WideInt i;
	    for (i=0; i<len; i++) {
		Tcl_DecrRefCount(arithSeriesRepPtr->elements[i]);
	    }
	    Tcl_Free((char*)arithSeriesRepPtr->elements);
	}
	arithSeriesRepPtr->elements = NULL;

	resultObj = arithSeriesPtr;
    }

    Tcl_DecrRefCount(startObj);
    Tcl_DecrRefCount(endObj);
    Tcl_DecrRefCount(stepObj);

    return resultObj;
}
Added generic/tclArithSeries.h.












































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
/*
 * tclArithSeries.h --
 *
 *     This file contains the ArithSeries concrete abstract list
 *     implementation. It implements the inner workings of the lseq command.
 *
 * Copyright © 2022 Brian S. Griffin.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

/*
 * The structure used for the ArithSeries internal representation.
 * Note that the len can in theory be always computed by start,end,step
 * but it's faster to cache it inside the internal representation.
 */
typedef struct ArithSeries {
    Tcl_WideInt start;
    Tcl_WideInt end;
    Tcl_WideInt step;
    Tcl_WideInt len;
    Tcl_Obj **elements;
    int isDouble;
} ArithSeries;
typedef struct ArithSeriesDbl {
    double start;
    double end;
    double step;
    Tcl_WideInt len;
    Tcl_Obj **elements;
    int isDouble;
} ArithSeriesDbl;


MODULE_SCOPE Tcl_Obj *	TclArithSeriesObjCopy(Tcl_Interp *interp,
			    Tcl_Obj *arithSeriesPtr);
MODULE_SCOPE int	TclArithSeriesObjStep(Tcl_Obj *arithSeriesPtr,
			    Tcl_Obj **stepObj);
MODULE_SCOPE int	TclArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr,
			    Tcl_WideInt index, Tcl_Obj **elementObj);
MODULE_SCOPE Tcl_WideInt TclArithSeriesObjLength(Tcl_Obj *arithSeriesPtr);
MODULE_SCOPE Tcl_Obj *	TclArithSeriesObjRange(Tcl_Obj *arithSeriesPtr,
			    int fromIdx, int toIdx);
MODULE_SCOPE Tcl_Obj *	TclArithSeriesObjReverse(Tcl_Obj *arithSeriesPtr);
MODULE_SCOPE int	TclArithSeriesGetElements(Tcl_Interp *interp,
			    Tcl_Obj *objPtr, size_t *objcPtr, Tcl_Obj ***objvPtr);
MODULE_SCOPE Tcl_Obj *	TclNewArithSeriesInt(Tcl_WideInt start,
			    Tcl_WideInt end, Tcl_WideInt step,
			    Tcl_WideInt len);
MODULE_SCOPE Tcl_Obj *	TclNewArithSeriesDbl(double start, double end,
			    double step, Tcl_WideInt len);
MODULE_SCOPE Tcl_Obj *	TclNewArithSeriesObj(int useDoubles, Tcl_Obj *startObj,
			    Tcl_Obj *endObj, Tcl_Obj *stepObj, Tcl_Obj *lenObj);
Changes to generic/tclBasic.c.
305
306
307
308
309
310
311

312
313
314
315
316
317
318
    {"lpop",		Tcl_LpopObjCmd,		NULL,			NULL,	CMD_IS_SAFE},
    {"lrange",		Tcl_LrangeObjCmd,	TclCompileLrangeCmd,	NULL,	CMD_IS_SAFE},
    {"lremove", 	Tcl_LremoveObjCmd,	NULL,           	NULL,	CMD_IS_SAFE},
    {"lrepeat",		Tcl_LrepeatObjCmd,	NULL,			NULL,	CMD_IS_SAFE},
    {"lreplace",	Tcl_LreplaceObjCmd,	TclCompileLreplaceCmd,	NULL,	CMD_IS_SAFE},
    {"lreverse",	Tcl_LreverseObjCmd,	NULL,			NULL,	CMD_IS_SAFE},
    {"lsearch",		Tcl_LsearchObjCmd,	NULL,			NULL,	CMD_IS_SAFE},

    {"lset",		Tcl_LsetObjCmd,		TclCompileLsetCmd,	NULL,	CMD_IS_SAFE},
    {"lsort",		Tcl_LsortObjCmd,	NULL,			NULL,	CMD_IS_SAFE},
    {"package",		Tcl_PackageObjCmd,	NULL,			TclNRPackageObjCmd,	CMD_IS_SAFE},
    {"proc",		procObjCmd,		NULL,			NULL,	CMD_IS_SAFE},
    {"regexp",		Tcl_RegexpObjCmd,	TclCompileRegexpCmd,	NULL,	CMD_IS_SAFE},
    {"regsub",		Tcl_RegsubObjCmd,	TclCompileRegsubCmd,	NULL,	CMD_IS_SAFE},
    {"rename",		Tcl_RenameObjCmd,	NULL,			NULL,	CMD_IS_SAFE},







>







305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
    {"lpop",		Tcl_LpopObjCmd,		NULL,			NULL,	CMD_IS_SAFE},
    {"lrange",		Tcl_LrangeObjCmd,	TclCompileLrangeCmd,	NULL,	CMD_IS_SAFE},
    {"lremove", 	Tcl_LremoveObjCmd,	NULL,           	NULL,	CMD_IS_SAFE},
    {"lrepeat",		Tcl_LrepeatObjCmd,	NULL,			NULL,	CMD_IS_SAFE},
    {"lreplace",	Tcl_LreplaceObjCmd,	TclCompileLreplaceCmd,	NULL,	CMD_IS_SAFE},
    {"lreverse",	Tcl_LreverseObjCmd,	NULL,			NULL,	CMD_IS_SAFE},
    {"lsearch",		Tcl_LsearchObjCmd,	NULL,			NULL,	CMD_IS_SAFE},
    {"lseq",            Tcl_LseqObjCmd,         NULL,                   NULL,   CMD_IS_SAFE},
    {"lset",		Tcl_LsetObjCmd,		TclCompileLsetCmd,	NULL,	CMD_IS_SAFE},
    {"lsort",		Tcl_LsortObjCmd,	NULL,			NULL,	CMD_IS_SAFE},
    {"package",		Tcl_PackageObjCmd,	NULL,			TclNRPackageObjCmd,	CMD_IS_SAFE},
    {"proc",		procObjCmd,		NULL,			NULL,	CMD_IS_SAFE},
    {"regexp",		Tcl_RegexpObjCmd,	TclCompileRegexpCmd,	NULL,	CMD_IS_SAFE},
    {"regsub",		Tcl_RegsubObjCmd,	TclCompileRegsubCmd,	NULL,	CMD_IS_SAFE},
    {"rename",		Tcl_RenameObjCmd,	NULL,			NULL,	CMD_IS_SAFE},
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
buildInfoObjCmd(
    void *clientData,
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    if (objc > 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "?option?");
	return TCL_ERROR;
    }
    if (objc == 2) {
	int len;
	const char *arg = TclGetStringFromObj(objv[1], &len);
	if (len == 7 && !strcmp(arg, "version")) {







|


|


|







605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
buildInfoObjCmd2(
    void *clientData,
    Tcl_Interp *interp,		/* Current interpreter. */
    size_t objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    if (objc - 1 > 1) {
	Tcl_WrongNumArgs(interp, 1, objv, "?option?");
	return TCL_ERROR;
    }
    if (objc == 2) {
	int len;
	const char *arg = TclGetStringFromObj(objv[1], &len);
	if (len == 7 && !strcmp(arg, "version")) {
688
689
690
691
692
693
694










695
696
697
698
699
700
701
	}
	Tcl_AppendResult(interp, "0", NULL);
	return TCL_OK;
    }
    Tcl_AppendResult(interp, (char *)clientData, NULL);
    return TCL_OK;
}











/*
 *----------------------------------------------------------------------
 *
 * Tcl_CreateInterp --
 *
 *	Create a new TCL command interpreter.







>
>
>
>
>
>
>
>
>
>







689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
	}
	Tcl_AppendResult(interp, "0", NULL);
	return TCL_OK;
    }
    Tcl_AppendResult(interp, (char *)clientData, NULL);
    return TCL_OK;
}

static int
buildInfoObjCmd(
    void *clientData,
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    return buildInfoObjCmd2(clientData, interp, (size_t)objc, objv);
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_CreateInterp --
 *
 *	Create a new TCL command interpreter.
760
761
762
763
764
765
766

767
768
769
770
771
772
773
	    Tcl_InitHashTable(&cancelTable, TCL_ONE_WORD_KEYS);
	    cancelTableInitialized = 1;
	}

	Tcl_MutexUnlock(&cancelLock);
    }


    if (commandTypeInit == 0) {
        TclRegisterCommandTypeName(TclObjInterpProc, "proc");
        TclRegisterCommandTypeName(TclEnsembleImplementationCmd, "ensemble");
        TclRegisterCommandTypeName(TclAliasObjCmd, "alias");
        TclRegisterCommandTypeName(TclLocalAliasObjCmd, "alias");
        TclRegisterCommandTypeName(TclChildObjCmd, "interp");
        TclRegisterCommandTypeName(TclInvokeImportedCmd, "import");







>







771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
	    Tcl_InitHashTable(&cancelTable, TCL_ONE_WORD_KEYS);
	    cancelTableInitialized = 1;
	}

	Tcl_MutexUnlock(&cancelLock);
    }

#undef TclObjInterpProc
    if (commandTypeInit == 0) {
        TclRegisterCommandTypeName(TclObjInterpProc, "proc");
        TclRegisterCommandTypeName(TclEnsembleImplementationCmd, "ensemble");
        TclRegisterCommandTypeName(TclAliasObjCmd, "alias");
        TclRegisterCommandTypeName(TclLocalAliasObjCmd, "alias");
        TclRegisterCommandTypeName(TclChildObjCmd, "interp");
        TclRegisterCommandTypeName(TclInvokeImportedCmd, "import");
1230
1231
1232
1233
1234
1235
1236

1237
1238
1239



1240
1241
1242
1243
1244
1245
1246
     * Register Tcl's version number.
     * TIP #268: Full patchlevel instead of just major.minor
     * TIP #599: Extended build information "+<UUID>.<tag1>.<tag2>...."
     */

    Tcl_PkgProvideEx(interp, "Tcl", TCL_PATCH_LEVEL, &tclStubs);
    Tcl_PkgProvideEx(interp, "tcl", TCL_PATCH_LEVEL, &tclStubs);

    Tcl_CreateObjCommand(interp, "::tcl::build-info",
	    buildInfoObjCmd, (void *)version, NULL);





    if (TclTommath_Init(interp) != TCL_OK) {
	Tcl_Panic("%s", Tcl_GetStringResult(interp));
    }

    if (TclOOInit(interp) != TCL_OK) {
	Tcl_Panic("%s", Tcl_GetStringResult(interp));







>
|

|
>
>
>







1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
     * Register Tcl's version number.
     * TIP #268: Full patchlevel instead of just major.minor
     * TIP #599: Extended build information "+<UUID>.<tag1>.<tag2>...."
     */

    Tcl_PkgProvideEx(interp, "Tcl", TCL_PATCH_LEVEL, &tclStubs);
    Tcl_PkgProvideEx(interp, "tcl", TCL_PATCH_LEVEL, &tclStubs);
    Tcl_CmdInfo info2;
    Tcl_Command buildInfoCmd = Tcl_CreateObjCommand(interp, "::tcl::build-info",
	    buildInfoObjCmd, (void *)version, NULL);
    Tcl_GetCommandInfoFromToken(buildInfoCmd, &info2);
    info2.objProc2 = buildInfoObjCmd2;
    info2.objClientData2 = (void *)version;
    Tcl_SetCommandInfoFromToken(buildInfoCmd, &info2);

    if (TclTommath_Init(interp) != TCL_OK) {
	Tcl_Panic("%s", Tcl_GetStringResult(interp));
    }

    if (TclOOInit(interp) != TCL_OK) {
	Tcl_Panic("%s", Tcl_GetStringResult(interp));
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
 *	the table, deleteProc will be called. See the manual entry for details
 *	on the calling sequence.
 *
 *----------------------------------------------------------------------
 */

typedef struct {

    void *clientData; /* Arbitrary value to pass to object function. */
    Tcl_ObjCmdProc2 *proc;

    Tcl_ObjCmdProc2 *nreProc;
    Tcl_CmdDeleteProc *deleteProc;
} CmdWrapperInfo;


static int cmdWrapperProc(void *clientData,
	Tcl_Interp *interp,
	int objc,
    Tcl_Obj * const *objv)
{
    CmdWrapperInfo *info = (CmdWrapperInfo *)clientData;



    return info->proc(info->clientData, interp, objc, objv);
}

static void cmdWrapperDeleteProc(void *clientData) {
    CmdWrapperInfo *info = (CmdWrapperInfo *)clientData;

    clientData = info->clientData;
    Tcl_CmdDeleteProc *deleteProc = info->deleteProc;
    Tcl_Free(info);
    if (deleteProc != NULL) {
	deleteProc(clientData);
    }
}








>
|
|
>

<




|
|



>
>
>
|





|







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
 *	the table, deleteProc will be called. See the manual entry for details
 *	on the calling sequence.
 *
 *----------------------------------------------------------------------
 */

typedef struct {
    Tcl_ObjCmdProc2 *proc;
    void *clientData; /* Arbitrary value to pass to proc function. */
    Tcl_CmdDeleteProc *deleteProc;
    void *deleteData; /* Arbitrary value to pass to deleteProc function. */
    Tcl_ObjCmdProc2 *nreProc;

} CmdWrapperInfo;


static int cmdWrapperProc(void *clientData,
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj * const *objv)
{
    CmdWrapperInfo *info = (CmdWrapperInfo *)clientData;
    if (objc < 0) {
	objc = -1;
    }
    return info->proc(info->clientData, interp, (size_t)objc, objv);
}

static void cmdWrapperDeleteProc(void *clientData) {
    CmdWrapperInfo *info = (CmdWrapperInfo *)clientData;

    clientData = info->deleteData;
    Tcl_CmdDeleteProc *deleteProc = info->deleteProc;
    Tcl_Free(info);
    if (deleteProc != NULL) {
	deleteProc(clientData);
    }
}

2673
2674
2675
2676
2677
2678
2679

2680
2681
2682
2683
2684
2685
2686
2687
2688
    Tcl_CmdDeleteProc *deleteProc
				/* If not NULL, gives a function to call when
				 * this command is deleted. */
)
{
    CmdWrapperInfo *info = (CmdWrapperInfo *)Tcl_Alloc(sizeof(CmdWrapperInfo));
    info->proc = proc;

    info->deleteProc = deleteProc;
    info->clientData = clientData;

    return Tcl_CreateObjCommand(interp, cmdName,
	    (proc ? cmdWrapperProc : NULL),
	    info, cmdWrapperDeleteProc);
}

Tcl_Command







>

|







2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
    Tcl_CmdDeleteProc *deleteProc
				/* If not NULL, gives a function to call when
				 * this command is deleted. */
)
{
    CmdWrapperInfo *info = (CmdWrapperInfo *)Tcl_Alloc(sizeof(CmdWrapperInfo));
    info->proc = proc;
    info->clientData = clientData;
    info->deleteProc = deleteProc;
    info->deleteData = clientData;

    return Tcl_CreateObjCommand(interp, cmdName,
	    (proc ? cmdWrapperProc : NULL),
	    info, cmdWrapperDeleteProc);
}

Tcl_Command
3260
3261
3262
3263
3264
3265
3266































3267
3268
3269
3270
3271
3272
3273
 *	returned. If the command doesn't exist then 0 is returned.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
































int
Tcl_SetCommandInfoFromToken(
    Tcl_Command cmd,
    const Tcl_CmdInfo *infoPtr)
{
    Command *cmdPtr;		/* Internal representation of the command */







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







3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
 *	returned. If the command doesn't exist then 0 is returned.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
invokeObj2Command(
    void *clientData,	/* Points to command's Command structure. */
    Tcl_Interp *interp,		/* Current interpreter. */
    size_t objc,		/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    int result;
    Command *cmdPtr = (Command *) clientData;

    if (objc > INT_MAX) {
	objc = TCL_INDEX_NONE;
    }
    if (cmdPtr->objProc != NULL) {
	result = cmdPtr->objProc(cmdPtr->objClientData, interp, objc, objv);
    } else {
	result = Tcl_NRCallObjProc(interp, cmdPtr->nreProc,
		cmdPtr->objClientData, objc, objv);
    }
    return result;
}

static int cmdWrapper2Proc(void *clientData,
    Tcl_Interp *interp,
    size_t objc,
    Tcl_Obj *const objv[])
{
    Command *cmdPtr = (Command *)clientData;
    return cmdPtr->objProc(cmdPtr->objClientData, interp, objc, objv);
}

int
Tcl_SetCommandInfoFromToken(
    Tcl_Command cmd,
    const Tcl_CmdInfo *infoPtr)
{
    Command *cmdPtr;		/* Internal representation of the command */
3292
3293
3294
3295
3296
3297
3298











3299





3300





3301
3302
3303

3304
3305
3306
3307
3308
3309
3310
	    cmdPtr->nreProc = NULL;
	    cmdPtr->objProc = infoPtr->objProc;
	}
	cmdPtr->objClientData = infoPtr->objClientData;
    }
    if (cmdPtr->deleteProc == cmdWrapperDeleteProc) {
	CmdWrapperInfo *info = (CmdWrapperInfo *)cmdPtr->deleteData;











	info->deleteProc = infoPtr->deleteProc;





	info->clientData = infoPtr->deleteData;





    } else {
	cmdPtr->deleteProc = infoPtr->deleteProc;
	cmdPtr->deleteData = infoPtr->deleteData;

    }
    return 1;
}

/*
 *----------------------------------------------------------------------
 *







>
>
>
>
>
>
>
>
>
>
>

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







3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
	    cmdPtr->nreProc = NULL;
	    cmdPtr->objProc = infoPtr->objProc;
	}
	cmdPtr->objClientData = infoPtr->objClientData;
    }
    if (cmdPtr->deleteProc == cmdWrapperDeleteProc) {
	CmdWrapperInfo *info = (CmdWrapperInfo *)cmdPtr->deleteData;
	if (infoPtr->objProc2 == NULL) {
	    info->proc = invokeObj2Command;
	    info->clientData = cmdPtr;
	    info->nreProc = NULL;
	} else {
	    if (infoPtr->objProc2 != info->proc) {
		info->nreProc = NULL;
		info->proc = infoPtr->objProc2;
	    }
	    info->clientData = infoPtr->objClientData2;
	}
	info->deleteProc = infoPtr->deleteProc;
	info->deleteData = infoPtr->deleteData;
    } else {
	if ((infoPtr->objProc2 != NULL) && (infoPtr->objProc2 != cmdWrapper2Proc)) {
	    CmdWrapperInfo *info = (CmdWrapperInfo *)Tcl_Alloc(sizeof(CmdWrapperInfo));
	    info->proc = infoPtr->objProc2;
	    info->clientData = infoPtr->objClientData2;
	    info->nreProc = NULL;
	    info->deleteProc = infoPtr->deleteProc;
	    info->deleteData = infoPtr->deleteData;
	    cmdPtr->deleteProc = cmdWrapperDeleteProc;
	    cmdPtr->deleteData = info;
	} else {
	    cmdPtr->deleteProc = infoPtr->deleteProc;
	    cmdPtr->deleteData = infoPtr->deleteData;
	}
    }
    return 1;
}

/*
 *----------------------------------------------------------------------
 *
3364
3365
3366
3367
3368
3369
3370
3371

3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384





3385
3386
3387


3388
3389
3390
3391
3392
3393
3394

    if (cmd == NULL) {
	return 0;
    }

    /*
     * Set isNativeObjectProc 1 if objProc was registered by a call to
     * Tcl_CreateObjCommand. Otherwise set it to 0.

     */

    cmdPtr = (Command *) cmd;
    infoPtr->isNativeObjectProc =
	    (cmdPtr->objProc != TclInvokeStringCommand);
    infoPtr->objProc = cmdPtr->objProc;
    infoPtr->objClientData = cmdPtr->objClientData;
    infoPtr->proc = cmdPtr->proc;
    infoPtr->clientData = cmdPtr->clientData;
    if (cmdPtr->deleteProc == cmdWrapperDeleteProc) {
	CmdWrapperInfo *info = (CmdWrapperInfo *)cmdPtr->deleteData;
	infoPtr->deleteProc = info->deleteProc;
	infoPtr->deleteData = info->clientData;





    } else {
	infoPtr->deleteProc = cmdPtr->deleteProc;
	infoPtr->deleteData = cmdPtr->deleteData;


    }
    infoPtr->namespacePtr = (Tcl_Namespace *) cmdPtr->nsPtr;
    return 1;
}

/*
 *----------------------------------------------------------------------







|
>












|
>
>
>
>
>



>
>







3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476

    if (cmd == NULL) {
	return 0;
    }

    /*
     * Set isNativeObjectProc 1 if objProc was registered by a call to
     * Tcl_CreateObjCommand. Set isNativeObjectProc 2 if objProc was
     * registered by a call to Tcl_CreateObjCommand2. Otherwise set it to 0.
     */

    cmdPtr = (Command *) cmd;
    infoPtr->isNativeObjectProc =
	    (cmdPtr->objProc != TclInvokeStringCommand);
    infoPtr->objProc = cmdPtr->objProc;
    infoPtr->objClientData = cmdPtr->objClientData;
    infoPtr->proc = cmdPtr->proc;
    infoPtr->clientData = cmdPtr->clientData;
    if (cmdPtr->deleteProc == cmdWrapperDeleteProc) {
	CmdWrapperInfo *info = (CmdWrapperInfo *)cmdPtr->deleteData;
	infoPtr->deleteProc = info->deleteProc;
	infoPtr->deleteData = info->deleteData;
	infoPtr->objProc2 = info->proc;
	infoPtr->objClientData2 = info->clientData;
	if (cmdPtr->objProc == cmdWrapperProc) {
	    infoPtr->isNativeObjectProc = 2;
	}
    } else {
	infoPtr->deleteProc = cmdPtr->deleteProc;
	infoPtr->deleteData = cmdPtr->deleteData;
	infoPtr->objProc2 = cmdWrapper2Proc;
	infoPtr->objClientData2 = cmdPtr;
    }
    infoPtr->namespacePtr = (Tcl_Namespace *) cmdPtr->nsPtr;
    return 1;
}

/*
 *----------------------------------------------------------------------
8407
8408
8409
8410
8411
8412
8413



8414
8415
8416
8417
8418
8419
8420
8421
8422
8423
8424





8425
8426
8427
8428
8429
8430
8431
    int objc,
    Tcl_Obj *const objv[])
{
    CmdWrapperInfo *info = (CmdWrapperInfo *)clientData;
    clientData = info->clientData;
    Tcl_ObjCmdProc2 *proc = info->proc;
    Tcl_Free(info);



    return proc(clientData, interp, objc, objv);
}

int
Tcl_NRCallObjProc2(
    Tcl_Interp *interp,
    Tcl_ObjCmdProc2 *objProc,
    void *clientData,
    size_t objc,
    Tcl_Obj *const objv[])
{





    NRE_callback *rootPtr = TOP_CB(interp);
    CmdWrapperInfo *info = (CmdWrapperInfo *)Tcl_Alloc(sizeof(CmdWrapperInfo));
    info->clientData = clientData;
    info->proc = objProc;

    TclNRAddCallback(interp, Dispatch, wrapperNRObjProc, info,
	    INT2PTR(objc), objv);







>
>
>
|










>
>
>
>
>







8489
8490
8491
8492
8493
8494
8495
8496
8497
8498
8499
8500
8501
8502
8503
8504
8505
8506
8507
8508
8509
8510
8511
8512
8513
8514
8515
8516
8517
8518
8519
8520
8521
    int objc,
    Tcl_Obj *const objv[])
{
    CmdWrapperInfo *info = (CmdWrapperInfo *)clientData;
    clientData = info->clientData;
    Tcl_ObjCmdProc2 *proc = info->proc;
    Tcl_Free(info);
    if (objc < 0) {
	objc = -1;
    }
    return proc(clientData, interp, (size_t)objc, objv);
}

int
Tcl_NRCallObjProc2(
    Tcl_Interp *interp,
    Tcl_ObjCmdProc2 *objProc,
    void *clientData,
    size_t objc,
    Tcl_Obj *const objv[])
{
    if (objc > INT_MAX) {
	Tcl_WrongNumArgs(interp, 1, objv, "?args?");
	return TCL_ERROR;
    }

    NRE_callback *rootPtr = TOP_CB(interp);
    CmdWrapperInfo *info = (CmdWrapperInfo *)Tcl_Alloc(sizeof(CmdWrapperInfo));
    info->clientData = clientData;
    info->proc = objProc;

    TclNRAddCallback(interp, Dispatch, wrapperNRObjProc, info,
	    INT2PTR(objc), objv);
8463
8464
8465
8466
8467
8468
8469



8470
8471
8472
8473
8474
8475
8476
8477
static int cmdWrapperNreProc(
    void *clientData,
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj *const objv[])
{
    CmdWrapperInfo *info = (CmdWrapperInfo *)clientData;



    return info->nreProc(info->clientData, interp, objc, objv);
}

Tcl_Command
Tcl_NRCreateCommand2(
    Tcl_Interp *interp,		/* Token for command interpreter (returned by
				 * previous call to Tcl_CreateInterp). */
    const char *cmdName,	/* Name of command. If it contains namespace







>
>
>
|







8553
8554
8555
8556
8557
8558
8559
8560
8561
8562
8563
8564
8565
8566
8567
8568
8569
8570
static int cmdWrapperNreProc(
    void *clientData,
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj *const objv[])
{
    CmdWrapperInfo *info = (CmdWrapperInfo *)clientData;
    if (objc < 0) {
	objc = -1;
    }
    return info->nreProc(info->clientData, interp, (size_t)objc, objv);
}

Tcl_Command
Tcl_NRCreateCommand2(
    Tcl_Interp *interp,		/* Token for command interpreter (returned by
				 * previous call to Tcl_CreateInterp). */
    const char *cmdName,	/* Name of command. If it contains namespace
8487
8488
8489
8490
8491
8492
8493

8494
8495
8496
8497
8498
8499
8500
8501
8502
8503
				 * function. */
    Tcl_CmdDeleteProc *deleteProc)
				/* If not NULL, gives a function to call when
				 * this command is deleted. */
{
    CmdWrapperInfo *info = (CmdWrapperInfo *)Tcl_Alloc(sizeof(CmdWrapperInfo));
    info->proc = proc;

    info->nreProc = nreProc;
    info->deleteProc = deleteProc;
    info->clientData = clientData;
    return Tcl_NRCreateCommand(interp, cmdName,
	    (proc ? cmdWrapperProc : NULL),
	    (nreProc ? cmdWrapperNreProc : NULL),
	    info, cmdWrapperDeleteProc);
}

Tcl_Command







>


|







8580
8581
8582
8583
8584
8585
8586
8587
8588
8589
8590
8591
8592
8593
8594
8595
8596
8597
				 * function. */
    Tcl_CmdDeleteProc *deleteProc)
				/* If not NULL, gives a function to call when
				 * this command is deleted. */
{
    CmdWrapperInfo *info = (CmdWrapperInfo *)Tcl_Alloc(sizeof(CmdWrapperInfo));
    info->proc = proc;
    info->clientData = clientData;
    info->nreProc = nreProc;
    info->deleteProc = deleteProc;
    info->deleteData = clientData;
    return Tcl_NRCreateCommand(interp, cmdName,
	    (proc ? cmdWrapperProc : NULL),
	    (nreProc ? cmdWrapperNreProc : NULL),
	    info, cmdWrapperDeleteProc);
}

Tcl_Command
Changes to generic/tclCmdAH.c.
11
12
13
14
15
16
17

18
19
20
21
22
23
24
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tclInt.h"
#ifdef _WIN32
#   include "tclWinInt.h"
#endif


/*
 * The state structure used by [foreach]. Note that the actual structure has
 * all its working arrays appended afterwards so they can be allocated and
 * freed in a single step.
 */








>







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tclInt.h"
#ifdef _WIN32
#   include "tclWinInt.h"
#endif
#include "tclArithSeries.h"

/*
 * The state structure used by [foreach]. Note that the actual structure has
 * all its working arrays appended afterwards so they can be allocated and
 * freed in a single step.
 */

267
268
269
270
271
272
273

274


275
276
277
278
279
280
281
	Tcl_WrongNumArgs(interp, 1, objv, "?dirName?");
	return TCL_ERROR;
    }

    if (objc == 2) {
	dir = objv[1];
    } else {

	TclNewLiteralStringObj(dir, "~");


	Tcl_IncrRefCount(dir);
    }
    if (Tcl_FSConvertToPathType(interp, dir) != TCL_OK) {
	result = TCL_ERROR;
    } else {
	result = Tcl_FSChdir(dir);
	if (result != TCL_OK) {







>
|
>
>







268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
	Tcl_WrongNumArgs(interp, 1, objv, "?dirName?");
	return TCL_ERROR;
    }

    if (objc == 2) {
	dir = objv[1];
    } else {
	dir = TclGetHomeDirObj(interp, NULL);
	if (dir == NULL) {
	    return TCL_ERROR;
	}
	Tcl_IncrRefCount(dir);
    }
    if (Tcl_FSConvertToPathType(interp, dir) != TCL_OK) {
	result = TCL_ERROR;
    } else {
	result = Tcl_FSChdir(dir);
	if (result != TCL_OK) {
1038
1039
1040
1041
1042
1043
1044

1045
1046
1047
1048
1049
1050
1051
	{"channels",	TclChannelNamesCmd,	TclCompileBasic0Or1ArgCmd, NULL, NULL, 0},
	{"copy",	TclFileCopyCmd,		NULL, NULL, NULL, 1},
	{"delete",	TclFileDeleteCmd,	TclCompileBasicMin0ArgCmd, NULL, NULL, 1},
	{"dirname",	PathDirNameCmd,		TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"executable",	FileAttrIsExecutableCmd, TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"exists",	FileAttrIsExistingCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"extension",	PathExtensionCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},

	{"isdirectory",	FileAttrIsDirectoryCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"isfile",	FileAttrIsFileCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"join",	PathJoinCmd,		TclCompileBasicMin1ArgCmd, NULL, NULL, 0},
	{"link",	TclFileLinkCmd,		TclCompileBasic1To3ArgCmd, NULL, NULL, 1},
	{"lstat",	FileAttrLinkStatCmd,	TclCompileBasic2ArgCmd, NULL, NULL, 1},
	{"mtime",	FileAttrModifyTimeCmd,	TclCompileBasic1Or2ArgCmd, NULL, NULL, 1},
	{"mkdir",	TclFileMakeDirsCmd,	TclCompileBasicMin0ArgCmd, NULL, NULL, 1},







>







1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
	{"channels",	TclChannelNamesCmd,	TclCompileBasic0Or1ArgCmd, NULL, NULL, 0},
	{"copy",	TclFileCopyCmd,		NULL, NULL, NULL, 1},
	{"delete",	TclFileDeleteCmd,	TclCompileBasicMin0ArgCmd, NULL, NULL, 1},
	{"dirname",	PathDirNameCmd,		TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"executable",	FileAttrIsExecutableCmd, TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"exists",	FileAttrIsExistingCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"extension",	PathExtensionCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"home",	TclFileHomeCmd,		TclCompileBasic0Or1ArgCmd, NULL, NULL, 1},
	{"isdirectory",	FileAttrIsDirectoryCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"isfile",	FileAttrIsFileCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"join",	PathJoinCmd,		TclCompileBasicMin1ArgCmd, NULL, NULL, 0},
	{"link",	TclFileLinkCmd,		TclCompileBasic1To3ArgCmd, NULL, NULL, 1},
	{"lstat",	FileAttrLinkStatCmd,	TclCompileBasic2ArgCmd, NULL, NULL, 1},
	{"mtime",	FileAttrModifyTimeCmd,	TclCompileBasic1Or2ArgCmd, NULL, NULL, 1},
	{"mkdir",	TclFileMakeDirsCmd,	TclCompileBasicMin0ArgCmd, NULL, NULL, 1},
1061
1062
1063
1064
1065
1066
1067

1068
1069
1070
1071
1072
1073
1074
	{"size",	FileAttrSizeCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"split",	PathSplitCmd,		TclCompileBasic1ArgCmd, NULL, NULL, 0},
	{"stat",	FileAttrStatCmd,	TclCompileBasic2ArgCmd, NULL, NULL, 1},
	{"system",	PathFilesystemCmd,	TclCompileBasic0Or1ArgCmd, NULL, NULL, 0},
	{"tail",	PathTailCmd,		TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"tempdir",	TclFileTempDirCmd,	TclCompileBasic0Or1ArgCmd, NULL, NULL, 1},
	{"tempfile",	TclFileTemporaryCmd,	TclCompileBasic0To2ArgCmd, NULL, NULL, 1},

	{"type",	FileAttrTypeCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"volumes",	FilesystemVolumesCmd,	TclCompileBasic0ArgCmd, NULL, NULL, 1},
	{"writable",	FileAttrIsWritableCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{NULL, NULL, NULL, NULL, NULL, 0}
    };
    return TclMakeEnsemble(interp, "file", initMap);
}







>







1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
	{"size",	FileAttrSizeCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"split",	PathSplitCmd,		TclCompileBasic1ArgCmd, NULL, NULL, 0},
	{"stat",	FileAttrStatCmd,	TclCompileBasic2ArgCmd, NULL, NULL, 1},
	{"system",	PathFilesystemCmd,	TclCompileBasic0Or1ArgCmd, NULL, NULL, 0},
	{"tail",	PathTailCmd,		TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"tempdir",	TclFileTempDirCmd,	TclCompileBasic0Or1ArgCmd, NULL, NULL, 1},
	{"tempfile",	TclFileTemporaryCmd,	TclCompileBasic0To2ArgCmd, NULL, NULL, 1},
	{"tildeexpand",	TclFileTildeExpandCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"type",	FileAttrTypeCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{"volumes",	FilesystemVolumesCmd,	TclCompileBasic0ArgCmd, NULL, NULL, 1},
	{"writable",	FileAttrIsWritableCmd,	TclCompileBasic1ArgCmd, NULL, NULL, 1},
	{NULL, NULL, NULL, NULL, NULL, 0}
    };
    return TclMakeEnsemble(interp, "file", initMap);
}
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
    }

    /*
     * Break up the value lists and variable lists into elements.
     */

    for (i=0 ; i<numLists ; i++) {


	statePtr->vCopyList[i] = TclListObjCopy(interp, objv[1+i*2]);
	if (statePtr->vCopyList[i] == NULL) {
	    result = TCL_ERROR;
	    goto done;
	}
	TclListObjGetElementsM(NULL, statePtr->vCopyList[i],
		&statePtr->varcList[i], &statePtr->varvList[i]);
	if (statePtr->varcList[i] < 1) {
	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		    "%s varlist is empty",
		    (statePtr->resultList != NULL ? "lmap" : "foreach")));
	    Tcl_SetErrorCode(interp, "TCL", "OPERATION",
		    (statePtr->resultList != NULL ? "LMAP" : "FOREACH"),
		    "NEEDVARS", NULL);
	    result = TCL_ERROR;
	    goto done;
	}













	statePtr->aCopyList[i] = TclListObjCopy(interp, objv[2+i*2]);
	if (statePtr->aCopyList[i] == NULL) {
	    result = TCL_ERROR;
	    goto done;
	}
	TclListObjGetElementsM(NULL, statePtr->aCopyList[i],
		&statePtr->argcList[i], &statePtr->argvList[i]);


	j = statePtr->argcList[i] / statePtr->varcList[i];
	if ((statePtr->argcList[i] % statePtr->varcList[i]) != 0) {
	    j++;
	}
	if (j > statePtr->maxj) {
	    statePtr->maxj = j;
	}







>
>






|


|
|

|
|




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

|
>







2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
    }

    /*
     * Break up the value lists and variable lists into elements.
     */

    for (i=0 ; i<numLists ; i++) {
	/* List */
	/* Variables */
	statePtr->vCopyList[i] = TclListObjCopy(interp, objv[1+i*2]);
	if (statePtr->vCopyList[i] == NULL) {
	    result = TCL_ERROR;
	    goto done;
	}
	TclListObjGetElementsM(NULL, statePtr->vCopyList[i],
	    &statePtr->varcList[i], &statePtr->varvList[i]);
	if (statePtr->varcList[i] < 1) {
	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		"%s varlist is empty",
		(statePtr->resultList != NULL ? "lmap" : "foreach")));
	    Tcl_SetErrorCode(interp, "TCL", "OPERATION",
		(statePtr->resultList != NULL ? "LMAP" : "FOREACH"),
		"NEEDVARS", NULL);
	    result = TCL_ERROR;
	    goto done;
	}

	/* Values */
	if (TclHasInternalRep(objv[2+i*2],&tclArithSeriesType)) {
	    /* Special case for Arith Series */
	    statePtr->vCopyList[i] = TclArithSeriesObjCopy(interp, objv[2+i*2]);
	    if (statePtr->vCopyList[i] == NULL) {
		result = TCL_ERROR;
		goto done;
	    }
	    /* Don't compute values here, wait until the last momement */
	    statePtr->argcList[i] = TclArithSeriesObjLength(statePtr->vCopyList[i]);
	} else {
	    /* List values */
	    statePtr->aCopyList[i] = TclListObjCopy(interp, objv[2+i*2]);
	    if (statePtr->aCopyList[i] == NULL) {
		result = TCL_ERROR;
		goto done;
	    }
	    TclListObjGetElementsM(NULL, statePtr->aCopyList[i],
		&statePtr->argcList[i], &statePtr->argvList[i]);
	}
	/* account for variable <> value mismatch */
	j = statePtr->argcList[i] / statePtr->varcList[i];
	if ((statePtr->argcList[i] % statePtr->varcList[i]) != 0) {
	    j++;
	}
	if (j > statePtr->maxj) {
	    statePtr->maxj = j;
	}
2796
2797
2798
2799
2800
2801
2802

2803
2804
2805
2806









2807

2808
2809
2810
2811
2812
2813
2814
    struct ForeachState *statePtr)
{
    int i;
    size_t v, k;
    Tcl_Obj *valuePtr, *varValuePtr;

    for (i=0 ; i<statePtr->numLists ; i++) {

	for (v=0 ; v<statePtr->varcList[i] ; v++) {
	    k = statePtr->index[i]++;

	    if (k < statePtr->argcList[i]) {









		valuePtr = statePtr->argvList[i][k];

	    } else {
		TclNewObj(valuePtr);	/* Empty string */
	    }

	    varValuePtr = Tcl_ObjSetVar2(interp, statePtr->varvList[i][v],
		    NULL, valuePtr, TCL_LEAVE_ERR_MSG);








>


<

>
>
>
>
>
>
>
>
>
|
>







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
    struct ForeachState *statePtr)
{
    int i;
    size_t v, k;
    Tcl_Obj *valuePtr, *varValuePtr;

    for (i=0 ; i<statePtr->numLists ; i++) {
	int isarithseries = TclHasInternalRep(statePtr->vCopyList[i],&tclArithSeriesType);
	for (v=0 ; v<statePtr->varcList[i] ; v++) {
	    k = statePtr->index[i]++;

	    if (k < statePtr->argcList[i]) {
		if (isarithseries) {
		    if (TclArithSeriesObjIndex(statePtr->vCopyList[i], k, &valuePtr) != TCL_OK) {
			Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
			"\n    (setting %s loop variable \"%s\")",
			(statePtr->resultList != NULL ? "lmap" : "foreach"),
			TclGetString(statePtr->varvList[i][v])));
			return TCL_ERROR;
		    }
		} else {
		    valuePtr = statePtr->argvList[i][k];
		}
	    } else {
		TclNewObj(valuePtr);	/* Empty string */
	    }

	    varValuePtr = Tcl_ObjSetVar2(interp, statePtr->varvList[i][v],
		    NULL, valuePtr, TCL_LEAVE_ERR_MSG);

Changes to generic/tclCmdIL.c.
15
16
17
18
19
20
21



22
23
24
25
26
27
28
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tclInt.h"
#include "tclRegexp.h"




/*
 * During execution of the "lsort" command, structures of the following type
 * are used to arrange the objects being sorted into a collection of linked
 * lists.
 */








>
>
>







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tclInt.h"
#include "tclRegexp.h"
#include "tclArithSeries.h"
#include <math.h>
#include <assert.h>

/*
 * During execution of the "lsort" command, structures of the following type
 * are used to arrange the objects being sorted into a collection of linked
 * lists.
 */

89
90
91
92
93
94
95

















96
97
98
99
100
101
102
#define SORTMODE_ASCII		0
#define SORTMODE_INTEGER	1
#define SORTMODE_REAL		2
#define SORTMODE_COMMAND	3
#define SORTMODE_DICTIONARY	4
#define SORTMODE_ASCII_NC	8


















/*
 * Forward declarations for procedures defined in this file:
 */

static int		DictionaryCompare(const char *left, const char *right);
static Tcl_NRPostProc	IfConditionCallback;
static Tcl_ObjCmdProc	InfoArgsCmd;







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







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
#define SORTMODE_ASCII		0
#define SORTMODE_INTEGER	1
#define SORTMODE_REAL		2
#define SORTMODE_COMMAND	3
#define SORTMODE_DICTIONARY	4
#define SORTMODE_ASCII_NC	8

/*
 * Definitions for [lseq] command
 */
static const char *const seq_operations[] = {
    "..", "to", "count", "by", NULL
};
typedef enum Sequence_Operators {
    LSEQ_DOTS, LSEQ_TO, LSEQ_COUNT, LSEQ_BY
} SequenceOperators;
static const char *const seq_step_keywords[] = {"by", NULL};
typedef enum Step_Operators {
    STEP_BY = 4
} SequenceByMode;
typedef enum Sequence_Decoded {
     NoneArg, NumericArg, RangeKeywordArg, ByKeywordArg
} SequenceDecoded;

/*
 * Forward declarations for procedures defined in this file:
 */

static int		DictionaryCompare(const char *left, const char *right);
static Tcl_NRPostProc	IfConditionCallback;
static Tcl_ObjCmdProc	InfoArgsCmd;
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
Tcl_JoinObjCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* The argument objects. */
{
    size_t length, listLen;

    Tcl_Obj *resObjPtr = NULL, *joinObjPtr, **elemPtrs;

    if ((objc < 2) || (objc > 3)) {
	Tcl_WrongNumArgs(interp, 1, objv, "list ?joinString?");
	return TCL_ERROR;
    }

    /*
     * Make sure the list argument is a list object and get its length and a
     * pointer to its array of element pointers.
     */





    if (TclListObjGetElementsM(interp, objv[1], &listLen,
	    &elemPtrs) != TCL_OK) {
	return TCL_ERROR;

    }

    if (listLen == 0) {
	/* No elements to join; default empty result is correct. */
	return TCL_OK;
    }
    if (listLen == 1) {
	/* One element; return it */







	Tcl_SetObjResult(interp, elemPtrs[0]);

	return TCL_OK;
    }

    joinObjPtr = (objc == 2) ? Tcl_NewStringObj(" ", 1) : objv[2];
    Tcl_IncrRefCount(joinObjPtr);

    (void) Tcl_GetStringFromObj(joinObjPtr, &length);
    if (length == 0) {
	resObjPtr = TclStringCat(interp, listLen, elemPtrs, 0);
    } else {
	size_t i;

	resObjPtr = Tcl_NewObj();


	for (i = 0;  i < listLen;  i++) {
	    if (i > 0) {

		/*
		 * NOTE: This code is relying on Tcl_AppendObjToObj() **NOT**
		 * to shimmer joinObjPtr.  If it did, then the case where
		 * objv[1] and objv[2] are the same value would not be safe.
		 * Accessing elemPtrs would crash.
		 */

		Tcl_AppendObjToObj(resObjPtr, joinObjPtr);
	    }



















	    Tcl_AppendObjToObj(resObjPtr, elemPtrs[i]);

	}
    }
    Tcl_DecrRefCount(joinObjPtr);
    if (resObjPtr) {
	Tcl_SetObjResult(interp, resObjPtr);
	return TCL_OK;
    }







>












>
>
>
>
|

|
>








>
>
>
>
>
>
>
|
>













>
>
|
|

|
|
|
|
|
|

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







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
Tcl_JoinObjCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* The argument objects. */
{
    size_t length, listLen;
    int isArithSeries = 0;
    Tcl_Obj *resObjPtr = NULL, *joinObjPtr, **elemPtrs;

    if ((objc < 2) || (objc > 3)) {
	Tcl_WrongNumArgs(interp, 1, objv, "list ?joinString?");
	return TCL_ERROR;
    }

    /*
     * Make sure the list argument is a list object and get its length and a
     * pointer to its array of element pointers.
     */

    if (TclHasInternalRep(objv[1],&tclArithSeriesType)) {
	isArithSeries = 1;
	listLen = TclArithSeriesObjLength(objv[1]);
    } else {
	if (TclListObjGetElementsM(interp, objv[1], &listLen,
	    &elemPtrs) != TCL_OK) {
	    return TCL_ERROR;
	}
    }

    if (listLen == 0) {
	/* No elements to join; default empty result is correct. */
	return TCL_OK;
    }
    if (listLen == 1) {
	/* One element; return it */
	if (isArithSeries) {
	    Tcl_Obj *valueObj;
	    if (TclArithSeriesObjIndex(objv[1], 0, &valueObj) != TCL_OK) {
		return TCL_ERROR;
	    }
	    Tcl_SetObjResult(interp, valueObj);
	} else {
	    Tcl_SetObjResult(interp, elemPtrs[0]);
	}
	return TCL_OK;
    }

    joinObjPtr = (objc == 2) ? Tcl_NewStringObj(" ", 1) : objv[2];
    Tcl_IncrRefCount(joinObjPtr);

    (void) Tcl_GetStringFromObj(joinObjPtr, &length);
    if (length == 0) {
	resObjPtr = TclStringCat(interp, listLen, elemPtrs, 0);
    } else {
	size_t i;

	resObjPtr = Tcl_NewObj();
	if (isArithSeries) {
	    Tcl_Obj *valueObj;
	    for (i = 0;  i < listLen;  i++) {
		if (i > 0) {

		    /*
		     * NOTE: This code is relying on Tcl_AppendObjToObj() **NOT**
		     * to shimmer joinObjPtr.  If it did, then the case where
		     * objv[1] and objv[2] are the same value would not be safe.
		     * Accessing elemPtrs would crash.
		     */

		    Tcl_AppendObjToObj(resObjPtr, joinObjPtr);
		}
		if (TclArithSeriesObjIndex(objv[1], i, &valueObj) != TCL_OK) {
		    return TCL_ERROR;
		}
		Tcl_AppendObjToObj(resObjPtr, valueObj);
		Tcl_DecrRefCount(valueObj);
	    }
	} else {
	    for (i = 0;  i < listLen;  i++) {
		if (i > 0) {

		    /*
		     * NOTE: This code is relying on Tcl_AppendObjToObj() **NOT**
		     * to shimmer joinObjPtr.  If it did, then the case where
		     * objv[1] and objv[2] are the same value would not be safe.
		     * Accessing elemPtrs would crash.
		     */

		    Tcl_AppendObjToObj(resObjPtr, joinObjPtr);
		}
		Tcl_AppendObjToObj(resObjPtr, elemPtrs[i]);
	    }
	}
    }
    Tcl_DecrRefCount(joinObjPtr);
    if (resObjPtr) {
	Tcl_SetObjResult(interp, resObjPtr);
	return TCL_OK;
    }
2687
2688
2689
2690
2691
2692
2693



2694

2695
2696
2697
2698
2699
2700
2701

    result = TclGetIntForIndexM(interp, objv[3], /*endValue*/ listLen - 1,
	    &last);
    if (result != TCL_OK) {
	return result;
    }




    Tcl_SetObjResult(interp, TclListObjRange(objv[1], first, last));

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_LremoveObjCmd --







>
>
>
|
>







2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761

    result = TclGetIntForIndexM(interp, objv[3], /*endValue*/ listLen - 1,
	    &last);
    if (result != TCL_OK) {
	return result;
    }

    if (TclHasInternalRep(objv[1],&tclArithSeriesType)) {
	Tcl_SetObjResult(interp, TclArithSeriesObjRange(objv[1], first, last));
    } else {
	Tcl_SetObjResult(interp, TclListObjRange(objv[1], first, last));
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_LremoveObjCmd --
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
     */

    objc -= 2;
    objv += 2;

    /* Final sanity check. Do not exceed limits on max list length. */

    if (elementCount && objc > LIST_MAX/elementCount) {
	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		"max length of a Tcl list (%d elements) exceeded", LIST_MAX));
	Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL);
	return TCL_ERROR;
    }
    totalElems = objc * elementCount;

    /*
     * Get an empty list object that is allocated large enough to hold each
     * init value elementCount times.
     */

    listPtr = Tcl_NewListObj(totalElems, NULL);
    if (totalElems) {
	List *listRepPtr = ListRepPtr(listPtr);








	listRepPtr->elemCount = elementCount*objc;
	dataArray = listRepPtr->elements;
    }

    /*
     * Set the elements. Note that we handle the common degenerate case of a
     * single value being repeated separately to permit the compiler as much
     * room as possible to optimize a loop that might be run a very large
     * number of times.







|

|












|
>
>
>
>
>
>
>
|
<
<







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

    objc -= 2;
    objv += 2;

    /* Final sanity check. Do not exceed limits on max list length. */

    if (elementCount && (size_t)objc > LIST_MAX/elementCount) {
	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		"max length of a Tcl list (%" TCL_Z_MODIFIER "u elements) exceeded", LIST_MAX));
	Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL);
	return TCL_ERROR;
    }
    totalElems = objc * elementCount;

    /*
     * Get an empty list object that is allocated large enough to hold each
     * init value elementCount times.
     */

    listPtr = Tcl_NewListObj(totalElems, NULL);
    if (totalElems) {
	ListRep listRep;
	ListObjGetRep(listPtr, &listRep);
	dataArray = ListRepElementsBase(&listRep);
	listRep.storePtr->numUsed = totalElems;
	if (listRep.spanPtr) {
	    /* Future proofing in case Tcl_NewListObj returns a span */
	    listRep.spanPtr->spanStart = listRep.storePtr->firstUsed;
	    listRep.spanPtr->spanLength = listRep.storePtr->numUsed;
	}


    }

    /*
     * Set the elements. Note that we handle the common degenerate case of a
     * single value being repeated separately to permit the compiler as much
     * room as possible to optimize a loop that might be run a very large
     * number of times.
3066
3067
3068
3069
3070
3071
3072











3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092


3093
3094





3095
3096
3097
3098
3099
3100
3101
    Tcl_Obj **elemv;
    size_t elemc, i, j;

    if (objc != 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "list");
	return TCL_ERROR;
    }











    if (TclListObjGetElementsM(interp, objv[1], &elemc, &elemv) != TCL_OK) {
	return TCL_ERROR;
    }

    /*
     * If the list is empty, just return it. [Bug 1876793]
     */

    if (!elemc) {
	Tcl_SetObjResult(interp, objv[1]);
	return TCL_OK;
    }

    if (Tcl_IsShared(objv[1])
	    || (ListRepPtr(objv[1])->refCount > 1)) {	/* Bug 1675044 */
	Tcl_Obj *resultObj, **dataArray;
	List *listRepPtr;

	resultObj = Tcl_NewListObj(elemc, NULL);
	listRepPtr = ListRepPtr(resultObj);


	listRepPtr->elemCount = elemc;
	dataArray = listRepPtr->elements;






	for (i=0,j=elemc-1 ; i<elemc ; i++,j--) {
	    dataArray[j] = elemv[i];
	    Tcl_IncrRefCount(elemv[i]);
	}

	Tcl_SetObjResult(interp, resultObj);







>
>
>
>
>
>
>
>
>
>
>














|

|


|
>
>
|
|
>
>
>
>
>







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
    Tcl_Obj **elemv;
    size_t elemc, i, j;

    if (objc != 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "list");
	return TCL_ERROR;
    }

    /*
     *  Handle ArithSeries special case - don't shimmer a series into a list
     *  just to reverse it.
     */
    if (TclHasInternalRep(objv[1],&tclArithSeriesType)) {
        Tcl_SetObjResult(interp, TclArithSeriesObjReverse(objv[1]));
	return TCL_OK;
    } /* end ArithSeries */

    /* True List */
    if (TclListObjGetElementsM(interp, objv[1], &elemc, &elemv) != TCL_OK) {
	return TCL_ERROR;
    }

    /*
     * If the list is empty, just return it. [Bug 1876793]
     */

    if (!elemc) {
	Tcl_SetObjResult(interp, objv[1]);
	return TCL_OK;
    }

    if (Tcl_IsShared(objv[1])
	|| ListObjRepIsShared(objv[1])) { /* Bug 1675044 */
	Tcl_Obj *resultObj, **dataArray;
	ListRep listRep;

	resultObj = Tcl_NewListObj(elemc, NULL);

	/* Modify the internal rep in-place */
	ListObjGetRep(resultObj, &listRep);
	listRep.storePtr->numUsed = elemc;
	dataArray = ListRepElementsBase(&listRep);
	if (listRep.spanPtr) {
	    /* Future proofing */
	    listRep.spanPtr->spanStart = listRep.storePtr->firstUsed;
	    listRep.spanPtr->spanLength = listRep.storePtr->numUsed;
	}

	for (i=0,j=elemc-1 ; i<elemc ; i++,j--) {
	    dataArray[j] = elemv[i];
	    Tcl_IncrRefCount(elemv[i]);
	}

	Tcl_SetObjResult(interp, resultObj);
3963
3964
3965
3966
3967
3968
3969

















































































































































































































































































































































































































3970
3971
3972
3973
3974
3975
3976
    Tcl_SetObjResult(interp, listPtr);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *

















































































































































































































































































































































































































 * Tcl_LsortObjCmd --
 *
 *	This procedure is invoked to process the "lsort" Tcl command. See the
 *	user documentation for details on what it does.
 *
 * Results:
 *	A standard Tcl result.







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







4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
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
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
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
    Tcl_SetObjResult(interp, listPtr);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * SequenceIdentifyArgument --
 *   (for [lseq] command)
 *
 *  Given a Tcl_Obj, identify if it is a keyword or a number
 *
 *  Return Value
 *    0 - failure, unexpected value
 *    1 - value is a number
 *    2 - value is an operand keyword
 *    3 - value is a by keyword
 *
 *  The decoded value will be assigned to the appropriate
 *  pointer, if supplied.
 */

static SequenceDecoded
SequenceIdentifyArgument(
     Tcl_Interp *interp,        /* for error reporting  */
     Tcl_Obj *argPtr,           /* Argument to decode   */
     Tcl_Obj **numValuePtr,     /* Return numeric value */
     int *keywordIndexPtr)      /* Return keyword enum  */
{
    int status;
    SequenceOperators opmode;
    SequenceByMode bymode;
    union {
	Tcl_WideInt i;
	double d;
    } nvalue;

    status = TclGetNumberFromObj(NULL, argPtr, (ClientData*)&nvalue, keywordIndexPtr);
    if (status == TCL_OK) {
	if (numValuePtr) {
	    *numValuePtr = argPtr;
	}
	return NumericArg;
    } else {
	/* Check for an index expression */
	long value;
	double dvalue;
	Tcl_Obj *exprValueObj;
	int keyword;
	Tcl_InterpState savedstate;
	savedstate = Tcl_SaveInterpState(interp, status);
	if (Tcl_ExprLongObj(interp, argPtr, &value) != TCL_OK) {
	    status = Tcl_RestoreInterpState(interp, savedstate);
	    exprValueObj = argPtr;
	} else {
	    // Determine if expression is double or int
	    if (Tcl_ExprDoubleObj(interp, argPtr, &dvalue) != TCL_OK) {
		keyword = TCL_NUMBER_INT;
		exprValueObj = argPtr;
	    } else {
		if (floor(dvalue) == dvalue) {
		    exprValueObj = Tcl_NewWideIntObj(value);
		    keyword = TCL_NUMBER_INT;
		} else {
		    exprValueObj = Tcl_NewDoubleObj(dvalue);
		    keyword = TCL_NUMBER_DOUBLE;
		}
	    }
	    status = Tcl_RestoreInterpState(interp, savedstate);
	    if (numValuePtr) {
		*numValuePtr = exprValueObj;
	    }
	    if (keywordIndexPtr) {
		*keywordIndexPtr = keyword ;// type of expression result
	    }
	    return NumericArg;
	}
    }

    status = Tcl_GetIndexFromObj(NULL, argPtr, seq_operations,
				 "range operation", 0, &opmode);
    if (status == TCL_OK) {
	if (keywordIndexPtr) {
	    *keywordIndexPtr = opmode;
	}
	return RangeKeywordArg;
    }

    status = Tcl_GetIndexFromObj(NULL, argPtr, seq_step_keywords,
				 "step keyword", 0, &bymode);
    if (status == TCL_OK) {
	if (keywordIndexPtr) {
	    *keywordIndexPtr = bymode;
	}
	return ByKeywordArg;
    }
    return NoneArg;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_LseqObjCmd --
 *
 *	This procedure is invoked to process the "lseq" Tcl command.
 *	See the user documentation for details on what it does.
 *
 * Enumerated possible argument patterns:
 *
 * 1:
 *    lseq n
 * 2:
 *    lseq n n
 * 3:
 *    lseq n n n
 *    lseq n 'to' n
 *    lseq n 'count' n
 *    lseq n 'by' n
 * 4:
 *    lseq n 'to' n n
 *    lseq n n 'by' n
 *    lseq n 'count' n n
 * 5:
 *    lseq n 'to' n 'by' n
 *    lseq n 'count' n 'by' n
 *
 * Results:
 *	A standard Tcl object result.
 *
 * Side effects:
 *	See the user documentation.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_LseqObjCmd(
    TCL_UNUSED(ClientData),
    Tcl_Interp *interp,	   /* Current interpreter. */
    int objc,		   /* Number of arguments. */
    Tcl_Obj *const objv[]) /* The argument objects. */
{
    Tcl_Obj *elementCount = NULL;
    Tcl_Obj *start = NULL, *end = NULL, *step = NULL;
    Tcl_WideInt values[5];
    Tcl_Obj *numValues[5];
    Tcl_Obj *numberObj;
    int status, keyword, useDoubles = 0;
    Tcl_Obj *arithSeriesPtr;
    SequenceOperators opmode;
    SequenceDecoded decoded;
    int i, arg_key = 0, value_i = 0;
    // Default constants
    Tcl_Obj *zero = Tcl_NewIntObj(0);
    Tcl_Obj *one = Tcl_NewIntObj(1);

    /*
     * Create a decoding key by looping through the arguments and identify
     * what kind of argument each one is.  Encode each argument as a decimal
     * digit.
     */
    if (objc > 6) {
	 /* Too many arguments */
	 arg_key=0;
    } else for (i=1; i<objc; i++) {
	 arg_key = (arg_key * 10);
	 numValues[value_i] = NULL;
	 decoded = SequenceIdentifyArgument(interp, objv[i], &numberObj, &keyword);
	 switch (decoded) {

	 case NoneArg:
	      /*
	       * Unrecognizable argument
	       * Reproduce operation error message
	       */
	      status = Tcl_GetIndexFromObj(interp, objv[i], seq_operations,
		           "operation", 0, &opmode);
	      goto done;

	 case NumericArg:
	      arg_key += NumericArg;
	      numValues[value_i] = numberObj;
	      Tcl_IncrRefCount(numValues[value_i]);
	      values[value_i] = keyword;  // This is the TCL_NUMBER_* value
	      useDoubles = useDoubles ? useDoubles : keyword == TCL_NUMBER_DOUBLE;
	      value_i++;
	      break;

	 case RangeKeywordArg:
	      arg_key += RangeKeywordArg;
	      values[value_i] = keyword;
	      value_i++;
	      break;

	 case ByKeywordArg:
	      arg_key += ByKeywordArg;
	      values[value_i] = keyword;
	      value_i++;
	      break;

	 default:
	      arg_key += 9; // Error state
	      value_i++;
	      break;
	 }
    }

    /*
     * The key encoding defines a valid set of arguments, or indicates an
     * error condition; process the values accordningly.
     */
    switch (arg_key) {

/*    No argument */
    case 0:
	 Tcl_WrongNumArgs(interp, 1, objv,
	     "n ??op? n ??by? n??");
	 status = TCL_ERROR;
	 goto done;
	 break;

/*    range n */
    case 1:
	start = zero;
	elementCount = numValues[0];
	end = NULL;
	step = one;
	break;

/*    range n n */
    case 11:
	start = numValues[0];
	end = numValues[1];
	break;

/*    range n n n */
    case 111:
	start = numValues[0];
	end = numValues[1];
	step = numValues[2];
	break;

/*    range n 'to' n    */
/*    range n 'count' n */
/*    range n 'by' n    */
    case 121:
	opmode = (SequenceOperators)values[1];
	switch (opmode) {
	case LSEQ_DOTS:
	case LSEQ_TO:
	    start = numValues[0];
	    end = numValues[2];
	    break;
	case LSEQ_BY:
	    start = zero;
	    elementCount = numValues[0];
	    step = numValues[2];
	    break;
	case LSEQ_COUNT:
	    start = numValues[0];
	    elementCount = numValues[2];
	    step = one;
	    break;
	default:
	    status = TCL_ERROR;
	    goto done;
	}
	break;

/*    range n 'to' n n    */
/*    range n 'count' n n */
    case 1211:
	opmode = (SequenceOperators)values[1];
	switch (opmode) {
	case LSEQ_DOTS:
	case LSEQ_TO:
	    start = numValues[0];
	    end = numValues[2];
	    step = numValues[3];
	    break;
	case LSEQ_COUNT:
	    start = numValues[0];
	    elementCount = numValues[2];
	    step = numValues[3];
	    break;
	case LSEQ_BY:
	    /* Error case */
	    status = TCL_ERROR;
	    goto done;
	    break;
	default:
	    status = TCL_ERROR;
	    goto done;
	    break;
	}
	break;

/*    range n n 'by' n */
    case 1121:
	start = numValues[0];
	end = numValues[1];
	opmode = (SequenceOperators)values[2];
	switch (opmode) {
	case LSEQ_BY:
	    step = numValues[3];
	    break;
	case LSEQ_DOTS:
	case LSEQ_TO:
	case LSEQ_COUNT:
	default:
	    status = TCL_ERROR;
	    goto done;
	    break;
	}
	break;

/*    range n 'to' n 'by' n    */
/*    range n 'count' n 'by' n */
    case 12121:
	start = numValues[0];
	opmode = (SequenceOperators)values[3];
	switch (opmode) {
	case LSEQ_BY:
	    step = numValues[4];
	    break;
	default:
	    status = TCL_ERROR;
	    goto done;
	    break;
	}
	opmode = (SequenceOperators)values[1];
	switch (opmode) {
	case LSEQ_DOTS:
	case LSEQ_TO:
	    start = numValues[0];
	    end = numValues[2];
	    break;
	case LSEQ_COUNT:
	    start = numValues[0];
	    elementCount = numValues[2];
	    break;
	default:
	    status = TCL_ERROR;
	    goto done;
	    break;
	}
	break;

/*    Error cases: incomplete arguments */
    case 12:
	 opmode = (SequenceOperators)values[1]; goto KeywordError; break;
    case 112:
	 opmode = (SequenceOperators)values[2]; goto KeywordError; break;
    case 1212:
	 opmode = (SequenceOperators)values[3]; goto KeywordError; break;
    KeywordError:
	 status = TCL_ERROR;
	 switch (opmode) {
	 case LSEQ_DOTS:
	 case LSEQ_TO:
	      Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		  "missing \"to\" value."));
	      break;
	 case LSEQ_COUNT:
	      Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		  "missing \"count\" value."));
	      break;
	 case LSEQ_BY:
	      Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		  "missing \"by\" value."));
	      break;
	 }
	 status = TCL_ERROR;
	 goto done;
	 break;

/*    All other argument errors */
    default:
	 Tcl_WrongNumArgs(interp, 1, objv, "n ??op? n ??by? n??");
	 status = TCL_ERROR;
	 goto done;
	 break;
    }

    /*
     * Success!  Now lets create the series object.
     */
    arithSeriesPtr = TclNewArithSeriesObj(useDoubles, start, end, step, elementCount);

    Tcl_SetObjResult(interp, arithSeriesPtr);
    status = TCL_OK;

 done:
    // Free number arguments.
    while (--value_i>=0) {
	if (numValues[value_i]) Tcl_DecrRefCount(numValues[value_i]);
    }

    // Free constants
    Tcl_DecrRefCount(zero);
    Tcl_DecrRefCount(one);

    return status;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_LsortObjCmd --
 *
 *	This procedure is invoked to process the "lsort" Tcl command. See the
 *	user documentation for details on what it does.
 *
 * Results:
 *	A standard Tcl result.
4231
4232
4233
4234
4235
4236
4237




4238
4239

4240
4241
4242
4243
4244
4245
4246
	    sortInfo.resultCode = TCL_ERROR;
	    goto done;
	}
	Tcl_ListObjAppendElement(interp, newCommandPtr, Tcl_NewObj());
	sortInfo.compareCmdPtr = newCommandPtr;
    }





    sortInfo.resultCode = TclListObjGetElementsM(interp, listObj,
	    &length, &listObjPtrs);

    if (sortInfo.resultCode != TCL_OK || length <= 0) {
	goto done;
    }

    /*
     * Check for sanity when grouping elements of the overall list together
     * because of the -stride option. [TIP #326]







>
>
>
>
|

>







4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
	    sortInfo.resultCode = TCL_ERROR;
	    goto done;
	}
	Tcl_ListObjAppendElement(interp, newCommandPtr, Tcl_NewObj());
	sortInfo.compareCmdPtr = newCommandPtr;
    }

    if (TclHasInternalRep(listObj,&tclArithSeriesType)) {
	sortInfo.resultCode = TclArithSeriesGetElements(interp,
	    listObj, &length, &listObjPtrs);
    } else {
	sortInfo.resultCode = TclListObjGetElementsM(interp, listObj,
	    &length, &listObjPtrs);
    }
    if (sortInfo.resultCode != TCL_OK || length <= 0) {
	goto done;
    }

    /*
     * Check for sanity when grouping elements of the overall list together
     * because of the -stride option. [TIP #326]
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
    }

    /*
     * Now store the sorted elements in the result list.
     */

    if (sortInfo.resultCode == TCL_OK) {
	List *listRepPtr;
	Tcl_Obj **newArray, *objPtr;

	resultPtr = Tcl_NewListObj(sortInfo.numElements * groupSize, NULL);
	listRepPtr = ListRepPtr(resultPtr);
	newArray = listRepPtr->elements;
	if (group) {
	    for (i=0; elementPtr!=NULL ; elementPtr=elementPtr->nextPtr) {
		idx = elementPtr->payload.index;
		for (j = 0; j < groupSize; j++) {
		    if (indices) {
			TclNewIndexObj(objPtr, idx + j - groupOffset);
			newArray[i++] = objPtr;







|



|
|







4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
    }

    /*
     * Now store the sorted elements in the result list.
     */

    if (sortInfo.resultCode == TCL_OK) {
	ListRep listRep;
	Tcl_Obj **newArray, *objPtr;

	resultPtr = Tcl_NewListObj(sortInfo.numElements * groupSize, NULL);
	ListObjGetRep(resultPtr, &listRep);
	newArray = ListRepElementsBase(&listRep);
	if (group) {
	    for (i=0; elementPtr!=NULL ; elementPtr=elementPtr->nextPtr) {
		idx = elementPtr->payload.index;
		for (j = 0; j < groupSize; j++) {
		    if (indices) {
			TclNewIndexObj(objPtr, idx + j - groupOffset);
			newArray[i++] = objPtr;
4449
4450
4451
4452
4453
4454
4455

4456



4457
4458
4459
4460
4461
4462
4463
	} else {
	    for (i=0; elementPtr != NULL ; elementPtr = elementPtr->nextPtr) {
		objPtr = elementPtr->payload.objPtr;
		newArray[i++] = objPtr;
		Tcl_IncrRefCount(objPtr);
	    }
	}

	listRepPtr->elemCount = i;



	Tcl_SetObjResult(interp, resultPtr);
    }

  done:
    if (sortMode == SORTMODE_COMMAND) {
	TclDecrRefCount(sortInfo.compareCmdPtr);
	TclDecrRefCount(listObj);







>
|
>
>
>







4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
	} else {
	    for (i=0; elementPtr != NULL ; elementPtr = elementPtr->nextPtr) {
		objPtr = elementPtr->payload.objPtr;
		newArray[i++] = objPtr;
		Tcl_IncrRefCount(objPtr);
	    }
	}
	listRep.storePtr->numUsed = i;
	if (listRep.spanPtr) {
	    listRep.spanPtr->spanStart = listRep.storePtr->firstUsed;
	    listRep.spanPtr->spanLength = listRep.storePtr->numUsed;
	}
	Tcl_SetObjResult(interp, resultPtr);
    }

  done:
    if (sortMode == SORTMODE_COMMAND) {
	TclDecrRefCount(sortInfo.compareCmdPtr);
	TclDecrRefCount(listObj);
Changes to generic/tclCmdMZ.c.
3756
3757
3758
3759
3760
3761
3762

3763
3764



3765
3766
3767
3768
3769
3770
3771
		Tcl_ListObjAppendElement(NULL, indicesObj,
			Tcl_NewListObj(2, rangeObjAry));
	    }

	    if (matchVarObj != NULL) {
		Tcl_Obj *substringObj;


		substringObj = Tcl_GetRange(stringObj,
			info.matches[j].start, info.matches[j].end-1);




		/*
		 * Never fails; the object is always clean at this point.
		 */

		Tcl_ListObjAppendElement(NULL, matchesObj, substringObj);
	    }







>
|
|
>
>
>







3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
		Tcl_ListObjAppendElement(NULL, indicesObj,
			Tcl_NewListObj(2, rangeObjAry));
	    }

	    if (matchVarObj != NULL) {
		Tcl_Obj *substringObj;

		if (info.matches[j].end + 1 > 1) {
		    substringObj = Tcl_GetRange(stringObj,
			    info.matches[j].start, info.matches[j].end-1);
		} else {
		    TclNewObj(substringObj);
		}

		/*
		 * Never fails; the object is always clean at this point.
		 */

		Tcl_ListObjAppendElement(NULL, matchesObj, substringObj);
	    }
Changes to generic/tclCompile.c.
2821
2822
2823
2824
2825
2826
2827




2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
    objArrayBytes = envPtr->literalArrayNext * sizeof(Tcl_Obj *);
    exceptArrayBytes = envPtr->exceptArrayNext * sizeof(ExceptionRange);
    auxDataArrayBytes = envPtr->auxDataArrayNext * sizeof(AuxData);
    cmdLocBytes = GetCmdLocEncodingSize(envPtr);

    /*
     * Compute the total number of bytes needed for this bytecode.




     */

    structureSize = sizeof(ByteCode);
    structureSize += TCL_ALIGN(codeBytes);	  /* align object array */
    structureSize += TCL_ALIGN(objArrayBytes);	  /* align exc range arr */
    structureSize += TCL_ALIGN(exceptArrayBytes); /* align AuxData array */
    structureSize += auxDataArrayBytes;
    structureSize += cmdLocBytes;

    if (envPtr->iPtr->varFramePtr != NULL) {







>
>
>
>


|







2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
    objArrayBytes = envPtr->literalArrayNext * sizeof(Tcl_Obj *);
    exceptArrayBytes = envPtr->exceptArrayNext * sizeof(ExceptionRange);
    auxDataArrayBytes = envPtr->auxDataArrayNext * sizeof(AuxData);
    cmdLocBytes = GetCmdLocEncodingSize(envPtr);

    /*
     * Compute the total number of bytes needed for this bytecode.
     *
     * Note that code bytes need not be aligned but since later elements are we
     * need to pad anyway, either directly after ByteCode or after codeBytes,
     * and it's easier and more consistent to do the former.
     */

    structureSize = TCL_ALIGN(sizeof(ByteCode));  /* align code bytes */
    structureSize += TCL_ALIGN(codeBytes);	  /* align object array */
    structureSize += TCL_ALIGN(objArrayBytes);	  /* align exc range arr */
    structureSize += TCL_ALIGN(exceptArrayBytes); /* align AuxData array */
    structureSize += auxDataArrayBytes;
    structureSize += cmdLocBytes;

    if (envPtr->iPtr->varFramePtr != NULL) {
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
    codePtr->numLitObjects = numLitObjects;
    codePtr->numExceptRanges = envPtr->exceptArrayNext;
    codePtr->numAuxDataItems = envPtr->auxDataArrayNext;
    codePtr->numCmdLocBytes = cmdLocBytes;
    codePtr->maxExceptDepth = envPtr->maxExceptDepth;
    codePtr->maxStackDepth = envPtr->maxStackDepth;

    p += sizeof(ByteCode);
    codePtr->codeStart = p;
    memcpy(p, envPtr->codeStart, codeBytes);

    p += TCL_ALIGN(codeBytes);		/* align object array */
    codePtr->objArrayPtr = (Tcl_Obj **) p;
    for (i = 0;  i < numLitObjects;  i++) {
	codePtr->objArrayPtr[i] = TclFetchLiteral(envPtr, i);







|







2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
    codePtr->numLitObjects = numLitObjects;
    codePtr->numExceptRanges = envPtr->exceptArrayNext;
    codePtr->numAuxDataItems = envPtr->auxDataArrayNext;
    codePtr->numCmdLocBytes = cmdLocBytes;
    codePtr->maxExceptDepth = envPtr->maxExceptDepth;
    codePtr->maxStackDepth = envPtr->maxStackDepth;

    p += TCL_ALIGN(sizeof(ByteCode));	/* align code bytes */
    codePtr->codeStart = p;
    memcpy(p, envPtr->codeStart, codeBytes);

    p += TCL_ALIGN(codeBytes);		/* align object array */
    codePtr->objArrayPtr = (Tcl_Obj **) p;
    for (i = 0;  i < numLitObjects;  i++) {
	codePtr->objArrayPtr[i] = TclFetchLiteral(envPtr, i);
Changes to generic/tclDecls.h.
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
				Tcl_DString *resultPtr);
/* 187 */
EXTERN int		Tcl_LinkVar(Tcl_Interp *interp, const char *varName,
				void *addr, int type);
/* Slot 188 is reserved */
/* 189 */
EXTERN Tcl_Channel	Tcl_MakeFileChannel(void *handle, int mode);
/* 190 */
EXTERN int		Tcl_MakeSafe(Tcl_Interp *interp);
/* 191 */
EXTERN Tcl_Channel	Tcl_MakeTcpClientChannel(void *tcpSocket);
/* 192 */
EXTERN char *		Tcl_Merge(size_t argc, const char *const *argv);
/* 193 */
EXTERN Tcl_HashEntry *	Tcl_NextHashEntry(Tcl_HashSearch *searchPtr);
/* 194 */







|
<







524
525
526
527
528
529
530
531

532
533
534
535
536
537
538
				Tcl_DString *resultPtr);
/* 187 */
EXTERN int		Tcl_LinkVar(Tcl_Interp *interp, const char *varName,
				void *addr, int type);
/* Slot 188 is reserved */
/* 189 */
EXTERN Tcl_Channel	Tcl_MakeFileChannel(void *handle, int mode);
/* Slot 190 is reserved */

/* 191 */
EXTERN Tcl_Channel	Tcl_MakeTcpClientChannel(void *tcpSocket);
/* 192 */
EXTERN char *		Tcl_Merge(size_t argc, const char *const *argv);
/* 193 */
EXTERN Tcl_HashEntry *	Tcl_NextHashEntry(Tcl_HashSearch *searchPtr);
/* 194 */
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
    int (*tcl_InputBuffered) (Tcl_Channel chan); /* 183 */
    int (*tcl_InterpDeleted) (Tcl_Interp *interp); /* 184 */
    int (*tcl_IsSafe) (Tcl_Interp *interp); /* 185 */
    char * (*tcl_JoinPath) (size_t argc, const char *const *argv, Tcl_DString *resultPtr); /* 186 */
    int (*tcl_LinkVar) (Tcl_Interp *interp, const char *varName, void *addr, int type); /* 187 */
    void (*reserved188)(void);
    Tcl_Channel (*tcl_MakeFileChannel) (void *handle, int mode); /* 189 */
    int (*tcl_MakeSafe) (Tcl_Interp *interp); /* 190 */
    Tcl_Channel (*tcl_MakeTcpClientChannel) (void *tcpSocket); /* 191 */
    char * (*tcl_Merge) (size_t argc, const char *const *argv); /* 192 */
    Tcl_HashEntry * (*tcl_NextHashEntry) (Tcl_HashSearch *searchPtr); /* 193 */
    void (*tcl_NotifyChannel) (Tcl_Channel channel, int mask); /* 194 */
    Tcl_Obj * (*tcl_ObjGetVar2) (Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, int flags); /* 195 */
    Tcl_Obj * (*tcl_ObjSetVar2) (Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags); /* 196 */
    Tcl_Channel (*tcl_OpenCommandChannel) (Tcl_Interp *interp, size_t argc, const char **argv, int flags); /* 197 */







|







2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
    int (*tcl_InputBuffered) (Tcl_Channel chan); /* 183 */
    int (*tcl_InterpDeleted) (Tcl_Interp *interp); /* 184 */
    int (*tcl_IsSafe) (Tcl_Interp *interp); /* 185 */
    char * (*tcl_JoinPath) (size_t argc, const char *const *argv, Tcl_DString *resultPtr); /* 186 */
    int (*tcl_LinkVar) (Tcl_Interp *interp, const char *varName, void *addr, int type); /* 187 */
    void (*reserved188)(void);
    Tcl_Channel (*tcl_MakeFileChannel) (void *handle, int mode); /* 189 */
    void (*reserved190)(void);
    Tcl_Channel (*tcl_MakeTcpClientChannel) (void *tcpSocket); /* 191 */
    char * (*tcl_Merge) (size_t argc, const char *const *argv); /* 192 */
    Tcl_HashEntry * (*tcl_NextHashEntry) (Tcl_HashSearch *searchPtr); /* 193 */
    void (*tcl_NotifyChannel) (Tcl_Channel channel, int mask); /* 194 */
    Tcl_Obj * (*tcl_ObjGetVar2) (Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, int flags); /* 195 */
    Tcl_Obj * (*tcl_ObjSetVar2) (Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags); /* 196 */
    Tcl_Channel (*tcl_OpenCommandChannel) (Tcl_Interp *interp, size_t argc, const char **argv, int flags); /* 197 */
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
#define Tcl_JoinPath \
	(tclStubsPtr->tcl_JoinPath) /* 186 */
#define Tcl_LinkVar \
	(tclStubsPtr->tcl_LinkVar) /* 187 */
/* Slot 188 is reserved */
#define Tcl_MakeFileChannel \
	(tclStubsPtr->tcl_MakeFileChannel) /* 189 */
#define Tcl_MakeSafe \
	(tclStubsPtr->tcl_MakeSafe) /* 190 */
#define Tcl_MakeTcpClientChannel \
	(tclStubsPtr->tcl_MakeTcpClientChannel) /* 191 */
#define Tcl_Merge \
	(tclStubsPtr->tcl_Merge) /* 192 */
#define Tcl_NextHashEntry \
	(tclStubsPtr->tcl_NextHashEntry) /* 193 */
#define Tcl_NotifyChannel \







<
|







2885
2886
2887
2888
2889
2890
2891

2892
2893
2894
2895
2896
2897
2898
2899
#define Tcl_JoinPath \
	(tclStubsPtr->tcl_JoinPath) /* 186 */
#define Tcl_LinkVar \
	(tclStubsPtr->tcl_LinkVar) /* 187 */
/* Slot 188 is reserved */
#define Tcl_MakeFileChannel \
	(tclStubsPtr->tcl_MakeFileChannel) /* 189 */

/* Slot 190 is reserved */
#define Tcl_MakeTcpClientChannel \
	(tclStubsPtr->tcl_MakeTcpClientChannel) /* 191 */
#define Tcl_Merge \
	(tclStubsPtr->tcl_Merge) /* 192 */
#define Tcl_NextHashEntry \
	(tclStubsPtr->tcl_NextHashEntry) /* 193 */
#define Tcl_NotifyChannel \
Changes to generic/tclDisassemble.c.
296
297
298
299
300
301
302
303

304
305
306
307
308
309
310
311
312
	    codePtr->numSrcBytes?
		    codePtr->structureSize/(float)codePtr->numSrcBytes :
#endif
	    0.0);

#ifdef TCL_COMPILE_STATS
    Tcl_AppendPrintfToObj(bufferObj,
	    "  Code %" TCL_Z_MODIFIER "u = header %" TCL_Z_MODIFIER "u+inst %" TCL_Z_MODIFIER "u+litObj %" TCL_Z_MODIFIER "u+exc %" TCL_Z_MODIFIER "u+aux %" TCL_Z_MODIFIER "u+cmdMap %" TCL_Z_MODIFIER "u\n",

	    codePtr->structureSize,
	    sizeof(ByteCode) - sizeof(size_t) - sizeof(Tcl_Time),
	    codePtr->numCodeBytes,
	    codePtr->numLitObjects * sizeof(Tcl_Obj *),
	    codePtr->numExceptRanges*sizeof(ExceptionRange),
	    codePtr->numAuxDataItems * sizeof(AuxData),
	    codePtr->numCmdLocBytes);
#endif /* TCL_COMPILE_STATS */








|
>

|







296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
	    codePtr->numSrcBytes?
		    codePtr->structureSize/(float)codePtr->numSrcBytes :
#endif
	    0.0);

#ifdef TCL_COMPILE_STATS
    Tcl_AppendPrintfToObj(bufferObj,
	    "  Code %" TCL_Z_MODIFIER "u = header %" TCL_Z_MODIFIER "u+inst %" TCL_Z_MODIFIER "u+litObj %"
	    TCL_Z_MODIFIER "u+exc %" TCL_Z_MODIFIER "u+aux %" TCL_Z_MODIFIER "u+cmdMap %" TCL_Z_MODIFIER "u\n",
	    codePtr->structureSize,
	    offsetof(ByteCode, localCachePtr),
	    codePtr->numCodeBytes,
	    codePtr->numLitObjects * sizeof(Tcl_Obj *),
	    codePtr->numExceptRanges*sizeof(ExceptionRange),
	    codePtr->numAuxDataItems * sizeof(AuxData),
	    codePtr->numCmdLocBytes);
#endif /* TCL_COMPILE_STATS */

Changes to generic/tclEnv.c.
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
	 */

	Tcl_Free(p);
#endif /* HAVE_PUTENV_THAT_COPIES */
    }

    Tcl_MutexUnlock(&envMutex);

    if (!strcmp(name, "HOME")) {
	/*
	 * If the user's home directory has changed, we must invalidate the
	 * filesystem cache, because '~' expansions will now be incorrect.
	 */

	Tcl_FSMountsChanged(NULL);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_PutEnv --
 *







<
<
<
<
<
<
<
<
<







360
361
362
363
364
365
366









367
368
369
370
371
372
373
	 */

	Tcl_Free(p);
#endif /* HAVE_PUTENV_THAT_COPIES */
    }

    Tcl_MutexUnlock(&envMutex);









}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_PutEnv --
 *
Changes to generic/tclEvent.c.
45
46
47
48
49
50
51













52
53
54
55
56
57
58
				 * waiting to be processed for this
				 * interpreter (NULL if none). */
    BgError *lastBgPtr;		/* Last in list of all background errors
				 * waiting to be processed for this
				 * interpreter (NULL if none). */
} ErrAssocData;














/*
 * For each exit handler created with a call to Tcl_Create(Late)ExitHandler
 * there is a structure of the following type:
 */

typedef struct ExitHandler {
    Tcl_ExitProc *proc;		/* Function to call when process exits. */







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







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
				 * waiting to be processed for this
				 * interpreter (NULL if none). */
    BgError *lastBgPtr;		/* Last in list of all background errors
				 * waiting to be processed for this
				 * interpreter (NULL if none). */
} ErrAssocData;

/*
 * For each "vwait" event source a structure of the following type
 * is used:
 */

typedef struct {
    int *donePtr;		/* Pointer to flag to signal or NULL. */
    int sequence;		/* Order of occurrence. */
    int mask;			/* 0, or TCL_READABLE/TCL_WRITABLE. */
    Tcl_Obj *sourceObj;		/* Name of the event source, either a
				 * variable name or channel name. */
} VwaitItem;

/*
 * For each exit handler created with a call to Tcl_Create(Late)ExitHandler
 * there is a structure of the following type:
 */

typedef struct ExitHandler {
    Tcl_ExitProc *proc;		/* Function to call when process exits. */
112
113
114
115
116
117
118



119
120
121
122
123
124
125
/*
 * Prototypes for functions referenced only in this file:
 */

static void		BgErrorDeleteProc(void *clientData,
			    Tcl_Interp *interp);
static void		HandleBgErrors(void *clientData);



static char *		VwaitVarProc(void *clientData,
			    Tcl_Interp *interp, const char *name1,
			    const char *name2, int flags);
static void		InvokeExitHandlers(void);
static void		FinalizeThread(int quick);

/*







>
>
>







125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*
 * Prototypes for functions referenced only in this file:
 */

static void		BgErrorDeleteProc(void *clientData,
			    Tcl_Interp *interp);
static void		HandleBgErrors(void *clientData);
static void		VwaitChannelReadProc(void *clientData, int mask);
static void		VwaitChannelWriteProc(void *clientData, int mask);
static void		VwaitTimeoutProc(void *clientData);
static char *		VwaitVarProc(void *clientData,
			    Tcl_Interp *interp, const char *name1,
			    const char *name2, int flags);
static void		InvokeExitHandlers(void);
static void		FinalizeThread(int quick);

/*
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
int
Tcl_VwaitObjCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    int done, foundEvent;
    const char *nameString;

    if (objc != 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "name");
	return TCL_ERROR;
    }
    nameString = TclGetString(objv[1]);
    if (Tcl_TraceVar2(interp, nameString, NULL,
	    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
	    VwaitVarProc, &done) != TCL_OK) {



	return TCL_ERROR;
    };
































    done = 0;



















































































































































































    foundEvent = 1;
    while (!done && foundEvent) {

	foundEvent = Tcl_DoOneEvent(TCL_ALL_EVENTS);
	if (Tcl_Canceled(interp, TCL_LEAVE_ERR_MSG) == TCL_ERROR) {
	    break;
	}
	if (Tcl_LimitExceeded(interp)) {
	    Tcl_ResetResult(interp);
	    Tcl_SetObjResult(interp, Tcl_NewStringObj("limit exceeded", -1));

	    break;
	}








    }
    Tcl_UntraceVar2(interp, nameString, NULL,
	    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
	    VwaitVarProc, &done);


    if (!foundEvent) {
	Tcl_ResetResult(interp);
	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		"can't wait for variable \"%s\": would wait forever",

		nameString));
	Tcl_SetErrorCode(interp, "TCL", "EVENT", "NO_SOURCES", NULL);
	return TCL_ERROR;

    }

    if (!done) {
	/*
	 * The interpreter's result was already set to the right error message
	 * prior to exiting the loop above.
	 */



	return TCL_ERROR;








    }

    /*
     * Clear out the interpreter's result, since it may have been set by event
     * handlers.
     */




































































    Tcl_ResetResult(interp);




















    return TCL_OK;



































}

static char *
VwaitVarProc(
    void *clientData,		/* Pointer to integer to set to 1. */
    Tcl_Interp *interp,		/* Interpreter containing variable. */
    const char *name1,		/* Name of variable. */
    const char *name2,		/* Second part of variable name. */
    TCL_UNUSED(int) /*flags*/)	/* Information about what happened. */
{
    int *donePtr = (int *)clientData;




    *donePtr = 1;

    Tcl_UntraceVar2(interp, name1, name2, TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
	    VwaitVarProc, clientData);
    return NULL;
}

/*
 *----------------------------------------------------------------------







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

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

|
>
|






>


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



|
|
>
|

|
>

>
|




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



|
<

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




|





|

>
>
>
|
>







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
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
int
Tcl_VwaitObjCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    int i, done = 0, timedOut = 0, foundEvent, any = 1, timeout = 0;
    int numItems = 0, extended = 0, result, mode, mask = TCL_ALL_EVENTS;
    Tcl_InterpState saved = NULL;
    Tcl_TimerToken timer = NULL;
    Tcl_Time before, after;


    Tcl_Channel chan;
    Tcl_WideInt diff = -1;

    VwaitItem localItems[32], *vwaitItems = localItems;
    static const char *const options[] = {
	"-all",	"-extended", "-nofileevents", "-noidleevents",
	"-notimerevents", "-nowindowevents", "-readable",
	"-timeout", "-variable", "-writable", "--", NULL
    };
    enum options {
	OPT_ALL, OPT_EXTD, OPT_NO_FEVTS, OPT_NO_IEVTS,
	OPT_NO_TEVTS, OPT_NO_WEVTS, OPT_READABLE,
	OPT_TIMEOUT, OPT_VARIABLE, OPT_WRITABLE, OPT_LAST
    } index;

    if ((objc == 2) && (strcmp(Tcl_GetString(objv[1]), "--") != 0)) {
	/*
	 * Legacy "vwait" syntax, skip option handling.
	 */
	i = 1;
	goto endOfOptionLoop;
    }

    if ((unsigned) objc - 1 > sizeof(localItems) / sizeof(localItems[0])) {
	vwaitItems = (VwaitItem *) Tcl_Alloc(sizeof(VwaitItem) * (objc - 1));
    }

    for (i = 1; i < objc; i++) {
	const char *name;

	name = TclGetString(objv[i]);
	if (name[0] != '-') {
	    break;
	}
	if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0,
		&index) != TCL_OK) {
	    result = TCL_ERROR;
	    goto done;
	}
	switch (index) {
	case OPT_ALL:
	    any = 0;
	    break;
	case OPT_EXTD:
	    extended = 1;
	    break;
	case OPT_NO_FEVTS:
	    mask &= ~TCL_FILE_EVENTS;
	    break;
	case OPT_NO_IEVTS:
	    mask &= ~TCL_IDLE_EVENTS;
	    break;
	case OPT_NO_TEVTS:
	    mask &= ~TCL_TIMER_EVENTS;
	    break;
	case OPT_NO_WEVTS:
	    mask &= ~TCL_WINDOW_EVENTS;
	    break;
	case OPT_TIMEOUT:
	    if (++i >= objc) {
	needArg:
		Tcl_ResetResult(interp);
		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
			"argument required for \"%s\"", options[index]));
		Tcl_SetErrorCode(interp, "TCL", "EVENT", "ARGUMENT", NULL);
		result = TCL_ERROR;
		goto done;
	    }
	    if (Tcl_GetIntFromObj(interp, objv[i], &timeout) != TCL_OK) {
		result = TCL_ERROR;
		goto done;
	    }
	    if (timeout < 0) {
		Tcl_ResetResult(interp);
		Tcl_SetObjResult(interp, Tcl_NewStringObj(
			"timeout must be positive", -1));
		Tcl_SetErrorCode(interp, "TCL", "EVENT", "NEGTIME", NULL);
		result = TCL_ERROR;
		goto done;
	    }
	    break;
	case OPT_LAST:
	    i++;
	    goto endOfOptionLoop;
	case OPT_VARIABLE:
	    if (++i >= objc) {
		goto needArg;
	    }
	    result = Tcl_TraceVar2(interp, TclGetString(objv[i]), NULL,
			    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
			    VwaitVarProc, &vwaitItems[numItems]);
	    if (result != TCL_OK) {
		goto done;
	    }
	    vwaitItems[numItems].donePtr = &done;
	    vwaitItems[numItems].sequence = -1;
	    vwaitItems[numItems].mask = 0;
	    vwaitItems[numItems].sourceObj = objv[i];
	    numItems++;
	    break;
	case OPT_READABLE:
	    if (++i >= objc) {
		goto needArg;
	    }
	    if (TclGetChannelFromObj(interp, objv[i], &chan, &mode, 0)
		!= TCL_OK) {
		result = TCL_ERROR;
		goto done;
	    }
	    if (!(mode & TCL_READABLE)) {
		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
			"channel \"%s\" wasn't open for reading",
			TclGetString(objv[i])));
		result = TCL_ERROR;
		goto done;
	    }
	    Tcl_CreateChannelHandler(chan, TCL_READABLE,
		    VwaitChannelReadProc, &vwaitItems[numItems]);
	    vwaitItems[numItems].donePtr = &done;
	    vwaitItems[numItems].sequence = -1;
	    vwaitItems[numItems].mask = TCL_READABLE;
	    vwaitItems[numItems].sourceObj = objv[i];
	    numItems++;
	    break;
	case OPT_WRITABLE:
	    if (++i >= objc) {
		goto needArg;
	    }
	    if (TclGetChannelFromObj(interp, objv[i], &chan, &mode, 0)
		!= TCL_OK) {
		result = TCL_ERROR;
		goto done;
	    }
	    if (!(mode & TCL_WRITABLE)) {
		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
			"channel \"%s\" wasn't open for writing",
			TclGetString(objv[i])));
		result = TCL_ERROR;
		goto done;
	    }
	    Tcl_CreateChannelHandler(chan, TCL_WRITABLE,
		    VwaitChannelWriteProc, &vwaitItems[numItems]);
	    vwaitItems[numItems].donePtr = &done;
	    vwaitItems[numItems].sequence = -1;
	    vwaitItems[numItems].mask = TCL_WRITABLE;
	    vwaitItems[numItems].sourceObj = objv[i];
	    numItems++;
	    break;
	}
    }

  endOfOptionLoop:
    if ((mask & (TCL_FILE_EVENTS | TCL_IDLE_EVENTS |
		 TCL_TIMER_EVENTS | TCL_WINDOW_EVENTS)) == 0) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"can't wait: would block forever", -1));
	Tcl_SetErrorCode(interp, "TCL", "EVENT", "NO_SOURCES", NULL);
	result = TCL_ERROR;
	goto done;
    }

    if ((timeout > 0) && ((mask & TCL_TIMER_EVENTS) == 0)) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"timer events disabled with timeout specified", -1));
	Tcl_SetErrorCode(interp, "TCL", "EVENT", "NO_TIME", NULL);
	result = TCL_ERROR;
	goto done;
    }

    for (result = TCL_OK; i < objc; i++) {
	result = Tcl_TraceVar2(interp, TclGetString(objv[i]), NULL,
			TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
			VwaitVarProc, &vwaitItems[numItems]);
	if (result != TCL_OK) {
	    break;
	}
	vwaitItems[numItems].donePtr = &done;
	vwaitItems[numItems].sequence = -1;
	vwaitItems[numItems].mask = 0;
	vwaitItems[numItems].sourceObj = objv[i];
	numItems++;
    }
    if (result != TCL_OK) {
	result = TCL_ERROR;
	goto done;
    }

    if (!(mask & TCL_FILE_EVENTS)) {
	for (i = 0; i < numItems; i++) {
	    if (vwaitItems[i].mask) {
		Tcl_SetObjResult(interp, Tcl_NewStringObj(
			"file events disabled with channel(s) specified", -1));
		Tcl_SetErrorCode(interp, "TCL", "EVENT", "NO_FILE_EVENT", NULL);
		result = TCL_ERROR;
		goto done;
	    }
	}
    }

    if (timeout > 0) {
	vwaitItems[numItems].donePtr = &timedOut;
	vwaitItems[numItems].sequence = -1;
	vwaitItems[numItems].mask = 0;
	vwaitItems[numItems].sourceObj = NULL;
	timer = Tcl_CreateTimerHandler(timeout, VwaitTimeoutProc,
			&vwaitItems[numItems]);
	Tcl_GetTime(&before);
    } else {
	timeout = 0;
    }

    if ((numItems == 0) && (timeout == 0)) {
	/*
	 * "vwait" is equivalent to "update",
	 * "vwait -nofileevents -notimerevents -nowindowevents"
	 * is equivalent to "update idletasks"
	 */
	any = 1;
	mask |= TCL_DONT_WAIT;
    }

    foundEvent = 1;
    while (!timedOut && foundEvent &&
	   ((!any && (done < numItems)) || (any && !done))) {
	foundEvent = Tcl_DoOneEvent(mask);
	if (Tcl_Canceled(interp, TCL_LEAVE_ERR_MSG) == TCL_ERROR) {
	    break;
	}
	if (Tcl_LimitExceeded(interp)) {
	    Tcl_ResetResult(interp);
	    Tcl_SetObjResult(interp, Tcl_NewStringObj("limit exceeded", -1));
	    Tcl_SetErrorCode(interp, "TCL", "EVENT", "LIMIT", NULL);
	    break;
	}
	if ((numItems == 0) && (timeout == 0)) {
	    /*
	     * Behavior like "update": clear interpreter's result because
	     * event handlers could have executed commands.
	     */
	    Tcl_ResetResult(interp);
	    result = TCL_OK;
	    goto done;
	}



    }

    if (!foundEvent) {
	Tcl_ResetResult(interp);
	Tcl_SetObjResult(interp, Tcl_NewStringObj((numItems == 0) ?
		"can't wait: would wait forever" :
		"can't wait for variable(s)/channel(s): would wait forever",
		-1));
	Tcl_SetErrorCode(interp, "TCL", "EVENT", "NO_SOURCES", NULL);
	result = TCL_ERROR;
	goto done;
    }

    if (!done && !timedOut) {
	/*
	 * The interpreter's result was already set to the right error message
	 * prior to exiting the loop above.
	 */
	result = TCL_ERROR;
	goto done;
    }

    result = TCL_OK;
    if (timeout <= 0) {
	/*
	 * Clear out the interpreter's result, since it may have been set
	 * by event handlers.
	 */
	Tcl_ResetResult(interp);
	goto done;
    }

    /*
     * When timeout was specified, report milliseconds left or -1 on timeout.

     */
    if (timedOut) {
	diff = -1;
    } else {
	Tcl_GetTime(&after);
	diff = after.sec * 1000 + after.usec / 1000;
	diff -= before.sec * 1000 + before.usec / 1000;
	diff = timeout - diff;
	if (diff < 0) {
	    diff = 0;
	}
    }

  done:
    if ((timeout > 0) && (timer != NULL)) {
	Tcl_DeleteTimerHandler(timer);
    }
    if (result != TCL_OK) {
	saved = Tcl_SaveInterpState(interp, result);
    }
    for (i = 0; i < numItems; i++) {
	if (vwaitItems[i].mask & TCL_READABLE) {
	    if (TclGetChannelFromObj(interp, vwaitItems[i].sourceObj,
		    &chan, &mode, 0) == TCL_OK) {
		Tcl_DeleteChannelHandler(chan, VwaitChannelReadProc,
			&vwaitItems[i]);
	    }
	} else if (vwaitItems[i].mask & TCL_WRITABLE) {
	    if (TclGetChannelFromObj(interp, vwaitItems[i].sourceObj,
		    &chan, &mode, 0) == TCL_OK) {
		Tcl_DeleteChannelHandler(chan, VwaitChannelWriteProc,
			&vwaitItems[i]);
	    }
	} else {
	    Tcl_UntraceVar2(interp, TclGetString(vwaitItems[i].sourceObj),
		    NULL, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
		    VwaitVarProc, &vwaitItems[i]);
	}
    }

    if (result == TCL_OK) {
	if (extended) {
	    int k;
	    Tcl_Obj *listObj, *keyObj;

	    TclNewObj(listObj);
	    for (k = 0; k < done; k++) {
		for (i = 0; i < numItems; i++) {
		    if (vwaitItems[i].sequence != k) {
			continue;
		    }
		    if (vwaitItems[i].mask & TCL_READABLE) {
			TclNewLiteralStringObj(keyObj, "readable");
		    } else if (vwaitItems[i].mask & TCL_WRITABLE) {
			TclNewLiteralStringObj(keyObj, "writable");
		    } else {
			TclNewLiteralStringObj(keyObj, "variable");
		    }
		    Tcl_ListObjAppendElement(NULL, listObj, keyObj);
		    Tcl_ListObjAppendElement(NULL, listObj,
			    vwaitItems[i].sourceObj);
		}
	    }
	    if (timeout > 0) {
		TclNewLiteralStringObj(keyObj, "timeleft");
		Tcl_ListObjAppendElement(NULL, listObj, keyObj);
		Tcl_ListObjAppendElement(NULL, listObj,
			Tcl_NewWideIntObj(diff));
	    }
	    Tcl_SetObjResult(interp, listObj);
	} else if (timeout > 0) {
	    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(diff));
	}
    } else {
	result = Tcl_RestoreInterpState(interp, saved);
    }
    if (vwaitItems != localItems) {
	Tcl_Free(vwaitItems);
    }
    return result;
}

static void
VwaitChannelReadProc(
    void *clientData,		/* Pointer to vwait info record. */
    int mask)			/* Event mask, must be TCL_READABLE. */
{
    VwaitItem *itemPtr = (VwaitItem *) clientData;

    if (!(mask & TCL_READABLE)) {
	return;
    }
    if (itemPtr->donePtr != NULL) {
	itemPtr->sequence = itemPtr->donePtr[0];
	itemPtr->donePtr[0] += 1;
	itemPtr->donePtr = NULL;
    }
}

static void
VwaitChannelWriteProc(
    void *clientData,		/* Pointer to vwait info record. */
    int mask)			/* Event mask, must be TCL_WRITABLE. */
{
    VwaitItem *itemPtr = (VwaitItem *) clientData;

    if (!(mask & TCL_WRITABLE)) {
	return;
    }
    if (itemPtr->donePtr != NULL) {
	itemPtr->sequence = itemPtr->donePtr[0];
	itemPtr->donePtr[0] += 1;
	itemPtr->donePtr = NULL;
    }
}

static void
VwaitTimeoutProc(
    void *clientData)		/* Pointer to vwait info record. */
{
    VwaitItem *itemPtr = (VwaitItem *) clientData;

    if (itemPtr->donePtr != NULL) {
	itemPtr->donePtr[0] = 1;
	itemPtr->donePtr = NULL;
    }
}

static char *
VwaitVarProc(
    void *clientData,		/* Pointer to vwait info record. */
    Tcl_Interp *interp,		/* Interpreter containing variable. */
    const char *name1,		/* Name of variable. */
    const char *name2,		/* Second part of variable name. */
    TCL_UNUSED(int) /*flags*/)	/* Information about what happened. */
{
    VwaitItem *itemPtr = (VwaitItem *) clientData;

    if (itemPtr->donePtr != NULL) {
	itemPtr->sequence = itemPtr->donePtr[0];
	itemPtr->donePtr[0] += 1;
	itemPtr->donePtr = NULL;
    }
    Tcl_UntraceVar2(interp, name1, name2, TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
	    VwaitVarProc, clientData);
    return NULL;
}

/*
 *----------------------------------------------------------------------
Changes to generic/tclExecute.c.
15
16
17
18
19
20
21

22
23
24
25
26
27
28
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tclInt.h"
#include "tclCompile.h"
#include "tclOOInt.h"
#include "tclTomMath.h"

#include <math.h>
#include <assert.h>

/*
 * Hack to determine whether we may expect IEEE floating point. The hack is
 * formally incorrect in that non-IEEE platforms might have the same precision
 * and range, but VAX, IBM, and Cray do not; are there any other floating







>







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tclInt.h"
#include "tclCompile.h"
#include "tclOOInt.h"
#include "tclTomMath.h"
#include "tclArithSeries.h"
#include <math.h>
#include <assert.h>

/*
 * Hack to determine whether we may expect IEEE floating point. The hack is
 * formally incorrect in that non-IEEE platforms might have the same precision
 * and range, but VAX, IBM, and Cray do not; are there any other floating
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
 * Helpers for NR - non-recursive calls to TEBC
 * Minimal data required to fully reconstruct the execution state.
 */

typedef struct {
    ByteCode *codePtr;		/* Constant until the BC returns */
				/* -----------------------------------------*/
    ptrdiff_t *catchTop;	/* These fields are used on return TO this */
    Tcl_Obj *auxObjList;	/* this level: they record the state when a */
    CmdFrame cmdFrame;		/* new codePtr was received for NR */
                                /* execution. */
    void *stack[1];		/* Start of the actual combined catch and obj
				 * stacks; the struct will be expanded as
				 * necessary */
} TEBCdata;

#define TEBC_YIELD() \
    do {						\
	esPtr->tosPtr = tosPtr;				\







|



|







108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
 * Helpers for NR - non-recursive calls to TEBC
 * Minimal data required to fully reconstruct the execution state.
 */

typedef struct {
    ByteCode *codePtr;		/* Constant until the BC returns */
				/* -----------------------------------------*/
    Tcl_Obj **catchTop;		/* These fields are used on return TO this */
    Tcl_Obj *auxObjList;	/* this level: they record the state when a */
    CmdFrame cmdFrame;		/* new codePtr was received for NR */
                                /* execution. */
    Tcl_Obj *stack[1];		/* Start of the actual combined catch and obj
				 * stacks; the struct will be expanded as
				 * necessary */
} TEBCdata;

#define TEBC_YIELD() \
    do {						\
	esPtr->tosPtr = tosPtr;				\
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

#define OBJ_AT_TOS	*tosPtr

#define OBJ_UNDER_TOS	*(tosPtr-1)

#define OBJ_AT_DEPTH(n)	*(tosPtr-(n))

#define CURR_DEPTH	((ptrdiff_t) (tosPtr - initTosPtr))

#define STACK_BASE(esPtr) ((esPtr)->stackWords - 1)

/*
 * Macros used to trace instruction execution. The macros TRACE,
 * TRACE_WITH_OBJ, and O2S are only used inside TclNRExecuteByteCode. O2S is
 * only used in TRACE* calls to get a string from an object.
 */

#ifdef TCL_COMPILE_DEBUG
#   define TRACE(a) \
    while (traceInstructions) {					\
	fprintf(stdout, "%2" TCL_Z_MODIFIER "d: %2d (%" TCL_Z_MODIFIER "u) %s ", iPtr->numLevels,	\
		(int) CURR_DEPTH,				\
		(size_t) (pc - codePtr->codeStart),		\
		GetOpcodeName(pc));				\
	printf a;						\
	break;							\
    }
#   define TRACE_APPEND(a) \
    while (traceInstructions) {		\
	printf a;			\
	break;				\
    }
#   define TRACE_ERROR(interp) \
    TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp))));
#   define TRACE_WITH_OBJ(a, objPtr) \
    while (traceInstructions) {					\
	fprintf(stdout, "%2" TCL_Z_MODIFIER "d: %2d (%" TCL_Z_MODIFIER "u) %s ", iPtr->numLevels,	\
		(int) CURR_DEPTH,				\
		(size_t) (pc - codePtr->codeStart),		\
		GetOpcodeName(pc));				\
	printf a;						\
	TclPrintObject(stdout, objPtr, 30);			\
	fprintf(stdout, "\n");					\
	break;							\
    }
#   define O2S(objPtr) \







|












|
|
|













|
|
|







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

#define OBJ_AT_TOS	*tosPtr

#define OBJ_UNDER_TOS	*(tosPtr-1)

#define OBJ_AT_DEPTH(n)	*(tosPtr-(n))

#define CURR_DEPTH	((size_t)(tosPtr - initTosPtr))

#define STACK_BASE(esPtr) ((esPtr)->stackWords - 1)

/*
 * Macros used to trace instruction execution. The macros TRACE,
 * TRACE_WITH_OBJ, and O2S are only used inside TclNRExecuteByteCode. O2S is
 * only used in TRACE* calls to get a string from an object.
 */

#ifdef TCL_COMPILE_DEBUG
#   define TRACE(a) \
    while (traceInstructions) {					\
	fprintf(stdout, "%2" TCL_Z_MODIFIER "u: %2" TCL_Z_MODIFIER "u (%" TCL_Z_MODIFIER "u) %s ", iPtr->numLevels,	\
		CURR_DEPTH,				\
		(size_t)(pc - codePtr->codeStart),		\
		GetOpcodeName(pc));				\
	printf a;						\
	break;							\
    }
#   define TRACE_APPEND(a) \
    while (traceInstructions) {		\
	printf a;			\
	break;				\
    }
#   define TRACE_ERROR(interp) \
    TRACE_APPEND(("ERROR: %.30s\n", O2S(Tcl_GetObjResult(interp))));
#   define TRACE_WITH_OBJ(a, objPtr) \
    while (traceInstructions) {					\
	fprintf(stdout, "%2" TCL_Z_MODIFIER "u: %2" TCL_Z_MODIFIER "u (%" TCL_Z_MODIFIER "u) %s ", iPtr->numLevels,	\
		CURR_DEPTH,				\
		(size_t)(pc - codePtr->codeStart),		\
		GetOpcodeName(pc));				\
	printf a;						\
	TclPrintObject(stdout, objPtr, 30);			\
	fprintf(stdout, "\n");					\
	break;							\
    }
#   define O2S(objPtr) \
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
			    Tcl_Obj *const objv[]);
#endif /* TCL_COMPILE_STATS */
#ifdef TCL_COMPILE_DEBUG
static const char *	GetOpcodeName(const unsigned char *pc);
static void		PrintByteCodeInfo(ByteCode *codePtr);
static const char *	StringForResultCode(int result);
static void		ValidatePcAndStackTop(ByteCode *codePtr,
			    const unsigned char *pc, int stackTop,
			    int checkStack);
#endif /* TCL_COMPILE_DEBUG */
static ByteCode *	CompileExprObj(Tcl_Interp *interp, Tcl_Obj *objPtr);
static void		DeleteExecStack(ExecStack *esPtr);
static void		DupExprCodeInternalRep(Tcl_Obj *srcPtr,
			    Tcl_Obj *copyPtr);
static Tcl_Obj *	ExecuteExtendedBinaryMathOp(Tcl_Interp *interp,







|







617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
			    Tcl_Obj *const objv[]);
#endif /* TCL_COMPILE_STATS */
#ifdef TCL_COMPILE_DEBUG
static const char *	GetOpcodeName(const unsigned char *pc);
static void		PrintByteCodeInfo(ByteCode *codePtr);
static const char *	StringForResultCode(int result);
static void		ValidatePcAndStackTop(ByteCode *codePtr,
			    const unsigned char *pc, size_t stackTop,
			    int checkStack);
#endif /* TCL_COMPILE_DEBUG */
static ByteCode *	CompileExprObj(Tcl_Interp *interp, Tcl_Obj *objPtr);
static void		DeleteExecStack(ExecStack *esPtr);
static void		DupExprCodeInternalRep(Tcl_Obj *srcPtr,
			    Tcl_Obj *copyPtr);
static Tcl_Obj *	ExecuteExtendedBinaryMathOp(Tcl_Interp *interp,
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
 *
 * Side effects:
 *	Almost certainly, depending on the ByteCode's instructions.
 *
 *----------------------------------------------------------------------
 */
#define	bcFramePtr	(&TD->cmdFrame)
#define	initCatchTop	((ptrdiff_t *) (TD->stack-1))
#define	initTosPtr	((Tcl_Obj **) (initCatchTop+codePtr->maxExceptDepth))
#define esPtr		(iPtr->execEnvPtr->execStackPtr)

int
TclNRExecuteByteCode(
    Tcl_Interp *interp,		/* Token for command interpreter. */
    ByteCode *codePtr)		/* The bytecode sequence to interpret. */
{







|
|







1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
 *
 * Side effects:
 *	Almost certainly, depending on the ByteCode's instructions.
 *
 *----------------------------------------------------------------------
 */
#define	bcFramePtr	(&TD->cmdFrame)
#define	initCatchTop	(TD->stack-1)
#define	initTosPtr	(initCatchTop+codePtr->maxExceptDepth)
#define esPtr		(iPtr->execEnvPtr->execStackPtr)

int
TclNRExecuteByteCode(
    Tcl_Interp *interp,		/* Token for command interpreter. */
    ByteCode *codePtr)		/* The bytecode sequence to interpret. */
{
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
    TclResetRewriteEnsemble(interp, 1);

    /*
     * Push the callback for bytecode execution
     */

    TclNRAddCallback(interp, TEBCresume, TD, /* pc */ NULL,
	    /* cleanup */ INT2PTR(0), INT2PTR(iPtr->evalFlags));

    /*
     * Reset discard result flag - because it is applicable for this call only,
     * and should not affect all the nested invocations may return result.
     */
    iPtr->evalFlags &= ~TCL_EVAL_DISCARD_RESULT;








|







1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
    TclResetRewriteEnsemble(interp, 1);

    /*
     * Push the callback for bytecode execution
     */

    TclNRAddCallback(interp, TEBCresume, TD, /* pc */ NULL,
	    /* cleanup */ NULL, INT2PTR(iPtr->evalFlags));

    /*
     * Reset discard result flag - because it is applicable for this call only,
     * and should not affect all the nested invocations may return result.
     */
    iPtr->evalFlags &= ~TCL_EVAL_DISCARD_RESULT;

2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
#endif

    TEBC_DATA_DIG();

#ifdef TCL_COMPILE_DEBUG
    if (!pc && (tclTraceExec >= 2)) {
	PrintByteCodeInfo(codePtr);
	fprintf(stdout, "  Starting stack top=%d\n", (int) CURR_DEPTH);
	fflush(stdout);
    }
#endif

    if (!pc) {
	/* bytecode is starting from scratch */
	pc = codePtr->codeStart;







|







2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
#endif

    TEBC_DATA_DIG();

#ifdef TCL_COMPILE_DEBUG
    if (!pc && (tclTraceExec >= 2)) {
	PrintByteCodeInfo(codePtr);
	fprintf(stdout, "  Starting stack top=%" TCL_Z_MODIFIER "u\n", CURR_DEPTH);
	fflush(stdout);
    }
#endif

    if (!pc) {
	/* bytecode is starting from scratch */
	pc = codePtr->codeStart;
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
#ifdef TCL_COMPILE_DEBUG
    /*
     * Skip the stack depth check if an expansion is in progress.
     */

    CHECK_STACK();
    if (traceInstructions) {
	fprintf(stdout, "%2" TCL_Z_MODIFIER "d: %2d ", iPtr->numLevels, (int) CURR_DEPTH);
	TclPrintInstruction(codePtr, pc);
	fflush(stdout);
    }
#endif /* TCL_COMPILE_DEBUG */

    TCL_DTRACE_INST_NEXT();








|







2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
#ifdef TCL_COMPILE_DEBUG
    /*
     * Skip the stack depth check if an expansion is in progress.
     */

    CHECK_STACK();
    if (traceInstructions) {
	fprintf(stdout, "%2" TCL_Z_MODIFIER "u: %2" TCL_Z_MODIFIER "u ", iPtr->numLevels, CURR_DEPTH);
	TclPrintInstruction(codePtr, pc);
	fflush(stdout);
    }
#endif /* TCL_COMPILE_DEBUG */

    TCL_DTRACE_INST_NEXT();

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
	 * we do not define a special tclObjType for it. It is not dangerous
	 * as the obj is never passed anywhere, so that all manipulations are
	 * performed here and in INST_INVOKE_EXPANDED (in case of an expansion
	 * error, also in INST_EXPAND_STKTOP).
	 */

	TclNewObj(objPtr);
	objPtr->internalRep.twoPtrValue.ptr2 = INT2PTR(CURR_DEPTH);
	objPtr->length = 0;
	PUSH_TAUX_OBJ(objPtr);
	TRACE(("=> mark depth as %d\n", (int) CURR_DEPTH));
	NEXT_INST_F(1, 0, 0);
    break;

    case INST_EXPAND_DROP:
	/*
	 * Drops an element of the auxObjList, popping stack elements to
	 * restore the stack to the state before the point where the aux
	 * element was created.
	 */

	CLANG_ASSERT(auxObjList);
	objc = CURR_DEPTH - PTR2INT(auxObjList->internalRep.twoPtrValue.ptr2);
	POP_TAUX_OBJ();
#ifdef TCL_COMPILE_DEBUG
	/* Ugly abuse! */
	starting = 1;
#endif
	TRACE(("=> drop %" TCL_Z_MODIFIER "u items\n", objc));
	NEXT_INST_V(1, objc, 0);

    case INST_EXPAND_STKTOP: {
	size_t i;

	ptrdiff_t moved;

	/*
	 * Make sure that the element at stackTop is a list; if not, just
	 * leave with an error. Note that the element from the expand list
	 * will be removed at checkForCatch.
	 */








|


|











|










>
|







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
	 * we do not define a special tclObjType for it. It is not dangerous
	 * as the obj is never passed anywhere, so that all manipulations are
	 * performed here and in INST_INVOKE_EXPANDED (in case of an expansion
	 * error, also in INST_EXPAND_STKTOP).
	 */

	TclNewObj(objPtr);
	objPtr->internalRep.twoPtrValue.ptr2 = UINT2PTR(CURR_DEPTH);
	objPtr->length = 0;
	PUSH_TAUX_OBJ(objPtr);
	TRACE(("=> mark depth as %" TCL_Z_MODIFIER "u\n", CURR_DEPTH));
	NEXT_INST_F(1, 0, 0);
    break;

    case INST_EXPAND_DROP:
	/*
	 * Drops an element of the auxObjList, popping stack elements to
	 * restore the stack to the state before the point where the aux
	 * element was created.
	 */

	CLANG_ASSERT(auxObjList);
	objc = CURR_DEPTH - PTR2UINT(auxObjList->internalRep.twoPtrValue.ptr2);
	POP_TAUX_OBJ();
#ifdef TCL_COMPILE_DEBUG
	/* Ugly abuse! */
	starting = 1;
#endif
	TRACE(("=> drop %" TCL_Z_MODIFIER "u items\n", objc));
	NEXT_INST_V(1, objc, 0);

    case INST_EXPAND_STKTOP: {
	size_t i;
	TEBCdata *newTD;
	ptrdiff_t oldCatchTopOff, oldTosPtrOff;

	/*
	 * Make sure that the element at stackTop is a list; if not, just
	 * leave with an error. Note that the element from the expand list
	 * will be removed at checkForCatch.
	 */

2688
2689
2690
2691
2692
2693
2694



2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714

	auxObjList->length += objc - 1;
	if ((objc > 1) && (auxObjList->length > 0)) {
	    length = auxObjList->length /* Total expansion room we need */
		    + codePtr->maxStackDepth /* Beyond the original max */
		    - CURR_DEPTH;	/* Relative to where we are */
	    DECACHE_STACK_INFO();



	    moved = GrowEvaluationStack(iPtr->execEnvPtr, length, 1)
		    - (Tcl_Obj **) TD;
	    if (moved) {
		/*
		 * Change the global data to point to the new stack: move the
		 * TEBCdataPtr TD, recompute the position of every other
		 * stack-allocated parameter, update the stack pointers.
		 */

		TD = (TEBCdata *) (((Tcl_Obj **)TD) + moved);

		catchTop += moved;
		tosPtr += moved;
	    }
	}

	/*
	 * Expand the list at stacktop onto the stack; free the list. Knowing
	 * that it has a freeIntRepProc we use Tcl_DecrRefCount().
	 */







>
>
>
|
<
|






|

|
|







2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700

2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718

	auxObjList->length += objc - 1;
	if ((objc > 1) && (auxObjList->length > 0)) {
	    length = auxObjList->length /* Total expansion room we need */
		    + codePtr->maxStackDepth /* Beyond the original max */
		    - CURR_DEPTH;	/* Relative to where we are */
	    DECACHE_STACK_INFO();
	    oldCatchTopOff = catchTop - initCatchTop;
	    oldTosPtrOff = tosPtr - initTosPtr;
	    newTD = (TEBCdata *)
		    GrowEvaluationStack(iPtr->execEnvPtr, length, 1);

	    if (newTD != TD) {
		/*
		 * Change the global data to point to the new stack: move the
		 * TEBCdataPtr TD, recompute the position of every other
		 * stack-allocated parameter, update the stack pointers.
		 */

		TD = newTD;

		catchTop = initCatchTop + oldCatchTopOff;
		tosPtr = initTosPtr + oldTosPtrOff;
	    }
	}

	/*
	 * Expand the list at stacktop onto the stack; free the list. Knowing
	 * that it has a freeIntRepProc we use Tcl_DecrRefCount().
	 */
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
	TEBC_YIELD();
	/* add TEBCResume for object at top of stack */
	return TclNRExecuteByteCode(interp,
		    TclCompileObj(interp, OBJ_AT_TOS, NULL, 0));

    case INST_INVOKE_EXPANDED:
	CLANG_ASSERT(auxObjList);
	objc = CURR_DEPTH - PTR2INT(auxObjList->internalRep.twoPtrValue.ptr2);
	POP_TAUX_OBJ();
	if (objc) {
	    pcAdjustment = 1;
	    goto doInvocation;
	}

	/*







|







2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
	TEBC_YIELD();
	/* add TEBCResume for object at top of stack */
	return TclNRExecuteByteCode(interp,
		    TclCompileObj(interp, OBJ_AT_TOS, NULL, 0));

    case INST_INVOKE_EXPANDED:
	CLANG_ASSERT(auxObjList);
	objc = CURR_DEPTH - PTR2UINT(auxObjList->internalRep.twoPtrValue.ptr2);
	POP_TAUX_OBJ();
	if (objc) {
	    pcAdjustment = 1;
	    goto doInvocation;
	}

	/*
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
	if (Tcl_IsShared(objResultPtr)) {
	    Tcl_Obj *newValue = Tcl_DuplicateObj(objResultPtr);

	    TclDecrRefCount(objResultPtr);
	    varPtr->value.objPtr = objResultPtr = newValue;
	    Tcl_IncrRefCount(newValue);
	}
	if (Tcl_ListObjReplace(interp, objResultPtr, len, 0, objc, objv)
		!= TCL_OK) {
	    TRACE_ERROR(interp);
	    goto gotError;
	}
	TRACE_APPEND(("%.30s\n", O2S(objResultPtr)));
	NEXT_INST_V(pcAdjustment, cleanup, 1);








|







3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
	if (Tcl_IsShared(objResultPtr)) {
	    Tcl_Obj *newValue = Tcl_DuplicateObj(objResultPtr);

	    TclDecrRefCount(objResultPtr);
	    varPtr->value.objPtr = objResultPtr = newValue;
	    Tcl_IncrRefCount(newValue);
	}
	if (TclListObjAppendElements(interp, objResultPtr, objc, objv)
		!= TCL_OK) {
	    TRACE_ERROR(interp);
	    goto gotError;
	}
	TRACE_APPEND(("%.30s\n", O2S(objResultPtr)));
	NEXT_INST_V(pcAdjustment, cleanup, 1);

3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
	    } else {
		if (Tcl_IsShared(objResultPtr)) {
		    valueToAssign = Tcl_DuplicateObj(objResultPtr);
		    createdNewObj = 1;
		} else {
		    valueToAssign = objResultPtr;
		}
		if (Tcl_ListObjReplace(interp, valueToAssign, len, 0,
			objc, objv) != TCL_OK) {
		    if (createdNewObj) {
			TclDecrRefCount(valueToAssign);
		    }
		    goto errorInLappendListPtr;
		}
	    }







|







3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
	    } else {
		if (Tcl_IsShared(objResultPtr)) {
		    valueToAssign = Tcl_DuplicateObj(objResultPtr);
		    createdNewObj = 1;
		} else {
		    valueToAssign = objResultPtr;
		}
		if (TclListObjAppendElements(interp, valueToAssign,
			objc, objv) != TCL_OK) {
		    if (createdNewObj) {
			TclDecrRefCount(valueToAssign);
		    }
		    goto errorInLappendListPtr;
		}
	    }
4651
4652
4653
4654
4655
4656
4657

















4658
4659
4660
4661
4662
4663
4664
	NEXT_INST_F(1, 1, 1);

    case INST_LIST_INDEX:	/* lindex with objc == 3 */
	value2Ptr = OBJ_AT_TOS;
	valuePtr = OBJ_UNDER_TOS;
	TRACE(("\"%.30s\" \"%.30s\" => ", O2S(valuePtr), O2S(value2Ptr)));


















	/*
	 * Extract the desired list element.
	 */

	if ((TclListObjGetElementsM(interp, valuePtr, &objc, &objv) == TCL_OK)
		&& !TclHasInternalRep(value2Ptr, &tclListType)) {
	    int code;







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







4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
	NEXT_INST_F(1, 1, 1);

    case INST_LIST_INDEX:	/* lindex with objc == 3 */
	value2Ptr = OBJ_AT_TOS;
	valuePtr = OBJ_UNDER_TOS;
	TRACE(("\"%.30s\" \"%.30s\" => ", O2S(valuePtr), O2S(value2Ptr)));


	/* special case for ArithSeries */
	if (TclHasInternalRep(valuePtr,&tclArithSeriesType)) {
	    length = TclArithSeriesObjLength(valuePtr);
	    if (TclGetIntForIndexM(interp, value2Ptr, length-1, &index)!=TCL_OK) {
		CACHE_STACK_INFO();
		TRACE_ERROR(interp);
		goto gotError;
	    }
	    if (TclArithSeriesObjIndex(valuePtr, index, &objResultPtr) != TCL_OK) {
		CACHE_STACK_INFO();
		TRACE_ERROR(interp);
		goto gotError;
	    }
	    goto lindexDone;
	}

	/*
	 * Extract the desired list element.
	 */

	if ((TclListObjGetElementsM(interp, valuePtr, &objc, &objv) == TCL_OK)
		&& !TclHasInternalRep(value2Ptr, &tclListType)) {
	    int code;
4672
4673
4674
4675
4676
4677
4678


4679
4680
4681
4682
4683
4684
4685
		pcAdjustment = 1;
		goto lindexFastPath;
	    }
	    Tcl_ResetResult(interp);
	}

	objResultPtr = TclLindexList(interp, valuePtr, value2Ptr);


	if (!objResultPtr) {
	    TRACE_ERROR(interp);
	    goto gotError;
	}

	/*
	 * Stash the list element on the stack.







>
>







4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
		pcAdjustment = 1;
		goto lindexFastPath;
	    }
	    Tcl_ResetResult(interp);
	}

	objResultPtr = TclLindexList(interp, valuePtr, value2Ptr);

    lindexDone:
	if (!objResultPtr) {
	    TRACE_ERROR(interp);
	    goto gotError;
	}

	/*
	 * Stash the list element on the stack.
4694
4695
4696
4697
4698
4699
4700






















4701
4702
4703
4704
4705
4706
4707
	/*
	 * Pop the list and get the index.
	 */

	valuePtr = OBJ_AT_TOS;
	opnd = TclGetInt4AtPtr(pc+1);
	TRACE(("\"%.30s\" %d => ", O2S(valuePtr), opnd));























	/*
	 * Get the contents of the list, making sure that it really is a list
	 * in the process.
	 */

	if (TclListObjGetElementsM(interp, valuePtr, &objc, &objv) != TCL_OK) {







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







4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
	/*
	 * Pop the list and get the index.
	 */

	valuePtr = OBJ_AT_TOS;
	opnd = TclGetInt4AtPtr(pc+1);
	TRACE(("\"%.30s\" %d => ", O2S(valuePtr), opnd));

	/* special case for ArithSeries */
	if (TclHasInternalRep(valuePtr,&tclArithSeriesType)) {
	    length = TclArithSeriesObjLength(valuePtr);

	    /* Decode end-offset index values. */

	    index = TclIndexDecode(opnd, length);

	    /* Compute value @ index */
	    if (index < length) {
		if (TclArithSeriesObjIndex(valuePtr, index, &objResultPtr) != TCL_OK) {
		    CACHE_STACK_INFO();
		    TRACE_ERROR(interp);
		    goto gotError;
		}
	    } else {
		TclNewObj(objResultPtr);
	    }
	    pcAdjustment = 5;
	    goto lindexFastPath2;
	}

	/*
	 * Get the contents of the list, making sure that it really is a list
	 * in the process.
	 */

	if (TclListObjGetElementsM(interp, valuePtr, &objc, &objv) != TCL_OK) {
4716
4717
4718
4719
4720
4721
4722


4723
4724
4725
4726
4727
4728
4729

    lindexFastPath:
	if (index < (size_t)objc) {
	    objResultPtr = objv[index];
	} else {
	    TclNewObj(objResultPtr);
	}



	TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr)));
	NEXT_INST_F(pcAdjustment, 1, 1);

    case INST_LIST_INDEX_MULTI:	/* 'lindex' with multiple index args */
	/*
	 * Determine the count of index args.







>
>







4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776

    lindexFastPath:
	if (index < (size_t)objc) {
	    objResultPtr = objv[index];
	} else {
	    TclNewObj(objResultPtr);
	}

    lindexFastPath2:

	TRACE_APPEND(("\"%.30s\"\n", O2S(objResultPtr)));
	NEXT_INST_F(pcAdjustment, 1, 1);

    case INST_LIST_INDEX_MULTI:	/* 'lindex' with multiple index args */
	/*
	 * Determine the count of index args.
4892
4893
4894
4895
4896
4897
4898



4899

4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934



4935
4936
4937
4938
4939
4940
4941
	 */
	if (fromIdx == TCL_INDEX_NONE) {
	    fromIdx = TCL_INDEX_START;
	}

	fromIdx = TclIndexDecode(fromIdx, objc - 1);




	objResultPtr = TclListObjRange(valuePtr, fromIdx, toIdx);


	TRACE_APPEND(("\"%.30s\"", O2S(objResultPtr)));
	NEXT_INST_F(9, 1, 1);

    case INST_LIST_IN:
    case INST_LIST_NOT_IN:	/* Basic list containment operators. */
	value2Ptr = OBJ_AT_TOS;
	valuePtr = OBJ_UNDER_TOS;

	s1 = Tcl_GetStringFromObj(valuePtr, &s1len);
	TRACE(("\"%.30s\" \"%.30s\" => ", O2S(valuePtr), O2S(value2Ptr)));
	if (TclListObjLengthM(interp, value2Ptr, &length) != TCL_OK) {
	    TRACE_ERROR(interp);
	    goto gotError;
	}
	match = 0;
	if (length > 0) {
	    size_t i = 0;
	    Tcl_Obj *o;

	    /*
	     * An empty list doesn't match anything.
	     */

	    do {
		Tcl_ListObjIndex(NULL, value2Ptr, i, &o);
		if (o != NULL) {
		    s2 = Tcl_GetStringFromObj(o, &s2len);
		} else {
		    s2 = "";
		    s2len = 0;
		}
		if (s1len == s2len) {
		    match = (memcmp(s1, s2, s1len) == 0);
		}



		i++;
	    } while (i < length && match == 0);
	}

	if (*pc == INST_LIST_NOT_IN) {
	    match = !match;
	}







>
>
>
|
>



















|















>
>
>







4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
	 */
	if (fromIdx == TCL_INDEX_NONE) {
	    fromIdx = TCL_INDEX_START;
	}

	fromIdx = TclIndexDecode(fromIdx, objc - 1);

	if (TclHasInternalRep(valuePtr,&tclArithSeriesType)) {
	    objResultPtr = TclArithSeriesObjRange(valuePtr, fromIdx, toIdx);
	} else {
	    objResultPtr = TclListObjRange(valuePtr, fromIdx, toIdx);
	}

	TRACE_APPEND(("\"%.30s\"", O2S(objResultPtr)));
	NEXT_INST_F(9, 1, 1);

    case INST_LIST_IN:
    case INST_LIST_NOT_IN:	/* Basic list containment operators. */
	value2Ptr = OBJ_AT_TOS;
	valuePtr = OBJ_UNDER_TOS;

	s1 = Tcl_GetStringFromObj(valuePtr, &s1len);
	TRACE(("\"%.30s\" \"%.30s\" => ", O2S(valuePtr), O2S(value2Ptr)));
	if (TclListObjLengthM(interp, value2Ptr, &length) != TCL_OK) {
	    TRACE_ERROR(interp);
	    goto gotError;
	}
	match = 0;
	if (length > 0) {
	    size_t i = 0;
	    Tcl_Obj *o;
	    int isArithSeries = TclHasInternalRep(value2Ptr,&tclArithSeriesType);
	    /*
	     * An empty list doesn't match anything.
	     */

	    do {
		Tcl_ListObjIndex(NULL, value2Ptr, i, &o);
		if (o != NULL) {
		    s2 = Tcl_GetStringFromObj(o, &s2len);
		} else {
		    s2 = "";
		    s2len = 0;
		}
		if (s1len == s2len) {
		    match = (memcmp(s1, s2, s1len) == 0);
		}
		if (isArithSeries) {
		    TclDecrRefCount(o);
		}
		i++;
	    } while (i < length && match == 0);
	}

	if (*pc == INST_LIST_NOT_IN) {
	    match = !match;
	}
5770
5771
5772
5773
5774
5775
5776
5777
5778
5779
5780
5781
5782
5783
5784
		} else {
		    int shift = (int) w2;

		    /*
		     * Handle shifts within the native long range.
		     */

		    if (((size_t) shift < CHAR_BIT*sizeof(long))
			    && !((w1>0 ? w1 : ~w1) &
				-(1UL<<(CHAR_BIT*sizeof(long) - 1 - shift)))) {
			wResult = (Tcl_WideUInt)w1 << shift;
			goto wideResultOfArithmetic;
		    }
		}








|







5824
5825
5826
5827
5828
5829
5830
5831
5832
5833
5834
5835
5836
5837
5838
		} else {
		    int shift = (int) w2;

		    /*
		     * Handle shifts within the native long range.
		     */

		    if (((size_t)shift < CHAR_BIT*sizeof(long))
			    && !((w1>0 ? w1 : ~w1) &
				-(1UL<<(CHAR_BIT*sizeof(long) - 1 - shift)))) {
			wResult = (Tcl_WideUInt)w1 << shift;
			goto wideResultOfArithmetic;
		    }
		}

6411
6412
6413
6414
6415
6416
6417
6418
6419
6420
6421
6422
6423
6424
6425
6426
6427
6428
6429
6430
6431
6432
6433
6434
6435
6436
6437
6438
    case INST_BEGIN_CATCH4:
	/*
	 * Record start of the catch command with exception range index equal
	 * to the operand. Push the current stack depth onto the special catch
	 * stack.
	 */

	*(++catchTop) = CURR_DEPTH;
	TRACE(("%u => catchTop=%d, stackTop=%d\n",
		TclGetUInt4AtPtr(pc+1), (int) (catchTop - initCatchTop - 1),
		(int) CURR_DEPTH));
	NEXT_INST_F(5, 0, 0);
    break;

    case INST_END_CATCH:
	catchTop--;
	DECACHE_STACK_INFO();
	Tcl_ResetResult(interp);
	CACHE_STACK_INFO();
	result = TCL_OK;
	TRACE(("=> catchTop=%d\n", (int) (catchTop - initCatchTop - 1)));
	NEXT_INST_F(1, 0, 0);
    break;

    case INST_PUSH_RESULT:
	objResultPtr = Tcl_GetObjResult(interp);
	TRACE_WITH_OBJ(("=> "), objResultPtr);








|
|
|
|









|







6465
6466
6467
6468
6469
6470
6471
6472
6473
6474
6475
6476
6477
6478
6479
6480
6481
6482
6483
6484
6485
6486
6487
6488
6489
6490
6491
6492
    case INST_BEGIN_CATCH4:
	/*
	 * Record start of the catch command with exception range index equal
	 * to the operand. Push the current stack depth onto the special catch
	 * stack.
	 */

	*(++catchTop) = (Tcl_Obj *)UINT2PTR(CURR_DEPTH);
	TRACE(("%u => catchTop=%" TCL_Z_MODIFIER "u, stackTop=%" TCL_Z_MODIFIER "u\n",
		TclGetUInt4AtPtr(pc+1), (size_t)(catchTop - initCatchTop - 1),
		CURR_DEPTH));
	NEXT_INST_F(5, 0, 0);
    break;

    case INST_END_CATCH:
	catchTop--;
	DECACHE_STACK_INFO();
	Tcl_ResetResult(interp);
	CACHE_STACK_INFO();
	result = TCL_OK;
	TRACE(("=> catchTop=%" TCL_Z_MODIFIER "u\n", (size_t)(catchTop - initCatchTop - 1)));
	NEXT_INST_F(1, 0, 0);
    break;

    case INST_PUSH_RESULT:
	objResultPtr = Tcl_GetObjResult(interp);
	TRACE_WITH_OBJ(("=> "), objResultPtr);

7326
7327
7328
7329
7330
7331
7332
7333
7334
7335
7336
7337
7338
7339
7340
7341
	/*
	 * Clear all expansions that may have started after the last
	 * INST_BEGIN_CATCH.
	 */

	while (auxObjList) {
	    if ((catchTop != initCatchTop)
		    && (*catchTop > (ptrdiff_t)
			auxObjList->internalRep.twoPtrValue.ptr2)) {
		break;
	    }
	    POP_TAUX_OBJ();
	}

	/*
	 * We must not catch if the script in progress has been canceled with







|
|







7380
7381
7382
7383
7384
7385
7386
7387
7388
7389
7390
7391
7392
7393
7394
7395
	/*
	 * Clear all expansions that may have started after the last
	 * INST_BEGIN_CATCH.
	 */

	while (auxObjList) {
	    if ((catchTop != initCatchTop)
		    && (PTR2UINT(*catchTop) >
			PTR2UINT(auxObjList->internalRep.twoPtrValue.ptr2))) {
		break;
	    }
	    POP_TAUX_OBJ();
	}

	/*
	 * We must not catch if the script in progress has been canceled with
7402
7403
7404
7405
7406
7407
7408
7409
7410
7411
7412
7413
7414
7415
7416
7417
7418
7419
7420
7421
7422
7423
7424
7425
	 * "exception". It was found either by checkForCatch just above or by
	 * an instruction during break, continue, or error processing. Jump to
	 * its catchOffset after unwinding the operand stack to the depth it
	 * had when starting to execute the range's catch command.
	 */

    processCatch:
	while (CURR_DEPTH > *catchTop) {
	    valuePtr = POP_OBJECT();
	    TclDecrRefCount(valuePtr);
	}
#ifdef TCL_COMPILE_DEBUG
	if (traceInstructions) {
	    fprintf(stdout, "  ... found catch at %" TCL_Z_MODIFIER "u, catchTop=%d, "
		    "unwound to %ld, new pc %" TCL_Z_MODIFIER "u\n",
		    rangePtr->codeOffset, (int) (catchTop - initCatchTop - 1),
		    (long)*catchTop, rangePtr->catchOffset);
	}
#endif
	pc = (codePtr->codeStart + rangePtr->catchOffset);
	NEXT_INST_F(0, 0, 0);	/* Restart the execution loop at pc. */

	/*
	 * end of infinite loop dispatching on instructions.







|





|
|
|
|







7456
7457
7458
7459
7460
7461
7462
7463
7464
7465
7466
7467
7468
7469
7470
7471
7472
7473
7474
7475
7476
7477
7478
7479
	 * "exception". It was found either by checkForCatch just above or by
	 * an instruction during break, continue, or error processing. Jump to
	 * its catchOffset after unwinding the operand stack to the depth it
	 * had when starting to execute the range's catch command.
	 */

    processCatch:
	while (CURR_DEPTH > PTR2UINT(*catchTop)) {
	    valuePtr = POP_OBJECT();
	    TclDecrRefCount(valuePtr);
	}
#ifdef TCL_COMPILE_DEBUG
	if (traceInstructions) {
	    fprintf(stdout, "  ... found catch at %" TCL_Z_MODIFIER "u, catchTop=%" TCL_Z_MODIFIER "u, "
		    "unwound to %" TCL_Z_MODIFIER "u, new pc %" TCL_Z_MODIFIER "u\n",
		    rangePtr->codeOffset, (size_t)(catchTop - initCatchTop - 1),
		    PTR2UINT(*catchTop), (size_t)rangePtr->catchOffset);
	}
#endif
	pc = (codePtr->codeStart + rangePtr->catchOffset);
	NEXT_INST_F(0, 0, 0);	/* Restart the execution loop at pc. */

	/*
	 * end of infinite loop dispatching on instructions.
7448
7449
7450
7451
7452
7453
7454
7455
7456
7457
7458
7459
7460
7461
7462
7463
7464
	    objPtr = POP_OBJECT();
	    Tcl_DecrRefCount(objPtr);
	}

	if (tosPtr < initTosPtr) {
	    fprintf(stderr,
		    "\nTclNRExecuteByteCode: abnormal return at pc %" TCL_Z_MODIFIER "u: "
		    "stack top %d < entry stack top %d\n",
		    (size_t)(pc - codePtr->codeStart),
		    (int) CURR_DEPTH, 0);
	    Tcl_Panic("TclNRExecuteByteCode execution failure: end stack top < start stack top");
	}
	CLANG_ASSERT(bcFramePtr);
    }

    iPtr->cmdFramePtr = bcFramePtr->nextPtr;
    TclReleaseByteCode(codePtr);







|

|







7502
7503
7504
7505
7506
7507
7508
7509
7510
7511
7512
7513
7514
7515
7516
7517
7518
	    objPtr = POP_OBJECT();
	    Tcl_DecrRefCount(objPtr);
	}

	if (tosPtr < initTosPtr) {
	    fprintf(stderr,
		    "\nTclNRExecuteByteCode: abnormal return at pc %" TCL_Z_MODIFIER "u: "
		    "stack top %" TCL_Z_MODIFIER "u < entry stack top %d\n",
		    (size_t)(pc - codePtr->codeStart),
		    CURR_DEPTH, 0);
	    Tcl_Panic("TclNRExecuteByteCode execution failure: end stack top < start stack top");
	}
	CLANG_ASSERT(bcFramePtr);
    }

    iPtr->cmdFramePtr = bcFramePtr->nextPtr;
    TclReleaseByteCode(codePtr);
8687
8688
8689
8690
8691
8692
8693
8694

8695
8696
8697
8698
8699
8700
8701
8702
8703
#ifdef TCL_COMPILE_STATS
	    codePtr->numSrcBytes?
		    ((float)codePtr->structureSize)/codePtr->numSrcBytes :
#endif
	    0.0);

#ifdef TCL_COMPILE_STATS
    fprintf(stdout, "  Code %" TCL_Z_MODIFIER "u = header %" TCL_Z_MODIFIER "u+inst %" TCL_Z_MODIFIER "u+litObj %" TCL_Z_MODIFIER "u+exc %" TCL_Z_MODIFIER "u+aux %" TCL_Z_MODIFIER "u+cmdMap %" TCL_Z_MODIFIER "u\n",

	    codePtr->structureSize,
	    sizeof(ByteCode)-sizeof(size_t)-sizeof(Tcl_Time),
	    codePtr->numCodeBytes,
	    codePtr->numLitObjects * sizeof(Tcl_Obj *),
	    codePtr->numExceptRanges*sizeof(ExceptionRange),
	    codePtr->numAuxDataItems * sizeof(AuxData),
	    codePtr->numCmdLocBytes);
#endif /* TCL_COMPILE_STATS */
    if (procPtr != NULL) {







|
>

|







8741
8742
8743
8744
8745
8746
8747
8748
8749
8750
8751
8752
8753
8754
8755
8756
8757
8758
#ifdef TCL_COMPILE_STATS
	    codePtr->numSrcBytes?
		    ((float)codePtr->structureSize)/codePtr->numSrcBytes :
#endif
	    0.0);

#ifdef TCL_COMPILE_STATS
    fprintf(stdout, "  Code %" TCL_Z_MODIFIER "u = header %" TCL_Z_MODIFIER "u+inst %" TCL_Z_MODIFIER
        "u+litObj %" TCL_Z_MODIFIER "u+exc %" TCL_Z_MODIFIER "u+aux %" TCL_Z_MODIFIER "u+cmdMap %" TCL_Z_MODIFIER "u\n",
	    codePtr->structureSize,
	    offsetof(ByteCode, localCachePtr),
	    codePtr->numCodeBytes,
	    codePtr->numLitObjects * sizeof(Tcl_Obj *),
	    codePtr->numExceptRanges*sizeof(ExceptionRange),
	    codePtr->numAuxDataItems * sizeof(AuxData),
	    codePtr->numCmdLocBytes);
#endif /* TCL_COMPILE_STATS */
    if (procPtr != NULL) {
8731
8732
8733
8734
8735
8736
8737
8738
8739
8740
8741
8742
8743
8744
8745
8746
8747

8748
8749
8750
8751
8752
8753
8754
8755
8756
8757
8758
8759
8760
8761
8762
8763
8764
8765
8766
8767
8768
8769
8770
8771
8772
8773
#ifdef TCL_COMPILE_DEBUG
static void
ValidatePcAndStackTop(
    ByteCode *codePtr,	/* The bytecode whose summary is printed to
				 * stdout. */
    const unsigned char *pc,	/* Points to first byte of a bytecode
				 * instruction. The program counter. */
    int stackTop,		/* Current stack top. Must be between
				 * stackLowerBound and stackUpperBound
				 * (inclusive). */
    int checkStack)		/* 0 if the stack depth check should be
				 * skipped. */
{
    int stackUpperBound = codePtr->maxStackDepth;
				/* Greatest legal value for stackTop. */
    size_t relativePc = pc - codePtr->codeStart;
    const unsigned char *codeStart = codePtr->codeStart;

    const unsigned char *codeEnd = codePtr->codeStart + codePtr->numCodeBytes;
    unsigned char opCode = *pc;

    if ((pc < codeStart) || (pc > codeEnd)) {
	fprintf(stderr, "\nBad instruction pc 0x%p in TclNRExecuteByteCode\n",
		pc);
	Tcl_Panic("TclNRExecuteByteCode execution failure: bad pc");
    }
    if (opCode >= LAST_INST_OPCODE) {
	fprintf(stderr, "\nBad opcode %u at pc %" TCL_Z_MODIFIER "u in TclNRExecuteByteCode\n",
		opCode, relativePc);
	Tcl_Panic("TclNRExecuteByteCode execution failure: bad opcode");
    }
    if (checkStack &&
	    ((stackTop < 0) || (stackTop > stackUpperBound))) {
	size_t numChars;
	const char *cmd = GetSrcInfoForPc(pc, codePtr, &numChars, NULL, NULL);

	fprintf(stderr, "\nBad stack top %d at pc %" TCL_Z_MODIFIER "u in TclNRExecuteByteCode (min 0, max %i)",
		stackTop, relativePc, stackUpperBound);
	if (cmd != NULL) {
	    Tcl_Obj *message;

	    TclNewLiteralStringObj(message, "\n executing ");
	    Tcl_IncrRefCount(message);
	    Tcl_AppendLimitedToObj(message, cmd, numChars, 100, NULL);







|





|

|
|
>
|


|










|



|







8786
8787
8788
8789
8790
8791
8792
8793
8794
8795
8796
8797
8798
8799
8800
8801
8802
8803
8804
8805
8806
8807
8808
8809
8810
8811
8812
8813
8814
8815
8816
8817
8818
8819
8820
8821
8822
8823
8824
8825
8826
8827
8828
8829
#ifdef TCL_COMPILE_DEBUG
static void
ValidatePcAndStackTop(
    ByteCode *codePtr,	/* The bytecode whose summary is printed to
				 * stdout. */
    const unsigned char *pc,	/* Points to first byte of a bytecode
				 * instruction. The program counter. */
    size_t stackTop,		/* Current stack top. Must be between
				 * stackLowerBound and stackUpperBound
				 * (inclusive). */
    int checkStack)		/* 0 if the stack depth check should be
				 * skipped. */
{
    size_t stackUpperBound = codePtr->maxStackDepth;
				/* Greatest legal value for stackTop. */
    size_t relativePc = (size_t)(pc - codePtr->codeStart);
    size_t codeStart = (size_t)codePtr->codeStart;
    size_t codeEnd = (size_t)
	    (codePtr->codeStart + codePtr->numCodeBytes);
    unsigned char opCode = *pc;

    if ((PTR2UINT(pc) < codeStart) || (PTR2UINT(pc) > codeEnd)) {
	fprintf(stderr, "\nBad instruction pc 0x%p in TclNRExecuteByteCode\n",
		pc);
	Tcl_Panic("TclNRExecuteByteCode execution failure: bad pc");
    }
    if (opCode >= LAST_INST_OPCODE) {
	fprintf(stderr, "\nBad opcode %u at pc %" TCL_Z_MODIFIER "u in TclNRExecuteByteCode\n",
		opCode, relativePc);
	Tcl_Panic("TclNRExecuteByteCode execution failure: bad opcode");
    }
    if (checkStack &&
	    (stackTop > stackUpperBound)) {
	size_t numChars;
	const char *cmd = GetSrcInfoForPc(pc, codePtr, &numChars, NULL, NULL);

	fprintf(stderr, "\nBad stack top %" TCL_Z_MODIFIER "u at pc %" TCL_Z_MODIFIER "u in TclNRExecuteByteCode (min 0, max %" TCL_Z_MODIFIER "u)",
		stackTop, relativePc, stackUpperBound);
	if (cmd != NULL) {
	    Tcl_Obj *message;

	    TclNewLiteralStringObj(message, "\n executing ");
	    Tcl_IncrRefCount(message);
	    Tcl_AppendLimitedToObj(message, cmd, numChars, 100, NULL);
9323
9324
9325
9326
9327
9328
9329
9330
9331
9332
9333
9334
9335
9336
9337
	    + (statsPtr->numLiteralsCreated * sizeof(Tcl_Obj))
	    + statsPtr->totalLitStringBytes;
    totalCodeBytes = statsPtr->totalByteCodeBytes + totalLiteralBytes;

    numCurrentByteCodes =
	    statsPtr->numCompilations - statsPtr->numByteCodesFreed;
    currentHeaderBytes = numCurrentByteCodes
	    * (sizeof(ByteCode) - sizeof(size_t) - sizeof(Tcl_Time));
    literalMgmtBytes = sizeof(LiteralTable)
	    + (iPtr->literalTable.numBuckets * sizeof(LiteralEntry *))
	    + (iPtr->literalTable.numEntries * sizeof(LiteralEntry));
    currentLiteralBytes = literalMgmtBytes
	    + iPtr->literalTable.numEntries * sizeof(Tcl_Obj)
	    + statsPtr->currentLitStringBytes;
    currentCodeBytes = statsPtr->currentByteCodeBytes + currentLiteralBytes;







|







9379
9380
9381
9382
9383
9384
9385
9386
9387
9388
9389
9390
9391
9392
9393
	    + (statsPtr->numLiteralsCreated * sizeof(Tcl_Obj))
	    + statsPtr->totalLitStringBytes;
    totalCodeBytes = statsPtr->totalByteCodeBytes + totalLiteralBytes;

    numCurrentByteCodes =
	    statsPtr->numCompilations - statsPtr->numByteCodesFreed;
    currentHeaderBytes = numCurrentByteCodes
	    * offsetof(ByteCode, localCachePtr);
    literalMgmtBytes = sizeof(LiteralTable)
	    + (iPtr->literalTable.numBuckets * sizeof(LiteralEntry *))
	    + (iPtr->literalTable.numEntries * sizeof(LiteralEntry));
    currentLiteralBytes = literalMgmtBytes
	    + iPtr->literalTable.numEntries * sizeof(Tcl_Obj)
	    + statsPtr->currentLitStringBytes;
    currentCodeBytes = statsPtr->currentByteCodeBytes + currentLiteralBytes;
Changes to generic/tclFCmd.c.
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
 *	None.
 *
 *---------------------------------------------------------------------------
 */

static Tcl_Obj *
FileBasename(
    Tcl_Interp *interp,		/* Interp, for error return. */
    Tcl_Obj *pathPtr)		/* Path whose basename to extract. */
{
    size_t objc;
    Tcl_Obj *splitPtr;
    Tcl_Obj *resultPtr = NULL;

    splitPtr = Tcl_FSSplitPath(pathPtr, &objc);
    Tcl_IncrRefCount(splitPtr);

    if (objc != 0) {
	if ((objc == 1) && (*TclGetString(pathPtr) == '~')) {
	    Tcl_DecrRefCount(splitPtr);
	    if (Tcl_FSConvertToPathType(interp, pathPtr) != TCL_OK) {
		return NULL;
	    }
	    splitPtr = Tcl_FSSplitPath(pathPtr, &objc);
	    Tcl_IncrRefCount(splitPtr);
	}

	/*
	 * Return the last component, unless it is the only component, and it
	 * is the root of an absolute path.
	 */

	if (objc > 0) {
	    Tcl_ListObjIndex(NULL, splitPtr, objc-1, &resultPtr);
	    if ((objc == 1) &&
		    (Tcl_FSGetPathType(resultPtr) != TCL_PATH_RELATIVE)) {







|










<
<
<
<
<
<
<
<
<
|
|







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
 *	None.
 *
 *---------------------------------------------------------------------------
 */

static Tcl_Obj *
FileBasename(
    TCL_UNUSED(Tcl_Interp *),	/* Interp, for error return. */
    Tcl_Obj *pathPtr)		/* Path whose basename to extract. */
{
    size_t objc;
    Tcl_Obj *splitPtr;
    Tcl_Obj *resultPtr = NULL;

    splitPtr = Tcl_FSSplitPath(pathPtr, &objc);
    Tcl_IncrRefCount(splitPtr);

    if (objc != 0) {









        /*
         * Return the last component, unless it is the only component, and it
	 * is the root of an absolute path.
	 */

	if (objc > 0) {
	    Tcl_ListObjIndex(NULL, splitPtr, objc-1, &resultPtr);
	    if ((objc == 1) &&
		    (Tcl_FSGetPathType(resultPtr) != TCL_PATH_RELATIVE)) {
1645
1646
1647
1648
1649
1650
1651
1652
1653












































































1654
1655
1656
1657
1658
1659
		"can't create temporary directory: %s",
		Tcl_PosixError(interp)));
	return TCL_ERROR;
    }
    Tcl_SetObjResult(interp, dirNameObj);
    return TCL_OK;
}

/*












































































 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */









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






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
		"can't create temporary directory: %s",
		Tcl_PosixError(interp)));
	return TCL_ERROR;
    }
    Tcl_SetObjResult(interp, dirNameObj);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TclFileHomeCmd --
 *
 *	This function is invoked to process the "file home" Tcl command.
 *	See the user documentation for details on what it does.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TclFileHomeCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj *const objv[])
{
    Tcl_Obj *homeDirObj;

    if (objc != 1 && objc != 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "?user?");
	return TCL_ERROR;
    }
    homeDirObj = TclGetHomeDirObj(interp, objc == 1 ? NULL : Tcl_GetString(objv[1]));
    if (homeDirObj == NULL) {
	return TCL_ERROR;
    }
    Tcl_SetObjResult(interp, homeDirObj);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TclFileTildeExpandCmd --
 *
 *	This function is invoked to process the "file tildeexpand" Tcl command.
 *	See the user documentation for details on what it does.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TclFileTildeExpandCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj *const objv[])
{
    Tcl_Obj *expandedPathObj;

    if (objc != 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "path");
	return TCL_ERROR;
    }
    expandedPathObj = TclResolveTildePath(interp, objv[1]);
    if (expandedPathObj == NULL) {
	return TCL_ERROR;
    }
    Tcl_SetObjResult(interp, expandedPathObj);
    return TCL_OK;
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */
Changes to generic/tclFileName.c.
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

TclPlatformType tclPlatform = TCL_PLATFORM_UNIX;

/*
 * Prototypes for local procedures defined in this file:
 */

static const char *	DoTildeSubst(Tcl_Interp *interp,
			    const char *user, Tcl_DString *resultPtr);
static const char *	ExtractWinRoot(const char *path,
			    Tcl_DString *resultPtr, int offset,
			    Tcl_PathType *typePtr);
static int		SkipToChar(char **stringPtr, int match);
static Tcl_Obj *	SplitWinPath(const char *path);
static Tcl_Obj *	SplitUnixPath(const char *path);
static int		DoGlob(Tcl_Interp *interp, Tcl_Obj *resultPtr,







<
<







22
23
24
25
26
27
28


29
30
31
32
33
34
35

TclPlatformType tclPlatform = TCL_PLATFORM_UNIX;

/*
 * Prototypes for local procedures defined in this file:
 */



static const char *	ExtractWinRoot(const char *path,
			    Tcl_DString *resultPtr, int offset,
			    Tcl_PathType *typePtr);
static int		SkipToChar(char **stringPtr, int match);
static Tcl_Obj *	SplitWinPath(const char *path);
static Tcl_Obj *	SplitUnixPath(const char *path);
static int		DoGlob(Tcl_Interp *interp, Tcl_Obj *resultPtr,
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
 *	Determines whether a given path is relative to the current directory,
 *	relative to the current volume, or absolute, but ONLY FOR THE NATIVE
 *	FILESYSTEM. This function is called from tclIOUtil.c (but needs to be
 *	here due to its dependence on static variables/functions in this
 *	file). The exported function Tcl_FSGetPathType should be used by
 *	extensions.
 *
 *	Note that '~' paths are always considered TCL_PATH_ABSOLUTE, even
 *	though expanding the '~' could lead to any possible path type. This
 *	function should therefore be considered a low-level, string
 *	manipulation function only -- it doesn't actually do any expansion in
 *	making its determination.
 *
 * Results:
 *	Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
 *	TCL_PATH_VOLUME_RELATIVE.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

Tcl_PathType
TclpGetNativePathType(
    Tcl_Obj *pathPtr,		/* Native path of interest */
    size_t *driveNameLengthPtr,	/* Returns length of drive, if non-NULL and
				 * path was absolute */
    Tcl_Obj **driveNameRef)
{
    Tcl_PathType type = TCL_PATH_ABSOLUTE;
    const char *path = TclGetString(pathPtr);

    if (path[0] == '~') {
	/*
	 * This case is common to all platforms. Paths that begin with ~ are
	 * absolute.
	 */

	if (driveNameLengthPtr != NULL) {
	    const char *end = path + 1;
	    while ((*end != '\0') && (*end != '/')) {
		end++;
	    }
	    *driveNameLengthPtr = end - path;
	}
    } else {
	switch (tclPlatform) {
	case TCL_PLATFORM_UNIX: {
	    const char *origPath = path;

	    /*
	     * Paths that begin with / are absolute.
	     */

	    if (path[0] == '/') {
		++path;
#if defined(__CYGWIN__) || defined(__QNX__)
		/*
		 * Check for "//" network path prefix
		 */
		if ((*path == '/') && path[1] && (path[1] != '/')) {
		    path += 2;
		    while (*path && *path != '/') {
			++path;
		    }
#if defined(__CYGWIN__)
		    /* UNC paths need to be followed by a share name */
		    if (*path++ && (*path && *path != '/')) {
			++path;
			while (*path && *path != '/') {
			    ++path;
			}
		    } else {
			path = origPath + 1;
		    }
#endif
		}
#endif
		if (driveNameLengthPtr != NULL) {
		    /*
		     * We need this addition in case the QNX or Cygwin code was used.
		     */

		    *driveNameLengthPtr = (path - origPath);
		}
	    } else {
		type = TCL_PATH_RELATIVE;
	    }
	    break;
	}
	case TCL_PLATFORM_WINDOWS: {
	    Tcl_DString ds;
	    const char *rootEnd;

	    Tcl_DStringInit(&ds);
	    rootEnd = ExtractWinRoot(path, &ds, 0, &type);
	    if ((rootEnd != path) && (driveNameLengthPtr != NULL)) {
		*driveNameLengthPtr = rootEnd - path;
		if (driveNameRef != NULL) {
		    *driveNameRef = TclDStringToObj(&ds);
		    Tcl_IncrRefCount(*driveNameRef);
		}
	    }
	    Tcl_DStringFree(&ds);
	    break;
	}
	}
    }
    return type;
}

/*
 *---------------------------------------------------------------------------
 *







<
<
<
<
<
<




















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

|
|
|

|
|

|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|

|

|
|
|
|

|
|
|
|
|
|
|
|
|
|

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







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
 *	Determines whether a given path is relative to the current directory,
 *	relative to the current volume, or absolute, but ONLY FOR THE NATIVE
 *	FILESYSTEM. This function is called from tclIOUtil.c (but needs to be
 *	here due to its dependence on static variables/functions in this
 *	file). The exported function Tcl_FSGetPathType should be used by
 *	extensions.
 *






 * Results:
 *	Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
 *	TCL_PATH_VOLUME_RELATIVE.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

Tcl_PathType
TclpGetNativePathType(
    Tcl_Obj *pathPtr,		/* Native path of interest */
    size_t *driveNameLengthPtr,	/* Returns length of drive, if non-NULL and
				 * path was absolute */
    Tcl_Obj **driveNameRef)
{
    Tcl_PathType type = TCL_PATH_ABSOLUTE;
    const char *path = TclGetString(pathPtr);















    switch (tclPlatform) {
    case TCL_PLATFORM_UNIX: {
        const char *origPath = path;

        /*
         * Paths that begin with / are absolute.
         */

        if (path[0] == '/') {
            ++path;
#if defined(__CYGWIN__) || defined(__QNX__)
            /*
             * Check for "//" network path prefix
             */
            if ((*path == '/') && path[1] && (path[1] != '/')) {
                path += 2;
                while (*path && *path != '/') {
                    ++path;
                }
#if defined(__CYGWIN__)
                /* UNC paths need to be followed by a share name */
                if (*path++ && (*path && *path != '/')) {
                    ++path;
                    while (*path && *path != '/') {
                        ++path;
                    }
                } else {
                    path = origPath + 1;
                }
#endif
            }
#endif
            if (driveNameLengthPtr != NULL) {
                /*
                 * We need this addition in case the QNX or Cygwin code was used.
                 */

                *driveNameLengthPtr = (path - origPath);
            }
        } else {
            type = TCL_PATH_RELATIVE;
        }
        break;
    }
    case TCL_PLATFORM_WINDOWS: {
        Tcl_DString ds;
        const char *rootEnd;

        Tcl_DStringInit(&ds);
        rootEnd = ExtractWinRoot(path, &ds, 0, &type);
        if ((rootEnd != path) && (driveNameLengthPtr != NULL)) {
            *driveNameLengthPtr = rootEnd - path;
            if (driveNameRef != NULL) {
                *driveNameRef = TclDStringToObj(&ds);
                Tcl_IncrRefCount(*driveNameRef);
            }
        }
        Tcl_DStringFree(&ds);
        break;
    }

    }
    return type;
}

/*
 *---------------------------------------------------------------------------
 *
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
	Tcl_ListObjAppendElement(NULL, result, rootElt);
	while (*path == '/') {
	    ++path;
	}
    }

    /*
     * Split on slashes. Embedded elements that start with tilde will be
     * prefixed with "./" so they are not affected by tilde substitution.
     */

    for (;;) {
	elementStart = path;
	while ((*path != '\0') && (*path != '/')) {
	    path++;
	}
	length = path - elementStart;
	if (length > 0) {
	    Tcl_Obj *nextElt;
	    if ((elementStart[0] == '~') && (elementStart != origPath)) {
		TclNewLiteralStringObj(nextElt, "./");
		Tcl_AppendToObj(nextElt, elementStart, length);
	    } else {
		nextElt = Tcl_NewStringObj(elementStart, length);
	    }
	    Tcl_ListObjAppendElement(NULL, result, nextElt);
	}
	if (*path++ == '\0') {
	    break;
	}
    }
    return result;
}







|
<










<
<
<
<
|
<
|







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
	Tcl_ListObjAppendElement(NULL, result, rootElt);
	while (*path == '/') {
	    ++path;
	}
    }

    /*
     * Split on slashes.

     */

    for (;;) {
	elementStart = path;
	while ((*path != '\0') && (*path != '/')) {
	    path++;
	}
	length = path - elementStart;
	if (length > 0) {
	    Tcl_Obj *nextElt;




            nextElt = Tcl_NewStringObj(elementStart, length);

            Tcl_ListObjAppendElement(NULL, result, nextElt);
	}
	if (*path++ == '\0') {
	    break;
	}
    }
    return result;
}
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

    if (p != path) {
	Tcl_ListObjAppendElement(NULL, result, TclDStringToObj(&buf));
    }
    Tcl_DStringFree(&buf);

    /*
     * Split on slashes. Embedded elements that start with tilde or a drive
     * letter will be prefixed with "./" so they are not affected by tilde
     * substitution.
     */

    do {
	elementStart = p;
	while ((*p != '\0') && (*p != '/') && (*p != '\\')) {
	    p++;
	}
	length = p - elementStart;
	if (length > 0) {
	    Tcl_Obj *nextElt;
	    if ((elementStart != path) && ((elementStart[0] == '~')
		    || (isalpha(UCHAR(elementStart[0]))
			&& elementStart[1] == ':'))) {
		TclNewLiteralStringObj(nextElt, "./");
		Tcl_AppendToObj(nextElt, elementStart, length);
	    } else {
		nextElt = Tcl_NewStringObj(elementStart, length);
	    }
	    Tcl_ListObjAppendElement(NULL, result, nextElt);
	}
    } while (*p++ != '\0');







|
<
<










|
|
|
|







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

    if (p != path) {
	Tcl_ListObjAppendElement(NULL, result, TclDStringToObj(&buf));
    }
    Tcl_DStringFree(&buf);

    /*
     * Split on slashes.


     */

    do {
	elementStart = p;
	while ((*p != '\0') && (*p != '/') && (*p != '\\')) {
	    p++;
	}
	length = p - elementStart;
	if (length > 0) {
	    Tcl_Obj *nextElt;
            if ((elementStart != path) &&
                isalpha(UCHAR(elementStart[0])) &&
                (elementStart[1] == ':')) {
                TclNewLiteralStringObj(nextElt, "./");
		Tcl_AppendToObj(nextElt, elementStart, length);
	    } else {
		nextElt = Tcl_NewStringObj(elementStart, length);
	    }
	    Tcl_ListObjAppendElement(NULL, result, nextElt);
	}
    } while (*p++ != '\0');
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
    char *dest;
    const char *p;
    const char *start;

    start = Tcl_GetStringFromObj(prefix, &length);

    /*
     * Remove the ./ from tilde prefixed elements, and drive-letter prefixed
     * elements on Windows, unless it is the first component.
     */

    p = joining;

    if (length != 0) {
	if ((p[0] == '.') && (p[1] == '/') && ((p[2] == '~')

		|| (tclPlatform==TCL_PLATFORM_WINDOWS && isalpha(UCHAR(p[2]))

		&& (p[3] == ':')))) {
	    p += 2;
	}
    }
    if (*p == '\0') {
	return;
    }

    switch (tclPlatform) {







|






|
>
|
>
|
|







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
    char *dest;
    const char *p;
    const char *start;

    start = Tcl_GetStringFromObj(prefix, &length);

    /*
     * Remove the ./ from drive-letter prefixed
     * elements on Windows, unless it is the first component.
     */

    p = joining;

    if (length != 0) {
	if ((p[0] == '.') &&
            (p[1] == '/') &&
            (tclPlatform==TCL_PLATFORM_WINDOWS) &&
            isalpha(UCHAR(p[2])) &&
            (p[3] == ':')) {
            p += 2;
	}
    }
    if (*p == '\0') {
	return;
    }

    switch (tclPlatform) {
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

/*
 *---------------------------------------------------------------------------
 *
 * Tcl_TranslateFileName --
 *
 *	Converts a file name into a form usable by the native system
 *	interfaces. If the name starts with a tilde, it will produce a name
 *	where the tilde and following characters have been replaced by the
 *	home directory location for the named user.
 *
 * Results:
 *	The return value is a pointer to a string containing the name after

 *	tilde substitution. If there was no tilde substitution, the return
 *	value is a pointer to a copy of the original string. If there was an
 *	error in processing the name, then an error message is left in the
 *	interp's result (if interp was not NULL) and the return value is NULL.
 *	Space for the return value is allocated in bufferPtr; the caller must
 *	call Tcl_DStringFree() to free the space if the return value was not
 *	NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

char *
Tcl_TranslateFileName(
    Tcl_Interp *interp,		/* Interpreter in which to store error message
				 * (if necessary). */
    const char *name,		/* File name, which may begin with "~" (to
				 * indicate current user's home directory) or
				 * "~<user>" (to indicate any user's home
				 * directory). */
    Tcl_DString *bufferPtr)	/* Uninitialized or free DString filled with
				 * name after tilde substitution. */
{
    Tcl_Obj *path = Tcl_NewStringObj(name, -1);
    Tcl_Obj *transPtr;

    Tcl_IncrRefCount(path);
    transPtr = Tcl_FSGetTranslatedPath(interp, path);
    if (transPtr == NULL) {







|
<
<


|
>
|
|


<
<
<
















|







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

/*
 *---------------------------------------------------------------------------
 *
 * Tcl_TranslateFileName --
 *
 *	Converts a file name into a form usable by the native system
 *	interfaces.


 *
 * Results:
 *	The return value is a pointer to a string containing the name.
 *      This may either be the name pointer passed in or space allocated in
 *      bufferPtr. In all cases, if the return value is not NULL, the caller
 *	must call Tcl_DStringFree() to free the space. If there was an
 *	error in processing the name, then an error message is left in the
 *	interp's result (if interp was not NULL) and the return value is NULL.



 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

char *
Tcl_TranslateFileName(
    Tcl_Interp *interp,		/* Interpreter in which to store error message
				 * (if necessary). */
    const char *name,		/* File name, which may begin with "~" (to
				 * indicate current user's home directory) or
				 * "~<user>" (to indicate any user's home
				 * directory). */
    Tcl_DString *bufferPtr)	/* Uninitialized or free DString filled with
				 * name. */
{
    Tcl_Obj *path = Tcl_NewStringObj(name, -1);
    Tcl_Obj *transPtr;

    Tcl_IncrRefCount(path);
    transPtr = Tcl_FSGetTranslatedPath(interp, path);
    if (transPtr == NULL) {
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
     * so that "foo..o" would be split into "foo" and "..o". This is a
     * confusing and usually incorrect behavior, so now we split at the last
     * period in the name.
     */

    return p;
}

/*
 *----------------------------------------------------------------------
 *
 * DoTildeSubst --
 *
 *	Given a string following a tilde, this routine returns the
 *	corresponding home directory.
 *
 * Results:
 *	The result is a pointer to a static string containing the home
 *	directory in native format. If there was an error in processing the
 *	substitution, then an error message is left in the interp's result and
 *	the return value is NULL. On success, the results are appended to
 *	resultPtr, and the contents of resultPtr are returned.
 *
 * Side effects:
 *	Information may be left in resultPtr.
 *
 *----------------------------------------------------------------------
 */

static const char *
DoTildeSubst(
    Tcl_Interp *interp,		/* Interpreter in which to store error message
				 * (if necessary). */
    const char *user,		/* Name of user whose home directory should be
				 * substituted, or "" for current user. */
    Tcl_DString *resultPtr)	/* Initialized DString filled with name after
				 * tilde substitution. */
{
    const char *dir;

    if (*user == '\0') {
	Tcl_DString dirString;

	dir = TclGetEnv("HOME", &dirString);
	if (dir == NULL) {
	    if (interp) {
		Tcl_SetObjResult(interp, Tcl_NewStringObj(
			"couldn't find HOME environment "
			"variable to expand path", -1));
		Tcl_SetErrorCode(interp, "TCL", "FILENAME", "NO_HOME", NULL);
	    }
	    return NULL;
	}
	Tcl_JoinPath(1, &dir, resultPtr);
	Tcl_DStringFree(&dirString);
    } else if (TclpGetUserHome(user, resultPtr) == NULL) {
	if (interp) {
	    Tcl_ResetResult(interp);
	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		    "user \"%s\" doesn't exist", user));
	    Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "USER", user, NULL);
	}
	return NULL;
    }
    return Tcl_DStringValue(resultPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_GlobObjCmd --
 *
 *	This procedure is invoked to process the "glob" Tcl command. See the







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







1108
1109
1110
1111
1112
1113
1114



























































1115
1116
1117
1118
1119
1120
1121
     * so that "foo..o" would be split into "foo" and "..o". This is a
     * confusing and usually incorrect behavior, so now we split at the last
     * period in the name.
     */

    return p;
}




























































/*
 *----------------------------------------------------------------------
 *
 * Tcl_GlobObjCmd --
 *
 *	This procedure is invoked to process the "glob" Tcl command. See the
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
}

/*
 *----------------------------------------------------------------------
 *
 * TclGlob --
 *
 *	Sets the separator string based on the platform, performs tilde
 *	substitution, and calls DoGlob.
 *
 *	The interpreter's result, on entry to this function, must be a valid
 *	Tcl list (e.g. it could be empty), since we will lappend any new
 *	results to that list. If it is not a valid list, this function will
 *	fail to do anything very meaningful.
 *
 *	Note that if globFlags contains 'TCL_GLOBMODE_TAILS' then pathPrefix







|
<







1596
1597
1598
1599
1600
1601
1602
1603

1604
1605
1606
1607
1608
1609
1610
}

/*
 *----------------------------------------------------------------------
 *
 * TclGlob --
 *
 *	Sets the separator string based on the platform	and calls DoGlob.

 *
 *	The interpreter's result, on entry to this function, must be a valid
 *	Tcl list (e.g. it could be empty), since we will lappend any new
 *	results to that list. If it is not a valid list, this function will
 *	fail to do anything very meaningful.
 *
 *	Note that if globFlags contains 'TCL_GLOBMODE_TAILS' then pathPrefix
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
    Tcl_Obj *pathPrefix,	/* Path prefix to glob pattern, if non-null,
				 * which is considered literally. */
    int globFlags,		/* Stores or'ed combination of flags */
    Tcl_GlobTypeData *types)	/* Struct containing acceptable types. May be
				 * NULL. */
{
    const char *separators;
    const char *head;
    char *tail, *start;
    int result;
    Tcl_Obj *filenamesObj, *savedResultObj;

    separators = NULL;
    switch (tclPlatform) {
    case TCL_PLATFORM_UNIX:
	separators = "/";
	break;
    case TCL_PLATFORM_WINDOWS:
	separators = "/\\:";
	break;
    }

    if (pathPrefix == NULL) {
	char c;
	Tcl_DString buffer;
	Tcl_DStringInit(&buffer);

	start = pattern;

	/*
	 * Perform tilde substitution, if needed.
	 */

	if (start[0] == '~') {
	    /*
	     * Find the first path separator after the tilde.
	     */

	    for (tail = start; *tail != '\0'; tail++) {
		if (*tail == '\\') {
		    if (strchr(separators, tail[1]) != NULL) {
			break;
		    }
		} else if (strchr(separators, *tail) != NULL) {
		    break;
		}
	    }

	    /*
	     * Determine the home directory for the specified user.
	     */

	    c = *tail;
	    *tail = '\0';
	    head = DoTildeSubst(interp, start+1, &buffer);
	    *tail = c;
	    if (head == NULL) {
		return TCL_ERROR;
	    }
	    if (head != Tcl_DStringValue(&buffer)) {
		Tcl_DStringAppend(&buffer, head, -1);
	    }
	    pathPrefix = TclDStringToObj(&buffer);
	    Tcl_IncrRefCount(pathPrefix);
	    globFlags |= TCL_GLOBMODE_DIR;
	    if (c != '\0') {
		tail++;
	    }
	    Tcl_DStringFree(&buffer);
	} else {
	    tail = pattern;
	}
    } else {
	Tcl_IncrRefCount(pathPrefix);
	tail = pattern;
    }

    /*
     * Handling empty path prefixes with glob patterns like 'C:' or
     * 'c:////////' is a pain on Windows if we leave it too late, since these
     * aren't really patterns at all! We therefore check the head of the
     * pattern now for such cases, if we don't have an unquoted prefix yet.
     *







<
|













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







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
    Tcl_Obj *pathPrefix,	/* Path prefix to glob pattern, if non-null,
				 * which is considered literally. */
    int globFlags,		/* Stores or'ed combination of flags */
    Tcl_GlobTypeData *types)	/* Struct containing acceptable types. May be
				 * NULL. */
{
    const char *separators;

    char *tail;
    int result;
    Tcl_Obj *filenamesObj, *savedResultObj;

    separators = NULL;
    switch (tclPlatform) {
    case TCL_PLATFORM_UNIX:
	separators = "/";
	break;
    case TCL_PLATFORM_WINDOWS:
	separators = "/\\:";
	break;
    }

    if (pathPrefix != NULL) {








































	Tcl_IncrRefCount(pathPrefix);



    }


    tail = pattern;






    /*
     * Handling empty path prefixes with glob patterns like 'C:' or
     * 'c:////////' is a pain on Windows if we leave it too late, since these
     * aren't really patterns at all! We therefore check the head of the
     * pattern now for such cases, if we don't have an unquoted prefix yet.
     *
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
	    Tcl_Obj **subdirv;

	    result = TclListObjGetElementsM(interp, subdirsPtr,
		    &subdirc, &subdirv);
	    for (i=0; result==TCL_OK && i<subdirc; i++) {
		Tcl_Obj *copy = NULL;

		if (pathPtr == NULL && TclGetString(subdirv[i])[0] == '~') {
		    TclListObjLengthM(NULL, matchesObj, &repair);
		    copy = subdirv[i];
		    subdirv[i] = Tcl_NewStringObj("./", 2);
		    Tcl_AppendObjToObj(subdirv[i], copy);
		    Tcl_IncrRefCount(subdirv[i]);
		}
		result = DoGlob(interp, matchesObj, separators, subdirv[i],
			1, p+1, types);
		if (copy) {
		    size_t end;

		    Tcl_DecrRefCount(subdirv[i]);
		    subdirv[i] = copy;
		    TclListObjLengthM(NULL, matchesObj, &end);







<
<
<
<
<
<
<
|







2203
2204
2205
2206
2207
2208
2209







2210
2211
2212
2213
2214
2215
2216
2217
	    Tcl_Obj **subdirv;

	    result = TclListObjGetElementsM(interp, subdirsPtr,
		    &subdirc, &subdirv);
	    for (i=0; result==TCL_OK && i<subdirc; i++) {
		Tcl_Obj *copy = NULL;








                result = DoGlob(interp, matchesObj, separators, subdirv[i],
			1, p+1, types);
		if (copy) {
		    size_t end;

		    Tcl_DecrRefCount(subdirv[i]);
		    subdirv[i] = copy;
		    TclListObjLengthM(NULL, matchesObj, &end);
Changes to generic/tclHash.c.
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
 */

static Tcl_HashEntry *
AllocArrayEntry(
    Tcl_HashTable *tablePtr,	/* Hash table. */
    void *keyPtr)			/* Key to store in the hash table entry. */
{
    int *array = (int *) keyPtr;
    int *iPtr1, *iPtr2;
    Tcl_HashEntry *hPtr;
    int count = tablePtr->keyType;
    TCL_HASH_TYPE size = sizeof(Tcl_HashEntry) + (count*sizeof(int)) - sizeof(hPtr->key);

    if (size < sizeof(Tcl_HashEntry)) {
	size = sizeof(Tcl_HashEntry);
    }
    hPtr = (Tcl_HashEntry *)Tcl_Alloc(size);

    for (iPtr1 = array, iPtr2 = hPtr->key.words;
	    count > 0; count--, iPtr1++, iPtr2++) {
	*iPtr2 = *iPtr1;
    }
    Tcl_SetHashValue(hPtr, NULL);

    return hPtr;
}

/*
 *----------------------------------------------------------------------







<
<

|
|






|
<
<
<







655
656
657
658
659
660
661


662
663
664
665
666
667
668
669
670
671



672
673
674
675
676
677
678
 */

static Tcl_HashEntry *
AllocArrayEntry(
    Tcl_HashTable *tablePtr,	/* Hash table. */
    void *keyPtr)			/* Key to store in the hash table entry. */
{


    Tcl_HashEntry *hPtr;
    TCL_HASH_TYPE count = tablePtr->keyType * sizeof(int);
    TCL_HASH_TYPE size = offsetof(Tcl_HashEntry, key) + count;

    if (size < sizeof(Tcl_HashEntry)) {
	size = sizeof(Tcl_HashEntry);
    }
    hPtr = (Tcl_HashEntry *)Tcl_Alloc(size);

    memcpy(hPtr->key.string, keyPtr, count);



    Tcl_SetHashValue(hPtr, NULL);

    return hPtr;
}

/*
 *----------------------------------------------------------------------
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
 */

static int
CompareArrayKeys(
    void *keyPtr,			/* New key to compare. */
    Tcl_HashEntry *hPtr)	/* Existing key to compare. */
{
    const int *iPtr1 = (const int *)keyPtr;
    const int *iPtr2 = hPtr->key.words;
    Tcl_HashTable *tablePtr = hPtr->tablePtr;
    int count;

    for (count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {
	if (count == 0) {
	    return 1;
	}
	if (*iPtr1 != *iPtr2) {
	    break;
	}
    }
    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * HashArrayKey --
 *







<
<
|
<

<
<
|
<
<
<
<
<
<







692
693
694
695
696
697
698


699

700


701






702
703
704
705
706
707
708
 */

static int
CompareArrayKeys(
    void *keyPtr,			/* New key to compare. */
    Tcl_HashEntry *hPtr)	/* Existing key to compare. */
{


    size_t count = hPtr->tablePtr->keyType * sizeof(int);




    return !memcmp(keyPtr, hPtr->key.string, count);






}

/*
 *----------------------------------------------------------------------
 *
 * HashArrayKey --
 *
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
    size_t size, allocsize;

    allocsize = size = strlen(string) + 1;
    if (size < sizeof(hPtr->key)) {
	allocsize = sizeof(hPtr->key);
    }
    hPtr = (Tcl_HashEntry *)Tcl_Alloc(offsetof(Tcl_HashEntry, key) + allocsize);
    memset(hPtr, 0, sizeof(Tcl_HashEntry) + allocsize - sizeof(hPtr->key));
    memcpy(hPtr->key.string, string, size);
    Tcl_SetHashValue(hPtr, NULL);
    return hPtr;
}

/*
 *----------------------------------------------------------------------







|







761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
    size_t size, allocsize;

    allocsize = size = strlen(string) + 1;
    if (size < sizeof(hPtr->key)) {
	allocsize = sizeof(hPtr->key);
    }
    hPtr = (Tcl_HashEntry *)Tcl_Alloc(offsetof(Tcl_HashEntry, key) + allocsize);
    memset(hPtr, 0, offsetof(Tcl_HashEntry, key) + allocsize);
    memcpy(hPtr->key.string, string, size);
    Tcl_SetHashValue(hPtr, NULL);
    return hPtr;
}

/*
 *----------------------------------------------------------------------
Changes to generic/tclIO.c.
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
    Tcl_Encoding encoding)
{
    ChannelState *statePtr = chanPtr->state;
				/* State info for channel */
    char *nextNewLine = NULL;
    int endEncoding, saved = 0, total = 0, flushed = 0, needNlFlush = 0;
    char safe[BUFFER_PADDING];


    if (srcLen) {
        WillWrite(chanPtr);
    }

    /*
     * Write the terminated escape sequence even if srcLen is 0.
     */

    endEncoding = ((statePtr->outputEncodingFlags & TCL_ENCODING_END) != 0);

    if (GotFlag(statePtr, CHANNEL_LINEBUFFERED)
	    || (statePtr->outputTranslation != TCL_TRANSLATE_LF)) {
	nextNewLine = (char *)memchr(src, '\n', srcLen);
    }

    while (srcLen + saved + endEncoding > 0) {
	ChannelBuffer *bufPtr;
	char *dst;
	int result, srcRead, dstLen, dstWrote, srcLimit = srcLen;

	if (nextNewLine) {
	    srcLimit = nextNewLine - src;
	}







>
















|







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
    Tcl_Encoding encoding)
{
    ChannelState *statePtr = chanPtr->state;
				/* State info for channel */
    char *nextNewLine = NULL;
    int endEncoding, saved = 0, total = 0, flushed = 0, needNlFlush = 0;
    char safe[BUFFER_PADDING];
    int encodingError = 0;

    if (srcLen) {
        WillWrite(chanPtr);
    }

    /*
     * Write the terminated escape sequence even if srcLen is 0.
     */

    endEncoding = ((statePtr->outputEncodingFlags & TCL_ENCODING_END) != 0);

    if (GotFlag(statePtr, CHANNEL_LINEBUFFERED)
	    || (statePtr->outputTranslation != TCL_TRANSLATE_LF)) {
	nextNewLine = (char *)memchr(src, '\n', srcLen);
    }

    while (srcLen + saved + endEncoding > 0 && !encodingError) {
	ChannelBuffer *bufPtr;
	char *dst;
	int result, srcRead, dstLen, dstWrote, srcLimit = srcLen;

	if (nextNewLine) {
	    srcLimit = nextNewLine - src;
	}
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
		dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL);

	/*
	 * See chan-io-1.[89]. Tcl Bug 506297.
	 */

	statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;














	if ((result != TCL_OK) && (srcRead + dstWrote == 0)) {
	    /*
	     * We're reading from invalid/incomplete UTF-8.
	     */

	    if (total == 0) {
		Tcl_SetErrno(EILSEQ);
		return -1;
	    }
	    break;
	}

	bufPtr->nextAdded += dstWrote;
	src += srcRead;
	srcLen -= srcRead;
	total += dstWrote;
	dst += dstWrote;







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






<
|
<
<
|







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
		dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL);

	/*
	 * See chan-io-1.[89]. Tcl Bug 506297.
	 */

	statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;

	/*
	 * See io-75.2, TCL bug 6978c01b65.
	 * Check, if an encoding error occured and should be reported to the
	 * script level.
	 * This happens, if a written character may not be represented by the
	 * current output encoding and strict encoding is active.
	 */

	if (result == TCL_CONVERT_UNKNOWN) {
	    encodingError = 1;
	    result = TCL_OK;
	}

	if ((result != TCL_OK) && (srcRead + dstWrote == 0)) {
	    /*
	     * We're reading from invalid/incomplete UTF-8.
	     */


	    encodingError = 1;


	    result = TCL_OK;
	}

	bufPtr->nextAdded += dstWrote;
	src += srcRead;
	srcLen -= srcRead;
	total += dstWrote;
	dst += dstWrote;
4459
4460
4461
4462
4463
4464
4465




4466
4467
4468
4469
4470
4471
4472
	if (FlushChannel(NULL, chanPtr, 0) != 0) {
	    return -1;
	}
    }

    UpdateInterest(chanPtr);





    return total;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tcl_Gets --







>
>
>
>







4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
	if (FlushChannel(NULL, chanPtr, 0) != 0) {
	    return -1;
	}
    }

    UpdateInterest(chanPtr);

    if (encodingError) {
	Tcl_SetErrno(EILSEQ);
	return -1;
    }
    return total;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tcl_Gets --
Changes to generic/tclIOUtil.c.
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
 *	(2) An additional mount point is established inside an existing
 *	filesystem (except for the native file system; see note below).
 *
 *	(3) A filesystem changes the list of available volumes (except for the
 *	native file system; see note below).
 *
 *	(4) The mapping from a string representation of a file to a full,
 *	normalized pathname changes. For example, if 'env(HOME)' is modified,
 *	then any pathname containing '~' maps to a different item, possibly in
 *	a different filesystem.
 *
 *	Tcl has no control over (2) and (3), so each registered filesystem must
 *	call Tcl_FSMountsChnaged in each of those circumstances.
 *
 *	The reason for the exception in 2,3 for the native filesystem is that
 *	the native filesystem claims every file without determining whether
 *	whether the file exists, or even whether the pathname makes sense.







|
<
<







1209
1210
1211
1212
1213
1214
1215
1216


1217
1218
1219
1220
1221
1222
1223
 *	(2) An additional mount point is established inside an existing
 *	filesystem (except for the native file system; see note below).
 *
 *	(3) A filesystem changes the list of available volumes (except for the
 *	native file system; see note below).
 *
 *	(4) The mapping from a string representation of a file to a full,
 *	normalized pathname changes.


 *
 *	Tcl has no control over (2) and (3), so each registered filesystem must
 *	call Tcl_FSMountsChnaged in each of those circumstances.
 *
 *	The reason for the exception in 2,3 for the native filesystem is that
 *	the native filesystem claims every file without determining whether
 *	whether the file exists, or even whether the pathname makes sense.
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955

	while ((*p != '\0') && (*p != separator)) {
	    p++;
	}
	length = p - elementStart;
	if (length > 0) {
	    Tcl_Obj *nextElt;

	    if (elementStart[0] == '~') {
		TclNewLiteralStringObj(nextElt, "./");
		Tcl_AppendToObj(nextElt, elementStart, length);
	    } else {
		nextElt = Tcl_NewStringObj(elementStart, length);
	    }
	    Tcl_ListObjAppendElement(NULL, result, nextElt);
	}
	if (*p++ == '\0') {
	    break;
	}
    }

    if (lenPtr != NULL) {







<
<
<
<
<
|
<
|







3932
3933
3934
3935
3936
3937
3938





3939

3940
3941
3942
3943
3944
3945
3946
3947

	while ((*p != '\0') && (*p != separator)) {
	    p++;
	}
	length = p - elementStart;
	if (length > 0) {
	    Tcl_Obj *nextElt;





            nextElt = Tcl_NewStringObj(elementStart, length);

            Tcl_ListObjAppendElement(NULL, result, nextElt);
	}
	if (*p++ == '\0') {
	    break;
	}
    }

    if (lenPtr != NULL) {
Changes to generic/tclIndexObj.c.
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
	}

	/*
	 * Append a space character (" ") if there is more text to follow
	 * (either another element from objv, or the message string).
	 */

	if (i<objc-1 || message!=NULL) {
	    Tcl_AppendStringsToObj(objPtr, " ", NULL);
	}
    }

    /*
     * Add any trailing message bits and set the resulting string as the
     * interpreter result. Caller is responsible for reporting this as an







|







940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
	}

	/*
	 * Append a space character (" ") if there is more text to follow
	 * (either another element from objv, or the message string).
	 */

	if (i + 1 < objc || message!=NULL) {
	    Tcl_AppendStringsToObj(objPtr, " ", NULL);
	}
    }

    /*
     * Add any trailing message bits and set the resulting string as the
     * interpreter result. Caller is responsible for reporting this as an
Changes to generic/tclInt.decls.
33
34
35
36
37
38
39





40
41
42
43
44
45
46
}
declare 6 {
    void TclCleanupCommand(Command *cmdPtr)
}
declare 7 {
    size_t TclCopyAndCollapse(size_t count, const char *src, char *dst)
}





# TclCreatePipeline unofficially exported for use by BLT.
declare 9 {
    size_t TclCreatePipeline(Tcl_Interp *interp, size_t argc, const char **argv,
	    Tcl_Pid **pidArrayPtr, TclFile *inPipePtr, TclFile *outPipePtr,
	    TclFile *errFilePtr)
}
declare 10 {







>
>
>
>
>







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
}
declare 6 {
    void TclCleanupCommand(Command *cmdPtr)
}
declare 7 {
    size_t TclCopyAndCollapse(size_t count, const char *src, char *dst)
}
# Removed in 9.0:
#declare 8 {
#    int TclCopyChannelOld(Tcl_Interp *interp, Tcl_Channel inChan,
#	    Tcl_Channel outChan, int toRead, Tcl_Obj *cmdPtr)
#}
# TclCreatePipeline unofficially exported for use by BLT.
declare 9 {
    size_t TclCreatePipeline(Tcl_Interp *interp, size_t argc, const char **argv,
	    Tcl_Pid **pidArrayPtr, TclFile *inPipePtr, TclFile *outPipePtr,
	    TclFile *errFilePtr)
}
declare 10 {
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
declare 31 {
    const char *TclGetExtension(const char *name)
}
declare 32 {
    int TclGetFrame(Tcl_Interp *interp, const char *str,
	    CallFrame **framePtrPtr)
}








declare 38 {
    int TclGetNamespaceForQualName(Tcl_Interp *interp, const char *qualName,
	    Namespace *cxtNsPtr, int flags, Namespace **nsPtrPtr,
	    Namespace **altNsPtrPtr, Namespace **actualCxtPtrPtr,
	    const char **simpleNamePtr)
}
declare 39 {
    Tcl_ObjCmdProc *TclGetObjInterpProc(void)
}
declare 40 {
    int TclGetOpenMode(Tcl_Interp *interp, const char *str, int *seekFlagPtr)
}
declare 41 {
    Tcl_Command TclGetOriginalCommand(Tcl_Command command)
}
declare 42 {
    const char *TclpGetUserHome(const char *name, Tcl_DString *bufferPtr)
}







declare 45 {
    int TclHideUnsafeCommands(Tcl_Interp *interp)
}
declare 46 {
    int TclInExit(void)
}





declare 51 {
    int TclInterpInit(Tcl_Interp *interp)
}
declare 53 {
    int TclInvokeObjectCommand(void *clientData, Tcl_Interp *interp,
	    int argc, const char **argv)
}







>
>
>
>
>
>
>
>


















>
>
>
>
>
>
>






>
>
>
>
>







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
declare 31 {
    const char *TclGetExtension(const char *name)
}
declare 32 {
    int TclGetFrame(Tcl_Interp *interp, const char *str,
	    CallFrame **framePtrPtr)
}
# Removed in 9.0:
#declare 34 {
#    int TclGetIntForIndex(Tcl_Interp *interp, Tcl_Obj *objPtr,
#	    int endValue, int *indexPtr)
#}
#declare 37 {
#    int TclGetLoadedPackages(Tcl_Interp *interp, const char *targetName)
#}
declare 38 {
    int TclGetNamespaceForQualName(Tcl_Interp *interp, const char *qualName,
	    Namespace *cxtNsPtr, int flags, Namespace **nsPtrPtr,
	    Namespace **altNsPtrPtr, Namespace **actualCxtPtrPtr,
	    const char **simpleNamePtr)
}
declare 39 {
    Tcl_ObjCmdProc *TclGetObjInterpProc(void)
}
declare 40 {
    int TclGetOpenMode(Tcl_Interp *interp, const char *str, int *seekFlagPtr)
}
declare 41 {
    Tcl_Command TclGetOriginalCommand(Tcl_Command command)
}
declare 42 {
    const char *TclpGetUserHome(const char *name, Tcl_DString *bufferPtr)
}
declare 43 {
    Tcl_ObjCmdProc2 *TclGetObjInterpProc2(void)
}
# Removed in 9.0:
#declare 44 {
#    int TclGuessPackageName(const char *fileName, Tcl_DString *bufPtr)
#}
declare 45 {
    int TclHideUnsafeCommands(Tcl_Interp *interp)
}
declare 46 {
    int TclInExit(void)
}
# Removed in 9.0:
#declare 50 {
#    void TclInitCompiledLocals(Tcl_Interp *interp, CallFrame *framePtr,
#	    Namespace *nsPtr)
#}
declare 51 {
    int TclInterpInit(Tcl_Interp *interp)
}
declare 53 {
    int TclInvokeObjectCommand(void *clientData, Tcl_Interp *interp,
	    int argc, const char **argv)
}
153
154
155
156
157
158
159




160
161
162





163
164
165
166
167
168
169
}
declare 75 {
    unsigned long long TclpGetClicks(void)
}
declare 76 {
    unsigned long long TclpGetSeconds(void)
}




declare 81 {
    void *TclpRealloc(void *ptr, size_t size)
}





declare 89 {
    int TclPreventAliasLoop(Tcl_Interp *interp, Tcl_Interp *cmdInterp,
	    Tcl_Command cmd)
}
declare 91 {
    void TclProcCleanupProc(Proc *procPtr)
}







>
>
>
>



>
>
>
>
>







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
}
declare 75 {
    unsigned long long TclpGetClicks(void)
}
declare 76 {
    unsigned long long TclpGetSeconds(void)
}
# Removed in 9.0:
#declare 77 {
#    void TclpGetTime(Tcl_Time *time)
#}
declare 81 {
    void *TclpRealloc(void *ptr, size_t size)
}
# Removed in 9.0:
#declare 88 {
#    char *TclPrecTraceProc(void *clientData, Tcl_Interp *interp,
#	    const char *name1, const char *name2, int flags)
#}
declare 89 {
    int TclPreventAliasLoop(Tcl_Interp *interp, Tcl_Interp *cmdInterp,
	    Tcl_Command cmd)
}
declare 91 {
    void TclProcCleanupProc(Proc *procPtr)
}
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
declare 102 {
    void TclSetupEnv(Tcl_Interp *interp)
}
declare 103 {
    int TclSockGetPort(Tcl_Interp *interp, const char *str, const char *proto,
	    int *portPtr)
}




declare 108 {
    void TclTeardownNamespace(Namespace *nsPtr)
}
declare 109 {
    int TclUpdateReturnInfo(Interp *iPtr)
}
declare 110 {
    int TclSockMinimumBuffers(void *sock, size_t size)
}
# Removed in 8.1:
#  declare 110 {
#      char *TclWordEnd(char *start, char *lastChar, int nested, int *semiPtr)
#  }

# Procedures used in conjunction with Tcl namespaces. They are
# defined here instead of in tcl.decls since they are not stable yet.

declare 111 {
    void Tcl_AddInterpResolvers(Tcl_Interp *interp, const char *name,
	    Tcl_ResolveCmdProc *cmdProc, Tcl_ResolveVarProc *varProc,
	    Tcl_ResolveCompiledVarProc *compiledVarProc)
}
























declare 118 {
    int Tcl_GetInterpResolvers(Tcl_Interp *interp, const char *name,
	    Tcl_ResolverInfo *resInfo)
}
declare 119 {
    int Tcl_GetNamespaceResolvers(Tcl_Namespace *namespacePtr,
	    Tcl_ResolverInfo *resInfo)
}
declare 120 {
    Tcl_Var Tcl_FindNamespaceVar(Tcl_Interp *interp, const char *name,
	    Tcl_Namespace *contextNsPtr, int flags)
}


















declare 126 {
    void Tcl_GetVariableFullName(Tcl_Interp *interp, Tcl_Var variable,
	    Tcl_Obj *objPtr)
}





declare 128 {
    void Tcl_PopCallFrame(Tcl_Interp *interp)
}
declare 129 {
    int Tcl_PushCallFrame(Tcl_Interp *interp, Tcl_CallFrame *framePtr,
	    Tcl_Namespace *nsPtr, int isProcCallFrame)
}
declare 130 {
    int Tcl_RemoveInterpResolvers(Tcl_Interp *interp, const char *name)
}
declare 131 {
    void Tcl_SetNamespaceResolvers(Tcl_Namespace *namespacePtr,
	    Tcl_ResolveCmdProc *cmdProc, Tcl_ResolveVarProc *varProc,
	    Tcl_ResolveCompiledVarProc *compiledVarProc)
}
declare 132 {
    int TclpHasSockets(Tcl_Interp *interp)
}




declare 138 {
    const char *TclGetEnv(const char *name, Tcl_DString *valuePtr)
}
# This is used by TclX, but should otherwise be considered private
declare 141 {
    const char *TclpGetCwd(Tcl_Interp *interp, Tcl_DString *cwdPtr)
}







>
>
>
>









<
<
<
<









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












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




>
>
>
>
>


















>
>
>
>







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
declare 102 {
    void TclSetupEnv(Tcl_Interp *interp)
}
declare 103 {
    int TclSockGetPort(Tcl_Interp *interp, const char *str, const char *proto,
	    int *portPtr)
}
# Removed in 9.0:
#declare 104 {
#    int TclSockMinimumBuffersOld(int sock, int size)
#}
declare 108 {
    void TclTeardownNamespace(Namespace *nsPtr)
}
declare 109 {
    int TclUpdateReturnInfo(Interp *iPtr)
}
declare 110 {
    int TclSockMinimumBuffers(void *sock, size_t size)
}





# Procedures used in conjunction with Tcl namespaces. They are
# defined here instead of in tcl.decls since they are not stable yet.

declare 111 {
    void Tcl_AddInterpResolvers(Tcl_Interp *interp, const char *name,
	    Tcl_ResolveCmdProc *cmdProc, Tcl_ResolveVarProc *varProc,
	    Tcl_ResolveCompiledVarProc *compiledVarProc)
}
# Removed in 9.0:
#declare 112 {
#    int TclAppendExportList(Tcl_Interp *interp, Tcl_Namespace *nsPtr,
#	    Tcl_Obj *objPtr)
#}
#declare 113 {
#    Tcl_Namespace *TclCreateNamespace(Tcl_Interp *interp, const char *name,
#	    void *clientData, Tcl_NamespaceDeleteProc *deleteProc)
#}
#declare 114 {
#    void TclDeleteNamespace(Tcl_Namespace *nsPtr)
#}
#declare 115 {
#    int TclExport(Tcl_Interp *interp, Tcl_Namespace *nsPtr,
#	    const char *pattern, int resetListFirst)
#}
#declare 116 {
#    Tcl_Command TclFindCommand(Tcl_Interp *interp, const char *name,
#	    Tcl_Namespace *contextNsPtr, int flags)
#}
#declare 117 {
#    Tcl_Namespace *TclFindNamespace(Tcl_Interp *interp, const char *name,
#	    Tcl_Namespace *contextNsPtr, int flags)
#}
declare 118 {
    int Tcl_GetInterpResolvers(Tcl_Interp *interp, const char *name,
	    Tcl_ResolverInfo *resInfo)
}
declare 119 {
    int Tcl_GetNamespaceResolvers(Tcl_Namespace *namespacePtr,
	    Tcl_ResolverInfo *resInfo)
}
declare 120 {
    Tcl_Var Tcl_FindNamespaceVar(Tcl_Interp *interp, const char *name,
	    Tcl_Namespace *contextNsPtr, int flags)
}
# Removed in 9.0:
#declare 121 {
#    int TclForgetImport(Tcl_Interp *interp, Tcl_Namespace *nsPtr,
#	    const char *pattern)
#}
#declare 122 {
#    Tcl_Command TclGetCommandFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr)
#}
#declare 123 {
#    void TclGetCommandFullName(Tcl_Interp *interp, Tcl_Command command,
#	    Tcl_Obj *objPtr)
#}
#declare 124 {
#    Tcl_Namespace *TclGetCurrentNamespace_(Tcl_Interp *interp)
#}
#declare 125 {
#    Tcl_Namespace *TclGetGlobalNamespace_(Tcl_Interp *interp)
#}
declare 126 {
    void Tcl_GetVariableFullName(Tcl_Interp *interp, Tcl_Var variable,
	    Tcl_Obj *objPtr)
}
# Removed in 9.0:
#declare 127 {
#    int TclImport(Tcl_Interp *interp, Tcl_Namespace *nsPtr,
#	    const char *pattern, int allowOverwrite)
#}
declare 128 {
    void Tcl_PopCallFrame(Tcl_Interp *interp)
}
declare 129 {
    int Tcl_PushCallFrame(Tcl_Interp *interp, Tcl_CallFrame *framePtr,
	    Tcl_Namespace *nsPtr, int isProcCallFrame)
}
declare 130 {
    int Tcl_RemoveInterpResolvers(Tcl_Interp *interp, const char *name)
}
declare 131 {
    void Tcl_SetNamespaceResolvers(Tcl_Namespace *namespacePtr,
	    Tcl_ResolveCmdProc *cmdProc, Tcl_ResolveVarProc *varProc,
	    Tcl_ResolveCompiledVarProc *compiledVarProc)
}
declare 132 {
    int TclpHasSockets(Tcl_Interp *interp)
}
# Removed in 9.0:
#declare 133 {
#    struct tm *TclpGetDate(const time_t *time, int useGMT)
#}
declare 138 {
    const char *TclGetEnv(const char *name, Tcl_DString *valuePtr)
}
# This is used by TclX, but should otherwise be considered private
declare 141 {
    const char *TclpGetCwd(Tcl_Interp *interp, Tcl_DString *cwdPtr)
}
302
303
304
305
306
307
308








309
310
311
312
313
314
315
declare 156 {
    void TclRegError(Tcl_Interp *interp, const char *msg,
	    int status)
}
declare 157 {
    Var *TclVarTraceExists(Tcl_Interp *interp, const char *varName)
}








declare 161 {
    int TclChannelTransform(Tcl_Interp *interp, Tcl_Channel chan,
	    Tcl_Obj *cmdObjPtr)
}
declare 162 {
    void TclChannelEventScriptInvoker(void *clientData, int flags)
}







>
>
>
>
>
>
>
>







387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
declare 156 {
    void TclRegError(Tcl_Interp *interp, const char *msg,
	    int status)
}
declare 157 {
    Var *TclVarTraceExists(Tcl_Interp *interp, const char *varName)
}
# Removed in 9.0:
#declare 158 {
#    void TclSetStartupScriptFileName(const char *filename)
#}
#declare 159 {
#    const char *TclGetStartupScriptFileName(void)
#}

declare 161 {
    int TclChannelTransform(Tcl_Interp *interp, Tcl_Channel chan,
	    Tcl_Obj *cmdObjPtr)
}
declare 162 {
    void TclChannelEventScriptInvoker(void *clientData, int flags)
}
338
339
340
341
342
343
344







345
346
347
348
349
350
351

# New function due to TIP #33
declare 166 {
    int TclListObjSetElement(Tcl_Interp *interp, Tcl_Obj *listPtr,
	    size_t index, Tcl_Obj *valuePtr)
}








# variant of Tcl_UtfNCmp that takes n as bytes, not chars
declare 169 {
    int TclpUtfNcmp2(const char *s1, const char *s2, size_t n)
}
declare 170 {
    int TclCheckInterpTraces(Tcl_Interp *interp, const char *command,
	    size_t numChars, Command *cmdPtr, int result, int traceFlags,







>
>
>
>
>
>
>







431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451

# New function due to TIP #33
declare 166 {
    int TclListObjSetElement(Tcl_Interp *interp, Tcl_Obj *listPtr,
	    size_t index, Tcl_Obj *valuePtr)
}

# Removed in 9.0:
#declare 167 {
#    void TclSetStartupScriptPath(Tcl_Obj *pathPtr)
#}
#declare 168 {
#    Tcl_Obj *TclGetStartupScriptPath(void)
#}
# variant of Tcl_UtfNCmp that takes n as bytes, not chars
declare 169 {
    int TclpUtfNcmp2(const char *s1, const char *s2, size_t n)
}
declare 170 {
    int TclCheckInterpTraces(Tcl_Interp *interp, const char *command,
	    size_t numChars, Command *cmdPtr, int result, int traceFlags,
370
371
372
373
374
375
376
















377
378
379
380
381
382
383
declare 176 {
    void TclCleanupVar(Var *varPtr, Var *arrayPtr)
}
declare 177 {
    void TclVarErrMsg(Tcl_Interp *interp, const char *part1, const char *part2,
	    const char *operation, const char *reason)
}
















declare 198 {
    int TclObjGetFrame(Tcl_Interp *interp, Tcl_Obj *objPtr,
	    CallFrame **framePtrPtr)
}
# 200-208 exported for use by the test suite [Bug 1054748]
declare 200 {
    int TclpObjRemoveDirectory(Tcl_Obj *pathPtr, int recursive,







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







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
declare 176 {
    void TclCleanupVar(Var *varPtr, Var *arrayPtr)
}
declare 177 {
    void TclVarErrMsg(Tcl_Interp *interp, const char *part1, const char *part2,
	    const char *operation, const char *reason)
}
# Removed in 9.0:
#declare 178 {
#    void TclSetStartupScript(Tcl_Obj *pathPtr, const char *encodingName)
#}
#declare 179 {
#    Tcl_Obj *TclGetStartupScript(const char **encodingNamePtr)
#}
#declare 182 {
#     struct tm *TclpLocaltime(const time_t *clock)
#}
#declare 183 {
#     struct tm *TclpGmtime(const time_t *clock)
#}

# For the new "Thread Storage" subsystem.

declare 198 {
    int TclObjGetFrame(Tcl_Interp *interp, Tcl_Obj *objPtr,
	    CallFrame **framePtrPtr)
}
# 200-208 exported for use by the test suite [Bug 1054748]
declare 200 {
    int TclpObjRemoveDirectory(Tcl_Obj *pathPtr, int recursive,
474
475
476
477
478
479
480




481
482
483
484
485
486
487
declare 234 {
    Var *TclVarHashCreateVar(TclVarHashTable *tablePtr, const char *key,
             int *newPtr)
}
declare 235 {
    void TclInitVarHashTable(TclVarHashTable *tablePtr, Namespace *nsPtr)
}





# TIP #285: Script cancellation support.
declare 237 {
    int TclResetCancellation(Tcl_Interp *interp, int force)
}

# NRE functions for "rogue" extensions to exploit NRE; they will need to







>
>
>
>







590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
declare 234 {
    Var *TclVarHashCreateVar(TclVarHashTable *tablePtr, const char *key,
             int *newPtr)
}
declare 235 {
    void TclInitVarHashTable(TclVarHashTable *tablePtr, Namespace *nsPtr)
}
# Removed in 9.0:
#declare 236 {
#    void TclBackgroundException(Tcl_Interp *interp, int code)
#}

# TIP #285: Script cancellation support.
declare 237 {
    int TclResetCancellation(Tcl_Interp *interp, int force)
}

# NRE functions for "rogue" extensions to exploit NRE; they will need to
579
580
581
582
583
584
585









586
587
588
589
590
591
592

# TIP 431: temporary directory creation function
declare 258 {
    Tcl_Obj *TclpCreateTemporaryDirectory(Tcl_Obj *dirObj,
	    Tcl_Obj *basenameObj)
}











##############################################################################

# Define the platform specific internal Tcl interface. These functions are
# only available on the designated platform.

interface tclIntPlat







>
>
>
>
>
>
>
>
>







699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721

# TIP 431: temporary directory creation function
declare 258 {
    Tcl_Obj *TclpCreateTemporaryDirectory(Tcl_Obj *dirObj,
	    Tcl_Obj *basenameObj)
}

# TIP 625: for unit testing - create list objects with span
declare 260 {
    Tcl_Obj *TclListTestObj(int length, int leadingSpace, int endSpace)
}

# TIP 625: for unit testing - check list invariants
declare 261 {
    void TclListObjValidate(Tcl_Interp *interp, Tcl_Obj *listObj)
}

##############################################################################

# Define the platform specific internal Tcl interface. These functions are
# only available on the designated platform.

interface tclIntPlat
Changes to generic/tclInt.h.
270
271
272
273
274
275
276

277



278
279
280
281
282
283
284
				 * strings; values have type (Namespace *). */
#else
    Tcl_HashTable *childTablePtr;
				/* Contains any child namespaces. Indexed by
				 * strings; values have type (Namespace *). If
				 * NULL, there are no children. */
#endif

    size_t nsId;		/* Unique id for the namespace. */



    Tcl_Interp *interp;	/* The interpreter containing this
				 * namespace. */
    int flags;			/* OR-ed combination of the namespace status
				 * flags NS_DYING and NS_DEAD listed below. */
    size_t activationCount;	/* Number of "activations" or active call
				 * frames for this namespace that are on the
				 * Tcl call stack. The namespace won't be







>

>
>
>







270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
				 * strings; values have type (Namespace *). */
#else
    Tcl_HashTable *childTablePtr;
				/* Contains any child namespaces. Indexed by
				 * strings; values have type (Namespace *). If
				 * NULL, there are no children. */
#endif
#if TCL_MAJOR_VERSION > 8
    size_t nsId;		/* Unique id for the namespace. */
#else
    unsigned long nsId;
#endif
    Tcl_Interp *interp;	/* The interpreter containing this
				 * namespace. */
    int flags;			/* OR-ed combination of the namespace status
				 * flags NS_DYING and NS_DEAD listed below. */
    size_t activationCount;	/* Number of "activations" or active call
				 * frames for this namespace that are on the
				 * Tcl call stack. The namespace won't be
1856
1857
1858
1859
1860
1861
1862

1863










1864
1865
1866
1867
1868
1869
1870
    Tcl_HashTable *hiddenCmdTablePtr;
				/* Hash table used by tclBasic.c to keep track
				 * of hidden commands on a per-interp
				 * basis. */
    void *interpInfo;	/* Information used by tclInterp.c to keep
				 * track of parent/child interps on a
				 * per-interp basis. */

    void (*optimizer)(void *envPtr);










    /*
     * Information related to procedures and variables. See tclProc.c and
     * tclVar.c for usage.
     */

    size_t numLevels;		/* Keeps track of how many nested calls to
				 * Tcl_Eval are in progress for this







>

>
>
>
>
>
>
>
>
>
>







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
    Tcl_HashTable *hiddenCmdTablePtr;
				/* Hash table used by tclBasic.c to keep track
				 * of hidden commands on a per-interp
				 * basis. */
    void *interpInfo;	/* Information used by tclInterp.c to keep
				 * track of parent/child interps on a
				 * per-interp basis. */
#if TCL_MAJOR_VERSION > 8
    void (*optimizer)(void *envPtr);
#else
    union {
	void (*optimizer)(void *envPtr);
	Tcl_HashTable unused2;	/* No longer used (was mathFuncTable). The
				 * unused space in interp was repurposed for
				 * pluggable bytecode optimizers. The core
				 * contains one optimizer, which can be
				 * selectively overridden by extensions. */
    } extra;
#endif
    /*
     * Information related to procedures and variables. See tclProc.c and
     * tclVar.c for usage.
     */

    size_t numLevels;		/* Keeps track of how many nested calls to
				 * Tcl_Eval are in progress for this
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
#if defined(__APPLE__)
#define TCL_ALLOCALIGN	16
#else
#define TCL_ALLOCALIGN	(2*sizeof(void *))
#endif

/*
 * This macro is used to determine the offset needed to safely allocate any
 * data structure in memory. Given a starting offset or size, it "rounds up"
 * or "aligns" the offset to the next 8-byte boundary so that any data
 * structure can be placed at the resulting offset without fear of an
 * alignment error.

 *
 * WARNING!! DO NOT USE THIS MACRO TO ALIGN POINTERS: it will produce the
 * wrong result on platforms that allocate addresses that are divisible by 4
 * or 2. Only use it for offsets or sizes.
 *
 * This macro is only used by tclCompile.c in the core (Bug 926445). It
 * however not be made file static, as extensions that touch bytecodes
 * (notably tbcload) require it.
 */











#define TCL_ALIGN(x) (((int)(x) + 7) & ~7)


/*
 * A common panic alert when memory allocation fails.
 */

#define TclOOM(ptr, size) \
	((size) && ((ptr)||(Tcl_Panic("unable to alloc %" TCL_Z_MODIFIER "u bytes", (size_t)(size)),1)))







|

|
|
|
>


|
|






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







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
#if defined(__APPLE__)
#define TCL_ALLOCALIGN	16
#else
#define TCL_ALLOCALIGN	(2*sizeof(void *))
#endif

/*
 * TCL_ALIGN is used to determine the offset needed to safely allocate any
 * data structure in memory. Given a starting offset or size, it "rounds up"
 * or "aligns" the offset to the next aligned (typically 8-byte) boundary so
 * that any data structure can be placed at the resulting offset without fear
 * of an alignment error. Note this is clamped to a minimum of 8 for API
 * compatibility.
 *
 * WARNING!! DO NOT USE THIS MACRO TO ALIGN POINTERS: it will produce the
 * wrong result on platforms that allocate addresses that are divisible by a
 * non-trivial factor of this alignment. Only use it for offsets or sizes.
 *
 * This macro is only used by tclCompile.c in the core (Bug 926445). It
 * however not be made file static, as extensions that touch bytecodes
 * (notably tbcload) require it.
 */

struct TclMaxAlignment {
    char unalign[8];
    union {
	long long maxAlignLongLong;
	double maxAlignDouble;
	void *maxAlignPointer;
    } aligned;
};
#define TCL_ALIGN_BYTES \
	offsetof(struct TclMaxAlignment, aligned)
#define TCL_ALIGN(x) \
	(((x) + (TCL_ALIGN_BYTES - 1)) & ~(TCL_ALIGN_BYTES - 1))

/*
 * A common panic alert when memory allocation fails.
 */

#define TclOOM(ptr, size) \
	((size) && ((ptr)||(Tcl_Panic("unable to alloc %" TCL_Z_MODIFIER "u bytes", (size_t)(size)),1)))
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
 */

#define	TCL_INVOKE_HIDDEN	(1<<0)
#define TCL_INVOKE_NO_UNKNOWN	(1<<1)
#define TCL_INVOKE_NO_TRACEBACK	(1<<2)

/*
 * The structure used as the internal representation of Tcl list objects. This
 * struct is grown (reallocated and copied) as necessary to hold all the
 * list's element pointers. The struct might contain more slots than currently
 * used to hold all element pointers. This is done to make append operations
 * faster.
 */


typedef struct List {
    size_t refCount;





    size_t maxElemCount;		/* Total number of element array slots. */

    size_t elemCount;		/* Current number of list elements. */
    int canonicalFlag;		/* Set if the string representation was

				 * derived from the list representation. May

				 * be ignored if there is no string rep at
				 * all.*/




    Tcl_Obj *elements[TCLFLEXARRAY];		/* First list element; the struct is grown to





				 * accommodate all elements. */










} List;
















#define LIST_MAX \

	((int)(((size_t)UINT_MAX - offsetof(List, elements))/sizeof(Tcl_Obj *)))

#define LIST_SIZE(numElems) \

	(TCL_HASH_TYPE)(offsetof(List, elements) + ((numElems) * sizeof(Tcl_Obj *)))













/*








 * Macro used to get the elements of a list object.










 */












#define ListRepPtr(listPtr) \












    ((List *) (listPtr)->internalRep.twoPtrValue.ptr1)


#define ListObjGetElements(listPtr, objc, objv) \

    ((objv) = ListRepPtr(listPtr)->elements, \



     (objc) = ListRepPtr(listPtr)->elemCount)




#define ListObjLength(listPtr, len) \


    ((len) = ListRepPtr(listPtr)->elemCount)






#define ListObjIsCanonical(listPtr) \


    (((listPtr)->bytes == NULL) || ListRepPtr(listPtr)->canonicalFlag)

































#define TclListObjGetElementsM(interp, listPtr, objcPtr, objvPtr) \
    (((listPtr)->typePtr == &tclListType) \
	    ? ((ListObjGetElements((listPtr), *(objcPtr), *(objvPtr))), TCL_OK)\

	    : Tcl_ListObjGetElements((interp), (listPtr), (objcPtr), (objvPtr)))







#define TclListObjLengthM(interp, listPtr, lenPtr) \
    (((listPtr)->typePtr == &tclListType) \
	    ? ((ListObjLength((listPtr), *(lenPtr))), TCL_OK)\
	    : Tcl_ListObjLength((interp), (listPtr), (lenPtr)))

#define TclListObjIsCanonical(listPtr) \
    (((listPtr)->typePtr == &tclListType) ? ListObjIsCanonical((listPtr)) : 0)

/*
 * Modes for collecting (or not) in the implementations of TclNRForeachCmd,
 * TclNRLmapCmd and their compilations.
 */

#define TCL_EACH_KEEP_NONE  0	/* Discard iteration result like [foreach] */







|
|
<
<
<

>

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

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


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


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

>
|
>
|
>
>
>
|
>
>

>
|
>
>
|
>
>
>
>

>
|
>
>
|
>
>
>
>
>
>

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

>
>
>
>
>
|
|
|
|

|
|







2425
2426
2427
2428
2429
2430
2431
2432
2433



2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
 */

#define	TCL_INVOKE_HIDDEN	(1<<0)
#define TCL_INVOKE_NO_UNKNOWN	(1<<1)
#define TCL_INVOKE_NO_TRACEBACK	(1<<2)

/*
 * ListSizeT is the type for holding list element counts. It's defined
 * simplify sharing source between Tcl8 and Tcl9.



 */
#if TCL_MAJOR_VERSION > 8

typedef size_t ListSizeT;

/*
 * SSIZE_MAX, NOT SIZE_MAX as negative differences need to be expressed
 * between values of the ListSizeT type so limit the range to signed
 */
#define ListSizeT_MAX ((ListSizeT)PTRDIFF_MAX)

#else

typedef int ListSizeT;
#define ListSizeT_MAX INT_MAX

#endif

/*
 * ListStore --
 *
 * A Tcl list's internal representation is defined through three structures.
 *
 * A ListStore struct is a structure that includes a variable size array that
 * serves as storage for a Tcl list. A contiguous sequence of slots in the
 * array, the "in-use" area, holds valid pointers to Tcl_Obj values that
 * belong to one or more Tcl lists. The unused slots before and after these
 * are free slots that may be used to prepend and append without having to
 * reallocate the struct. The ListStore may be shared amongst multiple lists
 * and reference counted.
 *
 * A ListSpan struct defines a sequence of slots within a ListStore. This sequence
 * always lies within the "in-use" area of the ListStore. Like ListStore, the
 * structure may be shared among multiple lists and is reference counted.
 *
 * A ListRep struct holds the internal representation of a Tcl list as stored
 * in a Tcl_Obj. It is composed of a ListStore and a ListSpan that together
 * define the content of the list. The ListSpan specifies the range of slots
 * within the ListStore that hold elements for this list. The ListSpan is
 * optional in which case the list includes all the "in-use" slots of the
 * ListStore.
 *
 */
typedef struct ListStore {
    ListSizeT firstUsed;    /* Index of first slot in use within slots[] */
    ListSizeT numUsed;      /* Number of slots in use (starting firstUsed) */
    ListSizeT numAllocated; /* Total number of slots[] array slots. */
    size_t refCount;           /* Number of references to this instance */
    int flags;              /* LISTSTORE_* flags */
    Tcl_Obj *slots[TCLFLEXARRAY];      /* Variable size array. Grown as needed */
} ListStore;

#define LISTSTORE_CANONICAL 0x1 /* All Tcl_Obj's referencing this
                                   store have their string representation
                                   derived from the list representation */

/* Max number of elements that can be contained in a list */
#define LIST_MAX                                               \
    ((ListSizeT_MAX - offsetof(ListStore, slots)) \
		   / sizeof(Tcl_Obj *))
/* Memory size needed for a ListStore to hold numSlots_ elements */
#define LIST_SIZE(numSlots_) \
	(offsetof(ListStore, slots) + ((numSlots_) * sizeof(Tcl_Obj *)))

/*
 * ListSpan --
 * See comments above for ListStore
 */
typedef struct ListSpan {
    ListSizeT spanStart;    /* Starting index of the span */
    ListSizeT spanLength;   /* Number of elements in the span */
    size_t refCount;     /* Count of references to this span record */
} ListSpan;
#ifndef LIST_SPAN_THRESHOLD /* May be set on build line */
#define LIST_SPAN_THRESHOLD 101
#endif

/*
 * ListRep --
 * See comments above for ListStore
 */
typedef struct ListRep {
    ListStore *storePtr;/* element array shared amongst different lists */
    ListSpan *spanPtr;  /* If not NULL, the span holds the range of slots
                           within *storePtr that contain this list elements. */
} ListRep;

/*
 * Macros used to get access list internal representations.
 *
 * Naming conventions:
 * ListRep* - expect a pointer to a valid ListRep
 * ListObj* - expect a pointer to a Tcl_Obj whose internal type is known to
 *            be a list (tclListType). Will crash otherwise.
 * TclListObj* - expect a pointer to a Tcl_Obj whose internal type may or may not
 *            be tclListType. These will convert as needed and return error if
 *            conversion not possible.
 */

/* Returns the starting slot for this listRep in the contained ListStore */
#define ListRepStart(listRepPtr_)                               \
    ((listRepPtr_)->spanPtr ? (listRepPtr_)->spanPtr->spanStart \
			    : (listRepPtr_)->storePtr->firstUsed)

/* Returns the number of elements in this listRep */
#define ListRepLength(listRepPtr_)                               \
    ((listRepPtr_)->spanPtr ? (listRepPtr_)->spanPtr->spanLength \
			    : (listRepPtr_)->storePtr->numUsed)

/* Returns a pointer to the first slot containing this ListRep elements */
#define ListRepElementsBase(listRepPtr_) \
    (&(listRepPtr_)->storePtr->slots[ListRepStart(listRepPtr_)])

/* Stores the number of elements and base address of the element array */
#define ListRepElements(listRepPtr_, objc_, objv_) \
    (((objv_) = ListRepElementsBase(listRepPtr_)), \
     ((objc_) = ListRepLength(listRepPtr_)))

/* Returns 1/0 whether the ListRep's ListStore is shared. */
#define ListRepIsShared(listRepPtr_) ((listRepPtr_)->storePtr->refCount > 1)

/* Returns a pointer to the ListStore component */
#define ListObjStorePtr(listObj_) \
    ((ListStore *)((listObj_)->internalRep.twoPtrValue.ptr1))

/* Returns a pointer to the ListSpan component */
#define ListObjSpanPtr(listObj_) \
    ((ListSpan *)((listObj_)->internalRep.twoPtrValue.ptr2))

/* Returns the ListRep internal representaton in a Tcl_Obj */
#define ListObjGetRep(listObj_, listRepPtr_)                 \
    do {                                                     \
	(listRepPtr_)->storePtr = ListObjStorePtr(listObj_); \
	(listRepPtr_)->spanPtr = ListObjSpanPtr(listObj_);   \
    } while (0)

/* Returns the length of the list */
#define ListObjLength(listObj_, len_)                                         \
    ((len_) = ListObjSpanPtr(listObj_) ? ListObjSpanPtr(listObj_)->spanLength \
				       : ListObjStorePtr(listObj_)->numUsed)

/* Returns the starting slot index of this list's elements in the ListStore */
#define ListObjStart(listObj_)                                      \
    (ListObjSpanPtr(listObj_) ? ListObjSpanPtr(listObj_)->spanStart \
			      : ListObjStorePtr(listObj_)->firstUsed)

/* Stores the element count and base address of this list's elements */
#define ListObjGetElements(listObj_, objc_, objv_) \
    (((objv_) = &ListObjStorePtr(listObj_)->slots[ListObjStart(listObj_)]), \
     (ListObjLength(listObj_, (objc_))))

/*
 * Returns 1/0 whether the internal representation (not the Tcl_Obj itself)
 * is shared.  Note by intent this only checks for sharing of ListStore,
 * not spans.
 */
#define ListObjRepIsShared(listObj_) (ListObjStorePtr(listObj_)->refCount > 1)

/*
 * Certain commands like concat are optimized if an existing string
 * representation of a list object is known to be in canonical format (i.e.
 * generated from the list representation). There are three conditions when
 * this will be the case:
 * (1) No string representation exists which means it will obviously have
 * to be generated from the list representation when needed
 * (2) The ListStore flags is marked canonical. This is done at the time
 * the string representation is generated from the list IF the list
 * representation does not have a span (see comments in UpdateStringOfList).
 * (3) The list representation does not have a span component. This is
 * because list Tcl_Obj's with spans are always created from existing lists
 * and never from strings (see SetListFromAny) and thus their string
 * representation will always be canonical.
 */
#define ListObjIsCanonical(listObj_)                             \
    (((listObj_)->bytes == NULL)                                 \
     || (ListObjStorePtr(listObj_)->flags & LISTSTORE_CANONICAL) \
     || ListObjSpanPtr(listObj_) != NULL)

/*
 * Converts the Tcl_Obj to a list if it isn't one and stores the element
 * count and base address of this list's elements in objcPtr_ and objvPtr_.
 * Return TCL_OK on success or TCL_ERROR if the Tcl_Obj cannot be
 * converted to a list.
 */
#define TclListObjGetElementsM(interp_, listObj_, objcPtr_, objvPtr_)    \
    (((listObj_)->typePtr == &tclListType)                              \
	 ? ((ListObjGetElements((listObj_), *(objcPtr_), *(objvPtr_))), \
	    TCL_OK)                                                     \
	 : Tcl_ListObjGetElements(                                      \
	     (interp_), (listObj_), (objcPtr_), (objvPtr_)))

/*
 * Converts the Tcl_Obj to a list if it isn't one and stores the element
 * count in lenPtr_.  Returns TCL_OK on success or TCL_ERROR if the
 * Tcl_Obj cannot be converted to a list.
 */
#define TclListObjLengthM(interp_, listObj_, lenPtr_)         \
    (((listObj_)->typePtr == &tclListType)                   \
	 ? ((ListObjLength((listObj_), *(lenPtr_))), TCL_OK) \
	 : Tcl_ListObjLength((interp_), (listObj_), (lenPtr_)))

#define TclListObjIsCanonical(listObj_) \
    (((listObj_)->typePtr == &tclListType) ? ListObjIsCanonical((listObj_)) : 0)

/*
 * Modes for collecting (or not) in the implementations of TclNRForeachCmd,
 * TclNRLmapCmd and their compilations.
 */

#define TCL_EACH_KEEP_NONE  0	/* Discard iteration result like [foreach] */
2714
2715
2716
2717
2718
2719
2720

2721
2722
2723
2724
2725
2726
2727

MODULE_SCOPE const Tcl_ObjType tclBignumType;
MODULE_SCOPE const Tcl_ObjType tclBooleanType;
MODULE_SCOPE const Tcl_ObjType tclByteCodeType;
MODULE_SCOPE const Tcl_ObjType tclDoubleType;
MODULE_SCOPE const Tcl_ObjType tclIntType;
MODULE_SCOPE const Tcl_ObjType tclListType;

MODULE_SCOPE const Tcl_ObjType tclDictType;
MODULE_SCOPE const Tcl_ObjType tclProcBodyType;
MODULE_SCOPE const Tcl_ObjType tclStringType;
MODULE_SCOPE const Tcl_ObjType tclEnsembleCmdType;
MODULE_SCOPE const Tcl_ObjType tclRegexpType;
MODULE_SCOPE Tcl_ObjType tclCmdNameType;








>







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

MODULE_SCOPE const Tcl_ObjType tclBignumType;
MODULE_SCOPE const Tcl_ObjType tclBooleanType;
MODULE_SCOPE const Tcl_ObjType tclByteCodeType;
MODULE_SCOPE const Tcl_ObjType tclDoubleType;
MODULE_SCOPE const Tcl_ObjType tclIntType;
MODULE_SCOPE const Tcl_ObjType tclListType;
MODULE_SCOPE const Tcl_ObjType tclArithSeriesType;
MODULE_SCOPE const Tcl_ObjType tclDictType;
MODULE_SCOPE const Tcl_ObjType tclProcBodyType;
MODULE_SCOPE const Tcl_ObjType tclStringType;
MODULE_SCOPE const Tcl_ObjType tclEnsembleCmdType;
MODULE_SCOPE const Tcl_ObjType tclRegexpType;
MODULE_SCOPE Tcl_ObjType tclCmdNameType;

2929
2930
2931
2932
2933
2934
2935


2936
2937
2938
2939
2940
2941
2942
MODULE_SCOPE Tcl_ObjCmdProc TclFileDeleteCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclFileLinkCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclFileMakeDirsCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclFileReadLinkCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclFileRenameCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclFileTempDirCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclFileTemporaryCmd;


MODULE_SCOPE void	TclCreateLateExitHandler(Tcl_ExitProc *proc,
			    void *clientData);
MODULE_SCOPE void	TclDeleteLateExitHandler(Tcl_ExitProc *proc,
			    void *clientData);
MODULE_SCOPE char *	TclDStringAppendObj(Tcl_DString *dsPtr,
			    Tcl_Obj *objPtr);
MODULE_SCOPE char *	TclDStringAppendDString(Tcl_DString *dsPtr,







>
>







3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
MODULE_SCOPE Tcl_ObjCmdProc TclFileDeleteCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclFileLinkCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclFileMakeDirsCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclFileReadLinkCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclFileRenameCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclFileTempDirCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclFileTemporaryCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclFileHomeCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclFileTildeExpandCmd;
MODULE_SCOPE void	TclCreateLateExitHandler(Tcl_ExitProc *proc,
			    void *clientData);
MODULE_SCOPE void	TclDeleteLateExitHandler(Tcl_ExitProc *proc,
			    void *clientData);
MODULE_SCOPE char *	TclDStringAppendObj(Tcl_DString *dsPtr,
			    Tcl_Obj *objPtr);
MODULE_SCOPE char *	TclDStringAppendDString(Tcl_DString *dsPtr,
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
MODULE_SCOPE double	TclFloor(const void *a);
MODULE_SCOPE void	TclFormatNaN(double value, char *buffer);
MODULE_SCOPE int	TclFSFileAttrIndex(Tcl_Obj *pathPtr,
			    const char *attributeName, int *indexPtr);
MODULE_SCOPE Tcl_Command TclNRCreateCommandInNs(Tcl_Interp *interp,
			    const char *cmdName, Tcl_Namespace *nsPtr,
			    Tcl_ObjCmdProc *proc, Tcl_ObjCmdProc *nreProc,
			    void *clientData,
			    Tcl_CmdDeleteProc *deleteProc);
MODULE_SCOPE int	TclNREvalFile(Tcl_Interp *interp, Tcl_Obj *pathPtr,
			    const char *encodingName);
MODULE_SCOPE void	TclFSUnloadTempFile(Tcl_LoadHandle loadHandle);
MODULE_SCOPE int *	TclGetAsyncReadyPtr(void);
MODULE_SCOPE Tcl_Obj *	TclGetBgErrorHandler(Tcl_Interp *interp);
MODULE_SCOPE int	TclGetChannelFromObj(Tcl_Interp *interp,
			    Tcl_Obj *objPtr, Tcl_Channel *chanPtr,







<
|







3154
3155
3156
3157
3158
3159
3160

3161
3162
3163
3164
3165
3166
3167
3168
MODULE_SCOPE double	TclFloor(const void *a);
MODULE_SCOPE void	TclFormatNaN(double value, char *buffer);
MODULE_SCOPE int	TclFSFileAttrIndex(Tcl_Obj *pathPtr,
			    const char *attributeName, int *indexPtr);
MODULE_SCOPE Tcl_Command TclNRCreateCommandInNs(Tcl_Interp *interp,
			    const char *cmdName, Tcl_Namespace *nsPtr,
			    Tcl_ObjCmdProc *proc, Tcl_ObjCmdProc *nreProc,

			    void *clientData, Tcl_CmdDeleteProc *deleteProc);
MODULE_SCOPE int	TclNREvalFile(Tcl_Interp *interp, Tcl_Obj *pathPtr,
			    const char *encodingName);
MODULE_SCOPE void	TclFSUnloadTempFile(Tcl_LoadHandle loadHandle);
MODULE_SCOPE int *	TclGetAsyncReadyPtr(void);
MODULE_SCOPE Tcl_Obj *	TclGetBgErrorHandler(Tcl_Interp *interp);
MODULE_SCOPE int	TclGetChannelFromObj(Tcl_Interp *interp,
			    Tcl_Obj *objPtr, Tcl_Channel *chanPtr,
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
MODULE_SCOPE void	TclInitNotifier(void);
MODULE_SCOPE void	TclInitObjSubsystem(void);
MODULE_SCOPE int	TclInterpReady(Tcl_Interp *interp);
MODULE_SCOPE int	TclIsDigitProc(int byte);
MODULE_SCOPE int	TclIsBareword(int byte);
MODULE_SCOPE Tcl_Obj *	TclJoinPath(size_t elements, Tcl_Obj * const objv[],
			    int forceRelative);






MODULE_SCOPE int	TclJoinThread(Tcl_ThreadId id, int *result);
MODULE_SCOPE void	TclLimitRemoveAllHandlers(Tcl_Interp *interp);
MODULE_SCOPE Tcl_Obj *	TclLindexList(Tcl_Interp *interp,
			    Tcl_Obj *listPtr, Tcl_Obj *argPtr);
MODULE_SCOPE Tcl_Obj *	TclLindexFlat(Tcl_Interp *interp, Tcl_Obj *listPtr,
			    size_t indexCount, Tcl_Obj *const indexArray[]);
/* TIP #280 */
MODULE_SCOPE void	TclListLines(Tcl_Obj *listObj, size_t line, int n,
			    int *lines, Tcl_Obj *const *elems);
MODULE_SCOPE Tcl_Obj *	TclListObjCopy(Tcl_Interp *interp, Tcl_Obj *listPtr);



MODULE_SCOPE Tcl_Obj *	TclListObjRange(Tcl_Obj *listPtr, size_t fromIdx,
			    size_t toIdx);
MODULE_SCOPE Tcl_Obj *	TclLsetList(Tcl_Interp *interp, Tcl_Obj *listPtr,
			    Tcl_Obj *indexPtr, Tcl_Obj *valuePtr);
MODULE_SCOPE Tcl_Obj *	TclLsetFlat(Tcl_Interp *interp, Tcl_Obj *listPtr,
			    size_t indexCount, Tcl_Obj *const indexArray[],
			    Tcl_Obj *valuePtr);
MODULE_SCOPE Tcl_Command TclMakeEnsemble(Tcl_Interp *interp, const char *name,
			    const EnsembleImplMap map[]);

MODULE_SCOPE int	TclMaxListLength(const char *bytes, size_t numBytes,
			    const char **endPtr);
MODULE_SCOPE int	TclMergeReturnOptions(Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[], Tcl_Obj **optionsPtrPtr,
			    int *codePtr, int *levelPtr);
MODULE_SCOPE Tcl_Obj *	TclNarrowToBytes(Tcl_Obj *objPtr);
MODULE_SCOPE Tcl_Obj *  TclNoErrorStack(Tcl_Interp *interp, Tcl_Obj *options);







>
>
>
>
>
>










>
>
>









>







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
MODULE_SCOPE void	TclInitNotifier(void);
MODULE_SCOPE void	TclInitObjSubsystem(void);
MODULE_SCOPE int	TclInterpReady(Tcl_Interp *interp);
MODULE_SCOPE int	TclIsDigitProc(int byte);
MODULE_SCOPE int	TclIsBareword(int byte);
MODULE_SCOPE Tcl_Obj *	TclJoinPath(size_t elements, Tcl_Obj * const objv[],
			    int forceRelative);
MODULE_SCOPE int	MakeTildeRelativePath(Tcl_Interp *interp, const char *user,
			    const char *subPath, Tcl_DString *dsPtr);
MODULE_SCOPE Tcl_Obj *	TclGetHomeDirObj(Tcl_Interp *interp, const char *user);
MODULE_SCOPE Tcl_Obj *	TclResolveTildePath(Tcl_Interp *interp,
			    Tcl_Obj *pathObj);
MODULE_SCOPE Tcl_Obj *	TclResolveTildePathList(Tcl_Obj *pathsObj);
MODULE_SCOPE int	TclJoinThread(Tcl_ThreadId id, int *result);
MODULE_SCOPE void	TclLimitRemoveAllHandlers(Tcl_Interp *interp);
MODULE_SCOPE Tcl_Obj *	TclLindexList(Tcl_Interp *interp,
			    Tcl_Obj *listPtr, Tcl_Obj *argPtr);
MODULE_SCOPE Tcl_Obj *	TclLindexFlat(Tcl_Interp *interp, Tcl_Obj *listPtr,
			    size_t indexCount, Tcl_Obj *const indexArray[]);
/* TIP #280 */
MODULE_SCOPE void	TclListLines(Tcl_Obj *listObj, size_t line, int n,
			    int *lines, Tcl_Obj *const *elems);
MODULE_SCOPE Tcl_Obj *	TclListObjCopy(Tcl_Interp *interp, Tcl_Obj *listPtr);
MODULE_SCOPE int	TclListObjAppendElements(Tcl_Interp *interp,
			    Tcl_Obj *toObj, size_t elemCount,
			    Tcl_Obj *const elemObjv[]);
MODULE_SCOPE Tcl_Obj *	TclListObjRange(Tcl_Obj *listPtr, size_t fromIdx,
			    size_t toIdx);
MODULE_SCOPE Tcl_Obj *	TclLsetList(Tcl_Interp *interp, Tcl_Obj *listPtr,
			    Tcl_Obj *indexPtr, Tcl_Obj *valuePtr);
MODULE_SCOPE Tcl_Obj *	TclLsetFlat(Tcl_Interp *interp, Tcl_Obj *listPtr,
			    size_t indexCount, Tcl_Obj *const indexArray[],
			    Tcl_Obj *valuePtr);
MODULE_SCOPE Tcl_Command TclMakeEnsemble(Tcl_Interp *interp, const char *name,
			    const EnsembleImplMap map[]);
MODULE_SCOPE int TclMakeSafe(Tcl_Interp *interp);
MODULE_SCOPE int	TclMaxListLength(const char *bytes, size_t numBytes,
			    const char **endPtr);
MODULE_SCOPE int	TclMergeReturnOptions(Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[], Tcl_Obj **optionsPtrPtr,
			    int *codePtr, int *levelPtr);
MODULE_SCOPE Tcl_Obj *	TclNarrowToBytes(Tcl_Obj *objPtr);
MODULE_SCOPE Tcl_Obj *  TclNoErrorStack(Tcl_Interp *interp, Tcl_Obj *options);
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
MODULE_SCOPE int	TclpObjLstat(Tcl_Obj *pathPtr, Tcl_StatBuf *buf);
MODULE_SCOPE Tcl_Obj *	TclpTempFileName(void);
MODULE_SCOPE Tcl_Obj *  TclpTempFileNameForLibrary(Tcl_Interp *interp,
			    Tcl_Obj* pathPtr);
MODULE_SCOPE Tcl_Obj *	TclNewFSPathObj(Tcl_Obj *dirPtr, const char *addStrRep,
			    size_t len);
MODULE_SCOPE void	TclpAlertNotifier(void *clientData);
MODULE_SCOPE ClientData	TclpNotifierData(void);
MODULE_SCOPE void	TclpServiceModeHook(int mode);
MODULE_SCOPE void	TclpSetTimer(const Tcl_Time *timePtr);
MODULE_SCOPE int	TclpWaitForEvent(const Tcl_Time *timePtr);
MODULE_SCOPE void	TclpCreateFileHandler(int fd, int mask,
			    Tcl_FileProc *proc, void *clientData);
MODULE_SCOPE int	TclpDeleteFile(const void *path);
MODULE_SCOPE void	TclpDeleteFileHandler(int fd);







|







3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
MODULE_SCOPE int	TclpObjLstat(Tcl_Obj *pathPtr, Tcl_StatBuf *buf);
MODULE_SCOPE Tcl_Obj *	TclpTempFileName(void);
MODULE_SCOPE Tcl_Obj *  TclpTempFileNameForLibrary(Tcl_Interp *interp,
			    Tcl_Obj* pathPtr);
MODULE_SCOPE Tcl_Obj *	TclNewFSPathObj(Tcl_Obj *dirPtr, const char *addStrRep,
			    size_t len);
MODULE_SCOPE void	TclpAlertNotifier(void *clientData);
MODULE_SCOPE void *TclpNotifierData(void);
MODULE_SCOPE void	TclpServiceModeHook(int mode);
MODULE_SCOPE void	TclpSetTimer(const Tcl_Time *timePtr);
MODULE_SCOPE int	TclpWaitForEvent(const Tcl_Time *timePtr);
MODULE_SCOPE void	TclpCreateFileHandler(int fd, int mask,
			    Tcl_FileProc *proc, void *clientData);
MODULE_SCOPE int	TclpDeleteFile(const void *path);
MODULE_SCOPE void	TclpDeleteFileHandler(int fd);
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
MODULE_SCOPE int	TclpThreadCreate(Tcl_ThreadId *idPtr,
			    Tcl_ThreadCreateProc *proc, void *clientData,
			    size_t stackSize, int flags);
MODULE_SCOPE size_t	TclpFindVariable(const char *name, size_t *lengthPtr);
MODULE_SCOPE void	TclpInitLibraryPath(char **valuePtr,
			    TCL_HASH_TYPE *lengthPtr, Tcl_Encoding *encodingPtr);
MODULE_SCOPE void	TclpInitLock(void);
MODULE_SCOPE ClientData	TclpInitNotifier(void);
MODULE_SCOPE void	TclpInitPlatform(void);
MODULE_SCOPE void	TclpInitUnlock(void);
MODULE_SCOPE Tcl_Obj *	TclpObjListVolumes(void);
MODULE_SCOPE void	TclpGlobalLock(void);
MODULE_SCOPE void	TclpGlobalUnlock(void);
MODULE_SCOPE int	TclpMatchFiles(Tcl_Interp *interp, char *separators,
			    Tcl_DString *dirPtr, char *pattern, char *tail);
MODULE_SCOPE int	TclpObjNormalizePath(Tcl_Interp *interp,
			    Tcl_Obj *pathPtr, int nextCheckpoint);
MODULE_SCOPE void	TclpNativeJoinPath(Tcl_Obj *prefix, const char *joining);
MODULE_SCOPE Tcl_Obj *	TclpNativeSplitPath(Tcl_Obj *pathPtr, size_t *lenPtr);
MODULE_SCOPE Tcl_PathType TclpGetNativePathType(Tcl_Obj *pathPtr,
			    size_t *driveNameLengthPtr, Tcl_Obj **driveNameRef);
MODULE_SCOPE int	TclCrossFilesystemCopy(Tcl_Interp *interp,
			    Tcl_Obj *source, Tcl_Obj *target);
MODULE_SCOPE int	TclpMatchInDirectory(Tcl_Interp *interp,
			    Tcl_Obj *resultPtr, Tcl_Obj *pathPtr,
			    const char *pattern, Tcl_GlobTypeData *types);
MODULE_SCOPE void	*TclpGetNativeCwd(void *clientData);
MODULE_SCOPE Tcl_FSDupInternalRepProc TclNativeDupInternalRep;
MODULE_SCOPE Tcl_Obj *	TclpObjLink(Tcl_Obj *pathPtr, Tcl_Obj *toPtr,
			    int linkType);
MODULE_SCOPE int	TclpObjChdir(Tcl_Obj *pathPtr);
MODULE_SCOPE Tcl_Channel TclpOpenTemporaryFile(Tcl_Obj *dirObj,
			    Tcl_Obj *basenameObj, Tcl_Obj *extensionObj,
			    Tcl_Obj *resultingNameObj);







|


















|







3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
MODULE_SCOPE int	TclpThreadCreate(Tcl_ThreadId *idPtr,
			    Tcl_ThreadCreateProc *proc, void *clientData,
			    size_t stackSize, int flags);
MODULE_SCOPE size_t	TclpFindVariable(const char *name, size_t *lengthPtr);
MODULE_SCOPE void	TclpInitLibraryPath(char **valuePtr,
			    TCL_HASH_TYPE *lengthPtr, Tcl_Encoding *encodingPtr);
MODULE_SCOPE void	TclpInitLock(void);
MODULE_SCOPE void *TclpInitNotifier(void);
MODULE_SCOPE void	TclpInitPlatform(void);
MODULE_SCOPE void	TclpInitUnlock(void);
MODULE_SCOPE Tcl_Obj *	TclpObjListVolumes(void);
MODULE_SCOPE void	TclpGlobalLock(void);
MODULE_SCOPE void	TclpGlobalUnlock(void);
MODULE_SCOPE int	TclpMatchFiles(Tcl_Interp *interp, char *separators,
			    Tcl_DString *dirPtr, char *pattern, char *tail);
MODULE_SCOPE int	TclpObjNormalizePath(Tcl_Interp *interp,
			    Tcl_Obj *pathPtr, int nextCheckpoint);
MODULE_SCOPE void	TclpNativeJoinPath(Tcl_Obj *prefix, const char *joining);
MODULE_SCOPE Tcl_Obj *	TclpNativeSplitPath(Tcl_Obj *pathPtr, size_t *lenPtr);
MODULE_SCOPE Tcl_PathType TclpGetNativePathType(Tcl_Obj *pathPtr,
			    size_t *driveNameLengthPtr, Tcl_Obj **driveNameRef);
MODULE_SCOPE int	TclCrossFilesystemCopy(Tcl_Interp *interp,
			    Tcl_Obj *source, Tcl_Obj *target);
MODULE_SCOPE int	TclpMatchInDirectory(Tcl_Interp *interp,
			    Tcl_Obj *resultPtr, Tcl_Obj *pathPtr,
			    const char *pattern, Tcl_GlobTypeData *types);
MODULE_SCOPE void *TclpGetNativeCwd(void *clientData);
MODULE_SCOPE Tcl_FSDupInternalRepProc TclNativeDupInternalRep;
MODULE_SCOPE Tcl_Obj *	TclpObjLink(Tcl_Obj *pathPtr, Tcl_Obj *toPtr,
			    int linkType);
MODULE_SCOPE int	TclpObjChdir(Tcl_Obj *pathPtr);
MODULE_SCOPE Tcl_Channel TclpOpenTemporaryFile(Tcl_Obj *dirObj,
			    Tcl_Obj *basenameObj, Tcl_Obj *extensionObj,
			    Tcl_Obj *resultingNameObj);
3461
3462
3463
3464
3465
3466
3467



3468
3469
3470
3471
3472
3473
3474
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
MODULE_SCOPE int	Tcl_LreverseObjCmd(void *clientData,
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
MODULE_SCOPE int	Tcl_LsearchObjCmd(void *clientData,
			    Tcl_Interp *interp, int objc,



			    Tcl_Obj *const objv[]);
MODULE_SCOPE int	Tcl_LsetObjCmd(void *clientData,
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
MODULE_SCOPE int	Tcl_LsortObjCmd(void *clientData,
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);







>
>
>







3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
MODULE_SCOPE int	Tcl_LreverseObjCmd(void *clientData,
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
MODULE_SCOPE int	Tcl_LsearchObjCmd(void *clientData,
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
MODULE_SCOPE int	Tcl_LseqObjCmd(void *clientData,
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
MODULE_SCOPE int	Tcl_LsetObjCmd(void *clientData,
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
MODULE_SCOPE int	Tcl_LsortObjCmd(void *clientData,
			    Tcl_Interp *interp, int objc,
			    Tcl_Obj *const objv[]);
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
#if TCL_UTF_MAX > 3
#define TclUtfToUniChar(str, chPtr) \
	(((UCHAR(*(str))) < 0x80) ?		\
	    ((*(chPtr) = UCHAR(*(str))), 1)	\
	    : Tcl_UtfToUniChar(str, chPtr))
#else
#define TclUtfToUniChar(str, chPtr) \
	((((unsigned char) *(str)) < 0x80) ?		\
	    ((*(chPtr) = (unsigned char) *(str)), 1)	\
	    : Tcl_UtfToChar16(str, chPtr))
#endif

/*
 *----------------------------------------------------------------
 * Macro counterpart of the Tcl_NumUtfChars() function. To be used in speed-
 * -sensitive points where it pays to avoid a function call in the common case







|
|







4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
#if TCL_UTF_MAX > 3
#define TclUtfToUniChar(str, chPtr) \
	(((UCHAR(*(str))) < 0x80) ?		\
	    ((*(chPtr) = UCHAR(*(str))), 1)	\
	    : Tcl_UtfToUniChar(str, chPtr))
#else
#define TclUtfToUniChar(str, chPtr) \
	(((UCHAR(*(str))) < 0x80) ?		\
	    ((*(chPtr) = UCHAR(*(str))), 1)	\
	    : Tcl_UtfToChar16(str, chPtr))
#endif

/*
 *----------------------------------------------------------------
 * Macro counterpart of the Tcl_NumUtfChars() function. To be used in speed-
 * -sensitive points where it pays to avoid a function call in the common case
Changes to generic/tclIntDecls.h.
126
127
128
129
130
131
132
133

134
135
136
137
138
139
140
EXTERN int		TclGetOpenMode(Tcl_Interp *interp, const char *str,
				int *seekFlagPtr);
/* 41 */
EXTERN Tcl_Command	TclGetOriginalCommand(Tcl_Command command);
/* 42 */
EXTERN const char *	TclpGetUserHome(const char *name,
				Tcl_DString *bufferPtr);
/* Slot 43 is reserved */

/* Slot 44 is reserved */
/* 45 */
EXTERN int		TclHideUnsafeCommands(Tcl_Interp *interp);
/* 46 */
EXTERN int		TclInExit(void);
/* Slot 47 is reserved */
/* Slot 48 is reserved */







|
>







126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
EXTERN int		TclGetOpenMode(Tcl_Interp *interp, const char *str,
				int *seekFlagPtr);
/* 41 */
EXTERN Tcl_Command	TclGetOriginalCommand(Tcl_Command command);
/* 42 */
EXTERN const char *	TclpGetUserHome(const char *name,
				Tcl_DString *bufferPtr);
/* 43 */
EXTERN Tcl_ObjCmdProc2 * TclGetObjInterpProc2(void);
/* Slot 44 is reserved */
/* 45 */
EXTERN int		TclHideUnsafeCommands(Tcl_Interp *interp);
/* 46 */
EXTERN int		TclInExit(void);
/* Slot 47 is reserved */
/* Slot 48 is reserved */
572
573
574
575
576
577
578







579
580
581
582
583
584
585
EXTERN void		TclStaticLibrary(Tcl_Interp *interp,
				const char *prefix,
				Tcl_LibraryInitProc *initProc,
				Tcl_LibraryInitProc *safeInitProc);
/* 258 */
EXTERN Tcl_Obj *	TclpCreateTemporaryDirectory(Tcl_Obj *dirObj,
				Tcl_Obj *basenameObj);








typedef struct TclIntStubs {
    int magic;
    void *hooks;

    void (*reserved0)(void);
    void (*reserved1)(void);







>
>
>
>
>
>
>







573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
EXTERN void		TclStaticLibrary(Tcl_Interp *interp,
				const char *prefix,
				Tcl_LibraryInitProc *initProc,
				Tcl_LibraryInitProc *safeInitProc);
/* 258 */
EXTERN Tcl_Obj *	TclpCreateTemporaryDirectory(Tcl_Obj *dirObj,
				Tcl_Obj *basenameObj);
/* Slot 259 is reserved */
/* 260 */
EXTERN Tcl_Obj *	TclListTestObj(int length, int leadingSpace,
				int endSpace);
/* 261 */
EXTERN void		TclListObjValidate(Tcl_Interp *interp,
				Tcl_Obj *listObj);

typedef struct TclIntStubs {
    int magic;
    void *hooks;

    void (*reserved0)(void);
    void (*reserved1)(void);
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
    void (*reserved36)(void);
    void (*reserved37)(void);
    int (*tclGetNamespaceForQualName) (Tcl_Interp *interp, const char *qualName, Namespace *cxtNsPtr, int flags, Namespace **nsPtrPtr, Namespace **altNsPtrPtr, Namespace **actualCxtPtrPtr, const char **simpleNamePtr); /* 38 */
    Tcl_ObjCmdProc * (*tclGetObjInterpProc) (void); /* 39 */
    int (*tclGetOpenMode) (Tcl_Interp *interp, const char *str, int *seekFlagPtr); /* 40 */
    Tcl_Command (*tclGetOriginalCommand) (Tcl_Command command); /* 41 */
    const char * (*tclpGetUserHome) (const char *name, Tcl_DString *bufferPtr); /* 42 */
    void (*reserved43)(void);
    void (*reserved44)(void);
    int (*tclHideUnsafeCommands) (Tcl_Interp *interp); /* 45 */
    int (*tclInExit) (void); /* 46 */
    void (*reserved47)(void);
    void (*reserved48)(void);
    void (*reserved49)(void);
    void (*reserved50)(void);







|







628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
    void (*reserved36)(void);
    void (*reserved37)(void);
    int (*tclGetNamespaceForQualName) (Tcl_Interp *interp, const char *qualName, Namespace *cxtNsPtr, int flags, Namespace **nsPtrPtr, Namespace **altNsPtrPtr, Namespace **actualCxtPtrPtr, const char **simpleNamePtr); /* 38 */
    Tcl_ObjCmdProc * (*tclGetObjInterpProc) (void); /* 39 */
    int (*tclGetOpenMode) (Tcl_Interp *interp, const char *str, int *seekFlagPtr); /* 40 */
    Tcl_Command (*tclGetOriginalCommand) (Tcl_Command command); /* 41 */
    const char * (*tclpGetUserHome) (const char *name, Tcl_DString *bufferPtr); /* 42 */
    Tcl_ObjCmdProc2 * (*tclGetObjInterpProc2) (void); /* 43 */
    void (*reserved44)(void);
    int (*tclHideUnsafeCommands) (Tcl_Interp *interp); /* 45 */
    int (*tclInExit) (void); /* 46 */
    void (*reserved47)(void);
    void (*reserved48)(void);
    void (*reserved49)(void);
    void (*reserved50)(void);
836
837
838
839
840
841
842



843
844
845
846
847
848
849
    Tcl_Obj * (*tclPtrGetVar) (Tcl_Interp *interp, Tcl_Var varPtr, Tcl_Var arrayPtr, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, int flags); /* 252 */
    Tcl_Obj * (*tclPtrSetVar) (Tcl_Interp *interp, Tcl_Var varPtr, Tcl_Var arrayPtr, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags); /* 253 */
    Tcl_Obj * (*tclPtrIncrObjVar) (Tcl_Interp *interp, Tcl_Var varPtr, Tcl_Var arrayPtr, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *incrPtr, int flags); /* 254 */
    int (*tclPtrObjMakeUpvar) (Tcl_Interp *interp, Tcl_Var otherPtr, Tcl_Obj *myNamePtr, int myFlags); /* 255 */
    int (*tclPtrUnsetVar) (Tcl_Interp *interp, Tcl_Var varPtr, Tcl_Var arrayPtr, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, int flags); /* 256 */
    void (*tclStaticLibrary) (Tcl_Interp *interp, const char *prefix, Tcl_LibraryInitProc *initProc, Tcl_LibraryInitProc *safeInitProc); /* 257 */
    Tcl_Obj * (*tclpCreateTemporaryDirectory) (Tcl_Obj *dirObj, Tcl_Obj *basenameObj); /* 258 */



} TclIntStubs;

extern const TclIntStubs *tclIntStubsPtr;

#ifdef __cplusplus
}
#endif







>
>
>







844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
    Tcl_Obj * (*tclPtrGetVar) (Tcl_Interp *interp, Tcl_Var varPtr, Tcl_Var arrayPtr, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, int flags); /* 252 */
    Tcl_Obj * (*tclPtrSetVar) (Tcl_Interp *interp, Tcl_Var varPtr, Tcl_Var arrayPtr, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags); /* 253 */
    Tcl_Obj * (*tclPtrIncrObjVar) (Tcl_Interp *interp, Tcl_Var varPtr, Tcl_Var arrayPtr, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *incrPtr, int flags); /* 254 */
    int (*tclPtrObjMakeUpvar) (Tcl_Interp *interp, Tcl_Var otherPtr, Tcl_Obj *myNamePtr, int myFlags); /* 255 */
    int (*tclPtrUnsetVar) (Tcl_Interp *interp, Tcl_Var varPtr, Tcl_Var arrayPtr, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, int flags); /* 256 */
    void (*tclStaticLibrary) (Tcl_Interp *interp, const char *prefix, Tcl_LibraryInitProc *initProc, Tcl_LibraryInitProc *safeInitProc); /* 257 */
    Tcl_Obj * (*tclpCreateTemporaryDirectory) (Tcl_Obj *dirObj, Tcl_Obj *basenameObj); /* 258 */
    void (*reserved259)(void);
    Tcl_Obj * (*tclListTestObj) (int length, int leadingSpace, int endSpace); /* 260 */
    void (*tclListObjValidate) (Tcl_Interp *interp, Tcl_Obj *listObj); /* 261 */
} TclIntStubs;

extern const TclIntStubs *tclIntStubsPtr;

#ifdef __cplusplus
}
#endif
915
916
917
918
919
920
921
922

923
924
925
926
927
928
929
	(tclIntStubsPtr->tclGetObjInterpProc) /* 39 */
#define TclGetOpenMode \
	(tclIntStubsPtr->tclGetOpenMode) /* 40 */
#define TclGetOriginalCommand \
	(tclIntStubsPtr->tclGetOriginalCommand) /* 41 */
#define TclpGetUserHome \
	(tclIntStubsPtr->tclpGetUserHome) /* 42 */
/* Slot 43 is reserved */

/* Slot 44 is reserved */
#define TclHideUnsafeCommands \
	(tclIntStubsPtr->tclHideUnsafeCommands) /* 45 */
#define TclInExit \
	(tclIntStubsPtr->tclInExit) /* 46 */
/* Slot 47 is reserved */
/* Slot 48 is reserved */







|
>







926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
	(tclIntStubsPtr->tclGetObjInterpProc) /* 39 */
#define TclGetOpenMode \
	(tclIntStubsPtr->tclGetOpenMode) /* 40 */
#define TclGetOriginalCommand \
	(tclIntStubsPtr->tclGetOriginalCommand) /* 41 */
#define TclpGetUserHome \
	(tclIntStubsPtr->tclpGetUserHome) /* 42 */
#define TclGetObjInterpProc2 \
	(tclIntStubsPtr->tclGetObjInterpProc2) /* 43 */
/* Slot 44 is reserved */
#define TclHideUnsafeCommands \
	(tclIntStubsPtr->tclHideUnsafeCommands) /* 45 */
#define TclInExit \
	(tclIntStubsPtr->tclInExit) /* 46 */
/* Slot 47 is reserved */
/* Slot 48 is reserved */
1250
1251
1252
1253
1254
1255
1256





1257
1258
1259
1260
1261
1262
1263
1264
1265
1266



1267
1268
1269
1270
1271
	(tclIntStubsPtr->tclPtrObjMakeUpvar) /* 255 */
#define TclPtrUnsetVar \
	(tclIntStubsPtr->tclPtrUnsetVar) /* 256 */
#define TclStaticLibrary \
	(tclIntStubsPtr->tclStaticLibrary) /* 257 */
#define TclpCreateTemporaryDirectory \
	(tclIntStubsPtr->tclpCreateTemporaryDirectory) /* 258 */






#endif /* defined(USE_TCL_STUBS) */

/* !END!: Do not edit above this line. */

#if defined(USE_TCL_STUBS)
#undef Tcl_StaticLibrary
#define Tcl_StaticLibrary \
	(tclIntStubsPtr->tclStaticLibrary)
#endif /* defined(USE_TCL_STUBS) */




#undef TCL_STORAGE_CLASS
#define TCL_STORAGE_CLASS DLLIMPORT

#endif /* _TCLINTDECLS */







>
>
>
>
>










>
>
>





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
	(tclIntStubsPtr->tclPtrObjMakeUpvar) /* 255 */
#define TclPtrUnsetVar \
	(tclIntStubsPtr->tclPtrUnsetVar) /* 256 */
#define TclStaticLibrary \
	(tclIntStubsPtr->tclStaticLibrary) /* 257 */
#define TclpCreateTemporaryDirectory \
	(tclIntStubsPtr->tclpCreateTemporaryDirectory) /* 258 */
/* Slot 259 is reserved */
#define TclListTestObj \
	(tclIntStubsPtr->tclListTestObj) /* 260 */
#define TclListObjValidate \
	(tclIntStubsPtr->tclListObjValidate) /* 261 */

#endif /* defined(USE_TCL_STUBS) */

/* !END!: Do not edit above this line. */

#if defined(USE_TCL_STUBS)
#undef Tcl_StaticLibrary
#define Tcl_StaticLibrary \
	(tclIntStubsPtr->tclStaticLibrary)
#endif /* defined(USE_TCL_STUBS) */
#undef TclObjInterpProc
#define TclObjInterpProc TclGetObjInterpProc()
#define TclObjInterpProc2 TclGetObjInterpProc2()

#undef TCL_STORAGE_CLASS
#define TCL_STORAGE_CLASS DLLIMPORT

#endif /* _TCLINTDECLS */
Changes to generic/tclInterp.c.
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
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument vector. */
{
    Alias *aliasPtr = (Alias *)clientData;
    int prefc, cmdc, i;
    Tcl_Obj **prefv, **cmdv;
    Tcl_Obj *listPtr;
    List *listRep;
    int flags = TCL_EVAL_INVOKE;

    /*
     * Append the arguments to the command prefix and invoke the command in
     * the target interp's global namespace.
     */

    prefc = aliasPtr->objc;
    prefv = &aliasPtr->objPtr;
    cmdc = prefc + objc - 1;


    listPtr = Tcl_NewListObj(cmdc, NULL);

    listRep = ListRepPtr(listPtr);
    listRep->elemCount = cmdc;
    cmdv = listRep->elements;




    prefv = &aliasPtr->objPtr;
    memcpy(cmdv, prefv, (prefc * sizeof(Tcl_Obj *)));
    memcpy(cmdv+prefc, objv+1, ((objc-1) * sizeof(Tcl_Obj *)));

    for (i=0; i<cmdc; i++) {
	Tcl_IncrRefCount(cmdv[i]);
    }

    /*
     * Use the ensemble rewriting machinery to ensure correct error messages:







|











>

>
|
|
|
>
>
|
>

|
|







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
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument vector. */
{
    Alias *aliasPtr = (Alias *)clientData;
    int prefc, cmdc, i;
    Tcl_Obj **prefv, **cmdv;
    Tcl_Obj *listPtr;
    ListRep listRep;
    int flags = TCL_EVAL_INVOKE;

    /*
     * Append the arguments to the command prefix and invoke the command in
     * the target interp's global namespace.
     */

    prefc = aliasPtr->objc;
    prefv = &aliasPtr->objPtr;
    cmdc = prefc + objc - 1;

    /* TODO - encapsulate this into tclListObj.c */
    listPtr = Tcl_NewListObj(cmdc, NULL);
    ListObjGetRep(listPtr, &listRep);
    cmdv = ListRepElementsBase(&listRep);
    listRep.storePtr->numUsed = cmdc;
    if (listRep.spanPtr) {
	listRep.spanPtr->spanStart = listRep.storePtr->firstUsed;
	listRep.spanPtr->spanLength = listRep.storePtr->numUsed;
    }

    prefv = &aliasPtr->objPtr;
    memcpy(cmdv, prefv, prefc * sizeof(Tcl_Obj *));
    memcpy(cmdv+prefc, objv+1, (objc-1) * sizeof(Tcl_Obj *));

    for (i=0; i<cmdc; i++) {
	Tcl_IncrRefCount(cmdv[i]);
    }

    /*
     * Use the ensemble rewriting machinery to ensure correct error messages:
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
     * Inherit the recursion limit.
     */

    ((Interp *) childInterp)->maxNestingDepth =
	    ((Interp *) parentInterp)->maxNestingDepth;

    if (safe) {
	if (Tcl_MakeSafe(childInterp) == TCL_ERROR) {
	    goto error;
	}
    } else {
	if (Tcl_Init(childInterp) == TCL_ERROR) {
	    goto error;
	}








|







2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
     * Inherit the recursion limit.
     */

    ((Interp *) childInterp)->maxNestingDepth =
	    ((Interp *) parentInterp)->maxNestingDepth;

    if (safe) {
	if (TclMakeSafe(childInterp) == TCL_ERROR) {
	    goto error;
	}
    } else {
	if (Tcl_Init(childInterp) == TCL_ERROR) {
	    goto error;
	}

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
    }
    return (iPtr->flags & SAFE_INTERP) ? 1 : 0;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_MakeSafe --
 *
 *	Makes its argument interpreter contain only functionality that is
 *	defined to be part of Safe Tcl. Unsafe commands are hidden, the env
 *	array is unset, and the standard channels are removed.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Hides commands in its argument interpreter, and removes settings and
 *	channels.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_MakeSafe(
    Tcl_Interp *interp)		/* Interpreter to be made safe. */
{
    Tcl_Channel chan;		/* Channel to remove from safe interpreter. */
    Interp *iPtr = (Interp *) interp;
    Tcl_Interp *parent = ((InterpInfo*) iPtr->interpInfo)->child.parentInterp;

    TclHideUnsafeCommands(interp);







|
















|







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
    }
    return (iPtr->flags & SAFE_INTERP) ? 1 : 0;
}

/*
 *----------------------------------------------------------------------
 *
 * TclMakeSafe --
 *
 *	Makes its argument interpreter contain only functionality that is
 *	defined to be part of Safe Tcl. Unsafe commands are hidden, the env
 *	array is unset, and the standard channels are removed.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Hides commands in its argument interpreter, and removes settings and
 *	channels.
 *
 *----------------------------------------------------------------------
 */

int
TclMakeSafe(
    Tcl_Interp *interp)		/* Interpreter to be made safe. */
{
    Tcl_Channel chan;		/* Channel to remove from safe interpreter. */
    Interp *iPtr = (Interp *) interp;
    Tcl_Interp *parent = ((InterpInfo*) iPtr->interpInfo)->child.parentInterp;

    TclHideUnsafeCommands(interp);
Changes to generic/tclListObj.c.
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18



19
20



21






































































































22



23








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






















































































































51
















52












53









54


55























56



57























58










59


















60

61































62





















63








64































65










66

67













68























69


70

71


72






































































73

























































































74
75
76
77
78
79
80
81
82
83
84
85

86

87
88
89
90


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

107

















108




109
110
111
112
113
114
115
116
117
118
119




















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





































134
135



136

137
138
139





































140














141




142






















143
144
145
146
147
148
149
150
151

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166



































































167




























168
169
170
171
172
173
174

175
176


177
178


179

180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240



241
242
243
244
245
246











247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298





































































































299
300
301
302
303
304
305



306








307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333

334
335
336


337
338
339


340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386

































































































































































































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


400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482

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
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
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
/*
 * tclListObj.c --
 *
 *	This file contains functions that implement the Tcl list object type.
 *
 * Copyright © 1995-1997 Sun Microsystems, Inc.
 * Copyright © 1998 Scriptics Corporation.
 * Copyright © 2001 Kevin B. Kenny.  All rights reserved.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */


#include "tclInt.h"
#include <assert.h>

/*
 * Prototypes for functions defined later in this file:



 */




static List *		AttemptNewList(Tcl_Interp *interp, size_t objc,






































































































			    Tcl_Obj *const objv[]);



static List *		NewListInternalRep(size_t objc, Tcl_Obj *const objv[], size_t p);








static void		DupListInternalRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr);
static void		FreeListInternalRep(Tcl_Obj *listPtr);
static int		SetListFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
static void		UpdateStringOfList(Tcl_Obj *listPtr);

/*
 * The structure below defines the list Tcl object type by means of functions
 * that can be invoked by generic object code.
 *
 * The internal representation of a list object is a two-pointer
 * representation. The first pointer designates a List structure that contains
 * an array of pointers to the element objects, together with integers that
 * represent the current element count and the allocated size of the array.
 * The second pointer is normally NULL; during execution of functions in this
 * file that operate on nested sublists, it is occasionally used as working
 * storage to avoid an auxiliary stack.
 */

const Tcl_ObjType tclListType = {
    "list",			/* name */
    FreeListInternalRep,	/* freeIntRepProc */
    DupListInternalRep,		/* dupIntRepProc */
    UpdateStringOfList,		/* updateStringProc */
    SetListFromAny		/* setFromAnyProc */
};

/* Macros to manipulate the List internal rep */







































































































































#define ListSetInternalRep(objPtr, listRepPtr)				\












    do {								\









	Tcl_ObjInternalRep ir;						\


	ir.twoPtrValue.ptr1 = (listRepPtr);				\























	ir.twoPtrValue.ptr2 = NULL;					\



	(listRepPtr)->refCount++;					\























	Tcl_StoreInternalRep((objPtr), &tclListType, &ir);			\










    } while (0)




















#define ListGetInternalRep(objPtr, listRepPtr)				\































    do {								\





















	const Tcl_ObjInternalRep *irPtr;					\








	irPtr = TclFetchInternalRep((objPtr), &tclListType);		\































	(listRepPtr) = irPtr ? (List *)irPtr->twoPtrValue.ptr1 : NULL;		\










    } while (0)















#define ListResetInternalRep(objPtr, listRepPtr) \























    TclFetchInternalRep((objPtr), &tclListType)->twoPtrValue.ptr1 = (listRepPtr)




#ifndef TCL_MIN_ELEMENT_GROWTH


#define TCL_MIN_ELEMENT_GROWTH TCL_MIN_GROWTH/sizeof(Tcl_Obj *)






































































#endif


























































































/*
 *----------------------------------------------------------------------
 *
 * NewListInternalRep --
 *
 *	Creates a 'List' structure with space for 'objc' elements.  'objc' must
 *	be > 0.  If 'objv' is not NULL, The list is initialized with first
 *	'objc' values in that array.  Otherwise the list is initialized to have
 *	0 elements, with space to add 'objc' more.  Flag value 'p' indicates
 *	how to behave on failure.
 *

 * Value

 *
 *	A new 'List' structure with refCount 0. If some failure
 *	prevents this NULL is returned if 'p' is 0 , and 'Tcl_Panic'
 *	is called if it is not.


 *
 * Effect
 *
 *	The refCount of each value in 'objv' is incremented as it is added
 *	to the list.
 *
 *----------------------------------------------------------------------
 */

static List *
NewListInternalRep(
    size_t objc,
    Tcl_Obj *const objv[],
    size_t p)
{
    List *listRepPtr;



















    listRepPtr = (List *)Tcl_AttemptAlloc(LIST_SIZE(objc));




    if (listRepPtr == NULL) {
	if (p) {
	    Tcl_Panic("list creation failed: unable to alloc %" TCL_Z_MODIFIER "u bytes",
		    LIST_SIZE(objc));
	}
	return NULL;
    }

    listRepPtr->canonicalFlag = 0;
    listRepPtr->refCount = 0;
    listRepPtr->maxElemCount = objc;





















    if (objv) {
	Tcl_Obj **elemPtrs;
	size_t i;

	listRepPtr->elemCount = objc;
	elemPtrs = listRepPtr->elements;
	for (i = 0;  i < objc;  i++) {
	    elemPtrs[i] = objv[i];
	    Tcl_IncrRefCount(elemPtrs[i]);
	}
    } else {
	listRepPtr->elemCount = 0;
    }





































    return listRepPtr;
}





/*
 *----------------------------------------------------------------------
 *





































 *  AttemptNewList --














 *




 *	Like NewListInternalRep, but additionally sets an error message on failure.






















 *
 *----------------------------------------------------------------------
 */

static List *
AttemptNewList(
    Tcl_Interp *interp,
    size_t objc,
    Tcl_Obj *const objv[])

{
    List *listRepPtr = NewListInternalRep(objc, objv, 0);

    if (interp != NULL && listRepPtr == NULL) {
	if (objc > LIST_MAX) {
	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		    "max length of a Tcl list (%d elements) exceeded",
		    LIST_MAX));
	} else {
	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		    "list creation failed: unable to alloc %" TCL_Z_MODIFIER "u bytes",
		    LIST_SIZE(objc)));
	}
	Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL);
    }



































































    return listRepPtr;




























}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_NewListObj --
 *

 *	Creates a new list object and adds values to it. When TCL_MEM_DEBUG is
 *	defined, 'Tcl_DbNewListObj' is called instead.


 *
 * Value


 *

 *	A new list 'Tcl_Obj' to which is appended values from 'objv', or if
 *	'objc' is less than or equal to zero, a list 'Tcl_Obj' having no
 *	elements.  The string representation of the new 'Tcl_Obj' is set to
 *	NULL.  The refCount of the list is 0.
 *
 * Effect
 *
 *	The refCount of each elements in 'objv' is incremented as it is added
 *	to the list.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MEM_DEBUG
#undef Tcl_NewListObj

Tcl_Obj *
Tcl_NewListObj(
    size_t objc,			/* Count of objects referenced by objv. */
    Tcl_Obj *const objv[])	/* An array of pointers to Tcl objects. */
{
    return Tcl_DbNewListObj(objc, objv, "unknown", 0);
}

#else /* if not TCL_MEM_DEBUG */

Tcl_Obj *
Tcl_NewListObj(
    size_t objc,			/* Count of objects referenced by objv. */
    Tcl_Obj *const objv[])	/* An array of pointers to Tcl objects. */
{
    List *listRepPtr;
    Tcl_Obj *listPtr;

    TclNewObj(listPtr);

    if (objc + 1 <= 1) {
	return listPtr;
    }

    /*
     * Create the internal rep.
     */

    listRepPtr = NewListInternalRep(objc, objv, 1);

    /*
     * Now create the object.
     */

    TclInvalidateStringRep(listPtr);
    ListSetInternalRep(listPtr, listRepPtr);
    return listPtr;
}
#endif /* if TCL_MEM_DEBUG */

/*
 *----------------------------------------------------------------------
 *
 *  Tcl_DbNewListObj --
 *



 *	Like 'Tcl_NewListObj', but it calls Tcl_DbCkalloc directly with the
 *	file name and line number from its caller.  This simplifies debugging
 *	since the [memory active] command will report the correct file
 *	name and line number when reporting objects that haven't been freed.
 *
 *	When TCL_MEM_DEBUG is not defined, 'Tcl_NewListObj' is called instead.











 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MEM_DEBUG

Tcl_Obj *
Tcl_DbNewListObj(
    size_t objc,			/* Count of objects referenced by objv. */
    Tcl_Obj *const objv[],	/* An array of pointers to Tcl objects. */
    const char *file,		/* The name of the source file calling this
				 * function; used for debugging. */
    int line)			/* Line number in the source file; used for
				 * debugging. */
{
    Tcl_Obj *listPtr;
    List *listRepPtr;

    TclDbNewObj(listPtr, file, line);

    if (objc + 1 <= 1) {
	return listPtr;
    }

    /*
     * Create the internal rep.
     */

    listRepPtr = NewListInternalRep(objc, objv, 1);

    /*
     * Now create the object.
     */

    TclInvalidateStringRep(listPtr);
    ListSetInternalRep(listPtr, listRepPtr);

    return listPtr;
}

#else /* if not TCL_MEM_DEBUG */

Tcl_Obj *
Tcl_DbNewListObj(
    size_t objc,			/* Count of objects referenced by objv. */
    Tcl_Obj *const objv[],	/* An array of pointers to Tcl objects. */
    TCL_UNUSED(const char *) /*file*/,
    TCL_UNUSED(int) /*line*/)
{
    return Tcl_NewListObj(objc, objv);
}
#endif /* TCL_MEM_DEBUG */






































































































/*
 *----------------------------------------------------------------------
 *
 * Tcl_SetListObj --
 *
 *	Like 'Tcl_NewListObj', but operates on an existing 'Tcl_Obj'instead of



 *	creating a new one.








 *
 *----------------------------------------------------------------------
 */

void
Tcl_SetListObj(
    Tcl_Obj *objPtr,		/* Object whose internal rep to init. */
    size_t objc,			/* Count of objects referenced by objv. */
    Tcl_Obj *const objv[])	/* An array of pointers to Tcl objects. */
{
    List *listRepPtr;

    if (Tcl_IsShared(objPtr)) {
	Tcl_Panic("%s called with shared object", "Tcl_SetListObj");
    }

    /*
     * Free any old string rep and any internal rep for the old type.
     */

    TclFreeInternalRep(objPtr);
    TclInvalidateStringRep(objPtr);

    /*
     * Set the object's type to "list" and initialize the internal rep.
     * However, if there are no elements to put in the list, just give the
     * object an empty string rep and a NULL type.

     */

    if (objc > 0) {


	listRepPtr = NewListInternalRep(objc, objv, 1);
	ListSetInternalRep(objPtr, listRepPtr);
    } else {


	Tcl_InitStringRep(objPtr, NULL, 0);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TclListObjCopy --
 *
 *	Creates a new 'Tcl_Obj' which is a pure copy of a list value. This
 *	provides for the C level a counterpart of the [lrange $list 0 end]
 *	command, while using internals details to be as efficient as possible.
 *
 * Value
 *
 *	The address of the new 'Tcl_Obj' which shares its internal
 *	representation with 'listPtr', and whose refCount is 0.  If 'listPtr'
 *	is not actually a list, the value is NULL, and an error message is left
 *	in 'interp' if it is not NULL.
 *
 * Effect
 *
 *	'listPtr' is converted to a list if it isn't one already.
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclListObjCopy(
    Tcl_Interp *interp,		/* Used to report errors if not NULL. */
    Tcl_Obj *listPtr)		/* List object for which an element array is
				 * to be returned. */
{
    Tcl_Obj *copyPtr;
    List *listRepPtr;

    ListGetInternalRep(listPtr, listRepPtr);
    if (NULL == listRepPtr) {
	if (SetListFromAny(interp, listPtr) != TCL_OK) {
	    return NULL;
	}
    }

    TclNewObj(copyPtr);
    TclInvalidateStringRep(copyPtr);
    DupListInternalRep(listPtr, copyPtr);
    return copyPtr;

































































































































































































}

/*
 *----------------------------------------------------------------------
 *
 * TclListObjRange --
 *
 *	Makes a slice of a list value.
 *      *listPtr must be known to be a valid list.
 *
 * Results:
 *	Returns a pointer to the sliced list.
 *      This may be a new object or the same object if not shared.


 *
 * Side effects:
 *	The possible conversion of the object referenced by listPtr
 *	to a list object.
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclListObjRange(
    Tcl_Obj *listPtr,		/* List object to take a range from. */
    size_t fromIdx,		/* Index of first element to include. */
    size_t toIdx)			/* Index of last element to include. */
{
    Tcl_Obj **elemPtrs;
    size_t listLen, i, newLen;
    List *listRepPtr;

    TclListObjGetElementsM(NULL, listPtr, &listLen, &elemPtrs);

    if (fromIdx == TCL_INDEX_NONE) {
	fromIdx = 0;
    }
    if (toIdx + 1 >= listLen + 1) {
	toIdx = listLen-1;
    }
    if (fromIdx + 1 > toIdx + 1) {
	Tcl_Obj *obj;
	TclNewObj(obj);
	return obj;
    }

    newLen = toIdx - fromIdx + 1;

    if (Tcl_IsShared(listPtr) ||
	    ((ListRepPtr(listPtr)->refCount > 1))) {
	return Tcl_NewListObj(newLen, &elemPtrs[fromIdx]);
    }

    /*
     * In-place is possible.
     */

    /*
     * Even if nothing below cause any changes, we still want the
     * string-canonizing effect of [lrange 0 end].
     */

    TclInvalidateStringRep(listPtr);

    /*
     * Delete elements that should not be included.
     */

    for (i = 0; i < fromIdx; i++) {
	TclDecrRefCount(elemPtrs[i]);
    }
    for (i = toIdx + 1; i < (size_t)listLen; i++) {
	TclDecrRefCount(elemPtrs[i]);
    }

    if (fromIdx > 0) {
	memmove(elemPtrs, &elemPtrs[fromIdx],
		(size_t) newLen * sizeof(Tcl_Obj*));
    }

    listRepPtr = ListRepPtr(listPtr);
    listRepPtr->elemCount = newLen;

    return listPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ListObjGetElements --
 *
 *	Retreive the elements in a list 'Tcl_Obj'.
 *
 * Value
 *
 *	TCL_OK
 *

 *	    A count of list elements is stored, 'objcPtr', And a pointer to the
 *	    array of elements in the list is stored in 'objvPtr'.



 *
 *	    The elements accessible via 'objvPtr' should be treated as readonly
 *	    and the refCount for each object is _not_ incremented; the caller
 *	    must do that if it holds on to a reference. Furthermore, the
 *	    pointer and length returned by this function may change as soon as
 *	    any function is called on the list object. Be careful about
 *	    retaining the pointer in a local data structure.
 *
 *	TCL_ERROR
 *
 *	    'listPtr' is not a valid list. An error message is left in the
 *	    interpreter's result if 'interp' is not NULL.
 *
 * Effect
 *

 *	'listPtr' is converted to a list object if it isn't one already.
 *
 *----------------------------------------------------------------------
 */

#undef Tcl_ListObjGetElements
int
Tcl_ListObjGetElements(
    Tcl_Interp *interp,		/* Used to report errors if not NULL. */
    Tcl_Obj *listPtr,	/* List object for which an element array is
				 * to be returned. */
    size_t *objcPtr,		/* Where to store the count of objects
				 * referenced by objv. */
    Tcl_Obj ***objvPtr)		/* Where to store the pointer to an array of
				 * pointers to the list's objects. */
{
    List *listRepPtr;

    ListGetInternalRep(listPtr, listRepPtr);

    if (listRepPtr == NULL) {
	int result;
	size_t length;

	(void) Tcl_GetStringFromObj(listPtr, &length);
	if (length == 0) {
	    *objcPtr = 0;
	    *objvPtr = NULL;
	    return TCL_OK;
	}
	result = SetListFromAny(interp, listPtr);
	if (result != TCL_OK) {
	    return result;
	}
	ListGetInternalRep(listPtr, listRepPtr);
    }
    *objcPtr = listRepPtr->elemCount;
    *objvPtr = listRepPtr->elements;
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ListObjAppendList --
 *
 *	Appends the elements of elemListPtr to those of listPtr.

 *
 * Value
 *
 *	TCL_OK
 *
 *	    Success.
 *
 *	TCL_ERROR
 *
 *	    'listPtr' or 'elemListPtr' are not valid lists.  An error
 *	    message is left in the interpreter's result if 'interp' is not NULL.
 *
 * Effect
 *
 *	The reference count of each element of 'elemListPtr' as it is added to
 *	'listPtr'. 'listPtr' and 'elemListPtr' are converted to 'tclListType'

 *	if they are not already. Appending the new elements may cause the
 *	array of element pointers in 'listObj' to grow.  If any objects are
 *	appended to 'listPtr'. Any preexisting string representation of
 *	'listPtr' is invalidated.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_ListObjAppendList(
    Tcl_Interp *interp,		/* Used to report errors if not NULL. */
    Tcl_Obj *listPtr,	/* List object to append elements to. */
    Tcl_Obj *elemListPtr)	/* List obj with elements to append. */
{
    size_t objc;
    Tcl_Obj **objv;

    if (Tcl_IsShared(listPtr)) {
	Tcl_Panic("%s called with shared object", "Tcl_ListObjAppendList");
    }

    /*
     * Pull the elements to append from elemListPtr.
     */

    if (TCL_OK != TclListObjGetElementsM(interp, elemListPtr, &objc, &objv)) {
	return TCL_ERROR;
    }

    /*
     * Insert the new elements starting after the lists's last element.
     * Delete zero existing elements.
     */



    return Tcl_ListObjReplace(interp, listPtr, LIST_MAX, 0, objc, objv);


















































































































































}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ListObjAppendElement --
 *
 *	Like 'Tcl_ListObjAppendList', but Appends a single value to a list.



 *
 * Value
 *
 *	TCL_OK
 *
 *	    'objPtr' is appended to the elements of 'listPtr'.
 *
 *	TCL_ERROR
 *
 *	    listPtr does not refer to a list object and the object can not be
 *	    converted to one. An error message will be left in the
 *	    interpreter's result if interp is not NULL.
 *
 * Effect
 *
 *	If 'listPtr' is not already of type 'tclListType', it is converted.
 *	The 'refCount' of 'objPtr' is incremented as it is added to 'listPtr'.

 *	Appending the new element may cause the the array of element pointers
 *	in 'listObj' to grow.  Any preexisting string representation of
 *	'listPtr' is invalidated.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_ListObjAppendElement(
    Tcl_Interp *interp,		/* Used to report errors if not NULL. */
    Tcl_Obj *listPtr,		/* List object to append objPtr to. */
    Tcl_Obj *objPtr)		/* Object to append to listPtr's list. */
{
    List *listRepPtr, *newPtr = NULL;
    size_t numElems, numRequired;
    int needGrow, isShared, attempt;

    if (Tcl_IsShared(listPtr)) {
	Tcl_Panic("%s called with shared object", "Tcl_ListObjAppendElement");
    }

    ListGetInternalRep(listPtr, listRepPtr);
    if (listRepPtr == NULL) {
	int result;
	size_t length;

	(void) Tcl_GetStringFromObj(listPtr, &length);
	if (length == 0) {
	    Tcl_SetListObj(listPtr, 1, &objPtr);
	    return TCL_OK;
	}
	result = SetListFromAny(interp, listPtr);
	if (result != TCL_OK) {
	    return result;
	}
	ListGetInternalRep(listPtr, listRepPtr);
    }

    numElems = listRepPtr->elemCount;
    numRequired = numElems + 1 ;
    needGrow = (numRequired > listRepPtr->maxElemCount);
    isShared = (listRepPtr->refCount > 1);

    if (numRequired > LIST_MAX) {
	if (interp != NULL) {
	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		    "max length of a Tcl list (%d elements) exceeded",
		    LIST_MAX));
	    Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL);
	}
	return TCL_ERROR;
    }

    if (needGrow && !isShared) {
	/*
	 * Need to grow + unshared internalrep => try to realloc
	 */

	attempt = 2 * numRequired;
	if (attempt <= LIST_MAX) {
	    newPtr = (List *)Tcl_AttemptRealloc(listRepPtr, LIST_SIZE(attempt));
	}
	if (newPtr == NULL) {
	    attempt = numRequired + 1 + TCL_MIN_ELEMENT_GROWTH;
	    if (attempt > LIST_MAX) {
		attempt = LIST_MAX;
	    }
	    newPtr = (List *)Tcl_AttemptRealloc(listRepPtr, LIST_SIZE(attempt));
	}
	if (newPtr == NULL) {
	    attempt = numRequired;
	    newPtr = (List *)Tcl_AttemptRealloc(listRepPtr, LIST_SIZE(attempt));
	}
	if (newPtr) {
	    listRepPtr = newPtr;
	    listRepPtr->maxElemCount = attempt;
	    needGrow = 0;
	}
    }
    if (isShared || needGrow) {
	Tcl_Obj **dst, **src = listRepPtr->elements;

	/*
	 * Either we have a shared internalrep and we must copy to write, or we
	 * need to grow and realloc attempts failed.  Attempt internalrep copy.
	 */

	attempt = 2 * numRequired;
	newPtr = AttemptNewList(NULL, attempt, NULL);
	if (newPtr == NULL) {
	    attempt = numRequired + 1 + TCL_MIN_ELEMENT_GROWTH;
	    if (attempt > LIST_MAX) {
		attempt = LIST_MAX;
	    }
	    newPtr = AttemptNewList(NULL, attempt, NULL);
	}
	if (newPtr == NULL) {
	    attempt = numRequired;
	    newPtr = AttemptNewList(interp, attempt, NULL);
	}
	if (newPtr == NULL) {
	    /*
	     * All growth attempts failed; throw the error.
	     */

	    return TCL_ERROR;
	}

	dst = newPtr->elements;
	newPtr->refCount++;
	newPtr->canonicalFlag = listRepPtr->canonicalFlag;
	newPtr->elemCount = listRepPtr->elemCount;

	if (isShared) {
	    /*
	     * The original internalrep must remain undisturbed.  Copy into the new
	     * one and bump refcounts
	     */
	    while (numElems--) {
		*dst = *src++;
		Tcl_IncrRefCount(*dst++);
	    }
	    listRepPtr->refCount--;
	} else {
	    /*
	     * Old internalrep to be freed, re-use refCounts.
	     */

	    memcpy(dst, src, numElems * sizeof(Tcl_Obj *));
	    Tcl_Free(listRepPtr);
	}
	listRepPtr = newPtr;
    }
    ListResetInternalRep(listPtr, listRepPtr);
    listRepPtr->refCount++;
    TclFreeInternalRep(listPtr);
    ListSetInternalRep(listPtr, listRepPtr);
    listRepPtr->refCount--;

    /*
     * Add objPtr to the end of listPtr's array of element pointers. Increment
     * the ref count for the (now shared) objPtr.
     */

    listRepPtr->elements[listRepPtr->elemCount] = objPtr;
    Tcl_IncrRefCount(objPtr);
    listRepPtr->elemCount++;

    /*
     * Invalidate any old string representation since the list's internal
     * representation has changed.
     */

    TclInvalidateStringRep(listPtr);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ListObjIndex --
 *
 * 	Retrieve a pointer to the element of 'listPtr' at 'index'.  The index
 * 	of the first element is 0.
 *
 * Value

 *
 * 	TCL_OK
 *
 *	    A pointer to the element at 'index' is stored in 'objPtrPtr'.  If
 *	    'index' is out of range, NULL is stored in 'objPtrPtr'.  This
 *	    object should be treated as readonly and its 'refCount' is _not_
 *	    incremented. The caller must do that if it holds on to the
 *	    reference.
 *
 * 	TCL_ERROR
 *
 * 	    'listPtr' is not a valid list. An an error message is left in the
 * 	    interpreter's result if 'interp' is not NULL.
 *
 *  Effect
 *
 * 	If 'listPtr' is not already of type 'tclListType', it is converted.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_ListObjIndex(
    Tcl_Interp *interp,		/* Used to report errors if not NULL. */
    Tcl_Obj *listPtr,	/* List object to index into. */
    size_t index,		/* Index of element to return. */
    Tcl_Obj **objPtrPtr)	/* The resulting Tcl_Obj* is stored here. */
{

    List *listRepPtr;

    ListGetInternalRep(listPtr, listRepPtr);
    if (listRepPtr == NULL) {
	int result;
	size_t length;

	(void) Tcl_GetStringFromObj(listPtr, &length);
	if (length == 0) {
	    *objPtrPtr = NULL;
	    return TCL_OK;
	}







	result = SetListFromAny(interp, listPtr);
	if (result != TCL_OK) {
	    return result;
	}
	ListGetInternalRep(listPtr, listRepPtr);
    }

    if (index >= listRepPtr->elemCount) {
	*objPtrPtr = NULL;
    } else {
	*objPtrPtr = listRepPtr->elements[index];
    }

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ListObjLength --
 *
 * 	Retrieve the number of elements in a list.


 *
 * Value
 *
 *	TCL_OK
 *
 *	    A count of list elements is stored at the address provided by


 *	    'intPtr'. If 'listPtr' is not already of type 'tclListPtr', it is
 *	    converted.
 *
 *	TCL_ERROR
 *
 *	    'listPtr' is not a valid list.  An error message will be left in
 *	    the interpreter's result if 'interp' is not NULL.
 *
 *----------------------------------------------------------------------
 */

#undef Tcl_ListObjLength
int
Tcl_ListObjLength(
    Tcl_Interp *interp,		/* Used to report errors if not NULL. */
    Tcl_Obj *listPtr,	/* List object whose #elements to return. */
    size_t *intPtr)	/* The resulting length is stored here. */
{
    List *listRepPtr;

    ListGetInternalRep(listPtr, listRepPtr);
    if (listRepPtr == NULL) {
	int result;
	size_t length;

	(void) Tcl_GetStringFromObj(listPtr, &length);
	if (length == 0) {
	    *intPtr = 0;
	    return TCL_OK;
	}
	result = SetListFromAny(interp, listPtr);
	if (result != TCL_OK) {
	    return result;
	}







	ListGetInternalRep(listPtr, listRepPtr);

    }

    *intPtr = listRepPtr->elemCount;
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ListObjReplace --
 *
 *	Replace values in a list.



 *
 *	If 'first' is zero or TCL_INDEX_NONE, it refers to the first element. If
 *	'first' outside the range of elements in the list, no elements are
 *	deleted.
 *

 *	If 'count' is zero or TCL_INDEX_NONE no elements are deleted, and any new
 *	elements are inserted at the beginning of the list.
 *
 * Value
 *
 *	TCL_OK
 *
 *	    The first 'objc' values of 'objv' replaced 'count' elements in 'listPtr'
 *	    starting at 'first'.  If 'objc' 0, no new elements are added.

 *


 *	TCL_ERROR
 *
 *	    'listPtr' is not a valid list.   An error message is left in the
 *	    interpreter's result if 'interp' is not NULL.
 *
 * Effect
 *
 *	If 'listPtr' is not of type 'tclListType', it is converted if possible.
 *
 *	The 'refCount' of each element appended to the list is incremented.
 *	Similarly, the 'refCount' for each replaced element is decremented.
 *

 *	If 'listPtr' is modified, any previous string representation is
 *	invalidated.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_ListObjReplace(
    Tcl_Interp *interp,		/* Used for error reporting if not NULL. */
    Tcl_Obj *listPtr,		/* List object whose elements to replace. */
    size_t first,			/* Index of first element to replace. */
    size_t count,			/* Number of elements to replace. */
    size_t objc,			/* Number of objects to insert. */
    Tcl_Obj *const objv[])	/* An array of objc pointers to Tcl objects to
				 * insert. */
{
    List *listRepPtr;







    Tcl_Obj **elemPtrs;
    size_t numElems, numRequired, numAfterLast, start, i, j;
    int needGrow, isShared;

    if (Tcl_IsShared(listPtr)) {
	Tcl_Panic("%s called with shared object", "Tcl_ListObjReplace");
    }

    ListGetInternalRep(listPtr, listRepPtr);
    if (listRepPtr == NULL) {
	size_t length;

	(void) Tcl_GetStringFromObj(listPtr, &length);
	if (length == 0) {
	    if (objc == 0) {
		return TCL_OK;
	    }
	    Tcl_SetListObj(listPtr, objc, NULL);
	} else {
	    int result = SetListFromAny(interp, listPtr);

	    if (result != TCL_OK) {
		return result;
	    }
	}
	ListGetInternalRep(listPtr, listRepPtr);
    }

    /*
     * Note that when count == 0 and objc == 0, this routine is logically a
     * no-op, removing and adding no elements to the list. However, by flowing
     * through this routine anyway, we get the important side effect that the
     * resulting listPtr is a list in canoncial form. This is important.
     * Resist any temptation to optimize this case.
     */

    elemPtrs = listRepPtr->elements;
    numElems = listRepPtr->elemCount;

    if (first == TCL_INDEX_NONE) {
	first = 0;
    }
    if (first >= numElems) {
	first = numElems;	/* So we'll insert after last element. */
    }
    if (count == TCL_INDEX_NONE) {
	count = 0;
    } else if (count > LIST_MAX /* Handle integer overflow */
	    || numElems < first+count) {


	count = numElems - first;


    }

    isShared = (listRepPtr->refCount > 1);


    numRequired = numElems - count + objc; /* Known <= LIST_MAX */




    needGrow = numRequired > listRepPtr->maxElemCount;


    for (i = 0;  i < objc;  i++) {
















	Tcl_IncrRefCount(objv[i]);

    }











    if (needGrow && !isShared) {

	/* Try to use realloc */
	List *newPtr = NULL;
	size_t attempt = 2 * numRequired;



	if (attempt <= LIST_MAX) {


	    newPtr = (List *)Tcl_AttemptRealloc(listRepPtr, LIST_SIZE(attempt));



	}
	if (newPtr == NULL) {
	    attempt = numRequired + 1 + TCL_MIN_ELEMENT_GROWTH;
	    if (attempt > LIST_MAX) {
		attempt = LIST_MAX;

	    }
	    newPtr = (List *)Tcl_AttemptRealloc(listRepPtr, LIST_SIZE(attempt));


	}



	if (newPtr == NULL) {
	    attempt = numRequired;
	    newPtr = (List *)Tcl_AttemptRealloc(listRepPtr, LIST_SIZE(attempt));
	}
	if (newPtr) {
	    listRepPtr = newPtr;
	    ListResetInternalRep(listPtr, listRepPtr);
	    elemPtrs = listRepPtr->elements;
	    listRepPtr->maxElemCount = attempt;
	    needGrow = numRequired > listRepPtr->maxElemCount;
	}
    }











    if (!needGrow && !isShared) {





	int shift;
















	/*
	 * Can use the current List struct. First "delete" count elements
	 * starting at first.
	 */

	for (j = first;  j < first + count;  j++) {
	    Tcl_Obj *victimPtr = elemPtrs[j];



	    TclDecrRefCount(victimPtr);
	}







	/*
	 * Shift the elements after the last one removed to their new

	 * locations.






	 */








	start = first + count;
	numAfterLast = numElems - start;
	shift = objc - count;	/* numNewElems - numDeleted */
	if ((numAfterLast > 0) && (shift != 0)) {


	    Tcl_Obj **src = elemPtrs + start;





	    memmove(src+shift, src, numAfterLast * sizeof(Tcl_Obj*));
	}
    } else {
	/*

	 * Cannot use the current List struct; it is shared, too small, or
	 * both. Allocate a new struct and insert elements into it.


	 */




	List *oldListRepPtr = listRepPtr;
	Tcl_Obj **oldPtrs = elemPtrs;




	int newMax;











	if (needGrow) {

	    newMax = 2 * numRequired;
	} else {

	    newMax = listRepPtr->maxElemCount;
	}





	listRepPtr = AttemptNewList(NULL, newMax, NULL);

	if (listRepPtr == NULL) {
	    unsigned int limit = LIST_MAX - numRequired;
	    unsigned int extra = numRequired - numElems
		    + TCL_MIN_ELEMENT_GROWTH;

	    int growth = (int) ((extra > limit) ? limit : extra);









	    listRepPtr = AttemptNewList(NULL, numRequired + growth, NULL);

	    if (listRepPtr == NULL) {
		listRepPtr = AttemptNewList(interp, numRequired, NULL);
		if (listRepPtr == NULL) {
		    for (i = 0;  i < objc;  i++) {

			/* See bug 3598580 */
			Tcl_DecrRefCount(objv[i]);


		    }
		    return TCL_ERROR;







		}



	    }
	}









	ListResetInternalRep(listPtr, listRepPtr);
	listRepPtr->refCount++;


	elemPtrs = listRepPtr->elements;



	if (isShared) {




	    /*
	     * The old struct will remain in place; need new refCounts for the
	     * new List struct references. Copy over only the surviving

	     * elements.
	     */


	    for (i=0; i < first; i++) {
		elemPtrs[i] = oldPtrs[i];
		Tcl_IncrRefCount(elemPtrs[i]);
	    }
	    for (i = first + count, j = first + objc;
		    j < numRequired; i++, j++) {
		elemPtrs[j] = oldPtrs[i];
		Tcl_IncrRefCount(elemPtrs[j]);
	    }

	    oldListRepPtr->refCount--;
	} else {


	    /*
	     * The old struct will be removed; use its inherited refCounts.



	     */





	    if (first > 0) {
		memcpy(elemPtrs, oldPtrs, first * sizeof(Tcl_Obj *));


















	    }







	    /*
	     * "Delete" count elements starting at first.
	     */








	    for (j = first;  j < first + count;  j++) {
		Tcl_Obj *victimPtr = oldPtrs[j];

		TclDecrRefCount(victimPtr);









	    }















	    /*
	     * Copy the elements after the last one removed, shifted to their
	     * new locations.


	     */





	    start = first + count;
	    numAfterLast = numElems - start;

	    if (numAfterLast > 0) {
		memcpy(elemPtrs + first + objc, oldPtrs + start,






			(size_t) numAfterLast * sizeof(Tcl_Obj *));
	    }



	    Tcl_Free(oldListRepPtr);

	}






    }

    /*
     * Insert the new elements into elemPtrs before "first".
     */






    for (i=0,j=first ; i<objc ; i++,j++) {
	elemPtrs[j] = objv[i];
    }







    /*
     * Update the count of elements.
     */



    listRepPtr->elemCount = numRequired;

    /*
     * Invalidate and free any old representations that may not agree
     * with the revised list's internal representation.
     */




    listRepPtr->refCount++;
    TclFreeInternalRep(listPtr);
    ListSetInternalRep(listPtr, listRepPtr);



    listRepPtr->refCount--;


    TclInvalidateStringRep(listPtr);


    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TclLindexList --
 *
 *	Implements the 'lindex' command when objc==3.
 *
 *	Implemented entirely as a wrapper around 'TclLindexFlat'. Reconfigures
 *	the argument format into required form while taking care to manage
 *	shimmering so as to tend to keep the most useful internalreps
 *	and/or avoid the most expensive conversions.
 *
 * Value
 *
 *	A pointer to the specified element, with its 'refCount' incremented, or
 *	NULL if an error occurred.
 *
 * Notes





 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclLindexList(
    Tcl_Interp *interp,		/* Tcl interpreter. */
    Tcl_Obj *listPtr,		/* List being unpacked. */
    Tcl_Obj *argPtr)		/* Index or index list. */
{

    size_t index;			/* Index into the list. */
    Tcl_Obj *indexListCopy;

    List *listRepPtr;

    /*
     * Determine whether argPtr designates a list or a single index. We have
     * to be careful about the order of the checks to avoid repeated

     * shimmering; see TIP#22 and TIP#33 for the details.
     */

    ListGetInternalRep(argPtr, listRepPtr);
    if ((listRepPtr == NULL)
	    && TclGetIntForIndexM(NULL , argPtr, (size_t)WIDE_MAX - 1, &index) == TCL_OK) {

	/*
	 * argPtr designates a single index.
	 */

	return TclLindexFlat(interp, listPtr, 1, &argPtr);
    }

    /*
     * Here we make a private copy of the index list argument to avoid any
     * shimmering issues that might invalidate the indices array below while
     * we are still using it. This is probably unnecessary. It does not appear
     * that any damaging shimmering is possible, and no test has been devised
     * to show any error when this private copy is not made. But it's cheap,
     * and it offers some future-proofing insurance in case the TclLindexFlat
     * implementation changes in some unexpected way, or some new form of
     * trace or callback permits things to happen that the current
     * implementation does not.
     */

    indexListCopy = TclListObjCopy(NULL, argPtr);
    if (indexListCopy == NULL) {
	/*
	 * argPtr designates something that is neither an index nor a
	 * well-formed list. Report the error via TclLindexFlat.

	 */

	return TclLindexFlat(interp, listPtr, 1, &argPtr);
    }

    ListGetInternalRep(indexListCopy, listRepPtr);

    assert(listRepPtr != NULL);

    listPtr = TclLindexFlat(interp, listPtr, listRepPtr->elemCount,
		listRepPtr->elements);
    Tcl_DecrRefCount(indexListCopy);
    return listPtr;
}

/*
 *----------------------------------------------------------------------
 *
 *  TclLindexFlat --
 *
 * 	The core of the 'lindex' command, with all index
 * 	arguments presented as a flat list.
 *
 *  Value



 *
 *	A pointer to the object extracted, with its 'refCount' incremented,  or
 *	NULL if an error occurred.  Thus, the calling code will usually do

 *	something like:
 *




 * 		Tcl_SetObjResult(interp, result);
 * 		Tcl_DecrRefCount(result);
 *
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclLindexFlat(
    Tcl_Interp *interp,		/* Tcl interpreter. */
    Tcl_Obj *listPtr,		/* Tcl object representing the list. */
    size_t indexCount,		/* Count of indices. */
    Tcl_Obj *const indexArray[])/* Array of pointers to Tcl objects that
				 * represent the indices in the list. */
{

    size_t i;










    Tcl_IncrRefCount(listPtr);












    for (i=0 ; i<indexCount && listPtr ; i++) {
	size_t index, listLen = 0;
	Tcl_Obj **elemPtrs = NULL, *sublistCopy;

	/*
	 * Here we make a private copy of the current sublist, so we avoid any
	 * shimmering issues that might invalidate the elemPtr array below
	 * while we are still using it. See test lindex-8.4.
	 */

	sublistCopy = TclListObjCopy(interp, listPtr);
	Tcl_DecrRefCount(listPtr);
	listPtr = NULL;

	if (sublistCopy == NULL) {
	    /*
	     * The sublist is not a list at all => error.
	     */

	    break;
	}

	TclListObjGetElementsM(NULL, sublistCopy, &listLen, &elemPtrs);

	if (TclGetIntForIndexM(interp, indexArray[i], /*endValue*/ listLen-1,
		&index) == TCL_OK) {
	    if (index >= (size_t)listLen) {
		/*
		 * Index is out of range. Break out of loop with empty result.
		 * First check remaining indices for validity
		 */

		while (++i < indexCount) {
		    if (TclGetIntForIndexM(interp, indexArray[i], (size_t)WIDE_MAX - 1, &index)

			!= TCL_OK) {
			Tcl_DecrRefCount(sublistCopy);
			return NULL;
		    }
		}
		TclNewObj(listPtr);
	    } else {
		/*
		 * Extract the pointer to the appropriate element.
		 */

		listPtr = elemPtrs[index];
	    }
	    Tcl_IncrRefCount(listPtr);
	}
	Tcl_DecrRefCount(sublistCopy);
    }

    return listPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TclLsetList --
 *
 *	The core of [lset] when objc == 4. Objv[2] may be either a
 *	scalar index or a list of indices.
 *      It also handles 'lpop' when given a NULL value.
 *
 *	Implemented entirely as a wrapper around 'TclLindexFlat', as described


 *	for 'TclLindexList'.
 *
 * Value

 *
 *	The new list, with the 'refCount' of 'valuPtr' incremented, or NULL if

 *	there was an error.




 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclLsetList(
    Tcl_Interp *interp,		/* Tcl interpreter. */
    Tcl_Obj *listPtr,		/* Pointer to the list being modified. */
    Tcl_Obj *indexArgPtr,	/* Index or index-list arg to 'lset'. */
    Tcl_Obj *valuePtr)		/* Value arg to 'lset' or NULL to 'lpop'. */
{
    size_t indexCount = 0;		/* Number of indices in the index list. */
    Tcl_Obj **indices = NULL;	/* Vector of indices in the index list. */
    Tcl_Obj *retValuePtr;	/* Pointer to the list to be returned. */
    size_t index;			/* Current index in the list - discarded. */
    Tcl_Obj *indexListCopy;
    List *listRepPtr;

    /*
     * Determine whether the index arg designates a list or a single index.
     * We have to be careful about the order of the checks to avoid repeated
     * shimmering; see TIP #22 and #23 for details.
     */

    ListGetInternalRep(indexArgPtr, listRepPtr);
    if (listRepPtr == NULL
	    && TclGetIntForIndexM(NULL, indexArgPtr, (size_t)WIDE_MAX - 1, &index) == TCL_OK) {
	/*

	 * indexArgPtr designates a single index.
	 */

	return TclLsetFlat(interp, listPtr, 1, &indexArgPtr, valuePtr);

    }

    indexListCopy = TclListObjCopy(NULL, indexArgPtr);
    if (indexListCopy == NULL) {
	/*
	 * indexArgPtr designates something that is neither an index nor a
	 * well formed list. Report the error via TclLsetFlat.
	 */

	return TclLsetFlat(interp, listPtr, 1, &indexArgPtr, valuePtr);
    }

    TclListObjGetElementsM(NULL, indexArgPtr, &indexCount, &indices);

    /*
     * Let TclLsetFlat handle the actual lset'ting.
     */

    retValuePtr = TclLsetFlat(interp, listPtr, indexCount, indices, valuePtr);

    Tcl_DecrRefCount(indexListCopy);
    return retValuePtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TclLsetFlat --
 *
 *	Core engine of the 'lset' command.
 *      It also handles 'lpop' when given a NULL value.
 *
 * Value
 *

 *	The resulting list
 *
 *	    The 'refCount' of 'valuePtr' is incremented.  If 'listPtr' was not

 *	    duplicated, its 'refCount' is incremented.  The reference count of

 *	    an unduplicated object is therefore 2 (one for the returned pointer
 *	    and one for the variable that holds it).  The reference count of a
 *	    duplicate object is 1, reflecting that result is the only active
 *	    reference. The caller is expected to store the result in the
 *	    variable and decrement its reference count. (INST_STORE_* does
 *	    exactly this.)
 *
 *	NULL
 *
 *	    An error occurred.  If 'listPtr' was duplicated, the reference
 *	    count on the duplicate is decremented so that it is 0, causing any
 *	    memory allocated by this function to be freed.
 *
 *
 * Effect
 *
 *	On entry, the reference count of 'listPtr' does not reflect any
 *	references held on the stack. The first action of this function is to
 *	determine whether 'listPtr' is shared and to create a duplicate
 *	unshared copy if it is.  The reference count of the duplicate is

 *	incremented. At this point, the reference count is 1 in either case so
 *	that the object is considered unshared.


 *
 *	The unshared list is altered directly to produce the result.
 *	'TclLsetFlat' maintains a linked list of 'Tcl_Obj' values whose string
 *	representations must be spoilt by threading via 'ptr2' of the
 *	two-pointer internal representation. On entry to 'TclLsetFlat', the
 *	values of 'ptr2' are immaterial; on exit, the 'ptr2' field of any
 *	Tcl_Obj that has been modified is set to NULL.
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclLsetFlat(
    Tcl_Interp *interp,		/* Tcl interpreter. */
    Tcl_Obj *listPtr,		/* Pointer to the list being modified. */
    size_t indexCount,		/* Number of index args. */
    Tcl_Obj *const indexArray[],
				/* Index args. */
    Tcl_Obj *valuePtr)		/* Value arg to 'lset' or NULL to 'lpop'. */
{
    size_t index, len;
    int result;
    Tcl_Obj *subListPtr, *retValuePtr, *chainPtr;
    Tcl_ObjInternalRep *irPtr;



    /*
     * If there are no indices, simply return the new value.  (Without
     * indices, [lset] is a synonym for [set].
     * [lpop] does not use this but protect for NULL valuePtr just in case.
     */

    if (indexCount == 0) {
	if (valuePtr != NULL) {
	    Tcl_IncrRefCount(valuePtr);
	}
	return valuePtr;
    }

    /*
     * If the list is shared, make a copy we can modify (copy-on-write).  We
     * use Tcl_DuplicateObj() instead of TclListObjCopy() for a few reasons:
     * 1) we have not yet confirmed listPtr is actually a list; 2) We make a
     * verbatim copy of any existing string rep, and when we combine that with
     * the delayed invalidation of string reps of modified Tcl_Obj's
     * implemented below, the outcome is that any error condition that causes
     * this routine to return NULL, will leave the string rep of listPtr and
     * all elements to be unchanged.
     */

    subListPtr = Tcl_IsShared(listPtr) ? Tcl_DuplicateObj(listPtr) : listPtr;

    /*
     * Anchor the linked list of Tcl_Obj's whose string reps must be
     * invalidated if the operation succeeds.
     */

    retValuePtr = subListPtr;
    chainPtr = NULL;
    result = TCL_OK;








    /*
     * Loop through all the index arguments, and for each one dive into the
     * appropriate sublist.
     */

    do {
	size_t elemCount;
	Tcl_Obj *parentList, **elemPtrs;

	/*
	 * Check for the possible error conditions...
	 */

	if (TclListObjGetElementsM(interp, subListPtr, &elemCount, &elemPtrs)
		!= TCL_OK) {
	    /* ...the sublist we're indexing into isn't a list at all. */
	    result = TCL_ERROR;
	    break;
	}

	/*
	 * WARNING: the macro TclGetIntForIndexM is not safe for
	 * post-increments, avoid '*indexArray++' here.
	 */

	if (TclGetIntForIndexM(interp, *indexArray, elemCount - 1, &index)
		!= TCL_OK)  {
	    /* ...the index we're trying to use isn't an index at all. */
	    result = TCL_ERROR;
	    indexArray++;
	    break;
	}
	indexArray++;

	if (index > elemCount
		|| (valuePtr == NULL && index >= elemCount)) {
	    /* ...the index points outside the sublist. */
	    if (interp != NULL) {
		Tcl_SetObjResult(interp, Tcl_ObjPrintf(

			"index \"%s\" out of range", Tcl_GetString(indexArray[-1])));
		Tcl_SetErrorCode(interp, "TCL", "VALUE", "INDEX"



			"OUTOFRANGE", NULL);

	    }
	    result = TCL_ERROR;
	    break;
	}

	/*
	 * No error conditions.  As long as we're not yet on the last index,
	 * determine the next sublist for the next pass through the loop, and
	 * take steps to make sure it is an unshared copy, as we intend to
	 * modify it.
	 */

	if (--indexCount) {
	    parentList = subListPtr;
	    if (index == (size_t)elemCount) {
		TclNewObj(subListPtr);
	    } else {
		subListPtr = elemPtrs[index];
	    }
	    if (Tcl_IsShared(subListPtr)) {
		subListPtr = Tcl_DuplicateObj(subListPtr);
	    }

	    /*
	     * Replace the original elemPtr[index] in parentList with a copy
	     * we know to be unshared.  This call will also deal with the
	     * situation where parentList shares its internalrep with other
	     * Tcl_Obj's.  Dealing with the shared internalrep case can cause
	     * subListPtr to become shared again, so detect that case and make
	     * and store another copy.
	     */

	    if (index == (size_t)elemCount) {
		Tcl_ListObjAppendElement(NULL, parentList, subListPtr);
	    } else {
		TclListObjSetElement(NULL, parentList, index, subListPtr);
	    }
	    if (Tcl_IsShared(subListPtr)) {
		subListPtr = Tcl_DuplicateObj(subListPtr);
		TclListObjSetElement(NULL, parentList, index, subListPtr);
	    }

	    /*
	     * The TclListObjSetElement() calls do not spoil the string rep of
	     * parentList, and that's fine for now, since all we've done so
	     * far is replace a list element with an unshared copy.  The list
	     * value remains the same, so the string rep. is still valid, and
	     * unchanged, which is good because if this whole routine returns
	     * NULL, we'd like to leave no change to the value of the lset
	     * variable.  Later on, when we set valuePtr in its proper place,
	     * then all containing lists will have their values changed, and

	     * will need their string reps spoiled.  We maintain a list of all
	     * those Tcl_Obj's (via a little internalrep surgery) so we can spoil
	     * them at that time.
	     */

	    irPtr = TclFetchInternalRep(parentList, &tclListType);
	    irPtr->twoPtrValue.ptr2 = chainPtr;
	    chainPtr = parentList;

	}
    } while (indexCount > 0);

    /*
     * Either we've detected and error condition, and exited the loop with
     * result == TCL_ERROR, or we've successfully reached the last index, and
     * we're ready to store valuePtr.  In either case, we need to clean up our

     * string spoiling list of Tcl_Obj's.
     */

    while (chainPtr) {
	Tcl_Obj *objPtr = chainPtr;
	List *listRepPtr;

	/*
	 * Clear away our internalrep surgery mess.
	 */

	irPtr = TclFetchInternalRep(objPtr, &tclListType);
	listRepPtr = (List *)irPtr->twoPtrValue.ptr1;
	chainPtr = (Tcl_Obj *)irPtr->twoPtrValue.ptr2;

	if (result == TCL_OK) {

	    /*
	     * We're going to store valuePtr, so spoil string reps of all
	     * containing lists.






	     */




	    listRepPtr->refCount++;
	    TclFreeInternalRep(objPtr);
	    ListSetInternalRep(objPtr, listRepPtr);
	    listRepPtr->refCount--;

	    TclInvalidateStringRep(objPtr);
	} else {
	    irPtr->twoPtrValue.ptr2 = NULL;
	}
    }



    if (result != TCL_OK) {
	/*
	 * Error return; message is already in interp. Clean up any excess
	 * memory.
	 */

	if (retValuePtr != listPtr) {
	    Tcl_DecrRefCount(retValuePtr);
	}
	return NULL;
    }

    /*
     * Store valuePtr in proper sublist and return. The TCL_INDEX_NONE is
     * to avoid a compiler warning (not a problem because we checked that
     * we have a proper list - or something convertible to one - above).
     */

    len = TCL_INDEX_NONE;
    TclListObjLengthM(NULL, subListPtr, &len);
    if (valuePtr == NULL) {

	Tcl_ListObjReplace(NULL, subListPtr, index, 1, 0, NULL);
    } else if (index == (size_t)len) {

	Tcl_ListObjAppendElement(NULL, subListPtr, valuePtr);
    } else {

	TclListObjSetElement(NULL, subListPtr, index, valuePtr);
	TclInvalidateStringRep(subListPtr);
    }
    Tcl_IncrRefCount(retValuePtr);
    return retValuePtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TclListObjSetElement --
 *
 *	Set a single element of a list to a specified value.
 *
 *	It is the caller's responsibility to invalidate the string
 *	representation of the 'listPtr'.
 *
 * Value
 *
 * 	TCL_OK
 *
 *	    Success.
 *
 *	TCL_ERROR
 *
 *	    'listPtr' does not refer to a list object and cannot be converted
 *	    to one.  An error message will be left in the interpreter result if
 *	    interp is not NULL.
 *
 *	TCL_ERROR
 *
 *	    An index designates an element outside the range [0..listLength-1],
 *	    where 'listLength' is the count of elements in the list object
 *	    designated by 'listPtr'.  An error message is left in the
 *	    interpreter result.
 *
 * Effect
 *
 *	If 'listPtr' designates a shared object, 'Tcl_Panic' is called.  If
 *	'listPtr' is not already of type 'tclListType', it is converted and the
 *	internal representation is unshared. The 'refCount' of the element at

 *	'index' is decremented and replaced in the list with the 'valuePtr',
 *	whose 'refCount' in turn is incremented.
 *
 *
 *----------------------------------------------------------------------
 */

int
TclListObjSetElement(
    Tcl_Interp *interp,		/* Tcl interpreter; used for error reporting
				 * if not NULL. */
    Tcl_Obj *listPtr,		/* List object in which element should be
				 * stored. */
    size_t index,			/* Index of element to store. */
    Tcl_Obj *valuePtr)		/* Tcl object to store in the designated list
				 * element. */
{
    List *listRepPtr;		/* Internal representation of the list being
				 * modified. */
    Tcl_Obj **elemPtrs;		/* Pointers to elements of the list. */
    size_t elemCount;		/* Number of elements in the list. */

    /*
     * Ensure that the listPtr parameter designates an unshared list.
     */

    if (Tcl_IsShared(listPtr)) {
	Tcl_Panic("%s called with shared object", "TclListObjSetElement");
    }

    ListGetInternalRep(listPtr, listRepPtr);
    if (listRepPtr == NULL) {
	int result;
	size_t length;

	(void) Tcl_GetStringFromObj(listPtr, &length);
	if (length == 0) {
	    if (interp != NULL) {
		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
			"index \"%" TCL_Z_MODIFIER "u\" out of range", index));
		Tcl_SetErrorCode(interp, "TCL", "VALUE", "INDEX",
			"OUTOFRANGE", NULL);
	    }
	    return TCL_ERROR;
	}
	result = SetListFromAny(interp, listPtr);
	if (result != TCL_OK) {
	    return result;
	}
	ListGetInternalRep(listPtr, listRepPtr);
    }

    elemCount = listRepPtr->elemCount;

    /*
     * Ensure that the index is in bounds.
     */

    if (index>=elemCount) {
	if (interp != NULL) {
		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
			"index \"%" TCL_Z_MODIFIER "u\" out of range", index));
	    Tcl_SetErrorCode(interp, "TCL", "VALUE", "INDEX",
		    "OUTOFRANGE", NULL);
	}
	return TCL_ERROR;
    }

    /*
     * If the internal rep is shared, replace it with an unshared copy.

     */



    if (listRepPtr->refCount > 1) {
	Tcl_Obj **dst, **src = listRepPtr->elements;
	List *newPtr = AttemptNewList(NULL, listRepPtr->maxElemCount, NULL);

	if (newPtr == NULL) {
	    newPtr = AttemptNewList(interp, elemCount, NULL);
	    if (newPtr == NULL) {
		return TCL_ERROR;
	    }
	}
	newPtr->refCount++;
	newPtr->elemCount = elemCount;
	newPtr->canonicalFlag = listRepPtr->canonicalFlag;

	dst = newPtr->elements;
	while (elemCount--) {
	    *dst = *src++;
	    Tcl_IncrRefCount(*dst++);
	}

	listRepPtr->refCount--;

	listRepPtr = newPtr;
	ListResetInternalRep(listPtr, listRepPtr);
    }
    elemPtrs = listRepPtr->elements;

    /*
     * Add a reference to the new list element.
     */

    Tcl_IncrRefCount(valuePtr);

    /*
     * Remove a reference from the old list element.

     */

    Tcl_DecrRefCount(elemPtrs[index]);

    /*
     * Stash the new object in the list.
     */

    elemPtrs[index] = valuePtr;

    /*
     * Invalidate outdated internalreps.
     */

    ListGetInternalRep(listPtr, listRepPtr);
    listRepPtr->refCount++;
    TclFreeInternalRep(listPtr);
    ListSetInternalRep(listPtr, listRepPtr);
    listRepPtr->refCount--;

    TclInvalidateStringRep(listPtr);

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * FreeListInternalRep --
 *
 *	Deallocate the storage associated with the internal representation of a
 *	a list object.
 *
 * Effect

 *

 *	Frees listPtr's List* internal representation, if no longer shared.
 *	May decrement the ref counts of element objects, which may free them.
 *
 *----------------------------------------------------------------------
 */

static void
FreeListInternalRep(
    Tcl_Obj *listPtr)		/* List object with internal rep to free. */
{
    List *listRepPtr;

    ListGetInternalRep(listPtr, listRepPtr);
    assert(listRepPtr != NULL);

    if (listRepPtr->refCount-- <= 1) {
	Tcl_Obj **elemPtrs = listRepPtr->elements;
	int i, numElems = listRepPtr->elemCount;

	for (i = 0;  i < numElems;  i++) {
	    Tcl_DecrRefCount(elemPtrs[i]);
	}
	Tcl_Free(listRepPtr);

    }
}

/*
 *----------------------------------------------------------------------
 *
 * DupListInternalRep --
 *
 *	Initialize the internal representation of a list 'Tcl_Obj' to share the
 *	internal representation of an existing list object.
 *
 * Effect

 *

 *	The 'refCount' of the List internal rep is incremented.
 *
 *----------------------------------------------------------------------
 */

static void
DupListInternalRep(
    Tcl_Obj *srcPtr,		/* Object with internal rep to copy. */
    Tcl_Obj *copyPtr)		/* Object with internal rep to set. */
{
    List *listRepPtr;

    ListGetInternalRep(srcPtr, listRepPtr);
    assert(listRepPtr != NULL);
    ListSetInternalRep(copyPtr, listRepPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * SetListFromAny --
 *
 *	Convert any object to a list.
 *
 * Value
 *
 *    TCL_OK
 *
 *	Success.  The internal representation of 'objPtr' is set, and the type
 *	of 'objPtr' is 'tclListType'.

 *
 *    TCL_ERROR
 *
 *	An error occured during conversion. An error message is left in the
 *	interpreter's result if 'interp' is not NULL.
 *
 *
 *----------------------------------------------------------------------
 */

static int
SetListFromAny(
    Tcl_Interp *interp,		/* Used for error reporting if not NULL. */
    Tcl_Obj *objPtr)		/* The object to convert. */
{
    List *listRepPtr;
    Tcl_Obj **elemPtrs;


    /*
     * Dictionaries are a special case; they have a string representation such
     * that *all* valid dictionaries are valid lists. Hence we can convert
     * more directly. Only do this when there's no existing string rep; if
     * there is, it is the string rep that's authoritative (because it could
     * describe duplicate keys).
     */

    if (!TclHasStringRep(objPtr) && TclHasInternalRep(objPtr, &tclDictType)) {
	Tcl_Obj *keyPtr, *valuePtr;
	Tcl_DictSearch search;
	int done;
	size_t size;

	/*
	 * Create the new list representation. Note that we do not need to do
	 * anything with the string representation as the transformation (and
	 * the reverse back to a dictionary) are both order-preserving. Also
	 * note that since we know we've got a valid dictionary (by
	 * representation) we also know that fetching the size of the
	 * dictionary or iterating over it will not fail.
	 */

	Tcl_DictObjSize(NULL, objPtr, &size);


	listRepPtr = AttemptNewList(interp, size > 0 ? 2*size : 1, NULL);
	if (!listRepPtr) {
	    return TCL_ERROR;
	}
	listRepPtr->elemCount = 2 * size;





	/*
	 * Populate the list representation.
	 */

	elemPtrs = listRepPtr->elements;
	Tcl_DictObjFirst(NULL, objPtr, &search, &keyPtr, &valuePtr, &done);
	while (!done) {
	    *elemPtrs++ = keyPtr;
	    *elemPtrs++ = valuePtr;
	    Tcl_IncrRefCount(keyPtr);
	    Tcl_IncrRefCount(valuePtr);
	    Tcl_DictObjNext(&search, &keyPtr, &valuePtr, &done);
	}




























    } else {
	size_t estCount, length;
	const char *limit, *nextElem = Tcl_GetStringFromObj(objPtr, &length);

	/*
	 * Allocate enough space to hold a (Tcl_Obj *) for each
	 * (possible) list element.
	 */

	estCount = TclMaxListLength(nextElem, length, &limit);
	estCount += (estCount == 0);	/* Smallest list struct holds 1
					 * element. */

	listRepPtr = AttemptNewList(interp, estCount, NULL);
	if (listRepPtr == NULL) {
	    return TCL_ERROR;
	}




	elemPtrs = listRepPtr->elements;

	/*
	 * Each iteration, parse and store a list element.
	 */

	while (nextElem < limit) {
	    const char *elemStart;
	    char *check;
	    size_t elemSize;
	    int literal;

	    if (TCL_OK != TclFindElement(interp, nextElem, limit - nextElem,
		    &elemStart, &nextElem, &elemSize, &literal)) {
	    fail:
		while (--elemPtrs >= listRepPtr->elements) {
		    Tcl_DecrRefCount(*elemPtrs);
		}
		Tcl_Free(listRepPtr);
		return TCL_ERROR;
	    }
	    if (elemStart == limit) {
		break;
	    }

	    TclNewObj(*elemPtrs);
	    TclInvalidateStringRep(*elemPtrs);
	    check = Tcl_InitStringRep(*elemPtrs, literal ? elemStart : NULL,
		    elemSize);
	    if (elemSize && check == NULL) {
		if (interp) {
		    Tcl_SetObjResult(interp, Tcl_NewStringObj(
			    "cannot construct list, out of memory", -1));
		    Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL);
		}
		goto fail;
	    }
	    if (!literal) {
		Tcl_InitStringRep(*elemPtrs, NULL,
			TclCopyAndCollapse(elemSize, elemStart, check));
	    }

	    Tcl_IncrRefCount(*elemPtrs++);/* Since list now holds ref to it. */
	}


 	listRepPtr->elemCount = elemPtrs - listRepPtr->elements;
    }



    /*
     * Store the new internalRep. We do this as late
     * as possible to allow the conversion code, in particular
     * Tcl_GetStringFromObj, to use the old internalRep.
     */







    ListSetInternalRep(objPtr, listRepPtr);




    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * UpdateStringOfList --
 *
 *	Update the string representation for a list object.
 *
 *	Any previously-exising string representation is not invalidated, so
 *	storage is lost if this has not been taken care of.
 *
 * Effect

 *
 *	The string representation of 'listPtr' is set to the resulting string.

 *	This string will be empty if the list has no elements. It is assumed
 *	that the list internal representation is not NULL.

 *
 *----------------------------------------------------------------------
 */

static void
UpdateStringOfList(
    Tcl_Obj *listPtr)		/* List object with string rep to update. */
{
#   define LOCAL_SIZE 64
    char localFlags[LOCAL_SIZE], *flagPtr = NULL;
    size_t numElems, i, length, bytesNeeded = 0;
    const char *elem, *start;
    char *dst;
    Tcl_Obj **elemPtrs;
    List *listRepPtr;

    ListGetInternalRep(listPtr, listRepPtr);

    assert(listRepPtr != NULL);

    numElems = listRepPtr->elemCount;

    /*
     * Mark the list as being canonical; although it will now have a string
     * rep, it is one we derived through proper "canonical" quoting and so
     * it's known to be free from nasties relating to [concat] and [eval].








     */




    listRepPtr->canonicalFlag = 1;

    /*
     * Handle empty list case first, so rest of the routine is simpler.
     */

    if (numElems == 0) {
	Tcl_InitStringRep(listPtr, NULL, 0);
	return;
    }

    /*
     * Pass 1: estimate space, gather flags.
     */

    if (numElems <= LOCAL_SIZE) {
	flagPtr = localFlags;
    } else {
	/*
	 * We know numElems <= LIST_MAX, so this is safe.
	 */

	flagPtr = (char *)Tcl_Alloc(numElems);
    }
    elemPtrs = listRepPtr->elements;
    for (i = 0; i < numElems; i++) {
	flagPtr[i] = (i ? TCL_DONT_QUOTE_HASH : 0);
	elem = Tcl_GetStringFromObj(elemPtrs[i], &length);
	bytesNeeded += TclScanElement(elem, length, flagPtr+i);



    }
    bytesNeeded += numElems - 1;

    /*
     * Pass 2: copy into string rep buffer.
     */

    start = dst = Tcl_InitStringRep(listPtr, NULL, bytesNeeded);
    TclOOM(dst, bytesNeeded);
    for (i = 0; i < numElems; i++) {
	flagPtr[i] |= (i ? TCL_DONT_QUOTE_HASH : 0);
	elem = Tcl_GetStringFromObj(elemPtrs[i], &length);
	dst += TclConvertElement(elem, length, dst, flagPtr[i]);
	*dst++ = ' ';
    }

    /* Set the string length to what was actually written, the safe choice */
    (void) Tcl_InitStringRep(listPtr, NULL, dst - 1 - start);

    if (flagPtr != localFlags) {
	Tcl_Free(flagPtr);
    }
}

























































/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */





<
<
|





>

|


<
>
>
>


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





|
<
<
<
<
<
<











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

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

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




|

|
|
<
|
|

>
|
>

<
|
|
>
>

|
<
|
|



<
|
|
|

|

|
>

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






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


|
<
|
<
<
<
<
<
<

|

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



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



<
|
|

|
|
>

|

|

|
<
<

<
<
|

<

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







>
|
<
>
>

<
>
>

>
|
|
|
|

|
<
|
|









|









|


|
|

|


|


<
<
<
|
<
|
<
<
<

<
<
|






|

>
>
>
|
|
|
|

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








|






|
|

|


|


<
<
<
|
<
|
<
<
<

<
<
<
|






|







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






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



<



|


<
<




<
<
<
<
<
<
<



|
>


|
>
>
|
|

>
>









|
|
|

|
|
|
|
|
<

|
|
<







|


|
<

|
<
|




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








|




>
>










|
|
|

<
|
|

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

<
<
<
<
|
<

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







|
|
<

<
|
>
|
|
>
>
>

|
|
|
|
|
|

<
<
<
<
<
|
<
>
|








|

|




|

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








|
>

|
<
|
<
<
<
|
<
<
|

|
<
|
<
>
|
|
|
<



<



|
|

|


|



<
<
<
<
|








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







|
>
>
>

|
<
|
<
<
|
<
<
|
|
|

|
<
<
|
>
|
|
|



<



|
|

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







|
|
|
|
>

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

|
<
|



<


|
|
|
|

>
|

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


|










|
>
>

|
<
|
<
|
>
>
|
<

<
|
|
<







|
|
|

|

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

|
<








|
>
>
>

|
|
|
<
>
|
|

<
<
<
|
|
|
>

>
>
|
<
|
|

|
<
<
|
<
|
<
>
|
|



<



|
|
|
|
|
<

|
>
>
>
>
>
>
>
|
<
|

|



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



|
|

|
|
|
|
>
|
|
>
>


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

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

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

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

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

>
>
>
|
<
|
>
|
>
>

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

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

|

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

<
<
>
>

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

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

>
>
>
>
>
>
|
<
<
<

>
>
|

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








|

|
|
|
|

|
|
<
<

|
>
>
>
>
>



<



|
|

<
|

>
|




>
|

<
|
<
|
>



<
|














|


|
|
>

<
|


<
|
<
<
|
<

|





|

|
|

|
>
>
>

<
<
>
|

>
>
>
>
|
|

<


<



|
|



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








|
|
|


<
|
<
<


>
|



|






|
>





|

<
|
<
<
|

|




|







|



|
>
>
|

|
>

<
>
|
>
>
>
>



<



|
|
|

|

|
|

<







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





<
|

>
|





|


|










|
|
>
|

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

|
|
|

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

<
<
<
<
<
<
<


<



|
|


|

|
|
|
|
>
>




|



|
|

|





|



|



|






|
<

>
>
>
>
>
>
>







|






|
|











|


|





|


|
>
|
|
>
>
>
|
>







|
|
|



|
|
|

|

|
|






|
|
|


|
|

|

|
|
|



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


<
<
|
>






|
>
|

|
|
|
<

<
<
<
|
<
|
<

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







|
|





|
|
|


|
|
|
>
|
|
>
|

>
|
|

|
|







|

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

|
<
|
|
<
>
|
|

<


<




|

|
|


|
<
|
|

<
|
<

|



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

<
|
<
<











|
>

>

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

<
<
<
|
|


|
>

|

<
<
<
<
<
|

<
<
<
|
<
<
<
<
<
|
<









|
|

|
>

>





<


|

|

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








|


|
>

>
|



<


|
|

|
|
<
|
<







|

|
<
|
<
|
<
>

|
<
|
<
|



<





<

>













|











>
>
|
|


|
>
>
>

>
|
|
<

|








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

|










>
|
|


>
>
>
>
|

<
|
<




|




|
|


|











|
<
<
<
<










>
|

>
>







>
>
>
>
>
>
|
>
>
>
>








|
<
|
|

|
>

|
>
|
|
>



<


|



|



|

<
|
|

|





>
>
>
>
>
>
>
>

>
>
>
|
<

<
|
<


|



<
|
<




<
|
<
<


<




>
>
>







|









|





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







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

2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335

2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347


2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361


2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376

2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413


2414

2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440


2441
2442
2443
2444
2445
2446
2447
2448
2449

2450
2451

2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473



2474
2475
2476
2477
2478
2479


2480
2481
2482
2483
2484
2485
2486
2487



2488
2489
2490
2491
2492

2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528


2529
2530
2531
2532
2533
2534
2535
2536
2537
2538

2539
2540
2541
2542
2543
2544

2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555

2556

2557
2558
2559
2560
2561

2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583

2584
2585
2586

2587


2588

2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605


2606
2607
2608
2609
2610
2611
2612
2613
2614
2615

2616
2617

2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665

2666


2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689

2690


2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717

2718
2719
2720
2721
2722
2723
2724
2725
2726

2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738

2739
2740
2741
2742
2743
2744
2745
2746

2747

2748
2749

2750
2751
2752
2753

2754
2755
2756
2757
2758
2759

2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787

2788
2789
2790
2791
2792


2793


2794
2795
2796
2797
2798
2799



2800

2801
2802
2803
2804
2805
2806
2807
2808







2809
2810

2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858

2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971


2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986

2987



2988

2989

2990
2991

2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005




3006



3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053



3054

3055





3056
3057




3058
3059
3060
3061
3062
3063

3064
3065

3066
3067
3068
3069

3070
3071

3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082

3083
3084
3085

3086

3087
3088
3089
3090
3091




3092








3093
3094



3095



3096
3097

3098


3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117

3118









3119





3120
3121
3122




3123



3124
3125
3126
3127
3128
3129
3130
3131
3132





3133
3134



3135





3136

3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157

3158
3159
3160
3161
3162
3163


3164
3165
3166
3167
3168

3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191

3192
3193
3194
3195
3196
3197
3198

3199

3200
3201
3202
3203
3204
3205
3206
3207
3208
3209

3210

3211

3212
3213
3214

3215

3216
3217
3218
3219

3220
3221
3222
3223
3224

3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265

3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326

3327

3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353




3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395

3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409

3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421

3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443

3444

3445

3446
3447
3448
3449
3450
3451

3452

3453
3454
3455
3456

3457


3458
3459

3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
/*
 * tclListObj.c --
 *
 *	This file contains functions that implement the Tcl list object type.
 *


 * Copyright © 2022 Ashok P. Nadkarni.  All rights reserved.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include <assert.h>
#include "tclInt.h"
#include "tclArithSeries.h"

/*

 * TODO - memmove is fast. Measure at what size we should prefer memmove
 * (for unshared objects only) in lieu of range operations. On the other
 * hand, more cache dirtied?
 */

/*
 * Macros for validation and bug checking.
 */

/*
 * Control whether asserts are enabled. Always enable in debug builds. In non-debug
 * builds, can be set with cdebug="-DENABLE_LIST_ASSERTS" on the nmake command line.
 */
#ifdef ENABLE_LIST_ASSERTS
# ifdef NDEBUG
#  undef NDEBUG /* Activate assert() macro */
# endif
#else
# ifndef NDEBUG
#  define ENABLE_LIST_ASSERTS /* Always activate list asserts in debug mode */
# endif
#endif

#ifdef ENABLE_LIST_ASSERTS

#define LIST_ASSERT(cond_) assert(cond_) /* TODO - is there a Tcl-specific one? */
/*
 * LIST_INDEX_ASSERT is to catch errors with negative indices and counts
 * being passed AFTER validation. On Tcl9 length types are unsigned hence
 * the checks against LIST_MAX. On Tcl8 length types are signed hence the
 * also checks against 0.
 */
#define LIST_INDEX_ASSERT(idxarg_)                                 \
    do {                                                           \
	ListSizeT idx_ = (idxarg_); /* To guard against ++ etc. */ \
	LIST_ASSERT(idx_ != TCL_INDEX_NONE && idx_ < LIST_MAX);                 \
    } while (0)
/* Ditto for counts except upper limit is different */
#define LIST_COUNT_ASSERT(countarg_)                                   \
    do {                                                               \
	ListSizeT count_ = (countarg_); /* To guard against ++ etc. */ \
	LIST_ASSERT(count_ != TCL_INDEX_NONE && count_ <= LIST_MAX);                \
    } while (0)

#else

#define LIST_ASSERT(cond_) ((void) 0)
#define LIST_INDEX_ASSERT(idx_) ((void) 0)
#define LIST_COUNT_ASSERT(count_) ((void) 0)

#endif

/* Checks for when caller should have already converted to internal list type */
#define LIST_ASSERT_TYPE(listObj_) \
    LIST_ASSERT((listObj_)->typePtr == &tclListType);


/*
 * If ENABLE_LIST_INVARIANTS is enabled (-DENABLE_LIST_INVARIANTS from the
 * command line), the entire list internal representation is checked for
 * inconsistencies. This has a non-trivial cost so has to be separately
 * enabled and not part of assertions checking. However, the test suite does
 * invoke ListRepValidate directly even without ENABLE_LIST_INVARIANTS.
 */
#ifdef ENABLE_LIST_INVARIANTS
#define LISTREP_CHECK(listRepPtr_) ListRepValidate(listRepPtr_, __FILE__, __LINE__)
#else
#define LISTREP_CHECK(listRepPtr_) (void) 0
#endif

/*
 * Flags used for controlling behavior of allocation of list
 * internal representations.
 *
 * If the LISTREP_PANIC_ON_FAIL bit is set, the function will panic if
 * list is too large or memory cannot be allocated. Without the flag
 * a NULL pointer is returned.
 *
 * The LISTREP_SPACE_FAVOR_NONE, LISTREP_SPACE_FAVOR_FRONT,
 * LISTREP_SPACE_FAVOR_BACK, LISTREP_SPACE_ONLY_BACK flags are used to
 * control additional space when allocating.
 * - If none of these flags is present, the exact space requested is
 *   allocated, nothing more.
 * - Otherwise, if only LISTREP_FAVOR_FRONT is present, extra space is
 *   allocated with more towards the front.
 * - Conversely, if only LISTREP_FAVOR_BACK is present extra space is allocated
 *   with more to the back.
 * - If both flags are present (LISTREP_SPACE_FAVOR_NONE), the extra space
 *   is equally apportioned.
 * - Finally if LISTREP_SPACE_ONLY_BACK is present, ALL extra space is at
 *   the back.
 */
#define LISTREP_PANIC_ON_FAIL         0x00000001
#define LISTREP_SPACE_FAVOR_FRONT     0x00000002
#define LISTREP_SPACE_FAVOR_BACK      0x00000004
#define LISTREP_SPACE_ONLY_BACK       0x00000008
#define LISTREP_SPACE_FAVOR_NONE \
    (LISTREP_SPACE_FAVOR_FRONT | LISTREP_SPACE_FAVOR_BACK)
#define LISTREP_SPACE_FLAGS                               \
    (LISTREP_SPACE_FAVOR_FRONT | LISTREP_SPACE_FAVOR_BACK \
     | LISTREP_SPACE_ONLY_BACK)

/*
 * Prototypes for non-inline static functions defined later in this file:
 */
static int	MemoryAllocationError(Tcl_Interp *, size_t size);
static int	ListLimitExceededError(Tcl_Interp *);
static ListStore *ListStoreNew(ListSizeT objc, Tcl_Obj *const objv[], int flags);
static int	ListRepInit(ListSizeT objc, Tcl_Obj *const objv[], int flags, ListRep *);
static int	ListRepInitAttempt(Tcl_Interp *,
		    ListSizeT objc,
		    Tcl_Obj *const objv[],
		    ListRep *);
static void	ListRepClone(ListRep *fromRepPtr, ListRep *toRepPtr, int flags);
static void	ListRepUnsharedFreeUnreferenced(const ListRep *repPtr);
static int	TclListObjGetRep(Tcl_Interp *, Tcl_Obj *listPtr, ListRep *repPtr);
static void	ListRepRange(ListRep *srcRepPtr,
		    ListSizeT rangeStart,
		    ListSizeT rangeEnd,
		    int preserveSrcRep,
		    ListRep *rangeRepPtr);
static ListStore *ListStoreReallocate(ListStore *storePtr, ListSizeT numSlots);
static void	ListRepValidate(const ListRep *repPtr, const char *file,
		    int lineNum);
static void	DupListInternalRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr);
static void	FreeListInternalRep(Tcl_Obj *listPtr);
static int	SetListFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
static void	UpdateStringOfList(Tcl_Obj *listPtr);

/*
 * The structure below defines the list Tcl object type by means of functions
 * that can be invoked by generic object code.
 *
 * The internal representation of a list object is ListRep defined in tcl.h.






 */

const Tcl_ObjType tclListType = {
    "list",			/* name */
    FreeListInternalRep,	/* freeIntRepProc */
    DupListInternalRep,		/* dupIntRepProc */
    UpdateStringOfList,		/* updateStringProc */
    SetListFromAny		/* setFromAnyProc */
};

/* Macros to manipulate the List internal rep */
#define ListRepIncrRefs(repPtr_)            \
    do {                                    \
	(repPtr_)->storePtr->refCount++;    \
	if ((repPtr_)->spanPtr)             \
	    (repPtr_)->spanPtr->refCount++; \
    } while (0)

/* Returns number of free unused slots at the back of the ListRep's ListStore */
#define ListRepNumFreeTail(repPtr_) \
    ((repPtr_)->storePtr->numAllocated \
     - ((repPtr_)->storePtr->firstUsed + (repPtr_)->storePtr->numUsed))

/* Returns number of free unused slots at the front of the ListRep's ListStore */
#define ListRepNumFreeHead(repPtr_) ((repPtr_)->storePtr->firstUsed)

/* Returns a pointer to the slot corresponding to list index listIdx_ */
#define ListRepSlotPtr(repPtr_, listIdx_) \
    (&(repPtr_)->storePtr->slots[ListRepStart(repPtr_) + (listIdx_)])

/*
 * Macros to replace the internal representation in a Tcl_Obj. There are
 * subtle differences in each so make sure to use the right one to avoid
 * memory leaks, access to freed memory and the like.
 *
 * ListObjStompRep - assumes the Tcl_Obj internal representation can be
 * overwritten AND that the passed ListRep already has reference counts that
 * include the reference from the Tcl_Obj. Basically just copies the pointers
 * and sets the internal Tcl_Obj type to list
 *
 * ListObjOverwriteRep - like ListObjOverwriteRep but additionally
 * increments reference counts on the passed ListRep. Generally used when
 * the string representation of the Tcl_Obj is not to be modified.
 *
 * ListObjReplaceRepAndInvalidate - Like ListObjOverwriteRep but additionally
 * assumes the Tcl_Obj internal rep is valid (and possibly even same as
 * passed ListRep) and frees it first. Additionally invalidates the string
 * representation. Generally used when modifying a Tcl_Obj value.
 */
#define ListObjStompRep(objPtr_, repPtr_)                              \
    do {                                                               \
	(objPtr_)->internalRep.twoPtrValue.ptr1 = (repPtr_)->storePtr; \
	(objPtr_)->internalRep.twoPtrValue.ptr2 = (repPtr_)->spanPtr;  \
	(objPtr_)->typePtr = &tclListType;                             \
    } while (0)

#define ListObjOverwriteRep(objPtr_, repPtr_) \
    do {                                      \
	ListRepIncrRefs(repPtr_);             \
	ListObjStompRep(objPtr_, repPtr_);    \
    } while (0)

#define ListObjReplaceRepAndInvalidate(objPtr_, repPtr_)           \
    do {                                                           \
	/* Note order important, don't use ListObjOverwriteRep! */ \
	ListRepIncrRefs(repPtr_);                                  \
	TclFreeInternalRep(objPtr_);                               \
	TclInvalidateStringRep(objPtr_);                           \
	ListObjStompRep(objPtr_, repPtr_);                         \
    } while (0)

/*
 *------------------------------------------------------------------------
 *
 * ListSpanNew --
 *
 *    Allocates and initializes memory for a new ListSpan. The reference
 *    count on the returned struct is 0.
 *
 * Results:
 *    Non-NULL pointer to the allocated ListSpan.
 *
 * Side effects:
 *    The function will panic on memory allocation failure.
 *
 *------------------------------------------------------------------------
 */
static inline ListSpan *
ListSpanNew(
    ListSizeT firstSlot, /* Starting slot index of the span */
    ListSizeT numSlots)  /* Number of slots covered by the span */
{
    ListSpan *spanPtr = (ListSpan *) Tcl_Alloc(sizeof(*spanPtr));
    spanPtr->refCount = 0;
    spanPtr->spanStart = firstSlot;
    spanPtr->spanLength = numSlots;
    return spanPtr;
}

/*
 *------------------------------------------------------------------------
 *
 * ListSpanDecrRefs --
 *
 *   Decrements the reference count on a span, freeing the memory if
 *   it drops to zero or less.
 *
 * Results:
 *   None.
 *
 * Side effects:
 *   The memory may be freed.
 *
 *------------------------------------------------------------------------
 */
static inline void
ListSpanDecrRefs(ListSpan *spanPtr)
{
    if (spanPtr->refCount <= 1) {
	Tcl_Free(spanPtr);
    } else {
	spanPtr->refCount -= 1;
    }
}

/*
 *------------------------------------------------------------------------
 *
 * ListSpanMerited --
 *
 *    Creation of a new list may sometimes be done as a span on existing
 *    storage instead of allocating new. The tradeoff is that if the
 *    original list is released, the new span-based list may hold on to
 *    more memory than desired. This function implements heuristics for
 *    deciding which option is better.
 *
 * Results:
 *    Returns non-0 if a span-based list is likely to be more optimal
 *    and 0 if not.
 *
 * Side effects:
 *    None.
 *
 *------------------------------------------------------------------------
 */
static inline int
ListSpanMerited(
    ListSizeT length,                 /* Length of the proposed span */
    ListSizeT usedStorageLength,      /* Number of slots currently in used */
    ListSizeT allocatedStorageLength) /* Length of the currently allocation */
{
    /*
     TODO
     - heuristics thresholds need to be determined
     - currently, information about the sharing (ref count) of existing
       storage is not passed. Perhaps it should be. For example if the
       existing storage has a "large" ref count, then it might make sense
       to do even a small span.
     */

    if (length < LIST_SPAN_THRESHOLD) {
	return 0;/* No span for small lists */
    }
    if (length < (allocatedStorageLength / 2 - allocatedStorageLength / 8)) {
	return 0; /* No span if less than 3/8 of allocation */
    }
    if (length < usedStorageLength / 2) {
	return 0; /* No span if less than half current storage */
    }

    return 1;
}

/*
 *------------------------------------------------------------------------
 *
 * ListStoreUpSize --
 *
 *    For reasons of efficiency, extra space is allocated for a ListStore
 *    compared to what was requested. This function calculates how many
 *    slots should actually be allocated for a given request size.
 *
 * Results:
 *    Number of slots to allocate.
 *
 * Side effects:
 *    None.
 *
 *------------------------------------------------------------------------
 */
static inline ListSizeT
ListStoreUpSize(ListSizeT numSlotsRequested) {
    /* TODO -how much extra? May be double only for smaller requests? */
    return numSlotsRequested < (LIST_MAX / 2) ? 2 * numSlotsRequested
						 : LIST_MAX;
}

/*
 *------------------------------------------------------------------------
 *
 * ListRepFreeUnreferenced --
 *
 *    Inline wrapper for ListRepUnsharedFreeUnreferenced that does quick checks
 *    before calling it.
 *
 *    IMPORTANT: this function must not be called on an internal
 *    representation of a Tcl_Obj that is itself shared.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    See comments for ListRepUnsharedFreeUnreferenced.
 *
 *------------------------------------------------------------------------
 */
static inline void
ListRepFreeUnreferenced(const ListRep *repPtr)
{
    if (! ListRepIsShared(repPtr) && repPtr->spanPtr) {
        /* T:listrep-1.5.1 */
	ListRepUnsharedFreeUnreferenced(repPtr);
    }
}

/*
 *------------------------------------------------------------------------
 *
 * ObjArrayIncrRefs --
 *
 *    Increments the reference counts for Tcl_Obj's in a subarray.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    As above.
 *
 *------------------------------------------------------------------------
 */
static inline void
ObjArrayIncrRefs(
    Tcl_Obj * const *objv,  /* Pointer to the array */
    ListSizeT startIdx,     /* Starting index of subarray within objv */
    ListSizeT count)        /* Number of elements in the subarray */
{
    Tcl_Obj * const *end;
    LIST_INDEX_ASSERT(startIdx);
    LIST_COUNT_ASSERT(count);
    objv += startIdx;
    end = objv + count;
    while (objv < end) {
	Tcl_IncrRefCount(*objv);
	++objv;
    }
}

/*
 *------------------------------------------------------------------------
 *
 * ObjArrayDecrRefs --
 *
 *    Decrements the reference counts for Tcl_Obj's in a subarray.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    As above.
 *
 *------------------------------------------------------------------------
 */
static inline void
ObjArrayDecrRefs(
    Tcl_Obj * const *objv, /* Pointer to the array */
    ListSizeT startIdx,    /* Starting index of subarray within objv */
    ListSizeT count)       /* Number of elements in the subarray */
{
    Tcl_Obj * const *end;
    LIST_INDEX_ASSERT(startIdx);
    LIST_COUNT_ASSERT(count);
    objv += startIdx;
    end = objv + count;
    while (objv < end) {
	Tcl_DecrRefCount(*objv);
	++objv;
    }
}

/*
 *------------------------------------------------------------------------
 *
 * ObjArrayCopy --
 *
 *    Copies an array of Tcl_Obj* pointers.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Reference counts on copied Tcl_Obj's are incremented.
 *
 *------------------------------------------------------------------------
 */
static inline void
ObjArrayCopy(
    Tcl_Obj **to,          /* Destination */
    ListSizeT count,       /* Number of pointers to copy */
    Tcl_Obj *const from[]) /* Source array of Tcl_Obj* */
{
    Tcl_Obj **end;
    LIST_COUNT_ASSERT(count);
    end = to + count;
    /* TODO - would memmove followed by separate IncrRef loop be faster? */
    while (to < end) {
	Tcl_IncrRefCount(*from);
	*to++ = *from++;
    }
}

/*
 *------------------------------------------------------------------------
 *
 * MemoryAllocationError --
 *
 *    Generates a memory allocation failure error.
 *
 * Results:
 *    Always TCL_ERROR.
 *
 * Side effects:
 *    Error message and code are stored in the interpreter if not NULL.
 *
 *------------------------------------------------------------------------
 */
static int
MemoryAllocationError(
    Tcl_Interp *interp, /* Interpreter for error message. May be NULL */
    ListSizeT size)        /* Size of attempted allocation that failed */
{
    if (interp != NULL) {
	Tcl_SetObjResult(
	    interp,
	    Tcl_ObjPrintf(
		"list construction failed: unable to alloc %" TCL_LL_MODIFIER
		"u bytes",
		(Tcl_WideInt)size));
	Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL);
    }
    return TCL_ERROR;
}

/*
 *------------------------------------------------------------------------
 *
 * ListLimitExceeded --
 *
 *    Generates an error for exceeding maximum list size.
 *
 * Results:
 *    Always TCL_ERROR.
 *
 * Side effects:
 *    Error message and code are stored in the interpreter if not NULL.
 *
 *------------------------------------------------------------------------
 */
static int
ListLimitExceededError(Tcl_Interp *interp)
{
    if (interp != NULL) {
	Tcl_SetObjResult(
	    interp,
	    Tcl_NewStringObj("max length of a Tcl list exceeded", -1));
	Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL);
    }
    return TCL_ERROR;
}

/*
 *------------------------------------------------------------------------
 *
 * ListRepUnsharedShiftDown --
 *
 *    Shifts the "in-use" contents in the ListStore for a ListRep down
 *    by the given number of slots. The ListStore must be unshared and
 *    the free space at the front of the storage area must be big enough.
 *    It is the caller's responsibility to check.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The contents of the ListRep's ListStore area are shifted down in the
 *    storage area. The ListRep's ListSpan is updated accordingly.
 *
 *------------------------------------------------------------------------
 */
static inline void
ListRepUnsharedShiftDown(ListRep *repPtr, ListSizeT shiftCount)
{
    ListStore *storePtr;

    LISTREP_CHECK(repPtr);
    LIST_ASSERT(!ListRepIsShared(repPtr));

    storePtr = repPtr->storePtr;

    LIST_COUNT_ASSERT(shiftCount);
    LIST_ASSERT(storePtr->firstUsed >= shiftCount);

    memmove(&storePtr->slots[storePtr->firstUsed - shiftCount],
	    &storePtr->slots[storePtr->firstUsed],
	    storePtr->numUsed * sizeof(Tcl_Obj *));
    storePtr->firstUsed -= shiftCount;
    if (repPtr->spanPtr) {
	repPtr->spanPtr->spanStart -= shiftCount;
	LIST_ASSERT(repPtr->spanPtr->spanLength == storePtr->numUsed);
    } else {
	/*
	 * If there was no span, firstUsed must have been 0 (Invariant)
	 * AND shiftCount must have been 0 (<= firstUsed on call)
	 * In other words, this would have been a no-op
	 */

	LIST_ASSERT(storePtr->firstUsed == 0);
	LIST_ASSERT(shiftCount == 0);
    }

    LISTREP_CHECK(repPtr);
}

/*
 *------------------------------------------------------------------------
 *
 * ListRepUnsharedShiftUp --
 *
 *    Shifts the "in-use" contents in the ListStore for a ListRep up
 *    by the given number of slots. The ListStore must be unshared and
 *    the free space at the back of the storage area must be big enough.
 *    It is the caller's responsibility to check.
 *    TODO - this function is not currently used.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The contents of the ListRep's ListStore area are shifted up in the
 *    storage area. The ListRep's ListSpan is updated accordingly.
 *
 *------------------------------------------------------------------------
 */
#if 0
static inline void
ListRepUnsharedShiftUp(ListRep *repPtr, ListSizeT shiftCount)
{
    ListStore *storePtr;

    LISTREP_CHECK(repPtr);
    LIST_ASSERT(!ListRepIsShared(repPtr));
    LIST_COUNT_ASSERT(shiftCount);

    storePtr = repPtr->storePtr;
    LIST_ASSERT((storePtr->firstUsed + storePtr->numUsed + shiftCount)
		<= storePtr->numAllocated);

    memmove(&storePtr->slots[storePtr->firstUsed + shiftCount],
	    &storePtr->slots[storePtr->firstUsed],
	    storePtr->numUsed * sizeof(Tcl_Obj *));
    storePtr->firstUsed += shiftCount;
    if (repPtr->spanPtr) {
	repPtr->spanPtr->spanStart += shiftCount;
    } else {
	/* No span means entire original list is span */
	/* Should have been zero before shift - Invariant TBD */
	LIST_ASSERT(storePtr->firstUsed == shiftCount);
	repPtr->spanPtr = ListSpanNew(shiftCount, storePtr->numUsed);
    }

    LISTREP_CHECK(repPtr);
}
#endif

/*
 *------------------------------------------------------------------------
 *
 * ListRepValidate --
 *
 *	Checks all invariants for a ListRep and panics on failure.
 *	Note this is independent of NDEBUG, assert etc.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Panics if any invariant is not met.
 *
 *------------------------------------------------------------------------
 */
static void
ListRepValidate(const ListRep *repPtr, const char *file, int lineNum)
{
    ListStore *storePtr = repPtr->storePtr;
    const char *condition;

    (void)storePtr; /* To stop gcc from whining about unused vars */

#define INVARIANT(cond_)        \
    do {                        \
	if (!(cond_)) {         \
	    condition = #cond_; \
	    goto failure;       \
	}                       \
    } while (0)

    /* Separate each condition so line number gives exact reason for failure */
    INVARIANT(storePtr != NULL);
    INVARIANT(storePtr->numAllocated <= LIST_MAX);
    INVARIANT(storePtr->firstUsed < storePtr->numAllocated);
    INVARIANT(storePtr->numUsed <= storePtr->numAllocated);
    INVARIANT(storePtr->firstUsed <= (storePtr->numAllocated - storePtr->numUsed));

    if (! ListRepIsShared(repPtr)) {
	/*
	 * If this is the only reference and there is no span, then store
	 * occupancy must begin at 0
	 */
	INVARIANT(repPtr->spanPtr || repPtr->storePtr->firstUsed == 0);
    }

    INVARIANT(ListRepStart(repPtr) >= storePtr->firstUsed);
    INVARIANT(ListRepLength(repPtr) <= storePtr->numUsed);
    INVARIANT(ListRepStart(repPtr) <= (storePtr->firstUsed + storePtr->numUsed - ListRepLength(repPtr)));

#undef INVARIANT

    return;

failure:
    Tcl_Panic("List internal failure in %s line %d. Condition: %s",
	      file,
	      lineNum,
	      condition);
}

/*
 *------------------------------------------------------------------------
 *
 * TclListObjValidate --
 *
 *    Wrapper around ListRepValidate. Primarily used from test suite.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Will panic if internal structure is not consistent or if object
 *    cannot be converted to a list object.
 *
 *------------------------------------------------------------------------
 */
void
TclListObjValidate(Tcl_Interp *interp, Tcl_Obj *listObj)
{
    ListRep listRep;
    if (TclListObjGetRep(interp, listObj, &listRep) != TCL_OK) {
	Tcl_Panic("Object passed to TclListObjValidate cannot be converted to "
		  "a list object.");
    }
    ListRepValidate(&listRep, __FILE__, __LINE__);
}

/*
 *----------------------------------------------------------------------
 *
 * ListStoreNew --
 *
 *	Allocates a new ListStore with space for at least objc elements. objc
 *	must be > 0.  If objv!=NULL, initializes with the first objc values

 *	in that array.  If objv==NULL, initalize 0 elements, with space
 *	to add objc more.
 *
 *      Normally the function allocates the exact space requested unless
 *      the flags arguments has any LISTREP_SPACE_*
 *      bits set. See the comments for those #defines.
 *

 * Results:
 *      On success, a pointer to the allocated ListStore is returned.
 *      On allocation failure, panics if LISTREP_PANIC_ON_FAIL is set in
 *      flags; otherwise returns NULL.
 *
 * Side effects:

 *	The ref counts of the elements in objv are incremented on success
 *	since the returned ListStore references them.
 *
 *----------------------------------------------------------------------
 */

static ListStore *
ListStoreNew(
    ListSizeT objc,
    Tcl_Obj *const objv[],
    int flags)
{
    ListStore *storePtr;
    ListSizeT capacity;

    /*
     * First check to see if we'd overflow and try to allocate an object
     * larger than our memory allocator allows.
     */
    if (objc > LIST_MAX) {
	if (flags & LISTREP_PANIC_ON_FAIL) {
	    Tcl_Panic("max length of a Tcl list exceeded");
	}
	return NULL;
    }

    if (flags & LISTREP_SPACE_FLAGS) {
	capacity = ListStoreUpSize(objc);
    } else {
	capacity = objc;
    }

    storePtr = (ListStore *)Tcl_AttemptAlloc(LIST_SIZE(capacity));
    if (storePtr == NULL && capacity != objc) {
	capacity = objc; /* Try allocating exact size */
	storePtr = (ListStore *)Tcl_AttemptAlloc(LIST_SIZE(capacity));
    }
    if (storePtr == NULL) {
	if (flags & LISTREP_PANIC_ON_FAIL) {
	    Tcl_Panic("list creation failed: unable to alloc %" TCL_Z_MODIFIER "u bytes",
		    LIST_SIZE(objc));
	}
	return NULL;
    }


    storePtr->refCount = 0;
    storePtr->flags = 0;
    storePtr->numAllocated = capacity;
    if (capacity == objc) {
	storePtr->firstUsed = 0;
    } else {
	ListSizeT extra = capacity - objc;
	int spaceFlags = flags & LISTREP_SPACE_FLAGS;
	if (spaceFlags == LISTREP_SPACE_ONLY_BACK) {
	    storePtr->firstUsed = 0;
	} else if (spaceFlags == LISTREP_SPACE_FAVOR_FRONT) {
	    /* Leave more space in the front */
	    storePtr->firstUsed =
		extra - (extra / 4); /* NOT same as 3*extra/4 */
	} else if (spaceFlags == LISTREP_SPACE_FAVOR_BACK) {
	    /* Leave more space in the back */
	    storePtr->firstUsed = extra / 4;
	} else {
	    /* Apportion equally */
	    storePtr->firstUsed = extra / 2;
	}
    }

    if (objv) {
	storePtr->numUsed = objc;

	ObjArrayCopy(&storePtr->slots[storePtr->firstUsed], objc, objv);






    } else {
	storePtr->numUsed = 0;
    }

    return storePtr;
}

/*
 *------------------------------------------------------------------------
 *
 * ListStoreReallocate --
 *
 *    Reallocates the memory for a ListStore.
 *
 * Results:
 *    Pointer to the ListStore which may be the same as storePtr or pointer
 *    to a new block of memory. On reallocation failure, NULL is returned.
 *
 *
 * Side effects:
 *    The memory pointed to by storePtr is freed if it a new block has to
 *    be returned.
 *
 *
 *------------------------------------------------------------------------
 */
ListStore *
ListStoreReallocate (ListStore *storePtr, ListSizeT numSlots)
{
    ListSizeT newCapacity;
    ListStore *newStorePtr;

    newCapacity = ListStoreUpSize(numSlots);
    newStorePtr =
	(ListStore *)Tcl_AttemptRealloc(storePtr, LIST_SIZE(newCapacity));
    if (newStorePtr == NULL) {
	newCapacity = numSlots;
	newStorePtr = (ListStore *)Tcl_AttemptRealloc(storePtr,
						    LIST_SIZE(newCapacity));
	if (newStorePtr == NULL)
	    return NULL;
    }
    /* Only the capacity has changed, fix it in the header */
    newStorePtr->numAllocated = newCapacity;
    return newStorePtr;
}

/*
 *----------------------------------------------------------------------
 *
 * ListRepInit --
 *
 *      Initializes a ListRep to hold a list internal representation
 *      with space for objc elements.
 *
 *      objc must be > 0. If objv!=NULL, initializes with the first objc
 *      values in that array. If objv==NULL, initalize list internal rep to
 *      have 0 elements, with space to add objc more.
 *
 *	Normally the function allocates the exact space requested unless
 *	the flags arguments has one of the LISTREP_SPACE_* bits set.
 *	See the comments for those #defines.
 *
 *      The reference counts of the ListStore and ListSpan (if present)
 *	pointed to by the initialized repPtr are set to zero.
 *	Caller has to manage them as necessary.
 *
 * Results:
 *      On success, TCL_OK is returned with *listRepPtr initialized.
 *      On failure, panics if LISTREP_PANIC_ON_FAIL is set in flags; otherwise
 *	returns TCL_ERROR with *listRepPtr fields set to NULL.
 *
 * Side effects:
 *	The ref counts of the elements in objv are incremented since the
 *	resulting list now refers to them.
 *
 *----------------------------------------------------------------------
 */
static int
ListRepInit(
    ListSizeT objc,
    Tcl_Obj *const objv[],
    int flags,
    ListRep *repPtr
    )
{
    ListStore *storePtr;

    storePtr = ListStoreNew(objc, objv, flags);
    if (storePtr) {
	repPtr->storePtr = storePtr;
	if (storePtr->firstUsed == 0) {
	    repPtr->spanPtr = NULL;
	} else {
	    repPtr->spanPtr =
		ListSpanNew(storePtr->firstUsed, storePtr->numUsed);
	}
	return TCL_OK;
    }
    /*
     * Initialize to keep gcc happy at the call site. Else it complains
     * about possibly uninitialized use.
     */
    repPtr->storePtr = NULL;
    repPtr->spanPtr = NULL;
    return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *
 * ListRepInitAttempt --
 *
 *	Creates a list internal rep with space for objc elements. See
 *	ListRepInit for requirements for parameters (in particular objc must
 *	be > 0). This function only adds error messages to the interpreter if
 *	not NULL.
 *
 *      The reference counts of the ListStore and ListSpan (if present)
 *	pointed to by the initialized repPtr are set to zero.
 *	Caller has to manage them as necessary.
 *
 * Results:
 *      On success, TCL_OK is returned with *listRepPtr initialized.
 *	On allocation failure, returnes TCL_ERROR with an error message
 *	in the interpreter if non-NULL.
 *
 * Side effects:
 *	The ref counts of the elements in objv are incremented since the
 *	resulting list now refers to them.
 *
 *----------------------------------------------------------------------
 */

static int
ListRepInitAttempt(
    Tcl_Interp *interp,
    ListSizeT objc,
    Tcl_Obj *const objv[],
    ListRep *repPtr)
{
    int result = ListRepInit(objc, objv, 0, repPtr);

    if (result != TCL_OK && interp != NULL) {
	if (objc > LIST_MAX) {
	    ListLimitExceededError(interp);


	} else {


	    MemoryAllocationError(interp, LIST_SIZE(objc));
	}

    }
    return result;
}

/*
 *------------------------------------------------------------------------
 *
 * ListRepClone --
 *
 *    Does a deep clone of an existing ListRep.
 *
 *    Normally the function allocates the exact space needed unless
 *    the flags arguments has one of the LISTREP_SPACE_* bits set.
 *    See the comments for those #defines.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The toRepPtr location is initialized with the ListStore and ListSpan
 *    (if needed) containing a copy of the list elements in fromRepPtr.
 *    The function will panic if memory cannot be allocated.
 *
 *------------------------------------------------------------------------
 */
static void
ListRepClone(ListRep *fromRepPtr, ListRep *toRepPtr, int flags)
{
    Tcl_Obj **fromObjs;
    ListSizeT numFrom;

    ListRepElements(fromRepPtr, numFrom, fromObjs);
    ListRepInit(numFrom, fromObjs, flags | LISTREP_PANIC_ON_FAIL, toRepPtr);
}

/*
 *------------------------------------------------------------------------
 *
 * ListRepUnsharedFreeUnreferenced --
 *
 *    Frees any Tcl_Obj's from the "in-use" area of the ListStore for a
 *    ListRep that are not actually references from any lists.
 *
 *    IMPORTANT: this function must not be called on a shared internal
 *    representation or the internal representation of a shared Tcl_Obj.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The firstUsed and numUsed fields of the ListStore are updated to
 *    reflect the new "in-use" extent.
 *
 *------------------------------------------------------------------------
 */
static void ListRepUnsharedFreeUnreferenced(const ListRep *repPtr)
{
    ListSizeT count;
    ListStore *storePtr;
    ListSpan *spanPtr;

    LIST_ASSERT(!ListRepIsShared(repPtr));
    LISTREP_CHECK(repPtr);

    storePtr = repPtr->storePtr;
    spanPtr = repPtr->spanPtr;
    if (spanPtr == NULL) {
	LIST_ASSERT(storePtr->firstUsed == 0); /* Invariant TBD */
	return;
    }

    /* Collect garbage at front */
    count = spanPtr->spanStart - storePtr->firstUsed;
    LIST_COUNT_ASSERT(count);
    if (count > 0) {
        /* T:listrep-1.5.1,6.{1:8} */
	ObjArrayDecrRefs(storePtr->slots, storePtr->firstUsed, count);
	storePtr->firstUsed = spanPtr->spanStart;
	LIST_ASSERT(storePtr->numUsed >= count);
	storePtr->numUsed -= count;
    }

    /* Collect garbage at back */
    count = (storePtr->firstUsed + storePtr->numUsed)
	  - (spanPtr->spanStart + spanPtr->spanLength);
    LIST_COUNT_ASSERT(count);
    if (count > 0) {
        /* T:listrep-6.{1:8} */
	ObjArrayDecrRefs(
	    storePtr->slots, spanPtr->spanStart + spanPtr->spanLength, count);
	LIST_ASSERT(storePtr->numUsed >= count);
	storePtr->numUsed -= count;
    }

    LIST_ASSERT(ListRepStart(repPtr) == storePtr->firstUsed);
    LIST_ASSERT(ListRepLength(repPtr) == storePtr->numUsed);
    LISTREP_CHECK(repPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_NewListObj --
 *
 *	This function is normally called when not debugging: i.e., when
 *	TCL_MEM_DEBUG is not defined. It creates a new list object from an

 *	(objc,objv) array: that is, each of the objc elements of the array
 *	referenced by objv is inserted as an element into a new Tcl object.
 *

 *	When TCL_MEM_DEBUG is defined, this function just returns the result
 *	of calling the debugging version Tcl_DbNewListObj.
 *
 * Results:
 *	A new list object is returned that is initialized from the object
 *	pointers in objv. If objc is less than or equal to zero, an empty
 *	object is returned. The new object's string representation is left
 *	NULL. The resulting new list object has ref count 0.
 *
 * Side effects:

 *	The ref counts of the elements in objv are incremented since the
 *	resulting list now refers to them.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MEM_DEBUG
#undef Tcl_NewListObj

Tcl_Obj *
Tcl_NewListObj(
    ListSizeT objc,		/* Count of objects referenced by objv. */
    Tcl_Obj *const objv[])	/* An array of pointers to Tcl objects. */
{
    return Tcl_DbNewListObj(objc, objv, "unknown", 0);
}

#else /* if not TCL_MEM_DEBUG */

Tcl_Obj *
Tcl_NewListObj(
    ListSizeT objc,		/* Count of objects referenced by objv. */
    Tcl_Obj *const objv[])	/* An array of pointers to Tcl objects. */
{
    ListRep listRep;
    Tcl_Obj *listObj;

    TclNewObj(listObj);

    if (objc + 1 <= 1) {
	return listObj;
    }




    ListRepInit(objc, objv, LISTREP_PANIC_ON_FAIL, &listRep);

    ListObjReplaceRepAndInvalidate(listObj, &listRep);






    return listObj;
}
#endif /* if TCL_MEM_DEBUG */

/*
 *----------------------------------------------------------------------
 *
 * Tcl_DbNewListObj --
 *
 *	This function is normally called when debugging: i.e., when
 *	TCL_MEM_DEBUG is defined. It creates new list objects. It is the same
 *	as the Tcl_NewListObj function above except that it calls
 *	Tcl_DbCkalloc directly with the file name and line number from its
 *	caller. This simplifies debugging since then the [memory active]
 *	command will report the correct file name and line number when
 *	reporting objects that haven't been freed.
 *
 *	When TCL_MEM_DEBUG is not defined, this function just returns the
 *	result of calling Tcl_NewListObj.
 *
 * Results:
 *	A new list object is returned that is initialized from the object
 *	pointers in objv. If objc is less than or equal to zero, an empty
 *	object is returned. The new object's string representation is left
 *	NULL. The new list object has ref count 0.
 *
 * Side effects:
 *	The ref counts of the elements in objv are incremented since the
 *	resulting list now refers to them.
 *
 *----------------------------------------------------------------------
 */

#ifdef TCL_MEM_DEBUG

Tcl_Obj *
Tcl_DbNewListObj(
    ListSizeT objc,		/* Count of objects referenced by objv. */
    Tcl_Obj *const objv[],	/* An array of pointers to Tcl objects. */
    const char *file,		/* The name of the source file calling this
				 * function; used for debugging. */
    int line)			/* Line number in the source file; used for
				 * debugging. */
{
    Tcl_Obj *listObj;
    ListRep listRep;

    TclDbNewObj(listObj, file, line);

    if (objc + 1 <= 1) {
	return listObj;
    }




    ListRepInit(objc, objv, LISTREP_PANIC_ON_FAIL, &listRep);

    ListObjReplaceRepAndInvalidate(listObj, &listRep);







    return listObj;
}

#else /* if not TCL_MEM_DEBUG */

Tcl_Obj *
Tcl_DbNewListObj(
    ListSizeT objc,		/* Count of objects referenced by objv. */
    Tcl_Obj *const objv[],	/* An array of pointers to Tcl objects. */
    TCL_UNUSED(const char *) /*file*/,
    TCL_UNUSED(int) /*line*/)
{
    return Tcl_NewListObj(objc, objv);
}
#endif /* TCL_MEM_DEBUG */

/*
 *------------------------------------------------------------------------
 *
 * TclNewListObj2 --
 *
 *    Create a new Tcl_Obj list comprising of the concatenation of two
 *    Tcl_Obj* arrays.
 *    TODO - currently this function is not used within tclListObj but
 *    need to see if it would be useful in other files that preallocate
 *    lists and then append.
 *
 * Results:
 *    Non-NULL pointer to the allocate Tcl_Obj.
 *
 * Side effects:
 *    None.
 *
 *------------------------------------------------------------------------
 */
Tcl_Obj *
TclNewListObj2(
    ListSizeT objc1,		/* Count of objects referenced by objv1. */
    Tcl_Obj *const objv1[],	/* First array of pointers to Tcl objects. */
    ListSizeT objc2,		/* Count of objects referenced by objv2. */
    Tcl_Obj *const objv2[]	/* Second array of pointers to Tcl objects. */
)
{
    Tcl_Obj *listObj;
    ListStore *storePtr;
    ListSizeT objc = objc1 + objc2;

    listObj = Tcl_NewListObj(objc, NULL);
    if (objc == 0) {
	return listObj; /* An empty object */
    }
    LIST_ASSERT_TYPE(listObj);

    storePtr = ListObjStorePtr(listObj);

    LIST_ASSERT(ListObjSpanPtr(listObj) == NULL);
    LIST_ASSERT(storePtr->firstUsed == 0);
    LIST_ASSERT(storePtr->numUsed == 0);
    LIST_ASSERT(storePtr->numAllocated >= objc);

    if (objc1) {
	ObjArrayCopy(storePtr->slots, objc1, objv1);
    }
    if (objc2) {
	ObjArrayCopy(&storePtr->slots[objc1], objc2, objv2);
    }
    storePtr->numUsed = objc;
    return listObj;
}

/*
 *----------------------------------------------------------------------
 *
 * TclListObjGetRep --
 *
 *	This function returns a copy of the ListRep stored
 *	as the internal representation of an object. The reference
 *	counts of the (ListStore, ListSpan) contained in the representation
 *	are NOT incremented.
 *
 * Results:
 *	The return value is normally TCL_OK; in this case *listRepP
 *	is set to a copy of the descriptor stored as the internal
 *	representation of the Tcl_Obj containing a list. if listPtr does not
 *	refer to a list object and the object can not be converted to one,
 *	TCL_ERROR is returned and an error message will be left in the
 *	interpreter's result if interp is not NULL.
 *
 * Side effects:
 *	The possible conversion of the object referenced by listPtr
 *	to a list object. *repPtr is initialized to the internal rep
 *      if result is TCL_OK, or set to NULL on error.
 *----------------------------------------------------------------------
 */

static int
TclListObjGetRep(
    Tcl_Interp *interp, /* Used to report errors if not NULL. */
    Tcl_Obj *listObj,   /* List object for which an element array is
			 * to be returned. */
    ListRep *repPtr) /* Location to store descriptor */
{
    if (!TclHasInternalRep(listObj, &tclListType)) {
	int result;
	result = SetListFromAny(interp, listObj);
	if (result != TCL_OK) {
	    /* Init to keep gcc happy wrt uninitialized fields at call site */
	    repPtr->storePtr = NULL;
	    repPtr->spanPtr = NULL;
	    return result;
	}
    }
    ListObjGetRep(listObj, repPtr);
    LISTREP_CHECK(repPtr);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_SetListObj --
 *
 *	Modify an object to be a list containing each of the objc elements of
 *	the object array referenced by objv.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The object is made a list object and is initialized from the object
 *	pointers in objv. If objc is less than or equal to zero, an empty
 *	object is returned. The new object's string representation is left
 *	NULL. The ref counts of the elements in objv are incremented since the
 *	list now refers to them. The object's old string and internal
 *	representations are freed and its type is set NULL.
 *
 *----------------------------------------------------------------------
 */

void
Tcl_SetListObj(
    Tcl_Obj *objPtr,		/* Object whose internal rep to init. */
    ListSizeT objc,		/* Count of objects referenced by objv. */
    Tcl_Obj *const objv[])	/* An array of pointers to Tcl objects. */
{


    if (Tcl_IsShared(objPtr)) {
	Tcl_Panic("%s called with shared object", "Tcl_SetListObj");
    }








    /*
     * Set the object's type to "list" and initialize the internal rep.
     * However, if there are no elements to put in the list, just give the
     * object an empty string rep and a NULL type. NOTE ListRepInit must
     * not be called with objc == 0!
     */

    if (objc + 1 > 1) {
	ListRep listRep;
	/* TODO - perhaps ask for extra space? */
	ListRepInit(objc, objv, LISTREP_PANIC_ON_FAIL, &listRep);
	ListObjReplaceRepAndInvalidate(objPtr, &listRep);
    } else {
	TclFreeInternalRep(objPtr);
	TclInvalidateStringRep(objPtr);
	Tcl_InitStringRep(objPtr, NULL, 0);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TclListObjCopy --
 *
 *	Makes a "pure list" copy of a list value. This provides for the C
 *	level a counterpart of the [lrange $list 0 end] command, while using
 *	internals details to be as efficient as possible.
 *
 * Results:
 *	Normally returns a pointer to a new Tcl_Obj, that contains the same
 *	list value as *listPtr does. The returned Tcl_Obj has a refCount of
 *	zero. If *listPtr does not hold a list, NULL is returned, and if
 *	interp is non-NULL, an error message is recorded there.

 *
 * Side effects:
 *	None.

 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclListObjCopy(
    Tcl_Interp *interp,		/* Used to report errors if not NULL. */
    Tcl_Obj *listObj)		/* List object for which an element array is
				 * to be returned. */
{
    Tcl_Obj *copyObj;


    if (!TclHasInternalRep(listObj, &tclListType)) {

	if (SetListFromAny(interp, listObj) != TCL_OK) {
	    return NULL;
	}
    }

    TclNewObj(copyObj);
    TclInvalidateStringRep(copyObj);
    DupListInternalRep(listObj, copyObj);
    return copyObj;
}

/*
 *------------------------------------------------------------------------
 *
 * ListRepRange --
 *
 *	Initializes a ListRep as a range within the passed ListRep.
 *	The range limits are clamped to the list boundaries.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *      The ListStore and ListSpan referenced by in the returned ListRep
 *      may or may not be the same as those passed in. For example, the
 *      ListStore may differ because the range is small enough that a new
 *      ListStore is more memory-optimal. The ListSpan may differ because
 *      it is NULL or shared. Regardless, reference counts on the returned
 *      values are not incremented. Generally, ListObjReplaceRepAndInvalidate
 *      may be used to store the new ListRep back into an object or a
 *      ListRepIncrRefs followed by ListRepDecrRefs to free in case of errors.
 *	Any other use should be carefully reconsidered.
 *      TODO WARNING:- this is an awkward interface and easy for caller
 *      to get wrong. Mostly due to refcount combinations. Perhaps passing
 *      in the source listObj instead of source listRep might simplify.
 *
 *------------------------------------------------------------------------
 */
static void
ListRepRange(
    ListRep *srcRepPtr,    /* Contains source of the range */
    ListSizeT rangeStart,  /* Index of first element to include */
    ListSizeT rangeEnd,    /* Index of last element to include */
    int preserveSrcRep,    /* If true, srcRepPtr contents must not be
			      modified (generally because a shared Tcl_Obj
			      references it) */
    ListRep *rangeRepPtr)  /* Output. Must NOT be == srcRepPtr */
{
    Tcl_Obj **srcElems;
    ListSizeT numSrcElems = ListRepLength(srcRepPtr);
    ListSizeT rangeLen;
    ListSizeT numAfterRangeEnd;

    LISTREP_CHECK(srcRepPtr);

    /* Take the opportunity to garbage collect */
    /* TODO - we probably do not need the preserveSrcRep here unlike later */
    if (!preserveSrcRep) {
        /* T:listrep-1.{4,5,8,9},2.{4:7},3.{15:18},4.{7,8} */
	ListRepFreeUnreferenced(srcRepPtr);
    } /* else T:listrep-2.{4.2,4.3,5.2,5.3,6.2,7.2,8.1} */

    if (rangeStart == TCL_INDEX_NONE) {
	rangeStart = 0;
    }
    if ((rangeEnd != TCL_INDEX_NONE) && (rangeEnd >= numSrcElems)) {
	rangeEnd = numSrcElems - 1;
    }
    if (rangeStart + 1 > rangeEnd + 1) {
	/* Empty list of capacity 1. */
	ListRepInit(1, NULL, LISTREP_PANIC_ON_FAIL, rangeRepPtr);
	return;
    }

    rangeLen = rangeEnd - rangeStart + 1;

    /*
     * We can create a range one of four ways:
     *  (0) Range encapsulates entire list
     *  (1) Special case: deleting in-place from end of an unshared object
     *  (2) Use a ListSpan referencing the current ListStore
     *  (3) Creating a new ListStore
     *  (4) Removing all elements outside the range in the current ListStore
     * Option (4) may only be done if caller has not disallowed it AND
     * the ListStore is not shared.
     *
     * The choice depends on heuristics related to speed and memory.
     * TODO - heuristics below need to be measured and tuned.
     *
     * Note: Even if nothing below cause any changes, we still want the
     * string-canonizing effect of [lrange 0 end] so the Tcl_Obj should not
     * be returned as is even if the range encompasses the whole list.
     */
    if (rangeStart == 0 && rangeEnd == (numSrcElems-1)) {
	/* Option 0 - entire list. This may be used to canonicalize */
        /* T:listrep-1.10.1,2.8.1 */
	*rangeRepPtr = *srcRepPtr; /* Not ref counts not incremented */
    } else if (rangeStart == 0 && (!preserveSrcRep)
	       && (!ListRepIsShared(srcRepPtr) && srcRepPtr->spanPtr == NULL)) {
	/* Option 1 - Special case unshared, exclude end elements, no span  */
	LIST_ASSERT(srcRepPtr->storePtr->firstUsed == 0); /* If no span */
	ListRepElements(srcRepPtr, numSrcElems, srcElems);
	numAfterRangeEnd = numSrcElems - (rangeEnd + 1);
	/* Assert: Because numSrcElems > rangeEnd earlier */
	if (numAfterRangeEnd != 0) {
            /* T:listrep-1.{8,9} */
	    ObjArrayDecrRefs(srcElems, rangeEnd + 1, numAfterRangeEnd);
	}
	/* srcRepPtr->storePtr->firstUsed,numAllocated unchanged */
	srcRepPtr->storePtr->numUsed = rangeLen;
	srcRepPtr->storePtr->flags = 0;
	rangeRepPtr->storePtr = srcRepPtr->storePtr; /* Note no incr ref */
	rangeRepPtr->spanPtr = NULL;
    } else if (ListSpanMerited(rangeLen,
			       srcRepPtr->storePtr->numUsed,
			       srcRepPtr->storePtr->numAllocated)) {
	/* Option 2 - because span would be most efficient */
	ListSizeT spanStart = ListRepStart(srcRepPtr) + rangeStart;
	if (!preserveSrcRep && srcRepPtr->spanPtr
	    && srcRepPtr->spanPtr->refCount <= 1) {
	    /* If span is not shared reuse it */
            /* T:listrep-2.7.3,3.{16,18} */
	    srcRepPtr->spanPtr->spanStart = spanStart;
	    srcRepPtr->spanPtr->spanLength = rangeLen;
	    *rangeRepPtr = *srcRepPtr;
	} else {
	    /* Span not present or is shared. */
            /* T:listrep-1.5,2.{5,7},4.{7,8} */
	    rangeRepPtr->storePtr = srcRepPtr->storePtr;
	    rangeRepPtr->spanPtr = ListSpanNew(spanStart, rangeLen);
	}
        /*
         * We have potentially created a new internal representation that
         * references the same storage as srcRep but not yet incremented its
         * reference count. So do NOT call freezombies if preserveSrcRep
         * is mandated.
         */
	if (!preserveSrcRep) {
            /* T:listrep-1.{5.1,5.2,5.4},2.{5,7},3.{16,18},4.{7,8} */
	    ListRepFreeUnreferenced(rangeRepPtr);
	}
    } else if (preserveSrcRep || ListRepIsShared(srcRepPtr)) {
	/* Option 3 - span or modification in place not allowed/desired */
        /* T:listrep-2.{4,6} */
	ListRepElements(srcRepPtr, numSrcElems, srcElems);
	/* TODO - allocate extra space? */
	ListRepInit(rangeLen,
		    &srcElems[rangeStart],
		    LISTREP_PANIC_ON_FAIL,
		    rangeRepPtr);
    } else {
	/*
	 * Option 4 - modify in place. Note that because of the invariant
	 * that spanless list stores must start at 0, we have to move
	 * everything to the front.
	 * TODO - perhaps if a span already exists, no need to move to front?
	 * or maybe no need to move all the way to the front?
	 * TODO - if range is small relative to allocation, allocate new?
	 */

	/* Asserts follow from call to ListRepFreeUnreferenced earlier */
	LIST_ASSERT(!preserveSrcRep);
	LIST_ASSERT(!ListRepIsShared(srcRepPtr));
	LIST_ASSERT(ListRepStart(srcRepPtr) == srcRepPtr->storePtr->firstUsed);
	LIST_ASSERT(ListRepLength(srcRepPtr) == srcRepPtr->storePtr->numUsed);

	ListRepElements(srcRepPtr, numSrcElems, srcElems);

	/* Free leading elements outside range */
	if (rangeStart != 0) {
            /* T:listrep-1.4,3.15 */
	    ObjArrayDecrRefs(srcElems, 0, rangeStart);
	}
	/* Ditto for trailing */
	numAfterRangeEnd = numSrcElems - (rangeEnd + 1);
	/* Assert: Because numSrcElems > rangeEnd earlier */
	if (numAfterRangeEnd != 0) {
            /* T:listrep-3.17 */
	    ObjArrayDecrRefs(srcElems, rangeEnd + 1, numAfterRangeEnd);
	}
	memmove(&srcRepPtr->storePtr->slots[0],
		&srcRepPtr->storePtr
		     ->slots[srcRepPtr->storePtr->firstUsed + rangeStart],
		rangeLen * sizeof(Tcl_Obj *));
	srcRepPtr->storePtr->firstUsed = 0;
	srcRepPtr->storePtr->numUsed = rangeLen;
	srcRepPtr->storePtr->flags = 0;
	if (srcRepPtr->spanPtr) {
	    /* In case the source has a span, update it for consistency */
            /* T:listrep-3.{15,17} */
	    srcRepPtr->spanPtr->spanStart = srcRepPtr->storePtr->firstUsed;
	    srcRepPtr->spanPtr->spanLength = srcRepPtr->storePtr->numUsed;
	}
	rangeRepPtr->storePtr = srcRepPtr->storePtr;
	rangeRepPtr->spanPtr = NULL;
    }

    /* TODO - call freezombies here if !preserveSrcRep? */

    /* Note ref counts intentionally not incremented */
    LISTREP_CHECK(rangeRepPtr);
    return;
}

/*
 *----------------------------------------------------------------------
 *
 * TclListObjRange --
 *
 *	Makes a slice of a list value.
 *      *listObj must be known to be a valid list.
 *
 * Results:
 *	Returns a pointer to the sliced list.
 *      This may be a new object or the same object if not shared.
 *	Returns NULL if passed listObj was not a list and could not be
 *	converted to one.
 *
 * Side effects:
 *	The possible conversion of the object referenced by listPtr
 *	to a list object.
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclListObjRange(
    Tcl_Obj *listObj,		/* List object to take a range from. */
    ListSizeT rangeStart,	/* Index of first element to include. */
    ListSizeT rangeEnd)		/* Index of last element to include. */
{

    ListRep listRep;
    ListRep resultRep;


    int isShared;
    if (TclListObjGetRep(NULL, listObj, &listRep) != TCL_OK)








	return NULL;

















    isShared = Tcl_IsShared(listObj);





    ListRepRange(&listRep, rangeStart, rangeEnd, isShared, &resultRep);






    if (isShared) {




        /* T:listrep-1.10.1,2.{4.2,4.3,5.2,5.3,6.2,7.2,8.1} */
	TclNewObj(listObj);
    } /* T:listrep-1.{4.3,5.1,5.2} */
    ListObjReplaceRepAndInvalidate(listObj, &resultRep);
    return listObj;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ListObjGetElements --
 *
 *	This function returns an (objc,objv) array of the elements in a list
 *	object.

 *

 * Results:
 *	The return value is normally TCL_OK; in this case *objcPtr is set to
 *	the count of list elements and *objvPtr is set to a pointer to an
 *	array of (*objcPtr) pointers to each list element. If listPtr does not
 *	refer to a list object and the object can not be converted to one,
 *	TCL_ERROR is returned and an error message will be left in the
 *	interpreter's result if interp is not NULL.
 *
 *	The objects referenced by the returned array should be treated as
 *	readonly and their ref counts are _not_ incremented; the caller must
 *	do that if it holds on to a reference. Furthermore, the pointer and
 *	length returned by this function may change as soon as any function is
 *	called on the list object; be careful about retaining the pointer in a
 *	local data structure.
 *





 * Side effects:

 *	The possible conversion of the object referenced by listPtr
 *	to a list object.
 *
 *----------------------------------------------------------------------
 */

#undef Tcl_ListObjGetElements
int
Tcl_ListObjGetElements(
    Tcl_Interp *interp,		/* Used to report errors if not NULL. */
    Tcl_Obj *objPtr,		/* List object for which an element array is
				 * to be returned. */
    ListSizeT *objcPtr,		/* Where to store the count of objects
				 * referenced by objv. */
    Tcl_Obj ***objvPtr)		/* Where to store the pointer to an array of
				 * pointers to the list's objects. */
{
    ListRep listRep;

    if (TclHasInternalRep(objPtr,&tclArithSeriesType)) {
	return TclArithSeriesGetElements(interp, objPtr, objcPtr, objvPtr);



    }







    if (TclListObjGetRep(interp, objPtr, &listRep) != TCL_OK)
	return TCL_ERROR;



    ListRepElements(&listRep, *objcPtr, *objvPtr);

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ListObjAppendList --
 *
 *	This function appends the elements in the list fromObj
 *	to toObj. toObj must not be shared else the function will panic.
 *
 * Results:

 *	The return value is normally TCL_OK. If fromObj or toObj do not



 *	refer to list values, TCL_ERROR is returned and an error message is


 *	left in the interpreter's result if interp is not NULL.
 *
 * Side effects:

 *	The reference counts of the elements in fromObj are incremented

 *	since the list now refers to them. toObj and fromObj are
 *	converted, if necessary, to list objects. Also, appending the new
 *	elements may cause toObj's array of element pointers to grow.
 *	toObj's old string representation, if any, is invalidated.

 *
 *----------------------------------------------------------------------
 */

int
Tcl_ListObjAppendList(
    Tcl_Interp *interp,		/* Used to report errors if not NULL. */
    Tcl_Obj *toObj,		/* List object to append elements to. */
    Tcl_Obj *fromObj)		/* List obj with elements to append. */
{
    ListSizeT objc;
    Tcl_Obj **objv;

    if (Tcl_IsShared(toObj)) {
	Tcl_Panic("%s called with shared object", "Tcl_ListObjAppendList");
    }





    if (TclListObjGetElementsM(interp, fromObj, &objc, &objv) != TCL_OK) {
	return TCL_ERROR;
    }

    /*
     * Insert the new elements starting after the lists's last element.
     * Delete zero existing elements.
     */

    return TclListObjAppendElements(interp, toObj, objc, objv);
}

/*
 *------------------------------------------------------------------------
 *
 * TclListObjAppendElements --
 *
 *      Appends multiple elements to a Tcl_Obj list object. If
 *      the passed Tcl_Obj is not a list object, it will be converted to one
 *      and an error raised if the conversion fails.
 *
 * 	The Tcl_Obj must not be shared though the internal representation
 * 	may be.
 *
 * Results:
 *	On success, TCL_OK is returned with the specified elements appended.
 *	On failure, TCL_ERROR is returned with an error message in the
 *	interpreter if not NULL.
 *
 * Side effects:
 *    None.
 *
 *------------------------------------------------------------------------
 */
 int TclListObjAppendElements (
    Tcl_Interp *interp,		/* Used to report errors if not NULL. */
    Tcl_Obj *toObj,		/* List object to append */
    ListSizeT elemCount,        /* Number of elements in elemObjs[] */
    Tcl_Obj * const elemObjv[])	/* Objects to append to toObj's list. */
{
    ListRep listRep;
    Tcl_Obj **toObjv;
    ListSizeT toLen;
    ListSizeT finalLen;

    if (Tcl_IsShared(toObj)) {
	Tcl_Panic("%s called with shared object", "TclListObjAppendElements");
    }

    if (TclListObjGetRep(interp, toObj, &listRep) != TCL_OK)
	return TCL_ERROR; /* Cannot be converted to a list */

    if (elemCount == 0)
	return TCL_OK; /* Nothing to do. Note AFTER check for list above */

    ListRepElements(&listRep, toLen, toObjv);
    if (elemCount > LIST_MAX || toLen > (LIST_MAX - elemCount)) {
	return ListLimitExceededError(interp);
    }

    finalLen = toLen + elemCount;
    if (!ListRepIsShared(&listRep)) {
	/*
	 * Reuse storage if possible. Even if too small, realloc-ing instead
	 * of creating a new ListStore will save us on manipulating Tcl_Obj
	 * reference counts on the elements which is a substantial cost
	 * if the list is not small.
	 */
	ListSizeT numTailFree;

	ListRepFreeUnreferenced(&listRep); /* Collect garbage before checking room */

	LIST_ASSERT(ListRepStart(&listRep) == listRep.storePtr->firstUsed);
	LIST_ASSERT(ListRepLength(&listRep) == listRep.storePtr->numUsed);
	LIST_ASSERT(toLen == listRep.storePtr->numUsed);

	if (finalLen > listRep.storePtr->numAllocated) {
	    /* T:listrep-1.{2,11},3.6 */
	    ListStore *newStorePtr;
	    newStorePtr = ListStoreReallocate(listRep.storePtr, finalLen);
	    if (newStorePtr == NULL) {
		return MemoryAllocationError(interp, LIST_SIZE(finalLen));
	    }
	    LIST_ASSERT(newStorePtr->numAllocated >= finalLen);
	    listRep.storePtr = newStorePtr;
	    /*
	     * WARNING: at this point the Tcl_Obj internal rep potentially
	     * points to freed storage if the reallocation returned a
	     * different location. Overwrite it to bring it back in sync.
	     */
	    ListObjStompRep(toObj, &listRep);
	} /* else T:listrep-3.{4,5} */
	LIST_ASSERT(listRep.storePtr->numAllocated >= finalLen);
	/* Current store big enough */
	numTailFree = ListRepNumFreeTail(&listRep);
	LIST_ASSERT((numTailFree + listRep.storePtr->firstUsed)
		    >= elemCount); /* Total free */
	if (numTailFree < elemCount) {
	    /* Not enough room at back. Move some to front */
            /* T:listrep-3.5 */
	    ListSizeT shiftCount = elemCount - numTailFree;
	    /* Divide remaining space between front and back */
	    shiftCount += (listRep.storePtr->numAllocated - finalLen) / 2;
	    LIST_ASSERT(shiftCount <= listRep.storePtr->firstUsed);
	    if (shiftCount) {
                /* T:listrep-3.5 */
		ListRepUnsharedShiftDown(&listRep, shiftCount);
            }
	} /* else T:listrep-3.{4,6} */
	ObjArrayCopy(&listRep.storePtr->slots[ListRepStart(&listRep)
					      + ListRepLength(&listRep)],
		     elemCount,
		     elemObjv);
	listRep.storePtr->numUsed = finalLen;
	if (listRep.spanPtr) {
            /* T:listrep-3.{4,5,6} */
	    LIST_ASSERT(listRep.spanPtr->spanStart
			== listRep.storePtr->firstUsed);
	    listRep.spanPtr->spanLength = finalLen;
	} /* else T:listrep-3.6.3 */
	LIST_ASSERT(ListRepStart(&listRep) == listRep.storePtr->firstUsed);
	LIST_ASSERT(ListRepLength(&listRep) == finalLen);
	LISTREP_CHECK(&listRep);

	ListObjReplaceRepAndInvalidate(toObj, &listRep);
	return TCL_OK;
    }

    /*
     * Have to make a new list rep, either shared or no room in old one.
     * If the old list did not have a span (all elements at front), do
     * not leave space in the front either, assuming all appends and no
     * prepends.
     */
    if (ListRepInit(finalLen,
		    NULL,
		    listRep.spanPtr ? LISTREP_SPACE_FAVOR_BACK
				    : LISTREP_SPACE_ONLY_BACK,
		    &listRep)
	!= TCL_OK) {
	return TCL_ERROR;
    }
    LIST_ASSERT(listRep.storePtr->numAllocated >= finalLen);

    if (toLen) {
        /* T:listrep-2.{2,9},4.5 */
	ObjArrayCopy(ListRepSlotPtr(&listRep, 0), toLen, toObjv);
    }
    ObjArrayCopy(ListRepSlotPtr(&listRep, toLen), elemCount, elemObjv);
    listRep.storePtr->numUsed = finalLen;
    if (listRep.spanPtr) {
        /* T:listrep-4.5 */
	LIST_ASSERT(listRep.spanPtr->spanStart == listRep.storePtr->firstUsed);
	listRep.spanPtr->spanLength = finalLen;
    }
    LISTREP_CHECK(&listRep);
    ListObjReplaceRepAndInvalidate(toObj, &listRep);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ListObjAppendElement --
 *
 *	This function is a special purpose version of Tcl_ListObjAppendList:
 *	it appends a single object referenced by elemObj to the list object
 *	referenced by toObj. If toObj is not already a list object, an
 *	attempt will be made to convert it to one.
 *
 * Results:

 *	The return value is normally TCL_OK; in this case elemObj is added to


 *	the end of toObj's list. If toObj does not refer to a list object


 *	and the object can not be converted to one, TCL_ERROR is returned and
 *	an error message will be left in the interpreter's result if interp is
 *	not NULL.
 *
 * Side effects:


 *	The ref count of elemObj is incremented since the list now refers to
 *	it. toObj will be converted, if necessary, to a list object. Also,
 *	appending the new element may cause listObj's array of element
 *	pointers to grow. toObj's old string representation, if any, is
 *	invalidated.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_ListObjAppendElement(
    Tcl_Interp *interp,		/* Used to report errors if not NULL. */
    Tcl_Obj *toObj,		/* List object to append elemObj to. */
    Tcl_Obj *elemObj)		/* Object to append to toObj's list. */
{









































    /*


     * TODO - compare perf with 8.6 to see if worth optimizing single


















































     * element case








     */









    return TclListObjAppendElements(interp, toObj, 1, &elemObj);



























}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ListObjIndex --
 *
 *	This function returns a pointer to the index'th object from the list
 *	referenced by listPtr. The first element has index 0. If index is
 *	negative or greater than or equal to the number of elements in the
 *	list, a NULL is returned. If listPtr is not a list object, an attempt
 *	will be made to convert it to a list.
 *
 * Results:

 *	The return value is normally TCL_OK; in this case objPtrPtr is set to
 *	the Tcl_Obj pointer for the index'th list element or NULL if index is
 *	out of range. This object should be treated as readonly and its ref
 *	count is _not_ incremented; the caller must do that if it holds on to
 *	the reference. If listPtr does not refer to a list and can't be

 *	converted to one, TCL_ERROR is returned and an error message is left


 *	in the interpreter's result if interp is not NULL.
 *
 * Side effects:

 *	listPtr will be converted, if necessary, to a list object.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_ListObjIndex(
    Tcl_Interp *interp,  /* Used to report errors if not NULL. */
    Tcl_Obj *listObj,    /* List object to index into. */
    ListSizeT index,           /* Index of element to return. */
    Tcl_Obj **objPtrPtr) /* The resulting Tcl_Obj* is stored here. */
{
    Tcl_Obj **elemObjs;
    ListSizeT numElems;

    if (TclHasInternalRep(listObj,&tclArithSeriesType)) {
	return TclArithSeriesObjIndex(listObj, index, objPtrPtr);


    }





    /*
     * TODO
     * Unlike the original list code, this does not optimize for lindex'ing
     * an empty string when the internal rep is not already a list. On the
     * other hand, this code will be faster for the case where the object
     * is currently a dict. Benchmark the two cases.
     */
    if (TclListObjGetElementsM(interp, listObj, &numElems, &elemObjs)
	!= TCL_OK) {
	return TCL_ERROR;
    }



    if (index >= numElems) {
	*objPtrPtr = NULL;
    } else {
	*objPtrPtr = elemObjs[index];
    }

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ListObjLength --
 *
 *	This function returns the number of elements in a list object. If the
 *	object is not already a list object, an attempt will be made to
 *	convert it to one.
 *
 * Results:

 *	The return value is normally TCL_OK; in this case *lenPtr will be set

 *	to the integer count of list elements. If listPtr does not refer to a
 *	list object and the object can not be converted to one, TCL_ERROR is
 *	returned and an error message will be left in the interpreter's result
 *	if interp is not NULL.

 *

 * Side effects:
 *	The possible conversion of the argument object to a list object.

 *
 *----------------------------------------------------------------------
 */

#undef Tcl_ListObjLength
int
Tcl_ListObjLength(
    Tcl_Interp *interp,	/* Used to report errors if not NULL. */
    Tcl_Obj *listObj,	/* List object whose #elements to return. */
    ListSizeT *lenPtr)	/* The resulting int is stored here. */
{
    ListRep listRep;

    if (TclHasInternalRep(listObj,&tclArithSeriesType)) {



	*lenPtr = TclArithSeriesObjLength(listObj);



	return TCL_OK;
    }




    /*
     * TODO
     * Unlike the original list code, this does not optimize for lindex'ing
     * an empty string when the internal rep is not already a list. On the
     * other hand, this code will be faster for the case where the object
     * is currently a dict. Benchmark the two cases.
     */
    if (TclListObjGetRep(interp, listObj, &listRep) != TCL_OK) {
	return TCL_ERROR;
    }
    *lenPtr = ListRepLength(&listRep);

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ListObjReplace --
 *
 *	This function replaces zero or more elements of the list referenced by
 *	listObj with the objects from an (objc,objv) array. The objc elements
 *	of the array referenced by objv replace the count elements in listPtr
 *	starting at first.
 *
 *	If the argument first is zero or negative, it refers to the first
 *	element. If first is greater than or equal to the number of elements
 *	in the list, then no elements are deleted; the new elements are

 *	appended to the list. Count gives the number of elements to replace.
 *	If count is zero or negative then no elements are deleted; the new
 *	elements are simply inserted before first.
 *



 *	The argument objv refers to an array of objc pointers to the new
 *	elements to be added to listPtr in place of those that were deleted.
 *	If objv is NULL, no new elements are added. If listPtr is not a list
 *	object, an attempt will be made to convert it to one.
 *
 * Results:
 *	The return value is normally TCL_OK. If listPtr does not refer to a
 *	list object and can not be converted to one, TCL_ERROR is returned and

 *	an error message will be left in the interpreter's result if interp is
 *	not NULL.
 *
 * Side effects:


 *	The ref counts of the objc elements in objv are incremented since the

 *	resulting list now refers to them. Similarly, the ref counts for

 *	replaced objects are decremented. listObj is converted, if necessary,
 *	to a list object. listObj's old string representation, if any, is
 *	freed.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_ListObjReplace(
    Tcl_Interp *interp,		/* Used for error reporting if not NULL. */
    Tcl_Obj *listObj,		/* List object whose elements to replace. */
    ListSizeT first,		/* Index of first element to replace. */
    ListSizeT numToDelete,	/* Number of elements to replace. */
    ListSizeT numToInsert,	/* Number of objects to insert. */
    Tcl_Obj *const insertObjs[])/* Tcl objects to insert */

{
    ListRep listRep;
    ListSizeT origListLen;
    ptrdiff_t lenChange;
    ptrdiff_t leadSegmentLen;
    ptrdiff_t tailSegmentLen;
    ListSizeT numFreeSlots;
    ptrdiff_t leadShift;
    ptrdiff_t tailShift;
    Tcl_Obj **listObjs;

    int favor;

    if (Tcl_IsShared(listObj)) {
	Tcl_Panic("%s called with shared object", "Tcl_ListObjReplace");
    }




    if (TclListObjGetRep(interp, listObj, &listRep) != TCL_OK)



	return TCL_ERROR; /* Cannot be converted to a list */




    /* TODO - will need modification if Tcl9 sticks to unsigned indices */






    /* Make limits sane */







    origListLen = ListRepLength(&listRep);



    if (first == TCL_INDEX_NONE) {
	first = 0;
    }
    if (first > origListLen) {
	first = origListLen;	/* So we'll insert after last element. */
    }
    if (numToDelete == TCL_INDEX_NONE) {
	numToDelete = 0;
    } else if (first > ListSizeT_MAX - numToDelete /* Handle integer overflow */
             || origListLen < first + numToDelete) {
	numToDelete = origListLen - first;
    }

    if (numToInsert > ListSizeT_MAX - (origListLen - numToDelete)) {
	return ListLimitExceededError(interp);
    }


    if ((first+numToDelete) >= origListLen) {
	/* Operating at back of list. Favor leaving space at back */
	favor = LISTREP_SPACE_FAVOR_BACK;
    } else if (first == 0) {
	/* Operating on front of list. Favor leaving space in front */
	favor = LISTREP_SPACE_FAVOR_FRONT;
    } else {
	/* Operating on middle of list. */
	favor = LISTREP_SPACE_FAVOR_NONE;
    }

    /*
     * There are a number of special cases to consider from an optimization
     * point of view.
     * (1) Pure deletes (numToInsert==0) from the front or back can be treated
     * as a range op irrespective of whether the ListStore is shared or not
     * (2) Pure inserts (numToDelete == 0)
     *   (2a) Pure inserts at the back can be treated as appends
     *   (2b) Pure inserts from the *front* can be optimized under certain
     *   conditions by inserting before first ListStore slot in use if there
     *   is room, again irrespective of sharing
     * (3) If the ListStore is shared OR there is insufficient free space
     * OR existing allocation is too large compared to new size, create
     * a new ListStore
     * (4) Unshared ListStore with sufficient free space. Delete, shift and
     * insert within the ListStore.
     */

    /* Note: do not do TclInvalidateStringRep as yet in case there are errors */

    /* Check Case (1) - Treat pure deletes from front or back as range ops */
    if (numToInsert == 0) {
	if (numToDelete == 0) {
	    /*
             * Should force canonical even for no-op. Remember Tcl_Obj unshared
             * so OK to invalidate string rep
             */
            /* T:listrep-1.10,2.8 */
	    TclInvalidateStringRep(listObj);
	    return TCL_OK;
	}
	if (first == 0) {
	    /* Delete from front, so return tail. */
            /* T:listrep-1.{4,5},2.{4,5},3.{15,16},4.7 */
	    ListRep tailRep;

	    ListRepRange(&listRep, numToDelete, origListLen-1, 0, &tailRep);
	    ListObjReplaceRepAndInvalidate(listObj, &tailRep);
	    return TCL_OK;
	} else if ((first+numToDelete) >= origListLen) {
	    /* Delete from tail, so return head */
            /* T:listrep-1.{8,9},2.{6,7},3.{17,18},4.8 */
	    ListRep headRep;
	    ListRepRange(&listRep, 0, first-1, 0, &headRep);
	    ListObjReplaceRepAndInvalidate(listObj, &headRep);
	    return TCL_OK;
	}




	/* Deletion from middle. Fall through to general case */
    }

    /* Garbage collect before checking the pure insert optimization */
    ListRepFreeUnreferenced(&listRep);

    /*
     * Check Case (2) - pure inserts under certain conditions:
     */




    if (numToDelete == 0) {
	/* Case (2a) - Append to list. */
	if (first == origListLen) {
            /* T:listrep-1.11,2.9,3.{5,6},2.2.1 */
	    return TclListObjAppendElements(
		interp, listObj, numToInsert, insertObjs);
	}

	/*
	 * Case (2b) - pure inserts at front under some circumstances
	 * (i) Insertion must be at head of list
	 * (ii) The list's span must be at head of the in-use slots in the store
	 * (iii) There must be unused room at front of the store
	 * NOTE THIS IS TRUE EVEN IF THE ListStore IS SHARED as it will not
	 * affect the other Tcl_Obj's referencing this ListStore.
	 */
	if (first == 0 &&                                            /* (i) */
	    ListRepStart(&listRep) == listRep.storePtr->firstUsed && /* (ii) */
	    numToInsert <= listRep.storePtr->firstUsed               /* (iii) */
	) {
	    ListSizeT newLen;
	    LIST_ASSERT(numToInsert); /* Else would have returned above */
	    listRep.storePtr->firstUsed -= numToInsert;
	    ObjArrayCopy(&listRep.storePtr->slots[listRep.storePtr->firstUsed],
			 numToInsert,
			 insertObjs);
	    listRep.storePtr->numUsed += numToInsert;
	    newLen = listRep.spanPtr->spanLength + numToInsert;
	    if (listRep.spanPtr && listRep.spanPtr->refCount <= 1) {
		/* An unshared span record, re-use it */
                /* T:listrep-3.1 */
		listRep.spanPtr->spanStart = listRep.storePtr->firstUsed;
		listRep.spanPtr->spanLength = newLen;
	    } else {
		/* Need a new span record */
		if (listRep.storePtr->firstUsed == 0) {
		    listRep.spanPtr = NULL;
		} else {
                    /* T:listrep-4.3 */
		    listRep.spanPtr =
			ListSpanNew(listRep.storePtr->firstUsed, newLen);
		}




	    }


	    ListObjReplaceRepAndInvalidate(listObj, &listRep);
	    return TCL_OK;
	}

    }

    /* Just for readability of the code */
    lenChange = numToInsert - numToDelete;
    leadSegmentLen = first;
    tailSegmentLen = origListLen - (first + numToDelete);
    numFreeSlots = listRep.storePtr->numAllocated - listRep.storePtr->numUsed;

    /*

     * Before further processing, if unshared, try and reallocate to avoid
     * new allocation below. This avoids expensive ref count manipulation
     * later by not having to go through the ListRepInit and
     * ListObjReplaceAndInvalidate below.
     * TODO - we could be smarter about the reallocate. Use of realloc
     * means all new free space is at the back. Instead, the realloc could
     * be an explicit alloc and memmove which would let us redistribute
     * free space.
     */
    if ((ptrdiff_t)numFreeSlots < lenChange && !ListRepIsShared(&listRep)) {
	/* T:listrep-1.{1,3,14,18,21},3.{3,10,11,14,27,32,41} */
	ListStore *newStorePtr =
	    ListStoreReallocate(listRep.storePtr, origListLen + lenChange);
	if (newStorePtr == NULL) {
	    return MemoryAllocationError(interp,
					 LIST_SIZE(origListLen + lenChange));
	}
	listRep.storePtr = newStorePtr;
	numFreeSlots =


	    listRep.storePtr->numAllocated - listRep.storePtr->numUsed;
	/*
	 * WARNING: at this point the Tcl_Obj internal rep potentially
	 * points to freed storage if the reallocation returned a
	 * different location. Overwrite it to bring it back in sync.
	 */
	ListObjStompRep(listObj, &listRep);
    }



    /*
     * Case (3) a new ListStore is required
     * (a) The passed-in ListStore is shared
     * (b) There is not enough free space in the unshared passed-in ListStore
     * (c) The new unshared size is much "smaller" (TODO) than the allocated space
     * TODO - for unshared case ONLY, consider a "move" based implementation
     */
    if (ListRepIsShared(&listRep) || /* 3a */
	(ptrdiff_t)numFreeSlots < lenChange ||  /* 3b */
	(origListLen + lenChange) < (listRep.storePtr->numAllocated / 4) /* 3c */
    ) {
	ListRep newRep;
	Tcl_Obj **toObjs;
	listObjs = &listRep.storePtr->slots[ListRepStart(&listRep)];
	ListRepInit(origListLen + lenChange,
		    NULL,
		    LISTREP_PANIC_ON_FAIL | favor,
		    &newRep);
	toObjs = ListRepSlotPtr(&newRep, 0);
	if (leadSegmentLen > 0) {
            /* T:listrep-2.{2,3,13:18},4.{6,9,13:18} */
	    ObjArrayCopy(toObjs, leadSegmentLen, listObjs);
	}
	if (numToInsert > 0) {
            /* T:listrep-2.{1,2,3,10:18},4.{1,2,4,6,10:18} */
	    ObjArrayCopy(&toObjs[leadSegmentLen],
			 numToInsert,
			 insertObjs);
	}
	if (tailSegmentLen > 0) {
            /* T:listrep-2.{1,2,3,10:15},4.{1,2,4,6,9:12,16:18} */
	    ObjArrayCopy(&toObjs[leadSegmentLen + numToInsert],

			 tailSegmentLen,
			 &listObjs[leadSegmentLen+numToDelete]);
	}
	newRep.storePtr->numUsed = origListLen + lenChange;
	if (newRep.spanPtr) {
            /* T:listrep-2.{1,2,3,10:18},4.{1,2,4,6,9:18} */
	    newRep.spanPtr->spanLength = newRep.storePtr->numUsed;
	}

	LISTREP_CHECK(&newRep);
	ListObjReplaceRepAndInvalidate(listObj, &newRep);


	return TCL_OK;
    }

    /*
     * Case (4) - unshared ListStore with sufficient room.
     * After deleting elements, there will be a corresponding gap. If this
     * gap does not match number of insertions, either the lead segment,
     * or the tail segment, or both will have to be moved.
     * The general strategy is to move the fewest number of elements. If
     *
     * TODO - what about appends to unshared ? Is below sufficiently optimal?
     */

    /* Following must hold for unshared listreps after ListRepFreeUnreferenced above */
    LIST_ASSERT(origListLen == listRep.storePtr->numUsed);
    LIST_ASSERT(origListLen == ListRepLength(&listRep));
    LIST_ASSERT(ListRepStart(&listRep) == listRep.storePtr->firstUsed);

    LIST_ASSERT((numToDelete + numToInsert) > 0);


    /* Base of slot array holding the list elements */
    listObjs = &listRep.storePtr->slots[ListRepStart(&listRep)];


    /*
     * Free up elements to be deleted. Before that, increment the ref counts
     * for objects to be inserted in case there is overlap. T:listobj-11.1
     */
    if (numToInsert) {
	/* T:listrep-1.{1,3,12:21},3.{2,3,7:14,23:41} */
	ObjArrayIncrRefs(insertObjs, 0, numToInsert);
    }
    if (numToDelete) {
        /* T:listrep-1.{6,7,12:21},3.{19:41} */
	ObjArrayDecrRefs(listObjs, first, numToDelete);
    }

    /*
     * TODO - below the moves are optimized but this may result in needing a
     * span allocation. Perhaps for small lists, it may be more efficient to
     * just move everything up front and save on allocating a span.
     */

    /*
     * Calculate shifts if necessary to accomodate insertions.
     * NOTE: all indices are relative to listObjs which is not necessarily the
     * start of the ListStore storage area.

     *
     * leadShift - how much to shift the lead segment
     * tailShift - how much to shift the tail segment
     * insertTarget - index where to insert.
     */

    if (lenChange == 0) {
	/* T:listrep-1.{12,15,19},3.{23,28,33}. Exact fit */
	leadShift = 0;
	tailShift = 0;
    } else if (lenChange < 0) {
	/*


	 * More deletions than insertions. The gap after deletions is large
	 * enough for insertions. Move a segment depending on size.
	 */
	if (leadSegmentLen > tailSegmentLen) {
	    /* Tail segment smaller. Insert after lead, move tail down */
            /* T:listrep-1.{7,17,20},3.{21,2229,35} */
	    leadShift = 0;
	    tailShift = lenChange;
	} else {
	    /* Lead segment smaller. Insert before tail, move lead up */
            /* T:listrep-1.{6,13,16},3.{19,20,24,34} */
	    leadShift = -lenChange;
	    tailShift = 0;
	}


    } else {
	LIST_ASSERT(lenChange > 0); /* Reminder */

	/*
	 * We need to make room for the insertions. Again we have multiple
	 * possibilities. We may be able to get by just shifting one segment
	 * or need to shift both. In the former case, favor shifting the
	 * smaller segment.
	 */
	ptrdiff_t leadSpace = ListRepNumFreeHead(&listRep);
	ptrdiff_t tailSpace = ListRepNumFreeTail(&listRep);
	ptrdiff_t finalFreeSpace = leadSpace + tailSpace - lenChange;

	LIST_ASSERT((leadSpace + tailSpace) >= lenChange);
	if (leadSpace >= lenChange

	    && (leadSegmentLen < tailSegmentLen || tailSpace < lenChange)) {
	    /* Move only lead to the front to make more room */
            /* T:listrep-3.25,36,38, */
	    leadShift = -lenChange;
	    tailShift = 0;
	    /*
	     * Redistribute the remaining free space between the front and
	     * back if either there is no tail space left or if the
	     * entire list is the head anyways. This is an important
	     * optimization for further operations like further asymmetric
	     * insertions.
	     */
	    if (finalFreeSpace > 1 && (tailSpace == 0 || tailSegmentLen == 0)) {
		ptrdiff_t postShiftLeadSpace = leadSpace - lenChange;
		if (postShiftLeadSpace > (finalFreeSpace/2)) {
		    ListSizeT extraShift = postShiftLeadSpace - (finalFreeSpace / 2);
		    leadShift -= extraShift;
		    tailShift = -extraShift; /* Move tail to the front as well */
		}
	    } /* else T:listrep-3.{7,12,25,38} */
	    LIST_ASSERT(leadShift >= 0 || leadSpace >= -leadShift);
	} else if (tailSpace >= lenChange) {
	    /* Move only tail segment to the back to make more room. */
            /* T:listrep-3.{8,10,11,14,26,27,30,32,37,39,41} */
	    leadShift = 0;
	    tailShift = lenChange;
	    /*
	     * See comments above. This is analogous.
	     */
	    if (finalFreeSpace > 1 && (leadSpace == 0 || leadSegmentLen == 0)) {
		ptrdiff_t postShiftTailSpace = tailSpace - lenChange;
		if (postShiftTailSpace > (finalFreeSpace/2)) {
		    /* T:listrep-1.{1,3,14,18,21},3.{2,3,26,27} */
		    ListSizeT extraShift = postShiftTailSpace - (finalFreeSpace / 2);
		    tailShift += extraShift;
		    leadShift = extraShift; /* Move head to the back as well */
		}


	    }

	    LIST_ASSERT(tailShift <= tailSpace);
	} else {
	    /*
	     * Both lead and tail need to be shifted to make room.
	     * Divide remaining free space equally between front and back.
	     */
            /* T:listrep-3.{9,13,31,40} */
	    LIST_ASSERT(leadSpace < lenChange);
	    LIST_ASSERT(tailSpace < lenChange);

	    /*
	     * leadShift = leadSpace - (finalFreeSpace/2)
	     * Thus leadShift <= leadSpace
	     * Also,
	     * = leadSpace - (leadSpace + tailSpace - lenChange)/2
	     * = leadSpace/2 - tailSpace/2 + lenChange/2
	     * >= 0 because lenChange > tailSpace
	     */
	    leadShift = leadSpace - (finalFreeSpace / 2);
	    tailShift = lenChange - leadShift;
	    if (tailShift > tailSpace) {
		/* Account for integer division errors */
		leadShift += 1;
		tailShift -= 1;
	    }
	    /*


	     * Following must be true because otherwise one of the previous
	     * if clauses would have been taken.
	     */
	    LIST_ASSERT(leadShift > 0 && leadShift < lenChange);
	    LIST_ASSERT(tailShift > 0 && tailShift < lenChange);
	    leadShift = -leadShift; /* Lead is actually shifted downward */
	}
    }


    /* Careful about order of moves! */
    if (leadShift > 0) {

	/* Will happen when we have to make room at bottom */
	if (tailShift != 0 && tailSegmentLen != 0) {
            /* T:listrep-1.{1,3,14,18},3.{2,3,26,27} */
	    ListSizeT tailStart = leadSegmentLen + numToDelete;
	    memmove(&listObjs[tailStart + tailShift],
		    &listObjs[tailStart],
		    tailSegmentLen * sizeof(Tcl_Obj *));
	}
	if (leadSegmentLen != 0) {
            /* T:listrep-1.{3,6,16,18,21},3.{19,20,34} */
	    memmove(&listObjs[leadShift],
		    &listObjs[0],
		    leadSegmentLen * sizeof(Tcl_Obj *));
	}
    } else {
	if (leadShift != 0 && leadSegmentLen != 0) {
            /* T:listrep-3.{7,9,12,13,31,36,38,40} */
	    memmove(&listObjs[leadShift],
		    &listObjs[0],
		    leadSegmentLen * sizeof(Tcl_Obj *));
	}
	if (tailShift != 0 && tailSegmentLen != 0) {



            /* T:listrep-1.{7,17},3.{8:11,13,14,21,22,35,37,39:41} */
	    ListSizeT tailStart = leadSegmentLen + numToDelete;
	    memmove(&listObjs[tailStart + tailShift],
		    &listObjs[tailStart],
		    tailSegmentLen * sizeof(Tcl_Obj *));
	}


    }
    if (numToInsert) {
	/* Do NOT use ObjArrayCopy here since we have already incr'ed ref counts */
        /* T:listrep-1.{1,3,12:21},3.{2,3,7:14,23:41} */
	memmove(&listObjs[leadSegmentLen + leadShift],
		insertObjs,
		numToInsert * sizeof(Tcl_Obj *));
    }




    listRep.storePtr->firstUsed += leadShift;
    listRep.storePtr->numUsed = origListLen + lenChange;
    listRep.storePtr->flags = 0;


    if (listRep.spanPtr && listRep.spanPtr->refCount <= 1) {
	/* An unshared span record, re-use it, even if not required */
        /* T:listrep-3.{2,3,7:14},3.{19:41} */
	listRep.spanPtr->spanStart = listRep.storePtr->firstUsed;
	listRep.spanPtr->spanLength = listRep.storePtr->numUsed;
    } else {
	/* Need a new span record */
	if (listRep.storePtr->firstUsed == 0) {
            /* T:listrep-1.{7,12,15,17,19,20} */
	    listRep.spanPtr = NULL;
	} else {
            /* T:listrep-1.{1,3,6.1,13,14,16,18,21} */
	    listRep.spanPtr = ListSpanNew(listRep.storePtr->firstUsed,
					  listRep.storePtr->numUsed);
	}
    }

    LISTREP_CHECK(&listRep);
    ListObjReplaceRepAndInvalidate(listObj, &listRep);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TclLindexList --
 *
 *	This procedure handles the 'lindex' command when objc==3.
 *
 * Results:
 *	Returns a pointer to the object extracted, or NULL if an error
 *	occurred. The returned object already includes one reference count for
 *	the pointer returned.
 *
 * Side effects:
 *	None.


 *
 * Notes:
 *	This procedure is implemented entirely as a wrapper around
 *	TclLindexFlat. All it does is reconfigure the argument format into the
 *	form required by TclLindexFlat, while taking care to manage shimmering
 *	in such a way that we tend to keep the most useful internalreps and/or
 *	avoid the most expensive conversions.
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclLindexList(
    Tcl_Interp *interp,		/* Tcl interpreter. */
    Tcl_Obj *listObj,		/* List being unpacked. */
    Tcl_Obj *argObj)		/* Index or index list. */
{

    ListSizeT index;			/* Index into the list. */
    Tcl_Obj *indexListCopy;
    Tcl_Obj **indexObjs;
    ListSizeT numIndexObjs;

    /*
     * Determine whether argPtr designates a list or a single index. We have
     * to be careful about the order of the checks to avoid repeated
     * shimmering; if internal rep is already a list do not shimmer it.
     * see TIP#22 and TIP#33 for the details.
     */

    if (!TclHasInternalRep(argObj, &tclListType)

	&& TclGetIntForIndexM(NULL, argObj, ListSizeT_MAX - 1, &index)
	       == TCL_OK) {
	/*
	 * argPtr designates a single index.
	 */

	return TclLindexFlat(interp, listObj, 1, &argObj);
    }

    /*
     * Here we make a private copy of the index list argument to avoid any
     * shimmering issues that might invalidate the indices array below while
     * we are still using it. This is probably unnecessary. It does not appear
     * that any damaging shimmering is possible, and no test has been devised
     * to show any error when this private copy is not made. But it's cheap,
     * and it offers some future-proofing insurance in case the TclLindexFlat
     * implementation changes in some unexpected way, or some new form of
     * trace or callback permits things to happen that the current
     * implementation does not.
     */

    indexListCopy = TclListObjCopy(NULL, argObj);
    if (indexListCopy == NULL) {
	/*
	 * The argument is neither an index nor a well-formed list.
	 * Report the error via TclLindexFlat.
	 * TODO - This is as original. why not directly return an error?
	 */

	return TclLindexFlat(interp, listObj, 1, &argObj);
    }


    ListObjGetElements(indexListCopy, numIndexObjs, indexObjs);


    listObj = TclLindexFlat(interp, listObj, numIndexObjs, indexObjs);

    Tcl_DecrRefCount(indexListCopy);
    return listObj;
}

/*
 *----------------------------------------------------------------------
 *
 * TclLindexFlat --
 *
 *	This procedure is the core of the 'lindex' command, with all index
 *	arguments presented as a flat list.
 *
 * Results:
 *	Returns a pointer to the object extracted, or NULL if an error
 *	occurred. The returned object already includes one reference count for
 *	the pointer returned.
 *


 * Side effects:
 *	None.
 *
 * Notes:
 *	The reference count of the returned object includes one reference
 *	corresponding to the pointer returned. Thus, the calling code will
 *	usually do something like:
 *		Tcl_SetObjResult(interp, result);
 *		Tcl_DecrRefCount(result);
 *

 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclLindexFlat(
    Tcl_Interp *interp,		/* Tcl interpreter. */
    Tcl_Obj *listObj,		/* Tcl object representing the list. */
    ListSizeT indexCount,		/* Count of indices. */
    Tcl_Obj *const indexArray[])/* Array of pointers to Tcl objects that
				 * represent the indices in the list. */
{
    ListSizeT i;

    /* Handle ArithSeries as special case */
    if (TclHasInternalRep(listObj,&tclArithSeriesType)) {
	ListSizeT index, listLen = TclArithSeriesObjLength(listObj);
	Tcl_Obj *elemObj = NULL;
	for (i=0 ; i<indexCount && listObj ; i++) {
	    if (TclGetIntForIndexM(interp, indexArray[i], /*endValue*/ listLen-1,
				   &index) == TCL_OK) {
	    }
	    if (i==0) {
		TclArithSeriesObjIndex(listObj, index, &elemObj);
		Tcl_IncrRefCount(elemObj);
	    } else if (index > 0) {
		Tcl_DecrRefCount(elemObj);
		TclNewObj(elemObj);
		Tcl_IncrRefCount(elemObj);
		break;
	    }
	}
	return elemObj;
    }

    Tcl_IncrRefCount(listObj);

    for (i=0 ; i<indexCount && listObj ; i++) {
	ListSizeT index, listLen = 0;
	Tcl_Obj **elemPtrs = NULL, *sublistCopy;

	/*
	 * Here we make a private copy of the current sublist, so we avoid any
	 * shimmering issues that might invalidate the elemPtr array below
	 * while we are still using it. See test lindex-8.4.
	 */

	sublistCopy = TclListObjCopy(interp, listObj);
	Tcl_DecrRefCount(listObj);
	listObj = NULL;

	if (sublistCopy == NULL) {

	    /* The sublist is not a list at all => error.  */


	    break;
	}
	LIST_ASSERT_TYPE(sublistCopy);
	ListObjGetElements(sublistCopy, listLen, elemPtrs);

	if (TclGetIntForIndexM(interp, indexArray[i], /*endValue*/ listLen-1,
		&index) == TCL_OK) {
	    if (index >= listLen) {
		/*
		 * Index is out of range. Break out of loop with empty result.
		 * First check remaining indices for validity
		 */

		while (++i < indexCount) {
		    if (TclGetIntForIndexM(
			    interp, indexArray[i], ListSizeT_MAX - 1, &index)
			!= TCL_OK) {
			Tcl_DecrRefCount(sublistCopy);
			return NULL;
		    }
		}
		TclNewObj(listObj);
	    } else {

		/* Extract the pointer to the appropriate element. */


		listObj = elemPtrs[index];
	    }
	    Tcl_IncrRefCount(listObj);
	}
	Tcl_DecrRefCount(sublistCopy);
    }

    return listObj;
}

/*
 *----------------------------------------------------------------------
 *
 * TclLsetList --
 *
 *	Core of the 'lset' command when objc == 4. Objv[2] may be either a
 *	scalar index or a list of indices.
 *      It also handles 'lpop' when given a NULL value.
 *
 * Results:
 *	Returns the new value of the list variable, or NULL if there was an
 *	error. The returned object includes one reference count for the
 *	pointer returned.
 *
 * Side effects:
 *	None.
 *

 * Notes:
 *	This procedure is implemented entirely as a wrapper around
 *	TclLsetFlat. All it does is reconfigure the argument format into the
 *	form required by TclLsetFlat, while taking care to manage shimmering
 *	in such a way that we tend to keep the most useful internalreps and/or
 *	avoid the most expensive conversions.
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclLsetList(
    Tcl_Interp *interp,		/* Tcl interpreter. */
    Tcl_Obj *listObj,		/* Pointer to the list being modified. */
    Tcl_Obj *indexArgObj,	/* Index or index-list arg to 'lset'. */
    Tcl_Obj *valueObj)		/* Value arg to 'lset' or NULL to 'lpop'. */
{
    ListSizeT indexCount = 0;   /* Number of indices in the index list. */
    Tcl_Obj **indices = NULL;	/* Vector of indices in the index list. */
    Tcl_Obj *retValueObj;	/* Pointer to the list to be returned. */
    ListSizeT index;            /* Current index in the list - discarded. */
    Tcl_Obj *indexListCopy;


    /*
     * Determine whether the index arg designates a list or a single index.
     * We have to be careful about the order of the checks to avoid repeated
     * shimmering; see TIP #22 and #23 for details.
     */

    if (!TclHasInternalRep(indexArgObj, &tclListType)

	&& TclGetIntForIndexM(NULL, indexArgObj, ListSizeT_MAX - 1, &index)

	       == TCL_OK) {
	/* indexArgPtr designates a single index. */

        /* T:listrep-1.{2.1,12.1,15.1,19.1},2.{2.3,9.3,10.1,13.1,16.1}, 3.{4,5,6}.3 */
	return TclLsetFlat(interp, listObj, 1, &indexArgObj, valueObj);
    }


    indexListCopy = TclListObjCopy(NULL, indexArgObj);
    if (indexListCopy == NULL) {
	/*
	 * indexArgPtr designates something that is neither an index nor a
	 * well formed list. Report the error via TclLsetFlat.
	 */

	return TclLsetFlat(interp, listObj, 1, &indexArgObj, valueObj);
    }
    LIST_ASSERT_TYPE(indexListCopy);
    ListObjGetElements(indexListCopy, indexCount, indices);

    /*
     * Let TclLsetFlat handle the actual lset'ting.
     */

    retValueObj = TclLsetFlat(interp, listObj, indexCount, indices, valueObj);

    Tcl_DecrRefCount(indexListCopy);
    return retValueObj;
}

/*
 *----------------------------------------------------------------------
 *
 * TclLsetFlat --
 *
 *	Core engine of the 'lset' command.
 *      It also handles 'lpop' when given a NULL value.
 *
 * Results:
 *	Returns the new value of the list variable, or NULL if an error
 *	occurred. The returned object includes one reference count for the
 *	pointer returned.
 *

 * Side effects:
 *	On entry, the reference count of the variable value does not reflect
 *	any references held on the stack. The first action of this function is
 *	to determine whether the object is shared, and to duplicate it if it
 *	is. The reference count of the duplicate is incremented. At this


 *	point, the reference count will be 1 for either case, so that the


 *	object will appear to be unshared.
 *
 *	If an error occurs, and the object has been duplicated, the reference
 *	count on the duplicate is decremented so that it is now 0: this
 *	dismisses any memory that was allocated by this function.
 *



 *	If no error occurs, the reference count of the original object is

 *	incremented if the object has not been duplicated, and nothing is done
 *	to a reference count of the duplicate. Now the reference count of an
 *	unduplicated object is 2 (the returned pointer, plus the one stored in
 *	the variable). The reference count of a duplicate object is 1,
 *	reflecting that the returned pointer is the only active reference. The
 *	caller is expected to store the returned value back in the variable
 *	and decrement its reference count. (INST_STORE_* does exactly this.)
 *







 *----------------------------------------------------------------------
 */

Tcl_Obj *
TclLsetFlat(
    Tcl_Interp *interp,		/* Tcl interpreter. */
    Tcl_Obj *listObj,		/* Pointer to the list being modified. */
    ListSizeT indexCount,		/* Number of index args. */
    Tcl_Obj *const indexArray[],
				/* Index args. */
    Tcl_Obj *valueObj)		/* Value arg to 'lset' or NULL to 'lpop'. */
{
    ListSizeT index, len;
	int result;
    Tcl_Obj *subListObj, *retValueObj;
    Tcl_Obj *pendingInvalidates[10];
    Tcl_Obj **pendingInvalidatesPtr = pendingInvalidates;
    ListSizeT numPendingInvalidates = 0;

    /*
     * If there are no indices, simply return the new value.  (Without
     * indices, [lset] is a synonym for [set].
     * [lpop] does not use this but protect for NULL valueObj just in case.
     */

    if (indexCount == 0) {
	if (valueObj != NULL) {
	    Tcl_IncrRefCount(valueObj);
	}
	return valueObj;
    }

    /*
     * If the list is shared, make a copy we can modify (copy-on-write).  We
     * use Tcl_DuplicateObj() instead of TclListObjCopy() for a few reasons:
     * 1) we have not yet confirmed listObj is actually a list; 2) We make a
     * verbatim copy of any existing string rep, and when we combine that with
     * the delayed invalidation of string reps of modified Tcl_Obj's
     * implemented below, the outcome is that any error condition that causes
     * this routine to return NULL, will leave the string rep of listObj and
     * all elements to be unchanged.
     */

    subListObj = Tcl_IsShared(listObj) ? Tcl_DuplicateObj(listObj) : listObj;

    /*
     * Anchor the linked list of Tcl_Obj's whose string reps must be
     * invalidated if the operation succeeds.
     */

    retValueObj = subListObj;

    result = TCL_OK;

    /* Allocate if static array for pending invalidations is too small */
    if (indexCount
        > (int) (sizeof(pendingInvalidates) / sizeof(pendingInvalidates[0]))) {
	pendingInvalidatesPtr =
	    (Tcl_Obj **) Tcl_Alloc(indexCount * sizeof(*pendingInvalidatesPtr));
    }

    /*
     * Loop through all the index arguments, and for each one dive into the
     * appropriate sublist.
     */

    do {
	ListSizeT elemCount;
	Tcl_Obj *parentList, **elemPtrs;

	/*
	 * Check for the possible error conditions...
	 */

	if (TclListObjGetElementsM(interp, subListObj, &elemCount, &elemPtrs)
	    != TCL_OK) {
	    /* ...the sublist we're indexing into isn't a list at all. */
	    result = TCL_ERROR;
	    break;
	}

	/*
	 * WARNING: the macro TclGetIntForIndexM is not safe for
	 * post-increments, avoid '*indexArray++' here.
	 */

	if (TclGetIntForIndexM(interp, *indexArray, elemCount - 1, &index)
	    != TCL_OK) {
	    /* ...the index we're trying to use isn't an index at all. */
	    result = TCL_ERROR;
	    indexArray++; /* Why bother with this increment? TBD */
	    break;
	}
	indexArray++;

	if (index > elemCount
	    || (valueObj == NULL && index >= elemCount)) {
	    /* ...the index points outside the sublist. */
	    if (interp != NULL) {
		Tcl_SetObjResult(interp,
		                 Tcl_ObjPrintf("index \"%s\" out of range",
		                               Tcl_GetString(indexArray[-1])));
		Tcl_SetErrorCode(interp,
		                 "TCL",
		                 "VALUE",
		                 "INDEX"
		                 "OUTOFRANGE",
		                 NULL);
	    }
	    result = TCL_ERROR;
	    break;
	}

	/*
	 * No error conditions.  As long as we're not yet on the last index,
	 * determine the next sublist for the next pass through the loop,
	 * and take steps to make sure it is an unshared copy, as we intend
	 * to modify it.
	 */

	if (--indexCount) {
	    parentList = subListObj;
	    if (index == elemCount) {
		TclNewObj(subListObj);
	    } else {
		subListObj = elemPtrs[index];
	    }
	    if (Tcl_IsShared(subListObj)) {
		subListObj = Tcl_DuplicateObj(subListObj);
	    }

	    /*
	     * Replace the original elemPtr[index] in parentList with a copy
	     * we know to be unshared.  This call will also deal with the
	     * situation where parentList shares its internalrep with other
	     * Tcl_Obj's.  Dealing with the shared internalrep case can
	     * cause subListObj to become shared again, so detect that case
	     * and make and store another copy.
	     */

	    if (index == elemCount) {
		Tcl_ListObjAppendElement(NULL, parentList, subListObj);
	    } else {
		TclListObjSetElement(NULL, parentList, index, subListObj);
	    }
	    if (Tcl_IsShared(subListObj)) {
		subListObj = Tcl_DuplicateObj(subListObj);
		TclListObjSetElement(NULL, parentList, index, subListObj);
	    }

	    /*
	     * The TclListObjSetElement() calls do not spoil the string rep
	     * of parentList, and that's fine for now, since all we've done
	     * so far is replace a list element with an unshared copy.  The
	     * list value remains the same, so the string rep. is still
	     * valid, and unchanged, which is good because if this whole
	     * routine returns NULL, we'd like to leave no change to the
	     * value of the lset variable.  Later on, when we set valueObj
	     * in its proper place, then all containing lists will have
	     * their values changed, and will need their string reps
	     * spoiled.  We maintain a list of all those Tcl_Obj's (via a
	     * little internalrep surgery) so we can spoil them at that
	     * time.
	     */



	    pendingInvalidatesPtr[numPendingInvalidates] = parentList;
	    ++numPendingInvalidates;
	}
    } while (indexCount > 0);

    /*
     * Either we've detected and error condition, and exited the loop with
     * result == TCL_ERROR, or we've successfully reached the last index, and
     * we're ready to store valueObj. On success, we need to invalidate
     * the string representations of intermediate lists whose contained
     * list element would have changed.
     */
    if (result == TCL_OK) {
	while (numPendingInvalidates > 0) {
	    Tcl_Obj *objPtr;





	    --numPendingInvalidates;

	    objPtr = pendingInvalidatesPtr[numPendingInvalidates];


	    if (result == TCL_OK) {

		/*
		 * We're going to store valueObj, so spoil string reps of all
		 * containing lists.
		 * TODO - historically, the storing of the internal rep was done
		 * because the ptr2 field of the internal rep was used to chain
		 * objects whose string rep needed to be invalidated. Now this
		 * is no longer the case, so replacing of the internal rep
		 * should not be needed. The TclInvalidateStringRep should
		 * suffice. Formulate a test case before changing.
		 */
		ListRep objInternalRep;
		TclListObjGetRep(NULL, objPtr, &objInternalRep);
		ListObjReplaceRepAndInvalidate(objPtr, &objInternalRep);
	    }




	}



    }

    if (pendingInvalidatesPtr != pendingInvalidates)
	Tcl_Free(pendingInvalidatesPtr);

    if (result != TCL_OK) {
	/*
	 * Error return; message is already in interp. Clean up any excess
	 * memory.
	 */

	if (retValueObj != listObj) {
	    Tcl_DecrRefCount(retValueObj);
	}
	return NULL;
    }

    /*
     * Store valueObj in proper sublist and return. The -1 is to avoid a
     * compiler warning (not a problem because we checked that we have a
     * proper list - or something convertible to one - above).
     */

    len = -1;
    TclListObjLengthM(NULL, subListObj, &len);
    if (valueObj == NULL) {
        /* T:listrep-1.{4.2,5.4,6.1,7.1,8.3},2.{4,5}.4 */
	Tcl_ListObjReplace(NULL, subListObj, index, 1, 0, NULL);
    } else if (index == len) {
        /* T:listrep-1.2.1,2.{2.3,9.3},3.{4,5,6}.3 */
	Tcl_ListObjAppendElement(NULL, subListObj, valueObj);
    } else {
        /* T:listrep-1.{12.1,15.1,19.1},2.{10,13,16}.1 */
	TclListObjSetElement(NULL, subListObj, index, valueObj);
	TclInvalidateStringRep(subListObj);
    }
    Tcl_IncrRefCount(retValueObj);
    return retValueObj;
}

/*
 *----------------------------------------------------------------------
 *
 * TclListObjSetElement --
 *
 *	Set a single element of a list to a specified value
 *



 * Results:

 *	The return value is normally TCL_OK. If listObj does not refer to a





 *	list object and cannot be converted to one, TCL_ERROR is returned and
 *	an error message will be left in the interpreter result if interp is




 *	not NULL. Similarly, if index designates an element outside the range
 *	[0..listLength-1], where listLength is the count of elements in the
 *	list object designated by listObj, TCL_ERROR is returned and an error
 *	message is left in the interpreter result.
 *
 * Side effects:

 *	Tcl_Panic if listObj designates a shared object. Otherwise, attempts
 *	to convert it to a list with a non-shared internal rep. Decrements the

 *	ref count of the object at the specified index within the list,
 *	replaces with the object designated by valueObj, and increments the
 *	ref count of the replacement object.
 *

 *----------------------------------------------------------------------
 */

int
TclListObjSetElement(
    Tcl_Interp *interp,		/* Tcl interpreter; used for error reporting
				 * if not NULL. */
    Tcl_Obj *listObj,		/* List object in which element should be
				 * stored. */
    ListSizeT index,		/* Index of element to store. */
    Tcl_Obj *valueObj)		/* Tcl object to store in the designated list
				 * element. */
{
    ListRep listRep;

    Tcl_Obj **elemPtrs;         /* Pointers to elements of the list. */
    ListSizeT elemCount;		/* Number of elements in the list. */


    /* Ensure that the listObj parameter designates an unshared list. */


    if (Tcl_IsShared(listObj)) {
	Tcl_Panic("%s called with shared object", "TclListObjSetElement");
    }





    if (TclListObjGetRep(interp, listObj, &listRep) != TCL_OK) {








	return TCL_ERROR;
    }







    elemCount = ListRepLength(&listRep);


    /* Ensure that the index is in bounds. */


    if (index>=elemCount) {
	if (interp != NULL) {
		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
			"index \"%" TCL_Z_MODIFIER "u\" out of range", index));
	    Tcl_SetErrorCode(interp, "TCL", "VALUE", "INDEX",
		    "OUTOFRANGE", NULL);
	}
	return TCL_ERROR;
    }

    /*
     * Note - garbage collect this only AFTER checking indices above.
     * Do not want to modify listrep and then not store it back in listObj.
     */
    ListRepFreeUnreferenced(&listRep);

    /* Replace a shared internal rep with an unshared copy */
    if (listRep.storePtr->refCount > 1) {
	ListRep newInternalRep;

        /* T:listrep-2.{10,13,16}.1 */









	/* TODO - leave extra space? */





	ListRepClone(&listRep, &newInternalRep, LISTREP_PANIC_ON_FAIL);
	listRep = newInternalRep;
    } /* else T:listrep-1.{12.1,15.1,19.1} */








    /* Retrieve element array AFTER potential cloning above */
    ListRepElements(&listRep, elemCount, elemPtrs);

    /*
     * Add a reference to the new list element and remove from old before
     * replacing it. Order is important!
     */
    Tcl_IncrRefCount(valueObj);
    Tcl_DecrRefCount(elemPtrs[index]);





    elemPtrs[index] = valueObj;




    /* Internal rep may be cloned so replace */





    ListObjReplaceRepAndInvalidate(listObj, &listRep);


    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * FreeListInternalRep --
 *
 *	Deallocate the storage associated with a list object's internal
 *	representation.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Frees listPtr's List* internal representation, if no longer shared.
 *	May decrement the ref counts of element objects, which may free them.
 *
 *----------------------------------------------------------------------
 */

static void
FreeListInternalRep(
    Tcl_Obj *listObj)		/* List object with internal rep to free. */
{
    ListRep listRep;



    ListObjGetRep(listObj, &listRep);
    if (listRep.storePtr->refCount-- <= 1) {
	ObjArrayDecrRefs(
	    listRep.storePtr->slots,
	    listRep.storePtr->firstUsed, listRep.storePtr->numUsed);

	Tcl_Free(listRep.storePtr);
    }
    if (listRep.spanPtr) {
	ListSpanDecrRefs(listRep.spanPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * DupListInternalRep --
 *
 *	Initialize the internal representation of a list Tcl_Obj to share the
 *	internal representation of an existing list object.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The reference count of the List internal rep is incremented.
 *
 *----------------------------------------------------------------------
 */

static void
DupListInternalRep(
    Tcl_Obj *srcObj,		/* Object with internal rep to copy. */
    Tcl_Obj *copyObj)		/* Object with internal rep to set. */
{
    ListRep listRep;
    ListObjGetRep(srcObj, &listRep);

    ListObjOverwriteRep(copyObj, &listRep);

}

/*
 *----------------------------------------------------------------------
 *
 * SetListFromAny --
 *
 *	Attempt to generate a list internal form for the Tcl object "objPtr".
 *
 * Results:

 *	The return value is TCL_OK or TCL_ERROR. If an error occurs during

 *	conversion, an error message is left in the interpreter's result

 *	unless "interp" is NULL.
 *
 * Side effects:

 *	If no error occurs, a list is stored as "objPtr"s internal

 *	representation.
 *
 *----------------------------------------------------------------------
 */

static int
SetListFromAny(
    Tcl_Interp *interp,		/* Used for error reporting if not NULL. */
    Tcl_Obj *objPtr)		/* The object to convert. */
{

    Tcl_Obj **elemPtrs;
    ListRep listRep;

    /*
     * Dictionaries are a special case; they have a string representation such
     * that *all* valid dictionaries are valid lists. Hence we can convert
     * more directly. Only do this when there's no existing string rep; if
     * there is, it is the string rep that's authoritative (because it could
     * describe duplicate keys).
     */

    if (!TclHasStringRep(objPtr) && TclHasInternalRep(objPtr, &tclDictType)) {
	Tcl_Obj *keyPtr, *valuePtr;
	Tcl_DictSearch search;
	int done;
	ListSizeT size;

	/*
	 * Create the new list representation. Note that we do not need to do
	 * anything with the string representation as the transformation (and
	 * the reverse back to a dictionary) are both order-preserving. Also
	 * note that since we know we've got a valid dictionary (by
	 * representation) we also know that fetching the size of the
	 * dictionary or iterating over it will not fail.
	 */

	Tcl_DictObjSize(NULL, objPtr, &size);
	/* TODO - leave space in front and/or back? */
	if (ListRepInitAttempt(
		interp, size > 0 ? 2 * size : 1, NULL, &listRep)
	    != TCL_OK) {
	    return TCL_ERROR;
	}

	LIST_ASSERT(listRep.spanPtr == NULL); /* Guard against future changes */
	LIST_ASSERT(listRep.storePtr->firstUsed == 0);
	LIST_ASSERT((listRep.storePtr->flags & LISTSTORE_CANONICAL) == 0);

	listRep.storePtr->numUsed = 2 * size;

	/* Populate the list representation. */


	elemPtrs = listRep.storePtr->slots;
	Tcl_DictObjFirst(NULL, objPtr, &search, &keyPtr, &valuePtr, &done);
	while (!done) {
	    *elemPtrs++ = keyPtr;
	    *elemPtrs++ = valuePtr;
	    Tcl_IncrRefCount(keyPtr);
	    Tcl_IncrRefCount(valuePtr);
	    Tcl_DictObjNext(&search, &keyPtr, &valuePtr, &done);
	}
    } else if (TclHasInternalRep(objPtr,&tclArithSeriesType)) {
	/*
	 * Convertion from Arithmetic Series is a special case
	 * because it can be done an order of magnitude faster
	 * and may occur frequently.
	 */
        ListSizeT j, size = TclArithSeriesObjLength(objPtr);

	/* TODO - leave space in front and/or back? */
	if (ListRepInitAttempt(
		interp, size > 0 ? size : 1, NULL, &listRep)
	    != TCL_OK) {
	    return TCL_ERROR;
	}

	LIST_ASSERT(listRep.spanPtr == NULL); /* Guard against future changes */
	LIST_ASSERT(listRep.storePtr->firstUsed == 0);
	LIST_ASSERT((listRep.storePtr->flags & LISTSTORE_CANONICAL) == 0);

	listRep.storePtr->numUsed = size;
	elemPtrs = listRep.storePtr->slots;
	for (j = 0; j < size; j++) {
	    if (TclArithSeriesObjIndex(objPtr, j, &elemPtrs[j]) != TCL_OK) {
		return TCL_ERROR;
	    }
	    Tcl_IncrRefCount(elemPtrs[j]);/* Since list now holds ref to it. */
	}

    } else {
	ListSizeT estCount, length;
	const char *limit, *nextElem = Tcl_GetStringFromObj(objPtr, &length);

	/*
	 * Allocate enough space to hold a (Tcl_Obj *) for each
	 * (possible) list element.
	 */

	estCount = TclMaxListLength(nextElem, length, &limit);
	estCount += (estCount == 0);	/* Smallest list struct holds 1
					 * element. */
	/* TODO - allocate additional space? */
	if (ListRepInitAttempt(interp, estCount, NULL, &listRep)
	    != TCL_OK) {
	    return TCL_ERROR;
	}

	LIST_ASSERT(listRep.spanPtr == NULL); /* Guard against future changes */
	LIST_ASSERT(listRep.storePtr->firstUsed == 0);

	elemPtrs = listRep.storePtr->slots;


	/* Each iteration, parse and store a list element. */


	while (nextElem < limit) {
	    const char *elemStart;
	    char *check;
	    ListSizeT elemSize;
	    int literal;

	    if (TCL_OK != TclFindElement(interp, nextElem, limit - nextElem,
		    &elemStart, &nextElem, &elemSize, &literal)) {
fail:
		while (--elemPtrs >= listRep.storePtr->slots) {
		    Tcl_DecrRefCount(*elemPtrs);
		}
		Tcl_Free(listRep.storePtr);
		return TCL_ERROR;
	    }
	    if (elemStart == limit) {
		break;
	    }

	    TclNewObj(*elemPtrs);
	    TclInvalidateStringRep(*elemPtrs);
	    check = Tcl_InitStringRep(*elemPtrs, literal ? elemStart : NULL,
		    elemSize);
	    if (elemSize && check == NULL) {
		MemoryAllocationError(interp, elemSize);




		goto fail;
	    }
	    if (!literal) {
		Tcl_InitStringRep(*elemPtrs, NULL,
			TclCopyAndCollapse(elemSize, elemStart, check));
	    }

	    Tcl_IncrRefCount(*elemPtrs++);/* Since list now holds ref to it. */
	}

	listRep.storePtr->numUsed =
	    elemPtrs - listRep.storePtr->slots;
    }

    LISTREP_CHECK(&listRep);

    /*
     * Store the new internalRep. We do this as late
     * as possible to allow the conversion code, in particular
     * Tcl_GetStringFromObj, to use the old internalRep.
     */

    /*
     * Note old string representation NOT to be invalidated.
     * So do NOT use ListObjReplaceRepAndInvalidate. InternalRep to be freed AFTER
     * IncrRefs so do not use ListObjOverwriteRep
     */
    ListRepIncrRefs(&listRep);
    TclFreeInternalRep(objPtr);
    objPtr->internalRep.twoPtrValue.ptr1 = listRep.storePtr;
    objPtr->internalRep.twoPtrValue.ptr2 = listRep.spanPtr;
    objPtr->typePtr = &tclListType;

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * UpdateStringOfList --
 *
 *	Update the string representation for a list object. Note: This

 *	function does not invalidate an existing old string rep so storage
 *	will be lost if this has not already been done.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The object's string is set to a valid string that results from the
 *	list-to-string conversion. This string will be empty if the list has
 *	no elements. The list internal representation should not be NULL and
 *	we assume it is not NULL.
 *
 *----------------------------------------------------------------------
 */

static void
UpdateStringOfList(
    Tcl_Obj *listObj)		/* List object with string rep to update. */
{
#   define LOCAL_SIZE 64
    char localFlags[LOCAL_SIZE], *flagPtr = NULL;
    ListSizeT numElems, i, length, bytesNeeded = 0;
    const char *elem, *start;
    char *dst;
    Tcl_Obj **elemPtrs;
    ListRep listRep;


    ListObjGetRep(listObj, &listRep);
    LISTREP_CHECK(&listRep);

    ListRepElements(&listRep, numElems, elemPtrs);

    /*
     * Mark the list as being canonical; although it will now have a string
     * rep, it is one we derived through proper "canonical" quoting and so
     * it's known to be free from nasties relating to [concat] and [eval].
     * However, we only do this if this is not a spanned list. Marking the
     * storage canonical for a spanned list make ALL lists using the storage
     * canonical which is not right. (Consider a list generated from a
     * string and then this function called for a spanned list generated
     * from it). On the other hand, a spanned list is always canonical
     * (never generated from a string) so it does not have to be explicitly
     * marked as such. The ListObjIsCanonical macro takes this into account.
     * See the comments there.
     */
    if (listRep.spanPtr == NULL) {
	LIST_ASSERT(listRep.storePtr->firstUsed == 0);/* Invariant */
	listRep.storePtr->flags |= LISTSTORE_CANONICAL;
    }



    /* Handle empty list case first, so rest of the routine is simpler. */


    if (numElems == 0) {
	Tcl_InitStringRep(listObj, NULL, 0);
	return;
    }


    /* Pass 1: estimate space, gather flags. */


    if (numElems <= LOCAL_SIZE) {
	flagPtr = localFlags;
    } else {

	/* We know numElems <= LIST_MAX, so this is safe. */


	flagPtr = (char *)Tcl_Alloc(numElems);
    }

    for (i = 0; i < numElems; i++) {
	flagPtr[i] = (i ? TCL_DONT_QUOTE_HASH : 0);
	elem = Tcl_GetStringFromObj(elemPtrs[i], &length);
	bytesNeeded += TclScanElement(elem, length, flagPtr+i);
	if (bytesNeeded > SIZE_MAX - numElems) {
	    Tcl_Panic("max size for a Tcl value (%" TCL_Z_MODIFIER "u bytes) exceeded", SIZE_MAX);
	}
    }
    bytesNeeded += numElems - 1;

    /*
     * Pass 2: copy into string rep buffer.
     */

    start = dst = Tcl_InitStringRep(listObj, NULL, bytesNeeded);
    TclOOM(dst, bytesNeeded);
    for (i = 0; i < numElems; i++) {
	flagPtr[i] |= (i ? TCL_DONT_QUOTE_HASH : 0);
	elem = Tcl_GetStringFromObj(elemPtrs[i], &length);
	dst += TclConvertElement(elem, length, dst, flagPtr[i]);
	*dst++ = ' ';
    }

    /* Set the string length to what was actually written, the safe choice */
    (void) Tcl_InitStringRep(listObj, NULL, dst - 1 - start);

    if (flagPtr != localFlags) {
	Tcl_Free(flagPtr);
    }
}


/*
 *------------------------------------------------------------------------
 *
 * TclListTestObj --
 *
 *    Returns a list object with a specific internal rep and content.
 *    Used specifically for testing so span can be controlled explicitly.
 *
 * Results:
 *    Pointer to the Tcl_Obj containing the list.
 *
 * Side effects:
 *    None.
 *
 *------------------------------------------------------------------------
 */
Tcl_Obj *
TclListTestObj (int length, int leadingSpace, int endSpace)
{
    if (length < 0)
	length = 0;
    if (leadingSpace < 0)
	leadingSpace = 0;
    if (endSpace < 0)
	endSpace = 0;

    ListRep listRep;
    ListSizeT capacity;
    Tcl_Obj *listObj;

    TclNewObj(listObj);

    /* Only a test object so ignoring overflow checks */
    capacity = length + leadingSpace + endSpace;
    if (capacity == 0) {
	return listObj;
    }

    ListRepInit(capacity, NULL, 0, &listRep);

    ListStore *storePtr = listRep.storePtr;
    int i;
    for (i = 0; i < length; ++i) {
	storePtr->slots[i + leadingSpace] = Tcl_NewIntObj(i);
	Tcl_IncrRefCount(storePtr->slots[i + leadingSpace]);
    }
    storePtr->firstUsed = leadingSpace;
    storePtr->numUsed = length;
    if (leadingSpace != 0) {
	listRep.spanPtr = ListSpanNew(leadingSpace, length);
    }
    ListObjReplaceRepAndInvalidate(listObj, &listRep);
    return listObj;
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */
Changes to generic/tclOO.c.
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
    Tcl_Interp *interp,		/* Interpreter context. */
    Tcl_Class cls,		/* Class to create an instance of. */
    const char *nameStr,	/* Name of object to create, or NULL to ask
				 * the code to pick its own unique name. */
    const char *nsNameStr,	/* Name of namespace to create inside object,
				 * or NULL to ask the code to pick its own
				 * unique name. */
    size_t objc1,			/* Number of arguments. Negative value means
				 * do not call constructor. */
    Tcl_Obj *const *objv,	/* Argument list. */
    int skip)			/* Number of arguments to _not_ pass to the
				 * constructor. */
{
    Class *classPtr = (Class *) cls;
    Object *oPtr;
    ClientData clientData[4];
    int objc = objc1;

    oPtr = TclNewObjectInstanceCommon(interp, classPtr, nameStr, nsNameStr);
    if (oPtr == NULL) {
	return NULL;
    }

    /*
     * Run constructors, except when objc < 0, which is a special flag case
     * used for object cloning only.
     */

    if (objc >= 0) {
	CallContext *contextPtr =
		TclOOGetCallContext(oPtr, NULL, CONSTRUCTOR, NULL, NULL, NULL);

	if (contextPtr != NULL) {
	    int isRoot, result;
	    Tcl_InterpState state;








|


|





<











|







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
    Tcl_Interp *interp,		/* Interpreter context. */
    Tcl_Class cls,		/* Class to create an instance of. */
    const char *nameStr,	/* Name of object to create, or NULL to ask
				 * the code to pick its own unique name. */
    const char *nsNameStr,	/* Name of namespace to create inside object,
				 * or NULL to ask the code to pick its own
				 * unique name. */
    size_t objc,			/* Number of arguments. Negative value means
				 * do not call constructor. */
    Tcl_Obj *const *objv,	/* Argument list. */
    size_t skip)			/* Number of arguments to _not_ pass to the
				 * constructor. */
{
    Class *classPtr = (Class *) cls;
    Object *oPtr;
    ClientData clientData[4];


    oPtr = TclNewObjectInstanceCommon(interp, classPtr, nameStr, nsNameStr);
    if (oPtr == NULL) {
	return NULL;
    }

    /*
     * Run constructors, except when objc < 0, which is a special flag case
     * used for object cloning only.
     */

    if (objc != TCL_INDEX_NONE) {
	CallContext *contextPtr =
		TclOOGetCallContext(oPtr, NULL, CONSTRUCTOR, NULL, NULL, NULL);

	if (contextPtr != NULL) {
	    int isRoot, result;
	    Tcl_InterpState state;

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
    Tcl_Interp *interp,		/* Interpreter context. */
    Tcl_Class cls,		/* Class to create an instance of. */
    const char *nameStr,	/* Name of object to create, or NULL to ask
				 * the code to pick its own unique name. */
    const char *nsNameStr,	/* Name of namespace to create inside object,
				 * or NULL to ask the code to pick its own
				 * unique name. */
    int objc,			/* Number of arguments. Negative value means
				 * do not call constructor. */
    Tcl_Obj *const *objv,	/* Argument list. */
    int skip,			/* Number of arguments to _not_ pass to the
				 * constructor. */
    Tcl_Object *objectPtr)	/* Place to write the object reference upon
				 * successful allocation. */
{
    Class *classPtr = (Class *) cls;
    CallContext *contextPtr;
    Tcl_InterpState state;
    Object *oPtr;

    oPtr = TclNewObjectInstanceCommon(interp, classPtr, nameStr, nsNameStr);
    if (oPtr == NULL) {
	return TCL_ERROR;
    }

    /*
     * Run constructors, except when objc < 0 (a special flag case used for
     * object cloning only). If there aren't any constructors, we do nothing.
     */

    if (objc < 0) {
	*objectPtr = (Tcl_Object) oPtr;
	return TCL_OK;
    }
    contextPtr = TclOOGetCallContext(oPtr, NULL, CONSTRUCTOR, NULL, NULL, NULL);
    if (contextPtr == NULL) {
	*objectPtr = (Tcl_Object) oPtr;
	return TCL_OK;







|


|















|



|







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
    Tcl_Interp *interp,		/* Interpreter context. */
    Tcl_Class cls,		/* Class to create an instance of. */
    const char *nameStr,	/* Name of object to create, or NULL to ask
				 * the code to pick its own unique name. */
    const char *nsNameStr,	/* Name of namespace to create inside object,
				 * or NULL to ask the code to pick its own
				 * unique name. */
    size_t objc,			/* Number of arguments. Negative value means
				 * do not call constructor. */
    Tcl_Obj *const *objv,	/* Argument list. */
    size_t skip,			/* Number of arguments to _not_ pass to the
				 * constructor. */
    Tcl_Object *objectPtr)	/* Place to write the object reference upon
				 * successful allocation. */
{
    Class *classPtr = (Class *) cls;
    CallContext *contextPtr;
    Tcl_InterpState state;
    Object *oPtr;

    oPtr = TclNewObjectInstanceCommon(interp, classPtr, nameStr, nsNameStr);
    if (oPtr == NULL) {
	return TCL_ERROR;
    }

    /*
     * Run constructors, except when objc == TCL_INDEX_NONE (a special flag case used for
     * object cloning only). If there aren't any constructors, we do nothing.
     */

    if (objc == TCL_INDEX_NONE) {
	*objectPtr = (Tcl_Object) oPtr;
	return TCL_OK;
    }
    contextPtr = TclOOGetCallContext(oPtr, NULL, CONSTRUCTOR, NULL, NULL, NULL);
    if (contextPtr == NULL) {
	*objectPtr = (Tcl_Object) oPtr;
	return TCL_OK;
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
 * ----------------------------------------------------------------------
 */

int
TclOOObjectCmdCore(
    Object *oPtr,		/* The object being invoked. */
    Tcl_Interp *interp,		/* The interpreter containing the object. */
    size_t objc1,			/* How many arguments are being passed in. */
    Tcl_Obj *const *objv,	/* The array of arguments. */
    int flags,			/* Whether this is an invocation through the
				 * public or the private command interface. */
    Class *startCls)		/* Where to start in the call chain, or NULL
				 * if we are to start at the front with
				 * filters and the object's methods (which is
				 * the normal case). */
{
    CallContext *contextPtr;
    Tcl_Obj *methodNamePtr;
    CallFrame *framePtr = ((Interp *) interp)->varFramePtr;
    Object *callerObjPtr = NULL;
    Class *callerClsPtr = NULL;
    int result;
    int objc = objc1;

    /*
     * If we've no method name, throw this directly into the unknown
     * processing.
     */

    if (objc < 2) {
	flags |= FORCE_UNKNOWN;
	methodNamePtr = NULL;
	goto noMapping;
    }

    /*
     * Determine if we're in a context that can see the extra, private methods







|














<






|







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

int
TclOOObjectCmdCore(
    Object *oPtr,		/* The object being invoked. */
    Tcl_Interp *interp,		/* The interpreter containing the object. */
    size_t objc,			/* How many arguments are being passed in. */
    Tcl_Obj *const *objv,	/* The array of arguments. */
    int flags,			/* Whether this is an invocation through the
				 * public or the private command interface. */
    Class *startCls)		/* Where to start in the call chain, or NULL
				 * if we are to start at the front with
				 * filters and the object's methods (which is
				 * the normal case). */
{
    CallContext *contextPtr;
    Tcl_Obj *methodNamePtr;
    CallFrame *framePtr = ((Interp *) interp)->varFramePtr;
    Object *callerObjPtr = NULL;
    Class *callerClsPtr = NULL;
    int result;


    /*
     * If we've no method name, throw this directly into the unknown
     * processing.
     */

    if (objc + 1 < 3) {
	flags |= FORCE_UNKNOWN;
	methodNamePtr = NULL;
	goto noMapping;
    }

    /*
     * Determine if we're in a context that can see the extra, private methods
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
 * ----------------------------------------------------------------------
 */

int
Tcl_ObjectContextInvokeNext(
    Tcl_Interp *interp,
    Tcl_ObjectContext context,
    size_t objc1,
    Tcl_Obj *const *objv,
    int skip)
{
    CallContext *contextPtr = (CallContext *) context;
    size_t savedIndex = contextPtr->index;
    size_t savedSkip = contextPtr->skip;
    int result;
    int objc = objc1;

    if (contextPtr->index + 1 >= contextPtr->callPtr->numChain) {
	/*
	 * We're at the end of the chain; generate an error message unless the
	 * interpreter is being torn down, in which case we might be getting
	 * here because of methods/destructors doing a [next] (or equivalent)
	 * unexpectedly.







|

|





<







2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809

2810
2811
2812
2813
2814
2815
2816
 * ----------------------------------------------------------------------
 */

int
Tcl_ObjectContextInvokeNext(
    Tcl_Interp *interp,
    Tcl_ObjectContext context,
    size_t objc,
    Tcl_Obj *const *objv,
    size_t skip)
{
    CallContext *contextPtr = (CallContext *) context;
    size_t savedIndex = contextPtr->index;
    size_t savedSkip = contextPtr->skip;
    int result;


    if (contextPtr->index + 1 >= contextPtr->callPtr->numChain) {
	/*
	 * We're at the end of the chain; generate an error message unless the
	 * interpreter is being torn down, in which case we might be getting
	 * here because of methods/destructors doing a [next] (or equivalent)
	 * unexpectedly.
Changes to generic/tclOO.decls.
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
    Tcl_Method Tcl_NewMethod(Tcl_Interp *interp, Tcl_Class cls,
	    Tcl_Obj *nameObj, int flags, const Tcl_MethodType *typePtr,
	    void *clientData)
}
declare 13 {
    Tcl_Object Tcl_NewObjectInstance(Tcl_Interp *interp, Tcl_Class cls,
	    const char *nameStr, const char *nsNameStr, size_t objc,
	    Tcl_Obj *const *objv, int skip)
}
declare 14 {
    int Tcl_ObjectDeleted(Tcl_Object object)
}
declare 15 {
    int Tcl_ObjectContextIsFiltering(Tcl_ObjectContext context)
}







|







65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
    Tcl_Method Tcl_NewMethod(Tcl_Interp *interp, Tcl_Class cls,
	    Tcl_Obj *nameObj, int flags, const Tcl_MethodType *typePtr,
	    void *clientData)
}
declare 13 {
    Tcl_Object Tcl_NewObjectInstance(Tcl_Interp *interp, Tcl_Class cls,
	    const char *nameStr, const char *nsNameStr, size_t objc,
	    Tcl_Obj *const *objv, size_t skip)
}
declare 14 {
    int Tcl_ObjectDeleted(Tcl_Object object)
}
declare 15 {
    int Tcl_ObjectContextIsFiltering(Tcl_ObjectContext context)
}
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
declare 22 {
    void Tcl_ObjectSetMetadata(Tcl_Object object,
	    const Tcl_ObjectMetadataType *typePtr, void *metadata)
}
declare 23 {
    int Tcl_ObjectContextInvokeNext(Tcl_Interp *interp,
	    Tcl_ObjectContext context, size_t objc, Tcl_Obj *const *objv,
	    int skip)
}
declare 24 {
    Tcl_ObjectMapMethodNameProc *Tcl_ObjectGetMethodNameMapper(
	    Tcl_Object object)
}
declare 25 {
    void Tcl_ObjectSetMethodNameMapper(Tcl_Object object,







|







101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
declare 22 {
    void Tcl_ObjectSetMetadata(Tcl_Object object,
	    const Tcl_ObjectMetadataType *typePtr, void *metadata)
}
declare 23 {
    int Tcl_ObjectContextInvokeNext(Tcl_Interp *interp,
	    Tcl_ObjectContext context, size_t objc, Tcl_Obj *const *objv,
	    size_t skip)
}
declare 24 {
    Tcl_ObjectMapMethodNameProc *Tcl_ObjectGetMethodNameMapper(
	    Tcl_Object object)
}
declare 25 {
    void Tcl_ObjectSetMethodNameMapper(Tcl_Object object,
Changes to generic/tclOOCall.c.
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
 * IsStillValid --
 *
 *	Calculates whether the given call chain can be used for executing a
 *	method for the given object. The condition on a chain from a cached
 *	location being reusable is:
 *	- Refers to the same object (same creation epoch), and
 *	- Still across the same class structure (same global epoch), and
 *	- Still across the same object strucutre (same local epoch), and
 *	- No public/private/filter magic leakage (same flags, modulo the fact
 *	  that a public chain will satisfy a non-public call).
 *
 * ----------------------------------------------------------------------
 */

static inline int







|







1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
 * IsStillValid --
 *
 *	Calculates whether the given call chain can be used for executing a
 *	method for the given object. The condition on a chain from a cached
 *	location being reusable is:
 *	- Refers to the same object (same creation epoch), and
 *	- Still across the same class structure (same global epoch), and
 *	- Still across the same object structure (same local epoch), and
 *	- No public/private/filter magic leakage (same flags, modulo the fact
 *	  that a public chain will satisfy a non-public call).
 *
 * ----------------------------------------------------------------------
 */

static inline int
Changes to generic/tclOODecls.h.
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
				Tcl_Obj *nameObj, int flags,
				const Tcl_MethodType *typePtr,
				void *clientData);
/* 13 */
TCLAPI Tcl_Object	Tcl_NewObjectInstance(Tcl_Interp *interp,
				Tcl_Class cls, const char *nameStr,
				const char *nsNameStr, size_t objc,
				Tcl_Obj *const *objv, int skip);
/* 14 */
TCLAPI int		Tcl_ObjectDeleted(Tcl_Object object);
/* 15 */
TCLAPI int		Tcl_ObjectContextIsFiltering(
				Tcl_ObjectContext context);
/* 16 */
TCLAPI Tcl_Method	Tcl_ObjectContextMethod(Tcl_ObjectContext context);







|







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
				Tcl_Obj *nameObj, int flags,
				const Tcl_MethodType *typePtr,
				void *clientData);
/* 13 */
TCLAPI Tcl_Object	Tcl_NewObjectInstance(Tcl_Interp *interp,
				Tcl_Class cls, const char *nameStr,
				const char *nsNameStr, size_t objc,
				Tcl_Obj *const *objv, size_t skip);
/* 14 */
TCLAPI int		Tcl_ObjectDeleted(Tcl_Object object);
/* 15 */
TCLAPI int		Tcl_ObjectContextIsFiltering(
				Tcl_ObjectContext context);
/* 16 */
TCLAPI Tcl_Method	Tcl_ObjectContextMethod(Tcl_ObjectContext context);
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/* 22 */
TCLAPI void		Tcl_ObjectSetMetadata(Tcl_Object object,
				const Tcl_ObjectMetadataType *typePtr,
				void *metadata);
/* 23 */
TCLAPI int		Tcl_ObjectContextInvokeNext(Tcl_Interp *interp,
				Tcl_ObjectContext context, size_t objc,
				Tcl_Obj *const *objv, int skip);
/* 24 */
TCLAPI Tcl_ObjectMapMethodNameProc * Tcl_ObjectGetMethodNameMapper(
				Tcl_Object object);
/* 25 */
TCLAPI void		Tcl_ObjectSetMethodNameMapper(Tcl_Object object,
				Tcl_ObjectMapMethodNameProc *mapMethodNameProc);
/* 26 */







|







96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/* 22 */
TCLAPI void		Tcl_ObjectSetMetadata(Tcl_Object object,
				const Tcl_ObjectMetadataType *typePtr,
				void *metadata);
/* 23 */
TCLAPI int		Tcl_ObjectContextInvokeNext(Tcl_Interp *interp,
				Tcl_ObjectContext context, size_t objc,
				Tcl_Obj *const *objv, size_t skip);
/* 24 */
TCLAPI Tcl_ObjectMapMethodNameProc * Tcl_ObjectGetMethodNameMapper(
				Tcl_Object object);
/* 25 */
TCLAPI void		Tcl_ObjectSetMethodNameMapper(Tcl_Object object,
				Tcl_ObjectMapMethodNameProc *mapMethodNameProc);
/* 26 */
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
    Tcl_Class (*tcl_MethodDeclarerClass) (Tcl_Method method); /* 6 */
    Tcl_Object (*tcl_MethodDeclarerObject) (Tcl_Method method); /* 7 */
    int (*tcl_MethodIsPublic) (Tcl_Method method); /* 8 */
    int (*tcl_MethodIsType) (Tcl_Method method, const Tcl_MethodType *typePtr, void **clientDataPtr); /* 9 */
    Tcl_Obj * (*tcl_MethodName) (Tcl_Method method); /* 10 */
    Tcl_Method (*tcl_NewInstanceMethod) (Tcl_Interp *interp, Tcl_Object object, Tcl_Obj *nameObj, int flags, const Tcl_MethodType *typePtr, void *clientData); /* 11 */
    Tcl_Method (*tcl_NewMethod) (Tcl_Interp *interp, Tcl_Class cls, Tcl_Obj *nameObj, int flags, const Tcl_MethodType *typePtr, void *clientData); /* 12 */
    Tcl_Object (*tcl_NewObjectInstance) (Tcl_Interp *interp, Tcl_Class cls, const char *nameStr, const char *nsNameStr, size_t objc, Tcl_Obj *const *objv, int skip); /* 13 */
    int (*tcl_ObjectDeleted) (Tcl_Object object); /* 14 */
    int (*tcl_ObjectContextIsFiltering) (Tcl_ObjectContext context); /* 15 */
    Tcl_Method (*tcl_ObjectContextMethod) (Tcl_ObjectContext context); /* 16 */
    Tcl_Object (*tcl_ObjectContextObject) (Tcl_ObjectContext context); /* 17 */
    size_t (*tcl_ObjectContextSkippedArgs) (Tcl_ObjectContext context); /* 18 */
    void * (*tcl_ClassGetMetadata) (Tcl_Class clazz, const Tcl_ObjectMetadataType *typePtr); /* 19 */
    void (*tcl_ClassSetMetadata) (Tcl_Class clazz, const Tcl_ObjectMetadataType *typePtr, void *metadata); /* 20 */
    void * (*tcl_ObjectGetMetadata) (Tcl_Object object, const Tcl_ObjectMetadataType *typePtr); /* 21 */
    void (*tcl_ObjectSetMetadata) (Tcl_Object object, const Tcl_ObjectMetadataType *typePtr, void *metadata); /* 22 */
    int (*tcl_ObjectContextInvokeNext) (Tcl_Interp *interp, Tcl_ObjectContext context, size_t objc, Tcl_Obj *const *objv, int skip); /* 23 */
    Tcl_ObjectMapMethodNameProc * (*tcl_ObjectGetMethodNameMapper) (Tcl_Object object); /* 24 */
    void (*tcl_ObjectSetMethodNameMapper) (Tcl_Object object, Tcl_ObjectMapMethodNameProc *mapMethodNameProc); /* 25 */
    void (*tcl_ClassSetConstructor) (Tcl_Interp *interp, Tcl_Class clazz, Tcl_Method method); /* 26 */
    void (*tcl_ClassSetDestructor) (Tcl_Interp *interp, Tcl_Class clazz, Tcl_Method method); /* 27 */
    Tcl_Obj * (*tcl_GetObjectName) (Tcl_Interp *interp, Tcl_Object object); /* 28 */
    int (*tcl_MethodIsPrivate) (Tcl_Method method); /* 29 */
    Tcl_Class (*tcl_GetClassOfObject) (Tcl_Object object); /* 30 */







|









|







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
    Tcl_Class (*tcl_MethodDeclarerClass) (Tcl_Method method); /* 6 */
    Tcl_Object (*tcl_MethodDeclarerObject) (Tcl_Method method); /* 7 */
    int (*tcl_MethodIsPublic) (Tcl_Method method); /* 8 */
    int (*tcl_MethodIsType) (Tcl_Method method, const Tcl_MethodType *typePtr, void **clientDataPtr); /* 9 */
    Tcl_Obj * (*tcl_MethodName) (Tcl_Method method); /* 10 */
    Tcl_Method (*tcl_NewInstanceMethod) (Tcl_Interp *interp, Tcl_Object object, Tcl_Obj *nameObj, int flags, const Tcl_MethodType *typePtr, void *clientData); /* 11 */
    Tcl_Method (*tcl_NewMethod) (Tcl_Interp *interp, Tcl_Class cls, Tcl_Obj *nameObj, int flags, const Tcl_MethodType *typePtr, void *clientData); /* 12 */
    Tcl_Object (*tcl_NewObjectInstance) (Tcl_Interp *interp, Tcl_Class cls, const char *nameStr, const char *nsNameStr, size_t objc, Tcl_Obj *const *objv, size_t skip); /* 13 */
    int (*tcl_ObjectDeleted) (Tcl_Object object); /* 14 */
    int (*tcl_ObjectContextIsFiltering) (Tcl_ObjectContext context); /* 15 */
    Tcl_Method (*tcl_ObjectContextMethod) (Tcl_ObjectContext context); /* 16 */
    Tcl_Object (*tcl_ObjectContextObject) (Tcl_ObjectContext context); /* 17 */
    size_t (*tcl_ObjectContextSkippedArgs) (Tcl_ObjectContext context); /* 18 */
    void * (*tcl_ClassGetMetadata) (Tcl_Class clazz, const Tcl_ObjectMetadataType *typePtr); /* 19 */
    void (*tcl_ClassSetMetadata) (Tcl_Class clazz, const Tcl_ObjectMetadataType *typePtr, void *metadata); /* 20 */
    void * (*tcl_ObjectGetMetadata) (Tcl_Object object, const Tcl_ObjectMetadataType *typePtr); /* 21 */
    void (*tcl_ObjectSetMetadata) (Tcl_Object object, const Tcl_ObjectMetadataType *typePtr, void *metadata); /* 22 */
    int (*tcl_ObjectContextInvokeNext) (Tcl_Interp *interp, Tcl_ObjectContext context, size_t objc, Tcl_Obj *const *objv, size_t skip); /* 23 */
    Tcl_ObjectMapMethodNameProc * (*tcl_ObjectGetMethodNameMapper) (Tcl_Object object); /* 24 */
    void (*tcl_ObjectSetMethodNameMapper) (Tcl_Object object, Tcl_ObjectMapMethodNameProc *mapMethodNameProc); /* 25 */
    void (*tcl_ClassSetConstructor) (Tcl_Interp *interp, Tcl_Class clazz, Tcl_Method method); /* 26 */
    void (*tcl_ClassSetDestructor) (Tcl_Interp *interp, Tcl_Class clazz, Tcl_Method method); /* 27 */
    Tcl_Obj * (*tcl_GetObjectName) (Tcl_Interp *interp, Tcl_Object object); /* 28 */
    int (*tcl_MethodIsPrivate) (Tcl_Method method); /* 29 */
    Tcl_Class (*tcl_GetClassOfObject) (Tcl_Object object); /* 30 */
Changes to generic/tclOOInt.h.
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
                        void *clientData);
MODULE_SCOPE Tcl_Method TclNewMethod(Tcl_Interp *interp, Tcl_Class cls,
                        Tcl_Obj *nameObj, int flags,
                        const Tcl_MethodType *typePtr,
                        void *clientData);
MODULE_SCOPE int	TclNRNewObjectInstance(Tcl_Interp *interp,
			    Tcl_Class cls, const char *nameStr,
			    const char *nsNameStr, int objc,
			    Tcl_Obj *const *objv, int skip,
			    Tcl_Object *objectPtr);
MODULE_SCOPE Object *	TclNewObjectInstanceCommon(Tcl_Interp *interp,
			    Class *classPtr,
			    const char *nameStr,
			    const char *nsNameStr);
MODULE_SCOPE int	TclOODecrRefCount(Object *oPtr);
MODULE_SCOPE int	TclOOObjectDestroyed(Object *oPtr);







|
|







501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
                        void *clientData);
MODULE_SCOPE Tcl_Method TclNewMethod(Tcl_Interp *interp, Tcl_Class cls,
                        Tcl_Obj *nameObj, int flags,
                        const Tcl_MethodType *typePtr,
                        void *clientData);
MODULE_SCOPE int	TclNRNewObjectInstance(Tcl_Interp *interp,
			    Tcl_Class cls, const char *nameStr,
			    const char *nsNameStr, size_t objc,
			    Tcl_Obj *const *objv, size_t skip,
			    Tcl_Object *objectPtr);
MODULE_SCOPE Object *	TclNewObjectInstanceCommon(Tcl_Interp *interp,
			    Class *classPtr,
			    const char *nameStr,
			    const char *nsNameStr);
MODULE_SCOPE int	TclOODecrRefCount(Object *oPtr);
MODULE_SCOPE int	TclOOObjectDestroyed(Object *oPtr);
Changes to generic/tclPathObj.c.
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

static Tcl_Obj *	AppendPath(Tcl_Obj *head, Tcl_Obj *tail);
static void		DupFsPathInternalRep(Tcl_Obj *srcPtr,
			    Tcl_Obj *copyPtr);
static void		FreeFsPathInternalRep(Tcl_Obj *pathPtr);
static void		UpdateStringOfFsPath(Tcl_Obj *pathPtr);
static int		SetFsPathFromAny(Tcl_Interp *interp, Tcl_Obj *pathPtr);
static size_t	FindSplitPos(const char *path, int separator);
static int		IsSeparatorOrNull(int ch);
static Tcl_Obj *	GetExtension(Tcl_Obj *pathPtr);
static int		MakePathFromNormalized(Tcl_Interp *interp,
			    Tcl_Obj *pathPtr);

/*
 * Define the 'path' object type, which Tcl uses to represent file paths







|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

static Tcl_Obj *	AppendPath(Tcl_Obj *head, Tcl_Obj *tail);
static void		DupFsPathInternalRep(Tcl_Obj *srcPtr,
			    Tcl_Obj *copyPtr);
static void		FreeFsPathInternalRep(Tcl_Obj *pathPtr);
static void		UpdateStringOfFsPath(Tcl_Obj *pathPtr);
static int		SetFsPathFromAny(Tcl_Interp *interp, Tcl_Obj *pathPtr);
static size_t		FindSplitPos(const char *path, int separator);
static int		IsSeparatorOrNull(int ch);
static Tcl_Obj *	GetExtension(Tcl_Obj *pathPtr);
static int		MakePathFromNormalized(Tcl_Interp *interp,
			    Tcl_Obj *pathPtr);

/*
 * Define the 'path' object type, which Tcl uses to represent file paths
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
 *	None.
 *
 *---------------------------------------------------------------------------
 */

Tcl_Obj *
TclPathPart(
    Tcl_Interp *interp,		/* Used for error reporting */
    Tcl_Obj *pathPtr,		/* Path to take dirname of */
    Tcl_PathPart portion)	/* Requested portion of name */
{
    if (TclHasInternalRep(pathPtr, &fsPathType)) {
	FsPath *fsPathPtr = PATHOBJ(pathPtr);

	if (PATHFLAGS(pathPtr) != 0) {







|







536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
 *	None.
 *
 *---------------------------------------------------------------------------
 */

Tcl_Obj *
TclPathPart(
    TCL_UNUSED(Tcl_Interp *),	/* Used for error reporting */
    Tcl_Obj *pathPtr,		/* Path to take dirname of */
    Tcl_PathPart portion)	/* Requested portion of name */
{
    if (TclHasInternalRep(pathPtr, &fsPathType)) {
	FsPath *fsPathPtr = PATHOBJ(pathPtr);

	if (PATHFLAGS(pathPtr) != 0) {
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
	 * Tcl_FSSplitPath in the handling of home directories;
	 * Tcl_FSSplitPath preserves the "~",  but this code computes the
	 * actual full path name, if we had just a single component.
	 */

	splitPtr = Tcl_FSSplitPath(pathPtr, &splitElements);
	Tcl_IncrRefCount(splitPtr);
	if (splitElements == 1  &&  TclGetString(pathPtr)[0] == '~') {
	    Tcl_Obj *norm;

	    TclDecrRefCount(splitPtr);
	    norm = Tcl_FSGetNormalizedPath(interp, pathPtr);
	    if (norm == NULL) {
		return NULL;
	    }
	    splitPtr = Tcl_FSSplitPath(norm, &splitElements);
	    Tcl_IncrRefCount(splitPtr);
	}
	if (portion == TCL_PATH_TAIL) {
	    /*
	     * Return the last component, unless it is the only component, and
	     * it is the root of an absolute path.
	     */

	    if ((splitElements > 0) && ((splitElements > 1) ||
		    (Tcl_FSGetPathType(pathPtr) == TCL_PATH_RELATIVE))) {







<
<

<
<
<
<
<
<
<
<
|







695
696
697
698
699
700
701


702








703
704
705
706
707
708
709
710
	 * Tcl_FSSplitPath in the handling of home directories;
	 * Tcl_FSSplitPath preserves the "~",  but this code computes the
	 * actual full path name, if we had just a single component.
	 */

	splitPtr = Tcl_FSSplitPath(pathPtr, &splitElements);
	Tcl_IncrRefCount(splitPtr);











        if (portion == TCL_PATH_TAIL) {
	    /*
	     * Return the last component, unless it is the only component, and
	     * it is the root of an absolute path.
	     */

	    if ((splitElements > 0) && ((splitElements > 1) ||
		    (Tcl_FSGetPathType(pathPtr) == TCL_PATH_RELATIVE))) {
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

    noQuickReturn:
	if (res == NULL) {
	    TclNewObj(res);
	}
	ptr = Tcl_GetStringFromObj(res, &length);

	/*
	 * Strip off any './' before a tilde, unless this is the beginning of
	 * the path.
	 */

	if (length > 0 && strEltLen > 0 && (strElt[0] == '.') &&
		(strElt[1] == '/') && (strElt[2] == '~')) {
	    strElt += 2;
	}

	/*
	 * A NULL value for fsPtr at this stage basically means we're trying
	 * to join a relative path onto something which is also relative (or
	 * empty). There's nothing particularly wrong with that.
	 */

	if (*strElt == '\0') {
	    continue;
	}







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







1024
1025
1026
1027
1028
1029
1030
1031










1032
1033
1034
1035
1036
1037
1038
1039

    noQuickReturn:
	if (res == NULL) {
	    TclNewObj(res);
	}
	ptr = Tcl_GetStringFromObj(res, &length);

        /*










         * A NULL value for fsPtr at this stage basically means we're trying
	 * to join a relative path onto something which is also relative (or
	 * empty). There's nothing particularly wrong with that.
	 */

	if (*strElt == '\0') {
	    continue;
	}
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
    size_t len)
{
    FsPath *fsPathPtr;
    Tcl_Obj *pathPtr;
    const char *p;
    int state = 0, count = 0;




    /* [Bug 2806250] - this is only a partial solution of the problem.
     * The PATHFLAGS != 0 representation assumes in many places that
     * the "tail" part stored in the normPathPtr field is itself a
     * relative path.  Strings that begin with "~" are not relative paths,
     * so we must prevent their storage in the normPathPtr field.
     *
     * More generally we ought to be testing "addStrRep" for any value
     * that is not a relative path, but in an unconstrained VFS world
     * that could be just about anything, and testing could be expensive.
     * Since this routine plays a big role in [glob], anything that slows
     * it down would be unwelcome.  For now, continue the risk of further
     * bugs when some Tcl_Filesystem uses otherwise relative path strings
     * as absolute path strings.  Sensible Tcl_Filesystems will avoid
     * that by mounting on path prefixes like foo:// which cannot be the
     * name of a file or directory read from a native [glob] operation.
     */
    if (addStrRep[0] == '~') {
	Tcl_Obj *tail = Tcl_NewStringObj(addStrRep, len);

	pathPtr = AppendPath(dirPtr, tail);
	Tcl_DecrRefCount(tail);
	return pathPtr;
    }

    TclNewObj(pathPtr);
    fsPathPtr = (FsPath *)Tcl_Alloc(sizeof(FsPath));

    /*
     * Set up the path.
     */







>
>
>
|















<
<
<
<
<
<
<







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
    size_t len)
{
    FsPath *fsPathPtr;
    Tcl_Obj *pathPtr;
    const char *p;
    int state = 0, count = 0;

    /*
     * This comment is kept from the days of tilde expansion because
     * it is illustrative of a more general problem.
     * [Bug 2806250] - this is only a partial solution of the problem.
     * The PATHFLAGS != 0 representation assumes in many places that
     * the "tail" part stored in the normPathPtr field is itself a
     * relative path.  Strings that begin with "~" are not relative paths,
     * so we must prevent their storage in the normPathPtr field.
     *
     * More generally we ought to be testing "addStrRep" for any value
     * that is not a relative path, but in an unconstrained VFS world
     * that could be just about anything, and testing could be expensive.
     * Since this routine plays a big role in [glob], anything that slows
     * it down would be unwelcome.  For now, continue the risk of further
     * bugs when some Tcl_Filesystem uses otherwise relative path strings
     * as absolute path strings.  Sensible Tcl_Filesystems will avoid
     * that by mounting on path prefixes like foo:// which cannot be the
     * name of a file or directory read from a native [glob] operation.
     */








    TclNewObj(pathPtr);
    fsPathPtr = (FsPath *)Tcl_Alloc(sizeof(FsPath));

    /*
     * Set up the path.
     */
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
 *---------------------------------------------------------------------------
 *
 * SetFsPathFromAny --
 *
 *	Attempt to convert the internal representation of pathPtr to
 *	fsPathType.
 *
 *	A tilde ("~") character at the beginnig of the filename indicates the
 *	current user's home directory, and "~<user>" indicates a particular
 *	user's directory.
 *
 * Results:
 *	Standard Tcl error code.
 *
 * Side effects:
 *	The old representation may be freed, and new memory allocated.
 *
 *---------------------------------------------------------------------------
 */

static int
SetFsPathFromAny(
    Tcl_Interp *interp,		/* Used for error reporting if not NULL. */
    Tcl_Obj *pathPtr)		/* The object to convert. */
{
    size_t len;
    FsPath *fsPathPtr;
    Tcl_Obj *transPtr;
    const char *name;

    if (TclHasInternalRep(pathPtr, &fsPathType)) {
	return TCL_OK;
    }

    /*
     * First step is to translate the filename. This is similar to
     * Tcl_TranslateFilename, but shouldn't convert everything to windows
     * backslashes on that platform. The current implementation of this piece
     * is a slightly optimised version of the various Tilde/Split/Join stuff
     * to avoid multiple split/join operations.
     *
     * We remove any trailing directory separator.
     *
     * However, the split/join routines are quite complex, and one has to make
     * sure not to break anything on Unix or Win (fCmd.test, fileName.test and
     * cmdAH.test exercise most of the code).
     */

    name = Tcl_GetStringFromObj(pathPtr, &len);

    /*
     * Handle tilde substitutions, if needed.
     */

    if (len && name[0] == '~') {
	Tcl_DString temp;
	size_t split;
	char separator = '/';

	/*
	 * We have multiple cases '~/foo/bar...', '~user/foo/bar...', etc.
	 * split becomes value 1 for '~/...' as well as for '~'.
	 */
	split = FindSplitPos(name, separator);

	/*
	 * Do some tilde substitution.
	 */

	if (split == 1) {
	    /*
	     * We have just '~' (or '~/...')
	     */

	    const char *dir;
	    Tcl_DString dirString;

	    dir = TclGetEnv("HOME", &dirString);
	    if (dir == NULL) {
		if (interp) {
		    Tcl_SetObjResult(interp, Tcl_NewStringObj(
			    "couldn't find HOME environment variable to"
			    " expand path", -1));
		    Tcl_SetErrorCode(interp, "TCL", "VALUE", "PATH",
			    "HOMELESS", NULL);
		}
		return TCL_ERROR;
	    }
	    Tcl_DStringInit(&temp);
	    Tcl_JoinPath(1, &dir, &temp);
	    Tcl_DStringFree(&dirString);
	} else {
	    /*
	     * There is a '~user'
	     */

	    const char *expandedUser;
	    Tcl_DString userName;

	    Tcl_DStringInit(&userName);
	    Tcl_DStringAppend(&userName, name+1, split-1);
	    expandedUser = Tcl_DStringValue(&userName);

	    Tcl_DStringInit(&temp);
	    if (TclpGetUserHome(expandedUser, &temp) == NULL) {
		if (interp != NULL) {
		    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
			    "user \"%s\" doesn't exist", expandedUser));
		    Tcl_SetErrorCode(interp, "TCL", "VALUE", "PATH", "NOUSER",
			    NULL);
		}
		Tcl_DStringFree(&userName);
		Tcl_DStringFree(&temp);
		return TCL_ERROR;
	    }
	    Tcl_DStringFree(&userName);
	}

	transPtr = TclDStringToObj(&temp);

	if (split != len) {
	    /*
	     * Join up the tilde substitution with the rest.
	     */

	    if (name[split+1] == separator) {
		/*
		 * Somewhat tricky case like ~//foo/bar. Make use of
		 * Split/Join machinery to get it right. Assumes all paths
		 * beginning with ~ are part of the native filesystem.
		 */

		size_t objc;
		Tcl_Obj **objv;
		Tcl_Obj *parts = TclpNativeSplitPath(pathPtr, NULL);

		TclListObjGetElementsM(NULL, parts, &objc, &objv);

		/*
		 * Skip '~'. It's replaced by its expansion.
		 */

		objc--; objv++;
		while (objc--) {
		    TclpNativeJoinPath(transPtr, TclGetString(*objv));
		    objv++;
		}
		TclDecrRefCount(parts);
	    } else {
		Tcl_Obj *pair[2];

		pair[0] = transPtr;
		pair[1] = Tcl_NewStringObj(name+split+1, -1);
		transPtr = TclJoinPath(2, pair, 1);
		if (transPtr != pair[0]) {
		    Tcl_DecrRefCount(pair[0]);
		}
		if (transPtr != pair[1]) {
		    Tcl_DecrRefCount(pair[1]);
		}
	    }
	}
    } else {
	transPtr = TclJoinPath(1, &pathPtr, 1);
    }

    /*
     * Now we have a translated filename in 'transPtr'. This will have forward
     * slashes on Windows, and will not contain any ~user sequences.
     */

    fsPathPtr = (FsPath *)Tcl_Alloc(sizeof(FsPath));







<
<
<
<











|





<



















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







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
 *---------------------------------------------------------------------------
 *
 * SetFsPathFromAny --
 *
 *	Attempt to convert the internal representation of pathPtr to
 *	fsPathType.
 *




 * Results:
 *	Standard Tcl error code.
 *
 * Side effects:
 *	The old representation may be freed, and new memory allocated.
 *
 *---------------------------------------------------------------------------
 */

static int
SetFsPathFromAny(
    TCL_UNUSED(Tcl_Interp *),	/* Used for error reporting if not NULL. */
    Tcl_Obj *pathPtr)		/* The object to convert. */
{
    size_t len;
    FsPath *fsPathPtr;
    Tcl_Obj *transPtr;


    if (TclHasInternalRep(pathPtr, &fsPathType)) {
	return TCL_OK;
    }

    /*
     * First step is to translate the filename. This is similar to
     * Tcl_TranslateFilename, but shouldn't convert everything to windows
     * backslashes on that platform. The current implementation of this piece
     * is a slightly optimised version of the various Tilde/Split/Join stuff
     * to avoid multiple split/join operations.
     *
     * We remove any trailing directory separator.
     *
     * However, the split/join routines are quite complex, and one has to make
     * sure not to break anything on Unix or Win (fCmd.test, fileName.test and
     * cmdAH.test exercise most of the code).
     */

    Tcl_GetStringFromObj(pathPtr, &len); /* TODO: Is this needed? */


















































































































    transPtr = TclJoinPath(1, &pathPtr, 1);


    /*
     * Now we have a translated filename in 'transPtr'. This will have forward
     * slashes on Windows, and will not contain any ~user sequences.
     */

    fsPathPtr = (FsPath *)Tcl_Alloc(sizeof(FsPath));
2553
2554
2555
2556
2557
2558
2559























































































































































































































































2560
2561
2562
2563
2564
2565
2566
2567

    /*
     * Path is of correct type, or is of non-zero length, so we accept it.
     */

    return TCL_OK;
}
























































































































































































































































/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */







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








2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670

    /*
     * Path is of correct type, or is of non-zero length, so we accept it.
     */

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * MakeTildeRelativePath --
 *
 *      Returns a path relative to the home directory of a user.
 *      Note there is a difference between not specifying a user and
 *	explicitly specifying the current user. This mimics Tcl8's tilde
 *	expansion.
 *
 *	The subPath argument is joined to the expanded home directory
 *	as in Tcl_JoinPath. This means if it is not relative, it will
 *	returned as the result with the home directory only checked
 *	for user name validity.
 *
 * Results:
 *	Returns TCL_OK on success with home directory path in *dsPtr
 *	and TCL_ERROR on failure with error message in interp if non-NULL.
 *
 *----------------------------------------------------------------------
 */
int
MakeTildeRelativePath(
    Tcl_Interp *interp,  /* May be NULL. Only used for error messages */
    const char *user,    /* User name. NULL -> current user */
    const char *subPath, /* Rest of path. May be NULL */
    Tcl_DString *dsPtr)  /* Output. Is initialized by the function. Must be
                           freed on success */
{
    const char *dir;
    Tcl_DString dirString;

    Tcl_DStringInit(dsPtr);
    Tcl_DStringInit(&dirString);

    if (user == NULL || user[0] == 0) {
        /* No user name specified -> current user */

	dir = TclGetEnv("HOME", &dirString);
	if (dir == NULL) {
            if (interp) {
                Tcl_SetObjResult(interp, Tcl_NewStringObj(
                                     "couldn't find HOME environment variable to"
                                     " expand path", -1));
                Tcl_SetErrorCode(interp, "TCL", "VALUE", "PATH",
                                 "HOMELESS", NULL);
            }
            return TCL_ERROR;
        }
    } else {
        /* User name specified - ~user */
	dir = TclpGetUserHome(user, &dirString);
	if (dir == NULL) {
	    if (interp != NULL) {
                Tcl_SetObjResult(interp, Tcl_ObjPrintf(
                                     "user \"%s\" doesn't exist", user));
                Tcl_SetErrorCode(interp, "TCL", "VALUE", "PATH", "NOUSER",
                                 NULL);
            }
            return TCL_ERROR;
	}
    }
    if (subPath) {
	const char *parts[2];
	parts[0] = dir;
	parts[1] = subPath;
	Tcl_JoinPath(2, parts, dsPtr);
    } else {
	Tcl_JoinPath(1, &dir, dsPtr);
    }

    Tcl_DStringFree(&dirString);

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TclGetHomeDirObj --
 *
 *	Wrapper around MakeTildeRelativePath. See that function.
 *
 * Results:
 *      Returns a Tcl_Obj containing the home directory of a user
 *	or NULL on failure with error message in interp if non-NULL.
 *
 *----------------------------------------------------------------------
 */
Tcl_Obj *
TclGetHomeDirObj(
    Tcl_Interp *interp, /* May be NULL. Only used for error messages */
    const char *user)   /* User name. NULL -> current user */
{
    Tcl_DString dirString;

    if (MakeTildeRelativePath(interp, user, NULL, &dirString) != TCL_OK) {
	return NULL;
    }
    return TclDStringToObj(&dirString);
}

/*
 *----------------------------------------------------------------------
 *
 * TclResolveTildePath --
 *
 *	If the passed path is begins with a tilde, does tilde resolution
 *	and returns a Tcl_Obj containing the resolved path. If the tilde
 *	component cannot be resolved, returns NULL. If the path does not
 *	begin with a tilde, returns as is.
 *
 * Results:
 *      Returns a Tcl_Obj with resolved path. This may be a new Tcl_Obj
 *	with ref count 0 or that pathObj that was passed in without its
 *	ref count modified.
 *      Returns NULL if the path begins with a ~ that cannot be resolved
 *	and stores an error message in interp if non-NULL.
 *
 *----------------------------------------------------------------------
 */
Tcl_Obj *
TclResolveTildePath(
    Tcl_Interp *interp, /* May be NULL. Only used for error messages */
    Tcl_Obj *pathObj)
{
    const char *path;
    size_t len;
    size_t split;
    Tcl_DString resolvedPath;

    path = Tcl_GetStringFromObj(pathObj, &len);
    if (path[0] != '~') {
	return pathObj;
    }

    /*
     * We have multiple cases '~/foo/bar...', '~user/foo/bar...', etc.
     * split becomes value 1 for '~/...' as well as for '~'. Note on
     * Windows FindSplitPos will implicitly check for '\' as separator
     * in addition to what is passed.
     */
    split = FindSplitPos(path, '/');

    if (split == 1) {
        /* No user name specified -> current user */
	if (MakeTildeRelativePath(
		interp, NULL, path[1] ? 2 + path : NULL, &resolvedPath)
	    != TCL_OK) {
	    return NULL;
	}
    } else {
        /* User name specified - ~user */
        const char *expandedUser;
        Tcl_DString userName;

        Tcl_DStringInit(&userName);
        Tcl_DStringAppend(&userName, path+1, split-1);
        expandedUser = Tcl_DStringValue(&userName);

	/* path[split] is / or \0 */
	if (MakeTildeRelativePath(interp,
				  expandedUser,
				  path[split] ? &path[split+1] : NULL,
				  &resolvedPath)
	    != TCL_OK) {
	    Tcl_DStringFree(&userName);
	    return NULL;
	}
	Tcl_DStringFree(&userName);
    }
    return TclDStringToObj(&resolvedPath);
}

/*
 *----------------------------------------------------------------------
 *
 * TclResolveTildePathList --
 *
 *	Given a Tcl_Obj that is a list of paths, returns a Tcl_Obj containing
 *	the paths with any ~-prefixed paths resolved.
 *
 *	Empty strings and ~-prefixed paths that cannot be resolved are
 *      removed from the returned list.
 *
 *      The trailing components of the path are returned verbatim. No
 *	processing is done on them. Moreover, no assumptions should be
 *	made about the separators in the returned path. They may be /
 *	or native. Appropriate path manipulations functions should be
 *	used by caller if desired.
 *
 * Results:
 *	Returns a Tcl_Obj with resolved paths. This may be a new Tcl_Obj with
 *	reference count 0 or the original passed-in Tcl_Obj if no paths needed
 *	resolution. A NULL is returned if the passed in value is not a list
 *	or was NULL.
 *
 *----------------------------------------------------------------------
 */
Tcl_Obj *
TclResolveTildePathList(
    Tcl_Obj *pathsObj)
{
    Tcl_Obj **objv;
    size_t objc;
    size_t i;
    Tcl_Obj *resolvedPaths;
    const char *path;

    if (pathsObj == NULL) {
        return NULL;
    }
    if (Tcl_ListObjGetElements(NULL, pathsObj, &objc, &objv) != TCL_OK) {
        return NULL; /* Not a list */
    }

    /*
     * Figure out if any paths need resolving to avoid unnecessary allocations.
     */
    for (i = 0; i < objc; ++i) {
        path = Tcl_GetString(objv[i]);
        if (path[0] == '~') {
            break; /* At least one path needs resolution */
        }
    }
    if (i == objc) {
        return pathsObj; /* No paths needed to be resolved */
    }

    resolvedPaths = Tcl_NewListObj(objc, NULL);
    for (i = 0; i < objc; ++i) {
	Tcl_Obj *resolvedPath;
        path = Tcl_GetString(objv[i]);
	if (path[0] == 0) {
	    continue; /* Skip empty strings */
	}
	resolvedPath = TclResolveTildePath(NULL, objv[i]);
	if (resolvedPath) {
	    /* Paths that cannot be resolved are skipped */
	    Tcl_ListObjAppendElement(NULL, resolvedPaths, resolvedPath);
	}
    }

    return resolvedPaths;
}


/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */
Changes to generic/tclProc.c.
144
145
146
147
148
149
150

151
152
153
154
155
156
157
 *
 * Side effects:
 *	A new procedure gets created.
 *
 *----------------------------------------------------------------------
 */


int
Tcl_ProcObjCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    size_t objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{







>







144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
 *
 * Side effects:
 *	A new procedure gets created.
 *
 *----------------------------------------------------------------------
 */

#undef TclObjInterpProc
int
Tcl_ProcObjCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    size_t objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
    /*
     * When we get here, the last formal argument remains to be defined:
     * defPtr and varPtr point to the last argument to be initialized.
     */

    varPtr->flags = 0;
    if (defPtr && defPtr->flags & VAR_IS_ARGS) {
	Tcl_Obj *listPtr = Tcl_NewListObj(argCt-i, argObjs+i);

	varPtr->value.objPtr = listPtr;
	Tcl_IncrRefCount(listPtr);	/* Local var is a reference. */
    } else if (argCt == numArgs) {
	Tcl_Obj *objPtr = argObjs[i];

	varPtr->value.objPtr = objPtr;







|







1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
    /*
     * When we get here, the last formal argument remains to be defined:
     * defPtr and varPtr point to the last argument to be initialized.
     */

    varPtr->flags = 0;
    if (defPtr && defPtr->flags & VAR_IS_ARGS) {
	Tcl_Obj *listPtr = Tcl_NewListObj((argCt>i)? argCt-i : 0, argObjs+i);

	varPtr->value.objPtr = listPtr;
	Tcl_IncrRefCount(listPtr);	/* Local var is a reference. */
    } else if (argCt == numArgs) {
	Tcl_Obj *objPtr = argObjs[i];

	varPtr->value.objPtr = objPtr;
1590
1591
1592
1593
1594
1595
1596

1597
1598
1599
1600
1601
1602
1603
 *
 * Side effects:
 *	Depends on the commands in the procedure.
 *
 *----------------------------------------------------------------------
 */


int
TclObjInterpProc(
    ClientData clientData,	/* Record describing procedure to be
				 * interpreted. */
    Tcl_Interp *interp,/* Interpreter in which procedure was
				 * invoked. */
    int objc,			/* Count of number of arguments to this







>







1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
 *
 * Side effects:
 *	Depends on the commands in the procedure.
 *
 *----------------------------------------------------------------------
 */

#undef TclObjInterpProc
int
TclObjInterpProc(
    ClientData clientData,	/* Record describing procedure to be
				 * interpreted. */
    Tcl_Interp *interp,/* Interpreter in which procedure was
				 * invoked. */
    int objc,			/* Count of number of arguments to this
1625
1626
1627
1628
1629
1630
1631





































1632
1633
1634
1635
1636
1637
1638
	    /*isLambda*/ 0);

    if (result != TCL_OK) {
	return TCL_ERROR;
    }
    return TclNRInterpProcCore(interp, objv[0], 1, &MakeProcError);
}






































/*
 *----------------------------------------------------------------------
 *
 * TclNRInterpProcCore --
 *
 *	When a Tcl procedure, lambda term or anything else that works like a







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







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
	    /*isLambda*/ 0);

    if (result != TCL_OK) {
	return TCL_ERROR;
    }
    return TclNRInterpProcCore(interp, objv[0], 1, &MakeProcError);
}

static int
NRInterpProc2(
    ClientData clientData,	/* Record describing procedure to be
				 * interpreted. */
    Tcl_Interp *interp,/* Interpreter in which procedure was
				 * invoked. */
    size_t objc,			/* Count of number of arguments to this
				 * procedure. */
    Tcl_Obj *const objv[])	/* Argument value objects. */
{
    int result = TclPushProcCallFrame(clientData, interp, objc, objv,
	    /*isLambda*/ 0);

    if (result != TCL_OK) {
	return TCL_ERROR;
    }
    return TclNRInterpProcCore(interp, objv[0], 1, &MakeProcError);
}

static int
ObjInterpProc2(
    ClientData clientData,	/* Record describing procedure to be
				 * interpreted. */
    Tcl_Interp *interp,/* Interpreter in which procedure was
				 * invoked. */
    size_t objc,			/* Count of number of arguments to this
				 * procedure. */
    Tcl_Obj *const objv[])	/* Argument value objects. */
{
    /*
     * Not used much in the core; external interface for iTcl
     */

    return Tcl_NRCallObjProc2(interp, NRInterpProc2, clientData, objc, objv);
}


/*
 *----------------------------------------------------------------------
 *
 * TclNRInterpProcCore --
 *
 *	When a Tcl procedure, lambda term or anything else that works like a
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
    Tcl_HashEntry *hePtr = NULL;
    CmdFrame *cfPtr = NULL;
    Interp *iPtr = procPtr->iPtr;

    if (bodyPtr != NULL) {
	/* procPtr is stored in body's ByteCode, so ensure to reset it. */
	ByteCode *codePtr;
	
	ByteCodeGetInternalRep(bodyPtr, &tclByteCodeType, codePtr);
	if (codePtr != NULL && codePtr->procPtr == procPtr) {
	    codePtr->procPtr = NULL;
	}
	Tcl_DecrRefCount(bodyPtr);
    }
    for (localPtr = procPtr->firstLocalPtr; localPtr != NULL; ) {







|







2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
    Tcl_HashEntry *hePtr = NULL;
    CmdFrame *cfPtr = NULL;
    Interp *iPtr = procPtr->iPtr;

    if (bodyPtr != NULL) {
	/* procPtr is stored in body's ByteCode, so ensure to reset it. */
	ByteCode *codePtr;

	ByteCodeGetInternalRep(bodyPtr, &tclByteCodeType, codePtr);
	if (codePtr != NULL && codePtr->procPtr == procPtr) {
	    codePtr->procPtr = NULL;
	}
	Tcl_DecrRefCount(bodyPtr);
    }
    for (localPtr = procPtr->firstLocalPtr; localPtr != NULL; ) {
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
    }
    return code;
}

/*
 *----------------------------------------------------------------------
 *
 * TclGetObjInterpProc --
 *
 *	Returns a pointer to the TclObjInterpProc function; this is different
 *	from the value obtained from the TclObjInterpProc reference on systems
 *	like Windows where import and export versions of a function exported
 *	by a DLL exist.
 *
 * Results:
 *	Returns the internal address of the TclObjInterpProc function.

 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

Tcl_ObjCmdProc *
TclGetObjInterpProc(void)
{
    return TclObjInterpProc;
}







/*
 *----------------------------------------------------------------------
 *
 * TclNewProcBodyObj --
 *
 *	Creates a new object, of type "procbody", whose internal







|

|
|
|
|


|
>












>
>
>
>
>
>







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
    }
    return code;
}

/*
 *----------------------------------------------------------------------
 *
 * TclGetObjInterpProc/TclGetObjInterpProc2 --
 *
 *	Returns a pointer to the TclObjInterpProc/ObjInterpProc2 functions;
 *	this is different from the value obtained from the TclObjInterpProc
 *	reference on systems like Windows where import and export versions
 *	of a function exported by a DLL exist.
 *
 * Results:
 *	Returns the internal address of the TclObjInterpProc/ObjInterpProc2
 *	functions.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

Tcl_ObjCmdProc *
TclGetObjInterpProc(void)
{
    return TclObjInterpProc;
}

Tcl_ObjCmdProc2 *
TclGetObjInterpProc2(void)
{
    return ObjInterpProc2;
}

/*
 *----------------------------------------------------------------------
 *
 * TclNewProcBodyObj --
 *
 *	Creates a new object, of type "procbody", whose internal
Changes to generic/tclStubInit.c.
63
64
65
66
67
68
69

70
71
72
73
74
75
76
#undef TclStaticLibrary
#undef Tcl_BackgroundError
#define TclStaticLibrary Tcl_StaticLibrary
#undef Tcl_UniCharToUtfDString
#undef Tcl_UtfToUniCharDString
#undef Tcl_UtfToUniChar
#undef Tcl_UniCharLen

#if !defined(_WIN32) && !defined(__CYGWIN__)
#undef Tcl_WinConvertError
#define Tcl_WinConvertError 0
#endif
#undef Tcl_Close
#define Tcl_Close 0
#undef TclGetByteArrayFromObj







>







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#undef TclStaticLibrary
#undef Tcl_BackgroundError
#define TclStaticLibrary Tcl_StaticLibrary
#undef Tcl_UniCharToUtfDString
#undef Tcl_UtfToUniCharDString
#undef Tcl_UtfToUniChar
#undef Tcl_UniCharLen
#undef TclObjInterpProc
#if !defined(_WIN32) && !defined(__CYGWIN__)
#undef Tcl_WinConvertError
#define Tcl_WinConvertError 0
#endif
#undef Tcl_Close
#define Tcl_Close 0
#undef TclGetByteArrayFromObj
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
    0, /* 36 */
    0, /* 37 */
    TclGetNamespaceForQualName, /* 38 */
    TclGetObjInterpProc, /* 39 */
    TclGetOpenMode, /* 40 */
    TclGetOriginalCommand, /* 41 */
    TclpGetUserHome, /* 42 */
    0, /* 43 */
    0, /* 44 */
    TclHideUnsafeCommands, /* 45 */
    TclInExit, /* 46 */
    0, /* 47 */
    0, /* 48 */
    0, /* 49 */
    0, /* 50 */







|







431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
    0, /* 36 */
    0, /* 37 */
    TclGetNamespaceForQualName, /* 38 */
    TclGetObjInterpProc, /* 39 */
    TclGetOpenMode, /* 40 */
    TclGetOriginalCommand, /* 41 */
    TclpGetUserHome, /* 42 */
    TclGetObjInterpProc2, /* 43 */
    0, /* 44 */
    TclHideUnsafeCommands, /* 45 */
    TclInExit, /* 46 */
    0, /* 47 */
    0, /* 48 */
    0, /* 49 */
    0, /* 50 */
646
647
648
649
650
651
652



653
654
655
656
657
658
659
    TclPtrGetVar, /* 252 */
    TclPtrSetVar, /* 253 */
    TclPtrIncrObjVar, /* 254 */
    TclPtrObjMakeUpvar, /* 255 */
    TclPtrUnsetVar, /* 256 */
    TclStaticLibrary, /* 257 */
    TclpCreateTemporaryDirectory, /* 258 */



};

static const TclIntPlatStubs tclIntPlatStubs = {
    TCL_STUB_MAGIC,
    0,
    0, /* 0 */
    TclpCloseFile, /* 1 */







>
>
>







647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
    TclPtrGetVar, /* 252 */
    TclPtrSetVar, /* 253 */
    TclPtrIncrObjVar, /* 254 */
    TclPtrObjMakeUpvar, /* 255 */
    TclPtrUnsetVar, /* 256 */
    TclStaticLibrary, /* 257 */
    TclpCreateTemporaryDirectory, /* 258 */
    0, /* 259 */
    TclListTestObj, /* 260 */
    TclListObjValidate, /* 261 */
};

static const TclIntPlatStubs tclIntPlatStubs = {
    TCL_STUB_MAGIC,
    0,
    0, /* 0 */
    TclpCloseFile, /* 1 */
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
    Tcl_InputBuffered, /* 183 */
    Tcl_InterpDeleted, /* 184 */
    Tcl_IsSafe, /* 185 */
    Tcl_JoinPath, /* 186 */
    Tcl_LinkVar, /* 187 */
    0, /* 188 */
    Tcl_MakeFileChannel, /* 189 */
    Tcl_MakeSafe, /* 190 */
    Tcl_MakeTcpClientChannel, /* 191 */
    Tcl_Merge, /* 192 */
    Tcl_NextHashEntry, /* 193 */
    Tcl_NotifyChannel, /* 194 */
    Tcl_ObjGetVar2, /* 195 */
    Tcl_ObjSetVar2, /* 196 */
    Tcl_OpenCommandChannel, /* 197 */







|







982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
    Tcl_InputBuffered, /* 183 */
    Tcl_InterpDeleted, /* 184 */
    Tcl_IsSafe, /* 185 */
    Tcl_JoinPath, /* 186 */
    Tcl_LinkVar, /* 187 */
    0, /* 188 */
    Tcl_MakeFileChannel, /* 189 */
    0, /* 190 */
    Tcl_MakeTcpClientChannel, /* 191 */
    Tcl_Merge, /* 192 */
    Tcl_NextHashEntry, /* 193 */
    Tcl_NotifyChannel, /* 194 */
    Tcl_ObjGetVar2, /* 195 */
    Tcl_ObjSetVar2, /* 196 */
    Tcl_OpenCommandChannel, /* 197 */
Changes to generic/tclTest.c.
50
51
52
53
54
55
56















57
58
59
60
61
62
63
 * Dynamic string shared by TestdcallCmd and DelCallbackProc; used to collect
 * the results of the various deletion callbacks.
 */

static Tcl_DString delString;
static Tcl_Interp *delInterp;
















/*
 * One of the following structures exists for each asynchronous handler
 * created by the "testasync" command".
 */

typedef struct TestAsyncHandler {
    int id;			/* Identifier for this handler. */







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







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
 * Dynamic string shared by TestdcallCmd and DelCallbackProc; used to collect
 * the results of the various deletion callbacks.
 */

static Tcl_DString delString;
static Tcl_Interp *delInterp;

/*
 * One of the following structures exists for each command created by the
 * "testcmdtoken" command.
 */

typedef struct TestCommandTokenRef {
    int id;			/* Identifier for this reference. */
    Tcl_Command token;		/* Tcl's token for the command. */
    struct TestCommandTokenRef *nextPtr;
				/* Next in list of references. */
} TestCommandTokenRef;

static TestCommandTokenRef *firstCommandTokenRef = NULL;
static int nextCommandTokenRefId = 1;

/*
 * One of the following structures exists for each asynchronous handler
 * created by the "testasync" command".
 */

typedef struct TestAsyncHandler {
    int id;			/* Identifier for this handler. */
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
 * Start of the socket driver state structure to acces field testFlags
 */

typedef struct TcpState TcpState;

struct TcpState {
    Tcl_Channel channel;	/* Channel associated with this socket. */
    int testFlags;              /* bit field for tests. Is set by testsocket
                                 * test procedure */
};

TCL_DECLARE_MUTEX(asyncTestMutex)

static TestAsyncHandler *firstHandler = NULL;

/*







|
<







87
88
89
90
91
92
93
94

95
96
97
98
99
100
101
 * Start of the socket driver state structure to acces field testFlags
 */

typedef struct TcpState TcpState;

struct TcpState {
    Tcl_Channel channel;	/* Channel associated with this socket. */
    int flags;			/* ORed combination of various bitfields. */

};

TCL_DECLARE_MUTEX(asyncTestMutex)

static TestAsyncHandler *firstHandler = NULL;

/*
247
248
249
250
251
252
253

254
255
256
257
258
259
260
static Tcl_CmdProc	TestgetintCmd;
static Tcl_CmdProc	TestlongsizeCmd;
static Tcl_CmdProc	TestgetplatformCmd;
static Tcl_ObjCmdProc	TestgetvarfullnameCmd;
static Tcl_CmdProc	TestinterpdeleteCmd;
static Tcl_CmdProc	TestlinkCmd;
static Tcl_ObjCmdProc	TestlinkarrayCmd;

static Tcl_ObjCmdProc	TestlocaleCmd;
static Tcl_CmdProc	TestmainthreadCmd;
static Tcl_CmdProc	TestsetmainloopCmd;
static Tcl_CmdProc	TestexitmainloopCmd;
static Tcl_CmdProc	TestpanicCmd;
static Tcl_ObjCmdProc	TestparseargsCmd;
static Tcl_ObjCmdProc	TestparserObjCmd;







>







261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
static Tcl_CmdProc	TestgetintCmd;
static Tcl_CmdProc	TestlongsizeCmd;
static Tcl_CmdProc	TestgetplatformCmd;
static Tcl_ObjCmdProc	TestgetvarfullnameCmd;
static Tcl_CmdProc	TestinterpdeleteCmd;
static Tcl_CmdProc	TestlinkCmd;
static Tcl_ObjCmdProc	TestlinkarrayCmd;
static Tcl_ObjCmdProc	TestlistrepCmd;
static Tcl_ObjCmdProc	TestlocaleCmd;
static Tcl_CmdProc	TestmainthreadCmd;
static Tcl_CmdProc	TestsetmainloopCmd;
static Tcl_CmdProc	TestexitmainloopCmd;
static Tcl_CmdProc	TestpanicCmd;
static Tcl_ObjCmdProc	TestparseargsCmd;
static Tcl_ObjCmdProc	TestparserObjCmd;
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
static Tcl_CmdProc	Testset2Cmd;
static Tcl_CmdProc	TestseterrorcodeCmd;
static Tcl_ObjCmdProc	TestsetobjerrorcodeCmd;
static Tcl_CmdProc	TestsetplatformCmd;
static Tcl_CmdProc	TeststaticlibraryCmd;
static Tcl_CmdProc	TesttranslatefilenameCmd;
static Tcl_CmdProc	TestupvarCmd;
static Tcl_ObjCmdProc	TestWrongNumArgsObjCmd;
static Tcl_ObjCmdProc	TestGetIndexFromObjStructObjCmd;
static Tcl_CmdProc	TestChannelCmd;
static Tcl_CmdProc	TestChannelEventCmd;
static Tcl_CmdProc	TestSocketCmd;
static Tcl_ObjCmdProc	TestFilesystemObjCmd;
static Tcl_ObjCmdProc	TestSimpleFilesystemObjCmd;
static void		TestReport(const char *cmd, Tcl_Obj *arg1,







|







288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
static Tcl_CmdProc	Testset2Cmd;
static Tcl_CmdProc	TestseterrorcodeCmd;
static Tcl_ObjCmdProc	TestsetobjerrorcodeCmd;
static Tcl_CmdProc	TestsetplatformCmd;
static Tcl_CmdProc	TeststaticlibraryCmd;
static Tcl_CmdProc	TesttranslatefilenameCmd;
static Tcl_CmdProc	TestupvarCmd;
static Tcl_ObjCmdProc2	TestWrongNumArgsObjCmd;
static Tcl_ObjCmdProc	TestGetIndexFromObjStructObjCmd;
static Tcl_CmdProc	TestChannelCmd;
static Tcl_CmdProc	TestChannelEventCmd;
static Tcl_CmdProc	TestSocketCmd;
static Tcl_ObjCmdProc	TestFilesystemObjCmd;
static Tcl_ObjCmdProc	TestSimpleFilesystemObjCmd;
static void		TestReport(const char *cmd, Tcl_Obj *arg1,
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
    }
#endif
    if (Tcl_OOInitStubs(interp) == NULL) {
	return TCL_ERROR;
    }

    if (Tcl_GetCommandInfo(interp, "::tcl::build-info", &info)) {






	Tcl_CreateObjCommand(interp, "::tcl::test::build-info",
		info.objProc, (void *)version, NULL);
    }
    if (Tcl_PkgProvideEx(interp, "tcl::test", TCL_PATCH_LEVEL, NULL) == TCL_ERROR) {
	return TCL_ERROR;
    }

    /*
     * Create additional commands and math functions for testing Tcl.
     */

    Tcl_CreateObjCommand(interp, "gettimes", GetTimesObjCmd, NULL, NULL);
    Tcl_CreateCommand(interp, "noop", NoopCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "noop", NoopObjCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "testpurebytesobj", TestpurebytesobjObjCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "testsetbytearraylength", TestsetbytearraylengthObjCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "testbytestring", TestbytestringObjCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "teststringbytes", TeststringbytesObjCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "testwrongnumargs", TestWrongNumArgsObjCmd,
	    NULL, NULL);
    Tcl_CreateObjCommand(interp, "testfilesystem", TestFilesystemObjCmd,
	    NULL, NULL);
    Tcl_CreateObjCommand(interp, "testsimplefilesystem", TestSimpleFilesystemObjCmd,
	    NULL, NULL);
    Tcl_CreateObjCommand(interp, "testgetindexfromobjstruct",
	    TestGetIndexFromObjStructObjCmd, NULL, NULL);







>
>
>
>
>
>


















|







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
    }
#endif
    if (Tcl_OOInitStubs(interp) == NULL) {
	return TCL_ERROR;
    }

    if (Tcl_GetCommandInfo(interp, "::tcl::build-info", &info)) {
#if TCL_MAJOR_VERSION > 8
	if (info.isNativeObjectProc == 2) {
	    Tcl_CreateObjCommand2(interp, "::tcl::test::build-info",
		    info.objProc2, (void *)version, NULL);
    } else
#endif
	Tcl_CreateObjCommand(interp, "::tcl::test::build-info",
		info.objProc, (void *)version, NULL);
    }
    if (Tcl_PkgProvideEx(interp, "tcl::test", TCL_PATCH_LEVEL, NULL) == TCL_ERROR) {
	return TCL_ERROR;
    }

    /*
     * Create additional commands and math functions for testing Tcl.
     */

    Tcl_CreateObjCommand(interp, "gettimes", GetTimesObjCmd, NULL, NULL);
    Tcl_CreateCommand(interp, "noop", NoopCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "noop", NoopObjCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "testpurebytesobj", TestpurebytesobjObjCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "testsetbytearraylength", TestsetbytearraylengthObjCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "testbytestring", TestbytestringObjCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "teststringbytes", TeststringbytesObjCmd, NULL, NULL);
    Tcl_CreateObjCommand2(interp, "testwrongnumargs", TestWrongNumArgsObjCmd,
	    NULL, NULL);
    Tcl_CreateObjCommand(interp, "testfilesystem", TestFilesystemObjCmd,
	    NULL, NULL);
    Tcl_CreateObjCommand(interp, "testsimplefilesystem", TestSimpleFilesystemObjCmd,
	    NULL, NULL);
    Tcl_CreateObjCommand(interp, "testgetindexfromobjstruct",
	    TestGetIndexFromObjStructObjCmd, NULL, NULL);
631
632
633
634
635
636
637

638
639
640
641
642
643
644
	    NULL, NULL);
    Tcl_CreateObjCommand(interp, "testgetvarfullname",
	    TestgetvarfullnameCmd, NULL, NULL);
    Tcl_CreateCommand(interp, "testinterpdelete", TestinterpdeleteCmd,
	    NULL, NULL);
    Tcl_CreateCommand(interp, "testlink", TestlinkCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "testlinkarray", TestlinkarrayCmd, NULL, NULL);

    Tcl_CreateObjCommand(interp, "testlocale", TestlocaleCmd, NULL,
	    NULL);
    Tcl_CreateCommand(interp, "testpanic", TestpanicCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "testparseargs", TestparseargsCmd,NULL,NULL);
    Tcl_CreateObjCommand(interp, "testparser", TestparserObjCmd,
	    NULL, NULL);
    Tcl_CreateObjCommand(interp, "testparsevar", TestparsevarObjCmd,







>







652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
	    NULL, NULL);
    Tcl_CreateObjCommand(interp, "testgetvarfullname",
	    TestgetvarfullnameCmd, NULL, NULL);
    Tcl_CreateCommand(interp, "testinterpdelete", TestinterpdeleteCmd,
	    NULL, NULL);
    Tcl_CreateCommand(interp, "testlink", TestlinkCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "testlinkarray", TestlinkarrayCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "testlistrep", TestlistrepCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "testlocale", TestlocaleCmd, NULL,
	    NULL);
    Tcl_CreateCommand(interp, "testpanic", TestpanicCmd, NULL, NULL);
    Tcl_CreateObjCommand(interp, "testparseargs", TestparseargsCmd,NULL,NULL);
    Tcl_CreateObjCommand(interp, "testparser", TestparserObjCmd,
	    NULL, NULL);
    Tcl_CreateObjCommand(interp, "testparsevar", TestparsevarObjCmd,
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
	    case 2: {
		int mode;
		Tcl_UnregisterChannel(interp,
			Tcl_GetChannel(interp, "stderr", &mode));
		return TCL_ERROR;
	    }
	    case 3:
		if (objc-1) {
		    Tcl_SetVar2Ex(interp, "tcl_rcFileName", NULL, objv[1],
			    TCL_GLOBAL_ONLY);
		}
		return TCL_ERROR;
	    }
	}
    }







|







767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
	    case 2: {
		int mode;
		Tcl_UnregisterChannel(interp,
			Tcl_GetChannel(interp, "stderr", &mode));
		return TCL_ERROR;
	    }
	    case 3:
		if (objc > 1) {
		    Tcl_SetVar2Ex(interp, "tcl_rcFileName", NULL, objv[1],
			    TCL_GLOBAL_ONLY);
		}
		return TCL_ERROR;
	    }
	}
    }
790
791
792
793
794
795
796






797
798
799
800
801
802
803
{
    Tcl_CmdInfo info;

    if (Tcl_InitStubs(interp, "8.7-", 0) == NULL) {
	return TCL_ERROR;
    }
    if (Tcl_GetCommandInfo(interp, "::tcl::build-info", &info)) {






	Tcl_CreateObjCommand(interp, "::tcl::test::build-info",
		info.objProc, (void *)version, NULL);
    }
    if (Tcl_PkgProvideEx(interp, "tcl::test", TCL_PATCH_LEVEL, NULL) == TCL_ERROR) {
	return TCL_ERROR;
    }
    return Procbodytest_SafeInit(interp);







>
>
>
>
>
>







812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
{
    Tcl_CmdInfo info;

    if (Tcl_InitStubs(interp, "8.7-", 0) == NULL) {
	return TCL_ERROR;
    }
    if (Tcl_GetCommandInfo(interp, "::tcl::build-info", &info)) {
#if TCL_MAJOR_VERSION > 8
	if (info.isNativeObjectProc == 2) {
	    Tcl_CreateObjCommand2(interp, "::tcl::test::build-info",
		    info.objProc2, (void *)version, NULL);
    } else
#endif
	Tcl_CreateObjCommand(interp, "::tcl::test::build-info",
		info.objProc, (void *)version, NULL);
    }
    if (Tcl_PkgProvideEx(interp, "tcl::test", TCL_PATCH_LEVEL, NULL) == TCL_ERROR) {
	return TCL_ERROR;
    }
    return Procbodytest_SafeInit(interp);
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
	for (asyncPtr = firstHandler; asyncPtr != NULL;
		asyncPtr = asyncPtr->nextPtr) {
	    if (asyncPtr->id == id) {
		Tcl_AsyncMark(asyncPtr->handler);
		break;
	    }
	}
	Tcl_SetObjResult(interp, Tcl_NewStringObj(argv[3], -1));
	Tcl_MutexUnlock(&asyncTestMutex);
	return code;
    } else if (strcmp(argv[1], "marklater") == 0) {
	if (argc != 3) {
	    goto wrongNumArgs;
	}
	if (Tcl_GetInt(interp, argv[2], &id) != TCL_OK) {







|







928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
	for (asyncPtr = firstHandler; asyncPtr != NULL;
		asyncPtr = asyncPtr->nextPtr) {
	    if (asyncPtr->id == id) {
		Tcl_AsyncMark(asyncPtr->handler);
		break;
	    }
	}
	Tcl_SetObjResult(interp, Tcl_NewStringObj(argv[3], TCL_INDEX_NONE));
	Tcl_MutexUnlock(&asyncTestMutex);
	return code;
    } else if (strcmp(argv[1], "marklater") == 0) {
	if (argc != 3) {
	    goto wrongNumArgs;
	}
	if (Tcl_GetInt(interp, argv[2], &id) != TCL_OK) {
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
    TclFormatInt(string, code);
    listArgv[0] = asyncPtr->command;
    listArgv[1] = Tcl_GetStringResult(interp);
    listArgv[2] = string;
    listArgv[3] = NULL;
    cmd = Tcl_Merge(3, listArgv);
    if (interp != NULL) {
	code = Tcl_EvalEx(interp, cmd, -1, 0);
    } else {
	/*
	 * this should not happen, but by definition of how async handlers are
	 * invoked, it's possible.  Better error checking is needed here.
	 */
    }
    Tcl_Free(cmd);







|







997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
    TclFormatInt(string, code);
    listArgv[0] = asyncPtr->command;
    listArgv[1] = Tcl_GetStringResult(interp);
    listArgv[2] = string;
    listArgv[3] = NULL;
    cmd = Tcl_Merge(3, listArgv);
    if (interp != NULL) {
	code = Tcl_EvalEx(interp, cmd, TCL_INDEX_NONE, 0);
    } else {
	/*
	 * this should not happen, but by definition of how async handlers are
	 * invoked, it's possible.  Better error checking is needed here.
	 */
    }
    Tcl_Free(cmd);
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
}

static void
CmdDelProc1(
    void *clientData)	/* String to save. */
{
    Tcl_DStringInit(&delString);
    Tcl_DStringAppend(&delString, "CmdDelProc1 ", -1);
    Tcl_DStringAppend(&delString, (char *) clientData, -1);
}

static void
CmdDelProc2(
    void *clientData)	/* String to save. */
{
    Tcl_DStringInit(&delString);
    Tcl_DStringAppend(&delString, "CmdDelProc2 ", -1);
    Tcl_DStringAppend(&delString, (char *) clientData, -1);
}

/*
 *----------------------------------------------------------------------
 *
 * TestcmdtokenCmd --
 *







|
|







|
|







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
}

static void
CmdDelProc1(
    void *clientData)	/* String to save. */
{
    Tcl_DStringInit(&delString);
    Tcl_DStringAppend(&delString, "CmdDelProc1 ", TCL_INDEX_NONE);
    Tcl_DStringAppend(&delString, (char *) clientData, TCL_INDEX_NONE);
}

static void
CmdDelProc2(
    void *clientData)	/* String to save. */
{
    Tcl_DStringInit(&delString);
    Tcl_DStringAppend(&delString, "CmdDelProc2 ", TCL_INDEX_NONE);
    Tcl_DStringAppend(&delString, (char *) clientData, TCL_INDEX_NONE);
}

/*
 *----------------------------------------------------------------------
 *
 * TestcmdtokenCmd --
 *
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
static int
TestcmdtokenCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    int argc,			/* Number of arguments. */
    const char **argv)		/* Argument strings. */
{
    Tcl_Command token;
    int *l;
    char buf[30];


    if (argc != 3) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" option arg\"", NULL);
	return TCL_ERROR;
    }
    if (strcmp(argv[1], "create") == 0) {

	token = Tcl_CreateCommand(interp, argv[2], CmdProc1,
		(void *) "original", NULL);




	sprintf(buf, "%p", (void *)token);
	Tcl_AppendResult(interp, buf, NULL);
    } else if (strcmp(argv[1], "name") == 0) {
	Tcl_Obj *objPtr;

	if (sscanf(argv[2], "%p", &l) != 1) {













	    Tcl_AppendResult(interp, "bad command token \"", argv[2],
		    "\"", NULL);
	    return TCL_ERROR;
	}

	objPtr = Tcl_NewObj();
	Tcl_GetCommandFullName(interp, (Tcl_Command) l, objPtr);

	Tcl_AppendElement(interp,
		Tcl_GetCommandName(interp, (Tcl_Command) l));
	Tcl_AppendElement(interp, Tcl_GetString(objPtr));
	Tcl_DecrRefCount(objPtr);
    } else {
	Tcl_AppendResult(interp, "bad option \"", argv[1],
		"\": must be create or name", NULL);
	return TCL_ERROR;
    }







|
<

>







>
|

>
>
>
>
|




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






|


|







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
static int
TestcmdtokenCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    int argc,			/* Number of arguments. */
    const char **argv)		/* Argument strings. */
{
    TestCommandTokenRef *refPtr;

    char buf[30];
    int id;

    if (argc != 3) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" option arg\"", NULL);
	return TCL_ERROR;
    }
    if (strcmp(argv[1], "create") == 0) {
	refPtr = (TestCommandTokenRef *)Tcl_Alloc(sizeof(TestCommandTokenRef));
	refPtr->token = Tcl_CreateCommand(interp, argv[2], CmdProc1,
		(void *) "original", NULL);
	refPtr->id = nextCommandTokenRefId;
	nextCommandTokenRefId++;
	refPtr->nextPtr = firstCommandTokenRef;
	firstCommandTokenRef = refPtr;
	sprintf(buf, "%d", refPtr->id);
	Tcl_AppendResult(interp, buf, NULL);
    } else if (strcmp(argv[1], "name") == 0) {
	Tcl_Obj *objPtr;

	if (sscanf(argv[2], "%d", &id) != 1) {
	    Tcl_AppendResult(interp, "bad command token \"", argv[2],
		    "\"", NULL);
	    return TCL_ERROR;
	}

	for (refPtr = firstCommandTokenRef; refPtr != NULL;
		refPtr = refPtr->nextPtr) {
	    if (refPtr->id == id) {
		break;
	    }
	}

	if (refPtr == NULL) {
	    Tcl_AppendResult(interp, "bad command token \"", argv[2],
		    "\"", NULL);
	    return TCL_ERROR;
	}

	objPtr = Tcl_NewObj();
	Tcl_GetCommandFullName(interp, refPtr->token, objPtr);

	Tcl_AppendElement(interp,
		Tcl_GetCommandName(interp, refPtr->token));
	Tcl_AppendElement(interp, Tcl_GetString(objPtr));
	Tcl_DecrRefCount(objPtr);
    } else {
	Tcl_AppendResult(interp, "bad option \"", argv[1],
		"\": must be create or name", NULL);
	return TCL_ERROR;
    }
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
		" option script\"", NULL);
	return TCL_ERROR;
    }

    if (strcmp(argv[1], "tracetest") == 0) {
	Tcl_DStringInit(&buffer);
	cmdTrace = Tcl_CreateTrace(interp, 50000, CmdTraceProc, &buffer);
	result = Tcl_EvalEx(interp, argv[2], -1, 0);
	if (result == TCL_OK) {
	    Tcl_ResetResult(interp);
	    Tcl_AppendResult(interp, Tcl_DStringValue(&buffer), NULL);
	}
	Tcl_DeleteTrace(interp, cmdTrace);
	Tcl_DStringFree(&buffer);
    } else if (strcmp(argv[1], "deletetest") == 0) {
	/*
	 * Create a command trace then eval a script to check whether it is
	 * called. Note that this trace procedure removes itself as a further
	 * check of the robustness of the trace proc calling code in
	 * TclNRExecuteByteCode.
	 */

	cmdTrace = Tcl_CreateTrace(interp, 50000, CmdTraceDeleteProc, NULL);
	Tcl_EvalEx(interp, argv[2], -1, 0);
    } else if (strcmp(argv[1], "leveltest") == 0) {
	Interp *iPtr = (Interp *) interp;
	Tcl_DStringInit(&buffer);
	cmdTrace = Tcl_CreateTrace(interp, iPtr->numLevels + 4, CmdTraceProc,
		&buffer);
	result = Tcl_EvalEx(interp, argv[2], -1, 0);
	if (result == TCL_OK) {
	    Tcl_ResetResult(interp);
	    Tcl_AppendResult(interp, Tcl_DStringValue(&buffer), NULL);
	}
	Tcl_DeleteTrace(interp, cmdTrace);
	Tcl_DStringFree(&buffer);
    } else if (strcmp(argv[1], "resulttest") == 0) {
	/* Create an object-based trace, then eval a script. This is used
	 * to test return codes other than TCL_OK from the trace engine.
	 */

	static int deleteCalled;

	deleteCalled = 0;
	cmdTrace = Tcl_CreateObjTrace(interp, 50000,
		TCL_ALLOW_INLINE_COMPILATION, ObjTraceProc,
		&deleteCalled, ObjTraceDeleteProc);
	result = Tcl_EvalEx(interp, argv[2], -1, 0);
	Tcl_DeleteTrace(interp, cmdTrace);
	if (!deleteCalled) {
	    Tcl_AppendResult(interp, "Delete wasn't called", NULL);
	    return TCL_ERROR;
	} else {
	    return result;
	}
    } else if (strcmp(argv[1], "doubletest") == 0) {
	Tcl_Trace t1, t2;

	Tcl_DStringInit(&buffer);
	t1 = Tcl_CreateTrace(interp, 1, CmdTraceProc, &buffer);
	t2 = Tcl_CreateTrace(interp, 50000, CmdTraceProc, &buffer);
	result = Tcl_EvalEx(interp, argv[2], -1, 0);
	if (result == TCL_OK) {
	    Tcl_ResetResult(interp);
	    Tcl_AppendResult(interp, Tcl_DStringValue(&buffer), NULL);
	}
	Tcl_DeleteTrace(interp, t2);
	Tcl_DeleteTrace(interp, t1);
	Tcl_DStringFree(&buffer);







|















|





|

















|













|







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
		" option script\"", NULL);
	return TCL_ERROR;
    }

    if (strcmp(argv[1], "tracetest") == 0) {
	Tcl_DStringInit(&buffer);
	cmdTrace = Tcl_CreateTrace(interp, 50000, CmdTraceProc, &buffer);
	result = Tcl_EvalEx(interp, argv[2], TCL_INDEX_NONE, 0);
	if (result == TCL_OK) {
	    Tcl_ResetResult(interp);
	    Tcl_AppendResult(interp, Tcl_DStringValue(&buffer), NULL);
	}
	Tcl_DeleteTrace(interp, cmdTrace);
	Tcl_DStringFree(&buffer);
    } else if (strcmp(argv[1], "deletetest") == 0) {
	/*
	 * Create a command trace then eval a script to check whether it is
	 * called. Note that this trace procedure removes itself as a further
	 * check of the robustness of the trace proc calling code in
	 * TclNRExecuteByteCode.
	 */

	cmdTrace = Tcl_CreateTrace(interp, 50000, CmdTraceDeleteProc, NULL);
	Tcl_EvalEx(interp, argv[2], TCL_INDEX_NONE, 0);
    } else if (strcmp(argv[1], "leveltest") == 0) {
	Interp *iPtr = (Interp *) interp;
	Tcl_DStringInit(&buffer);
	cmdTrace = Tcl_CreateTrace(interp, iPtr->numLevels + 4, CmdTraceProc,
		&buffer);
	result = Tcl_EvalEx(interp, argv[2], TCL_INDEX_NONE, 0);
	if (result == TCL_OK) {
	    Tcl_ResetResult(interp);
	    Tcl_AppendResult(interp, Tcl_DStringValue(&buffer), NULL);
	}
	Tcl_DeleteTrace(interp, cmdTrace);
	Tcl_DStringFree(&buffer);
    } else if (strcmp(argv[1], "resulttest") == 0) {
	/* Create an object-based trace, then eval a script. This is used
	 * to test return codes other than TCL_OK from the trace engine.
	 */

	static int deleteCalled;

	deleteCalled = 0;
	cmdTrace = Tcl_CreateObjTrace(interp, 50000,
		TCL_ALLOW_INLINE_COMPILATION, ObjTraceProc,
		&deleteCalled, ObjTraceDeleteProc);
	result = Tcl_EvalEx(interp, argv[2], TCL_INDEX_NONE, 0);
	Tcl_DeleteTrace(interp, cmdTrace);
	if (!deleteCalled) {
	    Tcl_AppendResult(interp, "Delete wasn't called", NULL);
	    return TCL_ERROR;
	} else {
	    return result;
	}
    } else if (strcmp(argv[1], "doubletest") == 0) {
	Tcl_Trace t1, t2;

	Tcl_DStringInit(&buffer);
	t1 = Tcl_CreateTrace(interp, 1, CmdTraceProc, &buffer);
	t2 = Tcl_CreateTrace(interp, 50000, CmdTraceProc, &buffer);
	result = Tcl_EvalEx(interp, argv[2], TCL_INDEX_NONE, 0);
	if (result == TCL_OK) {
	    Tcl_ResetResult(interp);
	    Tcl_AppendResult(interp, Tcl_DStringValue(&buffer), NULL);
	}
	Tcl_DeleteTrace(interp, t2);
	Tcl_DeleteTrace(interp, t1);
	Tcl_DStringFree(&buffer);
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
    TCL_UNUSED(Tcl_Command),
    TCL_UNUSED(int) /*objc*/,
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    const char *word = Tcl_GetString(objv[0]);

    if (!strcmp(word, "Error")) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(command, -1));
	return TCL_ERROR;
    } else if (!strcmp(word, "Break")) {
	return TCL_BREAK;
    } else if (!strcmp(word, "Continue")) {
	return TCL_CONTINUE;
    } else if (!strcmp(word, "Return")) {
	return TCL_RETURN;







|







1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
    TCL_UNUSED(Tcl_Command),
    TCL_UNUSED(int) /*objc*/,
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    const char *word = Tcl_GetString(objv[0]);

    if (!strcmp(word, "Error")) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(command, TCL_INDEX_NONE));
	return TCL_ERROR;
    } else if (!strcmp(word, "Break")) {
	return TCL_BREAK;
    } else if (!strcmp(word, "Continue")) {
	return TCL_CONTINUE;
    } else if (!strcmp(word, "Return")) {
	return TCL_RETURN;
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654

static void
DelDeleteProc(
    void *clientData)	/* String command to evaluate. */
{
    DelCmd *dPtr = (DelCmd *)clientData;

    Tcl_EvalEx(dPtr->interp, dPtr->deleteCmd, -1, 0);
    Tcl_ResetResult(dPtr->interp);
    Tcl_Free(dPtr->deleteCmd);
    Tcl_Free(dPtr);
}

/*
 *----------------------------------------------------------------------







|







1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700

static void
DelDeleteProc(
    void *clientData)	/* String command to evaluate. */
{
    DelCmd *dPtr = (DelCmd *)clientData;

    Tcl_EvalEx(dPtr->interp, dPtr->deleteCmd, TCL_INDEX_NONE, 0);
    Tcl_ResetResult(dPtr->interp);
    Tcl_Free(dPtr->deleteCmd);
    Tcl_Free(dPtr);
}

/*
 *----------------------------------------------------------------------
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
			       TCL_EXACT, &type) != TCL_OK) {
	fprintf(stderr, "bad value? %g\n", d);
	return TCL_ERROR;
    }
    type = types[type];
    if (objc > 4) {
	if (strcmp(Tcl_GetString(objv[4]), "shorten")) {
	    Tcl_SetObjResult(interp, Tcl_NewStringObj("bad flag", -1));
	    return TCL_ERROR;
	}
	type |= TCL_DD_SHORTEST;
    }
    str = TclDoubleDigits(d, ndigits, type, &decpt, &signum, &endPtr);
    strObj = Tcl_NewStringObj(str, endPtr-str);
    Tcl_Free(str);







|







1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
			       TCL_EXACT, &type) != TCL_OK) {
	fprintf(stderr, "bad value? %g\n", d);
	return TCL_ERROR;
    }
    type = types[type];
    if (objc > 4) {
	if (strcmp(Tcl_GetString(objv[4]), "shorten")) {
	    Tcl_SetObjResult(interp, Tcl_NewStringObj("bad flag", TCL_INDEX_NONE));
	    return TCL_ERROR;
	}
	type |= TCL_DD_SHORTEST;
    }
    str = TclDoubleDigits(d, ndigits, type, &decpt, &signum, &endPtr);
    strObj = Tcl_NewStringObj(str, endPtr-str);
    Tcl_Free(str);
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
    int *dstWrotePtr,		/* Filled with number of bytes stored. */
    int *dstCharsPtr)		/* Filled with number of chars stored. */
{
    int len;
    TclEncoding *encodingPtr;

    encodingPtr = (TclEncoding *) clientData;
    Tcl_EvalEx(encodingPtr->interp, encodingPtr->toUtfCmd, -1, TCL_EVAL_GLOBAL);

    len = strlen(Tcl_GetStringResult(encodingPtr->interp));
    if (len > dstLen) {
	len = dstLen;
    }
    memcpy(dst, Tcl_GetStringResult(encodingPtr->interp), len);
    Tcl_ResetResult(encodingPtr->interp);







|







2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
    int *dstWrotePtr,		/* Filled with number of bytes stored. */
    int *dstCharsPtr)		/* Filled with number of chars stored. */
{
    int len;
    TclEncoding *encodingPtr;

    encodingPtr = (TclEncoding *) clientData;
    Tcl_EvalEx(encodingPtr->interp, encodingPtr->toUtfCmd, TCL_INDEX_NONE, TCL_EVAL_GLOBAL);

    len = strlen(Tcl_GetStringResult(encodingPtr->interp));
    if (len > dstLen) {
	len = dstLen;
    }
    memcpy(dst, Tcl_GetStringResult(encodingPtr->interp), len);
    Tcl_ResetResult(encodingPtr->interp);
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
    int *dstWrotePtr,		/* Filled with number of bytes stored. */
    int *dstCharsPtr)		/* Filled with number of chars stored. */
{
    int len;
    TclEncoding *encodingPtr;

    encodingPtr = (TclEncoding *) clientData;
    Tcl_EvalEx(encodingPtr->interp, encodingPtr->fromUtfCmd, -1, TCL_EVAL_GLOBAL);

    len = strlen(Tcl_GetStringResult(encodingPtr->interp));
    if (len > dstLen) {
	len = dstLen;
    }
    memcpy(dst, Tcl_GetStringResult(encodingPtr->interp), len);
    Tcl_ResetResult(encodingPtr->interp);







|







2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
    int *dstWrotePtr,		/* Filled with number of bytes stored. */
    int *dstCharsPtr)		/* Filled with number of chars stored. */
{
    int len;
    TclEncoding *encodingPtr;

    encodingPtr = (TclEncoding *) clientData;
    Tcl_EvalEx(encodingPtr->interp, encodingPtr->fromUtfCmd, TCL_INDEX_NONE, TCL_EVAL_GLOBAL);

    len = strlen(Tcl_GetStringResult(encodingPtr->interp));
    if (len > dstLen) {
	len = dstLen;
    }
    memcpy(dst, Tcl_GetStringResult(encodingPtr->interp), len);
    Tcl_ResetResult(encodingPtr->interp);
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
		stringVar = NULL;
	    } else {
		stringVar = (char *)Tcl_Alloc(strlen(argv[5]) + 1);
		strcpy(stringVar, argv[5]);
	    }
	}
	if (argv[6][0] != 0) {
	    tmp = Tcl_NewStringObj(argv[6], -1);
	    if (Tcl_GetWideIntFromObj(interp, tmp, &wideVar) != TCL_OK) {
		Tcl_DecrRefCount(tmp);
		return TCL_ERROR;
	    }
	    Tcl_DecrRefCount(tmp);
	}
	if (argv[7][0]) {







|







3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
		stringVar = NULL;
	    } else {
		stringVar = (char *)Tcl_Alloc(strlen(argv[5]) + 1);
		strcpy(stringVar, argv[5]);
	    }
	}
	if (argv[6][0] != 0) {
	    tmp = Tcl_NewStringObj(argv[6], TCL_INDEX_NONE);
	    if (Tcl_GetWideIntFromObj(interp, tmp, &wideVar) != TCL_OK) {
		Tcl_DecrRefCount(tmp);
		return TCL_ERROR;
	    }
	    Tcl_DecrRefCount(tmp);
	}
	if (argv[7][0]) {
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
	    if (Tcl_GetDouble(interp, argv[14], &d) != TCL_OK) {
		return TCL_ERROR;
	    }
	    floatVar = (float) d;
	}
	if (argv[15][0]) {
	    Tcl_WideInt w;
	    tmp = Tcl_NewStringObj(argv[15], -1);
	    if (Tcl_GetWideIntFromObj(interp, tmp, &w) != TCL_OK) {
		Tcl_DecrRefCount(tmp);
		return TCL_ERROR;
	    }
	    Tcl_DecrRefCount(tmp);
	    uwideVar = (Tcl_WideUInt) w;
	}







|







3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
	    if (Tcl_GetDouble(interp, argv[14], &d) != TCL_OK) {
		return TCL_ERROR;
	    }
	    floatVar = (float) d;
	}
	if (argv[15][0]) {
	    Tcl_WideInt w;
	    tmp = Tcl_NewStringObj(argv[15], TCL_INDEX_NONE);
	    if (Tcl_GetWideIntFromObj(interp, tmp, &w) != TCL_OK) {
		Tcl_DecrRefCount(tmp);
		return TCL_ERROR;
	    }
	    Tcl_DecrRefCount(tmp);
	    uwideVar = (Tcl_WideUInt) w;
	}
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
	    } else {
		stringVar = (char *)Tcl_Alloc(strlen(argv[5]) + 1);
		strcpy(stringVar, argv[5]);
	    }
	    Tcl_UpdateLinkedVar(interp, "string");
	}
	if (argv[6][0] != 0) {
	    tmp = Tcl_NewStringObj(argv[6], -1);
	    if (Tcl_GetWideIntFromObj(interp, tmp, &wideVar) != TCL_OK) {
		Tcl_DecrRefCount(tmp);
		return TCL_ERROR;
	    }
	    Tcl_DecrRefCount(tmp);
	    Tcl_UpdateLinkedVar(interp, "wide");
	}







|







3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
	    } else {
		stringVar = (char *)Tcl_Alloc(strlen(argv[5]) + 1);
		strcpy(stringVar, argv[5]);
	    }
	    Tcl_UpdateLinkedVar(interp, "string");
	}
	if (argv[6][0] != 0) {
	    tmp = Tcl_NewStringObj(argv[6], TCL_INDEX_NONE);
	    if (Tcl_GetWideIntFromObj(interp, tmp, &wideVar) != TCL_OK) {
		Tcl_DecrRefCount(tmp);
		return TCL_ERROR;
	    }
	    Tcl_DecrRefCount(tmp);
	    Tcl_UpdateLinkedVar(interp, "wide");
	}
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
		return TCL_ERROR;
	    }
	    floatVar = (float) d;
	    Tcl_UpdateLinkedVar(interp, "float");
	}
	if (argv[15][0]) {
	    Tcl_WideInt w;
	    tmp = Tcl_NewStringObj(argv[15], -1);
	    if (Tcl_GetWideIntFromObj(interp, tmp, &w) != TCL_OK) {
		Tcl_DecrRefCount(tmp);
		return TCL_ERROR;
	    }
	    Tcl_DecrRefCount(tmp);
	    uwideVar = (Tcl_WideUInt) w;
	    Tcl_UpdateLinkedVar(interp, "uwide");







|







3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
		return TCL_ERROR;
	    }
	    floatVar = (float) d;
	    Tcl_UpdateLinkedVar(interp, "float");
	}
	if (argv[15][0]) {
	    Tcl_WideInt w;
	    tmp = Tcl_NewStringObj(argv[15], TCL_INDEX_NONE);
	    if (Tcl_GetWideIntFromObj(interp, tmp, &w) != TCL_OK) {
		Tcl_DecrRefCount(tmp);
		return TCL_ERROR;
	    }
	    Tcl_DecrRefCount(tmp);
	    uwideVar = (Tcl_WideUInt) w;
	    Tcl_UpdateLinkedVar(interp, "uwide");
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
























































































































































3386
3387
3388
3389
3390
3391
3392
	    i++;
	}
	if (Tcl_GetIndexFromObj(interp, objv[i++], LinkType, "type", 0,
 		&typeIndex) != TCL_OK) {
	    return TCL_ERROR;
	}
	if (Tcl_GetIntFromObj(interp, objv[i++], &size) == TCL_ERROR) {
	    Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong size value", -1));
	    return TCL_ERROR;
	}
	name = Tcl_GetString(objv[i++]);

	/*
	 * If no address is given request one in the underlying function
	 */

	if (i < objc) {
	    if (Tcl_GetWideIntFromObj(interp, objv[i], &addr) == TCL_ERROR) {
 		Tcl_SetObjResult(interp, Tcl_NewStringObj(
			"wrong address value", -1));
		return TCL_ERROR;
	    }
	} else {
	    addr = 0;
	}
	return Tcl_LinkArray(interp, name, INT2PTR(addr),
		LinkTypes[typeIndex] | readonly, size);
    }
    return TCL_OK;

  wrongArgs:
    Tcl_WrongNumArgs(interp, 2, objv, "?-readonly? type size name ?address?");
    return TCL_ERROR;
}

























































































































































/*
 *----------------------------------------------------------------------
 *
 * TestlocaleCmd --
 *
 *	This procedure implements the "testlocale" command.  It is used







|











|














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







3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
	    i++;
	}
	if (Tcl_GetIndexFromObj(interp, objv[i++], LinkType, "type", 0,
 		&typeIndex) != TCL_OK) {
	    return TCL_ERROR;
	}
	if (Tcl_GetIntFromObj(interp, objv[i++], &size) == TCL_ERROR) {
	    Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong size value", TCL_INDEX_NONE));
	    return TCL_ERROR;
	}
	name = Tcl_GetString(objv[i++]);

	/*
	 * If no address is given request one in the underlying function
	 */

	if (i < objc) {
	    if (Tcl_GetWideIntFromObj(interp, objv[i], &addr) == TCL_ERROR) {
 		Tcl_SetObjResult(interp, Tcl_NewStringObj(
			"wrong address value", TCL_INDEX_NONE));
		return TCL_ERROR;
	    }
	} else {
	    addr = 0;
	}
	return Tcl_LinkArray(interp, name, INT2PTR(addr),
		LinkTypes[typeIndex] | readonly, size);
    }
    return TCL_OK;

  wrongArgs:
    Tcl_WrongNumArgs(interp, 2, objv, "?-readonly? type size name ?address?");
    return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *
 * TestlistrepCmd --
 *
 *      This function is invoked to generate a list object with a specific
 *	internal representation.
 *
 * Results:
 *      A standard Tcl result.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TestlistrepCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,         /* Current interpreter. */
    int objc,                   /* Number of arguments. */
    Tcl_Obj *const objv[])      /* Argument objects. */
{
    /* Subcommands supported by this command */
    const char* subcommands[] = {
	"new",
	"describe",
	"config",
	"validate",
	NULL
    };
    enum {
	LISTREP_NEW,
	LISTREP_DESCRIBE,
	LISTREP_CONFIG,
	LISTREP_VALIDATE
    } cmdIndex;
    Tcl_Obj *resultObj = NULL;

    if (objc < 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "command ?arg ...?");
	return TCL_ERROR;
    }
    if (Tcl_GetIndexFromObj(
	    interp, objv[1], subcommands, "command", 0, &cmdIndex)
	!= TCL_OK) {
	return TCL_ERROR;
    }
    switch (cmdIndex) {
    case LISTREP_NEW:
	if (objc < 3 || objc > 5) {
	    Tcl_WrongNumArgs(interp, 2, objv, "length ?leadSpace endSpace?");
	    return TCL_ERROR;
	} else {
	    int length;
	    int leadSpace = 0;
	    int endSpace = 0;
	    if (Tcl_GetIntFromObj(interp, objv[2], &length) != TCL_OK) {
		return TCL_ERROR;
	    }
	    if (objc > 3) {
		if (Tcl_GetIntFromObj(interp, objv[3], &leadSpace) != TCL_OK) {
		    return TCL_ERROR;
		}
		if (objc > 4) {
		    if (Tcl_GetIntFromObj(interp, objv[4], &endSpace)
			!= TCL_OK) {
			return TCL_ERROR;
		    }
		}
	    }
	    resultObj = TclListTestObj(length, leadSpace, endSpace);
	}
	break;

    case LISTREP_DESCRIBE:
#define APPEND_FIELD(targetObj_, structPtr_, fld_)                        \
    do {                                                                  \
	Tcl_ListObjAppendElement(                                         \
	    interp, (targetObj_), Tcl_NewStringObj(#fld_, TCL_INDEX_NONE));           \
	Tcl_ListObjAppendElement(                                         \
	    interp, (targetObj_), Tcl_NewWideIntObj((structPtr_)->fld_)); \
    } while (0)
	if (objc != 3) {
	    Tcl_WrongNumArgs(interp, 2, objv, "object");
	    return TCL_ERROR;
	} else {
	    Tcl_Obj **objs;
	    ListSizeT nobjs;
	    ListRep listRep;
	    Tcl_Obj *listRepObjs[4];

	    /* Force list representation */
	    if (Tcl_ListObjGetElements(interp, objv[2], &nobjs, &objs) != TCL_OK) {
		return TCL_ERROR;
	    }
	    ListObjGetRep(objv[2], &listRep);
	    listRepObjs[0] = Tcl_NewStringObj("store", TCL_INDEX_NONE);
	    listRepObjs[1] = Tcl_NewListObj(12, NULL);
	    Tcl_ListObjAppendElement(
		interp, listRepObjs[1], Tcl_NewStringObj("memoryAddress", TCL_INDEX_NONE));
	    Tcl_ListObjAppendElement(
		interp, listRepObjs[1], Tcl_ObjPrintf("%p", listRep.storePtr));
	    APPEND_FIELD(listRepObjs[1], listRep.storePtr, firstUsed);
	    APPEND_FIELD(listRepObjs[1], listRep.storePtr, numUsed);
	    APPEND_FIELD(listRepObjs[1], listRep.storePtr, numAllocated);
	    APPEND_FIELD(listRepObjs[1], listRep.storePtr, refCount);
	    APPEND_FIELD(listRepObjs[1], listRep.storePtr, flags);
	    if (listRep.spanPtr) {
		listRepObjs[2] = Tcl_NewStringObj("span", TCL_INDEX_NONE);
		listRepObjs[3] = Tcl_NewListObj(8, NULL);
		Tcl_ListObjAppendElement(interp,
					 listRepObjs[3],
					 Tcl_NewStringObj("memoryAddress", TCL_INDEX_NONE));
		Tcl_ListObjAppendElement(
		    interp, listRepObjs[3], Tcl_ObjPrintf("%p", listRep.spanPtr));
		APPEND_FIELD(listRepObjs[3], listRep.spanPtr, spanStart);
		APPEND_FIELD(
		    listRepObjs[3], listRep.spanPtr, spanLength);
		APPEND_FIELD(listRepObjs[3], listRep.spanPtr, refCount);
	    }
	    resultObj = Tcl_NewListObj(listRep.spanPtr ? 4 : 2, listRepObjs);
	}
#undef APPEND_FIELD
	break;

    case LISTREP_CONFIG:
	if (objc != 2) {
	    Tcl_WrongNumArgs(interp, 2, objv, "object");
	    return TCL_ERROR;
	}
	resultObj = Tcl_NewListObj(2, NULL);
	Tcl_ListObjAppendElement(
	    NULL, resultObj, Tcl_NewStringObj("LIST_SPAN_THRESHOLD", TCL_INDEX_NONE));
	Tcl_ListObjAppendElement(
	    NULL, resultObj, Tcl_NewWideIntObj(LIST_SPAN_THRESHOLD));
	break;

    case LISTREP_VALIDATE:
	if (objc != 3) {
	    Tcl_WrongNumArgs(interp, 2, objv, "object");
	    return TCL_ERROR;
	}
	TclListObjValidate(interp, objv[2]); /* Panics if invalid */
	resultObj = Tcl_NewObj();
	break;
    }
    Tcl_SetObjResult(interp, resultObj);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TestlocaleCmd --
 *
 *	This procedure implements the "testlocale" command.  It is used
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
    if (objc == 3) {
	locale = Tcl_GetString(objv[2]);
    } else {
	locale = NULL;
    }
    locale = setlocale(lcTypes[index], locale);
    if (locale) {
	Tcl_SetStringObj(Tcl_GetObjResult(interp), locale, -1);
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *







|







3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
    if (objc == 3) {
	locale = Tcl_GetString(objv[2]);
    } else {
	locale = NULL;
    }
    locale = setlocale(lcTypes[index], locale);
    if (locale) {
	Tcl_SetStringObj(Tcl_GetObjResult(interp), locale, TCL_INDEX_NONE);
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
	    typeString = "operator";
	    break;
	default:
	    typeString = "??";
	    break;
	}
	Tcl_ListObjAppendElement(NULL, objPtr,
		Tcl_NewStringObj(typeString, -1));
	Tcl_ListObjAppendElement(NULL, objPtr,
		Tcl_NewStringObj(tokenPtr->start, tokenPtr->size));
	Tcl_ListObjAppendElement(NULL, objPtr,
		Tcl_NewWideIntObj(tokenPtr->numComponents));
    }
    Tcl_ListObjAppendElement(NULL, objPtr,
	    parsePtr->commandStart ?
	    Tcl_NewStringObj(parsePtr->commandStart + parsePtr->commandSize,
	    -1) : Tcl_NewObj());
}

/*
 *----------------------------------------------------------------------
 *
 * TestparsevarObjCmd --
 *







|








|







3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
	    typeString = "operator";
	    break;
	default:
	    typeString = "??";
	    break;
	}
	Tcl_ListObjAppendElement(NULL, objPtr,
		Tcl_NewStringObj(typeString, TCL_INDEX_NONE));
	Tcl_ListObjAppendElement(NULL, objPtr,
		Tcl_NewStringObj(tokenPtr->start, tokenPtr->size));
	Tcl_ListObjAppendElement(NULL, objPtr,
		Tcl_NewWideIntObj(tokenPtr->numComponents));
    }
    Tcl_ListObjAppendElement(NULL, objPtr,
	    parsePtr->commandStart ?
	    Tcl_NewStringObj(parsePtr->commandStart + parsePtr->commandSize,
	    TCL_INDEX_NONE) : Tcl_NewObj());
}

/*
 *----------------------------------------------------------------------
 *
 * TestparsevarObjCmd --
 *
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
	if (objc > 2 && (cflags&REG_EXPECT) && indices) {
	    const char *varName;
	    const char *value;
	    size_t start, end;
	    char resinfo[TCL_INTEGER_SPACE * 2];

	    varName = Tcl_GetString(objv[2]);
	    TclRegExpRangeUniChar(regExpr, -1, &start, &end);
	    sprintf(resinfo, "%" TCL_Z_MODIFIER "d %" TCL_Z_MODIFIER "d", start, (end-1));
	    value = Tcl_SetVar2(interp, varName, NULL, resinfo, 0);
	    if (value == NULL) {
		Tcl_AppendResult(interp, "couldn't set variable \"",
			varName, "\"", NULL);
		return TCL_ERROR;
	    }







|







4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
	if (objc > 2 && (cflags&REG_EXPECT) && indices) {
	    const char *varName;
	    const char *value;
	    size_t start, end;
	    char resinfo[TCL_INTEGER_SPACE * 2];

	    varName = Tcl_GetString(objv[2]);
	    TclRegExpRangeUniChar(regExpr, TCL_INDEX_NONE, &start, &end);
	    sprintf(resinfo, "%" TCL_Z_MODIFIER "d %" TCL_Z_MODIFIER "d", start, (end-1));
	    value = Tcl_SetVar2(interp, varName, NULL, resinfo, 0);
	    if (value == NULL) {
		Tcl_AppendResult(interp, "couldn't set variable \"",
			varName, "\"", NULL);
		return TCL_ERROR;
	    }
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
    if (strcmp(argv[1], "cmd") == 0) {
	if (argc != 3) {
	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		    " cmd script", NULL);
	    return TCL_ERROR;
	}
	if (interp2 != NULL) {
	    code = Tcl_EvalEx(interp2, argv[2], -1, TCL_EVAL_GLOBAL);
	    Tcl_SetObjResult(interp, Tcl_GetObjResult(interp2));
	    return code;
	} else {
	    Tcl_AppendResult(interp,
		    "called \"testfevent code\" before \"testfevent create\"",
		    NULL);
	    return TCL_ERROR;







|







4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
    if (strcmp(argv[1], "cmd") == 0) {
	if (argc != 3) {
	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		    " cmd script", NULL);
	    return TCL_ERROR;
	}
	if (interp2 != NULL) {
	    code = Tcl_EvalEx(interp2, argv[2], TCL_INDEX_NONE, TCL_EVAL_GLOBAL);
	    Tcl_SetObjResult(interp, Tcl_GetObjResult(interp2));
	    return code;
	} else {
	    Tcl_AppendResult(interp,
		    "called \"testfevent code\" before \"testfevent create\"",
		    NULL);
	    return TCL_ERROR;
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
    Tcl_GetTime(&stop);
    timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
    fprintf(stderr, "   %.3f usec per Tcl_DecrRefCount\n", timePer/5000);
    Tcl_Free(objv);

    /* TclGetString 100000 times */
    fprintf(stderr, "Tcl_GetStringFromObj of \"12345\" 100000 times\n");
    objPtr = Tcl_NewStringObj("12345", -1);
    Tcl_GetTime(&start);
    for (i = 0;  i < 100000;  i++) {
	(void) TclGetString(objPtr);
    }
    Tcl_GetTime(&stop);
    timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
    fprintf(stderr, "   %.3f usec per Tcl_GetStringFromObj of \"12345\"\n",







|







5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
    Tcl_GetTime(&stop);
    timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
    fprintf(stderr, "   %.3f usec per Tcl_DecrRefCount\n", timePer/5000);
    Tcl_Free(objv);

    /* TclGetString 100000 times */
    fprintf(stderr, "Tcl_GetStringFromObj of \"12345\" 100000 times\n");
    objPtr = Tcl_NewStringObj("12345", TCL_INDEX_NONE);
    Tcl_GetTime(&start);
    for (i = 0;  i < 100000;  i++) {
	(void) TclGetString(objPtr);
    }
    Tcl_GetTime(&stop);
    timePer = (stop.sec - start.sec)*1000000 + (stop.usec - start.usec);
    fprintf(stderr, "   %.3f usec per Tcl_GetStringFromObj of \"12345\"\n",
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
	Tcl_SetResult(interp, buf, TCL_DYNAMIC);
	break;
    }
    case RESULT_DYNAMIC:
	Tcl_SetResult(interp, (char *)"dynamic result", TestsaveresultFree);
	break;
    case RESULT_OBJECT:
	objPtr = Tcl_NewStringObj("object result", -1);
	Tcl_SetObjResult(interp, objPtr);
	break;
    }

    Tcl_SaveResult(interp, &state);

    if (index == RESULT_OBJECT) {
	result = Tcl_EvalObjEx(interp, objv[2], 0);
    } else {
	result = Tcl_EvalEx(interp, Tcl_GetString(objv[2]), -1, 0);
    }

    if (discard) {
	Tcl_DiscardResult(&state);
    } else {
	Tcl_RestoreResult(interp, &state);
	result = TCL_OK;







|









|







5525
5526
5527
5528
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
	Tcl_SetResult(interp, buf, TCL_DYNAMIC);
	break;
    }
    case RESULT_DYNAMIC:
	Tcl_SetResult(interp, (char *)"dynamic result", TestsaveresultFree);
	break;
    case RESULT_OBJECT:
	objPtr = Tcl_NewStringObj("object result", TCL_INDEX_NONE);
	Tcl_SetObjResult(interp, objPtr);
	break;
    }

    Tcl_SaveResult(interp, &state);

    if (index == RESULT_OBJECT) {
	result = Tcl_EvalObjEx(interp, objv[2], 0);
    } else {
	result = Tcl_EvalEx(interp, Tcl_GetString(objv[2]), TCL_INDEX_NONE, 0);
    }

    if (discard) {
	Tcl_DiscardResult(&state);
    } else {
	Tcl_RestoreResult(interp, &state);
	result = TCL_OK;
5586
5587
5588
5589
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
    } else {
	statePtr	= NULL;
	chan		= NULL;
    }

    if ((cmdName[0] == 's') && (strncmp(cmdName, "setchannelerror", len) == 0)) {

	Tcl_Obj *msg = Tcl_NewStringObj(argv[3],-1);

	Tcl_IncrRefCount(msg);
	Tcl_SetChannelError(chan, msg);
	Tcl_DecrRefCount(msg);

	Tcl_GetChannelError(chan, &msg);
	Tcl_SetObjResult(interp, msg);
	Tcl_DecrRefCount(msg);
	return TCL_OK;
    }
    if ((cmdName[0] == 's') && (strncmp(cmdName, "setchannelerrorinterp", len) == 0)) {

	Tcl_Obj *msg = Tcl_NewStringObj(argv[3],-1);

	Tcl_IncrRefCount(msg);
	Tcl_SetChannelErrorInterp(interp, msg);
	Tcl_DecrRefCount(msg);

	Tcl_GetChannelErrorInterp(interp, &msg);
	Tcl_SetObjResult(interp, msg);







|












|







5784
5785
5786
5787
5788
5789
5790
5791
5792
5793
5794
5795
5796
5797
5798
5799
5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
    } else {
	statePtr	= NULL;
	chan		= NULL;
    }

    if ((cmdName[0] == 's') && (strncmp(cmdName, "setchannelerror", len) == 0)) {

	Tcl_Obj *msg = Tcl_NewStringObj(argv[3], TCL_INDEX_NONE);

	Tcl_IncrRefCount(msg);
	Tcl_SetChannelError(chan, msg);
	Tcl_DecrRefCount(msg);

	Tcl_GetChannelError(chan, &msg);
	Tcl_SetObjResult(interp, msg);
	Tcl_DecrRefCount(msg);
	return TCL_OK;
    }
    if ((cmdName[0] == 's') && (strncmp(cmdName, "setchannelerrorinterp", len) == 0)) {

	Tcl_Obj *msg = Tcl_NewStringObj(argv[3], TCL_INDEX_NONE);

	Tcl_IncrRefCount(msg);
	Tcl_SetChannelErrorInterp(interp, msg);
	Tcl_DecrRefCount(msg);

	Tcl_GetChannelErrorInterp(interp, &msg);
	Tcl_SetObjResult(interp, msg);
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
5960
5961
	if (strcmp(argv[3], "-command") != 0) {
	    Tcl_AppendResult(interp, "bad argument \"", argv[3],
		    "\": should be \"-command\"", NULL);
	    return TCL_ERROR;
	}

	return TclChannelTransform(interp, chan,
		Tcl_NewStringObj(argv[4], -1));
    }

    if ((cmdName[0] == 'u') && (strncmp(cmdName, "unstack", len) == 0)) {
	/*
	 * Syntax: unstack channel
	 */








|







6145
6146
6147
6148
6149
6150
6151
6152
6153
6154
6155
6156
6157
6158
6159
	if (strcmp(argv[3], "-command") != 0) {
	    Tcl_AppendResult(interp, "bad argument \"", argv[3],
		    "\": should be \"-command\"", NULL);
	    return TCL_ERROR;
	}

	return TclChannelTransform(interp, chan,
		Tcl_NewStringObj(argv[4], TCL_INDEX_NONE));
    }

    if ((cmdName[0] == 'u') && (strncmp(cmdName, "unstack", len) == 0)) {
	/*
	 * Syntax: unstack channel
	 */

6038
6039
6040
6041
6042
6043
6044
6045
6046
6047
6048
6049
6050
6051
6052
	esPtr = (EventScriptRecord *)Tcl_Alloc(sizeof(EventScriptRecord));
	esPtr->nextPtr = statePtr->scriptRecordPtr;
	statePtr->scriptRecordPtr = esPtr;

	esPtr->chanPtr = chanPtr;
	esPtr->interp = interp;
	esPtr->mask = mask;
	esPtr->scriptPtr = Tcl_NewStringObj(argv[4], -1);
	Tcl_IncrRefCount(esPtr->scriptPtr);

	Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
		TclChannelEventScriptInvoker, esPtr);

	return TCL_OK;
    }







|







6236
6237
6238
6239
6240
6241
6242
6243
6244
6245
6246
6247
6248
6249
6250
	esPtr = (EventScriptRecord *)Tcl_Alloc(sizeof(EventScriptRecord));
	esPtr->nextPtr = statePtr->scriptRecordPtr;
	statePtr->scriptRecordPtr = esPtr;

	esPtr->chanPtr = chanPtr;
	esPtr->interp = interp;
	esPtr->mask = mask;
	esPtr->scriptPtr = Tcl_NewStringObj(argv[4], TCL_INDEX_NONE);
	Tcl_IncrRefCount(esPtr->scriptPtr);

	Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
		TclChannelEventScriptInvoker, esPtr);

	return TCL_OK;
    }
6105
6106
6107
6108
6109
6110
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
	}
	resultListPtr = Tcl_GetObjResult(interp);
	for (esPtr = statePtr->scriptRecordPtr;
	     esPtr != NULL;
	     esPtr = esPtr->nextPtr) {
	    if (esPtr->mask) {
		Tcl_ListObjAppendElement(interp, resultListPtr, Tcl_NewStringObj(
		    (esPtr->mask == TCL_READABLE) ? "readable" : "writable", -1));
	    } else {
		Tcl_ListObjAppendElement(interp, resultListPtr,
			Tcl_NewStringObj("none", -1));
	    }
	    Tcl_ListObjAppendElement(interp, resultListPtr, esPtr->scriptPtr);
	}
	Tcl_SetObjResult(interp, resultListPtr);
	return TCL_OK;
    }








|


|







6303
6304
6305
6306
6307
6308
6309
6310
6311
6312
6313
6314
6315
6316
6317
6318
6319
6320
	}
	resultListPtr = Tcl_GetObjResult(interp);
	for (esPtr = statePtr->scriptRecordPtr;
	     esPtr != NULL;
	     esPtr = esPtr->nextPtr) {
	    if (esPtr->mask) {
		Tcl_ListObjAppendElement(interp, resultListPtr, Tcl_NewStringObj(
		    (esPtr->mask == TCL_READABLE) ? "readable" : "writable", TCL_INDEX_NONE));
	    } else {
		Tcl_ListObjAppendElement(interp, resultListPtr,
			Tcl_NewStringObj("none", TCL_INDEX_NONE));
	    }
	    Tcl_ListObjAppendElement(interp, resultListPtr, esPtr->scriptPtr);
	}
	Tcl_SetObjResult(interp, resultListPtr);
	return TCL_OK;
    }

6198
6199
6200
6201
6202
6203
6204




6205
6206
6207
6208
6209
6210
6211
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */





static int
TestSocketCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Interpreter for result. */
    int argc,			/* Count of additional args. */
    const char **argv)		/* Additional arg strings. */
{







>
>
>
>







6396
6397
6398
6399
6400
6401
6402
6403
6404
6405
6406
6407
6408
6409
6410
6411
6412
6413
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

#define TCP_ASYNC_TEST_MODE	(1<<8)	/* Async testing activated.  Do not
					 * automatically continue connection
					 * process. */

static int
TestSocketCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Interpreter for result. */
    int argc,			/* Count of additional args. */
    const char **argv)		/* Additional arg strings. */
{
6219
6220
6221
6222
6223
6224
6225

6226
6227
6228
6229
6230
6231
6232
    }
    cmdName = argv[1];
    len = strlen(cmdName);

    if ((cmdName[0] == 't') && (strncmp(cmdName, "testflags", len) == 0)) {
        Tcl_Channel hChannel;
        int modePtr;

        TcpState *statePtr;
        /* Set test value in the socket driver
         */
        /* Check for argument "channel name"
         */
        if (argc < 4) {
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],







>







6421
6422
6423
6424
6425
6426
6427
6428
6429
6430
6431
6432
6433
6434
6435
    }
    cmdName = argv[1];
    len = strlen(cmdName);

    if ((cmdName[0] == 't') && (strncmp(cmdName, "testflags", len) == 0)) {
        Tcl_Channel hChannel;
        int modePtr;
        int testMode;
        TcpState *statePtr;
        /* Set test value in the socket driver
         */
        /* Check for argument "channel name"
         */
        if (argc < 4) {
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
6240
6241
6242
6243
6244
6245
6246




6247



6248
6249
6250
6251
6252
6253
6254
        }
        statePtr = (TcpState *)Tcl_GetChannelInstanceData(hChannel);
        if ( NULL == statePtr) {
            Tcl_AppendResult(interp, "No channel instance data:", argv[2],
                    NULL);
            return TCL_ERROR;
        }




        statePtr->testFlags = atoi(argv[3]);



        return TCL_OK;
    }

    Tcl_AppendResult(interp, "bad option \"", cmdName, "\": should be "
	    "testflags", NULL);
    return TCL_ERROR;
}







>
>
>
>
|
>
>
>







6443
6444
6445
6446
6447
6448
6449
6450
6451
6452
6453
6454
6455
6456
6457
6458
6459
6460
6461
6462
6463
6464
        }
        statePtr = (TcpState *)Tcl_GetChannelInstanceData(hChannel);
        if ( NULL == statePtr) {
            Tcl_AppendResult(interp, "No channel instance data:", argv[2],
                    NULL);
            return TCL_ERROR;
        }
        if (Tcl_GetBoolean(interp, argv[3], &testMode) != TCL_OK) {
            return TCL_ERROR;
        }
        if (testMode) {
            statePtr->flags |= TCP_ASYNC_TEST_MODE;
        } else {
            statePtr->flags &= ~TCP_ASYNC_TEST_MODE;
        }
        return TCL_OK;
    }

    Tcl_AppendResult(interp, "bad option \"", cmdName, "\": should be "
	    "testflags", NULL);
    return TCL_ERROR;
}
6317
6318
6319
6320
6321
6322
6323
6324
6325
6326
6327
6328
6329
6330
6331
6332
6333
6334
6335
6336
6337
6338
6339
6340
6341
6342
6343
6344
6345
6346
6347
6348
6349
6350
6351

6352
6353
6354
6355
6356
6357
6358
 *----------------------------------------------------------------------
 */

static int
TestWrongNumArgsObjCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    int i, length;
    const char *msg;

    if (objc < 3) {
	/*
	 * Don't use Tcl_WrongNumArgs here, as that is the function
	 * we want to test!
	 */
	Tcl_AppendResult(interp, "insufficient arguments", NULL);
	return TCL_ERROR;
    }

    if (Tcl_GetIntFromObj(interp, objv[1], &i) != TCL_OK) {
	return TCL_ERROR;
    }

    msg = Tcl_GetStringFromObj(objv[2], &length);
    if (length == 0) {
	msg = NULL;
    }

    if (i > objc - 3) {
	/*
	 * Asked for more arguments than were given.
	 */

	Tcl_AppendResult(interp, "insufficient arguments", NULL);
	return TCL_ERROR;
    }

    Tcl_WrongNumArgs(interp, i, &(objv[3]), msg);
    return TCL_OK;
}







|


|


|
<
<
|
<
<
<


|












>







6527
6528
6529
6530
6531
6532
6533
6534
6535
6536
6537
6538
6539
6540


6541



6542
6543
6544
6545
6546
6547
6548
6549
6550
6551
6552
6553
6554
6555
6556
6557
6558
6559
6560
6561
6562
6563
6564
 *----------------------------------------------------------------------
 */

static int
TestWrongNumArgsObjCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    size_t objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    size_t i, length;
    const char *msg;

    if (objc + 1 < 4) {


	goto insufArgs;



    }

    if (Tcl_GetIntForIndex(interp, objv[1], TCL_INDEX_NONE, &i) != TCL_OK) {
	return TCL_ERROR;
    }

    msg = Tcl_GetStringFromObj(objv[2], &length);
    if (length == 0) {
	msg = NULL;
    }

    if (i > objc - 3) {
	/*
	 * Asked for more arguments than were given.
	 */
    insufArgs:
	Tcl_AppendResult(interp, "insufficient arguments", NULL);
	return TCL_ERROR;
    }

    Tcl_WrongNumArgs(interp, i, &(objv[3]), msg);
    return TCL_OK;
}
6455
6456
6457
6458
6459
6460
6461
6462
6463
6464
6465
6466
6467
6468
6469
    if (boolVal) {
	res = Tcl_FSRegister(interp, &testReportingFilesystem);
	msg = (res == TCL_OK) ? "registered" : "failed";
    } else {
	res = Tcl_FSUnregister(&testReportingFilesystem);
	msg = (res == TCL_OK) ? "unregistered" : "failed";
    }
    Tcl_SetObjResult(interp, Tcl_NewStringObj(msg , -1));
    return res;
}

static int
TestReportInFilesystem(
    Tcl_Obj *pathPtr,
    void **clientDataPtr)







|







6661
6662
6663
6664
6665
6666
6667
6668
6669
6670
6671
6672
6673
6674
6675
    if (boolVal) {
	res = Tcl_FSRegister(interp, &testReportingFilesystem);
	msg = (res == TCL_OK) ? "registered" : "failed";
    } else {
	res = Tcl_FSUnregister(&testReportingFilesystem);
	msg = (res == TCL_OK) ? "unregistered" : "failed";
    }
    Tcl_SetObjResult(interp, Tcl_NewStringObj(msg , TCL_INDEX_NONE));
    return res;
}

static int
TestReportInFilesystem(
    Tcl_Obj *pathPtr,
    void **clientDataPtr)
6537
6538
6539
6540
6541
6542
6543
6544
6545
6546
6547
6548
6549
6550
6551
6552
6553
6554
6555
6556
6557
6558
6559
6560
6561
6562
6563
6564
    if (interp == NULL) {
	/* This is bad, but not much we can do about it */
    } else {
	Tcl_Obj *savedResult;
	Tcl_DString ds;

	Tcl_DStringInit(&ds);
	Tcl_DStringAppend(&ds, "lappend filesystemReport ", -1);
	Tcl_DStringStartSublist(&ds);
	Tcl_DStringAppendElement(&ds, cmd);
	if (path != NULL) {
	    Tcl_DStringAppendElement(&ds, Tcl_GetString(path));
	}
	if (arg2 != NULL) {
	    Tcl_DStringAppendElement(&ds, Tcl_GetString(arg2));
	}
	Tcl_DStringEndSublist(&ds);
	savedResult = Tcl_GetObjResult(interp);
	Tcl_IncrRefCount(savedResult);
	Tcl_SetObjResult(interp, Tcl_NewObj());
	Tcl_EvalEx(interp, Tcl_DStringValue(&ds), -1, 0);
	Tcl_DStringFree(&ds);
	Tcl_ResetResult(interp);
	Tcl_SetObjResult(interp, savedResult);
	Tcl_DecrRefCount(savedResult);
    }
}








|












|







6743
6744
6745
6746
6747
6748
6749
6750
6751
6752
6753
6754
6755
6756
6757
6758
6759
6760
6761
6762
6763
6764
6765
6766
6767
6768
6769
6770
    if (interp == NULL) {
	/* This is bad, but not much we can do about it */
    } else {
	Tcl_Obj *savedResult;
	Tcl_DString ds;

	Tcl_DStringInit(&ds);
	Tcl_DStringAppend(&ds, "lappend filesystemReport ", TCL_INDEX_NONE);
	Tcl_DStringStartSublist(&ds);
	Tcl_DStringAppendElement(&ds, cmd);
	if (path != NULL) {
	    Tcl_DStringAppendElement(&ds, Tcl_GetString(path));
	}
	if (arg2 != NULL) {
	    Tcl_DStringAppendElement(&ds, Tcl_GetString(arg2));
	}
	Tcl_DStringEndSublist(&ds);
	savedResult = Tcl_GetObjResult(interp);
	Tcl_IncrRefCount(savedResult);
	Tcl_SetObjResult(interp, Tcl_NewObj());
	Tcl_EvalEx(interp, Tcl_DStringValue(&ds), TCL_INDEX_NONE, 0);
	Tcl_DStringFree(&ds);
	Tcl_ResetResult(interp);
	Tcl_SetObjResult(interp, savedResult);
	Tcl_DecrRefCount(savedResult);
    }
}

6826
6827
6828
6829
6830
6831
6832
6833
6834
6835
6836
6837
6838
6839
6840
    if (boolVal) {
	res = Tcl_FSRegister(interp, &simpleFilesystem);
	msg = (res == TCL_OK) ? "registered" : "failed";
    } else {
	res = Tcl_FSUnregister(&simpleFilesystem);
	msg = (res == TCL_OK) ? "unregistered" : "failed";
    }
    Tcl_SetObjResult(interp, Tcl_NewStringObj(msg , -1));
    return res;
}

/*
 * Treats a file name 'simplefs:/foo' by using the file 'foo' in the current
 * (native) directory.
 */







|







7032
7033
7034
7035
7036
7037
7038
7039
7040
7041
7042
7043
7044
7045
7046
    if (boolVal) {
	res = Tcl_FSRegister(interp, &simpleFilesystem);
	msg = (res == TCL_OK) ? "registered" : "failed";
    } else {
	res = Tcl_FSUnregister(&simpleFilesystem);
	msg = (res == TCL_OK) ? "unregistered" : "failed";
    }
    Tcl_SetObjResult(interp, Tcl_NewStringObj(msg , TCL_INDEX_NONE));
    return res;
}

/*
 * Treats a file name 'simplefs:/foo' by using the file 'foo' in the current
 * (native) directory.
 */
6853
6854
6855
6856
6857
6858
6859
6860
6861
6862
6863
6864
6865
6866
6867

    str = Tcl_GetStringFromObj(pathPtr, &len);
    if (len < 10 || strncmp(str, "simplefs:/", 10)) {
	/* Probably shouldn't ever reach here */
	Tcl_IncrRefCount(pathPtr);
	return pathPtr;
    }
    origPtr = Tcl_NewStringObj(str+10,-1);
    Tcl_IncrRefCount(origPtr);
    return origPtr;
}

static int
SimpleMatchInDirectory(
    Tcl_Interp *interp,		/* Interpreter for error







|







7059
7060
7061
7062
7063
7064
7065
7066
7067
7068
7069
7070
7071
7072
7073

    str = Tcl_GetStringFromObj(pathPtr, &len);
    if (len < 10 || strncmp(str, "simplefs:/", 10)) {
	/* Probably shouldn't ever reach here */
	Tcl_IncrRefCount(pathPtr);
	return pathPtr;
    }
    origPtr = Tcl_NewStringObj(str+10, TCL_INDEX_NONE);
    Tcl_IncrRefCount(origPtr);
    return origPtr;
}

static int
SimpleMatchInDirectory(
    Tcl_Interp *interp,		/* Interpreter for error
6953
6954
6955
6956
6957
6958
6959
6960
6961
6962
6963
6964
6965
6966
6967

static Tcl_Obj *
SimpleListVolumes(void)
{
    /* Add one new volume */
    Tcl_Obj *retVal;

    retVal = Tcl_NewStringObj("simplefs:/", -1);
    Tcl_IncrRefCount(retVal);
    return retVal;
}

/*
 * Used to check operations of Tcl_UtfNext.
 *







|







7159
7160
7161
7162
7163
7164
7165
7166
7167
7168
7169
7170
7171
7172
7173

static Tcl_Obj *
SimpleListVolumes(void)
{
    /* Add one new volume */
    Tcl_Obj *retVal;

    retVal = Tcl_NewStringObj("simplefs:/", TCL_INDEX_NONE);
    Tcl_IncrRefCount(retVal);
    return retVal;
}

/*
 * Used to check operations of Tcl_UtfNext.
 *
6982
6983
6984
6985
6986
6987
6988
6989
6990
6991
6992
6993
6994
6995
6996
6997
    static const char tobetested[] = "A\xA0\xC0\xC1\xC2\xD0\xE0\xE8\xF2\xF7\xF8\xFE\xFF";
    const char *p = tobetested;

    if (objc != 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "?-bytestring? bytes");
	return TCL_ERROR;
    }
	bytes = Tcl_GetString(objv[1]);
	numBytes = objv[1]->length;

    if (numBytes + 4U > sizeof(buffer)) {
	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		"\"testutfnext\" can only handle %" TCL_Z_MODIFIER "u bytes",
		sizeof(buffer) - 4));
	return TCL_ERROR;
    }







|
<







7188
7189
7190
7191
7192
7193
7194
7195

7196
7197
7198
7199
7200
7201
7202
    static const char tobetested[] = "A\xA0\xC0\xC1\xC2\xD0\xE0\xE8\xF2\xF7\xF8\xFE\xFF";
    const char *p = tobetested;

    if (objc != 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "?-bytestring? bytes");
	return TCL_ERROR;
    }
	bytes = Tcl_GetStringFromObj(objv[1], &numBytes);


    if (numBytes + 4U > sizeof(buffer)) {
	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		"\"testutfnext\" can only handle %" TCL_Z_MODIFIER "u bytes",
		sizeof(buffer) - 4));
	return TCL_ERROR;
    }
7042
7043
7044
7045
7046
7047
7048
7049
7050
7051
7052
7053
7054
7055
7056
7057
    const char *result;

    if (objc < 2 || objc > 3) {
	Tcl_WrongNumArgs(interp, 1, objv, "bytes ?offset?");
	return TCL_ERROR;
    }

    bytes = Tcl_GetString(objv[1]);
    numBytes = objv[1]->length;

    if (objc == 3) {
	if (TCL_OK != Tcl_GetIntForIndex(interp, objv[2], numBytes, &offset)) {
	    return TCL_ERROR;
	}
	if (offset == TCL_INDEX_NONE) {
	    offset = 0;







|
<







7247
7248
7249
7250
7251
7252
7253
7254

7255
7256
7257
7258
7259
7260
7261
    const char *result;

    if (objc < 2 || objc > 3) {
	Tcl_WrongNumArgs(interp, 1, objv, "bytes ?offset?");
	return TCL_ERROR;
    }

    bytes = Tcl_GetStringFromObj(objv[1], &numBytes);


    if (objc == 3) {
	if (TCL_OK != Tcl_GetIntForIndex(interp, objv[2], numBytes, &offset)) {
	    return TCL_ERROR;
	}
	if (offset == TCL_INDEX_NONE) {
	    offset = 0;
7075
7076
7077
7078
7079
7080
7081
7082
7083
7084
7085
7086
7087
7088
7089
7090
7091
TestNumUtfCharsCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj *const objv[])
{
    if (objc > 1) {
	size_t len, limit = TCL_INDEX_NONE;
	const char *bytes = Tcl_GetString(objv[1]);
	size_t numBytes = objv[1]->length;

	if (objc > 2) {
	    if (Tcl_GetIntForIndex(interp, objv[2], numBytes, &limit) != TCL_OK) {
		return TCL_ERROR;
	    }
	    if (limit > numBytes + 1) {
		limit = numBytes + 1;







|
|
<







7279
7280
7281
7282
7283
7284
7285
7286
7287

7288
7289
7290
7291
7292
7293
7294
TestNumUtfCharsCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj *const objv[])
{
    if (objc > 1) {
	size_t numBytes, len, limit = TCL_INDEX_NONE;
	const char *bytes = Tcl_GetStringFromObj(objv[1], &numBytes);


	if (objc > 2) {
	    if (Tcl_GetIntForIndex(interp, objv[2], numBytes, &limit) != TCL_OK) {
		return TCL_ERROR;
	    }
	    if (limit > numBytes + 1) {
		limit = numBytes + 1;
7110
7111
7112
7113
7114
7115
7116
7117
7118
7119
7120
7121
7122
7123
7124
{
    if (objc > 1) {
	int len = -1;

	if (objc > 2) {
	    (void) Tcl_GetIntFromObj(interp, objv[2], &len);
	}
	Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_UtfFindFirst(Tcl_GetString(objv[1]), len), -1));
    }
    return TCL_OK;
}

/*
 * Used to check correct operation of Tcl_UtfFindLast
 */







|







7313
7314
7315
7316
7317
7318
7319
7320
7321
7322
7323
7324
7325
7326
7327
{
    if (objc > 1) {
	int len = -1;

	if (objc > 2) {
	    (void) Tcl_GetIntFromObj(interp, objv[2], &len);
	}
	Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_UtfFindFirst(Tcl_GetString(objv[1]), len), TCL_INDEX_NONE));
    }
    return TCL_OK;
}

/*
 * Used to check correct operation of Tcl_UtfFindLast
 */
7132
7133
7134
7135
7136
7137
7138
7139
7140
7141
7142
7143
7144
7145
7146
{
    if (objc > 1) {
	int len = -1;

	if (objc > 2) {
	    (void) Tcl_GetIntFromObj(interp, objv[2], &len);
	}
	Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_UtfFindLast(Tcl_GetString(objv[1]), len), -1));
    }
    return TCL_OK;
}

static int
TestGetIntForIndexCmd(
    TCL_UNUSED(void *),







|







7335
7336
7337
7338
7339
7340
7341
7342
7343
7344
7345
7346
7347
7348
7349
{
    if (objc > 1) {
	int len = -1;

	if (objc > 2) {
	    (void) Tcl_GetIntFromObj(interp, objv[2], &len);
	}
	Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_UtfFindLast(Tcl_GetString(objv[1]), len), TCL_INDEX_NONE));
    }
    return TCL_OK;
}

static int
TestGetIntForIndexCmd(
    TCL_UNUSED(void *),
7210
7211
7212
7213
7214
7215
7216
7217
7218
7219
7220
7221
7222
7223
7224
    }
    if (Tcl_GetIntFromObj(interp, objv[1], &index) != TCL_OK) {
	return TCL_ERROR;
    }
    status = TclWinCPUID(index, regs);
    if (status != TCL_OK) {
	Tcl_SetObjResult(interp,
		Tcl_NewStringObj("operation not available", -1));
	return status;
    }
    for (i=0 ; i<4 ; ++i) {
	regsObjs[i] = Tcl_NewWideIntObj(regs[i]);
    }
    Tcl_SetObjResult(interp, Tcl_NewListObj(4, regsObjs));
    return TCL_OK;







|







7413
7414
7415
7416
7417
7418
7419
7420
7421
7422
7423
7424
7425
7426
7427
    }
    if (Tcl_GetIntFromObj(interp, objv[1], &index) != TCL_OK) {
	return TCL_ERROR;
    }
    status = TclWinCPUID(index, regs);
    if (status != TCL_OK) {
	Tcl_SetObjResult(interp,
		Tcl_NewStringObj("operation not available", TCL_INDEX_NONE));
	return status;
    }
    for (i=0 ; i<4 ; ++i) {
	regsObjs[i] = Tcl_NewWideIntObj(regs[i]);
    }
    Tcl_SetObjResult(interp, Tcl_NewListObj(4, regsObjs));
    return TCL_OK;
7256
7257
7258
7259
7260
7261
7262
7263
7264
7265
7266
7267
7268
7269
7270
7271
7272
7273
7274
7275
7276
7277
7278
7279
7280
7281
7282
7283
7284
7285
7286
7287
7288
7289
7290
7291
7292
7293
	return TCL_ERROR;
    }

    for (i=0 ; i<limit ; i++) {
	hPtr = Tcl_CreateHashEntry(&hash, INT2PTR(i), &isNew);
	if (!isNew) {
	    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(i));
	    Tcl_AppendToObj(Tcl_GetObjResult(interp)," creation problem",-1);
	    Tcl_DeleteHashTable(&hash);
	    return TCL_ERROR;
	}
	Tcl_SetHashValue(hPtr, INT2PTR(i+42));
    }

    if (hash.numEntries != (size_t)limit) {
	Tcl_AppendResult(interp, "unexpected maximal size", NULL);
	Tcl_DeleteHashTable(&hash);
	return TCL_ERROR;
    }

    for (i=0 ; i<limit ; i++) {
	hPtr = Tcl_FindHashEntry(&hash, (char *) INT2PTR(i));
	if (hPtr == NULL) {
	    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(i));
	    Tcl_AppendToObj(Tcl_GetObjResult(interp)," lookup problem",-1);
	    Tcl_DeleteHashTable(&hash);
	    return TCL_ERROR;
	}
	if (PTR2INT(Tcl_GetHashValue(hPtr)) != i+42) {
	    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(i));
	    Tcl_AppendToObj(Tcl_GetObjResult(interp)," value problem",-1);
	    Tcl_DeleteHashTable(&hash);
	    return TCL_ERROR;
	}
	Tcl_DeleteHashEntry(hPtr);
    }

    if (hash.numEntries != 0) {







|
















|





|







7459
7460
7461
7462
7463
7464
7465
7466
7467
7468
7469
7470
7471
7472
7473
7474
7475
7476
7477
7478
7479
7480
7481
7482
7483
7484
7485
7486
7487
7488
7489
7490
7491
7492
7493
7494
7495
7496
	return TCL_ERROR;
    }

    for (i=0 ; i<limit ; i++) {
	hPtr = Tcl_CreateHashEntry(&hash, INT2PTR(i), &isNew);
	if (!isNew) {
	    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(i));
	    Tcl_AppendToObj(Tcl_GetObjResult(interp)," creation problem", TCL_INDEX_NONE);
	    Tcl_DeleteHashTable(&hash);
	    return TCL_ERROR;
	}
	Tcl_SetHashValue(hPtr, INT2PTR(i+42));
    }

    if (hash.numEntries != (size_t)limit) {
	Tcl_AppendResult(interp, "unexpected maximal size", NULL);
	Tcl_DeleteHashTable(&hash);
	return TCL_ERROR;
    }

    for (i=0 ; i<limit ; i++) {
	hPtr = Tcl_FindHashEntry(&hash, (char *) INT2PTR(i));
	if (hPtr == NULL) {
	    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(i));
	    Tcl_AppendToObj(Tcl_GetObjResult(interp)," lookup problem", TCL_INDEX_NONE);
	    Tcl_DeleteHashTable(&hash);
	    return TCL_ERROR;
	}
	if (PTR2INT(Tcl_GetHashValue(hPtr)) != i+42) {
	    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(i));
	    Tcl_AppendToObj(Tcl_GetObjResult(interp)," value problem", TCL_INDEX_NONE);
	    Tcl_DeleteHashTable(&hash);
	    return TCL_ERROR;
	}
	Tcl_DeleteHashEntry(hPtr);
    }

    if (hash.numEntries != 0) {
7461
7462
7463
7464
7465
7466
7467
7468
7469
7470
7471
7472
7473
7474
7475
7476
7477
7478
7479
7480
7481
7482
7483

    /*
     * Set the start of the error message as obj result; it will be cleared at
     * the end if no errors were found.
     */

    Tcl_SetObjResult(interp,
	    Tcl_NewStringObj("Tcl_ConcatObj is unsafe:", -1));

    emptyPtr = Tcl_NewObj();

    list1Ptr = Tcl_NewStringObj("foo bar sum", -1);
    Tcl_ListObjLength(NULL, list1Ptr, &len);
    Tcl_InvalidateStringRep(list1Ptr);

    list2Ptr = Tcl_NewStringObj("eeny meeny", -1);
    Tcl_ListObjLength(NULL, list2Ptr, &len);
    Tcl_InvalidateStringRep(list2Ptr);

    /*
     * Verify that concat'ing a list obj with one or more empty strings does
     * return a fresh Tcl_Obj (see also [Bug 2055782]).
     */







|



|



|







7664
7665
7666
7667
7668
7669
7670
7671
7672
7673
7674
7675
7676
7677
7678
7679
7680
7681
7682
7683
7684
7685
7686

    /*
     * Set the start of the error message as obj result; it will be cleared at
     * the end if no errors were found.
     */

    Tcl_SetObjResult(interp,
	    Tcl_NewStringObj("Tcl_ConcatObj is unsafe:", TCL_INDEX_NONE));

    emptyPtr = Tcl_NewObj();

    list1Ptr = Tcl_NewStringObj("foo bar sum", TCL_INDEX_NONE);
    Tcl_ListObjLength(NULL, list1Ptr, &len);
    Tcl_InvalidateStringRep(list1Ptr);

    list2Ptr = Tcl_NewStringObj("eeny meeny", TCL_INDEX_NONE);
    Tcl_ListObjLength(NULL, list2Ptr, &len);
    Tcl_InvalidateStringRep(list2Ptr);

    /*
     * Verify that concat'ing a list obj with one or more empty strings does
     * return a fresh Tcl_Obj (see also [Bug 2055782]).
     */
8032
8033
8034
8035
8036
8037
8038
8039
8040
8041
8042
8043
8044
8045
8046
{
    if (*name == 'T') {
 	MyResolvedVarInfo *resVarInfo = (MyResolvedVarInfo *)Tcl_Alloc(sizeof(MyResolvedVarInfo));

 	resVarInfo->vInfo.fetchProc = MyCompiledVarFetch;
 	resVarInfo->vInfo.deleteProc = MyCompiledVarFree;
 	resVarInfo->var = NULL;
 	resVarInfo->nameObj = Tcl_NewStringObj(name, -1);
 	Tcl_IncrRefCount(resVarInfo->nameObj);
 	*rPtr = &resVarInfo->vInfo;
 	return TCL_OK;
    }
    return TCL_CONTINUE;
}








|







8235
8236
8237
8238
8239
8240
8241
8242
8243
8244
8245
8246
8247
8248
8249
{
    if (*name == 'T') {
 	MyResolvedVarInfo *resVarInfo = (MyResolvedVarInfo *)Tcl_Alloc(sizeof(MyResolvedVarInfo));

 	resVarInfo->vInfo.fetchProc = MyCompiledVarFetch;
 	resVarInfo->vInfo.deleteProc = MyCompiledVarFree;
 	resVarInfo->var = NULL;
 	resVarInfo->nameObj = Tcl_NewStringObj(name, TCL_INDEX_NONE);
 	Tcl_IncrRefCount(resVarInfo->nameObj);
 	*rPtr = &resVarInfo->vInfo;
 	return TCL_OK;
    }
    return TCL_CONTINUE;
}

8116
8117
8118
8119
8120
8121
8122
8123
8124
8125
8126
8127
8128
8129
8130
8131
8132
8133
8134
8135
    Tcl_Obj *lambdaObjs[2];
    Tcl_Obj *evalObjs[2];
    Tcl_Obj *lambdaObj;
    int result;

    /* Create a lambda {{} {set a 42}} */
    lambdaObjs[0] = Tcl_NewObj(); /* No parameters */
    lambdaObjs[1] = Tcl_NewStringObj("set a 42", -1); /* Body */
    lambdaObj = Tcl_NewListObj(2, lambdaObjs);
    Tcl_IncrRefCount(lambdaObj);

    /* Create the command "apply {{} {set a 42}" */
    evalObjs[0] = Tcl_NewStringObj("apply", -1);
    Tcl_IncrRefCount(evalObjs[0]);
    /*
     * NOTE: IMPORTANT TO EXHIBIT THE BUG. We duplicate the lambda because
     * it will get shimmered to a Lambda internal representation but we
     * want to hold on to our list representation.
     */
    evalObjs[1] = Tcl_DuplicateObj(lambdaObj);







|




|







8319
8320
8321
8322
8323
8324
8325
8326
8327
8328
8329
8330
8331
8332
8333
8334
8335
8336
8337
8338
    Tcl_Obj *lambdaObjs[2];
    Tcl_Obj *evalObjs[2];
    Tcl_Obj *lambdaObj;
    int result;

    /* Create a lambda {{} {set a 42}} */
    lambdaObjs[0] = Tcl_NewObj(); /* No parameters */
    lambdaObjs[1] = Tcl_NewStringObj("set a 42", TCL_INDEX_NONE); /* Body */
    lambdaObj = Tcl_NewListObj(2, lambdaObjs);
    Tcl_IncrRefCount(lambdaObj);

    /* Create the command "apply {{} {set a 42}" */
    evalObjs[0] = Tcl_NewStringObj("apply", TCL_INDEX_NONE);
    Tcl_IncrRefCount(evalObjs[0]);
    /*
     * NOTE: IMPORTANT TO EXHIBIT THE BUG. We duplicate the lambda because
     * it will get shimmered to a Lambda internal representation but we
     * want to hold on to our list representation.
     */
    evalObjs[1] = Tcl_DuplicateObj(lambdaObj);
Changes to generic/tclTomMath.decls.
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
declare 15 {
    mp_err MP_WUR TclBN_mp_div_2(const mp_int *a, mp_int *q)
}
declare 16 {
    mp_err MP_WUR TclBN_mp_div_2d(const mp_int *a, int b, mp_int *q, mp_int *r)
}
# Removed in 9.0
#declare 17 {deprecated {is private function in libtommath}} {
#    mp_err TclBN_mp_div_3(const mp_int *a, mp_int *q, mp_digit *r)
#}
declare 18 {
    void TclBN_mp_exch(mp_int *a, mp_int *b)
}
declare 19 {
    mp_err MP_WUR TclBN_mp_expt_u32(const mp_int *a, uint32_t b, mp_int *c)







|







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
declare 15 {
    mp_err MP_WUR TclBN_mp_div_2(const mp_int *a, mp_int *q)
}
declare 16 {
    mp_err MP_WUR TclBN_mp_div_2d(const mp_int *a, int b, mp_int *q, mp_int *r)
}
# Removed in 9.0
#declare 17 {
#    mp_err TclBN_mp_div_3(const mp_int *a, mp_int *q, mp_digit *r)
#}
declare 18 {
    void TclBN_mp_exch(mp_int *a, mp_int *b)
}
declare 19 {
    mp_err MP_WUR TclBN_mp_expt_u32(const mp_int *a, uint32_t b, mp_int *c)
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
declare 37 {
    void TclBN_mp_rshd(mp_int *a, int shift)
}
declare 38 {
    mp_err MP_WUR TclBN_mp_shrink(mp_int *a)
}
# Removed in 9.0
#declare 39 {deprecated {macro calling mp_set_u64}} {
#    void TclBN_mp_set(mp_int *a, unsigned int b)
#}
# Removed in 9.0
#declare 40 {nostub {is private function in libtommath}} {
#    mp_err TclBN_mp_sqr(const mp_int *a, mp_int *b)
#}
declare 41 {







|







137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
declare 37 {
    void TclBN_mp_rshd(mp_int *a, int shift)
}
declare 38 {
    mp_err MP_WUR TclBN_mp_shrink(mp_int *a)
}
# Removed in 9.0
#declare 39 {
#    void TclBN_mp_set(mp_int *a, unsigned int b)
#}
# Removed in 9.0
#declare 40 {nostub {is private function in libtommath}} {
#    mp_err TclBN_mp_sqr(const mp_int *a, mp_int *b)
#}
declare 41 {
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
declare 48 {
    mp_err MP_WUR TclBN_mp_xor(const mp_int *a, const mp_int *b, mp_int *c)
}
declare 49 {
    void TclBN_mp_zero(mp_int *a)
}
# Removed in 9.0
#declare 61 {deprecated {macro calling mp_init_u64}} {
#    mp_err TclBN_mp_init_ul(mp_int *a, unsigned long i)
#}
# Removed in 9.0
#declare 62 {deprecated {macro calling mp_set_u64}} {
#    void TclBN_mp_set_ul(mp_int *a, unsigned long i)
#}
declare 63 {
    int MP_WUR TclBN_mp_cnt_lsb(const mp_int *a)
}
# Removed in 9.0
#declare 64 {deprecated {macro calling mp_init_i64}} {
#    int TclBN_mp_init_l(mp_int *bignum, long initVal)
#}
declare 65 {
    int MP_WUR TclBN_mp_init_i64(mp_int *bignum, int64_t initVal)
}
declare 66 {
    int MP_WUR TclBN_mp_init_u64(mp_int *bignum, uint64_t initVal)







|



|






|







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
declare 48 {
    mp_err MP_WUR TclBN_mp_xor(const mp_int *a, const mp_int *b, mp_int *c)
}
declare 49 {
    void TclBN_mp_zero(mp_int *a)
}
# Removed in 9.0
#declare 61 {
#    mp_err TclBN_mp_init_ul(mp_int *a, unsigned long i)
#}
# Removed in 9.0
#declare 62 {
#    void TclBN_mp_set_ul(mp_int *a, unsigned long i)
#}
declare 63 {
    int MP_WUR TclBN_mp_cnt_lsb(const mp_int *a)
}
# Removed in 9.0
#declare 64 {
#    int TclBN_mp_init_l(mp_int *bignum, long initVal)
#}
declare 65 {
    int MP_WUR TclBN_mp_init_i64(mp_int *bignum, int64_t initVal)
}
declare 66 {
    int MP_WUR TclBN_mp_init_u64(mp_int *bignum, uint64_t initVal)
Changes to generic/tclUniData.c.
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
























273

274





















275






























276
















































277




278
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
    11456, 1344, 11488, 11520, 11552, 3296, 3296, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 7776, 4704, 11584, 11616, 11648, 3296,
    3296, 11680, 11712, 11744, 11776, 4736, 11808, 3296, 11840, 11872,
    11904, 3296, 3296, 1344, 11936, 11968, 6880, 12000, 12032, 12064, 12096,
    12128, 3296, 12160, 12192, 1344, 12224, 12256, 12288, 12320, 12352,
    3296, 3296, 1344, 1344, 12384, 3296, 12416, 12448, 12480, 12512, 1344,
    12544, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 12576,
    1344, 12608, 3296, 3296, 12128, 12640, 12672, 12704, 12736, 12704,
    12768, 7776, 12800, 12832, 12864, 12896, 5280, 12928, 12960, 12992,
    13024, 13056, 13088, 13120, 5280, 13152, 13184, 13216, 13248, 13280,
    3296, 3296, 13312, 13344, 13376, 13408, 13440, 13472, 13504, 13536,
    3296, 3296, 3296, 3296, 1344, 13568, 13600, 13632, 1344, 13664, 13696,
    3296, 3296, 3296, 3296, 3296, 1344, 13728, 13760, 3296, 1344, 13792,
    13824, 13856, 1344, 13888, 13920, 3296, 4032, 13952, 13984, 3296, 3296,
    3296, 3296, 3296, 1344, 14016, 3296, 3296, 3296, 14048, 14080, 14112,
    14144, 14176, 14208, 3296, 3296, 14240, 14272, 14304, 14336, 14368,
    14400, 1344, 14432, 14464, 1344, 4608, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 14496, 14528, 14560, 14592, 14624, 14656, 3296, 3296,
    14688, 14720, 14752, 14784, 14816, 13920, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 14848, 3296, 3296, 3296, 3296, 3296, 14880,
    14912, 14944, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 9920, 3296, 3296, 3296, 10816,
    10816, 10816, 14976, 1344, 1344, 1344, 1344, 1344, 1344, 15008, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 12704, 1344, 1344,
    15040, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 15072,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    13984, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
























    4608, 4736, 15104, 1344, 4736, 15136, 15168, 1344, 15200, 15232, 15264,

    15296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,





















    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 14048,






























    14080, 15328, 3296, 3296, 3296, 1344, 1344, 15360, 15392, 15424, 3296,
















































    3296, 15456, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,




    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 15488, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 4704, 3296, 12384, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,





































    15520, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 15552,
    15584, 15616, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 9792, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 1344, 1344, 1344, 15648, 15680, 15712, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    704, 15744, 15776, 4928, 4928, 4928, 15808, 3296, 4928, 4928, 4928,
    4928, 4928, 4928, 4928, 8000, 4928, 15840, 4928, 15872, 15904, 15936,
    4928, 6848, 4928, 4928, 15968, 3296, 3296, 3296, 3296, 16000, 4928,
    4928, 16032, 16064, 3296, 3296, 3296, 3296, 16096, 16128, 16160, 16192,
    16224, 16256, 16288, 16320, 16352, 16384, 16416, 16448, 16480, 16096,
    16128, 16512, 16192, 16544, 16576, 16608, 16320, 16640, 16672, 16704,
    16736, 16768, 16800, 16832, 16864, 16896, 16928, 16960, 4928, 4928,
    4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928,
    4928, 4928, 704, 16992, 704, 17024, 17056, 17088, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 17120, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 17152, 17184, 3296, 3296, 3296, 3296, 3296,
    3296, 1344, 17216, 17248, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 12704, 17280, 1344, 17312, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 17344,




























































    1344, 1344, 1344, 1344, 1344, 1344, 17376, 3296, 17408, 17440, 17472,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    17504, 6880, 17536, 3296, 3296, 17568, 17600, 3296, 3296, 3296, 3296,
    3296, 3296, 17632, 17664, 17696, 17728, 17760, 17792, 3296, 17824,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 4928, 17856, 4928,
    4928, 7968, 17888, 17920, 8000, 17952, 4928, 4928, 4928, 4928, 17984,
    3296, 18016, 18048, 18080, 18112, 18144, 3296, 3296, 3296, 3296, 4928,
    4928, 4928, 4928, 4928, 4928, 4928, 18176, 4928, 4928, 4928, 4928,
    4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928,
    4928, 4928, 4928, 4928, 4928, 4928, 18208, 18240, 4928, 4928, 4928,
    7968, 4928, 4928, 18272, 18304, 17856, 4928, 18336, 4928, 18368, 18400,
    3296, 3296, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928,
    7968, 18432, 18464, 18496, 18528, 18560, 4928, 4928, 4928, 4928, 18592,
    4928, 6848, 18624, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,


    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,


    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 3296, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 4608, 1344, 1344, 1344, 1344, 1344, 1344, 11328, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 18656,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 18688, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 11328,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 18720
#endif /* TCL_UTF_MAX > 3 */
};

/*
 * The groupMap is indexed by combining the alternate page number with
 * the page offset and returns a group number that identifies a unique
 * set of character attributes.







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


|






|
|

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











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















<

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


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


>
>












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









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







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
    11456, 1344, 11488, 11520, 11552, 3296, 3296, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 7776, 4704, 11584, 11616, 11648, 3296,
    3296, 11680, 11712, 11744, 11776, 4736, 11808, 3296, 11840, 11872,
    11904, 3296, 3296, 1344, 11936, 11968, 6880, 12000, 12032, 12064, 12096,
    12128, 3296, 12160, 12192, 1344, 12224, 12256, 12288, 12320, 12352,
    3296, 3296, 1344, 1344, 12384, 3296, 12416, 12448, 12480, 12512, 1344,
    12544, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 12576,
    1344, 12608, 3296, 12640, 12128, 12672, 12704, 12736, 12768, 12736,
    12800, 7776, 12832, 12864, 12896, 12928, 5280, 12960, 12992, 13024,
    13056, 13088, 13120, 13152, 5280, 13184, 13216, 13248, 13280, 13312,
    13344, 3296, 13376, 13408, 13440, 13472, 13504, 13536, 13568, 13600,
    3296, 3296, 3296, 3296, 1344, 13632, 13664, 13696, 1344, 13728, 13760,
    3296, 3296, 3296, 3296, 3296, 1344, 13792, 13824, 3296, 1344, 13856,
    13888, 13920, 1344, 13952, 13984, 3296, 4032, 14016, 14048, 3296, 3296,
    3296, 3296, 3296, 1344, 14080, 3296, 3296, 3296, 14112, 14144, 14176,
    14208, 14240, 14272, 3296, 3296, 14304, 14336, 14368, 14400, 14432,
    14464, 1344, 14496, 14528, 1344, 4608, 14560, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 14592, 14624, 14656, 14688, 14720, 14752, 3296, 3296,
    14784, 14816, 14848, 14880, 14912, 13984, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 14944, 14976, 15008, 15040, 3296, 3296, 15072,
    15104, 15136, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 9920, 3296, 3296, 3296, 10816,
    10816, 10816, 15168, 1344, 1344, 1344, 1344, 1344, 1344, 15200, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 12736, 1344, 1344,
    15232, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 15264,
    15296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
























    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 14048, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 4608, 4736, 15328, 1344, 4736, 15360, 15392, 1344, 15424, 15456,
    15488, 15520, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    14112, 14144, 15552, 3296, 3296, 3296, 1344, 1344, 15584, 15616, 15648,
    3296, 3296, 15680, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 15712, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 4704, 3296, 12384, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 15744, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    15776, 15808, 15840, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 9792, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 1344, 1344, 1344, 15872, 15904, 15936, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 704, 15968, 16000, 4928, 4928, 4928, 16032, 3296, 4928, 4928,
    4928, 4928, 4928, 4928, 4928, 8000, 4928, 16064, 4928, 16096, 16128,
    16160, 4928, 6848, 4928, 4928, 16192, 3296, 3296, 3296, 16224, 16224,
    4928, 4928, 16256, 16288, 3296, 3296, 3296, 3296, 16320, 16352, 16384,
    16416, 16448, 16480, 16512, 16544, 16576, 16608, 16640, 16672, 16704,
    16320, 16352, 16736, 16416, 16768, 16800, 16832, 16544, 16864, 16896,
    16928, 16960, 16992, 17024, 17056, 17088, 17120, 17152, 17184, 4928,
    4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928,
    4928, 4928, 4928, 704, 17216, 704, 17248, 17280, 17312, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 17344, 17376, 3296,
    3296, 3296, 3296, 3296, 3296, 17408, 17440, 5664, 17472, 17504, 3296,
    3296, 3296, 1344, 17536, 17568, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 12736, 17600, 1344, 17632, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 12736,
    17664, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 17696, 1344, 1344, 1344, 1344, 1344, 1344, 17728, 3296, 17760,
    17792, 17824, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 17856, 6880, 17888, 3296, 3296, 17920, 17952, 3296,
    3296, 3296, 3296, 3296, 3296, 17984, 18016, 18048, 18080, 18112, 18144,
    3296, 18176, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 4928,
    18208, 4928, 4928, 7968, 18240, 18272, 8000, 18304, 4928, 4928, 4928,
    4928, 18336, 3296, 18368, 18400, 18432, 18464, 18496, 3296, 3296, 3296,
    3296, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 18528, 4928, 4928,
    4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928,
    4928, 4928, 4928, 4928, 4928, 4928, 4928, 4928, 18560, 18592, 4928,
    4928, 4928, 18624, 4928, 4928, 18656, 18688, 18208, 4928, 18720, 4928,
    18752, 18784, 3296, 3296, 4928, 4928, 4928, 4928, 4928, 4928, 4928,
    4928, 4928, 4928, 7968, 18816, 18848, 18880, 18912, 18944, 4928, 4928,
    4928, 4928, 18976, 4928, 6848, 19008, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,

    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,

























    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,

    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 3296, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 9920, 1344, 1344, 1344, 1344, 1344, 1344, 11328,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    19040, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 19072, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,

    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,




    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,

    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 11328, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296, 3296,
    3296, 3296, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1792, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,























    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344,
    1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 15520










































































































































#endif /* TCL_UTF_MAX > 3 */
};

/*
 * The groupMap is indexed by combining the alternate page number with
 * the page offset and returns a group number that identifies a unique
 * set of character attributes.
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
    9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 3, 18, 18, 18, 18,
    18, 18, 18, 14, 15, 93, 125, 125, 3, 15, 15, 15, 15, 15, 15, 15, 15,
    0, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 0, 0, 93, 15, 125, 93, 125,
    125, 125, 125, 125, 0, 93, 125, 125, 0, 125, 125, 93, 93, 0, 0, 0,
    0, 0, 0, 0, 125, 125, 0, 0, 0, 0, 0, 0, 15, 15, 0, 15, 15, 93, 93,
    0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 15, 15, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 93, 93, 125, 125, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 0, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 93, 15, 125, 125,
    125, 93, 93, 93, 93, 0, 125, 125, 125, 0, 125, 125, 125, 93, 15, 14,
    0, 0, 0, 0, 15, 15, 15, 125, 18, 18, 18, 18, 18, 18, 18, 15, 15, 15,
    93, 93, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 14, 15, 15, 15, 15, 15, 15, 0, 93, 125, 125, 0, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0,
    0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 0, 15, 0, 0, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 93, 0, 0, 0,
    0, 125, 125, 125, 93, 93, 93, 0, 93, 0, 125, 125, 125, 125, 125, 125,
    125, 125, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 125,
    125, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 93, 15, 15, 93, 93, 93, 93, 93, 93,
    93, 0, 0, 0, 0, 4, 15, 15, 15, 15, 15, 15, 92, 93, 93, 93, 93, 93,
    93, 93, 93, 3, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 3, 3, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 0, 15, 0, 15, 15, 15, 15, 15, 0, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 0, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 93, 15, 15, 93, 93, 93, 93, 93, 93, 93, 93, 93, 15, 0, 0, 15, 15,
    15, 15, 15, 0, 92, 0, 93, 93, 93, 93, 93, 93, 0, 0, 9, 9, 9, 9, 9,
    9, 9, 9, 9, 9, 0, 0, 15, 15, 15, 15, 15, 14, 14, 14, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 14, 3, 14, 14, 14, 93, 93, 14, 14, 14,
    14, 14, 14, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 14, 93, 14, 93, 14, 93, 5, 6, 5, 6, 125, 125, 15, 15, 15,
    15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 93, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 125, 93, 93, 93, 93, 93, 3, 93, 93,
    15, 15, 15, 15, 15, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 0,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 93, 0, 14, 14, 14, 14, 14, 14, 14, 14, 93, 14, 14, 14, 14, 14,
    14, 0, 14, 14, 3, 3, 3, 3, 3, 14, 14, 14, 14, 3, 3, 0, 0, 0, 0, 0,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 125, 125, 93, 93, 93, 93,
    125, 93, 93, 93, 93, 93, 93, 125, 93, 93, 125, 125, 93, 93, 15, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 3, 3, 3, 3, 3, 3, 15, 15, 15, 15, 15, 15,
    125, 125, 93, 93, 15, 15, 15, 15, 93, 93, 93, 15, 125, 125, 125, 15,
    15, 125, 125, 125, 125, 125, 125, 125, 15, 15, 15, 93, 93, 93, 93,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 125, 125, 93,
    93, 125, 125, 125, 125, 125, 125, 93, 15, 125, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 125, 125, 125, 93, 14, 14, 126, 126, 126, 126, 126, 126, 126,
    126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
    126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
    126, 126, 126, 0, 126, 0, 0, 0, 0, 0, 126, 0, 0, 127, 127, 127, 127,
    127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
    127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
    127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 3, 92, 127,
    127, 127, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 0,
    0, 15, 15, 15, 15, 15, 15, 15, 0, 15, 0, 15, 15, 15, 15, 0, 0, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 0, 0, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15,
    15, 0, 0, 15, 15, 15, 15, 15, 15, 15, 0, 15, 0, 15, 15, 15, 15, 0,
    0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 0, 0, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 93, 93,
    93, 3, 3, 3, 3, 3, 3, 3, 3, 3, 18, 18, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0, 0, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 128, 128,
    128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
    128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
    128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
    105, 105, 105, 105, 105, 105, 0, 0, 111, 111, 111, 111, 111, 111, 0,
    0, 8, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 3, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 5, 6, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 3, 3, 3, 129, 129, 129, 15, 15, 15, 15, 15, 15, 15,
    15, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 93, 93, 93, 125, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 93, 93, 125, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 93,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 0, 15, 15, 15, 0, 93, 93, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 93, 93, 125, 93, 93, 93, 93, 93, 93, 93,
    125, 125, 125, 125, 125, 125, 125, 125, 93, 125, 125, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 3, 3, 3, 92, 3, 3, 3, 4, 15, 93, 0, 0,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 8, 3, 3, 3,
    3, 93, 93, 93, 17, 93, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0,
    0, 15, 15, 15, 92, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15,
    93, 93, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 93, 15, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,







|
|
|

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


|
|
|

|
|
|
|
|
|
|
|
|
|

|
|
|
|

|
|
|
|
|
|
|
|
|
|


|


|
|
|
|

|
|
|
|
|

|
|
|
|
|



|
|

|
|

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







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
    9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 3, 18, 18, 18, 18,
    18, 18, 18, 14, 15, 93, 125, 125, 3, 15, 15, 15, 15, 15, 15, 15, 15,
    0, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 0, 0, 93, 15, 125, 93, 125,
    125, 125, 125, 125, 0, 93, 125, 125, 0, 125, 125, 93, 93, 0, 0, 0,
    0, 0, 0, 0, 125, 125, 0, 0, 0, 0, 0, 0, 15, 15, 0, 15, 15, 93, 93,
    0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 15, 15, 125, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 93, 93, 125, 125, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 0, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 93, 15, 125,
    125, 125, 93, 93, 93, 93, 0, 125, 125, 125, 0, 125, 125, 125, 93, 15,
    14, 0, 0, 0, 0, 15, 15, 15, 125, 18, 18, 18, 18, 18, 18, 18, 15, 15,
    15, 93, 93, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 14, 15, 15, 15, 15, 15, 15, 0, 93, 125, 125, 0, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 0, 15, 0, 0, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 93, 0, 0,
    0, 0, 125, 125, 125, 93, 93, 93, 0, 93, 0, 125, 125, 125, 125, 125,
    125, 125, 125, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0,
    125, 125, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 15, 15, 93, 93, 93, 93, 93,
    93, 93, 0, 0, 0, 0, 4, 15, 15, 15, 15, 15, 15, 92, 93, 93, 93, 93,
    93, 93, 93, 93, 3, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 3, 3, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 0, 15, 0, 15, 15, 15, 15, 15,
    0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 0, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 93, 15, 15, 93, 93, 93, 93, 93, 93, 93, 93, 93, 15, 0,
    0, 15, 15, 15, 15, 15, 0, 92, 0, 93, 93, 93, 93, 93, 93, 93, 0, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 15, 15, 15, 15, 15, 14, 14, 14, 3,
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 14, 3, 14, 14, 14, 93, 93,
    14, 14, 14, 14, 14, 14, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 14, 93, 14, 93, 14, 93, 5, 6, 5, 6, 125, 125,
    15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 125, 93, 93, 93, 93, 93,
    3, 93, 93, 15, 15, 15, 15, 15, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 93, 0, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 0, 14, 14, 14, 14, 14, 14, 14, 14, 93, 14, 14,
    14, 14, 14, 14, 0, 14, 14, 3, 3, 3, 3, 3, 14, 14, 14, 14, 3, 3, 0,
    0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 125, 125, 93,
    93, 93, 93, 125, 93, 93, 93, 93, 93, 93, 125, 93, 93, 125, 125, 93,
    93, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 3, 3, 3, 3, 3, 3, 15, 15, 15,
    15, 15, 15, 125, 125, 93, 93, 15, 15, 15, 15, 93, 93, 93, 15, 125,
    125, 125, 15, 15, 125, 125, 125, 125, 125, 125, 125, 15, 15, 15, 93,
    93, 93, 93, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93,
    125, 125, 93, 93, 125, 125, 125, 125, 125, 125, 93, 15, 125, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 125, 125, 125, 93, 14, 14, 126, 126, 126, 126,
    126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
    126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
    126, 126, 126, 126, 126, 126, 0, 126, 0, 0, 0, 0, 0, 126, 0, 0, 127,
    127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
    127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
    127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
    3, 92, 127, 127, 127, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15,
    15, 15, 0, 0, 15, 15, 15, 15, 15, 15, 15, 0, 15, 0, 15, 15, 15, 15,
    0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 0, 0,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0,
    15, 15, 15, 15, 0, 0, 15, 15, 15, 15, 15, 15, 15, 0, 15, 0, 15, 15,
    15, 15, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 0, 0, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    0, 0, 93, 93, 93, 3, 3, 3, 3, 3, 3, 3, 3, 3, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0, 0, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128,
    128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
    128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
    128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
    128, 128, 105, 105, 105, 105, 105, 105, 0, 0, 111, 111, 111, 111, 111,
    111, 0, 0, 8, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 3, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 5, 6, 0, 0, 0, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 3, 3, 3, 129, 129, 129, 15, 15, 15, 15, 15,
    15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 93, 93, 125, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 93, 93, 125, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    93, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 0, 93, 93, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 93, 93, 125, 93, 93, 93, 93, 93, 93,
    93, 125, 125, 125, 125, 125, 125, 125, 125, 93, 125, 125, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 3, 3, 3, 92, 3, 3, 3, 4, 15, 93, 0,
    0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 8, 3, 3,
    3, 3, 93, 93, 93, 17, 93, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0,
    0, 0, 15, 15, 15, 92, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15,
    93, 93, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 93, 15, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
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
    103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103,
    103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 0, 0, 0,
    0, 0, 0, 0, 18, 18, 18, 18, 18, 18, 15, 15, 15, 15, 93, 93, 93, 93,
    0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0,
    0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 93, 93, 8, 0, 0, 15, 15, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 18, 18, 18, 18, 18, 18,

    15, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 18, 18, 18, 18, 3, 3, 3, 3, 3, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 93, 93,
    93, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 18, 18, 18, 18, 18, 18, 18, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 93, 125,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 18, 18, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 93, 15, 15, 93, 93, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 125, 125, 125,
    93, 93, 93, 93, 125, 125, 93, 93, 3, 3, 17, 3, 3, 3, 3, 93, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0,
    0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 93,
    93, 93, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 93, 93, 93, 93, 93, 125, 93, 93, 93, 93, 93, 93, 93,
    93, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 3, 3, 3, 3, 15, 125, 125, 15,
    0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 93, 3, 3, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 125, 125, 125, 93, 93, 93, 93, 93, 93, 93, 93, 93, 125, 125,
    15, 15, 15, 15, 3, 3, 3, 3, 93, 93, 93, 93, 3, 125, 93, 9, 9, 9, 9,
    9, 9, 9, 9, 9, 9, 15, 3, 15, 3, 3, 3, 0, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 125, 125, 125,
    93, 93, 93, 125, 125, 93, 125, 93, 93, 3, 3, 3, 3, 3, 3, 93, 0, 15,


    15, 15, 15, 15, 15, 15, 0, 15, 0, 15, 15, 15, 15, 0, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 3, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 93, 125, 125, 125, 93, 93, 93, 93, 93, 93,
    93, 93, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0,
    0, 93, 93, 125, 125, 0, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 15, 15,
    0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 0,
    15, 15, 15, 15, 15, 0, 93, 93, 15, 125, 125, 93, 125, 125, 125, 125,
    0, 0, 125, 125, 0, 0, 125, 125, 125, 0, 0, 15, 0, 0, 0, 0, 0, 0, 125,
    0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 125, 125, 0, 0, 93, 93, 93, 93,
    93, 93, 93, 0, 0, 0, 93, 93, 93, 93, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 125, 125, 125, 93, 93, 93, 93, 93, 93, 93, 93,
    125, 125, 93, 93, 93, 125, 93, 15, 15, 15, 15, 3, 3, 3, 3, 3, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 3, 3, 0, 3, 93, 15, 15, 15, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,







|
>
|
|
|
|
|
|
|
|
|


|
|
|
|
|
|
|
|
|
|

|
|
|

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


|
|
|
|
|
|
|







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
    103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103,
    103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 0, 0, 0,
    0, 0, 0, 0, 18, 18, 18, 18, 18, 18, 15, 15, 15, 15, 93, 93, 93, 93,
    0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0,
    0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 93, 93, 8, 0, 0, 15, 15, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 93, 93,
    18, 18, 18, 18, 18, 18, 18, 15, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 18, 18, 18, 18,
    3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 93, 93, 93, 93, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 18, 18,
    18, 18, 18, 18, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 125, 93, 125, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 93, 15, 15, 93, 93, 15, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 93, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 125, 125, 125, 93, 93, 93, 93, 125, 125, 93, 93, 3, 3,
    17, 3, 3, 3, 3, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 0, 0, 0, 0, 0, 0, 93, 93, 93, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 93, 93, 93, 93, 125,
    93, 93, 93, 93, 93, 93, 93, 93, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 3,
    3, 3, 3, 15, 125, 125, 15, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 3, 3, 15,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 125, 125, 125, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 125, 125, 15, 15, 15, 15, 3, 3, 3, 3, 93, 93, 93,
    93, 3, 125, 93, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 15, 3, 15, 3, 3, 3, 0,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 125, 125, 125, 93, 93, 93, 125, 125, 93, 125, 93, 93,
    3, 3, 3, 3, 3, 3, 93, 15, 15, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15,
    15, 15, 15, 15, 0, 15, 0, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 3, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 93, 125, 125, 125, 93, 93, 93, 93, 93, 93, 93, 93,
    0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 93,
    93, 125, 125, 0, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 15, 15, 0, 0,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 0, 15,
    15, 15, 15, 15, 0, 93, 93, 15, 125, 125, 93, 125, 125, 125, 125, 0,
    0, 125, 125, 0, 0, 125, 125, 125, 0, 0, 15, 0, 0, 0, 0, 0, 0, 125,
    0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 125, 125, 0, 0, 93, 93, 93, 93,
    93, 93, 93, 0, 0, 0, 93, 93, 93, 93, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 125, 125, 125, 93, 93, 93, 93, 93, 93, 93, 93,
    125, 125, 93, 93, 93, 125, 93, 15, 15, 15, 15, 3, 3, 3, 3, 3, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 3, 3, 0, 3, 93, 15, 15, 15, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 93, 93,
    93, 93, 93, 125, 15, 93, 93, 93, 93, 3, 3, 3, 3, 3, 3, 3, 3, 93, 0,
    0, 0, 0, 0, 0, 0, 0, 15, 93, 93, 93, 93, 93, 93, 125, 125, 93, 93,
    93, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 125, 93, 93, 3, 3, 3, 15,
    3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,

    15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 125, 93, 93, 93, 93, 93, 93, 93, 0,
    93, 93, 93, 93, 93, 93, 125, 93, 15, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0, 0, 3, 3,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 93, 0, 125, 93, 93, 93, 93, 93, 93, 93, 125, 93, 93, 125, 93, 93,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 0,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 93, 93, 93, 93, 93, 93, 0, 0, 0, 93, 0, 93, 93, 0,
    93, 93, 93, 93, 93, 93, 93, 15, 93, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 0, 15,
    15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    125, 125, 125, 125, 125, 0, 93, 93, 0, 125, 125, 93, 125, 93, 15, 0,
    0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 93, 93, 125, 125, 3, 3, 0, 0, 0, 0, 0, 0, 0,





    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 14, 14, 14, 14, 14, 14,

    14, 14, 4, 4, 4, 4, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 129,
    129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
    0, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 17, 17, 17,
    17, 17, 17, 17, 17, 17, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 0, 0, 0, 0, 3, 3, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,

    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 93, 93, 93,
    93, 93, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 93, 93, 93, 93, 93, 93, 3,
    3, 3, 3, 3, 14, 14, 14, 14, 92, 92, 92, 92, 3, 14, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 18, 18, 18, 18, 18, 18,
    18, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 3, 3, 3, 3, 0,
    0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0,
    93, 15, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
    125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
    125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
    125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
    125, 0, 0, 0, 0, 0, 0, 0, 93, 93, 93, 93, 92, 92, 92, 92, 92, 92, 92,
    92, 92, 92, 92, 92, 92, 92, 92, 3, 92, 93, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 125, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15,

    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 92, 92, 92, 92, 0, 92, 92, 92, 92, 92, 92,
    92, 0, 92, 92, 0, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    0, 0, 14, 93, 93, 3, 17, 17, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 0, 0, 93, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,


    0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14,
    14, 14, 14, 14, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 125,
    125, 93, 93, 93, 14, 14, 14, 125, 125, 125, 125, 125, 125, 17, 17,
    17, 17, 17, 17, 17, 17, 93, 93, 93, 93, 93, 93, 93, 93, 14, 14, 93,
    93, 93, 93, 93, 93, 93, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 93, 93, 93, 93, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 93, 93, 93, 14, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0, 0,
    0, 0, 0, 0, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,



    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 21, 21, 21, 21, 21, 21, 21, 0, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 108,



    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 108, 0, 108, 108, 0, 0, 108, 0, 0, 108, 108,
    0, 0, 108, 108, 108, 108, 0, 108, 108, 108, 108, 108, 108, 108, 108,

    21, 21, 21, 21, 0, 21, 0, 21, 21, 21, 21, 21, 21, 21, 0, 21, 21, 21,


    21, 21, 21, 21, 21, 21, 21, 21, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,



    108, 108, 108, 108, 108, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,

    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 108, 108,
    0, 108, 108, 108, 108, 0, 0, 108, 108, 108, 108, 108, 108, 108, 108,
    0, 108, 108, 108, 108, 108, 108, 108, 0, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,

    21, 21, 108, 108, 0, 108, 108, 108, 108, 0, 108, 108, 108, 108, 108,
    0, 108, 0, 0, 0, 108, 108, 108, 108, 108, 108, 108, 0, 21, 21, 21,

    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,




    21, 21, 21, 21, 21, 21, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 108, 108,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    21, 21, 21, 21, 21, 21, 0, 0, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 7, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 7, 21, 21, 21, 21,
    21, 21, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 7,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 7, 21, 21, 21, 21, 21, 21, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 7, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 7, 21, 21, 21, 21, 21, 21, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 7, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 7, 21,
    21, 21, 21, 21, 21, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 7, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 7, 21, 21, 21, 21, 21, 21,
    108, 21, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 14, 14, 14, 14,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 14, 14, 14, 14, 14, 14, 14, 14, 93, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 93, 14, 14, 3, 3, 3, 3, 3, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 93, 93, 93, 93, 0, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    15, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,

    21, 21, 21, 21, 0, 93, 93, 93, 93, 93, 93, 93, 0, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 0, 0, 93, 93, 93, 93,
    93, 93, 93, 0, 93, 93, 0, 93, 93, 93, 93, 93, 0, 0, 0, 0, 0, 0, 0,


    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15,

    15, 15, 15, 15, 15, 15, 0, 0, 0, 93, 93, 93, 93, 93, 93, 93, 92, 92,
    92, 92, 92, 92, 92, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0,
    15, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,


    15, 15, 93, 93, 93, 93, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0,
    4, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 0, 15, 15, 0, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15,
    15, 15, 15, 0, 0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 93, 93, 93, 93,
    93, 93, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 206, 206, 206, 206, 206, 206,
    206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
    206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
    207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
    207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,







|
>
|

|
|
|
|
|
|
|
|
|
|

|
|
|
|

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



|
|
|
>
|
|
|
|
|
|
|

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


|

>
>
>
|
|

|
|
>
|
>
>
|

>
>
>
|
>
|
|
|

>
|
|
>

>
>
>
>


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

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







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
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 93, 93,
    93, 93, 93, 125, 15, 93, 93, 93, 93, 3, 3, 3, 3, 3, 3, 3, 3, 93, 0,
    0, 0, 0, 0, 0, 0, 0, 15, 93, 93, 93, 93, 93, 93, 125, 125, 93, 93,
    93, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 125, 93, 93, 3, 3, 3, 15,
    3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 125, 93, 93,
    93, 93, 93, 93, 93, 0, 93, 93, 93, 93, 93, 93, 125, 93, 15, 3, 3, 3,
    3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
    18, 0, 0, 0, 3, 3, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 0, 0, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 0, 125, 93, 93, 93, 93, 93, 93, 93, 125,
    93, 93, 125, 93, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15,
    15, 15, 0, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 93, 93, 93, 93, 93, 0, 0, 0,
    93, 0, 93, 93, 0, 93, 93, 93, 93, 93, 93, 93, 15, 93, 0, 0, 0, 0, 0,
    0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 15, 15, 15,
    15, 15, 15, 0, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 125, 125, 125, 125, 125, 0, 93, 93, 0, 125, 125, 93,
    125, 93, 15, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93, 93, 125, 125, 3, 3, 0,
    0, 0, 0, 0, 0, 0, 93, 93, 15, 125, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 125, 125, 93, 93, 93, 93, 93, 0, 0, 0, 125,
    125, 93, 125, 93, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 9, 9, 9, 9,
    9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 14, 14, 14, 14, 14, 14, 14, 14, 4, 4, 4, 4, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 129, 129, 129, 129, 129, 129, 129, 129,
    129, 129, 129, 129, 129, 129, 129, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 3, 3, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
    17, 17, 17, 17, 17, 93, 15, 15, 15, 15, 15, 15, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 3, 3, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 0, 0, 93, 93, 93, 93, 93, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    93, 93, 93, 93, 93, 93, 93, 3, 3, 3, 3, 3, 14, 14, 14, 14, 92, 92,
    92, 92, 3, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 0, 18, 18, 18, 18, 18, 18, 18, 0, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0,
    0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 3, 3, 3, 3, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 0, 0, 0, 0, 93, 15, 125, 125, 125, 125, 125, 125,
    125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
    125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
    125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
    125, 125, 125, 125, 125, 125, 125, 0, 0, 0, 0, 0, 0, 0, 93, 93, 93,
    93, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 3,
    92, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 125, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 92, 92,
    92, 0, 92, 92, 92, 92, 92, 92, 92, 0, 92, 92, 0, 15, 15, 15, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15,
    15, 15, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15,
    15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0,
    0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 14, 93, 93, 3, 17,
    17, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 0, 0, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 0, 0,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,


    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 125, 125, 93, 93, 93, 14,
    14, 14, 125, 125, 125, 125, 125, 125, 17, 17, 17, 17, 17, 17, 17, 17,
    93, 93, 93, 93, 93, 93, 93, 93, 14, 14, 93, 93, 93, 93, 93, 93, 93,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 93, 93, 93, 93,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 93, 93, 93, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0, 0, 0, 0, 0, 0, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 21, 21, 21, 21, 21, 21, 21, 0, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 108,
    0, 108, 108, 0, 0, 108, 0, 0, 108, 108, 0, 0, 108, 108, 108, 108, 0,
    108, 108, 108, 108, 108, 108, 108, 108, 21, 21, 21, 21, 0, 21, 0, 21,
    21, 21, 21, 21, 21, 21, 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 108, 108, 0, 108, 108, 108, 108, 0,
    0, 108, 108, 108, 108, 108, 108, 108, 108, 0, 108, 108, 108, 108, 108,
    108, 108, 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 108, 108, 0, 108, 108,
    108, 108, 0, 108, 108, 108, 108, 108, 0, 108, 0, 0, 0, 108, 108, 108,
    108, 108, 108, 108, 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 108, 108, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 21, 21, 21, 21, 21, 21, 0, 0, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 7, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 7, 21, 21, 21, 21, 21, 21, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 7, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 7,
    21, 21, 21, 21, 21, 21, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,












    108, 108, 7, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 7, 21, 21, 21, 21, 21,
    21, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 7, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 7, 21, 21, 21, 21, 21, 21, 108, 108, 108,







    108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
    108, 108, 108, 108, 108, 108, 108, 108, 7, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 7, 21, 21, 21, 21, 21, 21, 108, 21, 0, 0, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 14, 14, 14, 14, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 14, 14, 14, 14, 14, 14, 14,
    14, 93, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 93,
    14, 14, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    93, 93, 93, 93, 93, 0, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
    93, 93, 93, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 15, 21, 21, 21, 21, 21, 21, 21,
    21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 0, 0, 0, 0, 0,
    0, 21, 21, 21, 21, 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 93, 93, 93, 93, 93, 93, 93, 0, 93, 93, 93, 93,
    93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 0, 0, 93, 93, 93,
    93, 93, 93, 93, 0, 93, 93, 0, 93, 93, 93, 93, 93, 0, 0, 0, 0, 0, 92,
    92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,
    92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 93, 93, 93, 93,
    93, 93, 93, 92, 92, 92, 92, 92, 92, 92, 0, 0, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 0, 0, 0, 0, 15, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 93,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 93, 93, 93, 93, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 0, 0, 0, 0, 0, 4, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 92, 93, 93, 93, 93, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0,
    0, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15, 0, 15, 15, 0, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15,
    15, 15, 15, 0, 0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 93, 93, 93, 93,
    93, 93, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 206, 206, 206, 206, 206, 206,
    206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
    206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
    207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
    207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
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
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0,
    0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 14,
    14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 11, 11, 11, 11, 11,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,


    14, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14,
    14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14,
    14, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 0, 0, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 14, 14,
    14, 14, 14, 0, 0, 0, 14, 14, 14, 14, 14, 0, 0, 0, 14, 14, 14, 14, 14,
    14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 14, 14, 14,
    14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14,
    14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 15, 15, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0
#endif /* TCL_UTF_MAX > 3 */
};

/*
 * Each group represents a unique set of character attributes.  The attributes
 * are encoded into a 32-bit value as follows:
 *







|


|
>
>
|
|
|
|
|

|
|
|
|

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







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
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0,
    0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 14,
    14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 11, 11, 11, 11, 11,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0,
    0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0,
    0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0,
    0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14,
    14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 0,
    0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 0, 0, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 0, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 14, 14, 14,
    14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 15,
    15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15,
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0


#endif /* TCL_UTF_MAX > 3 */
};

/*
 * Each group represents a unique set of character attributes.  The attributes
 * are encoded into a 32-bit value as follows:
 *
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
    -2760383, -2760127, -2768575, 1859714, -9044927, -10823615, -12158,
    -10830783, -10833599, -10832575, -10830015, -10817983, -10824127,
    -10818751, 237633, -12223, -10830527, -9058239, 237698, 9949314,
    18, 17, 10305, 10370, 10049, 10114, 8769, 8834
};

#if TCL_UTF_MAX > 3 || TCL_MAJOR_VERSION > 8 || TCL_MINOR_VERSION > 6
#   define UNICODE_OUT_OF_RANGE(ch) (((ch) & 0x1FFFFF) >= 0x31360)
#else
#   define UNICODE_OUT_OF_RANGE(ch) (((ch) & 0x1F0000) != 0)
#endif

/*
 * The following constants are used to determine the category of a
 * Unicode character.







|







1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
    -2760383, -2760127, -2768575, 1859714, -9044927, -10823615, -12158,
    -10830783, -10833599, -10832575, -10830015, -10817983, -10824127,
    -10818751, 237633, -12223, -10830527, -9058239, 237698, 9949314,
    18, 17, 10305, 10370, 10049, 10114, 8769, 8834
};

#if TCL_UTF_MAX > 3 || TCL_MAJOR_VERSION > 8 || TCL_MINOR_VERSION > 6
#   define UNICODE_OUT_OF_RANGE(ch) (((ch) & 0x1FFFFF) >= 0x323C0)
#else
#   define UNICODE_OUT_OF_RANGE(ch) (((ch) & 0x1F0000) != 0)
#endif

/*
 * The following constants are used to determine the category of a
 * Unicode character.
Changes to generic/tclZipfs.c.
5742
5743
5744
5745
5746
5747
5748

5749
5750
5751
5752
5753
5754
5755
	    */
	}
    }
}

static void
ZipfsFinalize(void) {

    Tcl_DeleteHashTable(&ZipFS.fileHash);
    Tcl_Free(ZipFS.fallbackEntryEncoding);
    ZipFS.initialized = -1;
}

static void
ZipfsMountExitHandler(







>







5742
5743
5744
5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
	    */
	}
    }
}

static void
ZipfsFinalize(void) {
    Tcl_FSUnregister(&zipfsFilesystem);
    Tcl_DeleteHashTable(&ZipFS.fileHash);
    Tcl_Free(ZipFS.fallbackEntryEncoding);
    ZipFS.initialized = -1;
}

static void
ZipfsMountExitHandler(
Changes to library/http/http.tcl.
23
24
25
26
27
28
29

30
31
32
33
34
35
36
	    -cookiejar {}
	    -pipeline 1
	    -postfresh 0
	    -proxyhost {}
	    -proxyport {}
	    -proxyfilter http::ProxyRequired
	    -repost 0

	    -urlencoding utf-8
	    -zip 1
	}
	# We need a useragent string of this style or various servers will
	# refuse to send us compressed content even when we ask for it. This
	# follows the de-facto layout of user-agent strings in current browsers.
	# Safe interpreters do not have ::tcl_platform(os) or







>







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
	    -cookiejar {}
	    -pipeline 1
	    -postfresh 0
	    -proxyhost {}
	    -proxyport {}
	    -proxyfilter http::ProxyRequired
	    -repost 0
	    -threadlevel 0
	    -urlencoding utf-8
	    -zip 1
	}
	# We need a useragent string of this style or various servers will
	# refuse to send us compressed content even when we ask for it. This
	# follows the de-facto layout of user-agent strings in current browsers.
	# Safe interpreters do not have ::tcl_platform(os) or
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

	# Create a map for HTTP/1.1 open sockets
	variable socketMapping
	variable socketRdState
	variable socketWrState
	variable socketRdQueue
	variable socketWrQueue

	variable socketClosing
	variable socketPlayCmd

	if {[info exists socketMapping]} {
	    # Close open sockets on re-init.  Do not permit retries.
	    foreach {url sock} [array get socketMapping] {
		unset -nocomplain socketClosing($url)
		unset -nocomplain socketPlayCmd($url)
		CloseSocket $sock
	    }
	}

	# CloseSocket should have unset the socket* arrays, one element at
	# a time.  Now unset anything that was overlooked.
	# Traces on "unset socketRdState(*)" will call CancelReadPipeline and
	# cancel any queued responses.
	# Traces on "unset socketWrState(*)" will call CancelWritePipeline and
	# cancel any queued requests.
	array unset socketMapping
	array unset socketRdState
	array unset socketWrState
	array unset socketRdQueue
	array unset socketWrQueue

	array unset socketClosing
	array unset socketPlayCmd

	array set socketMapping {}
	array set socketRdState {}
	array set socketWrState {}
	array set socketRdQueue {}
	array set socketWrQueue {}

	array set socketClosing {}
	array set socketPlayCmd {}


    }
    init

    variable urlTypes
    if {![info exists urlTypes]} {
	set urlTypes(http) [list 80 ::socket]
    }

    variable encodings [string tolower [encoding names]]
    # This can be changed, but iso8859-1 is the RFC standard.
    variable defaultCharset
    if {![info exists defaultCharset]} {
	set defaultCharset "iso8859-1"







>


>




















>


>





>


>
>





|







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

	# Create a map for HTTP/1.1 open sockets
	variable socketMapping
	variable socketRdState
	variable socketWrState
	variable socketRdQueue
	variable socketWrQueue
	variable socketPhQueue
	variable socketClosing
	variable socketPlayCmd
	variable socketCoEvent
	if {[info exists socketMapping]} {
	    # Close open sockets on re-init.  Do not permit retries.
	    foreach {url sock} [array get socketMapping] {
		unset -nocomplain socketClosing($url)
		unset -nocomplain socketPlayCmd($url)
		CloseSocket $sock
	    }
	}

	# CloseSocket should have unset the socket* arrays, one element at
	# a time.  Now unset anything that was overlooked.
	# Traces on "unset socketRdState(*)" will call CancelReadPipeline and
	# cancel any queued responses.
	# Traces on "unset socketWrState(*)" will call CancelWritePipeline and
	# cancel any queued requests.
	array unset socketMapping
	array unset socketRdState
	array unset socketWrState
	array unset socketRdQueue
	array unset socketWrQueue
	array unset socketPhQueue
	array unset socketClosing
	array unset socketPlayCmd
	array unset socketCoEvent
	array set socketMapping {}
	array set socketRdState {}
	array set socketWrState {}
	array set socketRdQueue {}
	array set socketWrQueue {}
	array set socketPhQueue {}
	array set socketClosing {}
	array set socketPlayCmd {}
	array set socketCoEvent {}
	return
    }
    init

    variable urlTypes
    if {![info exists urlTypes]} {
	set urlTypes(http) [list 80 ::http::socket]
    }

    variable encodings [string tolower [encoding names]]
    # This can be changed, but iso8859-1 is the RFC standard.
    variable defaultCharset
    if {![info exists defaultCharset]} {
	set defaultCharset "iso8859-1"
136
137
138
139
140
141
142





































































143
144






145



146
147
148
149
150
151
152
153
154
155
156
	([!\u0023-+\u002D-:<-\u005B\u005D-~]*)         # Match the value
	(?:
	 \s* ; \s*                                     # LITERAL: semicolon
	 ([^\u0000]+)                                  # Match the options
	)?
    }






































































    namespace export geturl config reset wait formatQuery quoteString
    namespace export register unregister registerError






    # - Useful, but not exported: data, size, status, code, cleanup, error,



    #   meta, ncode, mapReply, init.  Comments suggest that "init" can be used
    #   for re-initialisation, although the command is undocumented.
    # - Not exported, probably should be upper-case initial letter as part
    #   of the internals: getTextLine, make-transformation-chunked.
}

# http::Log --
#
#	Debugging output -- define this to observe HTTP/1.1 socket usage.
#	Should echo any args received.
#







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

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







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
	([!\u0023-+\u002D-:<-\u005B\u005D-~]*)         # Match the value
	(?:
	 \s* ; \s*                                     # LITERAL: semicolon
	 ([^\u0000]+)                                  # Match the options
	)?
    }

    variable TmpSockCounter 0
    variable ThreadCounter  0

    variable reasonDict [dict create {*}{
        100 Continue
        101 {Switching Protocols}
        102 Processing
        103 {Early Hints}
        200 OK
        201 Created
        202 Accepted
        203 {Non-Authoritative Information}
        204 {No Content}
        205 {Reset Content}
        206 {Partial Content}
        207 Multi-Status
        208 {Already Reported}
        226 {IM Used}
        300 {Multiple Choices}
        301 {Moved Permanently}
        302 Found
        303 {See Other}
        304 {Not Modified}
        305 {Use Proxy}
        306 (Unused)
        307 {Temporary Redirect}
        308 {Permanent Redirect}
        400 {Bad Request}
        401 Unauthorized
        402 {Payment Required}
        403 Forbidden
        404 {Not Found}
        405 {Method Not Allowed}
        406 {Not Acceptable}
        407 {Proxy Authentication Required}
        408 {Request Timeout}
        409 Conflict
        410 Gone
        411 {Length Required}
        412 {Precondition Failed}
        413 {Content Too Large}
        414 {URI Too Long}
        415 {Unsupported Media Type}
        416 {Range Not Satisfiable}
        417 {Expectation Failed}
        418 (Unused)
        421 {Misdirected Request}
        422 {Unprocessable Content}
        423 Locked
        424 {Failed Dependency}
        425 {Too Early}
        426 {Upgrade Required}
        428 {Precondition Required}
        429 {Too Many Requests}
        431 {Request Header Fields Too Large}
        451 {Unavailable For Legal Reasons}
        500 {Internal Server Error}
        501 {Not Implemented}
        502 {Bad Gateway}
        503 {Service Unavailable}
        504 {Gateway Timeout}
        505 {HTTP Version Not Supported}
        506 {Variant Also Negotiates}
        507 {Insufficient Storage}
        508 {Loop Detected}
        510 {Not Extended (OBSOLETED)}
        511 {Network Authentication Required}
    }]

    namespace export geturl config reset wait formatQuery postError quoteString
    namespace export register unregister registerError
    namespace export requestLine requestHeaders requestHeaderValue
    namespace export responseLine responseHeaders responseHeaderValue
    namespace export responseCode responseBody responseInfo reasonPhrase
    # - Legacy aliases, were never exported:
    #     data, code, mapReply, meta, ncode
    # - Callable from outside (e.g. from TLS) by fully-qualified name, but
    #   not exported:
    #     socket
    # - Useful, but never exported (and likely to have naming collisions):
    #     size, status, cleanup, error, init
    #   Comments suggest that "init" can be used for re-initialisation,
    #   although the command is undocumented.
    # - Never exported, renamed from lower-case names:
    #   GetTextLine, MakeTransformationChunked.
}

# http::Log --
#
#	Debugging output -- define this to observe HTTP/1.1 socket usage.
#	Should echo any args received.
#
219
220
221
222
223
224
225



226
227
228
229
230



231
232

233
234



























235
236
237
238
239
240
241
    set pat ^-(?:[join $options |])$
    if {[llength $args] == 1} {
	set flag [lindex $args 0]
	if {![regexp -- $pat $flag]} {
	    return -code error "Unknown option $flag, must be: $usage"
	}
	return $http($flag)



    } else {
	foreach {flag value} $args {
	    if {![regexp -- $pat $flag]} {
		return -code error "Unknown option $flag, must be: $usage"
	    }



	    set http($flag) $value
	}

    }
}




























# http::Finish --
#
#	Clean up the socket and eval close time callbacks
#
# Arguments:
#	token	    Connection token.







>
>
>





>
>
>


>


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







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
    set pat ^-(?:[join $options |])$
    if {[llength $args] == 1} {
	set flag [lindex $args 0]
	if {![regexp -- $pat $flag]} {
	    return -code error "Unknown option $flag, must be: $usage"
	}
	return $http($flag)
    } elseif {[llength $args] % 2} {
	return -code error "If more than one argument is supplied, the\
		number of arguments must be even"
    } else {
	foreach {flag value} $args {
	    if {![regexp -- $pat $flag]} {
		return -code error "Unknown option $flag, must be: $usage"
	    }
	    if {($flag eq {-threadlevel}) && ($value ni {0 1 2})} {
		return -code error {Option -threadlevel must be 0, 1 or 2}
	    }
	    set http($flag) $value
	}
	return
    }
}

# ------------------------------------------------------------------------------
#  Proc http::reasonPhrase
# ------------------------------------------------------------------------------
# Command to return the IANA-recommended "reason phrase" for a HTTP Status Code.
# Information obtained from:
# https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
#
# Arguments:
# code        - A valid HTTP Status Code (integer from 100 to 599)
#
# Return Value: the reason phrase
# ------------------------------------------------------------------------------

proc http::reasonPhrase {code} {
    variable reasonDict
    if {![regexp -- {^[1-5][0-9][0-9]$} $code]} {
        set msg {argument must be a three-digit integer from 100 to 599}
        return -code error $msg
    }
    if {[dict exists $reasonDict $code]} {
        set reason [dict get $reasonDict $code]
    } else {
        set reason Unassigned
    }
    return $reason
}

# http::Finish --
#
#	Clean up the socket and eval close time callbacks
#
# Arguments:
#	token	    Connection token.
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

proc http::Finish {token {errormsg ""} {skipCB 0}} {
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue

    variable socketClosing
    variable socketPlayCmd


    variable $token
    upvar 0 $token state
    global errorInfo errorCode
    set closeQueue 0
    if {$errormsg ne ""} {
	set state(error) [list $errormsg $errorInfo $errorCode]
	set state(status) "error"
    }
    if {[info commands ${token}EventCoroutine] ne {}} {
	rename ${token}EventCoroutine {}








    }

    # Is this an upgrade request/response?
    set upgradeResponse \
	[expr {    [info exists state(upgradeRequest)] && $state(upgradeRequest)

		&& [info exists state(http)] && [ncode $token] eq {101}

		&& [info exists state(connection)] && "upgrade" in $state(connection)

		&& [info exists state(upgrade)] && "" ne $state(upgrade)}]



    if {  ($state(status) eq "timeout")
       || ($state(status) eq "error")
       || ($state(status) eq "eof")
    } {
	set closeQueue 1
	set connId $state(socketinfo)

	set sock $state(sock)
	CloseSocket $state(sock) $token













    } elseif {$upgradeResponse} {
	# Special handling for an upgrade request/response.
	# - geturl ensures that this is not a "persistent" socket used for
	#   multiple HTTP requests, so a call to KeepSocket is not needed.
	# - Leave socket open, so a call to CloseSocket is not needed either.
	# - Remove fileevent bindings.  The caller will set its own bindings.
	# - THE CALLER MUST PROCESS THE UPGRADED SOCKET IN THE CALLBACK COMMAND
	#   PASSED TO http::geturl AS -command callback.
	catch {fileevent $state(sock) readable {}}
	catch {fileevent $state(sock) writable {}}
    } elseif {
          ([info exists state(-keepalive)] && !$state(-keepalive))
       || ([info exists state(connection)] && ("close" in $state(connection)))
    } {
	set closeQueue 1
	set connId $state(socketinfo)

	set sock $state(sock)
	CloseSocket $state(sock) $token





    } elseif {
	  ([info exists state(-keepalive)] && $state(-keepalive))
       && ([info exists state(connection)] && ("close" ni $state(connection)))
    } {
	KeepSocket $token
    }
    if {[info exists state(after)]} {
	after cancel $state(after)
	unset state(after)
    }
    if {[info exists state(-command)] && (!$skipCB)
	    && (![info exists state(done-command-cb)])} {
	set state(done-command-cb) yes
	if {[catch {eval $state(-command) {$token}} err] && $errormsg eq ""} {
	    set state(error) [list $err $errorInfo $errorCode]
	    set state(status) error
	}
    }

    if {    $closeQueue
	 && [info exists socketMapping($connId)]
	 && ($socketMapping($connId) eq $sock)
    } {
	http::CloseQueuedQueries $connId $token

    }

}

# http::KeepSocket -
#
#	Keep a socket in the persistent sockets table and connect it to its next
#	queued task if possible.  Otherwise leave it idle and ready for its next
#	use.







>


>









|
|
>
>
>
>
>
>
>
>




|
>
|
>
|
>
|
>
>







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
















>
|
|
>
>
>
>
>













|










>

>







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

proc http::Finish {token {errormsg ""} {skipCB 0}} {
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    variable $token
    upvar 0 $token state
    global errorInfo errorCode
    set closeQueue 0
    if {$errormsg ne ""} {
	set state(error) [list $errormsg $errorInfo $errorCode]
	set state(status) "error"
    }
    if {[info commands ${token}--EventCoroutine] ne {}} {
	rename ${token}--EventCoroutine {}
    }
    if {[info commands ${token}--SocketCoroutine] ne {}} {
	rename ${token}--SocketCoroutine {}
    }
    if {[info exists state(socketcoro)]} {
        Log $token Cancel socket after-idle event (Finish)
        after cancel $state(socketcoro)
        unset state(socketcoro)
    }

    # Is this an upgrade request/response?
    set upgradeResponse \
	[expr {    [info exists state(upgradeRequest)]
		&& $state(upgradeRequest)
		&& [info exists state(http)]
		&& ([ncode $token] eq {101})
		&& [info exists state(connection)]
		&& ("upgrade" in $state(connection))
		&& [info exists state(upgrade)]
		&& ("" ne $state(upgrade))
	}]

    if {  ($state(status) eq "timeout")
       || ($state(status) eq "error")
       || ($state(status) eq "eof")
    } {
	set closeQueue 1
	set connId $state(socketinfo)
	if {[info exists state(sock)]} {
	    set sock $state(sock)
	    CloseSocket $state(sock) $token
	} else {
            # When opening the socket and calling http::reset
            # immediately, the socket may not yet exist.
            # Test http-4.11 may come here.
	}
	if {$state(tid) ne {}} {
            # When opening the socket in a thread, and calling http::reset
            # immediately, the thread may still exist.
            # Test http-4.11 may come here.
	    thread::release $state(tid)
	    set state(tid) {}
	} else {
	}
    } elseif {$upgradeResponse} {
	# Special handling for an upgrade request/response.
	# - geturl ensures that this is not a "persistent" socket used for
	#   multiple HTTP requests, so a call to KeepSocket is not needed.
	# - Leave socket open, so a call to CloseSocket is not needed either.
	# - Remove fileevent bindings.  The caller will set its own bindings.
	# - THE CALLER MUST PROCESS THE UPGRADED SOCKET IN THE CALLBACK COMMAND
	#   PASSED TO http::geturl AS -command callback.
	catch {fileevent $state(sock) readable {}}
	catch {fileevent $state(sock) writable {}}
    } elseif {
          ([info exists state(-keepalive)] && !$state(-keepalive))
       || ([info exists state(connection)] && ("close" in $state(connection)))
    } {
	set closeQueue 1
	set connId $state(socketinfo)
	if {[info exists state(sock)]} {
	    set sock $state(sock)
	    CloseSocket $state(sock) $token
	} else {
            # When opening the socket and calling http::reset
            # immediately, the socket may not yet exist.
            # Test http-4.11 may come here.
	}
    } elseif {
	  ([info exists state(-keepalive)] && $state(-keepalive))
       && ([info exists state(connection)] && ("close" ni $state(connection)))
    } {
	KeepSocket $token
    }
    if {[info exists state(after)]} {
	after cancel $state(after)
	unset state(after)
    }
    if {[info exists state(-command)] && (!$skipCB)
	    && (![info exists state(done-command-cb)])} {
	set state(done-command-cb) yes
	if {[catch {namespace eval :: $state(-command) $token} err] && $errormsg eq ""} {
	    set state(error) [list $err $errorInfo $errorCode]
	    set state(status) error
	}
    }

    if {    $closeQueue
	 && [info exists socketMapping($connId)]
	 && ($socketMapping($connId) eq $sock)
    } {
	http::CloseQueuedQueries $connId $token
	# This calls Unset.  Other cases do not need the call.
    }
    return
}

# http::KeepSocket -
#
#	Keep a socket in the persistent sockets table and connect it to its next
#	queued task if possible.  Otherwise leave it idle and ready for its next
#	use.
344
345
346
347
348
349
350

351
352

353
354
355
356
357
358
359
proc http::KeepSocket {token} {
    variable http
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue

    variable socketClosing
    variable socketPlayCmd


    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]

    # Keep this socket open for another request ("Keep-Alive").
    # React if the server half-closes the socket.







>


>







501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
proc http::KeepSocket {token} {
    variable http
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]

    # Keep this socket open for another request ("Keep-Alive").
    # React if the server half-closes the socket.
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
	     && [info exists socketRdQueue($connId)]
	     && [llength $socketRdQueue($connId)]
	} {
	    # The usual case for pipelined responses - if another response is
	    # queued, arrange to read it.
	    set token3 [lindex $socketRdQueue($connId) 0]
	    set socketRdQueue($connId) [lrange $socketRdQueue($connId) 1 end]
	    variable $token3
	    upvar 0 $token3 state3
	    set tk2 [namespace tail $token3]

	    #Log pipelined, GRANT read access to $token3 in KeepSocket
	    set socketRdState($connId) $token3
	    ReceiveResponse $token3

	    # Other pipelined cases.
	    # - The test above ensures that, for the pipelined cases in the two







<
<
<







539
540
541
542
543
544
545



546
547
548
549
550
551
552
	     && [info exists socketRdQueue($connId)]
	     && [llength $socketRdQueue($connId)]
	} {
	    # The usual case for pipelined responses - if another response is
	    # queued, arrange to read it.
	    set token3 [lindex $socketRdQueue($connId) 0]
	    set socketRdQueue($connId) [lrange $socketRdQueue($connId) 1 end]




	    #Log pipelined, GRANT read access to $token3 in KeepSocket
	    set socketRdState($connId) $token3
	    ReceiveResponse $token3

	    # Other pipelined cases.
	    # - The test above ensures that, for the pipelined cases in the two
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
	    #   "pending" write token is in front of the rest of the write
	    #   queue.
	    # - The write state is not Wready and therefore appears to be busy,
	    #   but because it is "pending" we know that it is reserved for the
	    #   first item in the write queue, a non-pipelined request that is
	    #   waiting for the read queue to empty.  That has now happened: so
	    #   give that request read and write access.
	    variable $token3
	    set conn [set ${token3}(tmpConnArgs)]
	    #Log nonpipeline, GRANT r/w access to $token3 in KeepSocket
	    set socketRdState($connId) $token3
	    set socketWrState($connId) $token3
	    set socketWrQueue($connId) [lrange $socketWrQueue($connId) 1 end]
	    # Connect does its own fconfigure.
	    fileevent $state(sock) writable [list http::Connect $token3 {*}$conn]
	    #Log ---- $state(sock) << conn to $token3 for HTTP request (c)







<
|







577
578
579
580
581
582
583

584
585
586
587
588
589
590
591
	    #   "pending" write token is in front of the rest of the write
	    #   queue.
	    # - The write state is not Wready and therefore appears to be busy,
	    #   but because it is "pending" we know that it is reserved for the
	    #   first item in the write queue, a non-pipelined request that is
	    #   waiting for the read queue to empty.  That has now happened: so
	    #   give that request read and write access.

	    set conn [set ${token3}(connArgs)]
	    #Log nonpipeline, GRANT r/w access to $token3 in KeepSocket
	    set socketRdState($connId) $token3
	    set socketWrState($connId) $token3
	    set socketWrQueue($connId) [lrange $socketWrQueue($connId) 1 end]
	    # Connect does its own fconfigure.
	    fileevent $state(sock) writable [list http::Connect $token3 {*}$conn]
	    #Log ---- $state(sock) << conn to $token3 for HTTP request (c)
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
	    # Tests:
	    # - In this case the read state (tested above) is Rready and the
	    #   write state (tested here) is Wready - there is no "pending"
	    #   request.
	    # Code:
	    # - The code is the same as the code below for the nonpipelined
	    #   case with a queued request.
	    variable $token3
	    set conn [set ${token3}(tmpConnArgs)]
	    #Log nonpipeline, GRANT r/w access to $token3 in KeepSocket
	    set socketRdState($connId) $token3
	    set socketWrState($connId) $token3
	    set socketWrQueue($connId) [lrange $socketWrQueue($connId) 1 end]
	    # Connect does its own fconfigure.
	    fileevent $state(sock) writable [list http::Connect $token3 {*}$conn]
	    #Log ---- $state(sock) << conn to $token3 for HTTP request (c)

	} elseif {
		(!$state(-pipeline))
	     && [info exists socketWrQueue($connId)]
	     && [llength $socketWrQueue($connId)]
	     && ("close" ni $state(connection))
	} {
	    # If not pipelined, (socketRdState eq Rready) tells us that we are
	    # ready for the next write - there is no need to check
	    # socketWrState. Write the next request, if one is waiting.
	    # If the next request is pipelined, it receives premature read
	    # access to the socket. This is not a problem.
	    set token3 [lindex $socketWrQueue($connId) 0]
	    variable $token3
	    set conn [set ${token3}(tmpConnArgs)]
	    #Log nonpipeline, GRANT r/w access to $token3 in KeepSocket
	    set socketRdState($connId) $token3
	    set socketWrState($connId) $token3
	    set socketWrQueue($connId) [lrange $socketWrQueue($connId) 1 end]
	    # Connect does its own fconfigure.
	    fileevent $state(sock) writable [list http::Connect $token3 {*}$conn]
	    #Log ---- $state(sock) << conn to $token3 for HTTP request (d)

	} elseif {(!$state(-pipeline))} {
	    set socketWrState($connId) Wready
	    # Rready and Wready and idle: nothing to do.
	}

    } else {
	CloseSocket $state(sock) $token
	# There is no socketMapping($state(socketinfo)), so it does not matter
	# that CloseQueuedQueries is not called.
    }

}

# http::CheckEof -
#
#	Read from a socket and close it if eof.
#	The command is bound to "fileevent readable" on an idle socket, and
#	"eof" is the only event that should trigger the binding, occurring when







<
|




















<
|


















>







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
	    # Tests:
	    # - In this case the read state (tested above) is Rready and the
	    #   write state (tested here) is Wready - there is no "pending"
	    #   request.
	    # Code:
	    # - The code is the same as the code below for the nonpipelined
	    #   case with a queued request.

	    set conn [set ${token3}(connArgs)]
	    #Log nonpipeline, GRANT r/w access to $token3 in KeepSocket
	    set socketRdState($connId) $token3
	    set socketWrState($connId) $token3
	    set socketWrQueue($connId) [lrange $socketWrQueue($connId) 1 end]
	    # Connect does its own fconfigure.
	    fileevent $state(sock) writable [list http::Connect $token3 {*}$conn]
	    #Log ---- $state(sock) << conn to $token3 for HTTP request (c)

	} elseif {
		(!$state(-pipeline))
	     && [info exists socketWrQueue($connId)]
	     && [llength $socketWrQueue($connId)]
	     && ("close" ni $state(connection))
	} {
	    # If not pipelined, (socketRdState eq Rready) tells us that we are
	    # ready for the next write - there is no need to check
	    # socketWrState. Write the next request, if one is waiting.
	    # If the next request is pipelined, it receives premature read
	    # access to the socket. This is not a problem.
	    set token3 [lindex $socketWrQueue($connId) 0]

	    set conn [set ${token3}(connArgs)]
	    #Log nonpipeline, GRANT r/w access to $token3 in KeepSocket
	    set socketRdState($connId) $token3
	    set socketWrState($connId) $token3
	    set socketWrQueue($connId) [lrange $socketWrQueue($connId) 1 end]
	    # Connect does its own fconfigure.
	    fileevent $state(sock) writable [list http::Connect $token3 {*}$conn]
	    #Log ---- $state(sock) << conn to $token3 for HTTP request (d)

	} elseif {(!$state(-pipeline))} {
	    set socketWrState($connId) Wready
	    # Rready and Wready and idle: nothing to do.
	}

    } else {
	CloseSocket $state(sock) $token
	# There is no socketMapping($state(socketinfo)), so it does not matter
	# that CloseQueuedQueries is not called.
    }
    return
}

# http::CheckEof -
#
#	Read from a socket and close it if eof.
#	The command is bound to "fileevent readable" on an idle socket, and
#	"eof" is the only event that should trigger the binding, occurring when
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

    if {[catch {eof $sock} res] || $res} {
	# The server has half-closed the socket.
	# If a new write has started, its transaction will fail and
	# will then be error-handled.
	CloseSocket $sock
    }

}

# http::CloseSocket -
#
#	Close a socket and remove it from the persistent sockets table.  If
#	possible an http token is included here but when we are called from a
#	fileevent on remote closure we need to find the correct entry - hence
#	the "else" block of the first "if" command.

proc http::CloseSocket {s {token {}}} {
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue

    variable socketClosing
    variable socketPlayCmd


    set tk [namespace tail $token]

    catch {fileevent $s readable {}}
    set connId {}
    if {$token ne ""} {
	variable $token







>















>


>







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

    if {[catch {eof $sock} res] || $res} {
	# The server has half-closed the socket.
	# If a new write has started, its transaction will fail and
	# will then be error-handled.
	CloseSocket $sock
    }
    return
}

# http::CloseSocket -
#
#	Close a socket and remove it from the persistent sockets table.  If
#	possible an http token is included here but when we are called from a
#	fileevent on remote closure we need to find the correct entry - hence
#	the "else" block of the first "if" command.

proc http::CloseSocket {s {token {}}} {
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    set tk [namespace tail $token]

    catch {fileevent $s readable {}}
    set connId {}
    if {$token ne ""} {
	variable $token
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
    if {    ($connId ne {})
	 && [info exists socketMapping($connId)]
	 && ($socketMapping($connId) eq $s)
    } {
	Log "Closing connection $connId (sock $socketMapping($connId))"
	if {[catch {close $socketMapping($connId)} err]} {
	    Log "Error closing connection: $err"

	}
	if {$token eq {}} {
	    # Cases with a non-empty token are handled by Finish, so the tokens
	    # are finished in connection order.
	    http::CloseQueuedQueries $connId

	}
    } else {
	Log "Closing socket $s (no connection info)"
	if {[catch {close $s} err]} {
	    Log "Error closing socket: $err"

	}
    }

}

# http::CloseQueuedQueries
#
#	connId  - identifier "domain:port" for the connection
#	token   - (optional) used only for logging
#
# Called from http::CloseSocket and http::Finish, after a connection is closed,
# to clear the read and write queues if this has not already been done.

proc http::CloseQueuedQueries {connId {token {}}} {
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue

    variable socketClosing
    variable socketPlayCmd



    if {![info exists socketMapping($connId)]} {
	# Command has already been called.
	# Don't come here again - especially recursively.
	return
    }

    # Used only for logging.







>





>





>


>
















>


>

>







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
    if {    ($connId ne {})
	 && [info exists socketMapping($connId)]
	 && ($socketMapping($connId) eq $s)
    } {
	Log "Closing connection $connId (sock $socketMapping($connId))"
	if {[catch {close $socketMapping($connId)} err]} {
	    Log "Error closing connection: $err"
	} else {
	}
	if {$token eq {}} {
	    # Cases with a non-empty token are handled by Finish, so the tokens
	    # are finished in connection order.
	    http::CloseQueuedQueries $connId
	} else {
	}
    } else {
	Log "Closing socket $s (no connection info)"
	if {[catch {close $s} err]} {
	    Log "Error closing socket: $err"
	} else {
	}
    }
    return
}

# http::CloseQueuedQueries
#
#	connId  - identifier "domain:port" for the connection
#	token   - (optional) used only for logging
#
# Called from http::CloseSocket and http::Finish, after a connection is closed,
# to clear the read and write queues if this has not already been done.

proc http::CloseQueuedQueries {connId {token {}}} {
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    ##Log CloseQueuedQueries $connId
    if {![info exists socketMapping($connId)]} {
	# Command has already been called.
	# Don't come here again - especially recursively.
	return
    }

    # Used only for logging.
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
	# Before unsetting, there is some unfinished business.
	# - If the server sent "Connection: close", we have stored the command
	#   for retrying any queued requests in socketPlayCmd, so copy that
	#   value for execution below.  socketClosing(*) was also set.
	# - Also clear the queues to prevent calls to Finish that would set the
	#   state for the requests that will be retried to "finished with error
	#   status".

	set unfinished $socketPlayCmd($connId)
	set socketRdQueue($connId) {}
	set socketWrQueue($connId) {}
    } else {
	set unfinished {}
    }

    Unset $connId

    if {$unfinished ne {}} {
	Log ^R$tk Any unfinished transactions (excluding $token) failed \
		- token $token
	{*}$unfinished

    }

}

# http::Unset
#
#	The trace on "unset socketRdState(*)" will call CancelReadPipeline
#	and cancel any queued responses.
#	The trace on "unset socketWrState(*)" will call CancelWritePipeline
#	and cancel any queued requests.

proc http::Unset {connId} {
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue

    variable socketClosing
    variable socketPlayCmd


    unset socketMapping($connId)
    unset socketRdState($connId)
    unset socketWrState($connId)
    unset -nocomplain socketRdQueue($connId)
    unset -nocomplain socketWrQueue($connId)
    unset -nocomplain socketClosing($connId)
    unset -nocomplain socketPlayCmd($connId)

}

# http::reset --
#
#	See documentation for details.
#
# Arguments:







>











|

>

>















>


>








>







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
	# Before unsetting, there is some unfinished business.
	# - If the server sent "Connection: close", we have stored the command
	#   for retrying any queued requests in socketPlayCmd, so copy that
	#   value for execution below.  socketClosing(*) was also set.
	# - Also clear the queues to prevent calls to Finish that would set the
	#   state for the requests that will be retried to "finished with error
	#   status".
	# - At this stage socketPhQueue is empty.
	set unfinished $socketPlayCmd($connId)
	set socketRdQueue($connId) {}
	set socketWrQueue($connId) {}
    } else {
	set unfinished {}
    }

    Unset $connId

    if {$unfinished ne {}} {
	Log ^R$tk Any unfinished transactions (excluding $token) failed \
		- token $token - unfinished $unfinished
	{*}$unfinished
	# Calls ReplayIfClose.
    }
    return
}

# http::Unset
#
#	The trace on "unset socketRdState(*)" will call CancelReadPipeline
#	and cancel any queued responses.
#	The trace on "unset socketWrState(*)" will call CancelWritePipeline
#	and cancel any queued requests.

proc http::Unset {connId} {
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    unset socketMapping($connId)
    unset socketRdState($connId)
    unset socketWrState($connId)
    unset -nocomplain socketRdQueue($connId)
    unset -nocomplain socketWrQueue($connId)
    unset -nocomplain socketClosing($connId)
    unset -nocomplain socketPlayCmd($connId)
    return
}

# http::reset --
#
#	See documentation for details.
#
# Arguments:
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
    catch {fileevent $state(sock) writable {}}
    Finish $token
    if {[info exists state(error)]} {
	set errorlist $state(error)
	unset state
	eval ::error $errorlist
    }

}

# http::geturl --
#
#	Establishes a connection to a remote url via http.
#
# Arguments:
#	url		The http URL to goget.
#	args		Option value pairs. Valid options include:
#				-blocksize, -validate, -headers, -timeout
# Results:
#	Returns a token for this connection. This token is the name of an
#	array that the caller should unset to garbage collect the state.

proc http::geturl {url args} {

















































































    variable http
    variable urlTypes
    variable defaultCharset
    variable defaultKeepalive
    variable strict


    # Initialize the state variable, an array. We'll return the name of this
    # array as the token for the transaction.




    if {![info exists http(uid)]} {
	set http(uid) 0
    }
    set token [namespace current]::[incr http(uid)]
    ##Log Starting http::geturl - token $token
    variable $token
    upvar 0 $token state







>















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





>




>
>
>







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
    catch {fileevent $state(sock) writable {}}
    Finish $token
    if {[info exists state(error)]} {
	set errorlist $state(error)
	unset state
	eval ::error $errorlist
    }
    return
}

# http::geturl --
#
#	Establishes a connection to a remote url via http.
#
# Arguments:
#	url		The http URL to goget.
#	args		Option value pairs. Valid options include:
#				-blocksize, -validate, -headers, -timeout
# Results:
#	Returns a token for this connection. This token is the name of an
#	array that the caller should unset to garbage collect the state.

proc http::geturl {url args} {
    variable urlTypes

    # The value is set in the namespace header of this file.  If the file has
    # not been modified the value is "::http::socket".
    set socketCmd [lindex $urlTypes(http) 1]

    # - If ::tls::socketCmd has its default value "::socket", change it to the
    #   new value $socketCmd.
    # - If the old value is different, then it has been modified either by the
    #   script or by the Tcl installation, and replaced by a new command.  The
    #   script or installation that modified ::tls::socketCmd is also
    #   responsible for integrating ::http::socket into its own "new" command,
    #   if it wishes to do so.

    if {[info exists ::tls::socketCmd] && ($::tls::socketCmd eq {::socket})} {
        set ::tls::socketCmd $socketCmd
    }

    set token [CreateToken $url {*}$args]
    variable $token
    upvar 0 $token state

    AsyncTransaction $token

    # --------------------------------------------------------------------------
    # Synchronous Call to http::geturl
    # --------------------------------------------------------------------------
    # - If the call to http::geturl is asynchronous, it is now complete (apart
    #   from delivering the return value).
    # - If the call to http::geturl is synchronous, the command must now wait
    #   for the HTTP transaction to be completed.  The call to http::wait uses
    #   vwait, which may be inappropriate if the caller makes other HTTP
    #   requests in the background.
    # --------------------------------------------------------------------------

    if {![info exists state(-command)]} {
	# geturl does EVERYTHING asynchronously, so if the user
	# calls it synchronously, we just do a wait here.
	http::wait $token

	if {![info exists state]} {
	    # If we timed out then Finish has been called and the users
	    # command callback may have cleaned up the token. If so we end up
	    # here with nothing left to do.
	    return $token
	} elseif {$state(status) eq "error"} {
	    # Something went wrong while trying to establish the connection.
	    # Clean up after events and such, but DON'T call the command
	    # callback (if available) because we're going to throw an
	    # exception from here instead.
	    set err [lindex $state(error) 0]
	    cleanup $token
	    return -code error $err
	}
    }

    return $token
}

# ------------------------------------------------------------------------------
#  Proc http::CreateToken
# ------------------------------------------------------------------------------
# Command to convert arguments into an initialised request token.
# The return value is the variable name of the token.
#
# Other effects:
# - Sets ::http::http(usingThread) if not already done
# - Sets ::http::http(uid) if not already done
# - Increments ::http::http(uid)
# - May increment ::http::TmpSockCounter
# - Alters ::http::socketPlayCmd, ::http::socketWrQueue if a -keepalive 1
#   request is appended to the queue of a persistent socket that is already
#   scheduled to close.
#   This also sets state(alreadyQueued) to 1.
# - Alters ::http::socketPhQueue if a -keepalive 1 request is appended to the
#   queue of a persistent socket that has not yet been created (and is therefore
#   represented by a placeholder).
#   This also sets state(ReusingPlaceholder) to 1.
# ------------------------------------------------------------------------------

proc http::CreateToken {url args} {
    variable http
    variable urlTypes
    variable defaultCharset
    variable defaultKeepalive
    variable strict
    variable TmpSockCounter

    # Initialize the state variable, an array. We'll return the name of this
    # array as the token for the transaction.

    if {![info exists http(usingThread)]} {
	set http(usingThread) 0
    }
    if {![info exists http(uid)]} {
	set http(uid) 0
    }
    set token [namespace current]::[incr http(uid)]
    ##Log Starting http::geturl - token $token
    variable $token
    upvar 0 $token state
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
	-queryblocksize 8192
	-validate	0
	-headers	{}
	-timeout	0
	-type		application/x-www-form-urlencoded
	-queryprogress	{}
	-protocol	1.1

	binary		0
	state		created
	meta		{}
	method		{}
	coding		{}
	currentsize	0
	totalsize	0
	querylength	0
	queryoffset	0
	type		text/html
	body		{}
	status		""
	http		""



	connection	keep-alive




    }
    set state(-keepalive) $defaultKeepalive
    set state(-strict) $strict
    # These flags have their types verified [Bug 811170]
    array set type {
	-binary		boolean
	-blocksize	integer

	-queryblocksize integer
	-strict		boolean
	-timeout	integer
	-validate	boolean
	-headers	list
    }
    set state(charset)	$defaultCharset
    set options {
	-binary -blocksize -channel -command -handler -headers -keepalive
	-method -myaddr -progress -protocol -query -queryblocksize
	-querychannel -queryprogress -strict -timeout -type -validate
    }
    set usage [join [lsort $options] ", "]
    set options [string map {- ""} $options]
    set pat ^-(?:[join $options |])$
    foreach {flag value} $args {
	if {[regexp -- $pat $flag]} {
	    # Validate numbers
	    if {    [info exists type($flag)]
	        && (![string is $type($flag) -strict $value])
	    } {
		unset $token
		return -code error \
		    "Bad value for $flag ($value), must be $type($flag)"
	    }
	    if {($flag eq "-headers") && ([llength $value] % 2 != 0)} {
		unset $token
		return -code error \
		    "Bad value for $flag ($value), number of list elements must be even"
	    }
	    set state($flag) $value
	} else {
	    unset $token
	    return -code error "Unknown option $flag, can be: $usage"
	}
    }







>









|



>
>
>

>
>
>
>







>








|


















|
|







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
	-queryblocksize 8192
	-validate	0
	-headers	{}
	-timeout	0
	-type		application/x-www-form-urlencoded
	-queryprogress	{}
	-protocol	1.1
	-guesstype      0
	binary		0
	state		created
	meta		{}
	method		{}
	coding		{}
	currentsize	0
	totalsize	0
	querylength	0
	queryoffset	0
	type		application/octet-stream
	body		{}
	status		""
	http		""
	httpResponse    {}
	responseCode    {}
	reasonPhrase    {}
	connection	keep-alive
	tid             {}
	requestHeaders  {}
	requestLine     {}
	transfer        {}
    }
    set state(-keepalive) $defaultKeepalive
    set state(-strict) $strict
    # These flags have their types verified [Bug 811170]
    array set type {
	-binary		boolean
	-blocksize	integer
	-guesstype      boolean
	-queryblocksize integer
	-strict		boolean
	-timeout	integer
	-validate	boolean
	-headers	list
    }
    set state(charset)	$defaultCharset
    set options {
	-binary -blocksize -channel -command -guesstype -handler -headers -keepalive
	-method -myaddr -progress -protocol -query -queryblocksize
	-querychannel -queryprogress -strict -timeout -type -validate
    }
    set usage [join [lsort $options] ", "]
    set options [string map {- ""} $options]
    set pat ^-(?:[join $options |])$
    foreach {flag value} $args {
	if {[regexp -- $pat $flag]} {
	    # Validate numbers
	    if {    [info exists type($flag)]
	        && (![string is $type($flag) -strict $value])
	    } {
		unset $token
		return -code error \
		    "Bad value for $flag ($value), must be $type($flag)"
	    }
	    if {($flag eq "-headers") && ([llength $value] % 2 != 0)} {
		unset $token
		return -code error "Bad value for $flag ($value), number\
			of list elements must be even"
	    }
	    set state($flag) $value
	} else {
	    unset $token
	    return -code error "Unknown option $flag, can be: $usage"
	}
    }
842
843
844
845
846
847
848



849
850
851
852
853
854
855
    # The "http" is the protocol, the user is "jschmoe", the password is
    # "xyzzy", the host is "www.bogus.net", the port is "8000", the path is
    # "/foo/bar.tml", the query is "q=foo", and the fragment is "changes".
    #
    # Note that the RE actually combines the user and password parts, as
    # recommended in RFC 3986. Indeed, that RFC states that putting passwords
    # in URLs is a Really Bad Idea, something with which I would agree utterly.



    #
    # From a validation perspective, we need to ensure that the parts of the
    # URL that are going to the server are correctly encoded.  This is only
    # done if $state(-strict) is true (inherited from $::http::strict).

    set URLmatcher {(?x)		# this is _expanded_ syntax
	^







>
>
>







1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
    # The "http" is the protocol, the user is "jschmoe", the password is
    # "xyzzy", the host is "www.bogus.net", the port is "8000", the path is
    # "/foo/bar.tml", the query is "q=foo", and the fragment is "changes".
    #
    # Note that the RE actually combines the user and password parts, as
    # recommended in RFC 3986. Indeed, that RFC states that putting passwords
    # in URLs is a Really Bad Idea, something with which I would agree utterly.
    # RFC 9110 Sec 4.2.4 goes further than this, and deprecates the format
    # "user:password@".  It is retained here for backward compatibility,
    # but its use is not recommended.
    #
    # From a validation perspective, we need to ensure that the parts of the
    # URL that are going to the server are correctly encoded.  This is only
    # done if $state(-strict) is true (inherited from $::http::strict).

    set URLmatcher {(?x)		# this is _expanded_ syntax
	^
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

    if {$port eq ""} {
	set port $defport
    }
    if {![catch {$http(-proxyfilter) $host} proxy]} {
	set phost [lindex $proxy 0]
	set pport [lindex $proxy 1]



    }

    # OK, now reassemble into a full URL
    set url ${proto}://
    if {$user ne ""} {
	append url $user
	append url @
    }
    append url $host
    if {$port != $defport} {
	append url : $port
    }
    append url $srvurl
    # Don't append the fragment!
    set state(url) $url

    set sockopts [list -async]

    # If we are using the proxy, we must pass in the full URL that includes
    # the server name.

    if {[info exists phost] && ($phost ne "")} {
	set srvurl $url
	set targetAddr [list $phost $pport]
    } else {
	set targetAddr [list $host $port]
    }
    # Proxy connections aren't shared among different hosts.
    set state(socketinfo) $host:$port

    # Save the accept types at this point to prevent a race condition. [Bug
    # c11a51c482]
    set state(accept-types) $http(-accept)








>
>
>













|


<
<
<
<
<
<
<
<
<
<
<







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

    if {$port eq ""} {
	set port $defport
    }
    if {![catch {$http(-proxyfilter) $host} proxy]} {
	set phost [lindex $proxy 0]
	set pport [lindex $proxy 1]
    } else {
	set phost {}
	set pport {}
    }

    # OK, now reassemble into a full URL
    set url ${proto}://
    if {$user ne ""} {
	append url $user
	append url @
    }
    append url $host
    if {$port != $defport} {
	append url : $port
    }
    append url $srvurl
    # Don't append the fragment! RFC 7230 Sec 5.1
    set state(url) $url












    # Proxy connections aren't shared among different hosts.
    set state(socketinfo) $host:$port

    # Save the accept types at this point to prevent a race condition. [Bug
    # c11a51c482]
    set state(accept-types) $http(-accept)

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

    # RFC 7320 A.1 - HTTP/1.0 Keep-Alive is problematic. We do not support it.
    if {$state(-protocol) eq "1.0"} {
	set state(connection) close
	set state(-keepalive) 0
    }




















    # See if we are supposed to use a previously opened channel.
    # - In principle, ANY call to http::geturl could use a previously opened
    #   channel if it is available - the "Connection: keep-alive" header is a
    #   request to leave the channel open AFTER completion of this call.
    # - In fact, we try to use an existing channel only if -keepalive 1 -- this
    #   means that at most one channel is left open for each value of
    #   $state(socketinfo). This property simplifies the mapping of open
    #   channels.
    set reusing 0
    set alreadyQueued 0

    if {$state(-keepalive)} {
	variable socketMapping
	variable socketRdState
	variable socketWrState
	variable socketRdQueue
	variable socketWrQueue

	variable socketClosing
	variable socketPlayCmd


	if {[info exists socketMapping($state(socketinfo))]} {
	    # - If the connection is idle, it has a "fileevent readable" binding
	    #   to http::CheckEof, in case the server times out and half-closes
	    #   the socket (http::CheckEof closes the other half).
	    # - We leave this binding in place until just before the last
	    #   puts+flush in http::Connected (GET/HEAD) or http::Write (POST),







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









|
>






>


>







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

    # RFC 7320 A.1 - HTTP/1.0 Keep-Alive is problematic. We do not support it.
    if {$state(-protocol) eq "1.0"} {
	set state(connection) close
	set state(-keepalive) 0
    }

    # If we are using the proxy, we must pass in the full URL that includes
    # the server name.
    if {$phost ne ""} {
	set srvurl $url
	set targetAddr [list $phost $pport]
    } else {
	set targetAddr [list $host $port]
    }

    set sockopts [list -async]

    # Pass -myaddr directly to the socket command
    if {[info exists state(-myaddr)]} {
	lappend sockopts -myaddr $state(-myaddr)
    }

    set state(connArgs) [list $proto $phost $srvurl]
    set state(openCmd) [list {*}$defcmd {*}$sockopts {*}$targetAddr]

    # See if we are supposed to use a previously opened channel.
    # - In principle, ANY call to http::geturl could use a previously opened
    #   channel if it is available - the "Connection: keep-alive" header is a
    #   request to leave the channel open AFTER completion of this call.
    # - In fact, we try to use an existing channel only if -keepalive 1 -- this
    #   means that at most one channel is left open for each value of
    #   $state(socketinfo). This property simplifies the mapping of open
    #   channels.
    set reusing 0
    set state(alreadyQueued) 0
    set state(ReusingPlaceholder) 0
    if {$state(-keepalive)} {
	variable socketMapping
	variable socketRdState
	variable socketWrState
	variable socketRdQueue
	variable socketWrQueue
	variable socketPhQueue
	variable socketClosing
	variable socketPlayCmd
	variable socketCoEvent

	if {[info exists socketMapping($state(socketinfo))]} {
	    # - If the connection is idle, it has a "fileevent readable" binding
	    #   to http::CheckEof, in case the server times out and half-closes
	    #   the socket (http::CheckEof closes the other half).
	    # - We leave this binding in place until just before the last
	    #   puts+flush in http::Connected (GET/HEAD) or http::Write (POST),
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
		# Since we have only one persistent socket per server, and the
		# old socket is not yet dead, add the request to the write queue
		# of the dying socket, which will be replayed by ReplayIfClose.
		# Also add it to socketWrQueue(*) which is used only if an error
		# causes a call to Finish.
		set reusing 1
		set sock $socketMapping($state(socketinfo))
		Log "reusing socket $sock for $state(socketinfo) - token $token"

		set alreadyQueued 1
		lassign $socketPlayCmd($state(socketinfo)) com0 com1 com2 com3
		lappend com3 $token
		set socketPlayCmd($state(socketinfo)) [list $com0 $com1 $com2 $com3]
		lappend socketWrQueue($state(socketinfo)) $token



	    } elseif {[catch {fconfigure $socketMapping($state(socketinfo))}]} {



		# FIXME Is it still possible for this code to be executed? If
		#       so, this could be another place to call TestForReplay,
		#       rather than discarding the queued transactions.
		Log "WARNING: socket for $state(socketinfo) was closed\
			- token $token"
		Log "WARNING - if testing, pay special attention to this\
			case (GH) which is seldom executed - token $token"

		# This will call CancelReadPipeline, CancelWritePipeline, and
		# cancel any queued requests, responses.
		Unset $state(socketinfo)
	    } else {
		# Use the persistent socket.
		# The socket may not be ready to write: an earlier request might
		# still be still writing (in the pipelined case) or
		# writing/reading (in the nonpipeline case). This possibility
		# is handled by socketWrQueue later in this command.

		set reusing 1
		set sock $socketMapping($state(socketinfo))


		Log "reusing socket $sock for $state(socketinfo) - token $token"



	    }
	    # Do not automatically close the connection socket.
	    set state(connection) keep-alive
	}
    }

    if {$reusing} {
	# Define state(tmpState) and state(tmpOpenCmd) for use
	# by http::ReplayIfDead if the persistent connection has died.
	set state(tmpState) [array get state]

	# Pass -myaddr directly to the socket command
	if {[info exists state(-myaddr)]} {
	    lappend sockopts -myaddr $state(-myaddr)
	}

	set state(tmpOpenCmd) [list {*}$defcmd {*}$sockopts {*}$targetAddr]
    }

    set state(reusing) $reusing

    # Excluding ReplayIfDead and the decision whether to call it, there are four














































    # places outside http::geturl where state(reusing) is used:
    # - Connected   - if reusing and not pipelined, start the state(-timeout)
    #                 timeout (when writing).
    # - DoneRequest - if reusing and pipelined, send the next pipelined write
    # - Event       - if reusing and pipelined, start the state(-timeout)
    #                 timeout (when reading).
    # - Event       - if (not reusing) and pipelined, send the next pipelined
    #                 write
































    # See comments above re the start of this timeout in other cases.
    if {(!$state(reusing)) && ($state(-timeout) > 0)} {
	set state(after) [after $state(-timeout) \
		[list http::reset $token timeout]]
    }


    if {![info exists sock]} {


	# Pass -myaddr directly to the socket command






	if {[info exists state(-myaddr)]} {
	    lappend sockopts -myaddr $state(-myaddr)





















	}
	set pre [clock milliseconds]


	##Log pre socket opened, - token $token
	##Log [concat $defcmd $sockopts $targetAddr] - token $token













	if {[catch {eval $defcmd $sockopts $targetAddr} sock errdict]} {



	    # Something went wrong while trying to establish the connection.









	    # Clean up after events and such, but DON'T call the command


	    # callback (if available) because we're going to throw an
	    # exception from here instead.












	    set state(sock) NONE














	    Finish $token $sock 1

























	    cleanup $token


	    dict unset errdict -level














	    return -options $errdict $sock












	} else {







	    # Initialisation of a new socket.
	    ##Log post socket opened, - token $token
	    ##Log socket opened, now fconfigure - token $token

	    set delay [expr {[clock milliseconds] - $pre}]
	    if {$delay > 3000} {
		Log socket delay $delay - token $token
	    }
	    fconfigure $sock -translation {auto crlf} \
			     -buffersize $state(-blocksize)
	    ##Log socket opened, DONE fconfigure - token $token
	}
    }
    # Command [socket] is called with -async, but takes 5s to 5.1s to return,
    # with probability of order 1 in 10,000.  This may be a bizarre scheduling
    # issue with my (KJN's) system (Fedora Linux).
    # This does not cause a problem (unless the request times out when this





    # command returns).

    set state(sock) $sock
    Log "Using $sock for $state(socketinfo) - token $token" \
	[expr {$state(-keepalive)?"keepalive":""}]














































    if {    $state(-keepalive)





	 && (![info exists socketMapping($state(socketinfo))])

    } {
	# Freshly-opened socket that we would like to become persistent.
	set socketMapping($state(socketinfo)) $sock

	if {![info exists socketRdState($state(socketinfo))]} {












	    set socketRdState($state(socketinfo)) {}

	    set varName ::http::socketRdState($state(socketinfo))
	    trace add variable $varName unset ::http::CancelReadPipeline
	}
	if {![info exists socketWrState($state(socketinfo))]} {
	    set socketWrState($state(socketinfo)) {}
	    set varName ::http::socketWrState($state(socketinfo))
	    trace add variable $varName unset ::http::CancelWritePipeline
	}

	if {$state(-pipeline)} {

	    #Log new, init for pipelined, GRANT write access to $token in geturl
	    # Also grant premature read access to the socket. This is OK.


	    set socketRdState($state(socketinfo)) $token
	    set socketWrState($state(socketinfo)) $token
	} else {

	    # socketWrState is not used by this non-pipelined transaction.



	    # We cannot leave it as "Wready" because the next call to
	    # http::geturl with a pipelined transaction would conclude that the

	    # socket is available for writing.


	    #Log new, init for nonpipeline, GRANT r/w access to $token in geturl

	    set socketRdState($state(socketinfo)) $token


	    set socketWrState($state(socketinfo)) $token

	}















	set socketRdQueue($state(socketinfo)) {}
	set socketWrQueue($state(socketinfo)) {}


	set socketClosing($state(socketinfo)) 0


	set socketPlayCmd($state(socketinfo)) {ReplayIfClose Wready {} {}}
    }

    if {![info exists phost]} {
	set phost ""



    }
    if {$reusing} {
	# For use by http::ReplayIfDead if the persistent connection has died.
	# Also used by NextPipelinedWrite.
	set state(tmpConnArgs) [list $proto $phost $srvurl]
    }



    # The element socketWrState($connId) has a value which is either the name of
    # the token that is permitted to write to the socket, or "Wready" if no
    # token is permitted to write.
    #
    # The code that sets the value to Wready immediately calls
    # http::NextPipelinedWrite, which examines socketWrQueue($connId) and
    # processes the next request in the queue, if there is one.  The value
    # Wready is not found when the interpreter is in the event loop unless the
    # socket is idle.
    #
    # The element socketRdState($connId) has a value which is either the name of
    # the token that is permitted to read from the socket, or "Rready" if no
    # token is permitted to read.
    #
    # The code that sets the value to Rready then examines
    # socketRdQueue($connId) and processes the next request in the queue, if
    # there is one.  The value Rready is not found when the interpreter is in
    # the event loop unless the socket is idle.









































    if {$alreadyQueued} {



	# A write may or may not be in progress.  There is no need to set
	# socketWrState to prevent another call stealing write access - all
	# subsequent calls on this socket will come here because the socket
	# will close after the current read, and its
	# socketClosing($connId) is 1.
	##Log "HTTP request for token $token is queued"








|

|




>
>
>
|
>
>
>













|
|
|
|
>


>
>
|
>
|
>






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

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







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



>







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

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


|
|
<


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

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

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







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
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
		# Since we have only one persistent socket per server, and the
		# old socket is not yet dead, add the request to the write queue
		# of the dying socket, which will be replayed by ReplayIfClose.
		# Also add it to socketWrQueue(*) which is used only if an error
		# causes a call to Finish.
		set reusing 1
		set sock $socketMapping($state(socketinfo))
		Log "reusing closing socket $sock for $state(socketinfo) - token $token"

		set state(alreadyQueued) 1
		lassign $socketPlayCmd($state(socketinfo)) com0 com1 com2 com3
		lappend com3 $token
		set socketPlayCmd($state(socketinfo)) [list $com0 $com1 $com2 $com3]
		lappend socketWrQueue($state(socketinfo)) $token
		##Log socketPlayCmd($state(socketinfo)) is $socketPlayCmd($state(socketinfo))
		##Log socketWrQueue($state(socketinfo)) is $socketWrQueue($state(socketinfo))
	    } elseif {
		   [catch {fconfigure $socketMapping($state(socketinfo))}]
		&& (![SockIsPlaceHolder $socketMapping($state(socketinfo))])
	    } {
		###Log "Socket $socketMapping($state(socketinfo)) for $state(socketinfo)"
		# FIXME Is it still possible for this code to be executed? If
		#       so, this could be another place to call TestForReplay,
		#       rather than discarding the queued transactions.
		Log "WARNING: socket for $state(socketinfo) was closed\
			- token $token"
		Log "WARNING - if testing, pay special attention to this\
			case (GH) which is seldom executed - token $token"

		# This will call CancelReadPipeline, CancelWritePipeline, and
		# cancel any queued requests, responses.
		Unset $state(socketinfo)
	    } else {
		# Use the persistent socket.
		# - The socket may not be ready to write: an earlier request might
		#   still be still writing (in the pipelined case) or
		#   writing/reading (in the nonpipeline case). This possibility
		#   is handled by socketWrQueue later in this command.
		# - The socket may not yet exist, and be defined with a placeholder.
		set reusing 1
		set sock $socketMapping($state(socketinfo))
		if {[SockIsPlaceHolder $sock]} {
		    set state(ReusingPlaceholder) 1
		    lappend socketPhQueue($sock) $token
		} else {
		}
		Log "reusing open socket $sock for $state(socketinfo) - token $token"
	    }
	    # Do not automatically close the connection socket.
	    set state(connection) keep-alive
	}
    }














    set state(reusing) $reusing
    unset reusing

    if {![info exists sock]} {
        # N.B. At this point ([info exists sock] == $state(reusing)).
        # This will no longer be true after we set a value of sock here.
	# Give the socket a placeholder name.
	set sock HTTP_PLACEHOLDER_[incr TmpSockCounter]
    }
    set state(sock) $sock

    if {$state(reusing)} {
	# Define these for use (only) by http::ReplayIfDead if the persistent
	# connection has died.
	set state(tmpConnArgs) $state(connArgs)
	set state(tmpState) [array get state]
	set state(tmpOpenCmd) $state(openCmd)
    }
    return $token
}


# ------------------------------------------------------------------------------
#  Proc ::http::SockIsPlaceHolder
# ------------------------------------------------------------------------------
# Command to return 0 if the argument is a genuine socket handle, or 1 if is a
# placeholder value generated by geturl or ReplayCore before the real socket is
# created.
#
# Arguments:
# sock        - either a valid socket handle or a placeholder value
#
# Return Value: 0 or 1
# ------------------------------------------------------------------------------

proc http::SockIsPlaceHolder {sock} {
    expr {[string range $sock 0 16] eq {HTTP_PLACEHOLDER_}}
}


# ------------------------------------------------------------------------------
# state(reusing)
# ------------------------------------------------------------------------------
# - state(reusing) is set by geturl, ReplayCore
# - state(reusing) is used by geturl, AsyncTransaction, OpenSocket,
#   ConfigureNewSocket, and ScheduleRequest when creating and configuring the
#   connection.
# - state(reusing) is used by Connect, Connected, Event x 2 when deciding
#   whether to call TestForReplay.
# - Other places where state(reusing) is used:
#   - Connected   - if reusing and not pipelined, start the state(-timeout)
#                   timeout (when writing).
#   - DoneRequest - if reusing and pipelined, send the next pipelined write
#   - Event       - if reusing and pipelined, start the state(-timeout)
#                   timeout (when reading).
#   - Event       - if (not reusing) and pipelined, send the next pipelined
#                   write.
# ------------------------------------------------------------------------------


# ------------------------------------------------------------------------------
#  Proc http::AsyncTransaction
# ------------------------------------------------------------------------------
# This command is called by geturl and ReplayCore to prepare the HTTP
# transaction prescribed by a suitably prepared token.
#
# Arguments:
# token         - connection token (name of an array)
#
# Return Value: none
# ------------------------------------------------------------------------------

proc http::AsyncTransaction {token} {
    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]

    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    set sock $state(sock)

    # See comments above re the start of this timeout in other cases.
    if {(!$state(reusing)) && ($state(-timeout) > 0)} {
	set state(after) [after $state(-timeout) \
		[list http::reset $token timeout]]
    }

    if {    $state(-keepalive)
	 && (![info exists socketMapping($state(socketinfo))])
    } {
	# This code is executed only for the first -keepalive request on a
	# socket.  It makes the socket persistent.
	##Log "  PreparePersistentConnection" $token -- $sock -- DO
        set DoLater [PreparePersistentConnection $token]
    } else {
        ##Log "  PreparePersistentConnection" $token -- $sock -- SKIP
        set DoLater {-traceread 0 -tracewrite 0}
    }

    if {$state(ReusingPlaceholder)} {
        # - This request was added to the socketPhQueue of a persistent
        #   connection.
        # - But the connection has not yet been created and is a placeholder;
        # - And the placeholder was created by an earlier request.
        # - When that earlier request calls OpenSocket, its placeholder is
        #   replaced with a true socket, and it then executes the equivalent of
        #   OpenSocket for any subsequent requests that have
        #   $state(ReusingPlaceholder).
        Log >J$tk after idle coro NO - ReusingPlaceholder
    } elseif {$state(alreadyQueued)} {
        # - This request was added to the socketWrQueue and socketPlayCmd
        #   of a persistent connection that will close at the end of its current
        #   read operation.
        Log >J$tk after idle coro NO - alreadyQueued
    } else {
        Log >J$tk after idle coro YES
        set CoroName ${token}--SocketCoroutine
        set cancel [after idle [list coroutine $CoroName ::http::OpenSocket \
                $token $DoLater]]
        dict set socketCoEvent($state(socketinfo)) $token $cancel
        set state(socketcoro) $cancel
    }

    return
}


# ------------------------------------------------------------------------------
#  Proc http::PreparePersistentConnection
# ------------------------------------------------------------------------------
# This command is called by AsyncTransaction to initialise a "persistent
# connection" based upon a socket placeholder.  It is called the first time the
# socket is associated with a "-keepalive" request.
#
# Arguments:
# token         - connection token (name of an array)
#
# Return Value: - DoLater, a dictionary of boolean values listing unfinished
#                 tasks; to be passed to ConfigureNewSocket via OpenSocket.
# ------------------------------------------------------------------------------

proc http::PreparePersistentConnection {token} {
    variable $token
    upvar 0 $token state

    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    set DoLater {-traceread 0 -tracewrite 0}
    set socketMapping($state(socketinfo)) $state(sock)


    if {![info exists socketRdState($state(socketinfo))]} {
        set socketRdState($state(socketinfo)) {}
        # set varName ::http::socketRdState($state(socketinfo))
        # trace add variable $varName unset ::http::CancelReadPipeline
        dict set DoLater -traceread 1
    }
    if {![info exists socketWrState($state(socketinfo))]} {
        set socketWrState($state(socketinfo)) {}
        # set varName ::http::socketWrState($state(socketinfo))
        # trace add variable $varName unset ::http::CancelWritePipeline
        dict set DoLater -tracewrite 1
    }

    if {$state(-pipeline)} {
        #Log new, init for pipelined, GRANT write access to $token in geturl
        # Also grant premature read access to the socket. This is OK.
        set socketRdState($state(socketinfo)) $token
        set socketWrState($state(socketinfo)) $token
    } else {
        # socketWrState is not used by this non-pipelined transaction.
        # We cannot leave it as "Wready" because the next call to
        # http::geturl with a pipelined transaction would conclude that the
        # socket is available for writing.
        #Log new, init for nonpipeline, GRANT r/w access to $token in geturl
        set socketRdState($state(socketinfo)) $token
        set socketWrState($state(socketinfo)) $token
    }

    set socketRdQueue($state(socketinfo)) {}
    set socketWrQueue($state(socketinfo)) {}
    set socketPhQueue($state(socketinfo)) {}
    set socketClosing($state(socketinfo)) 0
    set socketPlayCmd($state(socketinfo)) {ReplayIfClose Wready {} {}}
    set socketCoEvent($state(socketinfo)) {}

    return $DoLater
}

# ------------------------------------------------------------------------------
#  Proc ::http::OpenSocket
# ------------------------------------------------------------------------------
# This command is called as a coroutine idletask to start the asynchronous HTTP
# transaction in most cases.  For the exceptions, see the calling code in
# command AsyncTransaction.
#
# Arguments:
# token       - connection token (name of an array)
# DoLater     - dictionary of boolean values listing unfinished tasks
#
# Return Value: none
# ------------------------------------------------------------------------------

proc http::OpenSocket {token DoLater} {
    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]

    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    Log >K$tk Start OpenSocket coroutine

    if {![info exists state(-keepalive)]} {
        # The request has already been cancelled by the calling script.
        return
    }

    set sockOld $state(sock)

    dict unset socketCoEvent($state(socketinfo)) $token
    unset -nocomplain state(socketcoro)

    if {[catch {
        if {$state(reusing)} {
	    # If ($state(reusing)) is true, then we do not need to create a new
	    # socket, even if $sockOld is only a placeholder for a socket.
            set sock $sockOld
        } else {
	    # set sock in the [catch] below.
	    set pre [clock milliseconds]
	    ##Log pre socket opened, - token $token
	    ##Log $state(openCmd) - token $token
	    set sock [namespace eval :: $state(openCmd)]

	    # Normal return from $state(openCmd) always returns a valid socket.
	    # Initialisation of a new socket.
	    ##Log post socket opened, - token $token
	    ##Log socket opened, now fconfigure - token $token
	    set state(sock) $sock
	    set delay [expr {[clock milliseconds] - $pre}]
	    if {$delay > 3000} {
		Log socket delay $delay - token $token
	    }
	    fconfigure $sock -translation {auto crlf} \
			     -buffersize $state(-blocksize)
	    ##Log socket opened, DONE fconfigure - token $token
        }

        Log "Using $sock for $state(socketinfo) - token $token" \
	    [expr {$state(-keepalive)?"keepalive":""}]

        # Code above has set state(sock) $sock
        ConfigureNewSocket $token $sockOld $DoLater
    } result errdict]} {
        Finish $token $result
    }
    ##Log Leaving http::OpenSocket coroutine [info coroutine] - token $token
    return
}



# ------------------------------------------------------------------------------
#  Proc ::http::ConfigureNewSocket
# ------------------------------------------------------------------------------
# Command to initialise a newly-created socket.  Called only from OpenSocket.
#
# This command is called by OpenSocket whenever a genuine socket (sockNew) has
# been opened for for use by HTTP.  It does two things:
# (1) If $token uses a placeholder socket, this command replaces the placeholder
#     socket with the real socket, not only in $token but in all other requests
#     that use the same placeholder.
# (2) It calls ScheduleRequest to schedule each request that uses the socket.
#
#
# Value of sockOld/sockNew can be "sock" (genuine socket) or "ph" (placeholder).
# sockNew is ${token}(sock)
# sockOld   sockNew  CASES
#  sock       sock   (if $reusing, and sockOld is sock)
#  ph         sock   (if (not $reusing), and sockOld is ph)
#  ph         ph     (if $reusing, and sockOld is ph) - not called in this case
#  sock       ph     (cannot occur unless a bug)      - not called in this case
#                    (if (not $reusing), and sockOld is sock) - illogical
#
# Arguments:
# token         - connection token (name of an array)
# sockOld       - handle or placeholder used for a socket before the call to OpenSocket
# DoLater       - dictionary of boolean values listing unfinished tasks
#
# Return Value: none
# ------------------------------------------------------------------------------

proc http::ConfigureNewSocket {token sockOld DoLater} {
    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]

    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    set reusing $state(reusing)
    set sock $state(sock)
    ##Log "  ConfigureNewSocket" $token $sockOld ... -- $sock

    if {(!$reusing) && ($sock ne $sockOld)} {
        # Replace the placeholder value sockOld with sock.

        if {    [info exists socketMapping($state(socketinfo))]
             && ($socketMapping($state(socketinfo)) eq $sockOld)
        } {
            set socketMapping($state(socketinfo)) $sock
            ##Log set socketMapping($state(socketinfo)) $sock
        }

        # Now finish any tasks left over from PreparePersistentConnection on
        # the connection.
        #
        # The "unset" traces are fired by init (clears entire arrays), and
        # by http::Unset.
        # Unset is called by CloseQueuedQueries and (possibly never) by geturl.
        #
        # CancelReadPipeline, CancelWritePipeline call http::Finish for each
        # token.
        #
        # FIXME If Finish is placeholder-aware, these traces can be set earlier,
        # in PreparePersistentConnection.

        if {[dict get $DoLater -traceread]} {
	    set varName ::http::socketRdState($state(socketinfo))
	    trace add variable $varName unset ::http::CancelReadPipeline
        }
        if {[dict get $DoLater -tracewrite]} {

	    set varName ::http::socketWrState($state(socketinfo))
	    trace add variable $varName unset ::http::CancelWritePipeline
        }
    }

    # Do this in all cases.
    ScheduleRequest $token

    # Now look at all other tokens that use the placeholder $sockOld.
    if {    (!$reusing)
         && ($sock ne $sockOld)
         && [info exists socketPhQueue($sockOld)]
    } {
        ##Log "  ConfigureNewSocket" $token scheduled, now do $socketPhQueue($sockOld)
        foreach tok $socketPhQueue($sockOld) {
	    # 1. Amend the token's (sock).
	    ##Log set ${tok}(sock) $sock
	    set ${tok}(sock) $sock


	    # 2. Schedule the token's HTTP request.
	    # Every token in socketPhQueue(*) has reusing 1 alreadyQueued 0.
	    set ${tok}(reusing) 1
	    set ${tok}(alreadyQueued) 0
	    ScheduleRequest $tok
	}
	set socketPhQueue($sockOld) {}
    }
    ##Log "  ConfigureNewSocket" $token DONE

    return
}


# ------------------------------------------------------------------------------
# The values of array variables socketMapping etc.
# ------------------------------------------------------------------------------
# connId                 "$host:$port"
# socketMapping($connId) the handle or placeholder for the socket that is used
#                        for "-keepalive 1" requests to $connId.
# socketRdState($connId) the token that is currently reading from the socket.
#                        Other values: Rready (ready for next token to read).
# socketWrState($connId) the token that is currently writing to the socket.
#                        Other values: Wready (ready for next token to write),
#                        peNding (would be ready for next write, except that
#                        the integrity of a non-pipelined transaction requires
#                        waiting until the read(s) in progress are finished).
# socketRdQueue($connId) List of tokens that are queued for reading later.
# socketWrQueue($connId) List of tokens that are queued for writing later.
# socketPhQueue($connId) List of tokens that are queued to use a placeholder
#                        socket, when the real socket has not yet been created.
# socketClosing($connId) (boolean) true iff a server response header indicates
#                        that the server will close the connection at the end of
#                        the current response.
# socketPlayCmd($connId) The command to execute to replay pending and

#                        part-completed transactions if the socket closes early.


# socketCoEvent($connId) Identifier for the "after idle" event that will launch
#                        an OpenSocket coroutine to open or re-use a socket.
# ------------------------------------------------------------------------------






# ------------------------------------------------------------------------------
# Using socketWrState(*), socketWrQueue(*), socketRdState(*), socketRdQueue(*)
# ------------------------------------------------------------------------------
# The element socketWrState($connId) has a value which is either the name of
# the token that is permitted to write to the socket, or "Wready" if no
# token is permitted to write.
#
# The code that sets the value to Wready immediately calls
# http::NextPipelinedWrite, which examines socketWrQueue($connId) and
# processes the next request in the queue, if there is one.  The value
# Wready is not found when the interpreter is in the event loop unless the
# socket is idle.
#
# The element socketRdState($connId) has a value which is either the name of
# the token that is permitted to read from the socket, or "Rready" if no
# token is permitted to read.
#
# The code that sets the value to Rready then examines
# socketRdQueue($connId) and processes the next request in the queue, if
# there is one.  The value Rready is not found when the interpreter is in
# the event loop unless the socket is idle.
# ------------------------------------------------------------------------------


# ------------------------------------------------------------------------------
#  Proc http::ScheduleRequest
# ------------------------------------------------------------------------------
# Command to either begin the HTTP request, or add it to the appropriate queue.
# Called from two places in ConfigureNewSocket.
#
# Arguments:
# token         - connection token (name of an array)
#
# Return Value: none
# ------------------------------------------------------------------------------

proc http::ScheduleRequest {token} {
    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]

    Log >L$tk ScheduleRequest

    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    set Unfinished 0

    set reusing $state(reusing)
    set sockNew $state(sock)

    # The "if" tests below: must test against the current values of
    # socketWrState, socketRdState, and so the tests must be done here,
    # not earlier in PreparePersistentConnection.

    if {$state(alreadyQueued)} {
	# The request has been appended to the queue of a persistent socket
	# (that is scheduled to close and have its queue replayed).
	#
	# A write may or may not be in progress.  There is no need to set
	# socketWrState to prevent another call stealing write access - all
	# subsequent calls on this socket will come here because the socket
	# will close after the current read, and its
	# socketClosing($connId) is 1.
	##Log "HTTP request for token $token is queued"

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
	       && ($socketRdState($state(socketinfo)) ne "Rready")
    } {
	# A read is queued or in progress, but not a write.  Cannot start the
	# nonpipeline transaction, but must set socketWrState to prevent a
	# pipelined request jumping the queue.
	##Log "HTTP request for token $token is queued for nonpipeline use"
	#Log re-use nonpipeline, GRANT delayed write access to $token in geturl

	set socketWrState($state(socketinfo)) peNding
	lappend socketWrQueue($state(socketinfo)) $token

    } else {
	if {$reusing && $state(-pipeline)} {
	    #Log re-use pipelined, GRANT write access to $token in geturl


	    set socketWrState($state(socketinfo)) $token

	} elseif {$reusing} {

	    # Cf tests above - both are ready.


	    #Log re-use nonpipeline, GRANT r/w access to $token in geturl
	    set socketRdState($state(socketinfo)) $token
	    set socketWrState($state(socketinfo)) $token

	}




	# All (!$reusing) cases come here, and also some $reusing cases if the

	# connection is ready.




	#Log ---- $state(socketinfo) << conn to $token for HTTP request (a)

	# Connect does its own fconfigure.




	fileevent $sock writable \
		[list http::Connect $token $proto $phost $srvurl]



    }

    # Wait for the connection to complete.
    if {![info exists state(-command)]} {
	# geturl does EVERYTHING asynchronously, so if the user
	# calls it synchronously, we just do a wait here.
	http::wait $token

	if {![info exists state]} {
	    # If we timed out then Finish has been called and the users
	    # command callback may have cleaned up the token. If so we end up
	    # here with nothing left to do.
	    return $token

	} elseif {$state(status) eq "error"} {
	    # Something went wrong while trying to establish the connection.
	    # Clean up after events and such, but DON'T call the command
	    # callback (if available) because we're going to throw an
	    # exception from here instead.
	    set err [lindex $state(error) 0]
	    cleanup $token
	    return -code error $err
	}













    }

    ##Log Leaving http::geturl - token $token





    return $token
}

# http::Connected --
#
#	Callback used when the connection to the HTTP server is actually
#	established.
#







<




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

>
>
>
|
>
|
>
>
>
>

>

>
>
>
>
|
|
>
>
>
|

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







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
	       && ($socketRdState($state(socketinfo)) ne "Rready")
    } {
	# A read is queued or in progress, but not a write.  Cannot start the
	# nonpipeline transaction, but must set socketWrState to prevent a
	# pipelined request jumping the queue.
	##Log "HTTP request for token $token is queued for nonpipeline use"
	#Log re-use nonpipeline, GRANT delayed write access to $token in geturl

	set socketWrState($state(socketinfo)) peNding
	lappend socketWrQueue($state(socketinfo)) $token

    } else {
        if {$reusing && $state(-pipeline)} {
	    #Log new, init for pipelined, GRANT write access to $token in geturl
	    # DO NOT grant premature read access to the socket.
            # set socketRdState($state(socketinfo)) $token
            set socketWrState($state(socketinfo)) $token

        } elseif {$reusing} {
	    # socketWrState is not used by this non-pipelined transaction.
	    # We cannot leave it as "Wready" because the next call to
	    # http::geturl with a pipelined transaction would conclude that the
	    # socket is available for writing.
	    #Log new, init for nonpipeline, GRANT r/w access to $token in geturl
            set socketRdState($state(socketinfo)) $token
            set socketWrState($state(socketinfo)) $token
        } else {
        }

	# Process the request now.
	# - Command is not called unless $state(sock) is a real socket handle
	#   and not a placeholder.
	# - All (!$reusing) cases come here.
	# - Some $reusing cases come here too if the connection is
	#   marked as ready.  Those $reusing cases are:
	#   $reusing && ($socketWrState($state(socketinfo)) eq "Wready") &&
	#   EITHER !$pipeline && ($socketRdState($state(socketinfo)) eq "Rready")
	#   OR      $pipeline
	#
	#Log ---- $state(socketinfo) << conn to $token for HTTP request (a)
	##Log "  ScheduleRequest" $token -- fileevent $state(sock) writable for $token
	# Connect does its own fconfigure.

        lassign $state(connArgs) proto phost srvurl

	if {[catch {
		fileevent $state(sock) writable \
			[list http::Connect $token $proto $phost $srvurl]
	} res opts]} {
	    # The socket no longer exists.
	    ##Log bug -- socket gone -- $res -- $opts
	}






    }




    return
}









# ------------------------------------------------------------------------------
#  Proc http::SendHeader
# ------------------------------------------------------------------------------
# Command to send a request header, and keep a copy in state(requestHeaders)
# for debugging purposes.
#
# Arguments:
# token       - connection token (name of an array)
# key         - header name
# value       - header value
#
# Return Value: none
# ------------------------------------------------------------------------------

proc http::SendHeader {token key value} {
    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]
    set sock $state(sock)
    lappend state(requestHeaders) [string tolower $key] $value
    puts $sock "$key: $value"
    return
}

# http::Connected --
#
#	Callback used when the connection to the HTTP server is actually
#	established.
#
1350
1351
1352
1353
1354
1355
1356

1357
1358

1359
1360
1361
1362
1363
1364
1365
    variable http
    variable urlTypes
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue

    variable socketClosing
    variable socketPlayCmd


    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]

    if {$state(reusing) && (!$state(-pipeline)) && ($state(-timeout) > 0)} {
	set state(after) [after $state(-timeout) \







>


>







2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
    variable http
    variable urlTypes
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]

    if {$state(reusing) && (!$state(-pipeline)) && ($state(-timeout) > 0)} {
	set state(after) [after $state(-timeout) \
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
    }
    set accept_types_seen 0

    Log ^B$tk begin sending request - token $token

    if {[catch {
	set state(method) $how

	puts $sock "$how $srvurl HTTP/$state(-protocol)"

	set hostValue [GetFieldValue $state(-headers) Host]
	if {$hostValue ne {}} {
	    # Allow Host spoofing. [Bug 928154]
	    regexp {^[^:]+} $hostValue state(host)
	    puts $sock "Host: $hostValue"
	} elseif {$port == $defport} {
	    # Don't add port in this case, to handle broken servers. [Bug
	    # #504508]
	    set state(host) $host
	    puts $sock "Host: $host"
	} else {
	    set state(host) $host
	    puts $sock "Host: $host:$port"
	}
	puts $sock "User-Agent: $http(-useragent)"
	if {($state(-protocol) > 1.0) && $state(-keepalive)} {
	    # Send this header, because a 1.1 server is not compelled to treat
	    # this as the default.
	    puts $sock "Connection: keep-alive"
	}
	if {($state(-protocol) > 1.0) && !$state(-keepalive)} {
	    puts $sock "Connection: close" ;# RFC2616 sec 8.1.2.1
	}
	if {($state(-protocol) < 1.1)} {
	    # RFC7230 A.1
	    # Some server implementations of HTTP/1.0 have a faulty
	    # implementation of RFC 2068 Keep-Alive.
	    # Don't leave this to chance.
	    # For HTTP/1.0 we have already "set state(connection) close"
	    # and "state(-keepalive) 0".
	    puts $sock "Connection: close"
	}
	# RFC7230 A.1 - "clients are encouraged not to send the
	# Proxy-Connection header field in any requests"
	set accept_encoding_seen 0
	set content_type_seen 0
	foreach {key value} $state(-headers) {
	    set value [string map [list \n "" \r ""] $value]







>
|
>




|




|


|

|



|


|








|







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
    }
    set accept_types_seen 0

    Log ^B$tk begin sending request - token $token

    if {[catch {
	set state(method) $how
	set state(requestHeaders) {}
	set state(requestLine) "$how $srvurl HTTP/$state(-protocol)"
	puts $sock $state(requestLine)
	set hostValue [GetFieldValue $state(-headers) Host]
	if {$hostValue ne {}} {
	    # Allow Host spoofing. [Bug 928154]
	    regexp {^[^:]+} $hostValue state(host)
	    SendHeader $token Host $hostValue
	} elseif {$port == $defport} {
	    # Don't add port in this case, to handle broken servers. [Bug
	    # #504508]
	    set state(host) $host
	    SendHeader $token Host $host
	} else {
	    set state(host) $host
	    SendHeader $token Host "$host:$port"
	}
	SendHeader $token User-Agent $http(-useragent)
	if {($state(-protocol) > 1.0) && $state(-keepalive)} {
	    # Send this header, because a 1.1 server is not compelled to treat
	    # this as the default.
	    SendHeader $token Connection keep-alive
	}
	if {($state(-protocol) > 1.0) && !$state(-keepalive)} {
	    SendHeader $token Connection close ;# RFC2616 sec 8.1.2.1
	}
	if {($state(-protocol) < 1.1)} {
	    # RFC7230 A.1
	    # Some server implementations of HTTP/1.0 have a faulty
	    # implementation of RFC 2068 Keep-Alive.
	    # Don't leave this to chance.
	    # For HTTP/1.0 we have already "set state(connection) close"
	    # and "state(-keepalive) 0".
	    SendHeader $token Connection close
	}
	# RFC7230 A.1 - "clients are encouraged not to send the
	# Proxy-Connection header field in any requests"
	set accept_encoding_seen 0
	set content_type_seen 0
	foreach {key value} $state(-headers) {
	    set value [string map [list \n "" \r ""] $value]
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
		set content_type_seen 1
	    }
	    if {[string equal -nocase $key "content-length"]} {
		set contDone 1
		set state(querylength) $value
	    }
	    if {[string length $key]} {
		puts $sock "$key: $value"
	    }
	}
	# Allow overriding the Accept header on a per-connection basis. Useful
	# for working with REST services. [Bug c11a51c482]
	if {!$accept_types_seen} {
	    puts $sock "Accept: $state(accept-types)"
	}
	if {    (!$accept_encoding_seen)
	     && (![info exists state(-handler)])
	     && $http(-zip)
	} {


	    puts $sock "Accept-Encoding: gzip,deflate,compress"

	}
	if {$isQueryChannel && ($state(querylength) == 0)} {
	    # Try to determine size of data in channel. If we cannot seek, the
	    # surrounding catch will trap us

	    set start [tell $state(-querychannel)]
	    seek $state(-querychannel) 0 end







|





|





>
>
|
>







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
		set content_type_seen 1
	    }
	    if {[string equal -nocase $key "content-length"]} {
		set contDone 1
		set state(querylength) $value
	    }
	    if {[string length $key]} {
		SendHeader $token $key $value
	    }
	}
	# Allow overriding the Accept header on a per-connection basis. Useful
	# for working with REST services. [Bug c11a51c482]
	if {!$accept_types_seen} {
	    SendHeader $token Accept $state(accept-types)
	}
	if {    (!$accept_encoding_seen)
	     && (![info exists state(-handler)])
	     && $http(-zip)
	} {
	    SendHeader $token Accept-Encoding gzip,deflate
	} elseif {!$accept_encoding_seen} {
	    SendHeader $token Accept-Encoding identity
	} else {
	}
	if {$isQueryChannel && ($state(querylength) == 0)} {
	    # Try to determine size of data in channel. If we cannot seek, the
	    # surrounding catch will trap us

	    set start [tell $state(-querychannel)]
	    seek $state(-querychannel) 0 end
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
	    set separator ""
	    foreach {key value} [{*}$http(-cookiejar) \
		    getCookies $proto $host $state(path)] {
		append cookies $separator $key = $value
		set separator "; "
	    }
	    if {$cookies ne ""} {
		puts $sock "Cookie: $cookies"
	    }
	}

	# Flush the request header and set up the fileevent that will either
	# push the POST data or read the response.
	#
	# fileevent note:







|







2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
	    set separator ""
	    foreach {key value} [{*}$http(-cookiejar) \
		    getCookies $proto $host $state(path)] {
		append cookies $separator $key = $value
		set separator "; "
	    }
	    if {$cookies ne ""} {
		SendHeader $token Cookie $cookies
	    }
	}

	# Flush the request header and set up the fileevent that will either
	# push the POST data or read the response.
	#
	# fileevent note:
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
	# the same, and none behave all that well in any case. Servers should
	# always read their POST data if they expect the client to read their
	# response.

	if {$isQuery || $isQueryChannel} {
	    # POST method.
	    if {!$content_type_seen} {
		puts $sock "Content-Type: $state(-type)"
	    }
	    if {!$contDone} {
		puts $sock "Content-Length: $state(querylength)"
	    }
	    puts $sock ""
	    flush $sock
	    # Flush flushes the error in the https case with a bad handshake:
	    # else the socket never becomes writable again, and hangs until
	    # timeout (if any).








|


|







2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
	# the same, and none behave all that well in any case. Servers should
	# always read their POST data if they expect the client to read their
	# response.

	if {$isQuery || $isQueryChannel} {
	    # POST method.
	    if {!$content_type_seen} {
		SendHeader $token Content-Type $state(-type)
	    }
	    if {!$contDone} {
		SendHeader $token Content-Length $state(querylength)
	    }
	    puts $sock ""
	    flush $sock
	    # Flush flushes the error in the https case with a bad handshake:
	    # else the socket never becomes writable again, and hangs until
	    # timeout (if any).

1597
1598
1599
1600
1601
1602
1603

1604
1605
1606
1607
1608
1609
1610
		set msg {failed to use socket}
	    }
	    Finish $token $msg
	} elseif {$state(status) ne "error"} {
	    Finish $token $err
	}
    }

}

# http::registerError
#
#	Called (for example when processing TclTLS activity) to register
#	an error for a connection on a specific socket.  This helps
#	http::Connected to deliver meaningful error messages, e.g. when a TLS







>







2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
		set msg {failed to use socket}
	    }
	    Finish $token $msg
	} elseif {$state(status) ne "error"} {
	    Finish $token $err
	}
    }
    return
}

# http::registerError
#
#	Called (for example when processing TclTLS activity) to register
#	an error for a connection on a specific socket.  This helps
#	http::Connected to deliver meaningful error messages, e.g. when a TLS
1642
1643
1644
1645
1646
1647
1648

1649
1650

1651
1652
1653
1654
1655
1656
1657
proc http::DoneRequest {token} {
    variable http
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue

    variable socketClosing
    variable socketPlayCmd


    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]
    set sock $state(sock)

    # If pipelined, connect the next HTTP request to the socket.







>


>







2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
proc http::DoneRequest {token} {
    variable http
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]
    set sock $state(sock)

    # If pipelined, connect the next HTTP request to the socket.
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
	lappend socketRdQueue($state(socketinfo)) $token
    } else {
	# In the pipelined case, connection for reading depends on the
	# value of socketRdState.
	# In the nonpipeline case, connection for reading always occurs.
	ReceiveResponse $token
    }

}

# http::ReceiveResponse
#
#	Connects token to its socket for reading.

proc http::ReceiveResponse {token} {
    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]
    set sock $state(sock)

    #Log ---- $state(socketinfo) >> conn to $token for HTTP response
    lassign [fconfigure $sock -translation] trRead trWrite
    fconfigure $sock -translation [list auto $trWrite] \
		     -buffersize $state(-blocksize)
    Log ^D$tk begin receiving response - token $token

    coroutine ${token}EventCoroutine http::Event $sock $token
    if {[info exists state(-handler)] || [info exists state(-progress)]} {
        fileevent $sock readable [list http::EventGateway $sock $token]
    } else {
        fileevent $sock readable ${token}EventCoroutine
    }
    return
}


# http::EventGateway
#







>


















|



|







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
	lappend socketRdQueue($state(socketinfo)) $token
    } else {
	# In the pipelined case, connection for reading depends on the
	# value of socketRdState.
	# In the nonpipeline case, connection for reading always occurs.
	ReceiveResponse $token
    }
    return
}

# http::ReceiveResponse
#
#	Connects token to its socket for reading.

proc http::ReceiveResponse {token} {
    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]
    set sock $state(sock)

    #Log ---- $state(socketinfo) >> conn to $token for HTTP response
    lassign [fconfigure $sock -translation] trRead trWrite
    fconfigure $sock -translation [list auto $trWrite] \
		     -buffersize $state(-blocksize)
    Log ^D$tk begin receiving response - token $token

    coroutine ${token}--EventCoroutine http::Event $sock $token
    if {[info exists state(-handler)] || [info exists state(-progress)]} {
        fileevent $sock readable [list http::EventGateway $sock $token]
    } else {
        fileevent $sock readable ${token}--EventCoroutine
    }
    return
}


# http::EventGateway
#
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
#	  used only if -handler or -progress is specified.  In other cases,
#	  the coroutine is called directly.

proc http::EventGateway {sock token} {
    variable $token
    upvar 0 $token state
    fileevent $sock readable {}
    catch {${token}EventCoroutine} res opts
    if {[info commands ${token}EventCoroutine] ne {}} {
        # The coroutine can be deleted by completion (a non-yield return), by
        # http::Finish (when there is a premature end to the transaction), by
        # http::reset or http::cleanup, or if the caller set option -channel
        # but not option -handler: in the last case reading from the socket is
        # now managed by commands ::http::Copy*, http::ReceiveChunked, and
        # http::make-transformation-chunked.
        #
        # Catch in case the coroutine has closed the socket.
        catch {fileevent $sock readable [list http::EventGateway $sock $token]}
    }

    # If there was an error, re-throw it.
    return -options $opts $res







|
|





|







2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
#	  used only if -handler or -progress is specified.  In other cases,
#	  the coroutine is called directly.

proc http::EventGateway {sock token} {
    variable $token
    upvar 0 $token state
    fileevent $sock readable {}
    catch {${token}--EventCoroutine} res opts
    if {[info commands ${token}--EventCoroutine] ne {}} {
        # The coroutine can be deleted by completion (a non-yield return), by
        # http::Finish (when there is a premature end to the transaction), by
        # http::reset or http::cleanup, or if the caller set option -channel
        # but not option -handler: in the last case reading from the socket is
        # now managed by commands ::http::Copy*, http::ReceiveChunked, and
        # http::MakeTransformationChunked.
        #
        # Catch in case the coroutine has closed the socket.
        catch {fileevent $sock readable [list http::EventGateway $sock $token]}
    }

    # If there was an error, re-throw it.
    return -options $opts $res
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
	 && ([set token2 [lindex $socketWrQueue($connId) 0]
	      set ${token2}(-pipeline)
	     ]
	    )
    } {
	# - The usual case for a pipelined connection, ready for a new request.
	#Log pipelined, GRANT write access to $token2 in NextPipelinedWrite
	set conn [set ${token2}(tmpConnArgs)]
	set socketWrState($connId) $token2
	set socketWrQueue($connId) [lrange $socketWrQueue($connId) 1 end]
	# Connect does its own fconfigure.
	fileevent $state(sock) writable [list http::Connect $token2 {*}$conn]
	#Log ---- $connId << conn to $token2 for HTTP request (b)

	# In the tests below, the next request will be nonpipeline.







|







2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
	 && ([set token2 [lindex $socketWrQueue($connId) 0]
	      set ${token2}(-pipeline)
	     ]
	    )
    } {
	# - The usual case for a pipelined connection, ready for a new request.
	#Log pipelined, GRANT write access to $token2 in NextPipelinedWrite
	set conn [set ${token2}(connArgs)]
	set socketWrState($connId) $token2
	set socketWrQueue($connId) [lrange $socketWrQueue($connId) 1 end]
	# Connect does its own fconfigure.
	fileevent $state(sock) writable [list http::Connect $token2 {*}$conn]
	#Log ---- $connId << conn to $token2 for HTTP request (b)

	# In the tests below, the next request will be nonpipeline.
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858

	       && [info exists socketRdState($connId)]
	       && ($socketRdState($connId) eq "Rready")
    } {
	# The case in which the next request will be non-pipelined, and the read
	# and write queues is ready: which is the condition for a non-pipelined
	# write.
	variable $token3
	upvar 0 $token3 state3
	set conn [set ${token3}(tmpConnArgs)]
	#Log nonpipeline, GRANT r/w access to $token3 in NextPipelinedWrite
	set socketRdState($connId) $token3
	set socketWrState($connId) $token3
	set socketWrQueue($connId) [lrange $socketWrQueue($connId) 1 end]
	# Connect does its own fconfigure.
	fileevent $state(sock) writable [list http::Connect $token3 {*}$conn]
	#Log ---- $state(sock) << conn to $token3 for HTTP request (c)







<
<
|







2519
2520
2521
2522
2523
2524
2525


2526
2527
2528
2529
2530
2531
2532
2533

	       && [info exists socketRdState($connId)]
	       && ($socketRdState($connId) eq "Rready")
    } {
	# The case in which the next request will be non-pipelined, and the read
	# and write queues is ready: which is the condition for a non-pipelined
	# write.


	set conn [set ${token3}(connArgs)]
	#Log nonpipeline, GRANT r/w access to $token3 in NextPipelinedWrite
	set socketRdState($connId) $token3
	set socketWrState($connId) $token3
	set socketWrQueue($connId) [lrange $socketWrQueue($connId) 1 end]
	# Connect does its own fconfigure.
	fileevent $state(sock) writable [list http::Connect $token3 {*}$conn]
	#Log ---- $state(sock) << conn to $token3 for HTTP request (c)
1876
1877
1878
1879
1880
1881
1882

1883
1884
1885
1886
1887
1888
1889
	# - Because socketWrState($connId) is not set to Wready, the assignment
	#   of the connection to $token2 will be done elsewhere - by command
	#   http::KeepSocket when $socketRdState($connId) is set to "Rready".

	#Log re-use nonpipeline, GRANT delayed write access to $token in NextP..
	set socketWrState($connId) peNding
    }

}

# http::CancelReadPipeline
#
#	Cancel pipelined responses on a closing "Keep-Alive" socket.
#
#	- Called by a variable trace on "unset socketRdState($connId)".







>







2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
	# - Because socketWrState($connId) is not set to Wready, the assignment
	#   of the connection to $token2 will be done elsewhere - by command
	#   http::KeepSocket when $socketRdState($connId) is set to "Rready".

	#Log re-use nonpipeline, GRANT delayed write access to $token in NextP..
	set socketWrState($connId) peNding
    }
    return
}

# http::CancelReadPipeline
#
#	Cancel pipelined responses on a closing "Keep-Alive" socket.
#
#	- Called by a variable trace on "unset socketRdState($connId)".
1908
1909
1910
1911
1912
1913
1914

1915
1916
1917
1918
1919
1920
1921
	    set tk [namespace tail $token]
	    Log ^X$tk end of response "($msg)" - token $token
	    set ${token}(status) eof
	    Finish $token ;#$msg
	}
	set socketRdQueue($connId) {}
    }

}

# http::CancelWritePipeline
#
#	Cancel queued events on a closing "Keep-Alive" socket.
#
#	- Called by a variable trace on "unset socketWrState($connId)".







>







2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
	    set tk [namespace tail $token]
	    Log ^X$tk end of response "($msg)" - token $token
	    set ${token}(status) eof
	    Finish $token ;#$msg
	}
	set socketRdQueue($connId) {}
    }
    return
}

# http::CancelWritePipeline
#
#	Cancel queued events on a closing "Keep-Alive" socket.
#
#	- Called by a variable trace on "unset socketWrState($connId)".
1941
1942
1943
1944
1945
1946
1947

1948
1949
1950
1951
1952
1953
1954
	    set tk [namespace tail $token]
	    Log ^X$tk end of response "($msg)" - token $token
	    set ${token}(status) eof
	    Finish $token ;#$msg
	}
	set socketWrQueue($connId) {}
    }

}

# http::ReplayIfDead --
#
# - A query on a re-used persistent socket failed at the earliest opportunity,
#   because the socket had been closed by the server.  Keep the token, tidy up,
#   and try to connect on a fresh socket.







>







2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
	    set tk [namespace tail $token]
	    Log ^X$tk end of response "($msg)" - token $token
	    set ${token}(status) eof
	    Finish $token ;#$msg
	}
	set socketWrQueue($connId) {}
    }
    return
}

# http::ReplayIfDead --
#
# - A query on a re-used persistent socket failed at the earliest opportunity,
#   because the socket had been closed by the server.  Keep the token, tidy up,
#   and try to connect on a fresh socket.
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
#
# Arguments:
#	token	Connection token.
#
# Side Effects:
#	Use the same token, but try to open a new socket.

proc http::ReplayIfDead {tokenArg doing} {
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue

    variable socketClosing
    variable socketPlayCmd


    variable $tokenArg
    upvar 0 $tokenArg stateArg

    Log running http::ReplayIfDead for $tokenArg $doing

    # 1. Merge the tokens for transactions in flight, the read (response) queue,
    #    and the write (request) queue.

    set InFlightR {}
    set InFlightW {}

    # Obtain the tokens for transactions in flight.
    if {$stateArg(-pipeline)} {
	# Two transactions may be in flight.  The "read" transaction was first.
	# It is unlikely that the server would close the socket if a response
	# was pending; however, an earlier request (as well as the present
	# request) may have been sent and ignored if the socket was half-closed
	# by the server.

	if {    [info exists socketRdState($stateArg(socketinfo))]
	     && ($socketRdState($stateArg(socketinfo)) ne "Rready")
	} {
	    lappend InFlightR $socketRdState($stateArg(socketinfo))
	} elseif {($doing eq "read")} {
	    lappend InFlightR $tokenArg
	}

	if {    [info exists socketWrState($stateArg(socketinfo))]
	     && $socketWrState($stateArg(socketinfo)) ni {Wready peNding}
	} {
	    lappend InFlightW $socketWrState($stateArg(socketinfo))
	} elseif {($doing eq "write")} {
	    lappend InFlightW $tokenArg
	}

	# Report any inconsistency of $tokenArg with socket*state.
	if {    ($doing eq "read")
	     && [info exists socketRdState($stateArg(socketinfo))]
	     && ($tokenArg ne $socketRdState($stateArg(socketinfo)))
	} {
	    Log WARNING - ReplayIfDead pipelined tokenArg $tokenArg $doing \
		    ne socketRdState($stateArg(socketinfo)) \
		      $socketRdState($stateArg(socketinfo))

	} elseif {
		($doing eq "write")
	     && [info exists socketWrState($stateArg(socketinfo))]
	     && ($tokenArg ne $socketWrState($stateArg(socketinfo)))
	} {
	    Log WARNING - ReplayIfDead pipelined tokenArg $tokenArg $doing \
		    ne socketWrState($stateArg(socketinfo)) \
		      $socketWrState($stateArg(socketinfo))
	}
    } else {
	# One transaction should be in flight.
	# socketRdState, socketWrQueue are used.
	# socketRdQueue should be empty.

	# Report any inconsistency of $tokenArg with socket*state.
	if {$tokenArg ne $socketRdState($stateArg(socketinfo))} {
	    Log WARNING - ReplayIfDead nonpipeline tokenArg $tokenArg $doing \
		    ne socketRdState($stateArg(socketinfo)) \
		      $socketRdState($stateArg(socketinfo))
	}

	# Report the inconsistency that socketRdQueue is non-empty.
	if {    [info exists socketRdQueue($stateArg(socketinfo))]
	     && ($socketRdQueue($stateArg(socketinfo)) ne {})
	} {
	    Log WARNING - ReplayIfDead nonpipeline tokenArg $tokenArg $doing \
		    has read queue socketRdQueue($stateArg(socketinfo)) \
		    $socketRdQueue($stateArg(socketinfo)) ne {}
	}

	lappend InFlightW $socketRdState($stateArg(socketinfo))
	set socketRdQueue($stateArg(socketinfo)) {}
    }

    set newQueue {}
    lappend newQueue {*}$InFlightR
    lappend newQueue {*}$socketRdQueue($stateArg(socketinfo))
    lappend newQueue {*}$InFlightW
    lappend newQueue {*}$socketWrQueue($stateArg(socketinfo))


    # 2. Tidy up tokenArg.  This is a cut-down form of Finish/CloseSocket.
    #    Do not change state(status).
    #    No need to after cancel stateArg(after) - either this is done in
    #    ReplayCore/ReInit, or Finish is called.

    catch {close $stateArg(sock)}


    # 2a. Tidy the tokens in the queues - this is done in ReplayCore/ReInit.
    # - Transactions, if any, that are awaiting responses cannot be completed.
    #   They are listed for re-sending in newQueue.
    # - All tokens are preserved for re-use by ReplayCore, and their variables
    #   will be re-initialised by calls to ReInit.
    # - The relevant element of socketMapping, socketRdState, socketWrState,
    #   socketRdQueue, socketWrQueue, socketClosing, socketPlayCmd will be set
    #   to new values in ReplayCore.

    ReplayCore $newQueue

}

# http::ReplayIfClose --
#
#	A request on a socket that was previously "Connection: keep-alive" has
#	received a "Connection: close" response header.  The server supplies
#	that response correctly, but any later requests already queued on this







|





>


>

|
|

|








|






|
|

|

|


|
|

|

|


|

|
|

|
|
|



|
|

|
|
|






|
|
|
|
|



|
|

|
|
|


|
|




|

|


|

|


|
>











>







2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
#
# Arguments:
#	token	Connection token.
#
# Side Effects:
#	Use the same token, but try to open a new socket.

proc http::ReplayIfDead {token doing} {
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    variable $token
    upvar 0 $token state

    Log running http::ReplayIfDead for $token $doing

    # 1. Merge the tokens for transactions in flight, the read (response) queue,
    #    and the write (request) queue.

    set InFlightR {}
    set InFlightW {}

    # Obtain the tokens for transactions in flight.
    if {$state(-pipeline)} {
	# Two transactions may be in flight.  The "read" transaction was first.
	# It is unlikely that the server would close the socket if a response
	# was pending; however, an earlier request (as well as the present
	# request) may have been sent and ignored if the socket was half-closed
	# by the server.

	if {    [info exists socketRdState($state(socketinfo))]
	     && ($socketRdState($state(socketinfo)) ne "Rready")
	} {
	    lappend InFlightR $socketRdState($state(socketinfo))
	} elseif {($doing eq "read")} {
	    lappend InFlightR $token
	}

	if {    [info exists socketWrState($state(socketinfo))]
	     && $socketWrState($state(socketinfo)) ni {Wready peNding}
	} {
	    lappend InFlightW $socketWrState($state(socketinfo))
	} elseif {($doing eq "write")} {
	    lappend InFlightW $token
	}

	# Report any inconsistency of $token with socket*state.
	if {    ($doing eq "read")
	     && [info exists socketRdState($state(socketinfo))]
	     && ($token ne $socketRdState($state(socketinfo)))
	} {
	    Log WARNING - ReplayIfDead pipelined token $token $doing \
		    ne socketRdState($state(socketinfo)) \
		      $socketRdState($state(socketinfo))

	} elseif {
		($doing eq "write")
	     && [info exists socketWrState($state(socketinfo))]
	     && ($token ne $socketWrState($state(socketinfo)))
	} {
	    Log WARNING - ReplayIfDead pipelined token $token $doing \
		    ne socketWrState($state(socketinfo)) \
		      $socketWrState($state(socketinfo))
	}
    } else {
	# One transaction should be in flight.
	# socketRdState, socketWrQueue are used.
	# socketRdQueue should be empty.

	# Report any inconsistency of $token with socket*state.
	if {$token ne $socketRdState($state(socketinfo))} {
	    Log WARNING - ReplayIfDead nonpipeline token $token $doing \
		    ne socketRdState($state(socketinfo)) \
		      $socketRdState($state(socketinfo))
	}

	# Report the inconsistency that socketRdQueue is non-empty.
	if {    [info exists socketRdQueue($state(socketinfo))]
	     && ($socketRdQueue($state(socketinfo)) ne {})
	} {
	    Log WARNING - ReplayIfDead nonpipeline token $token $doing \
		    has read queue socketRdQueue($state(socketinfo)) \
		    $socketRdQueue($state(socketinfo)) ne {}
	}

	lappend InFlightW $socketRdState($state(socketinfo))
	set socketRdQueue($state(socketinfo)) {}
    }

    set newQueue {}
    lappend newQueue {*}$InFlightR
    lappend newQueue {*}$socketRdQueue($state(socketinfo))
    lappend newQueue {*}$InFlightW
    lappend newQueue {*}$socketWrQueue($state(socketinfo))


    # 2. Tidy up token.  This is a cut-down form of Finish/CloseSocket.
    #    Do not change state(status).
    #    No need to after cancel state(after) - either this is done in
    #    ReplayCore/ReInit, or Finish is called.

    catch {close $state(sock)}
    Unset $state(socketinfo)

    # 2a. Tidy the tokens in the queues - this is done in ReplayCore/ReInit.
    # - Transactions, if any, that are awaiting responses cannot be completed.
    #   They are listed for re-sending in newQueue.
    # - All tokens are preserved for re-use by ReplayCore, and their variables
    #   will be re-initialised by calls to ReInit.
    # - The relevant element of socketMapping, socketRdState, socketWrState,
    #   socketRdQueue, socketWrQueue, socketClosing, socketPlayCmd will be set
    #   to new values in ReplayCore.

    ReplayCore $newQueue
    return
}

# http::ReplayIfClose --
#
#	A request on a socket that was previously "Connection: keep-alive" has
#	received a "Connection: close" response header.  The server supplies
#	that response correctly, but any later requests already queued on this
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119

2120
2121
2122
2123
2124
2125
2126
    }

    # 1. Create newQueue
    set InFlightW {}
    if {$Wstate ni {Wready peNding}} {
	lappend InFlightW $Wstate
    }

    set newQueue {}
    lappend newQueue {*}$Rqueue
    lappend newQueue {*}$InFlightW
    lappend newQueue {*}$Wqueue

    # 2. Cleanup - none needed, done by the caller.

    ReplayCore $newQueue

}

# http::ReInit --
#
#	Command to restore a token's state to a condition that
#	makes it ready to replay a request.
#







|








>







2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
    }

    # 1. Create newQueue
    set InFlightW {}
    if {$Wstate ni {Wready peNding}} {
	lappend InFlightW $Wstate
    }
    ##Log $Rqueue -- $InFlightW -- $Wqueue
    set newQueue {}
    lappend newQueue {*}$Rqueue
    lappend newQueue {*}$InFlightW
    lappend newQueue {*}$Wqueue

    # 2. Cleanup - none needed, done by the caller.

    ReplayCore $newQueue
    return
}

# http::ReInit --
#
#	Command to restore a token's state to a condition that
#	makes it ready to replay a request.
#
2156
2157
2158
2159
2160
2161
2162





2163
2164
2165
2166
2167
2168
2169
	return 0
    }

    if {[info exists state(after)]} {
	after cancel $state(after)
	unset state(after)
    }






    # Don't alter state(status) - this would trigger http::wait if it is in use.
    set tmpState    $state(tmpState)
    set tmpOpenCmd  $state(tmpOpenCmd)
    set tmpConnArgs $state(tmpConnArgs)
    foreach name [array names state] {
	if {$name ne "status"} {







>
>
>
>
>







2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
	return 0
    }

    if {[info exists state(after)]} {
	after cancel $state(after)
	unset state(after)
    }
    if {[info exists state(socketcoro)]} {
        Log $token Cancel socket after-idle event (ReInit)
        after cancel $state(socketcoro)
        unset state(socketcoro)
    }

    # Don't alter state(status) - this would trigger http::wait if it is in use.
    set tmpState    $state(tmpState)
    set tmpOpenCmd  $state(tmpOpenCmd)
    set tmpConnArgs $state(tmpConnArgs)
    foreach name [array names state] {
	if {$name ne "status"} {
2195
2196
2197
2198
2199
2200
2201


2202
2203
2204
2205
2206

2207
2208

2209
2210
2211
2212
2213
2214
2215
# Arguments:
#	newQueue	List of connection tokens.
#
# Side Effects:
#	Use existing tokens, but try to open a new socket.

proc http::ReplayCore {newQueue} {


    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue

    variable socketClosing
    variable socketPlayCmd


    if {[llength $newQueue] == 0} {
	# Nothing to do.
	return
    }

    ##Log running ReplayCore for {*}$newQueue







>
>





>


>







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
# Arguments:
#	newQueue	List of connection tokens.
#
# Side Effects:
#	Use existing tokens, but try to open a new socket.

proc http::ReplayCore {newQueue} {
    variable TmpSockCounter

    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    if {[llength $newQueue] == 0} {
	# Nothing to do.
	return
    }

    ##Log running ReplayCore for {*}$newQueue
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305

2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352





2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365






















2366
2367
2368













2369
























































































2370
2371
2372
2373
2374
2375
2376
2377








2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394



2395
2396
2397
2398
2399





2400
2401
2402

2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419


2420
2421
2422
2423

2424








2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464

2465
2466

2467
2468
2469
2470
2471
2472
2473
    set tmpOpenCmd  $state(tmpOpenCmd)
    set tmpConnArgs $state(tmpConnArgs)
    unset state(tmpState)
    unset state(tmpOpenCmd)
    unset state(tmpConnArgs)

    set state(reusing) 0

    if {$state(-timeout) > 0} {
	set resetCmd [list http::reset $token timeout]
	set state(after) [after $state(-timeout) $resetCmd]
    }

    set pre [clock milliseconds]
    ##Log pre socket opened, - token $token
    ##Log $tmpOpenCmd - token $token
    # 4. Open a socket.
    if {[catch {eval $tmpOpenCmd} sock]} {
	# Something went wrong while trying to establish the connection.
	Log FAILED - $sock
	set state(sock) NONE
	Finish $token $sock
	return
    }
    ##Log post socket opened, - token $token
    set delay [expr {[clock milliseconds] - $pre}]
    if {$delay > 3000} {
	Log socket delay $delay - token $token
    }
    # Command [socket] is called with -async, but takes 5s to 5.1s to return,
    # with probability of order 1 in 10,000.  This may be a bizarre scheduling
    # issue with my (KJN's) system (Fedora Linux).
    # This does not cause a problem (unless the request times out when this
    # command returns).

    # 5. Configure the persistent socket data.
    if {$state(-keepalive)} {
	set socketMapping($state(socketinfo)) $sock

	if {![info exists socketRdState($state(socketinfo))]} {
	    set socketRdState($state(socketinfo)) {}
	    set varName ::http::socketRdState($state(socketinfo))
	    trace add variable $varName unset ::http::CancelReadPipeline
	}

	if {![info exists socketWrState($state(socketinfo))]} {
	    set socketWrState($state(socketinfo)) {}
	    set varName ::http::socketWrState($state(socketinfo))
	    trace add variable $varName unset ::http::CancelWritePipeline
	}

	if {$state(-pipeline)} {
	    #Log new, init for pipelined, GRANT write acc to $token ReplayCore
	    set socketRdState($state(socketinfo)) $token
	    set socketWrState($state(socketinfo)) $token
	} else {
	    #Log new, init for nonpipeline, GRANT r/w acc to $token ReplayCore
	    set socketRdState($state(socketinfo)) $token
	    set socketWrState($state(socketinfo)) $token
	}

	set socketRdQueue($state(socketinfo)) {}
	set socketWrQueue($state(socketinfo)) $newQueue
	set socketClosing($state(socketinfo)) 0
	set socketPlayCmd($state(socketinfo)) {ReplayIfClose Wready {} {}}
    }

    ##Log pre newQueue ReInit, - token $token
    # 6. Configure sockets in the queue.
    foreach tok $newQueue {
	if {[ReInit $tok]} {
	    set ${tok}(reusing) 1
	    set ${tok}(sock) $sock

	} else {
	    set ${tok}(reusing) 1
	    set ${tok}(sock) NONE
	    Finish $token {cannot send this request again}
	}
    }

    # 7. Configure the socket for newToken to send a request.
    set state(sock) $sock
    Log "Using $sock for $state(socketinfo) - token $token" \
	[expr {$state(-keepalive)?"keepalive":""}]

    # Initialisation of a new socket.
    ##Log socket opened, now fconfigure - token $token
    fconfigure $sock -translation {auto crlf} -buffersize $state(-blocksize)
    ##Log socket opened, DONE fconfigure - token $token

    # Connect does its own fconfigure.
    fileevent $sock writable [list http::Connect $token {*}$tmpConnArgs]
    #Log ---- $sock << conn to $token for HTTP request (e)
}

# Data access functions:
# Data - the URL data
# Status - the transaction status: ok, reset, eof, timeout, error
# Code - the HTTP transaction code, e.g., 200
# Size - the size of the URL data

proc http::data {token} {
    variable $token
    upvar 0 $token state
    return $state(body)
}
proc http::status {token} {
    if {![info exists $token]} {
	return "error"
    }
    variable $token
    upvar 0 $token state
    return $state(status)
}
proc http::code {token} {
    variable $token
    upvar 0 $token state
    return $state(http)
}
proc http::ncode {token} {





    variable $token
    upvar 0 $token state
    if {[regexp {[0-9]{3}} $state(http) numeric_code]} {
	return $numeric_code
    } else {
	return $state(http)
    }
}
proc http::size {token} {
    variable $token
    upvar 0 $token state
    return $state(currentsize)
}






















proc http::meta {token} {
    variable $token
    upvar 0 $token state













    return $state(meta)
























































































}
proc http::error {token} {
    variable $token
    upvar 0 $token state
    if {[info exists state(error)]} {
	return $state(error)
    }
    return ""








}

# http::cleanup
#
#	Garbage collect the state associated with a transaction
#
# Arguments
#	token	The token returned from http::geturl
#
# Side Effects
#	unsets the state array

proc http::cleanup {token} {
    variable $token
    upvar 0 $token state
    if {[info commands ${token}EventCoroutine] ne {}} {
	rename ${token}EventCoroutine {}



    }
    if {[info exists state(after)]} {
	after cancel $state(after)
	unset state(after)
    }





    if {[info exists state]} {
	unset state
    }

}

# http::Connect
#
#	This callback is made when an asyncronous connection completes.
#
# Arguments
#	token	The token returned from http::geturl
#
# Side Effects
#	Sets the status of the connection, which unblocks
# 	the waiting geturl call

proc http::Connect {token proto phost srvurl} {
    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]


    set err "due to unexpected EOF"
    if {
	[eof $state(sock)] ||
	[set err [fconfigure $state(sock) -error]] ne ""

    } {








	Log "WARNING - if testing, pay special attention to this\
		case (GJ) which is seldom executed - token $token"
	if {[info exists state(reusing)] && $state(reusing)} {
	    # The socket was closed at the server end, and closed at
	    # this end by http::CheckEof.
	    if {[TestForReplay $token write $err b]} {
		return
	    }

	    # else:
	    # This is NOT a persistent socket that has been closed since its
	    # last use.
	    # If any other requests are in flight or pipelined/queued, they will
	    # be discarded.
	}
	Finish $token "connect failed $err"
    } else {
	set state(state) connecting
	fileevent $state(sock) writable {}
	::http::Connected $token $proto $phost $srvurl
    }
}

# http::Write
#
#	Write POST query data to the socket
#
# Arguments
#	token	The token for the connection
#
# Side Effects
#	Write the socket and handle callbacks.

proc http::Write {token} {
    variable http
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue

    variable socketClosing
    variable socketPlayCmd


    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]
    set sock $state(sock)

    # Output a block.  Tcl will buffer this if the socket blocks







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




>



|



<
<
|
<

<
<
<
<
|
<
<
<








|












|




|
>
>
>
>
>













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


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







|
>
>
>
>
>
>
>
>















|
|
>
>
>





>
>
>
>
>



>

















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
















|
<
<
<
<



















>


>







2925
2926
2927
2928
2929
2930
2931
2932


2933
2934
2935



2936



2937


2938










2939



2940






























2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952


2953

2954




2955



2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197


3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225




3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
    set tmpOpenCmd  $state(tmpOpenCmd)
    set tmpConnArgs $state(tmpConnArgs)
    unset state(tmpState)
    unset state(tmpOpenCmd)
    unset state(tmpConnArgs)

    set state(reusing) 0
    set state(ReusingPlaceholder) 0


    set state(alreadyQueued) 0

    # Give the socket a placeholder name before it is created.



    set sock HTTP_PLACEHOLDER_[incr TmpSockCounter]



    set state(sock) $sock













    # Move the $newQueue into the placeholder socket's socketPhQueue.



    set socketPhQueue($sock) {}






























    foreach tok $newQueue {
	if {[ReInit $tok]} {
	    set ${tok}(reusing) 1
	    set ${tok}(sock) $sock
	    lappend socketPhQueue($sock) $tok
	} else {
	    set ${tok}(reusing) 1
	    set ${tok}(sock) NONE
	    Finish $tok {cannot send this request again}
	}
    }



    AsyncTransaction $token






    return



}

# Data access functions:
# Data - the URL data
# Status - the transaction status: ok, reset, eof, timeout, error
# Code - the HTTP transaction code, e.g., 200
# Size - the size of the URL data

proc http::responseBody {token} {
    variable $token
    upvar 0 $token state
    return $state(body)
}
proc http::status {token} {
    if {![info exists $token]} {
	return "error"
    }
    variable $token
    upvar 0 $token state
    return $state(status)
}
proc http::responseLine {token} {
    variable $token
    upvar 0 $token state
    return $state(http)
}
proc http::requestLine {token} {
    variable $token
    upvar 0 $token state
    return $state(requestLine)
}
proc http::responseCode {token} {
    variable $token
    upvar 0 $token state
    if {[regexp {[0-9]{3}} $state(http) numeric_code]} {
	return $numeric_code
    } else {
	return $state(http)
    }
}
proc http::size {token} {
    variable $token
    upvar 0 $token state
    return $state(currentsize)
}
proc http::requestHeaders {token args} {
    set lenny  [llength $args]
    if {$lenny > 1} {
        return -code error {usage: ::http::requestHeaders token ?headerName?}
    } else {
        return [Meta $token request {*}$args]
    }
}
proc http::responseHeaders {token args} {
    set lenny  [llength $args]
    if {$lenny > 1} {
        return -code error {usage: ::http::responseHeaders token ?headerName?}
    } else {
        return [Meta $token response {*}$args]
    }
}
proc http::requestHeaderValue {token header} {
    Meta $token request $header VALUE
}
proc http::responseHeaderValue {token header} {
    Meta $token response $header VALUE
}
proc http::Meta {token who args} {
    variable $token
    upvar 0 $token state

    if {$who eq {request}} {
        set whom requestHeaders
    } elseif {$who eq {response}} {
        set whom meta
    } else {
        return -code error {usage: ::http::Meta token request|response ?headerName ?VALUE??}
    }

    set header [string tolower [lindex $args 0]]
    set how    [string tolower [lindex $args 1]]
    set lenny  [llength $args]
    if {$lenny == 0} {
        return $state($whom)
    } elseif {($lenny > 2) || (($lenny == 2) && ($how ne {value}))} {
        return -code error {usage: ::http::Meta token request|response ?headerName ?VALUE??}
    } else {
        set result {}
        set combined {}
        foreach {key value} $state($whom) {
            if {$key eq $header} {
                lappend result $key $value
                append combined $value {, }
            }
        }
        if {$lenny == 1} {
            return $result
        } else {
            return [string range $combined 0 end-2]
        }
    }
}


# ------------------------------------------------------------------------------
#  Proc http::responseInfo
# ------------------------------------------------------------------------------
# Command to return a dictionary of the most useful metadata of a HTTP
# response.
#
# Arguments:
# token       - connection token (name of an array)
#
# Return Value: a dict. See man page http(n) for a description of each item.
# ------------------------------------------------------------------------------

proc http::responseInfo {token} {
    variable $token
    upvar 0 $token state
    set result {}
    foreach {key origin name} {
        stage                 STATE  state
        status                STATE  status
        responseCode          STATE  responseCode
        reasonPhrase          STATE  reasonPhrase
        contentType           STATE  type
        binary                STATE  binary
        redirection           RESP   location
        upgrade               STATE  upgrade
        error                 ERROR  -
        postError             STATE  posterror
        method                STATE  method
        charset               STATE  charset
        compression           STATE  coding
        httpRequest           STATE  -protocol
        httpResponse          STATE  httpResponse
        url                   STATE  url
        connectionRequest     REQ    connection
        connectionResponse    RESP   connection
        connectionActual      STATE  connection
        transferEncoding      STATE  transfer
        totalPost             STATE  querylength
        currentPost           STATE  queryoffset
        totalSize             STATE  totalsize
        currentSize           STATE  currentsize
    } {
        if {$origin eq {STATE}} {
            if {[info exists state($name)]} {
                dict set result $key $state($name)
            } else {
                # Should never come here
                dict set result $key {}
            }
        } elseif {$origin eq {REQ}} {
            dict set result $key [requestHeaderValue $token $name]
        } elseif {$origin eq {RESP}} {
            dict set result $key [responseHeaderValue $token $name]
        } elseif {$origin eq {ERROR}} {
            # Don't flood the dict with data.  The command ::http::error is
            # available.
            if {[info exists state(error)]} {
                set msg [lindex $state(error) 0]
            } else {
                set msg {}
            }
            dict set result $key $msg
        } else {
            # Should never come here
            dict set result $key {}
        }
    }
    return $result
}
proc http::error {token} {
    variable $token
    upvar 0 $token state
    if {[info exists state(error)]} {
	return $state(error)
    }
    return
}
proc http::postError {token} {
    variable $token
    upvar 0 $token state
    if {[info exists state(postErrorFull)]} {
	return $state(postErrorFull)
    }
    return
}

# http::cleanup
#
#	Garbage collect the state associated with a transaction
#
# Arguments
#	token	The token returned from http::geturl
#
# Side Effects
#	unsets the state array

proc http::cleanup {token} {
    variable $token
    upvar 0 $token state
    if {[info commands ${token}--EventCoroutine] ne {}} {
	rename ${token}--EventCoroutine {}
    }
    if {[info commands ${token}--SocketCoroutine] ne {}} {
	rename ${token}--SocketCoroutine {}
    }
    if {[info exists state(after)]} {
	after cancel $state(after)
	unset state(after)
    }
    if {[info exists state(socketcoro)]} {
        Log $token Cancel socket after-idle event (cleanup)
        after cancel $state(socketcoro)
        unset state(socketcoro)
    }
    if {[info exists state]} {
	unset state
    }
    return
}

# http::Connect
#
#	This callback is made when an asyncronous connection completes.
#
# Arguments
#	token	The token returned from http::geturl
#
# Side Effects
#	Sets the status of the connection, which unblocks
# 	the waiting geturl call

proc http::Connect {token proto phost srvurl} {
    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]

    if {[catch {eof $state(sock)} tmp] || $tmp} {
        set err "due to unexpected EOF"


    } elseif {[set err [fconfigure $state(sock) -error]] ne ""} {
        # set err is done in test
    } else {
        # All OK
	set state(state) connecting
	fileevent $state(sock) writable {}
	::http::Connected $token $proto $phost $srvurl
	return
    }

    # Error cases.
	Log "WARNING - if testing, pay special attention to this\
		case (GJ) which is seldom executed - token $token"
	if {[info exists state(reusing)] && $state(reusing)} {
	    # The socket was closed at the server end, and closed at
	    # this end by http::CheckEof.
	    if {[TestForReplay $token write $err b]} {
		return
	    }

	    # else:
	    # This is NOT a persistent socket that has been closed since its
	    # last use.
	    # If any other requests are in flight or pipelined/queued, they will
	    # be discarded.
	}
	Finish $token "connect failed $err"
    return




}

# http::Write
#
#	Write POST query data to the socket
#
# Arguments
#	token	The token for the connection
#
# Side Effects
#	Write the socket and handle callbacks.

proc http::Write {token} {
    variable http
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]
    set sock $state(sock)

    # Output a block.  Tcl will buffer this if the socket blocks
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531



2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549

2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571

2572
2573

2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587

2588

2589
2590
2591
2592

2593
2594
2595
2596
2597
2598
2599
2600
2601
2602

2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638

2639
2640
2641
2642
2643
2644
2645

2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663














2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678

2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696















2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725


2726
2727
2728
2729
2730
2731
2732

2733

2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748

2749
2750
2751
2752
2753
2754
2755
2756

2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786

2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797

2798
2799
2800
2801

2802
2803
2804
2805
2806
2807
2808

2809

2810
2811
2812
2813
2814
2815

2816
2817
2818
2819
2820
2821
2822
	    }
	    puts -nonewline $sock $outStr
	    incr state(queryoffset) [string length $outStr]
	    if {[eof $state(-querychannel)]} {
		set done 1
	    }
	}
    } err]} {
	# Do not call Finish here, but instead let the read half of the socket
	# process whatever server reply there is to get.

	set state(posterror) $err



	set done 1
    }

    if {$done} {
	catch {flush $sock}
	fileevent $sock writable {}
	Log ^C$tk end sending request - token $token
	# End of writing (POST method).  The request has been sent.

	DoneRequest $token
    }

    # Callback to the client after we've completely handled everything.

    if {[string length $state(-queryprogress)]} {
	eval $state(-queryprogress) \
	    [list $token $state(querylength) $state(queryoffset)]
    }

}

# http::Event
#
#	Handle input on the socket. This command is the core of
#	the coroutine commands ${token}EventCoroutine that are
#	bound to "fileevent $sock readable" and process input.
#
# Arguments
#	sock	The socket receiving input.
#	token	The token returned from http::geturl
#
# Side Effects
#	Read the socket and handle callbacks.

proc http::Event {sock token} {
    variable http
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue

    variable socketClosing
    variable socketPlayCmd


    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]
    while 1 {
	yield
	##Log Event call - token $token

	if {![info exists state]} {
	    Log "Event $sock with invalid token '$token' - remote close?"
	    if {![eof $sock]} {
		if {[set d [read $sock]] ne ""} {
		    Log "WARNING: additional data left on closed socket\
			    - token $token"

		}

	    }
	    Log ^X$tk end of response (token error) - token $token
	    CloseSocket $sock
	    return

	}
	if {$state(state) eq "connecting"} {
	    ##Log - connecting - token $token
	    if {    $state(reusing)
		 && $state(-pipeline)
		 && ($state(-timeout) > 0)
		 && (![info exists state(after)])
	    } {
		set state(after) [after $state(-timeout) \
			[list http::reset $token timeout]]

	    }

	    if {[catch {gets $sock state(http)} nsl]} {
		Log "WARNING - if testing, pay special attention to this\
			case (GK) which is seldom executed - token $token"
		if {[info exists state(reusing)] && $state(reusing)} {
		    # The socket was closed at the server end, and closed at
		    # this end by http::CheckEof.

		    if {[TestForReplay $token read $nsl c]} {
			return
		    }

		    # else:
		    # This is NOT a persistent socket that has been closed since
		    # its last use.
		    # If any other requests are in flight or pipelined/queued,
		    # they will be discarded.
		} else {
		    Log ^X$tk end of response (error) - token $token
		    Finish $token $nsl
		    return
		}
	    } elseif {$nsl >= 0} {
		##Log - connecting 1 - token $token
		set state(state) "header"
	    } elseif {    [eof $sock]
		       && [info exists state(reusing)]
		       && $state(reusing)
	    } {
		# The socket was closed at the server end, and we didn't notice.
		# This is the first read - where the closure is usually first
		# detected.

		if {[TestForReplay $token read {} d]} {
		    return

		}

		# else:
		# This is NOT a persistent socket that has been closed since its
		# last use.
		# If any other requests are in flight or pipelined/queued, they
		# will be discarded.

	    }
	} elseif {$state(state) eq "header"} {
	    if {[catch {gets $sock line} nhl]} {
		##Log header failed - token $token
		Log ^X$tk end of response (error) - token $token
		Finish $token $nhl
		return
	    } elseif {$nhl == 0} {
		##Log header done - token $token
		Log ^E$tk end of response headers - token $token
		# We have now read all headers
		# We ignore HTTP/1.1 100 Continue returns. RFC2616 sec 8.2.3
		if {    ($state(http) == "")
		     || ([regexp {^\S+\s(\d+)} $state(http) {} x] && $x == 100)
		} {
		    set state(state) "connecting"
		    continue
		    # This was a "return" in the pre-coroutine code.














		}

		if {    ([info exists state(connection)])
		     && ([info exists socketMapping($state(socketinfo))])
		     && ("keep-alive" in $state(connection))
		     && ($state(-keepalive))
		     && (!$state(reusing))
		     && ($state(-pipeline))
		} {
		    # Response headers received for first request on a
		    # persistent socket.  Now ready for pipelined writes (if
		    # any).
		    # Previous value is $token. It cannot be "pending".
		    set socketWrState($state(socketinfo)) Wready
		    http::NextPipelinedWrite $token

		}

		# Once a "close" has been signaled, the client MUST NOT send any
		# more requests on that connection.
		#
		# If either the client or the server sends the "close" token in
		# the Connection header, that request becomes the last one for
		# the connection.

		if {    ([info exists state(connection)])
		     && ([info exists socketMapping($state(socketinfo))])
		     && ("close" in $state(connection))
		     && ($state(-keepalive))
		} {
		    # The server warns that it will close the socket after this
		    # response.
		    ##Log WARNING - socket will close after response for $token
		    # Prepare data for a call to ReplayIfClose.















		    if {    ($socketRdQueue($state(socketinfo)) ne {})
			 || ($socketWrQueue($state(socketinfo)) ne {})
			 || ($socketWrState($state(socketinfo)) ni
						[list Wready peNding $token])
		    } {
			set InFlightW $socketWrState($state(socketinfo))
			if {$InFlightW in [list Wready peNding $token]} {
			    set InFlightW Wready
			} else {
			    set msg "token ${InFlightW} is InFlightW"
			    ##Log $msg - token $token
			}

			set socketPlayCmd($state(socketinfo)) \
				[list ReplayIfClose $InFlightW \
				$socketRdQueue($state(socketinfo)) \
				$socketWrQueue($state(socketinfo))]

			# - All tokens are preserved for re-use by ReplayCore.
			# - Queues are preserved in case of Finish with error,
			#   but are not used for anything else because
			#   socketClosing(*) is set below.
			# - Cancel the state(after) timeout events.
			foreach tokenVal $socketRdQueue($state(socketinfo)) {
			    if {[info exists ${tokenVal}(after)]} {
				after cancel [set ${tokenVal}(after)]
				unset ${tokenVal}(after)
			    }
			}



		    } else {
			set socketPlayCmd($state(socketinfo)) \
				{ReplayIfClose Wready {} {}}
		    }

		    # Do not allow further connections on this socket.

		    set socketClosing($state(socketinfo)) 1

		}

		set state(state) body

		# According to
		# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection
		# any comma-separated "Connection:" list implies keep-alive, but I
		# don't see this in the RFC so we'll play safe and
		# scan any list for "close".
		# Done here to support combining duplicate header field's values.
		if {   [info exists state(connection)]
		    && ("close" ni $state(connection))
		    && ("keep-alive" ni $state(connection))
		} {
		    lappend state(connection) "keep-alive"

		}

		# If doing a HEAD, then we won't get any body
		if {$state(-validate)} {
		    Log ^F$tk end of response for HEAD request - token $token
		    set state(state) complete
		    Eot $token
		    return

		}

		# - For non-chunked transfer we may have no body - in this case
		#   we may get no further file event if the connection doesn't
		#   close and no more data is sent. We can tell and must finish
		#   up now - not later - the alternative would be to wait until
		#   the server times out.
		# - In this case, the server has NOT told the client it will
		#   close the connection, AND it has NOT indicated the resource
		#   length EITHER by setting the Content-Length (totalsize) OR
		#   by using chunked Transfer-Encoding.
		# - Do not worry here about the case (Connection: close) because
		#   the server should close the connection.
		# - IF (NOT Connection: close) AND (NOT chunked encoding) AND
		#      (totalsize == 0).

		if {    (!(    [info exists state(connection)]
			    && ("close" in $state(connection))
			  )
			)
		     && (![info exists state(transfer)])
		     && ($state(totalsize) == 0)
		} {
		    set msg {body size is 0 and no events likely - complete}
		    Log "$msg - token $token"
		    set msg {(length unknown, set to 0)}
		    Log ^F$tk end of response body {*}$msg - token $token
		    set state(state) complete
		    Eot $token
		    return

		}

		# We have to use binary translation to count bytes properly.
		lassign [fconfigure $sock -translation] trRead trWrite
		fconfigure $sock -translation [list binary $trWrite]

		if {
		    $state(-binary) || [IsBinaryContentType $state(type)]
		} {
		    # Turn off conversions for non-text data.
		    set state(binary) 1

		}
		if {[info exists state(-channel)]} {
		    if {$state(binary) || [llength [ContentEncoding $token]]} {
			fconfigure $state(-channel) -translation binary

		    }
		    if {![info exists state(-handler)]} {
			# Initiate a sequence of background fcopies.
			fileevent $sock readable {}
			rename ${token}EventCoroutine {}
			CopyStart $sock $token
			return

		    }

		}
	    } elseif {$nhl > 0} {
		# Process header lines.
		##Log header - token $token - $line
		if {[regexp -nocase {^([^:]+):(.+)$} $line x key value]} {
		    switch -- [string tolower $key] {

			content-type {
			    set state(type) [string trim [string tolower $value]]
			    # Grab the optional charset information.
			    if {[regexp -nocase \
				    {charset\s*=\s*\"((?:[^""]|\\\")*)\"} \
				    $state(type) -> cs]} {
				set state(charset) [string map {{\"} \"} $cs]







|


<

>
>
>















|


>





|
















>


>










|



>

>




>










>











|
|













|









>







>


















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















>


















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












<














|
|
>
>
|





|
>

>















>








>




















|









>











>




>




|


>

>





|
>







3302
3303
3304
3305
3306
3307
3308
3309
3310
3311

3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531

3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
	    }
	    puts -nonewline $sock $outStr
	    incr state(queryoffset) [string length $outStr]
	    if {[eof $state(-querychannel)]} {
		set done 1
	    }
	}
    } err opts]} {
	# Do not call Finish here, but instead let the read half of the socket
	# process whatever server reply there is to get.

	set state(posterror) $err
	set info [dict get $opts -errorinfo]
	set code [dict get $opts -code]
	set state(postErrorFull) [list $err $info $code]
	set done 1
    }

    if {$done} {
	catch {flush $sock}
	fileevent $sock writable {}
	Log ^C$tk end sending request - token $token
	# End of writing (POST method).  The request has been sent.

	DoneRequest $token
    }

    # Callback to the client after we've completely handled everything.

    if {[string length $state(-queryprogress)]} {
	namespace eval :: $state(-queryprogress) \
	    [list $token $state(querylength) $state(queryoffset)]
    }
    return
}

# http::Event
#
#	Handle input on the socket. This command is the core of
#	the coroutine commands ${token}--EventCoroutine that are
#	bound to "fileevent $sock readable" and process input.
#
# Arguments
#	sock	The socket receiving input.
#	token	The token returned from http::geturl
#
# Side Effects
#	Read the socket and handle callbacks.

proc http::Event {sock token} {
    variable http
    variable socketMapping
    variable socketRdState
    variable socketWrState
    variable socketRdQueue
    variable socketWrQueue
    variable socketPhQueue
    variable socketClosing
    variable socketPlayCmd
    variable socketCoEvent

    variable $token
    upvar 0 $token state
    set tk [namespace tail $token]
    while 1 {
	yield
	##Log Event call - token $token

	if {![info exists state]} {
	    Log "Event $sock with invalid token '$token' - remote close?"
	    if {!([catch {eof $sock} tmp] || $tmp)} {
		if {[set d [read $sock]] ne ""} {
		    Log "WARNING: additional data left on closed socket\
			    - token $token"
		} else {
		}
	    } else {
	    }
	    Log ^X$tk end of response (token error) - token $token
	    CloseSocket $sock
	    return
	} else {
	}
	if {$state(state) eq "connecting"} {
	    ##Log - connecting - token $token
	    if {    $state(reusing)
		 && $state(-pipeline)
		 && ($state(-timeout) > 0)
		 && (![info exists state(after)])
	    } {
		set state(after) [after $state(-timeout) \
			[list http::reset $token timeout]]
	    } else {
	    }

	    if {[catch {gets $sock state(http)} nsl]} {
		Log "WARNING - if testing, pay special attention to this\
			case (GK) which is seldom executed - token $token"
		if {[info exists state(reusing)] && $state(reusing)} {
		    # The socket was closed at the server end, and closed at
		    # this end by http::CheckEof.

		    if {[TestForReplay $token read $nsl c]} {
			return
		    } else {
		    }
		    # else:
		    # This is NOT a persistent socket that has been closed since
		    # its last use.
		    # If any other requests are in flight or pipelined/queued,
		    # they will be discarded.
		} else {
		    Log ^X$tk end of response (error) - token $token
		    Finish $token $nsl
		    return
		}
	    } elseif {$nsl >= 0} {
		##Log - connecting 1 - token $token
		set state(state) "header"
	    } elseif {    ([catch {eof $sock} tmp] || $tmp)
		       && [info exists state(reusing)]
		       && $state(reusing)
	    } {
		# The socket was closed at the server end, and we didn't notice.
		# This is the first read - where the closure is usually first
		# detected.

		if {[TestForReplay $token read {} d]} {
		    return
		} else {
		}

		# else:
		# This is NOT a persistent socket that has been closed since its
		# last use.
		# If any other requests are in flight or pipelined/queued, they
		# will be discarded.
	    } else {
	    }
	} elseif {$state(state) eq "header"} {
	    if {[catch {gets $sock line} nhl]} {
		##Log header failed - token $token
		Log ^X$tk end of response (error) - token $token
		Finish $token $nhl
		return
	    } elseif {$nhl == 0} {
		##Log header done - token $token
		Log ^E$tk end of response headers - token $token
		# We have now read all headers
		# We ignore HTTP/1.1 100 Continue returns. RFC2616 sec 8.2.3
		if {    ($state(http) == "")
		     || ([regexp {^\S+\s(\d+)} $state(http) {} x] && $x == 100)
		} {
		    set state(state) "connecting"
		    continue
		    # This was a "return" in the pre-coroutine code.
		} else {
		}

		# We have $state(http) so let's split it into its components.
		if {[regexp {^HTTP/(\S+) ([0-9]{3}) (.*)$} $state(http) \
			-> httpResponse responseCode reasonPhrase]
		} {
		    set state(httpResponse) $httpResponse
		    set state(responseCode) $responseCode
		    set state(reasonPhrase) $reasonPhrase
		} else {
		    set state(httpResponse) $state(http)
		    set state(responseCode) $state(http)
		    set state(reasonPhrase) $state(http)
		}

		if {    ([info exists state(connection)])
		     && ([info exists socketMapping($state(socketinfo))])
		     && ("keep-alive" in $state(connection))
		     && ($state(-keepalive))
		     && (!$state(reusing))
		     && ($state(-pipeline))
		} {
		    # Response headers received for first request on a
		    # persistent socket.  Now ready for pipelined writes (if
		    # any).
		    # Previous value is $token. It cannot be "pending".
		    set socketWrState($state(socketinfo)) Wready
		    http::NextPipelinedWrite $token
		} else {
		}

		# Once a "close" has been signaled, the client MUST NOT send any
		# more requests on that connection.
		#
		# If either the client or the server sends the "close" token in
		# the Connection header, that request becomes the last one for
		# the connection.

		if {    ([info exists state(connection)])
		     && ([info exists socketMapping($state(socketinfo))])
		     && ("close" in $state(connection))
		     && ($state(-keepalive))
		} {
		    # The server warns that it will close the socket after this
		    # response.
		    ##Log WARNING - socket will close after response for $token
		    # Prepare data for a call to ReplayIfClose.
		    Log $token socket will close after this transaction
		    # 1. Cancel socket-assignment coro events that have not yet
		    # launched, and add the tokens to the write queue.
		    if {[info exists socketCoEvent($state(socketinfo))]} {
			foreach {tok can} $socketCoEvent($state(socketinfo)) {
			    lappend socketWrQueue($state(socketinfo)) $tok
			    unset -nocomplain ${tok}(socketcoro)
			    after cancel $can
			    Log $tok Cancel socket after-idle event (Event)
			    Log Move $tok from socketCoEvent to socketWrQueue and cancel its after idle coro
			}
			set socketCoEvent($state(socketinfo)) {}
		    } else {
		    }

		    if {    ($socketRdQueue($state(socketinfo)) ne {})
			 || ($socketWrQueue($state(socketinfo)) ne {})
			 || ($socketWrState($state(socketinfo)) ni
						[list Wready peNding $token])
		    } {
			set InFlightW $socketWrState($state(socketinfo))
			if {$InFlightW in [list Wready peNding $token]} {
			    set InFlightW Wready
			} else {
			    set msg "token ${InFlightW} is InFlightW"
			    ##Log $msg - token $token
			}

			set socketPlayCmd($state(socketinfo)) \
				[list ReplayIfClose $InFlightW \
				$socketRdQueue($state(socketinfo)) \
				$socketWrQueue($state(socketinfo))]

			# - All tokens are preserved for re-use by ReplayCore.
			# - Queues are preserved in case of Finish with error,
			#   but are not used for anything else because
			#   socketClosing(*) is set below.
			# - Cancel the state(after) timeout events.
			foreach tokenVal $socketRdQueue($state(socketinfo)) {
			    if {[info exists ${tokenVal}(after)]} {
				after cancel [set ${tokenVal}(after)]
				unset ${tokenVal}(after)
			    } else {
			    }
			    # Tokens in the read queue have no (socketcoro) to
			    # cancel.
			}
		    } else {
			set socketPlayCmd($state(socketinfo)) \
				{ReplayIfClose Wready {} {}}
		    }

		    # Do not allow further connections on this socket (but
		    # geturl can add new requests to the replay).
		    set socketClosing($state(socketinfo)) 1
		} else {
		}

		set state(state) body

		# According to
		# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection
		# any comma-separated "Connection:" list implies keep-alive, but I
		# don't see this in the RFC so we'll play safe and
		# scan any list for "close".
		# Done here to support combining duplicate header field's values.
		if {   [info exists state(connection)]
		    && ("close" ni $state(connection))
		    && ("keep-alive" ni $state(connection))
		} {
		    lappend state(connection) "keep-alive"
		} else {
		}

		# If doing a HEAD, then we won't get any body
		if {$state(-validate)} {
		    Log ^F$tk end of response for HEAD request - token $token
		    set state(state) complete
		    Eot $token
		    return
		} else {
		}

		# - For non-chunked transfer we may have no body - in this case
		#   we may get no further file event if the connection doesn't
		#   close and no more data is sent. We can tell and must finish
		#   up now - not later - the alternative would be to wait until
		#   the server times out.
		# - In this case, the server has NOT told the client it will
		#   close the connection, AND it has NOT indicated the resource
		#   length EITHER by setting the Content-Length (totalsize) OR
		#   by using chunked Transfer-Encoding.
		# - Do not worry here about the case (Connection: close) because
		#   the server should close the connection.
		# - IF (NOT Connection: close) AND (NOT chunked encoding) AND
		#      (totalsize == 0).

		if {    (!(    [info exists state(connection)]
			    && ("close" in $state(connection))
			  )
			)
		     && ($state(transfer) eq {})
		     && ($state(totalsize) == 0)
		} {
		    set msg {body size is 0 and no events likely - complete}
		    Log "$msg - token $token"
		    set msg {(length unknown, set to 0)}
		    Log ^F$tk end of response body {*}$msg - token $token
		    set state(state) complete
		    Eot $token
		    return
		} else {
		}

		# We have to use binary translation to count bytes properly.
		lassign [fconfigure $sock -translation] trRead trWrite
		fconfigure $sock -translation [list binary $trWrite]

		if {
		    $state(-binary) || [IsBinaryContentType $state(type)]
		} {
		    # Turn off conversions for non-text data.
		    set state(binary) 1
		} else {
		}
		if {[info exists state(-channel)]} {
		    if {$state(binary) || [llength [ContentEncoding $token]]} {
			fconfigure $state(-channel) -translation binary
		    } else {
		    }
		    if {![info exists state(-handler)]} {
			# Initiate a sequence of background fcopies.
			fileevent $sock readable {}
			rename ${token}--EventCoroutine {}
			CopyStart $sock $token
			return
		    } else {
		    }
		} else {
		}
	    } elseif {$nhl > 0} {
		# Process header lines.
		##Log header - token $token - $line
		if {[regexp -nocase {^([^:]+):(.+)$} $line x key value]} {
		    set key [string tolower $key]
		    switch -- $key {
			content-type {
			    set state(type) [string trim [string tolower $value]]
			    # Grab the optional charset information.
			    if {[regexp -nocase \
				    {charset\s*=\s*\"((?:[^""]|\\\")*)\"} \
				    $state(type) -> cs]} {
				set state(charset) [string map {{\"} \"} $cs]
2835
2836
2837
2838
2839
2840
2841






2842
2843
2844
2845
2846
2847
2848
2849
2850
2851

2852
2853
2854
2855

2856

2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871

2872
2873
2874
2875
2876
2877
2878
			    set state(transfer) \
				    [string trim [string tolower $value]]
			}
			proxy-connection -
			connection {
			    # RFC 7230 Section 6.1 states that a comma-separated
			    # list is an acceptable value.






			    foreach el [SplitCommaSeparatedFieldValue $value] {
				lappend state(connection) [string tolower $el]
			    }
			}
			upgrade {
			    set state(upgrade) [string trim $value]
			}
			set-cookie {
			    if {$http(-cookiejar) ne ""} {
				ParseCookie $token [string trim $value]

			    }
			}
		    }
		    lappend state(meta) $key [string trim $value]

		}

	    }
	} else {
	    # Now reading body
	    ##Log body - token $token
	    if {[catch {
		if {[info exists state(-handler)]} {
		    set n [eval $state(-handler) [list $sock $token]]
		    ##Log handler $n - token $token
		    # N.B. the protocol has been set to 1.0 because the -handler
		    # logic is not expected to handle chunked encoding.
		    # FIXME Allow -handler with 1.1 on dechunked stacked chan.
		    if {$state(totalsize) == 0} {
			# We know the transfer is complete only when the server
			# closes the connection - i.e. eof is not an error.
			set state(state) complete

		    }
		    if {![string is integer -strict $n]} {
			if 1 {
			    # Do not tolerate bad -handler - fail with error
			    # status.
			    set msg {the -handler command for http::geturl must\
				    return an integer (the number of bytes\







>
>
>
>
>
>










>




>

>






|








>







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
			    set state(transfer) \
				    [string trim [string tolower $value]]
			}
			proxy-connection -
			connection {
			    # RFC 7230 Section 6.1 states that a comma-separated
			    # list is an acceptable value.
			    if {![info exists state(connectionRespFlag)]} {
				# This is the first "Connection" response header.
				# Scrub the earlier value set by iniitialisation.
				set state(connectionRespFlag) {}
				set state(connection) {}
			    }
			    foreach el [SplitCommaSeparatedFieldValue $value] {
				lappend state(connection) [string tolower $el]
			    }
			}
			upgrade {
			    set state(upgrade) [string trim $value]
			}
			set-cookie {
			    if {$http(-cookiejar) ne ""} {
				ParseCookie $token [string trim $value]
			    } else {
			    }
			}
		    }
		    lappend state(meta) $key [string trim $value]
		} else {
		}
	    } else {
	    }
	} else {
	    # Now reading body
	    ##Log body - token $token
	    if {[catch {
		if {[info exists state(-handler)]} {
		    set n [namespace eval :: $state(-handler) [list $sock $token]]
		    ##Log handler $n - token $token
		    # N.B. the protocol has been set to 1.0 because the -handler
		    # logic is not expected to handle chunked encoding.
		    # FIXME Allow -handler with 1.1 on dechunked stacked chan.
		    if {$state(totalsize) == 0} {
			# We know the transfer is complete only when the server
			# closes the connection - i.e. eof is not an error.
			set state(state) complete
		    } else {
		    }
		    if {![string is integer -strict $n]} {
			if 1 {
			    # Do not tolerate bad -handler - fail with error
			    # status.
			    set msg {the -handler command for http::geturl must\
				    return an integer (the number of bytes\
2894
2895
2896
2897
2898
2899
2900

2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
			    #     HTTP/1.0 equivalent; or it MUST fail (as
			    #     above) if the server sends
			    #     "Connection: keep-alive" or the HTTP/1.0
			    #     equivalent.
			    set n 0
			    set state(state) complete
			}

		    }
		} elseif {[info exists state(transfer_final)]} {
		    # This code forgives EOF in place of the final CRLF.
		    set line [getTextLine $sock]
		    set n [string length $line]
		    set state(state) complete
		    if {$n > 0} {
			# - HTTP trailers (late response headers) are permitted
			#   by Chunked Transfer-Encoding, and can be safely
			#   ignored.
			# - Do not count these bytes in the total received for







>



|







3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
			    #     HTTP/1.0 equivalent; or it MUST fail (as
			    #     above) if the server sends
			    #     "Connection: keep-alive" or the HTTP/1.0
			    #     equivalent.
			    set n 0
			    set state(state) complete
			}
		    } else {
		    }
		} elseif {[info exists state(transfer_final)]} {
		    # This code forgives EOF in place of the final CRLF.
		    set line [GetTextLine $sock]
		    set n [string length $line]
		    set state(state) complete
		    if {$n > 0} {
			# - HTTP trailers (late response headers) are permitted
			#   by Chunked Transfer-Encoding, and can be safely
			#   ignored.
			# - Do not count these bytes in the total received for
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
			Eot $token
		    }
		} elseif {    [info exists state(transfer)]
			   && ($state(transfer) eq "chunked")
		} {
		    ##Log chunked - token $token
		    set size 0
		    set hexLenChunk [getTextLine $sock]
		    #set ntl [string length $hexLenChunk]
		    if {[string trim $hexLenChunk] ne ""} {
			scan $hexLenChunk %x size
			if {$size != 0} {
			    ##Log chunk-measure $size - token $token
			    set chunk [BlockingRead $sock $size]
			    set n [string length $chunk]
			    if {$n >= 0} {
				append state(body) $chunk
				incr state(log_size) [string length $chunk]
				##Log chunk $n cumul $state(log_size) -\
					token $token

			    }
			    if {$size != [string length $chunk]} {
				Log "WARNING: mis-sized chunk:\
				    was [string length $chunk], should be\
				    $size - token $token"
				set n 0
				set state(connection) close
				Log ^X$tk end of response (chunk error) \
					- token $token
				set msg {error in chunked encoding - fetch\
					terminated}
				Eot $token $msg

			    }
			    # CRLF that follows chunk.
			    # If eof, this is handled at the end of this proc.
			    getTextLine $sock
			} else {
			    set n 0
			    set state(transfer_final) {}
			}
		    } else {
			# Line expected to hold chunk length is empty, or eof.
			##Log bad-chunk-measure - token $token







|












>












>



|







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
			Eot $token
		    }
		} elseif {    [info exists state(transfer)]
			   && ($state(transfer) eq "chunked")
		} {
		    ##Log chunked - token $token
		    set size 0
		    set hexLenChunk [GetTextLine $sock]
		    #set ntl [string length $hexLenChunk]
		    if {[string trim $hexLenChunk] ne ""} {
			scan $hexLenChunk %x size
			if {$size != 0} {
			    ##Log chunk-measure $size - token $token
			    set chunk [BlockingRead $sock $size]
			    set n [string length $chunk]
			    if {$n >= 0} {
				append state(body) $chunk
				incr state(log_size) [string length $chunk]
				##Log chunk $n cumul $state(log_size) -\
					token $token
			    } else {
			    }
			    if {$size != [string length $chunk]} {
				Log "WARNING: mis-sized chunk:\
				    was [string length $chunk], should be\
				    $size - token $token"
				set n 0
				set state(connection) close
				Log ^X$tk end of response (chunk error) \
					- token $token
				set msg {error in chunked encoding - fetch\
					terminated}
				Eot $token $msg
			    } else {
			    }
			    # CRLF that follows chunk.
			    # If eof, this is handled at the end of this proc.
			    GetTextLine $sock
			} else {
			    set n 0
			    set state(transfer_final) {}
			}
		    } else {
			# Line expected to hold chunk length is empty, or eof.
			##Log bad-chunk-measure - token $token
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
			    token $token
		    set block [read $sock $reqSize]
		    set n [string length $block]
		    if {$n >= 0} {
			append state(body) $block
			##Log non-chunk [string length $state(body)] -\
				token $token

		    }
		}
		# This calculation uses n from the -handler, chunked, or
		# unchunked case as appropriate.
		if {[info exists state]} {
		    if {$n >= 0} {
			incr state(currentsize) $n
			set c $state(currentsize)
			set t $state(totalsize)
			##Log another $n currentsize $c totalsize $t -\
				token $token

		    }
		    # If Content-Length - check for end of data.
		    if {
			   ($state(totalsize) > 0)
			&& ($state(currentsize) >= $state(totalsize))
		    } {
			Log ^F$tk end of response body (unchunked) -\
				token $token
			set state(state) complete
			Eot $token

		    }

		}
	    } err]} {
		Log ^X$tk end of response (error ${err}) - token $token
		Finish $token $err
		return
	    } else {
		if {[info exists state(-progress)]} {
		    eval $state(-progress) \
			[list $token $state(totalsize) $state(currentsize)]

		}
	    }
	}

	# catch as an Eot above may have closed the socket already
	# $state(state) may be connecting, header, body, or complete
	if {![set cc [catch {eof $sock} eof]] && $eof} {

	    ##Log eof - token $token
	    if {[info exists $token]} {
		set state(connection) close
		if {$state(state) eq "complete"} {
		    # This includes all cases in which the transaction
		    # can be completed by eof.
		    # The value "complete" is set only in http::Event, and it is







>











>










>

>







|

>






|
>







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
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
			    token $token
		    set block [read $sock $reqSize]
		    set n [string length $block]
		    if {$n >= 0} {
			append state(body) $block
			##Log non-chunk [string length $state(body)] -\
				token $token
		    } else {
		    }
		}
		# This calculation uses n from the -handler, chunked, or
		# unchunked case as appropriate.
		if {[info exists state]} {
		    if {$n >= 0} {
			incr state(currentsize) $n
			set c $state(currentsize)
			set t $state(totalsize)
			##Log another $n currentsize $c totalsize $t -\
				token $token
		    } else {
		    }
		    # If Content-Length - check for end of data.
		    if {
			   ($state(totalsize) > 0)
			&& ($state(currentsize) >= $state(totalsize))
		    } {
			Log ^F$tk end of response body (unchunked) -\
				token $token
			set state(state) complete
			Eot $token
		    } else {
		    }
		} else {
		}
	    } err]} {
		Log ^X$tk end of response (error ${err}) - token $token
		Finish $token $err
		return
	    } else {
		if {[info exists state(-progress)]} {
		    namespace eval :: $state(-progress) \
			[list $token $state(totalsize) $state(currentsize)]
		} else {
		}
	    }
	}

	# catch as an Eot above may have closed the socket already
	# $state(state) may be connecting, header, body, or complete
	if {(![catch {eof $sock} eof]) && $eof} {
	    # [eof sock] succeeded and the result was 1
	    ##Log eof - token $token
	    if {[info exists $token]} {
		set state(connection) close
		if {$state(state) eq "complete"} {
		    # This includes all cases in which the transaction
		    # can be completed by eof.
		    # The value "complete" is set only in http::Event, and it is
3051
3052
3053
3054
3055
3056
3057
3058
3059

3060
3061

3062
3063
3064
3065
3066
3067
3068
		    Eot $token eof
		}
	    } else {
		# open connection closed on a token that has been cleaned up.
		Log ^X$tk end of response (token error) - token $token
		CloseSocket $sock
	    }
	} elseif {$cc} {
	    return

	}
    }

}

# http::TestForReplay
#
#	Command called if eof is discovered when a socket is first used for a
#	new transaction.  Typically this occurs if a persistent socket is used
#	after a period of idleness and the server has half-closed the socket.







|
|
>


>







3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
		    Eot $token eof
		}
	    } else {
		# open connection closed on a token that has been cleaned up.
		Log ^X$tk end of response (token error) - token $token
		CloseSocket $sock
	    }
	} else {
	    # EITHER [eof sock] failed - presumed done by Eot
	    # OR     [eof sock] succeeded and the result was 0
	}
    }
    return
}

# http::TestForReplay
#
#	Command called if eof is discovered when a socket is first used for a
#	new transaction.  Typically this occurs if a persistent socket is used
#	after a period of idleness and the server has half-closed the socket.
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231

3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253


3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306






3307
3308
3309
3310
3311
3312






3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326

3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347

3348




3349
3350
3351
3352
3353
3354
3355

3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385

3386
3387
3388
3389
3390
3391
3392
	}
    }
    dict set realopts key $cookiename
    dict set realopts value $cookieval
    {*}$http(-cookiejar) storeCookie $realopts
}

# http::getTextLine --
#
#	Get one line with the stream in crlf mode.
#	Used if Transfer-Encoding is chunked.

#	Empty line is not distinguished from eof.  The caller must
#	be able to handle this.
#
# Arguments
#	sock	The socket receiving input.
#
# Results:
#	The line of text, without trailing newline

proc http::getTextLine {sock} {
    set tr [fconfigure $sock -translation]
    lassign $tr trRead trWrite
    fconfigure $sock -translation [list crlf $trWrite]
    set r [BlockingGets $sock]
    fconfigure $sock -translation $tr
    return $r
}

# http::BlockingRead
#
#	Replacement for a blocking read.
#	The caller must be a coroutine.



proc http::BlockingRead {sock size} {
    if {$size < 1} {
	return
    }
    set result {}
    while 1 {
	set need [expr {$size - [string length $result]}]
	set block [read $sock $need]
	set eof [eof $sock]
	append result $block
	if {[string length $result] >= $size || $eof} {
	    return $result
	} else {
	    yield
	}
    }
}

# http::BlockingGets
#
#	Replacement for a blocking gets.
#	The caller must be a coroutine.
#	Empty line is not distinguished from eof.  The caller must
#	be able to handle this.

proc http::BlockingGets {sock} {
    while 1 {
	set count [gets $sock line]
	set eof [eof $sock]
	if {$count >= 0 || $eof} {
	    return $line
	} else {
	    yield
	}
    }
}

# http::CopyStart
#
#	Error handling wrapper around fcopy
#
# Arguments
#	sock	The socket to copy from
#	token	The token returned from http::geturl
#
# Side Effects
#	This closes the connection upon error

proc http::CopyStart {sock token {initial 1}} {
    upvar #0 $token state
    if {[info exists state(transfer)] && $state(transfer) eq "chunked"} {
	foreach coding [ContentEncoding $token] {






	    lappend state(zlib) [zlib stream $coding]
	}
	make-transformation-chunked $sock [namespace code [list CopyChunk $token]]
    } else {
	if {$initial} {
	    foreach coding [ContentEncoding $token] {






		zlib push $coding $sock
	    }
	}
	if {[catch {
	    # FIXME Keep-Alive on https tls::socket with unchunked transfer
	    # hangs until the server times out. A workaround is possible, as for
	    # the case without -channel, but it does not use the neat "fcopy"
	    # solution.
	    fcopy $sock $state(-channel) -size $state(-blocksize) -command \
		[list http::CopyDone $token]
	} err]} {
	    Finish $token $err
	}
    }

}

proc http::CopyChunk {token chunk} {
    upvar 0 $token state
    if {[set count [string length $chunk]]} {
	incr state(currentsize) $count
	if {[info exists state(zlib)]} {
	    foreach stream $state(zlib) {
		set chunk [$stream add $chunk]
	    }
	}
	puts -nonewline $state(-channel) $chunk
	if {[info exists state(-progress)]} {
	    eval [linsert $state(-progress) end \
		      $token $state(totalsize) $state(currentsize)]
	}
    } else {
	Log "CopyChunk Finish - token $token"
	if {[info exists state(zlib)]} {
	    set excess ""
	    foreach stream $state(zlib) {

		catch {set excess [$stream add -finalize $excess]}




	    }
	    puts -nonewline $state(-channel) $excess
	    foreach stream $state(zlib) { $stream close }
	    unset state(zlib)
	}
	Eot $token ;# FIX ME: pipelining.
    }

}

# http::CopyDone
#
#	fcopy completion callback
#
# Arguments
#	token	The token returned from http::geturl
#	count	The amount transfered
#
# Side Effects
#	Invokes callbacks

proc http::CopyDone {token count {error {}}} {
    variable $token
    upvar 0 $token state
    set sock $state(sock)
    incr state(currentsize) $count
    if {[info exists state(-progress)]} {
	eval $state(-progress) \
	    [list $token $state(totalsize) $state(currentsize)]
    }
    # At this point the token may have been reset.
    if {[string length $error]} {
	Finish $token $error
    } elseif {[catch {eof $sock} iseof] || $iseof} {
	Eot $token
    } else {
	CopyStart $sock $token 0
    }

}

# http::Eot
#
#	Called when either:
#	a. An eof condition is detected on the socket.
#	b. The client decides that the response is complete.







|


|
>









|












>
>









|



















|




















|


>
>
>
>
>
>
|

|



>
>
>
>
>
>
|













>













|







>
|
>
>
>
>







>



















|










>







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
	}
    }
    dict set realopts key $cookiename
    dict set realopts value $cookieval
    {*}$http(-cookiejar) storeCookie $realopts
}

# http::GetTextLine --
#
#	Get one line with the stream in crlf mode.
#	Used if Transfer-Encoding is chunked, to read the line that
#	reports the size of the following chunk.
#	Empty line is not distinguished from eof.  The caller must
#	be able to handle this.
#
# Arguments
#	sock	The socket receiving input.
#
# Results:
#	The line of text, without trailing newline

proc http::GetTextLine {sock} {
    set tr [fconfigure $sock -translation]
    lassign $tr trRead trWrite
    fconfigure $sock -translation [list crlf $trWrite]
    set r [BlockingGets $sock]
    fconfigure $sock -translation $tr
    return $r
}

# http::BlockingRead
#
#	Replacement for a blocking read.
#	The caller must be a coroutine.
#	Used when we expect to read a chunked-encoding
#	chunk of known size.

proc http::BlockingRead {sock size} {
    if {$size < 1} {
	return
    }
    set result {}
    while 1 {
	set need [expr {$size - [string length $result]}]
	set block [read $sock $need]
	set eof [expr {[catch {eof $sock} tmp] || $tmp}]
	append result $block
	if {[string length $result] >= $size || $eof} {
	    return $result
	} else {
	    yield
	}
    }
}

# http::BlockingGets
#
#	Replacement for a blocking gets.
#	The caller must be a coroutine.
#	Empty line is not distinguished from eof.  The caller must
#	be able to handle this.

proc http::BlockingGets {sock} {
    while 1 {
	set count [gets $sock line]
	set eof [expr {[catch {eof $sock} tmp] || $tmp}]
	if {$count >= 0 || $eof} {
	    return $line
	} else {
	    yield
	}
    }
}

# http::CopyStart
#
#	Error handling wrapper around fcopy
#
# Arguments
#	sock	The socket to copy from
#	token	The token returned from http::geturl
#
# Side Effects
#	This closes the connection upon error

proc http::CopyStart {sock token {initial 1}} {
    upvar 0 $token state
    if {[info exists state(transfer)] && $state(transfer) eq "chunked"} {
	foreach coding [ContentEncoding $token] {
	    if {$coding eq {deflateX}} {
		# Use the standards-compliant choice.
		set coding2 decompress
	    } else {
		set coding2 $coding
	    }
	    lappend state(zlib) [zlib stream $coding2]
	}
	MakeTransformationChunked $sock [namespace code [list CopyChunk $token]]
    } else {
	if {$initial} {
	    foreach coding [ContentEncoding $token] {
		if {$coding eq {deflateX}} {
		    # Use the standards-compliant choice.
		    set coding2 decompress
		} else {
		    set coding2 $coding
		}
		zlib push $coding2 $sock
	    }
	}
	if {[catch {
	    # FIXME Keep-Alive on https tls::socket with unchunked transfer
	    # hangs until the server times out. A workaround is possible, as for
	    # the case without -channel, but it does not use the neat "fcopy"
	    # solution.
	    fcopy $sock $state(-channel) -size $state(-blocksize) -command \
		[list http::CopyDone $token]
	} err]} {
	    Finish $token $err
	}
    }
    return
}

proc http::CopyChunk {token chunk} {
    upvar 0 $token state
    if {[set count [string length $chunk]]} {
	incr state(currentsize) $count
	if {[info exists state(zlib)]} {
	    foreach stream $state(zlib) {
		set chunk [$stream add $chunk]
	    }
	}
	puts -nonewline $state(-channel) $chunk
	if {[info exists state(-progress)]} {
	    namespace eval :: [linsert $state(-progress) end \
		      $token $state(totalsize) $state(currentsize)]
	}
    } else {
	Log "CopyChunk Finish - token $token"
	if {[info exists state(zlib)]} {
	    set excess ""
	    foreach stream $state(zlib) {
		catch {
		    $stream put -finalize $excess
		    set excess ""
		    set overflood ""
		    while {[set overflood [$stream get]] ne ""} { append excess $overflood }
		}
	    }
	    puts -nonewline $state(-channel) $excess
	    foreach stream $state(zlib) { $stream close }
	    unset state(zlib)
	}
	Eot $token ;# FIX ME: pipelining.
    }
    return
}

# http::CopyDone
#
#	fcopy completion callback
#
# Arguments
#	token	The token returned from http::geturl
#	count	The amount transfered
#
# Side Effects
#	Invokes callbacks

proc http::CopyDone {token count {error {}}} {
    variable $token
    upvar 0 $token state
    set sock $state(sock)
    incr state(currentsize) $count
    if {[info exists state(-progress)]} {
	namespace eval :: $state(-progress) \
	    [list $token $state(totalsize) $state(currentsize)]
    }
    # At this point the token may have been reset.
    if {[string length $error]} {
	Finish $token $error
    } elseif {[catch {eof $sock} iseof] || $iseof} {
	Eot $token
    } else {
	CopyStart $sock $token 0
    }
    return
}

# http::Eot
#
#	Called when either:
#	a. An eof condition is detected on the socket.
#	b. The client decides that the response is complete.
3424
3425
3426
3427
3428
3429
3430












3431

3432
3433
3434
3435
3436
3437
3438
	# The response is complete.
	set state(status) ok
    }

    if {[string length $state(body)] > 0} {
	if {[catch {
	    foreach coding [ContentEncoding $token] {












		set state(body) [zlib $coding $state(body)]

	    }
	} err]} {
	    Log "error doing decompression for token $token: $err"
	    Finish $token $err
	    return
	}








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







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
	# The response is complete.
	set state(status) ok
    }

    if {[string length $state(body)] > 0} {
	if {[catch {
	    foreach coding [ContentEncoding $token] {
		if {$coding eq {deflateX}} {
		    # First try the standards-compliant choice.
		    set coding2 decompress
		    if {[catch {zlib $coding2 $state(body)} result]} {
			# If that fails, try the MS non-compliant choice.
			set coding2 inflate
			set state(body) [zlib $coding2 $state(body)]
		    } else {
			# error {failed at standards-compliant deflate}
			set state(body) $result
		    }
		} else {
		    set state(body) [zlib $coding $state(body)]
		}
	    }
	} err]} {
	    Log "error doing decompression for token $token: $err"
	    Finish $token $err
	    return
	}

3446
3447
3448
3449
3450
3451
3452


3453

3454

3455














































































3456
3457
3458
3459
3460
3461
3462
	    if {$enc ne "binary"} {
		set state(body) [encoding convertfrom $enc $state(body)]
	    }

	    # Translate text line endings.
	    set state(body) [string map {\r\n \n \r \n} $state(body)]
	}


    }

    Finish $token $reason

}















































































# http::wait --
#
#	See documentation for details.
#
# Arguments:
#	token	Connection token.







>
>
|
>

>

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







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
	    if {$enc ne "binary"} {
		set state(body) [encoding convertfrom $enc $state(body)]
	    }

	    # Translate text line endings.
	    set state(body) [string map {\r\n \n \r \n} $state(body)]
	}
	if {[info exists state(-guesstype)] && $state(-guesstype)} {
	    GuessType $token
	}
    }
    Finish $token $reason
    return
}


# ------------------------------------------------------------------------------
#  Proc http::GuessType
# ------------------------------------------------------------------------------
# Command to attempt limited analysis of a resource with undetermined
# Content-Type, i.e. "application/octet-stream".  This value can be set for two
# reasons:
# (a) by the server, in a Content-Type header
# (b) by http::geturl, as the default value if the server does not supply a
#     Content-Type header.
#
# This command converts a resource if:
# (1) it has type application/octet-stream
# (2) it begins with an XML declaration "<?xml name="value" ... >?"
# (3) one tag is named "encoding" and has a recognised value; or no "encoding"
#     tag exists (defaulting to utf-8)
#
# RFC 9110 Sec. 8.3 states:
# "If a Content-Type header field is not present, the recipient MAY either
# assume a media type of "application/octet-stream" ([RFC2046], Section 4.5.1)
# or examine the data to determine its type."
#
# The RFC goes on to describe the pitfalls of "MIME sniffing", including
# possible security risks.
#
# Arguments:
# token       - connection token
#
# Return Value: (boolean) true iff a change has been made
# ------------------------------------------------------------------------------

proc http::GuessType {token} {
    variable $token
    upvar 0 $token state

    if {$state(type) ne {application/octet-stream}} {
        return 0
    }

    set body $state(body)
    # e.g. {<?xml version="1.0" encoding="utf-8"?> ...}

    if {![regexp -nocase -- {^<[?]xml[[:space:]][^>?]*[?]>} $body match]} {
        return 0
    }
    # e.g. {<?xml version="1.0" encoding="utf-8"?>}

    set contents [regsub -- {[[:space:]]+} $match { }]
    set contents [string range [string tolower $contents] 6 end-2]
    # e.g. {version="1.0" encoding="utf-8"}
    # without excess whitespace or upper-case letters

    if {![regexp -- {^([^=" ]+="[^"]+" )+$} "$contents "]} {
        return 0
    }
    # The application/xml default encoding:
    set res utf-8

    set tagList [regexp -all -inline -- {[^=" ]+="[^"]+"} $contents]
    foreach tag $tagList {
        regexp -- {([^=" ]+)="([^"]+)"} $tag -> name value
        if {$name eq {encoding}} {
            set res $value
        }
    }
    set enc [CharsetToEncoding $res]
    if {$enc eq "binary"} {
        return 0
    }
    set state(body) [encoding convertfrom $enc $state(body)]
    set state(body) [string map {\r\n \n \r \n} $state(body)]
    set state(type) application/xml
    set state(binary) 0
    set state(charset) $res
    return 1
}


# http::wait --
#
#	See documentation for details.
#
# Arguments:
#	token	Connection token.
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552


3553
3554
3555
3556
3557
3558
3559
            -code error \
            -errorcode [list HTTP BADARGCNT $args] \
            {Incorrect number of arguments, must be an even number.}
    }
    set result ""
    set sep ""
    foreach i $args {
	append result $sep [mapReply $i]
	if {$sep eq "="} {
	    set sep &
	} else {
	    set sep =
	}
    }
    return $result
}

# http::mapReply --
#
#	Do x-www-urlencoded character mapping
#
# Arguments:
#	string	The string the needs to be encoded
#
# Results:
#       The encoded string

proc http::mapReply {string} {
    variable http
    variable formMap

    # The spec says: "non-alphanumeric characters are replaced by '%HH'". Use
    # a pre-computed map and [string map] to do the conversion (much faster
    # than [regsub]/[subst]). [Bug 1020491]

    set string [encoding convertto $http(-urlencoding) $string]
    return [string map $formMap $string]
}
interp alias {} http::quoteString {} http::mapReply

# http::ProxyRequired --
#	Default proxy filter.
#
# Arguments:
#	host	The destination host
#
# Results:
#       The current proxy settings

proc http::ProxyRequired {host} {
    variable http
    if {[info exists http(-proxyhost)] && [string length $http(-proxyhost)]} {
	if {
	    ![info exists http(-proxyport)] ||
	    ![string length $http(-proxyport)]
	} {
	    set http(-proxyport) 8080
	}
	return [list $http(-proxyhost) $http(-proxyport)]


    }
}

# http::CharsetToEncoding --
#
#	Tries to map a given IANA charset to a tcl encoding.  If no encoding
#	can be found, returns binary.







|









|









|










<




















>
>







4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504

4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
            -code error \
            -errorcode [list HTTP BADARGCNT $args] \
            {Incorrect number of arguments, must be an even number.}
    }
    set result ""
    set sep ""
    foreach i $args {
	append result $sep [quoteString $i]
	if {$sep eq "="} {
	    set sep &
	} else {
	    set sep =
	}
    }
    return $result
}

# http::quoteString --
#
#	Do x-www-urlencoded character mapping
#
# Arguments:
#	string	The string the needs to be encoded
#
# Results:
#       The encoded string

proc http::quoteString {string} {
    variable http
    variable formMap

    # The spec says: "non-alphanumeric characters are replaced by '%HH'". Use
    # a pre-computed map and [string map] to do the conversion (much faster
    # than [regsub]/[subst]). [Bug 1020491]

    set string [encoding convertto $http(-urlencoding) $string]
    return [string map $formMap $string]
}


# http::ProxyRequired --
#	Default proxy filter.
#
# Arguments:
#	host	The destination host
#
# Results:
#       The current proxy settings

proc http::ProxyRequired {host} {
    variable http
    if {[info exists http(-proxyhost)] && [string length $http(-proxyhost)]} {
	if {
	    ![info exists http(-proxyport)] ||
	    ![string length $http(-proxyport)]
	} {
	    set http(-proxyport) 8080
	}
	return [list $http(-proxyhost) $http(-proxyport)]
    } else {
	return
    }
}

# http::CharsetToEncoding --
#
#	Tries to map a given IANA charset to a tcl encoding.  If no encoding
#	can be found, returns binary.
3591
3592
3593
3594
3595
3596
3597




3598






















3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
    if {$idx >= 0} {
	return $encoding
    } else {
	return "binary"
    }
}





# Return the list of content-encoding transformations we need to do in order.






















proc http::ContentEncoding {token} {
    upvar 0 $token state
    set r {}
    if {[info exists state(coding)]} {
	foreach coding [split $state(coding) ,] {
	    switch -exact -- $coding {
		deflate { lappend r inflate }
		gzip - x-gzip { lappend r gunzip }
		compress - x-compress { lappend r decompress }
		identity {}
		br {
		    return -code error\
			    "content-encoding \"br\" not implemented"
		}
		default {
		    Log "unknown content-encoding \"$coding\" ignored"







>
>
>
>

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






|

<







4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606

4607
4608
4609
4610
4611
4612
4613
    if {$idx >= 0} {
	return $encoding
    } else {
	return "binary"
    }
}


# ------------------------------------------------------------------------------
#  Proc http::ContentEncoding
# ------------------------------------------------------------------------------
# Return the list of content-encoding transformations we need to do in order.
#
    # --------------------------------------------------------------------------
    # Options for Accept-Encoding, Content-Encoding: the switch command
    # --------------------------------------------------------------------------
    # The symbol deflateX allows http to attempt both versions of "deflate",
    # unless there is a -channel - for a -channel, only "decompress" is tried.
    # Alternative/extra lines for switch:
    # The standards-compliant version of "deflate" can be chosen with:
    #		deflate { lappend r decompress }
    # The Microsoft non-compliant version of "deflate" can be chosen with:
    #		deflate { lappend r inflate }
    # The previously used implementation of "compress", which appears to be
    # incorrect and is rarely used by web servers, can be chosen with:
    #		compress - x-compress { lappend r decompress }
    # --------------------------------------------------------------------------
#
# Arguments:
# token  - Connection token.
#
# Return Value: list
# ------------------------------------------------------------------------------

proc http::ContentEncoding {token} {
    upvar 0 $token state
    set r {}
    if {[info exists state(coding)]} {
	foreach coding [split $state(coding) ,] {
	    switch -exact -- $coding {
		deflate { lappend r deflateX }
		gzip - x-gzip { lappend r gunzip }

		identity {}
		br {
		    return -code error\
			    "content-encoding \"br\" not implemented"
		}
		default {
		    Log "unknown content-encoding \"$coding\" ignored"
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700























































































































































































































































































3701
3702
3703
3704
3705
		append r ", $value"
	    }
	}
    }
    return $r
}

proc http::make-transformation-chunked {chan command} {
    coroutine [namespace current]::dechunk$chan ::http::ReceiveChunked $chan $command
    chan event $chan readable [namespace current]::dechunk$chan























































































































































































































































































}

# Local variables:
# indent-tabs-mode: t
# End:







|


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





4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
		append r ", $value"
	    }
	}
    }
    return $r
}

proc http::MakeTransformationChunked {chan command} {
    coroutine [namespace current]::dechunk$chan ::http::ReceiveChunked $chan $command
    chan event $chan readable [namespace current]::dechunk$chan
    return
}

interp alias {} http::data {} http::responseBody
interp alias {} http::code {} http::responseLine
interp alias {} http::mapReply {} http::quoteString
interp alias {} http::meta {} http::responseHeaders
interp alias {} http::metaValue {} http::responseHeaderValue
interp alias {} http::ncode {} http::responseCode

# ------------------------------------------------------------------------------
#  Proc http::socket
# ------------------------------------------------------------------------------
# This command is a drop-in replacement for ::socket.
# Arguments and return value as for ::socket.
#
# Notes.
# - http::socket is specified in place of ::socket by the definition of urlTypes
#   in the namespace header of this file (http.tcl).
# - The command makes a simple call to ::socket unless the user has called
#   http::config to change the value of -threadlevel from the default value 0.
# - For -threadlevel 1 or 2, if the Thread package is available, the command
#   waits in the event loop while the socket is opened in another thread.  This
#   is a workaround for bug [824251] - it prevents http::geturl from blocking
#   the event loop if the DNS lookup or server connection is slow.
# - FIXME Use a thread pool if connections are very frequent.
# - FIXME The peer thread can transfer the socket only to the main interpreter
#   in the present thread.  Therefore this code works only if this script runs
#   in the main interpreter.  In a child interpreter, the parent must alias a
#   command to ::http::socket in the child, run http::socket in the parent,
#   and then transfer the socket to the child.
# - The http::socket command is simple, and can easily be replaced with an
#   alternative command that uses a different technique to open a socket while
#   entering the event loop.
# - Unexpected behaviour by thread::send -async (Thread 2.8.6).
#   An error in thread::send -async causes return of just the error message
#   (not the expected 3 elements), and raises a bgerror in the main thread.
#   Hence wrap the command with catch as a precaution.
# ------------------------------------------------------------------------------

proc http::socket {args} {
    variable ThreadVar
    variable ThreadCounter
    variable http

    LoadThreadIfNeeded

    set targ [lsearch -exact $args -token]
    if {$targ != -1} {
        set token [lindex $args $targ+1]
        set args [lreplace $args $targ $targ+1]
        upvar 0 $token state
    }

    if {!$http(usingThread)} {
        # Use plain "::socket".  This is the default.
        return [eval ::socket $args]
    }

    set defcmd ::socket
    set sockargs $args
    set script "
        set code \[catch {
            [list proc ::SockInThread {caller defcmd sockargs} [info body ::http::SockInThread]]
            [list ::SockInThread [thread::id] $defcmd $sockargs]
        } result opts\]
        list \$code \$opts \$result
    "

    set state(tid) [thread::create]
    set varName ::http::ThreadVar([incr ThreadCounter])
    thread::send -async $state(tid) $script $varName
    Log >T Thread Start Wait $args -- coro [info coroutine] $varName
    if {[info coroutine] ne {}} {
        # All callers in the http package are coroutines launched by
        # the event loop.
        # The cwait command requires a coroutine because it yields
        # to the caller; $varName is traced and the coroutine resumes
        # when the variable is written.
        cwait $varName
    } else {
        return -code error {code must run in a coroutine}
        # For testing with a non-coroutine caller outside the http package.
        # vwait $varName
    }
    Log >U Thread End Wait $args -- coro [info coroutine] $varName [set $varName]
    thread::release $state(tid)
    set state(tid) {}
    set result [set $varName]
    unset $varName
    if {(![string is list $result]) || ([llength $result] != 3)} {
        return -code error "result from peer thread is not a list of\
                length 3: it is \n$result"
    }
    lassign $result threadCode threadDict threadResult
    if {($threadCode != 0)} {
        # This is an error in thread::send.  Return the lot.
        return -options $threadDict -code error $threadResult
    }

    # Now the results of the catch in the peer thread.
    lassign $threadResult catchCode errdict sock

    if {($catchCode == 0) && ($sock ni [chan names])} {
        return -code error {Transfer of socket from peer thread failed.\
		Check that this script is not running in a child interpreter.}
    }
    return -options $errdict -code $catchCode $sock
}

# The commands below are dependencies of http::socket and
# are not used elsewhere.

# ------------------------------------------------------------------------------
#  Proc http::LoadThreadIfNeeded
# ------------------------------------------------------------------------------
# Command to load the Thread package if it is needed.  If it is needed and not
# loadable, the outcome depends on $http(-threadlevel):
# value 0 => Thread package not required, no problem
# value 1 => operate as if -threadlevel 0
# value 2 => error return
#
# Arguments: none
# Return Value: none
# ------------------------------------------------------------------------------

proc http::LoadThreadIfNeeded {} {
    variable http
    if {$http(usingThread) || ($http(-threadlevel) == 0)} {
        return
    }
    if {[catch {package require Thread}]} {
        if {$http(-threadlevel) == 2} {
            set msg {[http::config -threadlevel] has value 2,\
                     but the Thread package is not available}
            return -code error $msg
        }
        return
    }
    set http(usingThread) 1
    return
}


# ------------------------------------------------------------------------------
#  Proc http::SockInThread
# ------------------------------------------------------------------------------
# Command http::socket is a ::socket replacement.  It defines and runs this
# command, http::SockInThread, in a peer thread.
#
# Arguments:
# caller
# defcmd
# sockargs
#
# Return value: list of values that describe the outcome.  The return is
# intended to be a normal (non-error) return in all cases.
# ------------------------------------------------------------------------------

proc http::SockInThread {caller defcmd sockargs} {
    package require Thread

    set catchCode [catch {eval $defcmd $sockargs} sock errdict]
    if {$catchCode == 0} {
        set catchCode [catch {thread::transfer $caller $sock; set sock} sock errdict]
    }
    return [list $catchCode $errdict $sock]
}


# ------------------------------------------------------------------------------
#  Proc http::cwaiter::cwait
# ------------------------------------------------------------------------------
# Command to substitute for vwait, without the ordering issues.
# A command that uses cwait must be a coroutine that is launched by an event,
# e.g. fileevent or after idle, and has no calling code to be resumed upon
# "yield".  It cannot return a value.
#
# Arguments:
# varName      - fully-qualified name of the variable that the calling script
#                will write to resume the coroutine.  Any scalar variable or
#                array element is permitted.
# coroName     - (optional) name of the coroutine to be called when varName is
#                written - defaults to this coroutine
# timeout      - (optional) timeout value in ms
# timeoutValue - (optional) value to assign to varName if there is a timeout
#
# Return Value: none
# ------------------------------------------------------------------------------

namespace eval http::cwaiter {
    namespace export cwait
    variable log   {}
    variable logOn 0
}

proc http::cwaiter::cwait {
    varName {coroName {}} {timeout {}} {timeoutValue {}}
} {
    set thisCoro [info coroutine]
    if {$thisCoro eq {}} {
        return -code error {cwait cannot be called outside a coroutine}
    }
    if {$coroName eq {}} {
        set coroName $thisCoro
    }
    if {[string range $varName 0 1] ne {::}} {
        return -code error {argument varName must be fully qualified}
    }
    if {$timeout eq {}} {
        set toe {}
    } elseif {[string is integer -strict $timeout] && ($timeout > 0)} {
        set toe [after $timeout [list set $varName $timeoutValue]]
    } else {
        return -code error {if timeout is supplied it must be a positive integer}
    }

    set cmd [list ::http::cwaiter::CwaitHelper $varName $coroName $toe]
    trace add variable $varName write $cmd
    CoLog "Yield $varName $coroName"
    yield
    CoLog "Resume $varName $coroName"
    return
}


# ------------------------------------------------------------------------------
#  Proc http::cwaiter::CwaitHelper
# ------------------------------------------------------------------------------
# Helper command called by the trace set by cwait.
# - Ignores the arguments added by trace.
# - A simple call to $coroName works, and in error cases gives a suitable stack
#   trace, but because it is inside a trace the headline error message is
#   something like {can't set "::Result(6)": error}, not the actual
#   error.  So let the trace command return.
# - Remove the trace immediately.  We don't want multiple calls.
# ------------------------------------------------------------------------------

proc http::cwaiter::CwaitHelper {varName coroName toe args} {
    CoLog "got $varName for $coroName"
    set cmd [list ::http::cwaiter::CwaitHelper $varName $coroName $toe]
    trace remove variable $varName write $cmd
    after cancel $toe

    after 0 $coroName
    return
}


# ------------------------------------------------------------------------------
#  Proc http::cwaiter::LogInit
# ------------------------------------------------------------------------------
# Call this command to initiate debug logging and clear the log.
# ------------------------------------------------------------------------------

proc http::cwaiter::LogInit {} {
    variable log
    variable logOn
    set log {}
    set logOn 1
    return
}

proc http::cwaiter::LogRead {} {
    variable log
    return $log
}

proc http::cwaiter::CoLog {msg} {
    variable log
    variable logOn
    if {$logOn} {
        append log $msg \n
    }
    return
}

namespace eval http {
    namespace import ::http::cwaiter::*
}

# Local variables:
# indent-tabs-mode: t
# End:
Changes to library/init.tcl.
43
44
45
46
47
48
49
50








51
52
53
54
55
56
57
#
# (Ticket 41c9857bdd) In a safe interpreter, this file does not set
# ::auto_path (other than to {} if it is undefined). The caller, typically
# a Safe Base command, is responsible for setting ::auto_path.

if {![info exists auto_path]} {
    if {[info exists env(TCLLIBPATH)] && (![interp issafe])} {
	set auto_path $env(TCLLIBPATH)








    } else {
	set auto_path ""
    }
}
namespace eval tcl {
    if {![interp issafe]} {
	variable Dir







|
>
>
>
>
>
>
>
>







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#
# (Ticket 41c9857bdd) In a safe interpreter, this file does not set
# ::auto_path (other than to {} if it is undefined). The caller, typically
# a Safe Base command, is responsible for setting ::auto_path.

if {![info exists auto_path]} {
    if {[info exists env(TCLLIBPATH)] && (![interp issafe])} {
        set auto_path [apply {{} {
            lmap path $::env(TCLLIBPATH) {
                # Paths relative to unresolvable home dirs are ignored
                if {[catch {file tildeexpand $path} expanded_path]} {
                    continue
                }
                set expanded_path
            }
        }}]
    } else {
	set auto_path ""
    }
}
namespace eval tcl {
    if {![interp issafe]} {
	variable Dir
Changes to library/safe.tcl.
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
    ::interp alias $child ::tcl::encoding::system {} \
	::safe::AliasEncodingSystem $child

    # Subcommands of info
    ::interp alias $child ::tcl::info::nameofexecutable {} \
	::safe::AliasExeName $child

    # The allowed child variables already have been set by Tcl_MakeSafe(3)

    # Source init.tcl and tm.tcl into the child, to get auto_load and
    # other procedures defined:

    if {[catch {::interp eval $child {
	source [file join $tcl_library init.tcl]
    }} msg opt]} {
	Log $child "can't source init.tcl ($msg)"







<
<







520
521
522
523
524
525
526


527
528
529
530
531
532
533
    ::interp alias $child ::tcl::encoding::system {} \
	::safe::AliasEncodingSystem $child

    # Subcommands of info
    ::interp alias $child ::tcl::info::nameofexecutable {} \
	::safe::AliasExeName $child



    # Source init.tcl and tm.tcl into the child, to get auto_load and
    # other procedures defined:

    if {[catch {::interp eval $child {
	source [file join $tcl_library init.tcl]
    }} msg opt]} {
	Log $child "can't source init.tcl ($msg)"
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
}

# AliasFileSubcommand handles selected subcommands of [file] in safe
# interpreters that are *almost* safe. In particular, it just acts to
# prevent discovery of what home directories exist.

proc ::safe::AliasFileSubcommand {child subcommand name} {
    if {[string match ~* $name]} {
	set name ./$name
    }
    tailcall ::interp invokehidden $child tcl:file:$subcommand $name
}

# AliasGlob is the target of the "glob" alias in safe interpreters.

proc ::safe::AliasGlob {child args} {
    Log $child "GLOB ! $args" NOTICE







<
<
<







727
728
729
730
731
732
733



734
735
736
737
738
739
740
}

# AliasFileSubcommand handles selected subcommands of [file] in safe
# interpreters that are *almost* safe. In particular, it just acts to
# prevent discovery of what home directories exist.

proc ::safe::AliasFileSubcommand {child subcommand name} {



    tailcall ::interp invokehidden $child tcl:file:$subcommand $name
}

# AliasGlob is the target of the "glob" alias in safe interpreters.

proc ::safe::AliasGlob {child args} {
    Log $child "GLOB ! $args" NOTICE
Changes to library/tm.tcl.
334
335
336
337
338
339
340


341

342
343
344
345
346
347
348
    for {set n $minor} {$n >= 0} {incr n -1} {
	foreach ev [::list \
			TCL${major}.${n}_TM_PATH \
			TCL${major}_${n}_TM_PATH \
        ] {
	    if {![info exists env($ev)]} continue
	    foreach p [split $env($ev) $sep] {


		path add $p

	    }
	}
    }
    return
}

# ::tcl::tm::roots --







>
>
|
>







334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
    for {set n $minor} {$n >= 0} {incr n -1} {
	foreach ev [::list \
			TCL${major}.${n}_TM_PATH \
			TCL${major}_${n}_TM_PATH \
        ] {
	    if {![info exists env($ev)]} continue
	    foreach p [split $env($ev) $sep] {
                # Paths relative to unresolvable home dirs are ignored
                if {![catch {file tildeexpand $p} expanded_path]} {
                    path add $expanded_path
                }
	    }
	}
    }
    return
}

# ::tcl::tm::roots --
Changes to macosx/tclMacOSXNotify.c.
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
    int mask;			/* Mask of desired events: TCL_READABLE,
				 * etc. */
    int readyMask;		/* Mask of events that have been seen since
				 * the last time file handlers were invoked
				 * for this file. */
    Tcl_FileProc *proc;		/* Function to call, in the style of
				 * Tcl_CreateFileHandler. */
    ClientData clientData;	/* Argument to pass to proc. */
    struct FileHandler *nextPtr;/* Next in list of all files we care about. */
} FileHandler;

/*
 * The following structure is what is added to the Tcl event queue when file
 * handlers are ready to fire.
 */







|







307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
    int mask;			/* Mask of desired events: TCL_READABLE,
				 * etc. */
    int readyMask;		/* Mask of events that have been seen since
				 * the last time file handlers were invoked
				 * for this file. */
    Tcl_FileProc *proc;		/* Function to call, in the style of
				 * Tcl_CreateFileHandler. */
    void *clientData;	/* Argument to pass to proc. */
    struct FileHandler *nextPtr;/* Next in list of all files we care about. */
} FileHandler;

/*
 * The following structure is what is added to the Tcl event queue when file
 * handlers are ready to fire.
 */
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
#define CF_TIMEINTERVAL_FOREVER 5.05e8

/*
 * Static routines defined in this file.
 */

static void		StartNotifierThread(void);
static TCL_NORETURN void NotifierThreadProc(ClientData clientData);
static int		FileHandlerEventProc(Tcl_Event *evPtr, int flags);
static void		TimerWakeUp(CFRunLoopTimerRef timer, void *info);
static void		QueueFileEvents(void *info);
static void		UpdateWaitingListAndServiceEvents(
			    CFRunLoopObserverRef observer,
			    CFRunLoopActivity activity, void *info);
static int		OnOffWaitingList(ThreadSpecificData *tsdPtr,







|







501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
#define CF_TIMEINTERVAL_FOREVER 5.05e8

/*
 * Static routines defined in this file.
 */

static void		StartNotifierThread(void);
static TCL_NORETURN void NotifierThreadProc(void *clientData);
static int		FileHandlerEventProc(Tcl_Event *evPtr, int flags);
static void		TimerWakeUp(CFRunLoopTimerRef timer, void *info);
static void		QueueFileEvents(void *info);
static void		UpdateWaitingListAndServiceEvents(
			    CFRunLoopObserverRef observer,
			    CFRunLoopActivity activity, void *info);
static int		OnOffWaitingList(ThreadSpecificData *tsdPtr,
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

ClientData
TclpInitNotifier(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

#ifdef WEAK_IMPORT_SPINLOCKLOCK
    /*
     * Initialize support for weakly imported spinlock API.







|







608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void *
TclpInitNotifier(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

#ifdef WEAK_IMPORT_SPINLOCKLOCK
    /*
     * Initialize support for weakly imported spinlock API.
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
 *	notifier instance.
 *
 *----------------------------------------------------------------------
 */

void
TclpFinalizeNotifier(
    TCL_UNUSED(ClientData))
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    LOCK_NOTIFIER_INIT;
    notifierCount--;
    DISABLE_ASL;








|







864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
 *	notifier instance.
 *
 *----------------------------------------------------------------------
 */

void
TclpFinalizeNotifier(
    TCL_UNUSED(void *))
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    LOCK_NOTIFIER_INIT;
    notifierCount--;
    DISABLE_ASL;

966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
 *	Signals the notifier condition variable for the specified notifier.
 *
 *----------------------------------------------------------------------
 */

void
TclpAlertNotifier(
    ClientData clientData)
{
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;

    LOCK_NOTIFIER_TSD;
    if (tsdPtr->runLoop) {
	CFRunLoopSourceSignal(tsdPtr->runLoopSource);
	CFRunLoopWakeUp(tsdPtr->runLoop);







|







966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
 *	Signals the notifier condition variable for the specified notifier.
 *
 *----------------------------------------------------------------------
 */

void
TclpAlertNotifier(
    void *clientData)
{
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;

    LOCK_NOTIFIER_TSD;
    if (tsdPtr->runLoop) {
	CFRunLoopSourceSignal(tsdPtr->runLoopSource);
	CFRunLoopWakeUp(tsdPtr->runLoop);
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
 *
 *----------------------------------------------------------------------
 */

static void
TimerWakeUp(
    TCL_UNUSED(CFRunLoopTimerRef),
    TCL_UNUSED(ClientData))
{
}

/*
 *----------------------------------------------------------------------
 *
 * TclpServiceModeHook --







|







1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
 *
 *----------------------------------------------------------------------
 */

static void
TimerWakeUp(
    TCL_UNUSED(CFRunLoopTimerRef),
    TCL_UNUSED(void *))
{
}

/*
 *----------------------------------------------------------------------
 *
 * TclpServiceModeHook --
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
    int fd,			/* Handle of stream to watch. */
    int mask,			/* OR'ed combination of TCL_READABLE,
				 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
				 * conditions under which proc should be
				 * called. */
    Tcl_FileProc *proc,		/* Function to call for each selected
				 * event. */
    ClientData clientData)	/* Arbitrary data to pass to proc. */
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
    FileHandler *filePtr = LookUpFileHandler(tsdPtr, fd, NULL);

    if (filePtr == NULL) {
	filePtr = (FileHandler *) Tcl_Alloc(sizeof(FileHandler));
	filePtr->fd = fd;







|







1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
    int fd,			/* Handle of stream to watch. */
    int mask,			/* OR'ed combination of TCL_READABLE,
				 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
				 * conditions under which proc should be
				 * called. */
    Tcl_FileProc *proc,		/* Function to call for each selected
				 * event. */
    void *clientData)	/* Arbitrary data to pass to proc. */
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
    FileHandler *filePtr = LookUpFileHandler(tsdPtr, fd, NULL);

    if (filePtr == NULL) {
	filePtr = (FileHandler *) Tcl_Alloc(sizeof(FileHandler));
	filePtr->fd = fd;
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

ClientData
TclpNotifierData(void)
{
    return NULL;
}

/*
 *----------------------------------------------------------------------







|







1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void *
TclpNotifierData(void)
{
    return NULL;
}

/*
 *----------------------------------------------------------------------
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
 *----------------------------------------------------------------------
 */

int
TclAsyncNotifier(
    int sigNumber,		/* Signal number. */
    TCL_UNUSED(Tcl_ThreadId),	/* Target thread. */
    TCL_UNUSED(ClientData),	/* Notifier data. */
    int *flagPtr,		/* Flag to mark. */
    int value)			/* Value of mark. */
{
#if TCL_THREADS
    /*
     * WARNING:
     * This code most likely runs in a signal handler. Thus,







|







1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
 *----------------------------------------------------------------------
 */

int
TclAsyncNotifier(
    int sigNumber,		/* Signal number. */
    TCL_UNUSED(Tcl_ThreadId),	/* Target thread. */
    TCL_UNUSED(void *),	/* Notifier data. */
    int *flagPtr,		/* Flag to mark. */
    int value)			/* Value of mark. */
{
#if TCL_THREADS
    /*
     * WARNING:
     * This code most likely runs in a signal handler. Thus,
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
 *	the notifier thread first starts.
 *
 *----------------------------------------------------------------------
 */

static TCL_NORETURN void
NotifierThreadProc(
    TCL_UNUSED(ClientData))
{
    ThreadSpecificData *tsdPtr;
    fd_set readableMask, writableMask, exceptionalMask;
    int i, ret, numFdBits = 0, polling;
    struct timeval poll = {0., 0.}, *timePtr;
    char buf[2];








|







1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
 *	the notifier thread first starts.
 *
 *----------------------------------------------------------------------
 */

static TCL_NORETURN void
NotifierThreadProc(
    TCL_UNUSED(void *))
{
    ThreadSpecificData *tsdPtr;
    fd_set readableMask, writableMask, exceptionalMask;
    int i, ret, numFdBits = 0, polling;
    struct timeval poll = {0., 0.}, *timePtr;
    char buf[2];

Added tests-perf/comparePerf.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
#!/usr/bin/tclsh
# ------------------------------------------------------------------------
#
# comparePerf.tcl --
#
#  Script to compare performance data from multiple runs.
#
# ------------------------------------------------------------------------
#
# See the file "license.terms" for information on usage and redistribution
# of this file.
#
# Usage:
#   tclsh comparePerf.tcl [--regexp RE] [--ratio time|rate] [--combine] [--base BASELABEL] PERFFILE ...
#
# The test data from each input file is tabulated so as to compare the results
# of test runs. If a PERFFILE does not exist, it is retried by adding the
# .perf extension. If the --regexp is specified, only test results whose
# id matches RE are examined.
#
# If the --combine option is specified, results of test sets with the same
# label are combined and averaged in the output.
#
# If the --base option is specified, the BASELABEL is used as the label to use
# the base timing. Otherwise, the label of the first data file is used.
#
# If --ratio option is "time" the ratio of test timing vs base test timing
# is shown. If "rate" (default) the inverse is shown.
#
# If --no-header is specified, the header describing test configuration is
# not output.
#
# The format of input files is as follows:
#
# Each line must begin with one of the characters below followed by a space
# followed by a string whose semantics depend on the initial character.
# E - Full path to the Tcl executable that was used to generate the file
# V - The Tcl patchlevel of the implementation
# D - A description for the test run for human consumption
# L - A label used to identify run environment. The --combine option will
#     average all measuremets that have the same label. An input file without
#     a label is treated as having a unique label and not combined with any other.
# P - A test measurement (see below)
# R - The number of runs made for the each test
# # - A comment, may be an arbitrary string. Usually included in performance
#     data to describe the test. This is silently ignored
#
# Any lines not matching one of the above are ignored with a warning to stderr.
#
# A line beginning with the "P" marker is a test measurement. The first word
# following is a floating point number representing the test runtime.
# The remaining line (after trimming of whitespace) is the id of the test.
# Test generators are encouraged to make the id a well-defined machine-parseable
# as well human readable description of the test. The id must not appear more
# than once. An example test measurement line:
# P    2.32280 linsert in unshared L[10000] 1 elems 10000 times at 0 (var)
# Note here the iteration count is not present.
#

namespace eval perf::compare {
    # List of dictionaries, one per input file
    variable PerfData
}

proc perf::compare::warn {message} {
    puts stderr "Warning: $message"
}
proc perf::compare::print {text} {
    puts stdout $text
}
proc perf::compare::slurp {testrun_path} {
    variable PerfData

    set runtimes [dict create]

    set path [file normalize $testrun_path]
    set fd [open $path]
    array set header {}
    while {[gets $fd line] >= 0} {
        set line [regsub -all {\s+} [string trim $line] " "]
        switch -glob -- $line {
            "#*" {
                # Skip comments
            }
            "R *" -
            "L *" -
            "D *" -
            "V *" -
            "T *" -
            "E *" {
                set marker [lindex $line 0]
                if {[info exists header($marker)]} {
                    warn "Ignoring $marker record (duplicate): \"$line\""
                }
                set header($marker) [string range $line 2 end]
            }
            "P *" {
                if {[scan $line "P %f %n" runtime id_start] == 2} {
                    set id [string range $line $id_start end]
                    if {[dict exists $runtimes $id]} {
                        warn "Ignoring duplicate test id \"$id\""
                    } else {
                        dict set runtimes $id $runtime
                    }
                } else {
                    warn "Invalid test result line format: \"$line\""
                }
            }
            default {
                puts stderr "Warning: ignoring unrecognized line \"$line\""
            }
        }
    }
    close $fd

    set result [dict create Input $path Runtimes $runtimes]
    foreach {c k} {
        L Label
        V Version
        E Executable
        D Description
    } {
        if {[info exists header($c)]} {
            dict set result $k $header($c)
        }
    }

    return $result
}

proc perf::compare::burp {test_sets} {
    variable Options

    # Print the key for each test run
    set header "           "
    set separator "           "
    foreach test_set $test_sets {
        set test_set_key "\[[incr test_set_num]\]"
        if {! $Options(--no-header)} {
            print "$test_set_key"
            foreach k {Label Executable Version Input Description} {
                if {[dict exists $test_set $k]} {
                    print "$k: [dict get $test_set $k]"
                }
            }
        }
        append header $test_set_key $separator
        set separator "                 "; # Expand because later columns have ratio
    }
    set header [string trimright $header]

    if {! $Options(--no-header)} {
        print ""
        if {$Options(--ratio) eq "rate"} {
            set ratio_description "ratio of baseline to the measurement (higher is faster)."
        } else {
            set ratio_description "ratio of measurement to the baseline (lower is faster)."
        }
        print "The first column \[1\] is the baseline measurement."
        print "Subsequent columns are pairs of the additional measurement and "
        print $ratio_description
        print ""
    }

    # Print the actual test run data

    print $header
    set test_sets [lassign $test_sets base_set]
    set fmt {%#10.5f}
    set fmt_ratio {%-6.2f}
    foreach {id base_runtime} [dict get $base_set Runtimes] {
        if {[info exists Options(--regexp)]} {
            if {![regexp $Options(--regexp) $id]} {
                continue
            }
        }
        if {$Options(--print-test-number)} {
            set line "[format %-4s [incr counter].]"
        } else {
            set line ""
        }
        append line [format $fmt $base_runtime]
        foreach test_set $test_sets {
            if {[dict exists $test_set Runtimes $id]} {
                set runtime [dict get $test_set Runtimes $id]
                if {$Options(--ratio) eq "time"} {
                    if {$base_runtime != 0} {
                        set ratio [format $fmt_ratio [expr {$runtime/$base_runtime}]]
                    } else {
                        if {$runtime == 0} {
                            set ratio "NaN   "
                        } else {
                            set ratio "Inf   "
                        }
                    }
                } else {
                    if {$runtime != 0} {
                        set ratio [format $fmt_ratio [expr {$base_runtime/$runtime}]]
                    } else {
                        if {$base_runtime == 0} {
                            set ratio "NaN   "
                        } else {
                            set ratio "Inf   "
                        }
                    }
                }
                append line "|" [format $fmt $runtime] "|" $ratio
            } else {
                append line [string repeat { } 11]
            }
        }
        append line "|" $id
        print $line
    }
}

proc perf::compare::chew {test_sets} {
    variable Options

    # Combine test sets that have the same label, averaging the values
    set unlabeled_sets {}
    array set labeled_sets {}

    foreach test_set $test_sets {
        # If there is no label, treat as independent set
        if {![dict exists $test_set Label]} {
            lappend unlabeled_sets $test_set
        } else {
            lappend labeled_sets([dict get $test_set Label]) $test_set
        }
    }

    foreach label [array names labeled_sets] {
        set combined_set [lindex $labeled_sets($label) 0]
        set runtimes [dict get $combined_set Runtimes]
        foreach test_set [lrange $labeled_sets($label) 1 end] {
            dict for {id timing} [dict get $test_set Runtimes] {
                dict lappend runtimes $id $timing
            }
        }
        dict for {id timings} $runtimes {
            set total [tcl::mathop::+ {*}$timings]
            dict set runtimes $id [expr {$total/[llength $timings]}]
        }
        dict set combined_set Runtimes $runtimes
        set labeled_sets($label) $combined_set
    }

    # Choose the "base" test set
    if {![info exists Options(--base)]} {
        set first_set [lindex $test_sets 0]
        if {[dict exists $first_set Label]} {
            # Use label of first as the base
            set Options(--base) [dict get $first_set Label]
        }
    }

    if {[info exists Options(--base)] && $Options(--base) ne ""} {
        lappend combined_sets $labeled_sets($Options(--base));# Will error if no such
        unset labeled_sets($Options(--base))
    } else {
        lappend combined_sets [lindex $unlabeled_sets 0]
        set unlabeled_sets [lrange $unlabeled_sets 1 end]
    }
    foreach label [array names labeled_sets] {
        lappend combined_sets $labeled_sets($label)
    }
    lappend combined_sets {*}$unlabeled_sets

    return $combined_sets
}

proc perf::compare::setup {argv} {
    variable Options

    array set Options {
        --ratio rate
        --combine 0
        --print-test-number 0
        --no-header 0
    }
    while {[llength $argv]} {
        set argv [lassign $argv arg]
        switch -glob -- $arg {
            -r -
            --regexp {
                if {[llength $argv] == 0} {
                    error "Missing value for option $arg"
                }
                set argv [lassign $argv val]
                set Options(--regexp) $val
            }
            --ratio {
                if {[llength $argv] == 0} {
                    error "Missing value for option $arg"
                }
                set argv [lassign $argv val]
                if {$val ni {time rate}} {
                    error "Value for option $arg must be either \"time\" or \"rate\""
                }
                set Options(--ratio) $val
            }
            --print-test-number -
            --combine -
            --no-header {
                set Options($arg) 1
            }
            --base {
                if {[llength $argv] == 0} {
                    error "Missing value for option $arg"
                }
                set argv [lassign $argv val]
                set Options($arg) $val
            }
            -- {
                # Remaining will be passed back to the caller
                break
            }
            --* {
                error "Unknown option $arg"
            }
            -* {
                error "Unknown option -[lindex $arg 0]"
            }
            default {
                # Remaining will be passed back to the caller
                set argv [linsert $argv 0 $arg]
                break;
            }
        }
    }

    set paths {}
    foreach path $argv {
        set path [file join $path]; # Convert from native else glob fails
        if {[file isfile $path]} {
            lappend paths $path
            continue
        }
        if {[file isfile $path.perf]} {
            lappend paths $path.perf
            continue
        }
        lappend paths {*}[glob -nocomplain $path]
    }
    return $paths
}
proc perf::compare::main {} {
    variable Options

    set paths [setup $::argv]
    if {[llength $paths] == 0} {
        error "No test data files specified."
    }
    set test_data [list ]
    set seen [dict create]
    foreach path $paths {
        if {![dict exists $seen $path]} {
            lappend test_data [slurp $path]
            dict set seen $path ""
        }
    }

    if {$Options(--combine)} {
        set test_data [chew $test_data]
    }

    burp $test_data
}

perf::compare::main
Added tests-perf/listPerf.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
#!/usr/bin/tclsh
# ------------------------------------------------------------------------
#
# listPerf.tcl --
#
#  This file provides performance tests for list operations.
#
# ------------------------------------------------------------------------
#
# See the file "license.terms" for information on usage and redistribution
# of this file.
#
# Note: this file does not use the test-performance.tcl framework as we want
# more direct control over timerate options.

catch {package require twapi}

namespace eval perf::list {
    variable perfScript [file normalize [info script]]

    # Test for each of these lengths
    variable Lengths {10 100 1000 10000}

    variable RunTimes
    set RunTimes(command) 0.0
    set RunTimes(total) 0.0

    variable Options
    array set Options {
        --print-comments   0
        --print-iterations 0
    }

    # Procs used for calibrating overhead
    proc proc2args {a b} {}
    proc proc3args {a b c} {}

    proc print {s} {
        puts $s
    }
    proc print_usage {} {
        puts stderr "Usage: [file tail [info nameofexecutable]] $::argv0 \[options\] \[command ...\]"
        puts stderr "\t--description DESC\tHuman readable description of test run"
        puts stderr "\t--label LABEL\tA label used to identify test environment"
        puts stderr "\t--print-comments\tPrint comment for each test"
        puts stderr "\t--print-iterations\tPrint number of iterations run for each test"
    }

    proc setup {argv} {
        variable Options
        variable Lengths

        while {[llength $argv]} {
            set argv [lassign $argv arg]
            switch -glob -- $arg {
                --print-comments -
                --print-iterations {
                    set Options($arg) 1
                }
                --label -
                --description {
                    if {[llength $argv] == 0} {
                        error "Missing value for option $arg"
                    }
                    set argv [lassign $argv val]
                    set Options($arg) $val
                }
                --lengths {
                    if {[llength $argv] == 0} {
                        error "Missing value for option $arg"
                    }
                    set argv [lassign $argv val]
                    set Lengths $val
                }
                -- {
                    # Remaining will be passed back to the caller
                    break
                }
                --* {
                    error "Unknown option $arg"
                }
                default {
                    # Remaining will be passed back to the caller
                    set argv [linsert $argv 0 $arg]
                    break;
                }
            }
        }

        return $argv
    }
    proc format_timings {us iters} {
        variable Options
        if {!$Options(--print-iterations)} {
            return "[format {%#10.4f} $us]"
        }
        return "[format {%#10.4f} $us] [format {%8d} $iters]"
    }
    proc measure {id script args} {
        variable NullOverhead
        variable RunTimes
        variable Options

        set opts(-overhead) ""
        set opts(-runs) 5
        while {[llength $args]} {
            set args [lassign $args opt]
            if {[llength $args] == 0} {
                error "No argument supplied for $opt option. Test: $id"
            }
            set args [lassign $args val]
            switch $opt {
                -setup -
                -cleanup -
                -overhead -
                -time -
                -runs -
                -reps {
                    set opts($opt) $val
                }
                default {
                    error "Unknown option $opt. Test: $id"
                }
            }
        }

        set timerate_args {}
        if {[info exists opts(-time)]} {
            lappend timerate_args $opts(-time)
        }
        if {[info exists opts(-reps)]} {
            if {[info exists opts(-time)]} {
                set timerate_args [list $opts(-time) $opts(-reps)]
            } else {
                # Force the default for first time option
                set timerate_args [list 1000 $opts(-reps)]
            }
        } elseif {[info exists opts(-time)]} {
            set timerate_args [list $opts(-time)]
        }
        if {[info exists opts(-setup)]} {
            uplevel 1 $opts(-setup)
        }
        # Cache the empty overhead to prevent unnecessary delays. Note if you modify
        # to cache other scripts, the cache key must be AFTER substituting the
        # overhead script in the caller's context.
        if {$opts(-overhead) eq ""} {
            if {![info exists NullOverhead]} {
                set NullOverhead [lindex [timerate {}] 0]
            }
            set overhead_us $NullOverhead
        } else {
            # The overhead measurements might use setup so we need to setup
            # first and then cleanup in preparation for setting up again for
            # the script to be measured
            if {[info exists opts(-setup)]} {
                uplevel 1 $opts(-setup)
            }
            set overhead_us [lindex [uplevel 1 [list timerate $opts(-overhead)]] 0]
            if {[info exists opts(-cleanup)]} {
                uplevel 1 $opts(-cleanup)
            }
        }
        set timings {}
        for {set i 0} {$i < $opts(-runs)} {incr i} {
            if {[info exists opts(-setup)]} {
                uplevel 1 $opts(-setup)
            }
            lappend timings [uplevel 1 [list timerate -overhead $overhead_us $script {*}$timerate_args]]
            if {[info exists opts(-cleanup)]} {
                uplevel 1 $opts(-cleanup)
            }
        }
        set timings [lsort -real -index 0 $timings]
        if {$opts(-runs) > 15} {
            set ignore [expr {$opts(-runs)/8}]
        } elseif {$opts(-runs) >= 5} {
            set ignore 2
        } else {
            set ignore 0
        }
        # Ignore highest and lowest
        set timings [lrange $timings 0 end-$ignore]
        # Average it out
        set us 0
        set iters 0
        foreach timing $timings {
            set us [expr {$us + [lindex $timing 0]}]
            set iters [expr {$iters + [lindex $timing 2]}]
        }
        set us [expr {$us/[llength $timings]}]
        set iters [expr {$iters/[llength $timings]}]

        set RunTimes(command) [expr {$RunTimes(command) + $us}]
        print "P [format_timings $us $iters] $id"
    }
    proc comment {args} {
        variable Options
        if {$Options(--print-comments)} {
            print "# [join $args { }]"
        }
    }
    proc spanned_list {len} {
        # Note - for small len, this will not create a spanned list
        set delta [expr {$len/8}]
        return [lrange [lrepeat [expr {$len+(2*$delta)}] a] $delta [expr {$delta+$len-1}]]
    }
    proc print_separator {command} {
        comment [string repeat = 80]
        comment Command: $command
    }

    oo::class create ListPerf {
        constructor {args} {
            my variable Opts
            # Note default Opts can be overridden in construct as well as in measure
            set Opts [dict merge {
                -setup {
                    set L [lrepeat $len a]
                    set Lspan [perf::list::spanned_list $len]
                } -cleanup {
                    unset -nocomplain L
                    unset -nocomplain Lspan
                    unset -nocomplain L2
                }
            } $args]
        }
        method measure {comment script locals args} {
            my variable Opts
            dict with locals {}
            ::perf::list::measure $comment $script {*}[dict merge $Opts $args]
        }
        method option {opt val} {
            my variable Opts
            dict set Opts $opt $val
        }
        method option_unset {opt} {
            my variable Opts
            unset -nocomplain Opts($opt)
        }
    }

    proc linsert_describe {share_mode len at num iters} {
        return "linsert L\[$len\] $share_mode $num elems $iters times at $at"
    }
    proc linsert_perf {} {
        variable Lengths

        print_separator linsert

        ListPerf create perf -overhead {set L {}} -time 1000

        # Note: Const indices take different path through bytecode than variable
        # indices hence separate cases below


        # Var case
        foreach share_mode {shared unshared} {
            set idx 0
            if {$share_mode eq "shared"} {
                comment == Insert into empty lists
                comment Insert one element into empty list
                measure [linsert_describe shared 0 "0 (var)" 1 1] {linsert $L $idx ""} -setup {set idx 0; set L {}}
            } else {
                comment == Insert into empty lists
                comment Insert one element into empty list
                measure [linsert_describe unshared 0 "0 (var)" 1 1] {linsert {} $idx ""} -setup {set idx 0}
            }
            foreach idx_str [list 0 1 mid end-1 end] {
                foreach len $Lengths {
                    if {$idx_str eq "mid"} {
                        set idx [expr {$len/2}]
                    } else {
                        set idx $idx_str
                    }
                    # perf option -reps $reps
                    set reps 1000
                    if {$share_mode eq "shared"} {
                        comment Insert once to shared list with variable index
                        perf measure [linsert_describe shared $len "$idx (var)" 1 1] \
                            {linsert $L $idx x} [list len $len idx $idx] -overhead {} -reps 100000

                        comment Insert multiple times to shared list with variable index
                        perf measure [linsert_describe shared $len "$idx (var)" 1 $reps] {
                            set L [linsert $L $idx X]
                        } [list len $len idx $idx] -reps $reps

                        comment Insert multiple items multiple times to shared list with variable index
                        perf measure [linsert_describe shared $len "$idx (var)" 5 $reps] {
                            set L [linsert $L $idx X X X X X]
                        } [list len $len idx $idx] -reps $reps
                    } else {
                        # NOTE : the Insert once case is left out for unshared lists
                        # because it requires re-init on every iteration resulting
                        # in a lot of measurement noise
                        comment Insert multiple times to unshared list with variable index
                        perf measure [linsert_describe unshared $len "$idx (var)" 1 $reps] {
                            set L [linsert $L[set L {}] $idx X]
                        } [list len $len idx $idx] -reps $reps
                        comment Insert multiple items multiple times to unshared list with variable index
                        perf measure [linsert_describe unshared $len "$idx (var)" 5 $reps] {
                            set L [linsert $L[set L {}] $idx X X X X X]
                        } [list len $len idx $idx] -reps $reps
                    }
                }
            }
        }

        # Const index
        foreach share_mode {shared unshared} {
            if {$share_mode eq "shared"} {
                comment == Insert into empty lists
                comment Insert one element into empty list
                measure [linsert_describe shared 0 "0 (const)" 1 1] {linsert $L 0 ""} -setup {set L {}}
            } else {
                comment == Insert into empty lists
                comment Insert one element into empty list
                measure [linsert_describe unshared 0 "0 (const)" 1 1] {linsert {} 0 ""}
            }
            foreach idx_str [list 0 1 mid end end-1] {
                foreach len $Lengths {
                    # Note end, end-1 explicitly calculated as otherwise they
                    # are not treated as const
                    if {$idx_str eq "mid"} {
                        set idx [expr {$len/2}]
                    } elseif {$idx_str eq "end"} {
                        set idx [expr {$len-1}]
                    } elseif {$idx_str eq "end-1"} {
                        set idx [expr {$len-2}]
                    } else {
                        set idx $idx_str
                    }
                    #perf option -reps $reps
                    set reps 100
                    if {$share_mode eq "shared"} {
                        comment Insert once to shared list with const index
                        perf measure [linsert_describe shared $len "$idx (const)" 1 1] \
                            "linsert \$L $idx x" [list len $len] -overhead {} -reps 10000

                        comment Insert multiple times to shared list with const index
                        perf measure [linsert_describe shared $len "$idx (const)" 1 $reps] \
                            "set L \[linsert \$L $idx X\]" [list len $len] -reps $reps

                        comment Insert multiple items multiple times to shared list with const index
                        perf measure [linsert_describe shared $len "$idx (const)" 5 $reps] \
                            "set L \[linsert \$L $idx X X X X X\]" [list len $len] -reps $reps
                    } else {
                        comment Insert multiple times to unshared list with const index
                        perf measure [linsert_describe unshared $len "$idx (const)" 1 $reps] \
                            "set L \[linsert \$L\[set L {}\] $idx X]" [list len $len] -reps $reps

                        comment Insert multiple items multiple times to unshared list with const index
                        perf measure [linsert_describe unshared $len "$idx (const)" 5 $reps] \
                            "set L \[linsert \$L\[set L {}\] $idx X X X X X]" [list len $len] -reps $reps
                    }
                }
            }
        }

        # Note: no span tests because the inserts above will themselves create
        # spanned lists

        perf destroy
    }

    proc list_describe {len text} {
        return "list L\[$len\] $text"
    }
    proc list_perf {} {
        variable Lengths

        print_separator list

        ListPerf create perf
        foreach len $Lengths {
            set s [join [lrepeat $len x]]
            comment Create a list from a string
            perf measure [list_describe $len "from a string"] {list $s} [list s $s len $len]
        }
        foreach len $Lengths {
            comment Create a list from expansion - single list (special optimal case)
            perf measure [list_describe $len "from a {*}list"] {list {*}$L} [list len $len]
            comment Create a list from two lists - real test of expansion speed
            perf measure [list_describe $len "from a {*}list {*}list"] {list {*}$L {*}$L} [list len [expr {$len/2}]]
        }
    }

    proc lappend_describe {share_mode len num iters} {
        return "lappend L\[$len\] $share_mode $num elems $iters times"
    }
    proc lappend_perf {} {
        variable Lengths

        print_separator lappend

        ListPerf create perf -setup {set L [lrepeat [expr {$len/4}] x]}

        # Shared
        foreach len $Lengths {
            comment Append to a shared list variable multiple times
            perf measure [lappend_describe shared [expr {$len/2}] 1 $len] {
                set L2 $L; # Make shared
                lappend L x
            } [list len $len] -reps $len -overhead {set L2 $L}
        }

        # Unshared
        foreach len $Lengths {
            comment Append to a unshared list variable multiple times
            perf measure [lappend_describe unshared [expr {$len/2}] 1 $len] {
                lappend L x
            } [list len $len] -reps $len
        }

        # Span
        foreach len $Lengths {
            comment Append to a unshared-span list variable multiple times
            perf measure [lappend_describe unshared-span [expr {$len/2}] 1 $len] {
                lappend Lspan x
            } [list len $len] -reps $len
        }

        perf destroy
    }

    proc lpop_describe {share_mode len at reps} {
        return "lpop L\[$len\] $share_mode at $at $reps times"
    }
    proc lpop_perf {} {
        variable Lengths

        print_separator lpop

        ListPerf create perf

        # Shared
        perf option -overhead {set L2 $L}
        foreach len $Lengths {
            set reps [expr {($len >= 1000 ? ($len/2) : $len) - 2}]
            foreach idx {0 1 end-1 end}  {
                comment Pop element at position $idx from a shared list variable
                perf measure [lpop_describe shared $len $idx $reps] {
                    set L2 $L
                    lpop L $idx
                } [list len $len idx $idx] -reps $reps
            }
        }

        # Unshared
        perf option -overhead {}
        foreach len $Lengths {
            set reps [expr {($len >= 1000 ? ($len/2) : $len) - 2}]
            foreach idx {0 1 end-1 end}  {
                comment Pop element at position $idx from an unshared list variable
                perf measure [lpop_describe unshared $len $idx $reps] {
                    lpop L $idx
                } [list len $len idx $idx] -reps $reps
            }
        }

        perf destroy

        # Nested
        ListPerf create perf -setup {
            set L [lrepeat $len [list a b]]
        }

        # Shared, nested index
        perf option -overhead {set L2 $L; set L L2}
        foreach len $Lengths {
            set reps [expr {($len >= 1000 ? ($len/2) : $len) - 2}]
            foreach idx {0 1 end-1 end}  {
                perf measure [lpop_describe shared $len "{$idx 0}" $reps] {
                    set L2 $L
                    lpop L $idx 0
                    set L $L2
                } [list len $len idx $idx] -reps $reps
            }
        }

        # TODO - Nested Unshared
        # Not sure how to measure performance. When unshared there is no copy
        # so deleting a nested index repeatedly is not feasible

        perf destroy
    }

    proc lassign_describe {share_mode len num reps} {
        return "lassign L\[$len\] $share_mode $num elems $reps times"
    }
    proc lassign_perf {} {
        variable Lengths

        print_separator lassign

        ListPerf create perf

        foreach share_mode {shared unshared} {
            foreach len $Lengths {
                if {$share_mode eq "shared"} {
                    set reps 1000
                    comment Reflexive lassign - shared
                    perf measure [lassign_describe shared $len 1 $reps] {
                        set L2 $L
                        set L2 [lassign $L2 v]
                    } [list len $len] -overhead {set L2 $L} -reps $reps

                    comment Reflexive lassign - shared, multiple
                    perf measure [lassign_describe shared $len 5 $reps] {
                        set L2 $L
                        set L2 [lassign $L2 a b c d e]
                    } [list len $len] -overhead {set L2 $L} -reps $reps
                } else {
                    set reps [expr {($len >= 1000 ? ($len/2) : $len) - 2}]
                    comment Reflexive lassign - unshared
                    perf measure [lassign_describe unshared $len 1 $reps] {
                        set L [lassign $L v]
                    } [list len $len] -reps $reps
                }
            }
        }
        perf destroy
    }

    proc lrepeat_describe {len num} {
        return "lrepeat L\[$len\] $num elems at a time"
    }
    proc lrepeat_perf {} {
        variable Lengths

        print_separator lrepeat

        ListPerf create perf -reps 100000
        foreach len $Lengths {
            comment Generate a list from a single repeated element
            perf measure [lrepeat_describe $len 1] {
                lrepeat $len a
            } [list len $len]

            comment Generate a list from multiple repeated elements
            perf measure [lrepeat_describe $len 5] {
                lrepeat $len a b c d e
            } [list len $len]
        }

        perf destroy
    }

    proc lreverse_describe {share_mode len} {
        return "lreverse L\[$len\] $share_mode"
    }
    proc lreverse_perf {} {
        variable Lengths

        print_separator lreverse

        ListPerf create perf -reps 10000

        foreach share_mode {shared unshared} {
            foreach len $Lengths {
                if {$share_mode eq "shared"} {
                    comment Reverse a shared list
                    perf measure [lreverse_describe shared $len] {
                        lreverse $L
                    } [list len $len]

                    if {$len > 100} {
                        comment Reverse a shared-span list
                        perf measure [lreverse_describe shared-span $len] {
                            lreverse $Lspan
                        } [list len $len]
                    }
                } else {
                    comment Reverse a unshared list
                    perf measure [lreverse_describe unshared $len] {
                        set L [lreverse $L[set L {}]]
                    } [list len $len] -overhead {set L $L; set L {}}

                    if {$len >= 100} {
                        comment Reverse a unshared-span list
                        perf measure [lreverse_describe unshared-span $len] {
                            set Lspan [lreverse $Lspan[set Lspan {}]]
                        } [list len $len] -overhead {set Lspan $Lspan; set Lspan {}}
                    }
                }
            }
        }

        perf destroy
    }

    proc llength_describe {share_mode len} {
        return "llength L\[$len\] $share_mode"
    }
    proc llength_perf {} {
        variable Lengths

        print_separator llength

        ListPerf create perf -reps 100000

        foreach len $Lengths {
            comment Length of a list
            perf measure [llength_describe shared $len] {
                llength $L
            } [list len $len]

            if {$len >= 100} {
                comment Length of a span list
                perf measure [llength_describe shared-span $len] {
                    llength $Lspan
                } [list len $len]
            }
        }

        perf destroy
    }

    proc lindex_describe {share_mode len at} {
        return "lindex L\[$len\] $share_mode at $at"
    }
    proc lindex_perf {} {
        variable Lengths

        print_separator lindex

        ListPerf create perf -reps 100000

        foreach len $Lengths {
            comment Index into a list
            set idx [expr {$len/2}]
            perf measure [lindex_describe shared $len $idx] {
                lindex $L $idx
            } [list len $len idx $idx]

            if {$len >= 100} {
                comment Index into a span list
                perf measure [lindex_describe shared-span $len $idx] {
                    lindex $Lspan $idx
                } [list len $len idx $idx]
            }
        }

        perf destroy
    }

    proc lrange_describe {share_mode len range} {
        return "lrange L\[$len\] $share_mode range $range"
    }

    proc lrange_perf {} {
        variable Lengths

        print_separator lrange

        ListPerf create perf -time 1000 -reps 100000

        foreach share_mode {shared unshared} {
            foreach len $Lengths {
                set eighth [expr {$len/8}]
                set ranges [list \
                                [list 0 0]  [list 0 end-1] \
                                [list $eighth [expr {3*$eighth}]] \
                                [list $eighth [expr {7*$eighth}]] \
                                [list 1 end] [list end-1 end] \
                               ]
                foreach range $ranges {
                    comment Range $range in $share_mode list of length $len
                    if {$share_mode eq "shared"} {
                        perf measure [lrange_describe shared $len $range] \
                            "lrange \$L $range" [list len $len range $range]
                    } else {
                        perf measure [lrange_describe unshared $len $range] \
                            "lrange \[lrepeat \$len\ a] $range" \
                            [list len $len range $range] -overhead {lrepeat $len a}
                    }
                }

                if {$len >= 100} {
                    foreach range $ranges {
                        comment Range $range in ${share_mode}-span list of length $len
                        if {$share_mode eq "shared"} {
                            perf measure [lrange_describe shared-span $len $range] \
                                "lrange \$Lspan {*}$range" [list len $len range $range]
                        } else {
                            perf measure [lrange_describe unshared-span $len $range] \
                                "lrange \[perf::list::spanned_list \$len\] $range" \
                                [list len $len range $range] -overhead {perf::list::spanned_list $len}
                        }
                    }
                }
            }
        }

        perf destroy
    }

    proc lset_describe {share_mode len at} {
        return "lset L\[$len\] $share_mode at $at"
    }
    proc lset_perf {} {
        variable Lengths

        print_separator lset

        ListPerf create perf -reps 10000

        # Shared
        foreach share_mode {shared unshared} {
            foreach len $Lengths {
                foreach idx {0 1 end-1 end end+1}  {
                    comment lset at position $idx in a $share_mode list variable
                    if {$share_mode eq "shared"} {
                        perf measure [lset_describe shared $len $idx] {
                            set L2 $L
                            lset L $idx X
                        } [list len $len idx $idx] -overhead {set L2 $L}
                    } else {
                        perf measure [lset_describe unshared $len $idx] {
                            lset L $idx X
                        } [list len $len idx $idx]
                    }
                }
            }
        }

        perf destroy

        # Nested
        ListPerf create perf -setup {
            set L [lrepeat $len [list a b]]
        }

        foreach share_mode {shared unshared} {
            foreach len $Lengths {
                foreach idx {0 1 end-1 end}  {
                    comment lset at position $idx in a $share_mode list variable
                    if {$share_mode eq "shared"} {
                        perf measure [lset_describe shared $len "{$idx 0}"] {
                            set L2 $L
                            lset L $idx 0 X
                        } [list len $len idx $idx] -overhead {set L2 $L}
                    } else {
                        perf measure [lset_describe unshared $len "{$idx 0}"] {
                            lset L $idx 0 {X Y}
                        } [list len $len idx $idx]
                    }
                }
            }
        }

        perf destroy
    }

    proc lremove_describe {share_mode len at nremoved} {
        return "lremove L\[$len\] $share_mode $nremoved elements at $at"
    }
    proc lremove_perf {} {
        variable Lengths

        print_separator lremove

        ListPerf create perf -reps 10000

        foreach share_mode {shared unshared} {
            foreach len $Lengths {
                foreach idx [list 0 1 [expr {$len/2}] end-1 end] {
                    if {$share_mode eq "shared"} {
                        comment Remove one element from shared list
                        perf measure [lremove_describe shared $len $idx 1] \
                            {lremove $L $idx} [list len $len idx $idx]

                    } else {
                        comment Remove one element from unshared list
                        set reps [expr {$len >= 1000 ? ($len/8) : ($len-2)}]
                        perf measure [lremove_describe unshared $len $idx 1] \
                            {set L [lremove $L[set L {}] $idx]} [list len $len idx $idx] \
                            -overhead {set L $L; set L {}} -reps $reps
                    }
                }
                if {$share_mode eq "shared"} {
                    comment Remove multiple elements from shared list
                    perf measure [lremove_describe shared $len [list 0 1 [expr {$len/2}] end-1 end] 5] {
                        lremove $L 0 1 [expr {$len/2}] end-1 end
                    } [list len $len]
                }
            }
            # Span
            foreach len $Lengths {
                foreach idx [list 0 1 [expr {$len/2}] end-1 end] {
                    if {$share_mode eq "shared"} {
                        comment Remove one element from shared-span list
                        perf measure [lremove_describe shared-span $len $idx 1] \
                            {lremove $Lspan $idx} [list len $len idx $idx]
                    } else {
                        comment Remove one element from unshared-span list
                        set reps [expr {$len >= 1000 ? ($len/8) : ($len-2)}]
                        perf measure [lremove_describe unshared-span $len $idx 1] \
                            {set Lspan [lremove $Lspan[set Lspan {}] $idx]} [list len $len idx $idx] \
                            -overhead {set Lspan $Lspan; set Lspan {}} -reps $reps
                    }
                }
                if {$share_mode eq "shared"} {
                    comment Remove multiple elements from shared-span list
                    perf measure [lremove_describe shared-span $len [list 0 1 [expr {$len/2}] end-1 end] 5] {
                        lremove $Lspan 0 1 [expr {$len/2}] end-1 end
                    } [list len $len]
                }
            }
        }

        perf destroy
    }

    proc lreplace_describe {share_mode len first last ninsert {times 1}} {
        if {$last < $first} {
            return "lreplace L\[$len\] $share_mode 0 ($first:$last) elems at $first with $ninsert elems $times times."
        }
        return "lreplace L\[$len\] $share_mode $first:$last with $ninsert elems $times times."
    }
    proc lreplace_perf {} {
        variable Lengths

        print_separator lreplace

        set default_reps 10000
        ListPerf create perf -reps $default_reps

        foreach share_mode {shared unshared} {
            # Insert only
            foreach len $Lengths {
                set reps [expr {$len <= 100 ? ($len-2) : ($len/8)}]
                foreach first [list 0 1 [expr {$len/2}] end-1 end] {
                    if {$share_mode eq "shared"} {
                        comment Insert one to shared list
                        perf measure [lreplace_describe shared $len $first -1 1] {
                            lreplace $L $first -1 x
                        } [list len $len first $first]

                        comment Insert multiple to shared list
                        perf measure [lreplace_describe shared $len $first -1 10] {
                            lreplace $L $first -1 X X X X X X X X X X
                        } [list len $len first $first]

                        comment Insert one to shared list repeatedly
                        perf measure [lreplace_describe shared $len $first -1 1 $reps] {
                            set L [lreplace $L $first -1 x]
                        } [list len $len first $first] -reps $reps

                        comment Insert multiple to shared list repeatedly
                        perf measure [lreplace_describe shared $len $first -1 10 $reps] {
                            set L [lreplace $L $first -1 X X X X X X X X X X]
                        } [list len $len first $first] -reps $reps

                    } else {
                        comment Insert one to unshared list
                        perf measure [lreplace_describe unshared $len $first -1 1] {
                            set L [lreplace $L[set L {}] $first -1 x]
                        } [list len $len first $first] -overhead {
                            set L $L; set L {}
                        } -reps $reps

                        comment Insert multiple to unshared list
                        perf measure [lreplace_describe unshared $len $first -1 10] {
                            set L [lreplace $L[set L {}] $first -1 X X X X X X X X X X]
                        } [list len $len first $first] -overhead {
                            set L $L; set L {}
                        } -reps $reps
                    }
                }
            }

            # Delete only
            foreach len $Lengths {
                set reps [expr {$len <= 100 ? ($len-2) : ($len/8)}]
                foreach first [list 0 1 [expr {$len/2}] end-1 end] {
                    if {$share_mode eq "shared"} {
                        comment Delete one from shared list
                        perf measure [lreplace_describe shared $len $first $first 0] {
                            lreplace $L $first $first
                        } [list len $len first $first]
                    } else {
                        comment Delete one from unshared list
                        perf measure [lreplace_describe unshared $len $first $first 0] {
                            set L [lreplace $L[set L {}] $first $first x]
                        } [list len $len first $first] -overhead {
                            set L $L; set L {}
                        } -reps $reps
                    }
                }
            }

            # Insert + delete
            foreach len $Lengths {
                set reps [expr {$len <= 100 ? ($len-2) : ($len/8)}]
                foreach range [list {0 1} {1 2} {end-2 end-1} {end-1 end}] {
                    lassign $range first last
                    if {$share_mode eq "shared"} {
                        comment Insertions more than deletions from shared list
                        perf measure [lreplace_describe shared $len $first $last 3] {
                            lreplace $L $first $last X Y Z
                        } [list len $len first $first last $last]

                        comment Insertions same as deletions from shared list
                        perf measure [lreplace_describe shared $len $first $last 2] {
                            lreplace $L $first $last X Y
                        } [list len $len first $first last $last]

                        comment Insertions fewer than deletions from shared list
                        perf measure [lreplace_describe shared $len $first $last 1] {
                            lreplace $L $first $last X
                        } [list len $len first $first last $last]
                    } else {
                        comment Insertions more than deletions from unshared list
                        perf measure [lreplace_describe unshared $len $first $last 3] {
                            set L [lreplace $L[set L {}] $first $last X Y Z]
                        } [list len $len first $first last $last] -overhead {
                            set L $L; set L {}
                        } -reps $reps

                        comment Insertions same as deletions from unshared list
                        perf measure [lreplace_describe unshared $len $first $last 2] {
                            set L [lreplace $L[set L {}] $first $last X Y ]
                        } [list len $len first $first last $last] -overhead {
                            set L $L; set L {}
                        } -reps $reps

                        comment Insertions fewer than deletions from unshared list
                        perf measure [lreplace_describe unshared $len $first $last 1] {
                            set L [lreplace $L[set L {}] $first $last X]
                        } [list len $len first $first last $last] -overhead {
                            set L $L; set L {}
                        } -reps $reps
                    }
                }
            }
            # Spanned Insert + delete
            foreach len $Lengths {
                set reps [expr {$len <= 100 ? ($len-2) : ($len/8)}]
                foreach range [list {0 1} {1 2} {end-2 end-1} {end-1 end}] {
                    lassign $range first last
                    if {$share_mode eq "shared"} {
                        comment Insertions more than deletions from shared-span list
                        perf measure [lreplace_describe shared-span $len $first $last 3] {
                            lreplace $Lspan $first $last X Y Z
                        } [list len $len first $first last $last]

                        comment Insertions same as deletions from shared-span list
                        perf measure [lreplace_describe shared-span $len $first $last 2] {
                            lreplace $Lspan $first $last X Y
                        } [list len $len first $first last $last]

                        comment Insertions fewer than deletions from shared-span list
                        perf measure [lreplace_describe shared-span $len $first $last 1] {
                            lreplace $Lspan $first $last X
                        } [list len $len first $first last $last]
                    } else {
                        comment Insertions more than deletions from unshared-span list
                        perf measure [lreplace_describe unshared-span $len $first $last 3] {
                            set Lspan [lreplace $Lspan[set Lspan {}] $first $last X Y Z]
                        } [list len $len first $first last $last] -overhead {
                            set Lspan $Lspan; set Lspan {}
                        } -reps $reps

                        comment Insertions same as deletions from unshared-span list
                        perf measure [lreplace_describe unshared-span $len $first $last 2] {
                            set Lspan [lreplace $Lspan[set Lspan {}] $first $last X Y ]
                        } [list len $len first $first last $last] -overhead {
                            set Lspan $Lspan; set Lspan {}
                        } -reps $reps

                        comment Insertions fewer than deletions from unshared-span list
                        perf measure [lreplace_describe unshared-span $len $first $last 1] {
                            set Lspan [lreplace $Lspan[set Lspan {}] $first $last X]
                        } [list len $len first $first last $last] -overhead {
                            set Lspan $Lspan; set Lspan {}
                        } -reps $reps
                    }
                }
            }
        }

        perf destroy
    }

    proc split_describe {len} {
        return "split L\[$len\]"
    }
    proc split_perf {} {
        variable Lengths
        print_separator split

        ListPerf create perf -setup {set S [string repeat "x " $len]}
        foreach len $Lengths {
            comment Split a string
            perf measure [split_describe $len] {
                split $S " "
            } [list len $len]
        }
    }

    proc join_describe {share_mode len} {
        return "join L\[$len\] $share_mode"
    }
    proc join_perf {} {
        variable Lengths

        print_separator join

        ListPerf create perf -reps 10000
        foreach len $Lengths {
            comment Join a list
            perf measure [join_describe shared $len] {
                join $L
            } [list len $len]
        }
        foreach len $Lengths {
            comment Join a spanned list
            perf measure [join_describe shared-span $len] {
                join $Lspan
            } [list len $len]
        }
        perf destroy
    }

    proc lsearch_describe {share_mode len} {
        return "lsearch L\[$len\] $share_mode"
    }
    proc lsearch_perf {} {
        variable Lengths

        print_separator lsearch

        ListPerf create perf -reps 100000
        foreach len $Lengths {
            comment Search a list
            perf measure [lsearch_describe shared $len] {
                lsearch $L needle
            } [list len $len]
        }
        foreach len $Lengths {
            comment Search a spanned list
            perf measure [lsearch_describe shared-span $len] {
                lsearch $Lspan needle
            } [list len $len]
        }
        perf destroy
    }

    proc foreach_describe {share_mode len} {
        return "foreach L\[$len\] $share_mode"
    }
    proc foreach_perf {} {
        variable Lengths

        print_separator foreach

        ListPerf create perf -reps 10000
        foreach len $Lengths {
            comment Iterate through a list
            perf measure [foreach_describe shared $len] {
                foreach e $L {}
            } [list len $len]
        }
        foreach len $Lengths {
            comment Iterate a spanned list
            perf measure [foreach_describe shared-span $len] {
                foreach e $Lspan {}
            } [list len $len]
        }
        perf destroy
    }

    proc lmap_describe {share_mode len} {
        return "lmap L\[$len\] $share_mode"
    }
    proc lmap_perf {} {
        variable Lengths

        print_separator lmap

        ListPerf create perf -reps 10000
        foreach len $Lengths {
            comment Iterate through a list
            perf measure [lmap_describe shared $len] {
                lmap e $L {}
            } [list len $len]
        }
        foreach len $Lengths {
            comment Iterate a spanned list
            perf measure [lmap_describe shared-span $len] {
                lmap e $Lspan {}
            } [list len $len]
        }
        perf destroy
    }

    proc get_sort_sample {{spanned 0}} {
        variable perfScript
        variable sortSampleText

        if {![info exists sortSampleText]} {
            set fd [open $perfScript]
            set sortSampleText [split [read $fd] ""]
            close $fd
        }
        set sortSampleText [string range $sortSampleText 0 9999]

        # NOTE: do NOT cache list result in a variable as we need it unshared
        if {$spanned} {
            return [lrange [split $sortSampleText ""] 1 end-1]
        } else {
            return [split $sortSampleText ""]
        }
    }
    proc lsort_describe {share_mode len} {
        return "lsort L\[$len] $share_mode"
    }
    proc lsort_perf {} {
        print_separator lsort

        ListPerf create perf -setup {}

        comment Sort a shared list
        perf measure [lsort_describe shared [llength [perf::list::get_sort_sample]]] {
            lsort $L
        } {} -setup {set L [perf::list::get_sort_sample]}

        comment Sort a shared-span list
        perf measure [lsort_describe shared-span [llength [perf::list::get_sort_sample 1]]] {
            lsort $L
        } {} -setup {set L [perf::list::get_sort_sample 1]}

        comment Sort an unshared list
        perf measure [lsort_describe unshared [llength [perf::list::get_sort_sample]]] {
            lsort [perf::list::get_sort_sample]
        } {} -overhead {perf::list::get_sort_sample}

        comment Sort an unshared-span list
        perf measure [lsort_describe unshared-span [llength [perf::list::get_sort_sample 1]]] {
            lsort [perf::list::get_sort_sample 1]
        } {} -overhead {perf::list::get_sort_sample 1}

        perf destroy
    }

    proc concat_describe {canonicality len elemlen} {
        return "concat L\[$len\] $canonicality with elements of length $elemlen"
    }
    proc concat_perf {} {
        variable Lengths

        print_separator concat

        ListPerf create perf -reps 100000

        foreach len $Lengths {
            foreach elemlen {1 100} {
                comment Pure lists (no string representation)
                perf measure [concat_describe "pure lists" $len $elemlen] {
                    concat $L $L
                } [list len $len elemlen $elemlen] -setup {
                    set L [lrepeat $len [string repeat a $elemlen]]
                }

                comment Canonical lists (with string representation)
                perf measure [concat_describe "canonical lists" $len $elemlen] {
                    concat $L $L
                } [list len $len elemlen $elemlen] -setup {
                    set L [lrepeat $len [string repeat a $elemlen]]
                    append x x $L; # Generate string while keeping internal rep list
                    unset x
                }

                comment Non-canonical lists
                perf measure [concat_describe "non-canonical lists" $len $elemlen] {
                    concat $L $L
                } [list len $len elemlen $elemlen] -setup {
                    set L [string repeat "[string repeat a $elemlen] " $len]
                    llength $L
                }
            }
        }

        # Span version
        foreach len $Lengths {
            foreach elemlen {1 100} {
                comment Pure span lists (no string representation)
                perf measure [concat_describe "pure spanned lists" $len $elemlen] {
                    concat $L $L
                } [list len $len elemlen $elemlen] -setup {
                    set L [lrange [lrepeat [expr {$len+2}] [string repeat a $elemlen]] 1 end-1]
                }

                comment Canonical span lists (with string representation)
                perf measure [concat_describe "canonical spanned lists" $len $elemlen] {
                    concat $L $L
                } [list len $len elemlen $elemlen] -setup {
                    set L [lrange [lrepeat [expr {$len+2}] [string repeat a $elemlen]] 1 end-1]
                    append x x $L; # Generate string while keeping internal rep list
                    unset x
                }
            }
        }

        perf destroy
    }

    proc test {} {
        variable RunTimes
        variable Options

        set selections [perf::list::setup $::argv]
        if {[llength $selections] == 0} {
            set commands [info commands ::perf::list::*_perf]
        } else {
            set commands [lmap sel $selections {
                if {$sel eq "help"} {
                    print_usage
                    continue
                }
                set cmd ::perf::list::${sel}_perf
                if {$cmd ni [info commands ::perf::list::*_perf]} {
                    puts stderr "Error: command $sel is not known or supported. Skipping."
                    continue
                }
                set cmd
            }]
        }
        comment Setting up
        timerate -calibrate {}
        if {[info exists Options(--label)]} {
            print "L $Options(--label)"
        }
        print "V [info patchlevel]"
        print "E [info nameofexecutable]"
        if {[info exists Options(--description)]} {
            print "D $Options(--description)"
        }
        set twapi_keys {-privatebytes -workingset -workingsetpeak}
        if {[info commands ::twapi::get_process_memory_info] ne ""} {
            set twapi_vm_pre [::twapi::get_process_memory_info]
        }
        foreach cmd [lsort -dictionary $commands] {
            set RunTimes(command) 0.0
            $cmd
            set RunTimes(total) [expr {$RunTimes(total)+$RunTimes(command)}]
            print "P [format_timings $RunTimes(command) 1] [string range $cmd 14 end-5] total run time"
        }
        # Print total runtime in same format as timerate output
        print "P [format_timings $RunTimes(total) 1] Total run time"

        if {[info exists twapi_vm_pre]} {
            set twapi_vm_post [::twapi::get_process_memory_info]
            set MB 1048576.0
            foreach key $twapi_keys {
                set pre [expr {[dict get $twapi_vm_pre $key]/$MB}]
                set post [expr {[dict get $twapi_vm_post $key]/$MB}]
                print "P [format_timings $pre 1] Memory (MB) $key pre-test"
                print "P [format_timings $post 1] Memory (MB) $key post-test"
                print "P [format_timings [expr {$post-$pre}] 1] Memory (MB) delta $key"
            }
        }
        if {[info commands memory] ne ""} {
            foreach line [split [memory info] \n] {
                if {$line eq ""} continue
                set line [split $line]
                set val [expr {[lindex $line end]/1000.0}]
                set line [string trim [join [lrange $line 0 end-1]]]
                print "P [format_timings $val 1] memdbg $line (in thousands)"
            }
            print "# Allocations not freed on exit written to the lost-memory.tmp file."
            print "# These will have to be manually compared."
            # env TCL_FINALIZE_ON_EXIT must be set to 1 for this.
            # DO NOT SET HERE - set ::env(TCL_FINALIZE_ON_EXIT) 1
            # Must be set in environment before starting tclsh else bogus results
            if {[info exists Options(--label)]} {
                set dump_file list-memory-$Options(--label).memdmp
            } else {
                set dump_file list-memory-[pid].memdmp
            }
            memory onexit $dump_file
        }
    }
}


if {[info exists ::argv0] && [file tail $::argv0] eq [file tail [info script]]} {
    ::perf::list::test
}
Changes to tests/chan.test.
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
    chan configure stdout -eofchar Ā
} -returnCodes error -match glob -result {bad value*}
test chan-4.3 {chan command: [Bug 800753]} -body {
    chan configure stdout -eofchar \x00
} -returnCodes error -match glob -result {bad value*}
test chan-4.4 {chan command: check valid inValue, no outValue} -body {
    chan configure stdout -eofchar [list \x27 {}]
} -returnCodes ok -result {}
test chan-4.5 {chan command: check valid inValue, invalid outValue} -body {
    chan configure stdout -eofchar [list \x27 \x80]
} -returnCodes error -match glob -result {bad value for -eofchar:*}
test chan-4.6 {chan command: check no inValue, valid outValue} -body {
    chan configure stdout -eofchar [list {} \x27]
} -returnCodes ok -result {} -cleanup {chan configure stdout -eofchar [list {} {}]}

test chan-5.1 {chan command: copy subcommand} -body {
    chan copy foo
} -returnCodes error -result "wrong # args: should be \"chan copy input output ?-size size? ?-command callback?\""

test chan-6.1 {chan command: eof subcommand} -body {
    chan eof foo bar







|





|







51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
    chan configure stdout -eofchar Ā
} -returnCodes error -match glob -result {bad value*}
test chan-4.3 {chan command: [Bug 800753]} -body {
    chan configure stdout -eofchar \x00
} -returnCodes error -match glob -result {bad value*}
test chan-4.4 {chan command: check valid inValue, no outValue} -body {
    chan configure stdout -eofchar [list \x27 {}]
} -result {}
test chan-4.5 {chan command: check valid inValue, invalid outValue} -body {
    chan configure stdout -eofchar [list \x27 \x80]
} -returnCodes error -match glob -result {bad value for -eofchar:*}
test chan-4.6 {chan command: check no inValue, valid outValue} -body {
    chan configure stdout -eofchar [list {} \x27]
} -result {} -cleanup {chan configure stdout -eofchar [list {} {}]}

test chan-5.1 {chan command: copy subcommand} -body {
    chan copy foo
} -returnCodes error -result "wrong # args: should be \"chan copy input output ?-size size? ?-command callback?\""

test chan-6.1 {chan command: eof subcommand} -body {
    chan eof foo bar
Changes to tests/chanio.test.
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
    testConstraint largefileSupport [expr {$::tcl_platform(os) ne "Darwin"}]

    # some tests can only be run is umask is 2 if "umask" cannot be run, the
    # tests will be skipped.
    set umaskValue 0
    testConstraint umask [expr {![catch {set umaskValue [scan [exec /bin/sh -c umask] %o]}]}]

    testConstraint makeFileInHome [expr {![file exists ~/_test_] && [file writable ~]}]

    # set up a long data file for some of the following tests

    set path(longfile) [makeFile {} longfile]
    set f [open $path(longfile) w]
    chan configure $f -eofchar {} -translation lf
    for { set i 0 } { $i < 100 } { incr i} {







|







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
    testConstraint largefileSupport [expr {$::tcl_platform(os) ne "Darwin"}]

    # some tests can only be run is umask is 2 if "umask" cannot be run, the
    # tests will be skipped.
    set umaskValue 0
    testConstraint umask [expr {![catch {set umaskValue [scan [exec /bin/sh -c umask] %o]}]}]

    testConstraint makeFileInHome [expr {![file exists $::env(HOME)/_test_] && [file writable $::env(HOME)]}]

    # set up a long data file for some of the following tests

    set path(longfile) [makeFile {} longfile]
    set f [open $path(longfile) w]
    chan configure $f -eofchar {} -translation lf
    for { set i 0 } { $i < 100 } { incr i} {
5484
5485
5486
5487
5488
5489
5490
5491
5492

5493

5494
5495
5496


5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
    set f [open $path(test3) RDWR]
    chan puts -nonewline $f "ab"
    chan seek $f 0 current
    set x [chan gets $f]
    chan close $f
    lappend x [viewFile test3]
} {zzy abzzy}
test chan-io-40.16 {tilde substitution in open} -constraints makeFileInHome -setup {
    makeFile {Some text} _test_ ~

} -body {

    file exists [file join $::env(HOME) _test_]
} -cleanup {
    removeFile _test_ ~


} -result 1
test chan-io-40.17 {tilde substitution in open} -setup {
    set home $::env(HOME)
} -body {
    unset ::env(HOME)
    open ~/foo
} -returnCodes error -cleanup {
    set ::env(HOME) $home
} -result {couldn't find HOME environment variable to expand path}

test chan-io-41.1 {Tcl_FileeventCmd: errors} -constraints fileevent -body {
    chan event foo
} -returnCodes error -result {wrong # args: should be "chan event channelId event ?script?"}
test chan-io-41.2 {Tcl_FileeventCmd: errors} -constraints fileevent -body {
    chan event foo bar baz q
} -returnCodes error -result {wrong # args: should be "chan event channelId event ?script?"}







|
|
>

>
|

<
>
>

<
<
<
<
<
<
<
<







5484
5485
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497

5498
5499
5500








5501
5502
5503
5504
5505
5506
5507
    set f [open $path(test3) RDWR]
    chan puts -nonewline $f "ab"
    chan seek $f 0 current
    set x [chan gets $f]
    chan close $f
    lappend x [viewFile test3]
} {zzy abzzy}
test chan-io-40.16 {verify no tilde substitution in open} -setup {
    set curdir [pwd]
    cd [temporaryDirectory]
} -body {
    close [open ~ w]
    list [file isfile ~]
} -cleanup {

    file delete ./~ ;# ./ because don't want to delete home in case of bugs!
    cd $curdir
} -result 1









test chan-io-41.1 {Tcl_FileeventCmd: errors} -constraints fileevent -body {
    chan event foo
} -returnCodes error -result {wrong # args: should be "chan event channelId event ?script?"}
test chan-io-41.2 {Tcl_FileeventCmd: errors} -constraints fileevent -body {
    chan event foo bar baz q
} -returnCodes error -result {wrong # args: should be "chan event channelId event ?script?"}
Changes to tests/cmdAH.test.
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
    set oldpwd [pwd]
    set temp $env(HOME)
    file delete -force $foodir
} -body {
    set env(HOME) $oldpwd
    file mkdir $foodir
    cd $foodir
    cd ~
    string equal [pwd] $oldpwd
} -cleanup {
    cd $oldpwd
    file delete $foodir
    set env(HOME) $temp
} -result 1
test cmdAH-2.4 {Tcl_CdObjCmd} -setup {







|







96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
    set oldpwd [pwd]
    set temp $env(HOME)
    file delete -force $foodir
} -body {
    set env(HOME) $oldpwd
    file mkdir $foodir
    cd $foodir
    cd [file home]
    string equal [pwd] $oldpwd
} -cleanup {
    cd $oldpwd
    file delete $foodir
    set env(HOME) $temp
} -result 1
test cmdAH-2.4 {Tcl_CdObjCmd} -setup {
120
121
122
123
124
125
126
127













128
129
130
131
132
133
134
135
    string equal [pwd] $oldpwd
} -cleanup {
    cd $oldpwd
    file delete $foodir
    set env(HOME) $temp
} -result 1
test cmdAH-2.5 {Tcl_CdObjCmd} -returnCodes error -body {
    cd ~~













} -result {user "~" doesn't exist}
test cmdAH-2.6 {Tcl_CdObjCmd} -returnCodes error -body {
    cd _foobar
} -result {couldn't change working directory to "_foobar": no such file or directory}
test cmdAH-2.6.1 {Tcl_CdObjCmd} -returnCodes error -body {
    cd ""
} -result {couldn't change working directory to "": no such file or directory}
test cmdAH-2.6.2 {cd} -constraints {unix nonPortable} -setup {







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







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
    string equal [pwd] $oldpwd
} -cleanup {
    cd $oldpwd
    file delete $foodir
    set env(HOME) $temp
} -result 1
test cmdAH-2.5 {Tcl_CdObjCmd} -returnCodes error -body {
    cd ~
} -result {couldn't change working directory to "~": no such file or directory}
test cmdAH-2.5.1 {Tcl_CdObjCmd} -setup {
    set oldpwd [pwd]
    cd [temporaryDirectory]
    file delete ./~
    file mkdir ~
} -body {
    cd ~
    pwd
} -cleanup {
    cd [temporaryDirectory]
    file delete ./~
    cd $oldpwd
} -result [file join [temporaryDirectory] ~]
test cmdAH-2.6 {Tcl_CdObjCmd} -returnCodes error -body {
    cd _foobar
} -result {couldn't change working directory to "_foobar": no such file or directory}
test cmdAH-2.6.1 {Tcl_CdObjCmd} -returnCodes error -body {
    cd ""
} -result {couldn't change working directory to "": no such file or directory}
test cmdAH-2.6.2 {cd} -constraints {unix nonPortable} -setup {
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
}

test cmdAH-5.1 {Tcl_FileObjCmd} -returnCodes error -body {
    file
} -result {wrong # args: should be "file subcommand ?arg ...?"}
test cmdAH-5.2 {Tcl_FileObjCmd} -returnCodes error -body {
    file x
} -result {unknown or ambiguous subcommand "x": must be atime, attributes, channels, copy, delete, dirname, executable, exists, extension, isdirectory, isfile, join, link, lstat, mkdir, mtime, nativename, normalize, owned, pathtype, readable, readlink, rename, rootname, separator, size, split, stat, system, tail, tempdir, tempfile, type, volumes, or writable}
test cmdAH-5.3 {Tcl_FileObjCmd} -returnCodes error -body {
    file exists
} -result {wrong # args: should be "file exists name"}
test cmdAH-5.4 {Tcl_FileObjCmd} {
    file exists ""
} 0








|







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

test cmdAH-5.1 {Tcl_FileObjCmd} -returnCodes error -body {
    file
} -result {wrong # args: should be "file subcommand ?arg ...?"}
test cmdAH-5.2 {Tcl_FileObjCmd} -returnCodes error -body {
    file x
} -result {unknown or ambiguous subcommand "x": must be atime, attributes, channels, copy, delete, dirname, executable, exists, extension, home, isdirectory, isfile, join, link, lstat, mkdir, mtime, nativename, normalize, owned, pathtype, readable, readlink, rename, rootname, separator, size, split, stat, system, tail, tempdir, tempfile, tildeexpand, type, volumes, or writable}
test cmdAH-5.3 {Tcl_FileObjCmd} -returnCodes error -body {
    file exists
} -result {wrong # args: should be "file exists name"}
test cmdAH-5.4 {Tcl_FileObjCmd} {
    file exists ""
} 0

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
} ~bar
test cmdAH-8.43 {Tcl_FileObjCmd: dirname} -setup {
    global env
    set temp $env(HOME)
} -constraints testsetplatform -body {
    set env(HOME) "/homewontexist/test"
    testsetplatform unix
    file dirname ~
} -cleanup {
    set env(HOME) $temp
} -result /homewontexist
test cmdAH-8.44 {Tcl_FileObjCmd: dirname} -setup {
    global env
    set temp $env(HOME)
} -constraints testsetplatform -body {
    set env(HOME) "~"
    testsetplatform unix
    file dirname ~
} -cleanup {
    set env(HOME) $temp
} -result ~
test cmdAH-8.45 {Tcl_FileObjCmd: dirname} -setup {
    set temp $::env(HOME)
} -constraints {win testsetplatform} -match regexp -body {
    set ::env(HOME) "/homewontexist/test"
    testsetplatform windows
    file dirname ~
} -cleanup {
    set ::env(HOME) $temp
} -result {([a-zA-Z]:?)/homewontexist}
test cmdAH-8.46 {Tcl_FileObjCmd: dirname} {
    set f [file normalize [info nameof]]
    file exists $f
    set res1 [file dirname [file join $f foo/bar]]
    set res2 [file dirname "${f}/foo/bar"]
    if {$res1 eq $res2} {
	return "ok"







|









|


|
|
<
<
<
<

<
<
|







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
} ~bar
test cmdAH-8.43 {Tcl_FileObjCmd: dirname} -setup {
    global env
    set temp $env(HOME)
} -constraints testsetplatform -body {
    set env(HOME) "/homewontexist/test"
    testsetplatform unix
    file dirname [file home]
} -cleanup {
    set env(HOME) $temp
} -result /homewontexist
test cmdAH-8.44 {Tcl_FileObjCmd: dirname} -setup {
    global env
    set temp $env(HOME)
} -constraints testsetplatform -body {
    set env(HOME) "~"
    testsetplatform unix
    file dirname [file home]
} -cleanup {
    set env(HOME) $temp
} -result .
test cmdAH-8.45 {Tcl_FileObjCmd: dirname ~} -body {




    file dirname ~


} -result .
test cmdAH-8.46 {Tcl_FileObjCmd: dirname} {
    set f [file normalize [info nameof]]
    file exists $f
    set res1 [file dirname [file join $f foo/bar]]
    set res2 [file dirname "${f}/foo/bar"]
    if {$res1 eq $res2} {
	return "ok"
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
    testsetplatform windows
    file tail {//foo/bar/baz}
} baz
test cmdAH-9.26 {Tcl_FileObjCmd: tail} testsetplatform {
    testsetplatform windows
    file tail {//foo/bar}
} {}
test cmdAH-9.42 {Tcl_FileObjCmd: tail} -constraints testsetplatform -setup {
    global env
    set temp $env(HOME)
} -body {
    set env(HOME) "/home/test"
    testsetplatform unix
    file tail ~
} -cleanup {
    set env(HOME) $temp
} -result test
test cmdAH-9.43 {Tcl_FileObjCmd: tail} -constraints testsetplatform -setup {
    global env
    set temp $env(HOME)
} -body {
    set env(HOME) "~"
    testsetplatform unix
    file tail ~
} -cleanup {
    set env(HOME) $temp
} -result {}
test cmdAH-9.44 {Tcl_FileObjCmd: tail} -constraints testsetplatform -setup {
    global env
    set temp $env(HOME)
} -body {
    set env(HOME) "/home/test"
    testsetplatform windows
    file tail ~
} -cleanup {
    set env(HOME) $temp
} -result test
test cmdAH-9.46 {Tcl_FileObjCmd: tail} testsetplatform {
    testsetplatform unix
    file tail {f.oo\bar/baz.bat}
} baz.bat
test cmdAH-9.47 {Tcl_FileObjCmd: tail} testsetplatform {
    testsetplatform windows
    file tail c:foo







|
<
<
<
<
<

<
<
|






|


|
<
<
<
<
<
<
<
<
<
<







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
    testsetplatform windows
    file tail {//foo/bar/baz}
} baz
test cmdAH-9.26 {Tcl_FileObjCmd: tail} testsetplatform {
    testsetplatform windows
    file tail {//foo/bar}
} {}
test cmdAH-9.42 {Tcl_FileObjCmd: tail ~} -body {





    file tail ~


} -result ~
test cmdAH-9.43 {Tcl_FileObjCmd: tail} -constraints testsetplatform -setup {
    global env
    set temp $env(HOME)
} -body {
    set env(HOME) "~"
    testsetplatform unix
    file tail [file home]
} -cleanup {
    set env(HOME) $temp
} -result ~










test cmdAH-9.46 {Tcl_FileObjCmd: tail} testsetplatform {
    testsetplatform unix
    file tail {f.oo\bar/baz.bat}
} baz.bat
test cmdAH-9.47 {Tcl_FileObjCmd: tail} testsetplatform {
    testsetplatform windows
    file tail c:foo
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
} bar
test cmdAH-9.52 {Tcl_FileObjCmd: tail / normalize, bug 7a9dc52b29} {
    list \
	[file tail {~/~foo}] \
	[file tail {~/test/~foo}] \
	[file tail [file normalize {~/~foo}]] \
	[file tail [file normalize {~/test/~foo}]]
} [lrepeat 4 ./~foo]

# rootname
test cmdAH-10.1 {Tcl_FileObjCmd: rootname} -returnCodes error -body {
    file rootname a b
} -result {wrong # args: should be "file rootname name"}
test cmdAH-10.2 {Tcl_FileObjCmd: rootname} testsetplatform {
    testsetplatform unix







|







672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
} bar
test cmdAH-9.52 {Tcl_FileObjCmd: tail / normalize, bug 7a9dc52b29} {
    list \
	[file tail {~/~foo}] \
	[file tail {~/test/~foo}] \
	[file tail [file normalize {~/~foo}]] \
	[file tail [file normalize {~/test/~foo}]]
} [lrepeat 4 ~foo]

# rootname
test cmdAH-10.1 {Tcl_FileObjCmd: rootname} -returnCodes error -body {
    file rootname a b
} -result {wrong # args: should be "file rootname name"}
test cmdAH-10.2 {Tcl_FileObjCmd: rootname} testsetplatform {
    testsetplatform unix
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
    file join a b c d
} a/b/c/d

# error handling of Tcl_TranslateFileName
test cmdAH-15.1 {Tcl_FileObjCmd} -constraints testsetplatform -body {
    testsetplatform unix
    file atime ~_bad_user
} -returnCodes error -result {user "_bad_user" doesn't exist}

catch {testsetplatform $platform}

# readable
set gorpfile [makeFile abcde gorp.file]
set dirfile [makeDirectory dir.file]
test cmdAH-16.1 {Tcl_FileObjCmd: readable} {







|







926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
    file join a b c d
} a/b/c/d

# error handling of Tcl_TranslateFileName
test cmdAH-15.1 {Tcl_FileObjCmd} -constraints testsetplatform -body {
    testsetplatform unix
    file atime ~_bad_user
} -returnCodes error -result {could not read "~_bad_user": no such file or directory}

catch {testsetplatform $platform}

# readable
set gorpfile [makeFile abcde gorp.file]
set dirfile [makeDirectory dir.file]
test cmdAH-16.1 {Tcl_FileObjCmd: readable} {
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
} -constraints testsetplatform -cleanup {
    testsetplatform $platform
} -result {a\b}
test cmdAH-19.9 {Tcl_FileObjCmd: ~ : exists} {
    file exists ~nOsUcHuSeR
} 0
test cmdAH-19.10 {Tcl_FileObjCmd: ~ : nativename} -body {
    # should probably be a non-error in fact...
    file nativename ~nOsUcHuSeR
} -returnCodes error -match glob -result *
# The test below has to be done in /tmp rather than the current directory in
# order to guarantee (?) a local file system: some NFS file systems won't do
# the stuff below correctly.
test cmdAH-19.11 {Tcl_FileObjCmd: exists} -constraints {unix notRoot} -setup {
    file delete -force /tmp/tcl.foo.dir/file
    file delete -force /tmp/tcl.foo.dir
} -body {







<

|







1049
1050
1051
1052
1053
1054
1055

1056
1057
1058
1059
1060
1061
1062
1063
1064
} -constraints testsetplatform -cleanup {
    testsetplatform $platform
} -result {a\b}
test cmdAH-19.9 {Tcl_FileObjCmd: ~ : exists} {
    file exists ~nOsUcHuSeR
} 0
test cmdAH-19.10 {Tcl_FileObjCmd: ~ : nativename} -body {

    file nativename ~nOsUcHuSeR
} -result ~nOsUcHuSeR
# The test below has to be done in /tmp rather than the current directory in
# order to guarantee (?) a local file system: some NFS file systems won't do
# the stuff below correctly.
test cmdAH-19.11 {Tcl_FileObjCmd: exists} -constraints {unix notRoot} -setup {
    file delete -force /tmp/tcl.foo.dir/file
    file delete -force /tmp/tcl.foo.dir
} -body {
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
    }
    set res
} -result "characterSpecial"

# Error conditions
test cmdAH-30.1 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file gorp x
} -result {unknown or ambiguous subcommand "gorp": must be atime, attributes, channels, copy, delete, dirname, executable, exists, extension, isdirectory, isfile, join, link, lstat, mkdir, mtime, nativename, normalize, owned, pathtype, readable, readlink, rename, rootname, separator, size, split, stat, system, tail, tempdir, tempfile, type, volumes, or writable}
test cmdAH-30.2 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file ex x
} -match glob -result {unknown or ambiguous subcommand "ex": must be *}
test cmdAH-30.3 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file is x
} -match glob -result {unknown or ambiguous subcommand "is": must be *}
test cmdAH-30.4 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file z x
} -match glob -result {unknown or ambiguous subcommand "z": must be *}
test cmdAH-30.5 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file read x
} -match glob -result {unknown or ambiguous subcommand "read": must be *}
test cmdAH-30.6 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file s x
} -match glob -result {unknown or ambiguous subcommand "s": must be *}
test cmdAH-30.7 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file t x
} -match glob -result {unknown or ambiguous subcommand "t": must be *}
test cmdAH-30.8 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file dirname ~woohgy
} -result {user "woohgy" doesn't exist}

# channels
# In testing 'file channels', we need to make sure that a channel created in
# one interp isn't visible in another.

interp create simpleInterp
interp create -safe safeInterp







|


















<
<
<







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
    }
    set res
} -result "characterSpecial"

# Error conditions
test cmdAH-30.1 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file gorp x
} -result {unknown or ambiguous subcommand "gorp": must be atime, attributes, channels, copy, delete, dirname, executable, exists, extension, home, isdirectory, isfile, join, link, lstat, mkdir, mtime, nativename, normalize, owned, pathtype, readable, readlink, rename, rootname, separator, size, split, stat, system, tail, tempdir, tempfile, tildeexpand, type, volumes, or writable}
test cmdAH-30.2 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file ex x
} -match glob -result {unknown or ambiguous subcommand "ex": must be *}
test cmdAH-30.3 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file is x
} -match glob -result {unknown or ambiguous subcommand "is": must be *}
test cmdAH-30.4 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file z x
} -match glob -result {unknown or ambiguous subcommand "z": must be *}
test cmdAH-30.5 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file read x
} -match glob -result {unknown or ambiguous subcommand "read": must be *}
test cmdAH-30.6 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file s x
} -match glob -result {unknown or ambiguous subcommand "s": must be *}
test cmdAH-30.7 {Tcl_FileObjCmd: error conditions} -returnCodes error -body {
    file t x
} -match glob -result {unknown or ambiguous subcommand "t": must be *}




# channels
# In testing 'file channels', we need to make sure that a channel created in
# one interp isn't visible in another.

interp create simpleInterp
interp create -safe safeInterp
Changes to tests/cmdMZ.test.
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
    while executing
"return -level 0 -code error"} -errorline 1 -errorstack * -level 0}}
test cmdMZ-return-2.11 {return option handling} {
    list [catch {return -level 0 -code break} -> foo] [dictSort $foo]
} {3 {-code 3 -level 0}}
test cmdMZ-return-2.12 {return option handling} -body {
    return -level 0 -code error -options {-code ok}
} -returnCodes ok -result {}
test cmdMZ-return-2.13 {return option handling} -body {
    return -level 0 -code error -options {-code err}
} -returnCodes error -match glob -result {bad completion code "err": must be ok, error, return, break, continue*, or an integer}
test cmdMZ-return-2.14 {return option handling} -body {
    return -level 0 -code error -options {-code foo -options {-code break}}
} -returnCodes break -result {}
test cmdMZ-return-2.15 {return opton handling} {







|







154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
    while executing
"return -level 0 -code error"} -errorline 1 -errorstack * -level 0}}
test cmdMZ-return-2.11 {return option handling} {
    list [catch {return -level 0 -code break} -> foo] [dictSort $foo]
} {3 {-code 3 -level 0}}
test cmdMZ-return-2.12 {return option handling} -body {
    return -level 0 -code error -options {-code ok}
} -result {}
test cmdMZ-return-2.13 {return option handling} -body {
    return -level 0 -code error -options {-code err}
} -returnCodes error -match glob -result {bad completion code "err": must be ok, error, return, break, continue*, or an integer}
test cmdMZ-return-2.14 {return option handling} -body {
    return -level 0 -code error -options {-code foo -options {-code break}}
} -returnCodes break -result {}
test cmdMZ-return-2.15 {return opton handling} {
Changes to tests/compile.test.
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
# suite.
#
test compile-16.18.$noComp {TclCompileScript: word expansion} -body {
    proc LongList {} {return [lrepeat [expr {1<<10}] x]}
    llength [run "list [string repeat {{*}[LongList] } [expr {1<<10}]]"]
} -constraints [linsert $constraints 0 knownBug] -cleanup {
    rename LongList {}
} -returnCodes ok  -result [expr {1<<20}]
test compile-16.19.$noComp {TclCompileScript: word expansion} -body {
    proc LongList {} {return [lrepeat [expr {1<<11}] x]}
    llength [run "list [string repeat {{*}[LongList] } [expr {1<<11}]]"]
} -constraints [linsert $constraints 0 knownBug] -cleanup {
    rename LongList {}
} -returnCodes ok  -result [expr {1<<22}]
test compile-16.20.$noComp {TclCompileScript: word expansion} -body {
    proc LongList {} {return [lrepeat [expr {1<<12}] x]}
    llength [run "list [string repeat {{*}[LongList] } [expr {1<<12}]]"]
} -constraints [linsert $constraints 0 knownBug] -cleanup {
    rename LongList {}
} -returnCodes ok  -result [expr {1<<24}]
# This is the one that should cause overflow
test compile-16.21.$noComp {TclCompileScript: word expansion} -body {
    proc LongList {} {return [lrepeat [expr {1<<16}] x]}
    llength [run "list [string repeat {{*}[LongList] } [expr {1<<16}]]"]
} -constraints [linsert $constraints 0 knownBug] -cleanup {
    rename LongList {}
} -returnCodes ok  -result [expr {wide(1)<<32}]
test compile-16.22.$noComp {
    Bug 845412: TclCompileScript: word expansion not mandatory
} -body {
    # This test may crash and will fail unless Bug 845412 is fixed.
    proc ReturnResults args {return $args}
    run "ReturnResults [string repeat {x } 260]"
} -constraints $constraints -cleanup {
    rename ReturnResults {}
} -returnCodes ok -result [string trim [string repeat {x } 260]]
test compile-16.23.$noComp {
    Bug 1032805: defer parse error until run time
} -constraints $constraints -body {
    namespace eval x {
	run {
	    proc if {a b} {uplevel 1 [list set $a $b]}
	    if 1 {syntax {}{}}
	}
    }
} -cleanup {
    namespace delete x
} -returnCodes ok -result {syntax {}{}}
test compile-16.24.$noComp {
    Bug 1638414: bad list constant as first expanded term
} -constraints $constraints -body {
    run "{*}\"\{foo bar\""
} -returnCodes error -result {unmatched open brace in list}
test compile-16.25.$noComp {TclCompileScript: word expansion, naked backslashes} $constraints {
    run {list {*}{a \n b}}







|





|





|






|








|











|







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
# suite.
#
test compile-16.18.$noComp {TclCompileScript: word expansion} -body {
    proc LongList {} {return [lrepeat [expr {1<<10}] x]}
    llength [run "list [string repeat {{*}[LongList] } [expr {1<<10}]]"]
} -constraints [linsert $constraints 0 knownBug] -cleanup {
    rename LongList {}
}  -result [expr {1<<20}]
test compile-16.19.$noComp {TclCompileScript: word expansion} -body {
    proc LongList {} {return [lrepeat [expr {1<<11}] x]}
    llength [run "list [string repeat {{*}[LongList] } [expr {1<<11}]]"]
} -constraints [linsert $constraints 0 knownBug] -cleanup {
    rename LongList {}
}  -result [expr {1<<22}]
test compile-16.20.$noComp {TclCompileScript: word expansion} -body {
    proc LongList {} {return [lrepeat [expr {1<<12}] x]}
    llength [run "list [string repeat {{*}[LongList] } [expr {1<<12}]]"]
} -constraints [linsert $constraints 0 knownBug] -cleanup {
    rename LongList {}
}  -result [expr {1<<24}]
# This is the one that should cause overflow
test compile-16.21.$noComp {TclCompileScript: word expansion} -body {
    proc LongList {} {return [lrepeat [expr {1<<16}] x]}
    llength [run "list [string repeat {{*}[LongList] } [expr {1<<16}]]"]
} -constraints [linsert $constraints 0 knownBug] -cleanup {
    rename LongList {}
}  -result [expr {wide(1)<<32}]
test compile-16.22.$noComp {
    Bug 845412: TclCompileScript: word expansion not mandatory
} -body {
    # This test may crash and will fail unless Bug 845412 is fixed.
    proc ReturnResults args {return $args}
    run "ReturnResults [string repeat {x } 260]"
} -constraints $constraints -cleanup {
    rename ReturnResults {}
} -result [string trim [string repeat {x } 260]]
test compile-16.23.$noComp {
    Bug 1032805: defer parse error until run time
} -constraints $constraints -body {
    namespace eval x {
	run {
	    proc if {a b} {uplevel 1 [list set $a $b]}
	    if 1 {syntax {}{}}
	}
    }
} -cleanup {
    namespace delete x
} -result {syntax {}{}}
test compile-16.24.$noComp {
    Bug 1638414: bad list constant as first expanded term
} -constraints $constraints -body {
    run "{*}\"\{foo bar\""
} -returnCodes error -result {unmatched open brace in list}
test compile-16.25.$noComp {TclCompileScript: word expansion, naked backslashes} $constraints {
    run {list {*}{a \n b}}
Changes to tests/env.test.
103
104
105
106
107
108
109

110
111
112
113
114
115
116
    TCL_LIBRARY PATH LD_LIBRARY_PATH LIBPATH PURE_PROG_NAME DISPLAY
    SHLIB_PATH SYSTEMDRIVE SYSTEMROOT DYLD_LIBRARY_PATH DYLD_FRAMEWORK_PATH
    DYLD_NEW_LOCAL_SHARED_REGIONS DYLD_NO_FIX_PREBINDING MSYSTEM
    __CF_USER_TEXT_ENCODING SECURITYSESSIONID LANG WINDIR TERM
    CommonProgramFiles CommonProgramFiles(x86) ProgramFiles
    ProgramFiles(x86) CommonProgramW6432 ProgramW6432
    WINECONFIGDIR WINEDATADIR WINEDLLDIR0 WINEHOMEDIR PROCESSOR_ARCHITECTURE

}

variable printenvScript [makeFile [string map [list @keep@ [list $keep]] {
    encoding system iso8859-1
    proc lrem {listname name} {
	upvar $listname list
	set i [lsearch -nocase $list $name]







>







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
    TCL_LIBRARY PATH LD_LIBRARY_PATH LIBPATH PURE_PROG_NAME DISPLAY
    SHLIB_PATH SYSTEMDRIVE SYSTEMROOT DYLD_LIBRARY_PATH DYLD_FRAMEWORK_PATH
    DYLD_NEW_LOCAL_SHARED_REGIONS DYLD_NO_FIX_PREBINDING MSYSTEM
    __CF_USER_TEXT_ENCODING SECURITYSESSIONID LANG WINDIR TERM
    CommonProgramFiles CommonProgramFiles(x86) ProgramFiles
    ProgramFiles(x86) CommonProgramW6432 ProgramW6432
    WINECONFIGDIR WINEDATADIR WINEDLLDIR0 WINEHOMEDIR PROCESSOR_ARCHITECTURE
    USERPROFILE
}

variable printenvScript [makeFile [string map [list @keep@ [list $keep]] {
    encoding system iso8859-1
    proc lrem {listname name} {
	upvar $listname list
	set i [lsearch -nocase $list $name]
467
468
469
470
471
472
473







































474
475
476
477
478
479
480
} -body {
    set res [set env(__DUMMY__) {i'm with dummy}]
    unset env(__DUMMY__)
    return $res
} -result {i'm with dummy}










































# cleanup
rename getenv {}
rename envrestore {}
rename envprep {}
rename encodingrestore {}
rename encodingswitch {}







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







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
} -body {
    set res [set env(__DUMMY__) {i'm with dummy}]
    unset env(__DUMMY__)
    return $res
} -result {i'm with dummy}


test env-9.0 {
    Initialization of HOME from HOMEDRIVE and HOMEPATH
} -constraints win -setup {
    setup1
    unset -nocomplain ::env(HOME)
    set ::env(HOMEDRIVE) X:
    set ::env(HOMEPATH) \\home\\path
} -cleanup {
    cleanup1
} -body {
    set pipe [open |[list [interpreter]] r+]
    puts $pipe {puts $::env(HOME); flush stdout; exit}
    flush $pipe
    set result [gets $pipe]
    close $pipe
    set result
} -result {X:\home\path}

test env-9.1 {
    Initialization of HOME from USERPROFILE
} -constraints win -setup {
    setup1
    unset -nocomplain ::env(HOME)
    unset -nocomplain ::env(HOMEDRIVE)
    unset -nocomplain ::env(HOMEPATH)
} -cleanup {
    cleanup1
} -body {
    set pipe [open |[list [interpreter]] r+]
    puts $pipe {puts $::env(HOME); flush stdout; exit}
    flush $pipe
    set result [gets $pipe]
    close $pipe
    if {$result ne $::env(USERPROFILE)} {
	list ERROR $result ne $::env(USERPROFILE)
    }
} -result {}



# cleanup
rename getenv {}
rename envrestore {}
rename envprep {}
rename encodingrestore {}
rename encodingswitch {}
Changes to tests/event.test.
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
test event-10.1 {Tcl_Exit procedure} {stdio} {
    set child [open |[list [interpreter]] r+]
    puts $child "exit 3"
    list [catch {close $child} msg] $msg [lindex $::errorCode 0] \
        [lindex $::errorCode 2]
} {1 {child process exited abnormally} CHILDSTATUS 3}

test event-11.1 {Tcl_VwaitCmd procedure} -returnCodes error -body {
    vwait
} -result {wrong # args: should be "vwait name"}
test event-11.2 {Tcl_VwaitCmd procedure} -returnCodes error -body {
    vwait a b
} -result {wrong # args: should be "vwait name"}
test event-11.3 {Tcl_VwaitCmd procedure} -setup {
    catch {unset x}
} -body {
    set x 1
    vwait x(1)
} -returnCodes error -result {can't trace "x(1)": variable isn't array}
test event-11.4 {Tcl_VwaitCmd procedure} -setup {







|

|
<
<
<







505
506
507
508
509
510
511
512
513
514



515
516
517
518
519
520
521
test event-10.1 {Tcl_Exit procedure} {stdio} {
    set child [open |[list [interpreter]] r+]
    puts $child "exit 3"
    list [catch {close $child} msg] $msg [lindex $::errorCode 0] \
        [lindex $::errorCode 2]
} {1 {child process exited abnormally} CHILDSTATUS 3}

test event-11.1 {Tcl_VwaitCmd procedure} -body {
    vwait
} -result {}



test event-11.3 {Tcl_VwaitCmd procedure} -setup {
    catch {unset x}
} -body {
    set x 1
    vwait x(1)
} -returnCodes error -result {can't trace "x(1)": variable isn't array}
test event-11.4 {Tcl_VwaitCmd procedure} -setup {
Changes to tests/exec.test.
436
437
438
439
440
441
442
443
444






445
446
447
448
449
450
451
452
453
454
455
456
457
458
} -returnCodes error -result "channel \"$f\" wasn't opened for reading"
close $f
set f [open $path(gorp.file) r]
test exec-10.19 {errors in exec invocation} -constraints {exec} -body {
    exec cat >@ $f
} -returnCodes error -result "channel \"$f\" wasn't opened for writing"
close $f
test exec-10.20 {errors in exec invocation} -constraints {exec notValgrind} -body {
    exec ~non_existent_user/foo/bar






} -returnCodes error -result {user "non_existent_user" doesn't exist}
test exec-10.21 {errors in exec invocation} -constraints {exec notValgrind} -body {
    exec [interpreter] true | ~xyzzy_bad_user/x | false
} -returnCodes error -result {user "xyzzy_bad_user" doesn't exist}
test exec-10.22 {errors in exec invocation} -constraints {exec notValgrind} -body {
    exec echo test > ~non_existent_user/foo/bar
} -returnCodes error -result {user "non_existent_user" doesn't exist}
# Commands in background.

test exec-11.1 {commands in background} {exec} {
    set time [time {exec [interpreter] $path(sleep) 2 &}]
    expr {[lindex $time 0] < 1000000}
} 1
test exec-11.2 {commands in background} -constraints {exec} -body {







|

>
>
>
>
>
>
|
|

|


|







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
} -returnCodes error -result "channel \"$f\" wasn't opened for reading"
close $f
set f [open $path(gorp.file) r]
test exec-10.19 {errors in exec invocation} -constraints {exec} -body {
    exec cat >@ $f
} -returnCodes error -result "channel \"$f\" wasn't opened for writing"
close $f
test exec-10.20.1 {errors in exec invocation} -constraints {unix exec notValgrind} -body {
    exec ~non_existent_user/foo/bar
} -returnCodes error -result {couldn't execute "~non_existent_user/foo/bar": no such file or directory}
test exec-10.20.1 {errors in exec invocation} -constraints {win exec notValgrind} -body {
    exec ~non_existent_user/foo/bar
} -returnCodes error -result {couldn't execute "~non_existent_user\foo\bar": no such file or directory}
test exec-10.21.1 {errors in exec invocation} -constraints {unix exec notValgrind} -body {
    exec [interpreter] true | ~xyzzy_bad_user/x | false
} -returnCodes error -result {couldn't execute "~xyzzy_bad_user/x": no such file or directory}
test exec-10.21.2 {errors in exec invocation} -constraints {win exec notValgrind} -body {
    exec [interpreter] true | ~xyzzy_bad_user/x | false
} -returnCodes error -result {couldn't execute "~xyzzy_bad_user\x": no such file or directory}
test exec-10.22 {errors in exec invocation} -constraints {exec notValgrind} -body {
    exec echo test > ~non_existent_user/foo/bar
} -returnCodes error -result {couldn't write file "~non_existent_user/foo/bar": no such file or directory}
# Commands in background.

test exec-11.1 {commands in background} {exec} {
    set time [time {exec [interpreter] $path(sleep) 2 &}]
    expr {[lindex $time 0] < 1000000}
} 1
test exec-11.2 {commands in background} -constraints {exec} -body {
Changes to tests/fCmd.test.
92
93
94
95
96
97
98








99
100
101
102
103
104
105
	    regexp {^[^(]*\(([^)]*)\)} [exec id] -> user
	}
    }
    if {$user eq ""} {
	set user "root"
    }
}









proc createfile {file {string a}} {
    set f [open $file w]
    puts -nonewline $f $string
    close $f
    return $string
}







>
>
>
>
>
>
>
>







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
	    regexp {^[^(]*\(([^)]*)\)} [exec id] -> user
	}
    }
    if {$user eq ""} {
	set user "root"
    }
}
if {[testConstraint win]} {
    catch {
        set user $::env(USERNAME)
    }
    if {$user eq ""} {
        set user Administrator
    }
}

proc createfile {file {string a}} {
    set f [open $file w]
    puts -nonewline $f $string
    close $f
    return $string
}
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
    } on error {} {
	return 0
    }
    return [string match $matchString $fileString]
}

proc openup {path} {




    testchmod 0o777 $path
    if {[file isdirectory $path]} {
	catch {
	    foreach p [glob -directory $path *] {
		openup $p
	    }
	}
    }
}

proc cleanup {args} {
    set wd [list .]
    foreach p [concat $wd $args] {
	set x ""
	catch {
	    set x [glob -directory $p tf* td*]
	}
	foreach file $x {




	    if {
		[catch {file delete -force -- $file}]
		&& [testConstraint testchmod]
	    } then {
		catch {openup $file}
		catch {file delete -force -- $file}
	    }







>
>
>
>















|


>
>
>
>







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
    } on error {} {
	return 0
    }
    return [string match $matchString $fileString]
}

proc openup {path} {
    # Double check for inadvertent ~ -> home directory mapping
    if {[string match ~* $path]} {
        set file ./$path
    }
    testchmod 0o777 $path
    if {[file isdirectory $path]} {
	catch {
	    foreach p [glob -directory $path *] {
		openup $p
	    }
	}
    }
}

proc cleanup {args} {
    set wd [list .]
    foreach p [concat $wd $args] {
	set x ""
	catch {
	    set x [glob -directory $p tf* td* ~*]
	}
	foreach file $x {
            # Double check for inadvertent ~ -> home directory mapping
            if {[string match ~* $file]} {
                set file ./$file
            }
	    if {
		[catch {file delete -force -- $file}]
		&& [testConstraint testchmod]
	    } then {
		catch {openup $file}
		catch {file delete -force -- $file}
	    }
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
test fCmd-1.1 {TclFileRenameCmd} -constraints {notRoot} -setup {
    cleanup
} -body {
    createfile tf1
    file rename tf1 tf2
    glob tf*
} -result {tf2}






































test fCmd-2.1 {TclFileCopyCmd} -constraints {notRoot} -setup {
    cleanup
} -body {
    createfile tf1
    file copy tf1 tf2
    lsort [glob tf*]
} -result {tf1 tf2}





































test fCmd-3.1 {FileCopyRename: FileForceOption fails} -constraints {notRoot} -body {
    file rename -xyz
} -returnCodes error -result {bad option "-xyz": must be -force or --}
test fCmd-3.2 {FileCopyRename: not enough args} -constraints {notRoot} -body {
    file rename xyz
} -returnCodes error -result {wrong # args: should be "file rename ?-option value ...? source ?source ...? target"}
test fCmd-3.3 {FileCopyRename: Tcl_TranslateFileName fails} -constraints {notRoot} -body {
    file rename xyz ~_totally_bogus_user
} -returnCodes error -result {user "_totally_bogus_user" doesn't exist}
test fCmd-3.4 {FileCopyRename: Tcl_TranslateFileName passes} -setup {
    cleanup
} -constraints {notRoot} -returnCodes error -body {
    file copy tf1 ~
} -result {error copying "tf1": no such file or directory}
test fCmd-3.5 {FileCopyRename: target doesn't exist: stat(target) != 0} -setup {
    cleanup







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

>







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









|







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
test fCmd-1.1 {TclFileRenameCmd} -constraints {notRoot} -setup {
    cleanup
} -body {
    createfile tf1
    file rename tf1 tf2
    glob tf*
} -result {tf2}
test fCmd-1.2 {TclFileRenameCmd when target is ~} -setup {
    cleanup
    createfile tf1
} -cleanup {
    file delete ./~
} -body {
    file rename tf1 ~
    file isfile ~
} -result 1
test fCmd-1.3 {TclFileRenameCmd when target is ~user} -setup {
    cleanup
    createfile tf1
} -cleanup {
    file delete ./~$user
} -body {
    file rename tf1 ~$user
    file isfile ~$user
} -result 1
test fCmd-1.4 {TclFileRenameCmd when source is ~} -setup {
    cleanup
    createfile ./~
} -cleanup {
    file delete ./~
} -body {
    file rename ~ tf1
    list [file exists ~] [file exists tf1]
} -result {0 1}
test fCmd-1.5 {TclFileRenameCmd when source is ~user} -setup {
    cleanup
    createfile ./~$user
} -cleanup {
    file delete ./~$user
} -body {
    file rename ~$user tf1
    list [file exists ~$user] [file exists tf1]
} -result {0 1}


test fCmd-2.1 {TclFileCopyCmd} -constraints {notRoot} -setup {
    cleanup
} -body {
    createfile tf1
    file copy tf1 tf2
    lsort [glob tf*]
} -result {tf1 tf2}
test fCmd-2.2 {TclFileCopyCmd when target is ~} -setup {
    cleanup
    createfile tf1
} -cleanup {
    file delete ./~
} -body {
    file copy tf1 ~
    list [file exists tf1] [file exists ~]
} -result {1 1}
test fCmd-2.3 {TclFileCopyCmd when target is ~user} -setup {
    cleanup
    createfile tf1
} -cleanup {
    file delete ./~$user
} -body {
    file copy tf1 ~$user
    list [file exists tf1] [file exists ~$user]
} -result {1 1}
test fCmd-2.4 {TclFileCopyCmd when source is ~} -setup {
    cleanup
    createfile ./~
} -cleanup {
    file delete ./~
} -body {
    file copy ~ tf1
    list [file exists ~] [file exists tf1]
} -result {1 1}
test fCmd-2.5 {TclFileCopyCmd when source is ~user} -setup {
    cleanup
    createfile ./~$user
} -cleanup {
    file delete ./~$user
} -body {
    file copy ~$user tf1
    list [file exists ~$user] [file exists tf1]
} -result {1 1}

test fCmd-3.1 {FileCopyRename: FileForceOption fails} -constraints {notRoot} -body {
    file rename -xyz
} -returnCodes error -result {bad option "-xyz": must be -force or --}
test fCmd-3.2 {FileCopyRename: not enough args} -constraints {notRoot} -body {
    file rename xyz
} -returnCodes error -result {wrong # args: should be "file rename ?-option value ...? source ?source ...? target"}
test fCmd-3.3 {FileCopyRename: Tcl_TranslateFileName fails} -constraints {notRoot} -body {
    file rename xyz ~_totally_bogus_user
} -returnCodes error -result {error renaming "xyz": no such file or directory}
test fCmd-3.4 {FileCopyRename: Tcl_TranslateFileName passes} -setup {
    cleanup
} -constraints {notRoot} -returnCodes error -body {
    file copy tf1 ~
} -result {error copying "tf1": no such file or directory}
test fCmd-3.5 {FileCopyRename: target doesn't exist: stat(target) != 0} -setup {
    cleanup
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
	[contents [file join td1 tf3]] [contents [file join td1 tf4]]
} -result {tf1 tf2 tf3 tf4}
test fCmd-3.14 {FileCopyRename: FileBasename fails} -setup {
    cleanup
} -constraints {notRoot} -returnCodes error -body {
    file mkdir td1
    file rename ~_totally_bogus_user td1
} -result {user "_totally_bogus_user" doesn't exist}
test fCmd-3.15 {FileCopyRename: source[0] == '\x00'} -setup {
    cleanup
} -constraints {notRoot unixOrWin} -returnCodes error -body {
    file mkdir td1
    file rename / td1
} -result {error renaming "/" to "td1": file already exists}
test fCmd-3.16 {FileCopyRename: break on first error} -setup {







|







355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
	[contents [file join td1 tf3]] [contents [file join td1 tf4]]
} -result {tf1 tf2 tf3 tf4}
test fCmd-3.14 {FileCopyRename: FileBasename fails} -setup {
    cleanup
} -constraints {notRoot} -returnCodes error -body {
    file mkdir td1
    file rename ~_totally_bogus_user td1
} -result {error renaming "~_totally_bogus_user": no such file or directory}
test fCmd-3.15 {FileCopyRename: source[0] == '\x00'} -setup {
    cleanup
} -constraints {notRoot unixOrWin} -returnCodes error -body {
    file mkdir td1
    file rename / td1
} -result {error renaming "/" to "td1": file already exists}
test fCmd-3.16 {FileCopyRename: break on first error} -setup {
304
305
306
307
308
309
310
311
312
313





314

315
316
317
318
319
320
321
322
test fCmd-4.3 {TclFileMakeDirsCmd: stops on first error} -setup {
    cleanup
} -constraints {notRoot} -body {
    createfile tf1
    catch {file mkdir td1 td2 tf1 td3 td4}
    glob td1 td2 tf1 td3 td4
} -result {td1 td2 tf1}
test fCmd-4.4 {TclFileMakeDirsCmd: Tcl_TranslateFileName fails} -setup {
    cleanup
} -constraints {notRoot} -returnCodes error -body {





    file mkdir ~_totally_bogus_user

} -result {user "_totally_bogus_user" doesn't exist}
test fCmd-4.5 {TclFileMakeDirsCmd: Tcl_SplitPath returns 0: *name == '\x00'} -setup {
    cleanup
} -constraints {notRoot} -returnCodes error -body {
    file mkdir ""
} -result {can't create directory "": no such file or directory}
test fCmd-4.6 {TclFileMakeDirsCmd: one level deep} -setup {
    cleanup







|

|
>
>
>
>
>

>
|







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
test fCmd-4.3 {TclFileMakeDirsCmd: stops on first error} -setup {
    cleanup
} -constraints {notRoot} -body {
    createfile tf1
    catch {file mkdir td1 td2 tf1 td3 td4}
    glob td1 td2 tf1 td3 td4
} -result {td1 td2 tf1}
test fCmd-4.4 {TclFileMakeDirsCmd: Tcl_TranslateFileName treats ~ as normal char} -setup {
    cleanup
} -constraints {notRoot} -body {
    list [file isdir ~] [file mkdir ~] [file isdir ~]
} -result {0 {} 1}
test fCmd-4.4.1 {TclFileMakeDirsCmd: Tcl_TranslateFileName treats ~ as normal char} -setup {
    cleanup
} -constraints {notRoot} -body {
    file mkdir ~_totally_bogus_user
    file isdir ~_totally_bogus_user
} -result 1
test fCmd-4.5 {TclFileMakeDirsCmd: Tcl_SplitPath returns 0: *name == '\x00'} -setup {
    cleanup
} -constraints {notRoot} -returnCodes error -body {
    file mkdir ""
} -result {can't create directory "": no such file or directory}
test fCmd-4.6 {TclFileMakeDirsCmd: one level deep} -setup {
    cleanup
416
417
418
419
420
421
422


423
424
425

426
427
428
429
430
431
432
433
434
435
436
437
438
} -constraints {notRoot unixOrWin notWine} -body {
    createfile tf1
    createfile tf2
    file mkdir td1
    catch {file delete tf1 td1 $root tf2}
    list [file exists tf1] [file exists tf2] [file exists td1]
} -cleanup {cleanup} -result {0 1 0}


test fCmd-5.6 {TclFileDeleteCmd: Tcl_TranslateFileName fails} -constraints {notRoot} -body {
    file delete ~_totally_bogus_user
} -returnCodes error -result {user "_totally_bogus_user" doesn't exist}

test fCmd-5.7 {TclFileDeleteCmd: Tcl_TranslateFileName succeeds} -setup {
    catch {file delete ~/tf1}
} -constraints {notRoot} -body {
    createfile ~/tf1
    file delete ~/tf1
} -result {}
test fCmd-5.8 {TclFileDeleteCmd: file doesn't exist: lstat(name) != 0} -setup {
    cleanup
} -constraints {notRoot} -body {
    set x [file exists tf1]
    file delete tf1
    list $x [file exists tf1]
} -result {0 0}







>
>
|

|
>
|
<


<
|







511
512
513
514
515
516
517
518
519
520
521
522
523
524

525
526

527
528
529
530
531
532
533
534
} -constraints {notRoot unixOrWin notWine} -body {
    createfile tf1
    createfile tf2
    file mkdir td1
    catch {file delete tf1 td1 $root tf2}
    list [file exists tf1] [file exists tf2] [file exists td1]
} -cleanup {cleanup} -result {0 1 0}
test fCmd-5.6 {
    TclFileDeleteCmd: Tcl_TranslateFileName treats ~user as normal char
} -constraints {notRoot} -body {
    file delete ~_totally_bogus_user
} -result {}
test fCmd-5.7 {
    TclFileDeleteCmd: Tcl_TranslateFileName treats ~ as normal char

} -constraints {notRoot} -body {
    createfile ~/tf1

} -returnCodes error -result {couldn't open "~/tf1": no such file or directory}
test fCmd-5.8 {TclFileDeleteCmd: file doesn't exist: lstat(name) != 0} -setup {
    cleanup
} -constraints {notRoot} -body {
    set x [file exists tf1]
    file delete tf1
    list $x [file exists tf1]
} -result {0 0}
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
} -returnCodes error -cleanup {
    file attributes td1 -permissions 0o755
    cleanup
} -match regexp -result {^error renaming "td1"( to "/tmp/tcl\d+/td1")?: permission denied$}
test fCmd-6.24 {CopyRenameOneFile: error uses original name} -setup {
    cleanup
} -constraints {unix notRoot} -body {
    file mkdir ~/td1/td2
    set td1name [file join [file dirname ~] [file tail ~] td1]
    file attributes $td1name -permissions 0
    file copy ~/td1 td1
} -returnCodes error -cleanup {
    file attributes $td1name -permissions 0o755
    file delete -force ~/td1
} -result {error copying "~/td1": permission denied}
test fCmd-6.25 {CopyRenameOneFile: error uses original name} -setup {
    cleanup
} -constraints {unix notRoot} -body {
    file mkdir td2
    file mkdir ~/td1
    set td1name [file join [file dirname ~] [file tail ~] td1]
    file attributes $td1name -permissions 0
    file copy td2 ~/td1
} -returnCodes error -cleanup {
    file attributes $td1name -permissions 0o755
    file delete -force ~/td1
} -result {error copying "td2" to "~/td1/td2": permission denied}
test fCmd-6.26 {CopyRenameOneFile: doesn't use original name} -setup {
    cleanup
} -constraints {unix notRoot} -body {
    file mkdir ~/td1/td2
    set td2name [file join [file dirname ~] [file tail ~] td1 td2]
    file attributes $td2name -permissions 0
    file copy ~/td1 td1
} -returnCodes error -cleanup {
    file attributes $td2name -permissions 0o755
    file delete -force ~/td1
} -result "error copying \"~/td1\" to \"td1\": \"[file join $::env(HOME) td1 td2]\": permission denied"
test fCmd-6.27 {CopyRenameOneFile: TclpCopyDirectory failed} -setup {
    cleanup $tmpspace
} -constraints {notRoot xdev} -returnCodes error -body {
    file mkdir td1/td2/td3
    file mkdir [file join $tmpspace td1]
    createfile [file join $tmpspace td1 tf1]
    file rename -force td1 $tmpspace







|
|

|


|
|




|
|

|


|
|



|
|

|


|
|







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
} -returnCodes error -cleanup {
    file attributes td1 -permissions 0o755
    cleanup
} -match regexp -result {^error renaming "td1"( to "/tmp/tcl\d+/td1")?: permission denied$}
test fCmd-6.24 {CopyRenameOneFile: error uses original name} -setup {
    cleanup
} -constraints {unix notRoot} -body {
    file mkdir [file home]/td1/td2
    set td1name [file join [file dirname [file home]] [file tail [file home]] td1]
    file attributes $td1name -permissions 0
    file copy [file home]/td1 td1
} -returnCodes error -cleanup {
    file attributes $td1name -permissions 0o755
    file delete -force [file home]/td1
} -result "error copying \"[file home]/td1\": permission denied"
test fCmd-6.25 {CopyRenameOneFile: error uses original name} -setup {
    cleanup
} -constraints {unix notRoot} -body {
    file mkdir td2
    file mkdir [file home]/td1
    set td1name [file join [file dirname [file home]] [file tail [file home]] td1]
    file attributes $td1name -permissions 0
    file copy td2 [file home]/td1
} -returnCodes error -cleanup {
    file attributes $td1name -permissions 0o755
    file delete -force [file home]/td1
} -result "error copying \"td2\" to \"[file home]/td1/td2\": permission denied"
test fCmd-6.26 {CopyRenameOneFile: doesn't use original name} -setup {
    cleanup
} -constraints {unix notRoot} -body {
    file mkdir [file home]/td1/td2
    set td2name [file join [file dirname [file home]] [file tail [file home]] td1 td2]
    file attributes $td2name -permissions 0
    file copy [file home]/td1 td1
} -returnCodes error -cleanup {
    file attributes $td2name -permissions 0o755
    file delete -force [file home]/td1
} -result "error copying \"[file home]/td1\" to \"td1\": \"[file join $::env(HOME) td1 td2]\": permission denied"
test fCmd-6.27 {CopyRenameOneFile: TclpCopyDirectory failed} -setup {
    cleanup $tmpspace
} -constraints {notRoot xdev} -returnCodes error -body {
    file mkdir td1/td2/td3
    file mkdir [file join $tmpspace td1]
    createfile [file join $tmpspace td1 tf1]
    file rename -force td1 $tmpspace
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
    createfile --
    createfile -force
    file delete -force -force -- -- -force
    glob -- -- -force
} -result {no files matched glob patterns "-- -force"}

test fCmd-8.1 {FileBasename: basename of ~user: argc == 1 && *path == ~} \
    -constraints {unix notRoot knownBug} -body {
    # Labelled knownBug because it is dangerous [Bug: 3881]
    file mkdir td1
    file attr td1 -perm 0o40000
    file rename ~$user td1
} -returnCodes error -cleanup {
    file delete -force td1
} -result "error renaming \"~$user\" to \"td1/[file tail ~$user]\": permission denied"
test fCmd-8.2 {FileBasename: basename of ~user: argc == 1 && *path == ~} \
	-constraints {unix notRoot} -body {
    string equal [file tail ~$user] ~$user
} -result 0
test fCmd-8.3 {file copy and path translation: ensure correct error} -body {
    file copy ~ [file join this file doesnt exist]
} -returnCodes error -result [subst \
	{error copying "~" to "[file join this file doesnt exist]": no such file or directory}]

test fCmd-9.1 {file rename: comprehensive: EACCES} -setup {
    cleanup
} -constraints {unix notRoot} -body {
    file mkdir td1
    file mkdir td2
    file attr td2 -perm 0o40000







|










|

|

|







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
    createfile --
    createfile -force
    file delete -force -force -- -- -force
    glob -- -- -force
} -result {no files matched glob patterns "-- -force"}

test fCmd-8.1 {FileBasename: basename of ~user: argc == 1 && *path == ~} \
    -constraints {unix notRoot knownBug tildeexpansion} -body {
    # Labelled knownBug because it is dangerous [Bug: 3881]
    file mkdir td1
    file attr td1 -perm 0o40000
    file rename ~$user td1
} -returnCodes error -cleanup {
    file delete -force td1
} -result "error renaming \"~$user\" to \"td1/[file tail ~$user]\": permission denied"
test fCmd-8.2 {FileBasename: basename of ~user: argc == 1 && *path == ~} \
	-constraints {unix notRoot} -body {
    string equal [file tail ~$user] ~$user
} -result 1
test fCmd-8.3 {file copy and path translation: ensure correct error} -body {
    file copy [file home] [file join this file doesnt exist]
} -returnCodes error -result [subst \
   {error copying "[file home]" to "[file join this file doesnt exist]": no such file or directory}]

test fCmd-9.1 {file rename: comprehensive: EACCES} -setup {
    cleanup
} -constraints {unix notRoot} -body {
    file mkdir td1
    file mkdir td2
    file attr td2 -perm 0o40000
1494
1495
1496
1497
1498
1499
1500


1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
    file attributes tfa/dir -permissions 0o777
    file delete -force tfa tfa2
} -result {1}

#
# Coverage tests for TclMkdirCmd()
#


test fCmd-15.1 {TclMakeDirsCmd: target filename translation failing} -setup {
    set temp $::env(HOME)
} -constraints {notRoot} -body {
    global env
    unset env(HOME)
    catch {file mkdir ~/tfa}
} -cleanup {
    set ::env(HOME) $temp
} -result {1}
#
# Can Tcl_SplitPath return argc == 0? If so them we need a test for that code.
#
test fCmd-15.2 {TclMakeDirsCmd - one directory} -setup {
    catch {file delete -force -- tfa}
} -constraints {notRoot} -body {
    file mkdir tfa







>
>


|





|







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
    file attributes tfa/dir -permissions 0o777
    file delete -force tfa tfa2
} -result {1}

#
# Coverage tests for TclMkdirCmd()
#

# ~ is no longer a special char. Need a test case where translation fails.
test fCmd-15.1 {TclMakeDirsCmd: target filename translation failing} -setup {
    set temp $::env(HOME)
} -constraints {notRoot TODO} -body {
    global env
    unset env(HOME)
    catch {file mkdir ~/tfa}
} -cleanup {
    set ::env(HOME) $temp
} -result 1
#
# Can Tcl_SplitPath return argc == 0? If so them we need a test for that code.
#
test fCmd-15.2 {TclMakeDirsCmd - one directory} -setup {
    catch {file delete -force -- tfa}
} -constraints {notRoot} -body {
    file mkdir tfa
1595
1596
1597
1598
1599
1600
1601

1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
} -result {1}
test fCmd-16.4 {accept zero files (TIP 323)} -body {
    file delete
} -result {}
test fCmd-16.5 {accept zero files (TIP 323)} -body {
    file delete --
} -result {}

test fCmd-16.6 {delete: source filename translation failing} -setup {
    set temp $::env(HOME)
} -constraints {notRoot} -body {
    global env
    unset env(HOME)
    catch {file delete ~/tfa}
} -cleanup {
    set ::env(HOME) $temp
} -result {1}
test fCmd-16.7 {remove a non-empty directory without -force} -setup {







>


|







1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
} -result {1}
test fCmd-16.4 {accept zero files (TIP 323)} -body {
    file delete
} -result {}
test fCmd-16.5 {accept zero files (TIP 323)} -body {
    file delete --
} -result {}
# ~ is no longer a special char. Need a test case where translation fails.
test fCmd-16.6 {delete: source filename translation failing} -setup {
    set temp $::env(HOME)
} -constraints {notRoot TODO} -body {
    global env
    unset env(HOME)
    catch {file delete ~/tfa}
} -cleanup {
    set ::env(HOME) $temp
} -result {1}
test fCmd-16.7 {remove a non-empty directory without -force} -setup {
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
test fCmd-27.2 {TclFileAttrsCmd - Tcl_TranslateFileName fails} -setup {
    set platform [testgetplatform]
} -constraints {testsetplatform} -body {
    testsetplatform unix
    file attributes ~_totally_bogus_user
} -returnCodes error -cleanup {
    testsetplatform $platform
} -result {user "_totally_bogus_user" doesn't exist}
test fCmd-27.3 {TclFileAttrsCmd - all attributes} -setup {
    catch {file delete -force -- foo.tmp}
} -body {
    createfile foo.tmp
    file attributes foo.tmp
    # Must be non-empty result
} -cleanup {







|







2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
test fCmd-27.2 {TclFileAttrsCmd - Tcl_TranslateFileName fails} -setup {
    set platform [testgetplatform]
} -constraints {testsetplatform} -body {
    testsetplatform unix
    file attributes ~_totally_bogus_user
} -returnCodes error -cleanup {
    testsetplatform $platform
} -result {could not read "~_totally_bogus_user": no such file or directory}
test fCmd-27.3 {TclFileAttrsCmd - all attributes} -setup {
    catch {file delete -force -- foo.tmp}
} -body {
    createfile foo.tmp
    file attributes foo.tmp
    # Must be non-empty result
} -cleanup {
2552
2553
2554
2555
2556
2557
2558






























































































































2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
        set path $env(SystemDrive)/pagefile.sys
        lappend r exists [file exists $path]
        lappend r readable [file readable $path]
        lappend r stat [catch {file stat $path a} e] $e
    }
    return $r
} -result {exists 1 readable 0 stat 0 {}}































































































































# cleanup
cleanup
if {[testConstraint unix]} {
    removeDirectory tcl[pid] /tmp
}
::tcltest::cleanupTests
return

# Local Variables:
# mode: tcl
# fill-column: 78
# End:







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













2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
        set path $env(SystemDrive)/pagefile.sys
        lappend r exists [file exists $path]
        lappend r readable [file readable $path]
        lappend r stat [catch {file stat $path a} e] $e
    }
    return $r
} -result {exists 1 readable 0 stat 0 {}}

test fCmd-31.1 {file home} -body {
    file home
} -result [file join $::env(HOME)]
test fCmd-31.2 {file home - obeys env} -setup {
    set ::env(HOME) $::env(HOME)/xxx
} -cleanup {
    set ::env(HOME) [file dirname $::env(HOME)]
} -body {
    file home
} -result [file join $::env(HOME) xxx]
test fCmd-31.3 {file home - \ -> /} -constraints win -setup {
    set saved $::env(HOME)
    set ::env(HOME) C:\\backslash\\path
} -cleanup {
    set ::env(HOME) $saved
} -body {
    file home
} -result C:/backslash/path
test fCmd-31.4 {file home - error} -setup {
    set saved $::env(HOME)
    unset ::env(HOME)
} -cleanup {
    set ::env(HOME) $saved
} -body {
    file home
} -returnCodes error -result {couldn't find HOME environment variable to expand path}
test fCmd-31.5 {
    file home - relative path. Following 8.x ~ expansion behavior, relative
    paths are not made absolute
} -setup {
    set saved $::env(HOME)
    set ::env(HOME) relative/path
} -cleanup {
    set ::env(HOME) $saved
} -body {
    file home
} -result relative/path
test fCmd-31.6 {file home USER} -body {
    # Note - as in 8.x this form does NOT necessarily give same result as
    # env(HOME) even when user is current user. Assume result contains user
    # name, else not sure how to check
    file home $::tcl_platform(user)
} -match glob -result "*$::tcl_platform(user)*"
test fCmd-31.7 {file home UNKNOWNUSER} -body {
    file home nosuchuser
} -returnCodes error -result {user "nosuchuser" doesn't exist}
test fCmd-31.8 {file home extra arg} -body {
    file home $::tcl_platform(user) arg
} -returnCodes error -result {wrong # args: should be "file home ?user?"}

test fCmd-32.1 {file tildeexpand ~} -body {
    file tildeexpand ~
} -result [file join $::env(HOME)]
test fCmd-32.2 {file tildeexpand ~ - obeys env} -setup {
    set ::env(HOME) $::env(HOME)/xxx
} -cleanup {
    set ::env(HOME) [file dirname $::env(HOME)]
} -body {
    file tildeexpand ~
} -result [file join $::env(HOME) xxx]
test fCmd-32.3 {file tildeexpand ~ - error} -setup {
    set saved $::env(HOME)
    unset ::env(HOME)
} -cleanup {
    set ::env(HOME) $saved
} -body {
    file tildeexpand ~
} -returnCodes error -result {couldn't find HOME environment variable to expand path}
test fCmd-32.4 {
    file tildeexpand ~ - relative path. Following 8.x ~ expansion behavior, relative
    paths are not made absolute
} -setup {
    set saved $::env(HOME)
    set ::env(HOME) relative/path
} -cleanup {
    set ::env(HOME) $saved
} -body {
    file tildeexpand ~
} -result relative/path
test fCmd-32.5 {file tildeexpand ~USER} -body {
    # Note - as in 8.x this form does NOT necessarily give same result as
    # env(HOME) even when user is current user. Assume result contains user
    # name, else not sure how to check
    file tildeexpand ~$::tcl_platform(user)
} -match glob -result "*$::tcl_platform(user)*"
test fCmd-32.6 {file tildeexpand ~UNKNOWNUSER} -body {
    file tildeexpand ~nosuchuser
} -returnCodes error -result {user "nosuchuser" doesn't exist}
test fCmd-32.7 {file tildeexpand ~extra arg} -body {
    file tildeexpand ~ arg
} -returnCodes error -result {wrong # args: should be "file tildeexpand path"}
test fCmd-32.8 {file tildeexpand ~/path} -body {
    file tildeexpand ~/foo
} -result [file join $::env(HOME)/foo]
test fCmd-32.9 {file tildeexpand ~USER/bar} -body {
    # Note - as in 8.x this form does NOT necessarily give same result as
    # env(HOME) even when user is current user. Assume result contains user
    # name, else not sure how to check
    file tildeexpand ~$::tcl_platform(user)/bar
} -match glob -result "*$::tcl_platform(user)*/bar"
test fCmd-32.10 {file tildeexpand ~UNKNOWNUSER} -body {
    file tildeexpand ~nosuchuser/foo
} -returnCodes error -result {user "nosuchuser" doesn't exist}
test fCmd-32.11 {file tildeexpand /~/path} -body {
    file tildeexpand /~/foo
} -result /~/foo
test fCmd-32.12 {file tildeexpand /~user/path} -body {
    file tildeexpand /~$::tcl_platform(user)/foo
} -result /~$::tcl_platform(user)/foo
test fCmd-32.13 {file tildeexpand ./~} -body {
    file tildeexpand ./~
} -result ./~
test fCmd-32.14 {file tildeexpand relative/path} -body {
    file tildeexpand relative/path
} -result relative/path
test fCmd-32.15 {file tildeexpand ~\\path} -body {
    file tildeexpand ~\\foo
} -constraints win -result [file join $::env(HOME)/foo]
test fCmd-32.16 {file tildeexpand ~USER\\bar} -body {
    # Note - as in 8.x this form does NOT necessarily give same result as
    # env(HOME) even when user is current user. Assume result contains user
    # name, else not sure how to check
    file tildeexpand ~$::tcl_platform(user)\\bar
} -constraints win -match glob -result "*$::tcl_platform(user)*/bar"


# cleanup
cleanup
if {[testConstraint unix]} {
    removeDirectory tcl[pid] /tmp
}
::tcltest::cleanupTests
return

# Local Variables:
# mode: tcl
# fill-column: 78
# End:
Changes to tests/fileName.test.
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
test filename-1.4 {Tcl_GetPathType: unix} {testsetplatform} {
    testsetplatform unix
    file pathtype c:/foo
} relative
test filename-1.5 {Tcl_GetPathType: unix} {testsetplatform} {
    testsetplatform unix
    file pathtype ~
} absolute
test filename-1.6 {Tcl_GetPathType: unix} {testsetplatform} {
    testsetplatform unix
    file pathtype ~/foo
} absolute
test filename-1.7 {Tcl_GetPathType: unix} {testsetplatform} {
    testsetplatform unix
    file pathtype ~foo
} absolute
test filename-1.8 {Tcl_GetPathType: unix} {testsetplatform} {
    testsetplatform unix
    file pathtype ./~foo
} relative

test filename-3.1 {Tcl_GetPathType: windows} {testsetplatform} {
    testsetplatform windows







|



|



|







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
test filename-1.4 {Tcl_GetPathType: unix} {testsetplatform} {
    testsetplatform unix
    file pathtype c:/foo
} relative
test filename-1.5 {Tcl_GetPathType: unix} {testsetplatform} {
    testsetplatform unix
    file pathtype ~
} relative
test filename-1.6 {Tcl_GetPathType: unix} {testsetplatform} {
    testsetplatform unix
    file pathtype ~/foo
} relative
test filename-1.7 {Tcl_GetPathType: unix} {testsetplatform} {
    testsetplatform unix
    file pathtype ~foo
} relative
test filename-1.8 {Tcl_GetPathType: unix} {testsetplatform} {
    testsetplatform unix
    file pathtype ./~foo
} relative

test filename-3.1 {Tcl_GetPathType: windows} {testsetplatform} {
    testsetplatform windows
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
test filename-3.12 {Tcl_GetPathType: windows} {testsetplatform} {
    testsetplatform windows
    file pathtype //foo/bar
} absolute
test filename-3.13 {Tcl_GetPathType: windows} {testsetplatform} {
    testsetplatform windows
    file pathtype ~foo
} absolute
test filename-3.14 {Tcl_GetPathType: windows} {testsetplatform} {
    testsetplatform windows
    file pathtype ~
} absolute
test filename-3.15 {Tcl_GetPathType: windows} {testsetplatform} {
    testsetplatform windows
    file pathtype ~/foo
} absolute
test filename-3.16 {Tcl_GetPathType: windows} {testsetplatform} {
    testsetplatform windows
    file pathtype ./~foo
} relative

test filename-4.1 {Tcl_SplitPath: unix} {testsetplatform} {
    testsetplatform unix







|



|



|







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
test filename-3.12 {Tcl_GetPathType: windows} {testsetplatform} {
    testsetplatform windows
    file pathtype //foo/bar
} absolute
test filename-3.13 {Tcl_GetPathType: windows} {testsetplatform} {
    testsetplatform windows
    file pathtype ~foo
} relative
test filename-3.14 {Tcl_GetPathType: windows} {testsetplatform} {
    testsetplatform windows
    file pathtype ~
} relative
test filename-3.15 {Tcl_GetPathType: windows} {testsetplatform} {
    testsetplatform windows
    file pathtype ~/foo
} relative
test filename-3.16 {Tcl_GetPathType: windows} {testsetplatform} {
    testsetplatform windows
    file pathtype ./~foo
} relative

test filename-4.1 {Tcl_SplitPath: unix} {testsetplatform} {
    testsetplatform unix
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
test filename-4.15 {Tcl_SplitPath: unix} {testsetplatform} {
    testsetplatform unix
    file split ~foo
} {~foo}
test filename-4.16 {Tcl_SplitPath: unix} {testsetplatform} {
    testsetplatform unix
    file split ~foo/~bar
} {~foo ./~bar}
test filename-4.17 {Tcl_SplitPath: unix} {testsetplatform} {
    testsetplatform unix
    file split ~foo/~bar/~baz
} {~foo ./~bar ./~baz}
test filename-4.18 {Tcl_SplitPath: unix} {testsetplatform} {
    testsetplatform unix
    file split foo/bar~/baz
} {foo bar~ baz}
if {[testConstraint testsetplatform]} {
    testsetplatform $platform
}







|



|







209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
test filename-4.15 {Tcl_SplitPath: unix} {testsetplatform} {
    testsetplatform unix
    file split ~foo
} {~foo}
test filename-4.16 {Tcl_SplitPath: unix} {testsetplatform} {
    testsetplatform unix
    file split ~foo/~bar
} {~foo ~bar}
test filename-4.17 {Tcl_SplitPath: unix} {testsetplatform} {
    testsetplatform unix
    file split ~foo/~bar/~baz
} {~foo ~bar ~baz}
test filename-4.18 {Tcl_SplitPath: unix} {testsetplatform} {
    testsetplatform unix
    file split foo/bar~/baz
} {foo bar~ baz}
if {[testConstraint testsetplatform]} {
    testsetplatform $platform
}
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
test filename-6.26 {Tcl_SplitPath: win} {testsetplatform} {
    testsetplatform win
    file split ~foo
} {~foo}
test filename-6.27 {Tcl_SplitPath: win} {testsetplatform} {
    testsetplatform win
    file split ~foo/~bar
} {~foo ./~bar}
test filename-6.28 {Tcl_SplitPath: win} {testsetplatform} {
    testsetplatform win
    file split ~foo/~bar/~baz
} {~foo ./~bar ./~baz}
test filename-6.29 {Tcl_SplitPath: win} {testsetplatform} {
    testsetplatform win
    file split foo/bar~/baz
} {foo bar~ baz}
test filename-6.30 {Tcl_SplitPath: win} {testsetplatform} {
    testsetplatform win
    file split c:~foo
} {c: ./~foo}

test filename-7.1 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join / a
} {/a}
test filename-7.2 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix







|



|







|







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
test filename-6.26 {Tcl_SplitPath: win} {testsetplatform} {
    testsetplatform win
    file split ~foo
} {~foo}
test filename-6.27 {Tcl_SplitPath: win} {testsetplatform} {
    testsetplatform win
    file split ~foo/~bar
} {~foo ~bar}
test filename-6.28 {Tcl_SplitPath: win} {testsetplatform} {
    testsetplatform win
    file split ~foo/~bar/~baz
} {~foo ~bar ~baz}
test filename-6.29 {Tcl_SplitPath: win} {testsetplatform} {
    testsetplatform win
    file split foo/bar~/baz
} {foo bar~ baz}
test filename-6.30 {Tcl_SplitPath: win} {testsetplatform} {
    testsetplatform win
    file split c:~foo
} {c: ~foo}

test filename-7.1 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join / a
} {/a}
test filename-7.2 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
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
test filename-7.10 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join ~ a
} {~/a}
test filename-7.11 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join ~a ~b
} {~b}
test filename-7.12 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join ./~a b
} {./~a/b}
test filename-7.13 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join ./~a ~b
} {~b}
test filename-7.14 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join ./~a ./~b
} {./~a/~b}
test filename-7.15 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join a . b
} {a/./b}
test filename-7.16 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join a . ./~b
} {a/./~b}
test filename-7.17 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join //a b
} "/a/b"
test filename-7.18 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join /// a b







|







|



|







|







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
test filename-7.10 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join ~ a
} {~/a}
test filename-7.11 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join ~a ~b
} {~a/~b}
test filename-7.12 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join ./~a b
} {./~a/b}
test filename-7.13 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join ./~a ~b
} {./~a/~b}
test filename-7.14 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join ./~a ./~b
} {./~a/./~b}
test filename-7.15 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join a . b
} {a/./b}
test filename-7.16 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join a . ./~b
} {a/././~b}
test filename-7.17 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join //a b
} "/a/b"
test filename-7.18 {Tcl_JoinPath: unix} {testsetplatform} {
    testsetplatform unix
    file join /// a b
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
test filename-9.10 {Tcl_JoinPath: win} {testsetplatform} {
    testsetplatform win
    file join ~/~foo
} {~/~foo}
test filename-9.11 {Tcl_JoinPath: win} {testsetplatform} {
    testsetplatform win
    file join ~ ./~foo
} {~/~foo}
test filename-9.12 {Tcl_JoinPath: win} {testsetplatform} {
    testsetplatform win
    file join / ~foo
} {~foo}
test filename-9.13 {Tcl_JoinPath: win} {testsetplatform} {
    testsetplatform win
    file join ./a/ b c
} {./a/b/c}
test filename-9.14 {Tcl_JoinPath: win} {testsetplatform} {
    testsetplatform win
    file join ./~a/ b c







|



|







486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
test filename-9.10 {Tcl_JoinPath: win} {testsetplatform} {
    testsetplatform win
    file join ~/~foo
} {~/~foo}
test filename-9.11 {Tcl_JoinPath: win} {testsetplatform} {
    testsetplatform win
    file join ~ ./~foo
} {~/./~foo}
test filename-9.12 {Tcl_JoinPath: win} {testsetplatform} {
    testsetplatform win
    file join / ~foo
} {/~foo}
test filename-9.13 {Tcl_JoinPath: win} {testsetplatform} {
    testsetplatform win
    file join ./a/ b c
} {./a/b/c}
test filename-9.14 {Tcl_JoinPath: win} {testsetplatform} {
    testsetplatform win
    file join ./~a/ b c
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
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
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "/home/test"
    testsetplatform unix
    testtranslatefilename ~/foo
} -cleanup {
    set env(HOME) $temp
} -result {/home/test/foo}
test filename-10.7 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    unset env(HOME)
    testsetplatform unix
    testtranslatefilename ~/foo
} -returnCodes error -cleanup {
    set env(HOME) $temp
} -result {couldn't find HOME environment variable to expand path}
test filename-10.8 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "/home/test"
    testsetplatform unix
    testtranslatefilename ~
} -cleanup {
    set env(HOME) $temp
} -result {/home/test}
test filename-10.9 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "/home/test/"
    testsetplatform unix
    testtranslatefilename ~
} -cleanup {
    set env(HOME) $temp
} -result {/home/test}
test filename-10.10 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "/home/test/"
    testsetplatform unix
    testtranslatefilename ~/foo
} -cleanup {
    set env(HOME) $temp
} -result {/home/test/foo}
test filename-10.17 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "\\home\\"
    testsetplatform windows
    testtranslatefilename ~/foo
} -cleanup {
    set env(HOME) $temp
} -result {\home\foo}
test filename-10.18 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "\\home\\"
    testsetplatform windows
    testtranslatefilename ~/foo\\bar
} -cleanup {
    set env(HOME) $temp
} -result {\home\foo\bar}
test filename-10.19 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "c:"
    testsetplatform windows
    testtranslatefilename ~/foo
} -cleanup {
    set env(HOME) $temp
} -result {c:foo}
test filename-10.20 {Tcl_TranslateFileName} -returnCodes error -body {
    testtranslatefilename ~blorp/foo
} -constraints {testtranslatefilename testtranslatefilename} \
    -result {user "blorp" doesn't exist}
test filename-10.21 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "c:\\"
    testsetplatform windows
    testtranslatefilename ~/foo
} -cleanup {
    set env(HOME) $temp
} -result {c:\foo}
test filename-10.22 {Tcl_TranslateFileName} -body {
    testsetplatform windows
    testtranslatefilename foo//bar
} -constraints {testsetplatform testtranslatefilename} -result {foo\bar}
if {[testConstraint testsetplatform]} {
    testsetplatform $platform
}







|







|

|









|









|









|









|









|









|
|


|









|







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
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "/home/test"
    testsetplatform unix
    testtranslatefilename ~/foo
} -cleanup {
    set env(HOME) $temp
} -result {~/foo}
test filename-10.7 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    unset env(HOME)
    testsetplatform unix
    testtranslatefilename ~/foo
} -cleanup {
    set env(HOME) $temp
} -result {~/foo}
test filename-10.8 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "/home/test"
    testsetplatform unix
    testtranslatefilename ~
} -cleanup {
    set env(HOME) $temp
} -result {~}
test filename-10.9 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "/home/test/"
    testsetplatform unix
    testtranslatefilename ~
} -cleanup {
    set env(HOME) $temp
} -result {~}
test filename-10.10 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "/home/test/"
    testsetplatform unix
    testtranslatefilename ~/foo
} -cleanup {
    set env(HOME) $temp
} -result {~/foo}
test filename-10.17 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "\\home\\"
    testsetplatform windows
    testtranslatefilename ~/foo
} -cleanup {
    set env(HOME) $temp
} -result {~\foo}
test filename-10.18 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "\\home\\"
    testsetplatform windows
    testtranslatefilename ~/foo\\bar
} -cleanup {
    set env(HOME) $temp
} -result {~\foo\bar}
test filename-10.19 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "c:"
    testsetplatform windows
    testtranslatefilename ~/foo
} -cleanup {
    set env(HOME) $temp
} -result {~\foo}
test filename-10.20 {Tcl_TranslateFileName} -body {
    testtranslatefilename ~blorp/foo
} -constraints {testtranslatefilename testtranslatefilename} \
    -result {~blorp\foo}
test filename-10.21 {Tcl_TranslateFileName} -setup {
    global env
    set temp $env(HOME)
} -constraints {testsetplatform testtranslatefilename} -body {
    set env(HOME) "c:\\"
    testsetplatform windows
    testtranslatefilename ~/foo
} -cleanup {
    set env(HOME) $temp
} -result {~\foo}
test filename-10.22 {Tcl_TranslateFileName} -body {
    testsetplatform windows
    testtranslatefilename foo//bar
} -constraints {testsetplatform testtranslatefilename} -result {foo\bar}
if {[testConstraint testsetplatform]} {
    testsetplatform $platform
}
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
} -result {bad option "-gorp": must be -directory, -join, -nocomplain, -path, -tails, -types, or --}
test filename-11.3 {Tcl_GlobCmd} -body {
    glob -nocomplai
} -result {}
test filename-11.4 {Tcl_GlobCmd} -body {
    glob -nocomplain
} -result {}
test filename-11.5 {Tcl_GlobCmd} -returnCodes error -body {

    glob -nocomplain * ~xyqrszzz
} -result {user "xyqrszzz" doesn't exist}
test filename-11.6 {Tcl_GlobCmd} -returnCodes error -body {
    glob ~xyqrszzz
} -result {user "xyqrszzz" doesn't exist}
test filename-11.7 {Tcl_GlobCmd} -returnCodes error -body {
    glob -- -nocomplain
} -result {no files matched glob pattern "-nocomplain"}
test filename-11.8 {Tcl_GlobCmd} -body {
    glob -nocomplain -- -nocomplain
} -result {}
test filename-11.9 {Tcl_GlobCmd} -constraints {testsetplatform} -body {
    testsetplatform unix
    glob ~\\xyqrszzz/bar
} -returnCodes error -result {user "\xyqrszzz" doesn't exist}
test filename-11.10 {Tcl_GlobCmd} -constraints {testsetplatform} -body {
    testsetplatform unix
    glob -nocomplain ~\\xyqrszzz/bar
} -returnCodes error -result {user "\xyqrszzz" doesn't exist}
test filename-11.11 {Tcl_GlobCmd} -constraints {testsetplatform} -body {
    testsetplatform unix
    glob ~xyqrszzz\\/\\bar
} -returnCodes error -result {user "xyqrszzz" doesn't exist}
test filename-11.12 {Tcl_GlobCmd} -constraints {testsetplatform} -setup {
    testsetplatform unix
    set home $env(HOME)
} -body {
    unset env(HOME)
    glob ~/*
} -returnCodes error -cleanup {
    set env(HOME) $home
} -result {couldn't find HOME environment variable to expand path}
if {[testConstraint testsetplatform]} {
    testsetplatform $platform
}
test filename-11.13 {Tcl_GlobCmd} {
    file join [lindex [glob ~] 0]
} [file join $env(HOME)]
set oldpwd [pwd]
set oldhome $env(HOME)
catch {cd [makeDirectory tcl[pid]]}
set env(HOME) [pwd]
file delete -force globTest
file mkdir globTest/a1/b1
file mkdir globTest/a1/b2
file mkdir globTest/a2/b3
file mkdir globTest/a3
touch globTest/x1.c
touch globTest/y1.c
touch globTest/z1.c
touch "globTest/weird name.c"
touch globTest/a1/b1/x2.c
touch globTest/a1/b2/y2.c
touch globTest/.1
touch globTest/x,z1.c
test filename-11.14 {Tcl_GlobCmd} {
    glob ~/globTest
} [list [file join $env(HOME) globTest]]
test filename-11.15 {Tcl_GlobCmd} {
    glob ~\\/globTest
} [list [file join $env(HOME) globTest]]
test filename-11.16 {Tcl_GlobCmd} {
    glob globTest
} {globTest}
set globname "globTest"
set horribleglobname "glob\[\{Test"
set tildeglobname "./~test.txt"








|
>
|
|


|









|



|



|








|



|

|

















|

|
|

|







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
} -result {bad option "-gorp": must be -directory, -join, -nocomplain, -path, -tails, -types, or --}
test filename-11.3 {Tcl_GlobCmd} -body {
    glob -nocomplai
} -result {}
test filename-11.4 {Tcl_GlobCmd} -body {
    glob -nocomplain
} -result {}
test filename-11.5 {Tcl_GlobCmd} -body {
    # Should not error out because of ~
    catch {glob -nocomplain * ~xyqrszzz}
} -result 0
test filename-11.6 {Tcl_GlobCmd} -returnCodes error -body {
    glob ~xyqrszzz
} -result {no files matched glob pattern "~xyqrszzz"}
test filename-11.7 {Tcl_GlobCmd} -returnCodes error -body {
    glob -- -nocomplain
} -result {no files matched glob pattern "-nocomplain"}
test filename-11.8 {Tcl_GlobCmd} -body {
    glob -nocomplain -- -nocomplain
} -result {}
test filename-11.9 {Tcl_GlobCmd} -constraints {testsetplatform} -body {
    testsetplatform unix
    glob ~\\xyqrszzz/bar
} -returnCodes error -result {no files matched glob pattern "~\xyqrszzz/bar"}
test filename-11.10 {Tcl_GlobCmd} -constraints {testsetplatform} -body {
    testsetplatform unix
    glob -nocomplain ~\\xyqrszzz/bar
} -result {}
test filename-11.11 {Tcl_GlobCmd} -constraints {testsetplatform} -body {
    testsetplatform unix
    glob ~xyqrszzz\\/\\bar
} -returnCodes error -result {no files matched glob pattern "~xyqrszzz\/\bar"}
test filename-11.12 {Tcl_GlobCmd} -constraints {testsetplatform} -setup {
    testsetplatform unix
    set home $env(HOME)
} -body {
    unset env(HOME)
    glob ~/*
} -returnCodes error -cleanup {
    set env(HOME) $home
} -result {no files matched glob pattern "~/*"}
if {[testConstraint testsetplatform]} {
    testsetplatform $platform
}
test filename-11.13 {Tcl_GlobCmd} -body {
    file join [lindex [glob ~] 0]
} -returnCodes error -result {no files matched glob pattern "~"}
set oldpwd [pwd]
set oldhome $env(HOME)
catch {cd [makeDirectory tcl[pid]]}
set env(HOME) [pwd]
file delete -force globTest
file mkdir globTest/a1/b1
file mkdir globTest/a1/b2
file mkdir globTest/a2/b3
file mkdir globTest/a3
touch globTest/x1.c
touch globTest/y1.c
touch globTest/z1.c
touch "globTest/weird name.c"
touch globTest/a1/b1/x2.c
touch globTest/a1/b2/y2.c
touch globTest/.1
touch globTest/x,z1.c
test filename-11.14 {Tcl_GlobCmd} -body {
    glob ~/globTest
} -returnCodes error -result {no files matched glob pattern "~/globTest"}
test filename-11.15 {Tcl_GlobCmd} -body {
    glob ~\\/globTest
} -returnCodes error -result {no files matched glob pattern "~\/globTest"}
test filename-11.16 {Tcl_GlobCmd} {
    glob globTest
} {globTest}
set globname "globTest"
set horribleglobname "glob\[\{Test"
set tildeglobname "./~test.txt"

1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
    lsort [glob globTest/*/]
} {globTest/a1/ globTest/a2/ globTest/a3/}
test filename-14.17 {asterisks, question marks, and brackets} -setup {
    global env
    set temp $env(HOME)
} -body {
    set env(HOME) [file join $env(HOME) globTest]
    glob ~/z*
} -cleanup {
    set env(HOME) $temp
} -result [list [file join $env(HOME) globTest z1.c]]
test filename-14.18 {asterisks, question marks, and brackets} {unixOrWin} {
    lsort [glob globTest/*.c goo/*]
} {{globTest/weird name.c} globTest/x,z1.c globTest/x1.c globTest/y1.c globTest/z1.c}
test filename-14.20 {asterisks, question marks, and brackets} {







|







1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
    lsort [glob globTest/*/]
} {globTest/a1/ globTest/a2/ globTest/a3/}
test filename-14.17 {asterisks, question marks, and brackets} -setup {
    global env
    set temp $env(HOME)
} -body {
    set env(HOME) [file join $env(HOME) globTest]
    glob [file home]/z*
} -cleanup {
    set env(HOME) $temp
} -result [list [file join $env(HOME) globTest z1.c]]
test filename-14.18 {asterisks, question marks, and brackets} {unixOrWin} {
    lsort [glob globTest/*.c goo/*]
} {{globTest/weird name.c} globTest/x,z1.c globTest/x1.c globTest/y1.c globTest/z1.c}
test filename-14.20 {asterisks, question marks, and brackets} {
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
test filename-15.4 {unix specific no complain: no errors, good result} \
	{unix nonPortable} {
    # test fails because if an error occurs, the interp's result is reset...
    # or you don't run at scriptics where the outser and welch users exists
    glob -nocomplain ~ouster ~foo ~welch
} {/home/ouster /home/welch}
test filename-15.4.1 {no complain: errors, sequencing} {
    # test used to fail because if an error occurs, the interp's result is
    # reset... But, the sequence means we throw a different error first.
    list [catch {glob -nocomplain ~wontexist ~blahxyz ~} res1] $res1 \
	[catch {glob -nocomplain ~ ~blahxyz ~wontexist} res2] $res2
} {1 {user "wontexist" doesn't exist} 1 {user "blahxyz" doesn't exist}}

test filename-15.4.2 {no complain: errors, sequencing} -body {
    # test used to fail because if an error occurs, the interp's result is
    # reset...
    list [list [catch {glob -nocomplain ~wontexist *} res1] $res1] \
	[list [catch {glob -nocomplain * ~wontexist} res2] $res2]
} -match compareWords -result equal
test filename-15.5 {unix specific globbing} {unix nonPortable} {
    glob ~ouster/.csh*
} "/home/ouster/.cshrc"
touch globTest/odd\\\[\]*?\{\}name
test filename-15.6 {unix specific globbing} -constraints {unix} -setup {
    global env
    set temp $env(HOME)
} -body {
    set env(HOME) $env(HOME)/globTest/odd\\\[\]*?\{\}name
    glob ~
} -cleanup {
    set env(HOME) $temp
} -result [list [lindex [glob ~] 0]/globTest/odd\\\[\]*?\{\}name]
catch {file delete -force globTest/odd\\\[\]*?\{\}name}
test filename-15.7 {win specific globbing} -constraints {win} -body {
    glob ~
} -match regexp -result {[^/]$}
test filename-15.8 {win and unix specific globbing} -constraints {unixOrWin} -setup {
    global env
    set temp $env(HOME)
} -body {
    touch $env(HOME)/globTest/anyname
    set env(HOME) $env(HOME)/globTest/anyname
    glob ~
} -cleanup {
    set env(HOME) $temp
    catch {file delete -force $env(HOME)/globTest/anyname}
} -result [list [lindex [glob ~] 0]/globTest/anyname]

# The following tests are only valid for Windows systems.
set oldDir [pwd]
if {[testConstraint win]} {
    cd c:/
    file delete -force globTest
    file mkdir globTest







|
<


<
>









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

|










|







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
test filename-15.4 {unix specific no complain: no errors, good result} \
	{unix nonPortable} {
    # test fails because if an error occurs, the interp's result is reset...
    # or you don't run at scriptics where the outser and welch users exists
    glob -nocomplain ~ouster ~foo ~welch
} {/home/ouster /home/welch}
test filename-15.4.1 {no complain: errors, sequencing} {
    # ~xxx no longer expanded so errors about unknown users should not occur

    list [catch {glob -nocomplain ~wontexist ~blahxyz ~} res1] $res1 \
	[catch {glob -nocomplain ~ ~blahxyz ~wontexist} res2] $res2

} {0 {} 0 {}}
test filename-15.4.2 {no complain: errors, sequencing} -body {
    # test used to fail because if an error occurs, the interp's result is
    # reset...
    list [list [catch {glob -nocomplain ~wontexist *} res1] $res1] \
	[list [catch {glob -nocomplain * ~wontexist} res2] $res2]
} -match compareWords -result equal
test filename-15.5 {unix specific globbing} {unix nonPortable} {
    glob ~ouster/.csh*
} "/home/ouster/.cshrc"
# 15.6 removed. It checked if glob ~ returned valid information if

# home directory contained glob chars. Since ~ expansion is no longer



# supported, the test was meaningless




test filename-15.7 {glob tilde} -body {
    glob ~
} -returnCodes error -result {no files matched glob pattern "~"}
test filename-15.8 {win and unix specific globbing} -constraints {unixOrWin} -setup {
    global env
    set temp $env(HOME)
} -body {
    touch $env(HOME)/globTest/anyname
    set env(HOME) $env(HOME)/globTest/anyname
    glob ~
} -cleanup {
    set env(HOME) $temp
    catch {file delete -force $env(HOME)/globTest/anyname}
} -returnCodes error -result {no files matched glob pattern "~"}

# The following tests are only valid for Windows systems.
set oldDir [pwd]
if {[testConstraint win]} {
    cd c:/
    file delete -force globTest
    file mkdir globTest
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
    removeFile test $d
    removeDirectory ./~foo $dd
    removeDirectory isolate
} -result ~foo/test
test fileName-20.6 {Bug 2837800} -setup {
    # Recall that we have $env(HOME) set so that references
    # to ~ point to [temporaryDirectory]
    makeFile {} test ~
    set dd [makeDirectory isolate]
    set d [makeDirectory ./~ $dd]
    set savewd [pwd]
    cd $dd
} -body {
    glob -nocomplain */test
} -cleanup {







|







1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
    removeFile test $d
    removeDirectory ./~foo $dd
    removeDirectory isolate
} -result ~foo/test
test fileName-20.6 {Bug 2837800} -setup {
    # Recall that we have $env(HOME) set so that references
    # to ~ point to [temporaryDirectory]
    makeFile {} test [file home]
    set dd [makeDirectory isolate]
    set d [makeDirectory ./~ $dd]
    set savewd [pwd]
    cd $dd
} -body {
    glob -nocomplain */test
} -cleanup {
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
    makeFile {} ./~test $d
} -body {
    file tail [lindex [glob -nocomplain isolate/*] 0]
} -cleanup {
    removeFile ./~test $d
    removeDirectory isolate
    cd $savewd
} -result ./~test
test fileName-20.9 {globbing for special chars} -setup {
    makeFile {} test ~
    set d [makeDirectory isolate]
    set savewd [pwd]
    cd $d
} -body {
   glob -nocomplain -directory ~ test
} -cleanup {
    cd $savewd
    removeDirectory isolate
    removeFile test ~
} -result ~/test
test fileName-20.10 {globbing for special chars} -setup {
    set s [makeDirectory sub ~]
    makeFile {} fileName-20.10 $s
    set d [makeDirectory isolate]
    set savewd [pwd]
    cd $d
} -body {
   glob -nocomplain -directory ~ -join * fileName-20.10
} -cleanup {
    cd $savewd
    removeDirectory isolate
    removeFile fileName-20.10 $s
    removeDirectory sub ~
} -result ~/sub/fileName-20.10

# cleanup
catch {file delete -force C:/globTest}
cd [temporaryDirectory]
file delete -force globTest
cd $oldpwd
catch {removeDirectory tcl[pid]}







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

|





|




|
|







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
    makeFile {} ./~test $d
} -body {
    file tail [lindex [glob -nocomplain isolate/*] 0]
} -cleanup {
    removeFile ./~test $d
    removeDirectory isolate
    cd $savewd
} -result ~test












test fileName-20.10 {globbing for special chars} -setup {
    set s [makeDirectory sub [file home]]
    makeFile {} fileName-20.10 $s
    set d [makeDirectory isolate]
    set savewd [pwd]
    cd $d
} -body {
    glob -nocomplain -directory [file home] -join * fileName-20.10
} -cleanup {
    cd $savewd
    removeDirectory isolate
    removeFile fileName-20.10 $s
    removeDirectory sub [file home]
} -result [file home]/sub/fileName-20.10

# cleanup
catch {file delete -force C:/globTest}
cd [temporaryDirectory]
file delete -force globTest
cd $oldpwd
catch {removeDirectory tcl[pid]}
Changes to tests/fileSystem.test.
263
264
265
266
267
268
269
270


271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
file delete -force dir2.link
file delete -force link.file dir.link
file delete -force dir2
file delete -force [file join dir.dir dirinside.link]
removeFile [file join dir.dir inside.file]
removeDirectory [file join dir.dir dirinside.dir]
removeDirectory dir.dir
test filesystem-1.30 {normalisation of nonexistent user} -body {


    file normalize ~noonewiththisname
} -returnCodes error -result {user "noonewiththisname" doesn't exist}
test filesystem-1.30.1 {normalisation of existing user} -body {
    catch {file normalize ~$::tcl_platform(user)}
} -result {0}
test filesystem-1.30.2 {normalisation of nonexistent user specified as user@domain} -body {
    file normalize ~nonexistentuser@nonexistentdomain
} -returnCodes error -result {user "nonexistentuser@nonexistentdomain" doesn't exist}
test filesystem-1.31 {link normalisation: link near filesystem root} {testsetplatform} {
    testsetplatform unix
    file normalize /foo/../bar
} {/bar}
test filesystem-1.32 {link normalisation: link near filesystem root} {testsetplatform} {
    testsetplatform unix
    file normalize /../bar







|
>
>

|

|
|
<
<
<







263
264
265
266
267
268
269
270
271
272
273
274
275
276
277



278
279
280
281
282
283
284
file delete -force dir2.link
file delete -force link.file dir.link
file delete -force dir2
file delete -force [file join dir.dir dirinside.link]
removeFile [file join dir.dir inside.file]
removeDirectory [file join dir.dir dirinside.dir]
removeDirectory dir.dir
test filesystem-1.30 {
    normalisation of nonexistent user - verify no tilde expansion
} -body {
    file normalize ~noonewiththisname
} -result [file join [pwd] ~noonewiththisname]
test filesystem-1.30.1 {normalisation of existing user} -body {
    file normalize ~$::tcl_platform(user)
} -result [file join [pwd] ~$::tcl_platform(user)]



test filesystem-1.31 {link normalisation: link near filesystem root} {testsetplatform} {
    testsetplatform unix
    file normalize /foo/../bar
} {/bar}
test filesystem-1.32 {link normalisation: link near filesystem root} {testsetplatform} {
    testsetplatform unix
    file normalize /../bar
469
470
471
472
473
474
475

476


477
478
479
480
481
482
483
    testfilesystem 1
    set filesystemReport {}
    catch {glob *}
    testfilesystem 0
    return $filesystemReport
} -match glob -result {*{matchindirectory *}*}


test filesystem-5.1 {cache and ~} -constraints testfilesystem -setup {


    set orig $::env(HOME)
} -body {
    set ::env(HOME) /foo/bar/blah
    set testdir ~
    set res1 "Parent of ~ (/foo/bar/blah) is [file dirname $testdir]"
    set ::env(HOME) /a/b/c
    set res2 "Parent of ~ (/a/b/c) is [file dirname $testdir]"







>
|
>
>







468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
    testfilesystem 1
    set filesystemReport {}
    catch {glob *}
    testfilesystem 0
    return $filesystemReport
} -match glob -result {*{matchindirectory *}*}

# This test is meaningless if there is no tilde expansion
test filesystem-5.1 {cache and ~} -constraints {
    testfilesystem tildeexpansion
} -setup {
    set orig $::env(HOME)
} -body {
    set ::env(HOME) /foo/bar/blah
    set testdir ~
    set res1 "Parent of ~ (/foo/bar/blah) is [file dirname $testdir]"
    set ::env(HOME) /a/b/c
    set res2 "Parent of ~ (/a/b/c) is [file dirname $testdir]"
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
    lappend res $file
    lappend res [file exists $file] [catch {file tail $file} r] $r
    lappend res [catch {file tail $file} r] $r
} -cleanup {
    cd [tcltest::temporaryDirectory]
    file delete -force tilde
    cd $origdir
} -result {0 1 {user "testNotExist" doesn't exist} ~testNotExist 0 1 {user "testNotExist" doesn't exist} 1 {user "testNotExist" doesn't exist}}
test filesystem-9.8 {path objects and glob and file tail and tilde} -setup {
    set res {}
    set origdir [pwd]
    cd [tcltest::temporaryDirectory]
} -body {
    file mkdir tilde
    close [open tilde/~testNotExist w]
    cd tilde
    set file1 [lindex [glob *test*] 0]
    set file2 "~testNotExist"
    lappend res $file1 $file2
    lappend res [catch {file tail $file1} r] $r
    lappend res [catch {file tail $file2} r] $r
} -cleanup {
    cd [tcltest::temporaryDirectory]
    file delete -force tilde
    cd $origdir
} -result {~testNotExist ~testNotExist 1 {user "testNotExist" doesn't exist} 1 {user "testNotExist" doesn't exist}}
test filesystem-9.9 {path objects and glob and file tail and tilde} -setup {
    set res {}
    set origdir [pwd]
    cd [tcltest::temporaryDirectory]
} -body {
    file mkdir tilde
    close [open tilde/~testNotExist w]
    cd tilde
    set file1 [lindex [glob *test*] 0]
    set file2 "~testNotExist"
    lappend res [catch {file exists $file1} r] $r
    lappend res [catch {file exists $file2} r] $r
    lappend res [string equal $file1 $file2]
} -cleanup {
    cd [tcltest::temporaryDirectory]
    file delete -force tilde
    cd $origdir
} -result {0 0 0 0 1}

# ----------------------------------------------------------------------

test filesystem-10.1 {Bug 3414754} {
    string match */ [file join [pwd] foo/]
} 0








|

















|

















|







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
    lappend res $file
    lappend res [file exists $file] [catch {file tail $file} r] $r
    lappend res [catch {file tail $file} r] $r
} -cleanup {
    cd [tcltest::temporaryDirectory]
    file delete -force tilde
    cd $origdir
} -result {1 0 ~testNotExist ~testNotExist 1 0 ~testNotExist 0 ~testNotExist}
test filesystem-9.8 {path objects and glob and file tail and tilde} -setup {
    set res {}
    set origdir [pwd]
    cd [tcltest::temporaryDirectory]
} -body {
    file mkdir tilde
    close [open tilde/~testNotExist w]
    cd tilde
    set file1 [lindex [glob *test*] 0]
    set file2 "~testNotExist"
    lappend res $file1 $file2
    lappend res [catch {file tail $file1} r] $r
    lappend res [catch {file tail $file2} r] $r
} -cleanup {
    cd [tcltest::temporaryDirectory]
    file delete -force tilde
    cd $origdir
} -result {~testNotExist ~testNotExist 0 ~testNotExist 0 ~testNotExist}
test filesystem-9.9 {path objects and glob and file tail and tilde} -setup {
    set res {}
    set origdir [pwd]
    cd [tcltest::temporaryDirectory]
} -body {
    file mkdir tilde
    close [open tilde/~testNotExist w]
    cd tilde
    set file1 [lindex [glob *test*] 0]
    set file2 "~testNotExist"
    lappend res [catch {file exists $file1} r] $r
    lappend res [catch {file exists $file2} r] $r
    lappend res [string equal $file1 $file2]
} -cleanup {
    cd [tcltest::temporaryDirectory]
    file delete -force tilde
    cd $origdir
} -result {0 1 0 1 1}

# ----------------------------------------------------------------------

test filesystem-10.1 {Bug 3414754} {
    string match */ [file join [pwd] foo/]
} 0

Changes to tests/http.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
# this file, and for a DISCLAIMER OF ALL WARRANTIES.

if {"::tcltest" ni [namespace children]} {
    package require tcltest 2.5
    namespace import -force ::tcltest::*
}

if {[catch {package require http 2} version]} {
    if {[info exists http2]} {
	catch {puts "Cannot load http 2.* package"}
	return
    } else {
	catch {puts "Running http 2.* tests in child interp"}
	set interp [interp create http2]
	$interp eval [list set http2 "running"]
	$interp eval [list set argv $argv]
	$interp eval [list source [info script]]
	interp delete $interp
	return
    }
}

proc bgerror {args} {
    global errorInfo
    puts stderr "http.test bgerror"
    puts stderr [join $args]
    puts stderr $errorInfo
}







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







12
13
14
15
16
17
18
19













20
21
22
23
24
25
26
# this file, and for a DISCLAIMER OF ALL WARRANTIES.

if {"::tcltest" ni [namespace children]} {
    package require tcltest 2.5
    namespace import -force ::tcltest::*
}

package require http 2.10














proc bgerror {args} {
    global errorInfo
    puts stderr "http.test bgerror"
    puts stderr [join $args]
    puts stderr $errorInfo
}
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
    # Let the OS pick the port; that's much more flexible
    if {[catch {httpd_init 0} listen]} {
	puts "Cannot start http server, http test skipped"
	catch {unset port}
	return
    }
}





















test http-1.1 {http::config} {
    http::config -useragent UserAgent
    http::config
} [list -accept */* -cookiejar {} -pipeline 1 -postfresh 0 -proxyfilter http::ProxyRequired -proxyhost {} -proxyport {} -repost 0 -urlencoding utf-8 -useragent UserAgent -zip 1]
test http-1.2 {http::config} {
    http::config -proxyfilter
} http::ProxyRequired
test http-1.3 {http::config} {
    catch {http::config -junk}
} 1
test http-1.4 {http::config} {
    set savedconf [http::config]
    http::config -proxyhost nowhere.come -proxyport 8080 \
	-proxyfilter myFilter -useragent "Tcl Test Suite" \
	-urlencoding iso8859-1
    set x [http::config]
    http::config {*}$savedconf
    set x
} {-accept */* -cookiejar {} -pipeline 1 -postfresh 0 -proxyfilter myFilter -proxyhost nowhere.come -proxyport 8080 -repost 0 -urlencoding iso8859-1 -useragent {Tcl Test Suite} -zip 1}
test http-1.5 {http::config} -returnCodes error -body {
    http::config -proxyhost {} -junk 8080
} -result {Unknown option -junk, must be: -accept, -cookiejar, -pipeline, -postfresh, -proxyfilter, -proxyhost, -proxyport, -repost, -urlencoding, -useragent, -zip}
test http-1.6 {http::config} -setup {
    set oldenc [http::config -urlencoding]
} -body {
    set enc [list [http::config -urlencoding]]
    http::config -urlencoding iso8859-1
    lappend enc [http::config -urlencoding]
} -cleanup {







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




|














|


|







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
    # Let the OS pick the port; that's much more flexible
    if {[catch {httpd_init 0} listen]} {
	puts "Cannot start http server, http test skipped"
	catch {unset port}
	return
    }
}

if {![info exists ThreadLevel]} {
    if {[catch {package require Thread}] == 0} {
        set ValueRange {0 1 2}
    } else {
        set ValueRange {0 1}
    }

    # For each value of ThreadLevel, source this file recursively in the
    # same interpreter.
    foreach ThreadLevel $ValueRange {
        source [info script]
    }
    catch {unset ThreadLevel}
    catch {unset ValueRange}
    return
}

catch {puts "==== Test with ThreadLevel $ThreadLevel ===="}
http::config -threadlevel $ThreadLevel

test http-1.1 {http::config} {
    http::config -useragent UserAgent
    http::config
} [list -accept */* -cookiejar {} -pipeline 1 -postfresh 0 -proxyfilter http::ProxyRequired -proxyhost {} -proxyport {} -repost 0 -threadlevel $ThreadLevel -urlencoding utf-8 -useragent UserAgent -zip 1]
test http-1.2 {http::config} {
    http::config -proxyfilter
} http::ProxyRequired
test http-1.3 {http::config} {
    catch {http::config -junk}
} 1
test http-1.4 {http::config} {
    set savedconf [http::config]
    http::config -proxyhost nowhere.come -proxyport 8080 \
	-proxyfilter myFilter -useragent "Tcl Test Suite" \
	-urlencoding iso8859-1
    set x [http::config]
    http::config {*}$savedconf
    set x
} [list -accept */* -cookiejar {} -pipeline 1 -postfresh 0 -proxyfilter myFilter -proxyhost nowhere.come -proxyport 8080 -repost 0 -threadlevel $ThreadLevel -urlencoding iso8859-1 -useragent {Tcl Test Suite} -zip 1]
test http-1.5 {http::config} -returnCodes error -body {
    http::config -proxyhost {} -junk 8080
} -result {Unknown option -junk, must be: -accept, -cookiejar, -pipeline, -postfresh, -proxyfilter, -proxyhost, -proxyport, -repost, -threadlevel, -urlencoding, -useragent, -zip}
test http-1.6 {http::config} -setup {
    set oldenc [http::config -urlencoding]
} -body {
    set enc [list [http::config -urlencoding]]
    http::config -urlencoding iso8859-1
    lappend enc [http::config -urlencoding]
} -cleanup {
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
} iso8859-1
test http-2.8 {http::CharsetToEncoding} {
    http::CharsetToEncoding latin4
} binary

test http-3.1 {http::geturl} -returnCodes error -body {
    http::geturl -bogus flag
} -result {Unknown option flag, can be: -binary, -blocksize, -channel, -command, -handler, -headers, -keepalive, -method, -myaddr, -progress, -protocol, -query, -queryblocksize, -querychannel, -queryprogress, -strict, -timeout, -type, -validate}

test http-3.2 {http::geturl} -returnCodes error -body {
    http::geturl http:junk
} -result {Unsupported URL: http:junk}

set url //${::HOST}:$port
set badurl //${::HOST}:[expr {$port+1}]
test http-3.3 {http::geturl} -body {
    set token [http::geturl $url]
    http::data $token
} -cleanup {
    http::cleanup $token
} -result "<html><head><title>HTTP/1.0 TEST</title></head><body>
<h1>Hello, World!</h1>
<h2>GET /</h2>
</body></html>"

set tail /a/b/c
set url //${::HOST}:$port/a/b/c
set fullurl HTTP://user:pass@${::HOST}:$port/a/b/c
set binurl //${::HOST}:$port/binary
set xmlurl //${::HOST}:$port/xml
set posturl //${::HOST}:$port/post
set badposturl //${::HOST}:$port/droppost
set authorityurl //${::HOST}:$port
set ipv6url http://\[::1\]:$port/

test http-3.4 {http::geturl} -body {
    set token [http::geturl $url]
    http::data $token
} -cleanup {
    http::cleanup $token
} -result "<html><head><title>HTTP/1.0 TEST</title></head><body>
<h1>Hello, World!</h1>







|
>



>











>









>







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
} iso8859-1
test http-2.8 {http::CharsetToEncoding} {
    http::CharsetToEncoding latin4
} binary

test http-3.1 {http::geturl} -returnCodes error -body {
    http::geturl -bogus flag
} -result {Unknown option flag, can be: -binary, -blocksize, -channel, -command, -guesstype, -handler, -headers, -keepalive, -method, -myaddr, -progress, -protocol, -query, -queryblocksize, -querychannel, -queryprogress, -strict, -timeout, -type, -validate}

test http-3.2 {http::geturl} -returnCodes error -body {
    http::geturl http:junk
} -result {Unsupported URL: http:junk}

set url //${::HOST}:$port
set badurl //${::HOST}:[expr {$port+1}]
test http-3.3 {http::geturl} -body {
    set token [http::geturl $url]
    http::data $token
} -cleanup {
    http::cleanup $token
} -result "<html><head><title>HTTP/1.0 TEST</title></head><body>
<h1>Hello, World!</h1>
<h2>GET /</h2>
</body></html>"

set tail /a/b/c
set url //${::HOST}:$port/a/b/c
set fullurl HTTP://user:pass@${::HOST}:$port/a/b/c
set binurl //${::HOST}:$port/binary
set xmlurl //${::HOST}:$port/xml
set posturl //${::HOST}:$port/post
set badposturl //${::HOST}:$port/droppost
set authorityurl //${::HOST}:$port
set ipv6url http://\[::1\]:$port/

test http-3.4 {http::geturl} -body {
    set token [http::geturl $url]
    http::data $token
} -cleanup {
    http::cleanup $token
} -result "<html><head><title>HTTP/1.0 TEST</title></head><body>
<h1>Hello, World!</h1>
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
} -body {
    set token [http::geturl $url -timeout 3000]
    array set m [http::meta $token]
    lsort [array names m]
} -cleanup {
    http::cleanup $token
    unset -nocomplain m token
} -result {Content-Length Content-Type Date}
test http-3.26 {http::meta} -setup {
    unset -nocomplain m token
} -body {
    set token [http::geturl $url -headers {X-Check 1} -timeout 3000]
    array set m [http::meta $token]
    lsort [array names m]
} -cleanup {
    http::cleanup $token
    unset -nocomplain m token
} -result {Content-Length Content-Type Date X-Check}
test http-3.27 {http::geturl: -headers override -type} -body {
    set token [http::geturl $url/headers -type "text/plain" -query dummy \
	    -headers [list "Content-Type" "text/plain;charset=utf-8"]]
    http::data $token
} -cleanup {
    http::cleanup $token
} -match regexp -result {(?n)Host .*







|









|







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
} -body {
    set token [http::geturl $url -timeout 3000]
    array set m [http::meta $token]
    lsort [array names m]
} -cleanup {
    http::cleanup $token
    unset -nocomplain m token
} -result {content-length content-type date}
test http-3.26 {http::meta} -setup {
    unset -nocomplain m token
} -body {
    set token [http::geturl $url -headers {X-Check 1} -timeout 3000]
    array set m [http::meta $token]
    lsort [array names m]
} -cleanup {
    http::cleanup $token
    unset -nocomplain m token
} -result {content-length content-type date x-check}
test http-3.27 {http::geturl: -headers override -type} -body {
    set token [http::geturl $url/headers -type "text/plain" -query dummy \
	    -headers [list "Content-Type" "text/plain;charset=utf-8"]]
    http::data $token
} -cleanup {
    http::cleanup $token
} -match regexp -result {(?n)Host .*
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
    http::geturl http://test/t -headers {List Length 3}
} -result {Bad value for -headers (List Length 3), number of list elements must be even}

test http-4.1 {http::Event} -body {
    set token [http::geturl $url -keepalive 0]
    upvar #0 $token data
    array set meta $data(meta)
    expr {($data(totalsize) == $meta(Content-Length))}
} -cleanup {
    http::cleanup $token
} -result 1
test http-4.2 {http::Event} -body {
    set token [http::geturl $url]
    upvar #0 $token data
    array set meta $data(meta)
    string compare $data(type) [string trim $meta(Content-Type)]
} -cleanup {
    http::cleanup $token
} -result 0
test http-4.3 {http::Event} -body {
    set token [http::geturl $url]
    http::code $token
} -cleanup {







|







|







480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
    http::geturl http://test/t -headers {List Length 3}
} -result {Bad value for -headers (List Length 3), number of list elements must be even}

test http-4.1 {http::Event} -body {
    set token [http::geturl $url -keepalive 0]
    upvar #0 $token data
    array set meta $data(meta)
    expr {($data(totalsize) == $meta(content-length))}
} -cleanup {
    http::cleanup $token
} -result 1
test http-4.2 {http::Event} -body {
    set token [http::geturl $url]
    upvar #0 $token data
    array set meta $data(meta)
    string compare $data(type) [string trim $meta(content-type)]
} -cleanup {
    http::cleanup $token
} -result 0
test http-4.3 {http::Event} -body {
    set token [http::geturl $url]
    http::code $token
} -cleanup {
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
} -result {HTTP/1.0 200 Data follows}
test http-4.10 {http::Event} -body {
    set token [http::geturl $url -progress myProgress]
    http::size $token
} -cleanup {
    http::cleanup $token
} -result {111}

# Timeout cases
#	Short timeout to working server (the test server). This lets us try a
#	reset during the connection.
test http-4.11 {http::Event} -body {
    set token [http::geturl $url -timeout 1 -keepalive 0 -command \#]
    http::reset $token
    http::status $token
} -cleanup {
    http::cleanup $token
} -result {reset}

#	Longer timeout with reset.
test http-4.12 {http::Event} -body {
    set token [http::geturl $url/?timeout=10 -keepalive 0 -command \#]
    http::reset $token
    http::status $token
} -cleanup {
    http::cleanup $token
} -result {reset}

#	Medium timeout to working server that waits even longer. The timeout
#	hits while waiting for a reply.
test http-4.13 {http::Event} -body {
    set token [http::geturl $url?timeout=30 -keepalive 0 -timeout 10 -command \#]
    http::wait $token
    http::status $token
} -cleanup {
    http::cleanup $token
} -result {timeout}

#	Longer timeout to good host, bad port, gets an error after the
#	connection "completes" but the socket is bad.
test http-4.14 {http::Event} -body {
    set token [http::geturl $badurl/?timeout=10 -timeout 10000 -command \#]
    if {$token eq ""} {
	error "bogus return from http::geturl"
    }
    http::wait $token
    lindex [http::error $token] 0
} -cleanup {
    catch {http::cleanup $token}
} -result {connect failed connection refused}

# Bogus host
test http-4.15 {http::Event} -body {
    # This test may fail if you use a proxy server. That is to be
    # expected and is not a problem with Tcl.
    set token [http::geturl //not_a_host.tcl.tk -timeout 3000 -command \#]
    http::wait $token
    http::status $token
    # error codes vary among platforms.
} -cleanup {
    catch {http::cleanup $token}
} -returnCodes 1 -match glob -result "couldn't open socket*"

test http-4.16 {Leak with Close vs Keepalive (bug [6ca52aec14]} -setup {
    proc list-difference {l1 l2} {
	lmap item $l2 {if {$item in $l1} continue; set item}
    }
} -body {
    set before [chan names]
    set token [http::geturl $url -headers {X-Connection keep-alive}]







>










>








>









>












>






|



|
>







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
} -result {HTTP/1.0 200 Data follows}
test http-4.10 {http::Event} -body {
    set token [http::geturl $url -progress myProgress]
    http::size $token
} -cleanup {
    http::cleanup $token
} -result {111}

# Timeout cases
#	Short timeout to working server (the test server). This lets us try a
#	reset during the connection.
test http-4.11 {http::Event} -body {
    set token [http::geturl $url -timeout 1 -keepalive 0 -command \#]
    http::reset $token
    http::status $token
} -cleanup {
    http::cleanup $token
} -result {reset}

#	Longer timeout with reset.
test http-4.12 {http::Event} -body {
    set token [http::geturl $url/?timeout=10 -keepalive 0 -command \#]
    http::reset $token
    http::status $token
} -cleanup {
    http::cleanup $token
} -result {reset}

#	Medium timeout to working server that waits even longer. The timeout
#	hits while waiting for a reply.
test http-4.13 {http::Event} -body {
    set token [http::geturl $url?timeout=30 -keepalive 0 -timeout 10 -command \#]
    http::wait $token
    http::status $token
} -cleanup {
    http::cleanup $token
} -result {timeout}

#	Longer timeout to good host, bad port, gets an error after the
#	connection "completes" but the socket is bad.
test http-4.14 {http::Event} -body {
    set token [http::geturl $badurl/?timeout=10 -timeout 10000 -command \#]
    if {$token eq ""} {
	error "bogus return from http::geturl"
    }
    http::wait $token
    lindex [http::error $token] 0
} -cleanup {
    catch {http::cleanup $token}
} -result {connect failed connection refused}

# Bogus host
test http-4.15 {http::Event} -body {
    # This test may fail if you use a proxy server. That is to be
    # expected and is not a problem with Tcl.
    set token [http::geturl //not_a_host.tcl.tk -timeout 3000 -command \#]
    http::wait $token
    set result "[http::status $token] -- [lindex [http::error $token] 0]"
    # error codes vary among platforms.
} -cleanup {
    catch {http::cleanup $token}
} -match glob -result "error -- couldn't open socket*"

test http-4.16 {Leak with Close vs Keepalive (bug [6ca52aec14]} -setup {
    proc list-difference {l1 l2} {
	lmap item $l2 {if {$item in $l1} continue; set item}
    }
} -body {
    set before [chan names]
    set token [http::geturl $url -headers {X-Connection keep-alive}]
Changes to tests/http11.test.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.

if {"::tcltest" ni [namespace children]} {
    package require tcltest 2.5
    namespace import -force ::tcltest::*
}

package require http 2.9

# start the server
variable httpd_output
proc create_httpd {} {
    proc httpd_read {chan} {
        variable httpd_output
        if {[gets $chan line] >= 0} {







|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.

if {"::tcltest" ni [namespace children]} {
    package require tcltest 2.5
    namespace import -force ::tcltest::*
}

package require http 2.10

# start the server
variable httpd_output
proc create_httpd {} {
    proc httpd_read {chan} {
        variable httpd_output
        if {[gets $chan line] >= 0} {
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
        vwait httpd_output
        close $httpd
    }
    unset -nocomplain httpd_output httpd
}

proc meta {tok {key ""}} {
    set meta [http::meta $tok]
    if {$key ne ""} {
        if {[dict exists $meta $key]} {
            return [dict get $meta $key]
        } else {
            return ""
        }
    }
    return $meta
}

proc state {tok {key ""}} {
    upvar 1 $tok state
    if {$key ne ""} {
        if {[array names state -exact $key] ne {}} {
            return $state($key)







<
|
<
|
|
|
|
<
<







47
48
49
50
51
52
53

54

55
56
57
58


59
60
61
62
63
64
65
        vwait httpd_output
        close $httpd
    }
    unset -nocomplain httpd_output httpd
}

proc meta {tok {key ""}} {

    if {$key eq ""} {

        return [http::meta $tok]
    } else {
        return [http::metaValue $tok $key]
    }


}

proc state {tok {key ""}} {
    upvar 1 $tok state
    if {$key ne ""} {
        if {[array names state -exact $key] ne {}} {
            return $state($key)
83
84
85
86
87
88
89






















90
91
92
93
94
95
96
    if {$crc ne $chk} {
        return  "crc32 mismatch: $crc ne $chk"
    }
    return "ok"
}

makeFile "<html><head><title>test</title></head><body><p>this is a test</p>\n[string repeat {<p>This is a tcl test file.</p>} 4192]\n</body></html>" testdoc.html























# -------------------------------------------------------------------------

test http11-1.0 "normal request for document " -setup {
    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html -timeout 10000]







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







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
    if {$crc ne $chk} {
        return  "crc32 mismatch: $crc ne $chk"
    }
    return "ok"
}

makeFile "<html><head><title>test</title></head><body><p>this is a test</p>\n[string repeat {<p>This is a tcl test file.</p>} 4192]\n</body></html>" testdoc.html

makeFile "<html><head><title>test</title></head><body><p>this is a test</p>\n[string repeat {<p>This is a tcl test file.</p>} 5000]\n</body></html>" largedoc.html

if {![info exists ThreadLevel]} {
    if {[catch {package require Thread}] == 0} {
        set ValueRange {0 1 2}
    } else {
        set ValueRange {0 1}
    }

    # For each value of ThreadLevel, source this file recursively in the
    # same interpreter.
    foreach ThreadLevel $ValueRange {
        source [info script]
    }
    catch {unset ThreadLevel}
    catch {unset ValueRange}
    return
}

catch {puts "==== Test with ThreadLevel $ThreadLevel ===="}
http::config -threadlevel $ThreadLevel

# -------------------------------------------------------------------------

test http11-1.0 "normal request for document " -setup {
    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html -timeout 10000]
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
test http11-1.1 "normal,gzip,non-chunked" -setup {
    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html?close=1 \
                 -timeout 10000 -headers {accept-encoding gzip}]
    http::wait $tok
    list [http::status $tok] [http::code $tok] [check_crc $tok] \
        [meta $tok content-encoding] [meta $tok transfer-encoding]

} -cleanup {
    http::cleanup $tok
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok gzip {}}

test http11-1.2 "normal,deflated,non-chunked" -setup {
    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html?close=1 \
                 -timeout 10000 -headers {accept-encoding deflate}]
    http::wait $tok
    list [http::status $tok] [http::code $tok] [check_crc $tok] \
        [meta $tok content-encoding] [meta $tok transfer-encoding]
} -cleanup {
    http::cleanup $tok
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok deflate {}}














test http11-1.3 "normal,compressed,non-chunked" -setup {


    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html?close=1 \
                 -timeout 10000 -headers {accept-encoding compress}]
    http::wait $tok
    list [http::status $tok] [http::code $tok] [check_crc $tok] \
        [meta $tok content-encoding] [meta $tok transfer-encoding]







|
>



|














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







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
test http11-1.1 "normal,gzip,non-chunked" -setup {
    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html?close=1 \
                 -timeout 10000 -headers {accept-encoding gzip}]
    http::wait $tok
    list [http::status $tok] [http::code $tok] [check_crc $tok] \
        [meta $tok content-encoding] [meta $tok transfer-encoding] \
        [http::meta $tok content-encoding] [http::meta $tok transfer-encoding]
} -cleanup {
    http::cleanup $tok
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok gzip {} {content-encoding gzip} {}}

test http11-1.2 "normal,deflated,non-chunked" -setup {
    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html?close=1 \
                 -timeout 10000 -headers {accept-encoding deflate}]
    http::wait $tok
    list [http::status $tok] [http::code $tok] [check_crc $tok] \
        [meta $tok content-encoding] [meta $tok transfer-encoding]
} -cleanup {
    http::cleanup $tok
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok deflate {}}

test http11-1.2.1 "normal,deflated,non-chunked,msdeflate" -setup {
    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html?close=1&msdeflate=1 \
                 -timeout 10000 -headers {accept-encoding deflate}]
    http::wait $tok
    list [http::status $tok] [http::code $tok] [check_crc $tok] \
        [meta $tok content-encoding] [meta $tok transfer-encoding]
} -cleanup {
    http::cleanup $tok
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok deflate {}}

test http11-1.3 "normal,compressed,non-chunked" -constraints badCompress -setup {
    # The Tcl "compress" algorithm appears to be incorrect and has been removed.
    # Bug [a13b9d0ce1].
    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html?close=1 \
                 -timeout 10000 -headers {accept-encoding compress}]
    http::wait $tok
    list [http::status $tok] [http::code $tok] [check_crc $tok] \
        [meta $tok content-encoding] [meta $tok transfer-encoding]
169
170
171
172
173
174
175
176

177
178
179
180
181
182
183
184
185
186
187
test http11-1.6 "normal, specify 1.1 " -setup {
    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html \
                 -protocol 1.1 -timeout 10000]
    http::wait $tok
    list [http::status $tok] [http::code $tok] [check_crc $tok] \
        [meta $tok connection] [meta $tok transfer-encoding]

} -cleanup {
    http::cleanup $tok
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok close chunked}

test http11-1.7 "normal, 1.1 and keepalive " -setup {
    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html \
                 -protocol 1.1 -keepalive 1 -timeout 10000]
    http::wait $tok







|
>



|







203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
test http11-1.6 "normal, specify 1.1 " -setup {
    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html \
                 -protocol 1.1 -timeout 10000]
    http::wait $tok
    list [http::status $tok] [http::code $tok] [check_crc $tok] \
        [meta $tok connection] [meta $tok transfer-encoding] \
        [http::meta $tok connection] [http::meta $tok transfer-encoding]
} -cleanup {
    http::cleanup $tok
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok close chunked {connection close} {transfer-encoding chunked}}

test http11-1.7 "normal, 1.1 and keepalive " -setup {
    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html \
                 -protocol 1.1 -keepalive 1 -timeout 10000]
    http::wait $tok
227
228
229
230
231
232
233













234


235
236
237
238
239
240
241
    list [http::status $tok] [http::code $tok] [check_crc $tok] \
        [meta $tok content-encoding] [meta $tok transfer-encoding]
} -cleanup {
    http::cleanup $tok
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok deflate chunked}














test http11-1.11 "normal,compress,chunked" -setup {


    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html \
                 -timeout 10000 -headers {accept-encoding compress}]
    http::wait $tok
    list [http::status $tok] [http::code $tok] [check_crc $tok] \
        [meta $tok content-encoding] [meta $tok transfer-encoding]







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







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
    list [http::status $tok] [http::code $tok] [check_crc $tok] \
        [meta $tok content-encoding] [meta $tok transfer-encoding]
} -cleanup {
    http::cleanup $tok
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok deflate chunked}

test http11-1.10.1 "normal,deflate,chunked,msdeflate" -setup {
    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html?msdeflate=1 \
                 -timeout 10000 -headers {accept-encoding deflate}]
    http::wait $tok
    list [http::status $tok] [http::code $tok] [check_crc $tok] \
        [meta $tok content-encoding] [meta $tok transfer-encoding]
} -cleanup {
    http::cleanup $tok
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok deflate chunked}

test http11-1.11 "normal,compress,chunked" -constraints badCompress -setup {
    # The Tcl "compress" algorithm appears to be incorrect and has been removed.
    # Bug [a13b9d0ce1].
    variable httpd [create_httpd]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html \
                 -timeout 10000 -headers {accept-encoding compress}]
    http::wait $tok
    list [http::status $tok] [http::code $tok] [check_crc $tok] \
        [meta $tok content-encoding] [meta $tok transfer-encoding]
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
    set chan [open [makeFile {} testfile.tmp] wb+]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html \
                 -timeout 5000 -channel $chan -headers {accept-encoding gzip}]
    http::wait $tok
    seek $chan 0
    set data [read $chan]

    list [http::status $tok] [http::code $tok] [check_crc $tok $data]\
        [meta $tok connection] [meta $tok content-encoding]\
        [meta $tok transfer-encoding]
























} -cleanup {
    http::cleanup $tok
    close $chan
    removeFile testfile.tmp
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok close gzip chunked}

test http11-2.2 "-channel, encoding deflate" -setup {
    variable httpd [create_httpd]
    set chan [open [makeFile {} testfile.tmp] wb+]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html \
                 -timeout 5000 -channel $chan -headers {accept-encoding deflate}]
    http::wait $tok
    seek $chan 0
    set data [read $chan]
    list [http::status $tok] [http::code $tok] [check_crc $tok $data]\
        [meta $tok connection] [meta $tok content-encoding]\
        [meta $tok transfer-encoding]
} -cleanup {
    http::cleanup $tok
    close $chan
    removeFile testfile.tmp
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok close deflate chunked}




















test http11-2.3 "-channel,encoding compress" -setup {


    variable httpd [create_httpd]
    set chan [open [makeFile {} testfile.tmp] wb+]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html \
                 -timeout 5000 -channel $chan \
                 -headers {accept-encoding compress}]
    http::wait $tok







>


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





|




















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







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
    set chan [open [makeFile {} testfile.tmp] wb+]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html \
                 -timeout 5000 -channel $chan -headers {accept-encoding gzip}]
    http::wait $tok
    seek $chan 0
    set data [read $chan]
    set diff [expr {[file size testdoc.html] - [file size testfile.tmp]}]
    list [http::status $tok] [http::code $tok] [check_crc $tok $data]\
        [meta $tok connection] [meta $tok content-encoding]\
        [meta $tok transfer-encoding] -- $diff bytes lost
} -cleanup {
    http::cleanup $tok
    close $chan
    removeFile testfile.tmp
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok close gzip chunked -- 0 bytes lost}

# Cf. Bug [3610253] "CopyChunk does not drain decompressor(s)"
# This test failed before the bugfix.
# The pass/fail depended on file size.
test http11-2.1.1 "-channel, encoding gzip" -setup {
    variable httpd [create_httpd]
    set chan [open [makeFile {} testfile.tmp] wb+]
    set fileName largedoc.html
} -body {
    set tok [http::geturl http://localhost:$httpd_port/$fileName \
                 -timeout 5000 -channel $chan -headers {accept-encoding gzip}]
    http::wait $tok
    seek $chan 0
    set data [read $chan]
    set diff [expr {[file size $fileName] - [file size testfile.tmp]}]
    list [http::status $tok] [http::code $tok] [check_crc $tok $data]\
        [meta $tok connection] [meta $tok content-encoding]\
        [meta $tok transfer-encoding] -- $diff bytes lost
} -cleanup {
    http::cleanup $tok
    close $chan
    removeFile testfile.tmp
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok close gzip chunked -- 0 bytes lost}

test http11-2.2 "-channel, encoding deflate" -setup {
    variable httpd [create_httpd]
    set chan [open [makeFile {} testfile.tmp] wb+]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html \
                 -timeout 5000 -channel $chan -headers {accept-encoding deflate}]
    http::wait $tok
    seek $chan 0
    set data [read $chan]
    list [http::status $tok] [http::code $tok] [check_crc $tok $data]\
        [meta $tok connection] [meta $tok content-encoding]\
        [meta $tok transfer-encoding]
} -cleanup {
    http::cleanup $tok
    close $chan
    removeFile testfile.tmp
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok close deflate chunked}

test http11-2.2.1 "-channel, encoding deflate,msdeflate" -setup {
    variable httpd [create_httpd]
    set chan [open [makeFile {} testfile.tmp] wb+]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html?msdeflate=1 \
                 -timeout 5000 -channel $chan -headers {accept-encoding deflate}]
    http::wait $tok
    seek $chan 0
    set data [read $chan]
    list [http::status $tok] [http::code $tok] [check_crc $tok $data]\
        [meta $tok connection] [meta $tok content-encoding]\
        [meta $tok transfer-encoding]
} -cleanup {
    http::cleanup $tok
    close $chan
    removeFile testfile.tmp
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok close deflate chunked}

test http11-2.3 "-channel,encoding compress" -constraints badCompress -setup {
    # The Tcl "compress" algorithm appears to be incorrect and has been removed.
    # Bug [a13b9d0ce1].
    variable httpd [create_httpd]
    set chan [open [makeFile {} testfile.tmp] wb+]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html \
                 -timeout 5000 -channel $chan \
                 -headers {accept-encoding compress}]
    http::wait $tok
500
501
502
503
504
505
506























507


508
509
510
511
512
513
514
} -cleanup {
    http::cleanup $tok
    close $chan
    removeFile testfile.tmp
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok close deflate {} 0}
























test http11-2.8 "-channel,encoding compress,non-chunked" -setup {


    variable httpd [create_httpd]
    set chan [open [makeFile {} testfile.tmp] wb+]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html?close=1 \
                 -timeout 5000 -channel $chan -headers {accept-encoding compress}]
    http::wait $tok
    seek $chan 0







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







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
} -cleanup {
    http::cleanup $tok
    close $chan
    removeFile testfile.tmp
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok close deflate {} 0}

test http11-2.7.1 "-channel,encoding deflate,non-chunked,msdeflate" -constraints knownBug -setup {
    # Test fails because a -channel can only try one un-deflate algorithm, and the
    # compliant "decompress" is tried, not the non-compliant "inflate" of
    # the MS browser implementation.
    variable httpd [create_httpd]
    set chan [open [makeFile {} testfile.tmp] wb+]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html?close=1&msdeflate=1 \
                 -timeout 5000 -channel $chan -headers {accept-encoding deflate}]
    http::wait $tok
    seek $chan 0
    set data [read $chan]
    list [http::status $tok] [http::code $tok] [check_crc $tok $data]\
        [meta $tok connection] [meta $tok content-encoding]\
        [meta $tok transfer-encoding]\
        [expr {[file size testdoc.html]-[file size testfile.tmp]}]
} -cleanup {
    http::cleanup $tok
    close $chan
    removeFile testfile.tmp
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok close deflate {} 0}

test http11-2.8 "-channel,encoding compress,non-chunked" -constraints badCompress -setup {
    # The Tcl "compress" algorithm appears to be incorrect and has been removed.
    # Bug [a13b9d0ce1].
    variable httpd [create_httpd]
    set chan [open [makeFile {} testfile.tmp] wb+]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html?close=1 \
                 -timeout 5000 -channel $chan -headers {accept-encoding compress}]
    http::wait $tok
    seek $chan 0
561
562
563
564
565
566
567





















568
569
570
571
572
573
574
} -cleanup {
    http::cleanup $tok
    close $chan
    removeFile testfile.tmp
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok {} deflate chunked 0}






















test http11-2.11 "-channel,identity,keepalive" -setup {
    variable httpd [create_httpd]
    set chan [open [makeFile {} testfile.tmp] wb+]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html \
                 -headers {accept-encoding identity} \
                 -timeout 5000 -channel $chan -keepalive 1]







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







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
} -cleanup {
    http::cleanup $tok
    close $chan
    removeFile testfile.tmp
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok {} deflate chunked 0}

test http11-2.10.1 "-channel,deflate,keepalive,msdeflate" -setup {
    variable httpd [create_httpd]
    set chan [open [makeFile {} testfile.tmp] wb+]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html?msdeflate=1 \
                 -timeout 5000 -channel $chan -keepalive 1 \
		 -headers {accept-encoding deflate}]
    http::wait $tok
    seek $chan 0
    set data [read $chan]
    list [http::status $tok] [http::code $tok] [check_crc $tok $data]\
        [meta $tok connection] [meta $tok content-encoding]\
        [meta $tok transfer-encoding]\
        [expr {[file size testdoc.html]-[file size testfile.tmp]}]
} -cleanup {
    http::cleanup $tok
    close $chan
    removeFile testfile.tmp
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok {} deflate chunked 0}

test http11-2.11 "-channel,identity,keepalive" -setup {
    variable httpd [create_httpd]
    set chan [open [makeFile {} testfile.tmp] wb+]
} -body {
    set tok [http::geturl http://localhost:$httpd_port/testdoc.html \
                 -headers {accept-encoding identity} \
                 -timeout 5000 -channel $chan -keepalive 1]
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
        [meta $tok transfer-encoding] [meta $tok x-requested-encodings]\
        [expr {[file size testdoc.html]-[file size testfile.tmp]}]
} -cleanup {
    http::cleanup $tok
    close $chan
    removeFile testfile.tmp
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok {} gzip chunked gzip,deflate,compress 0}


# -------------------------------------------------------------------------
#
# The following tests for the -handler option will require changes in
# the future. At the moment we cannot handler chunked data with this
# option. Therefore we currently force HTTP/1.0 protocol version.







|







741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
        [meta $tok transfer-encoding] [meta $tok x-requested-encodings]\
        [expr {[file size testdoc.html]-[file size testfile.tmp]}]
} -cleanup {
    http::cleanup $tok
    close $chan
    removeFile testfile.tmp
    halt_httpd
} -result {ok {HTTP/1.1 200 OK} ok {} gzip chunked gzip,deflate 0}


# -------------------------------------------------------------------------
#
# The following tests for the -handler option will require changes in
# the future. At the moment we cannot handler chunked data with this
# option. Therefore we currently force HTTP/1.0 protocol version.
914
915
916
917
918
919
920

921
922
923
after 10
exec [info nameofexecutable] << {}

foreach p {create_httpd httpd_read halt_httpd meta check_crc} {
    if {[llength [info proc $p]]} {rename $p {}}
}
removeFile testdoc.html

unset -nocomplain httpd_port httpd p

::tcltest::cleanupTests







>



1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
after 10
exec [info nameofexecutable] << {}

foreach p {create_httpd httpd_read halt_httpd meta check_crc} {
    if {[llength [info proc $p]]} {rename $p {}}
}
removeFile testdoc.html
removeFile largedoc.html
unset -nocomplain httpd_port httpd p

::tcltest::cleanupTests
Changes to tests/httpPipeline.test.
9
10
11
12
13
14
15
16
























17
18
19
20
21
22
23
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.

if {"::tcltest" ni [namespace children]} {
    package require tcltest 2.5
    namespace import -force ::tcltest::*
}

package require http 2.9

























set sourcedir [file normalize [file dirname [info script]]]
source [file join $sourcedir httpTest.tcl]
source [file join $sourcedir httpTestScript.tcl]

# ------------------------------------------------------------------------------
# (1) Define the test scripts that will be used to generate logs for analysis -







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







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
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.

if {"::tcltest" ni [namespace children]} {
    package require tcltest 2.5
    namespace import -force ::tcltest::*
}

package require http 2.10

# ------------------------------------------------------------------------------
# (0) Socket Creation in Thread, which triples the number of tests.
# ------------------------------------------------------------------------------

if {![info exists ThreadLevel]} {
    if {[catch {package require Thread}] == 0} {
        set ValueRange {0 1 2}
    } else {
        set ValueRange {0 1}
    }

    # For each value of ThreadLevel, source this file recursively in the
    # same interpreter.
    foreach ThreadLevel $ValueRange {
        source [info script]
    }
    catch {unset ThreadLevel}
    catch {unset ValueRange}
    return
}

catch {puts "==== Test with ThreadLevel $ThreadLevel ===="}
http::config -threadlevel $ThreadLevel

set sourcedir [file normalize [file dirname [info script]]]
source [file join $sourcedir httpTest.tcl]
source [file join $sourcedir httpTestScript.tcl]

# ------------------------------------------------------------------------------
# (1) Define the test scripts that will be used to generate logs for analysis -
Changes to tests/httpd.
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
proc httpdAccept {newsock ipaddr port} {
    global httpd
    upvar #0 httpd$newsock data

    fconfigure $newsock -blocking 0 -translation {auto crlf}
    httpd_log $newsock Connect $ipaddr $port
    set data(ipaddr) $ipaddr
    after 50 [list fileevent $newsock readable [list httpdRead $newsock]]
}

# read data from a client request

proc httpdRead { sock } {
    upvar #0 httpd$sock data

    if {[eof $sock]} {
	set readCount -1
    } elseif {![info exists data(state)]} {

	# Read the protocol line and parse out the URL and query

	set readCount [gets $sock line]
	if {[regexp {(POST|GET|HEAD) ([^?]+)\??([^ ]*) HTTP/(1.[01])} $line \
		-> data(proto) data(url) data(query) data(httpversion)]} {
	    set data(state) mime
	    httpd_log $sock Query $line




	} else {
	    httpdError $sock 400
	    httpd_log $sock Error "bad first line:$line"
	    httpdSockDone $sock
	}
	return
    } elseif {$data(state) == "mime"} {







|


















>
>
>
>







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
proc httpdAccept {newsock ipaddr port} {
    global httpd
    upvar #0 httpd$newsock data

    fconfigure $newsock -blocking 0 -translation {auto crlf}
    httpd_log $newsock Connect $ipaddr $port
    set data(ipaddr) $ipaddr
    fileevent $newsock readable [list httpdRead $newsock]
}

# read data from a client request

proc httpdRead { sock } {
    upvar #0 httpd$sock data

    if {[eof $sock]} {
	set readCount -1
    } elseif {![info exists data(state)]} {

	# Read the protocol line and parse out the URL and query

	set readCount [gets $sock line]
	if {[regexp {(POST|GET|HEAD) ([^?]+)\??([^ ]*) HTTP/(1.[01])} $line \
		-> data(proto) data(url) data(query) data(httpversion)]} {
	    set data(state) mime
	    httpd_log $sock Query $line
	    if {[regexp {(?:^|[\?&])delay=([^&]+)} $data(query) {} val]} {
		fileevent $sock readable {}
		after $val [list fileevent $sock readable [list httpdRead $sock]]
	    }
	} else {
	    httpdError $sock 400
	    httpd_log $sock Error "bad first line:$line"
	    httpdSockDone $sock
	}
	return
    } elseif {$data(state) == "mime"} {
Changes to tests/httpd11.tcl.
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
    switch -exact -- $compression {
        gzip     { set data [zlib gzip $data] }
        deflate  { set data [zlib deflate $data] }
        compress { set data [zlib compress $data] }
    }

    set data ""
    set chunker [make-chunk-generator $data 512]
    while {[string length [set chunk [$chunker]]]} {
        append data $chunk
    }
    return $data
}

proc blow-chunks {data {ochan stdout} {compression gzip}} {
    switch -exact -- $compression {
        gzip     { set data [zlib gzip $data] }
        deflate  { set data [zlib deflate $data] }
        compress { set data [zlib compress $data] }
    }

    set chunker [make-chunk-generator $data 512]
    while {[string length [set chunk [$chunker]]]} {
        puts -nonewline $ochan $chunk
    }
    return
}

proc mime-type {filename} {







|













|







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
    switch -exact -- $compression {
        gzip     { set data [zlib gzip $data] }
        deflate  { set data [zlib deflate $data] }
        compress { set data [zlib compress $data] }
    }

    set data ""
    set chunker [make-chunk-generator $data 671]
    while {[string length [set chunk [$chunker]]]} {
        append data $chunk
    }
    return $data
}

proc blow-chunks {data {ochan stdout} {compression gzip}} {
    switch -exact -- $compression {
        gzip     { set data [zlib gzip $data] }
        deflate  { set data [zlib deflate $data] }
        compress { set data [zlib compress $data] }
    }

    set chunker [make-chunk-generator $data 671]
    while {[string length [set chunk [$chunker]]]} {
        puts -nonewline $ochan $chunk
    }
    return
}

proc mime-type {filename} {
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
            set code "200 OK"
            set close [expr {[dict get? $meta connection] eq "close"}]
        }

        if {$protocol eq "HTTP/1.1"} {
	    foreach enc [split [dict get? $meta accept-encoding] ,] {
		set enc [string trim $enc]






		if {$enc in {deflate gzip compress}} {
		    set encoding $enc
		    break
		}
	    }
            set transfer chunked
        } else {
            set close 1
        }

        set nosendclose 0

        foreach pair [split $query &] {
            if {[scan $pair {%[^=]=%s} key val] != 2} {set val ""}
            switch -exact -- $key {
                nosendclose  {set nosendclose 1}
                close        {set close 1 ; set transfer 0}
                transfer     {set transfer $val}
                content-type {set type $val}

            }
        }
        if {$protocol eq "HTTP/1.1"} {
            set nosendclose 0
        }

        chan configure $chan -buffering line -encoding iso8859-1 -translation crlf







>
>
>
>
>
>











>







>







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
            set code "200 OK"
            set close [expr {[dict get? $meta connection] eq "close"}]
        }

        if {$protocol eq "HTTP/1.1"} {
	    foreach enc [split [dict get? $meta accept-encoding] ,] {
		set enc [string trim $enc]
		# The current implementation of "compress" appears to be
		# incorrect (bug [a13b9d0ce1]).  Keep it here for
		# experimentation only.  The tests that use it have the
		# constraint "badCompress".  The client code in http has
		# been removed, but can be restored from comments if
		# experimentation is desired.
		if {$enc in {deflate gzip compress}} {
		    set encoding $enc
		    break
		}
	    }
            set transfer chunked
        } else {
            set close 1
        }

        set nosendclose 0
        set msdeflate 0
        foreach pair [split $query &] {
            if {[scan $pair {%[^=]=%s} key val] != 2} {set val ""}
            switch -exact -- $key {
                nosendclose  {set nosendclose 1}
                close        {set close 1 ; set transfer 0}
                transfer     {set transfer $val}
                content-type {set type $val}
                msdeflate    {set msdeflate $val}
            }
        }
        if {$protocol eq "HTTP/1.1"} {
            set nosendclose 0
        }

        chan configure $chan -buffering line -encoding iso8859-1 -translation crlf
207
208
209
210
211
212
213









214
215




216
217
218
219
220
221
222
223
224
        if {$transfer eq "chunked"} {
            Puts $chan "transfer-encoding: chunked"
        }
        puts $chan ""
        flush $chan

        chan configure $chan -buffering full -translation binary









        if {$transfer eq "chunked"} {
            blow-chunks $data $chan $encoding




        } elseif {$encoding ne "identity"} {
            puts -nonewline $chan [zlib $encoding $data]
        } else {
            puts -nonewline $chan $data
        }

        if {$close} {
            chan event $chan readable {}
            close $chan







>
>
>
>
>
>
>
>
>

|
>
>
>
>
|
|







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
        if {$transfer eq "chunked"} {
            Puts $chan "transfer-encoding: chunked"
        }
        puts $chan ""
        flush $chan

        chan configure $chan -buffering full -translation binary
        if {$encoding eq {deflate}} {
            # When http.tcl uses the correct decoder (bug [a13b9d0ce1]) for
            # "accept-encoding deflate", i.e. "zlib decompress", this choice of
            # encoding2 allows the tests to pass.  It appears to do "deflate"
            # correctly, but this has not been verified with a non-Tcl client.
            set encoding2 compress
        } else {
            set encoding2 $encoding
        }
        if {$transfer eq "chunked"} {
            blow-chunks $data $chan $encoding2
        } elseif {$encoding2 ne "identity" && $msdeflate eq {1}} {
            puts -nonewline $chan [string range [zlib $encoding2 $data] 2 end-4]
            # Used in some tests of "deflate" to produce the non-RFC-compliant
            # Microsoft version of "deflate".
        } elseif {$encoding2 ne "identity"} {
            puts -nonewline $chan [zlib $encoding2 $data]
        } else {
            puts -nonewline $chan $data
        }

        if {$close} {
            chan event $chan readable {}
            close $chan
Changes to tests/interp.test.
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
} -body {
    child hide coroutine
    catch {child invokehidden coroutine} m o
    dict get $o -errorinfo
} -cleanup {
    unset -nocomplain m 0
    interp delete child
} -returnCodes ok -result {wrong # args: should be "coroutine name cmd ?arg ...?"
    while executing
"coroutine"
    invoked from within
"child invokehidden coroutine"}

test interp-21.1 {interp hidden} {
    interp hidden {}







|







1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
} -body {
    child hide coroutine
    catch {child invokehidden coroutine} m o
    dict get $o -errorinfo
} -cleanup {
    unset -nocomplain m 0
    interp delete child
} -result {wrong # args: should be "coroutine name cmd ?arg ...?"
    while executing
"coroutine"
    invoked from within
"child invokehidden coroutine"}

test interp-21.1 {interp hidden} {
    interp hidden {}
Changes to tests/io.test.
5961
5962
5963
5964
5965
5966
5967
5968
5969
5970
5971
5972
5973
5974
5975
} -result 1
test io-40.17 {tilde substitution in open} {
    set home $::env(HOME)
    unset ::env(HOME)
    set x [list [catch {open ~/foo} msg] $msg]
    set ::env(HOME) $home
    set x
} {1 {couldn't find HOME environment variable to expand path}}

test io-41.1 {Tcl_FileeventCmd: errors} {fileevent} {
    list [catch {fileevent foo} msg] $msg
} {1 {wrong # args: should be "fileevent channelId event ?script?"}}
test io-41.2 {Tcl_FileeventCmd: errors} {fileevent} {
    list [catch {fileevent foo bar baz q} msg] $msg
} {1 {wrong # args: should be "fileevent channelId event ?script?"}}







|







5961
5962
5963
5964
5965
5966
5967
5968
5969
5970
5971
5972
5973
5974
5975
} -result 1
test io-40.17 {tilde substitution in open} {
    set home $::env(HOME)
    unset ::env(HOME)
    set x [list [catch {open ~/foo} msg] $msg]
    set ::env(HOME) $home
    set x
} {1 {couldn't open "~/foo": no such file or directory}}

test io-41.1 {Tcl_FileeventCmd: errors} {fileevent} {
    list [catch {fileevent foo} msg] $msg
} {1 {wrong # args: should be "fileevent channelId event ?script?"}}
test io-41.2 {Tcl_FileeventCmd: errors} {fileevent} {
    list [catch {fileevent foo bar baz q} msg] $msg
} {1 {wrong # args: should be "fileevent channelId event ?script?"}}
8562
8563
8564
8565
8566
8567
8568
8569
8570
8571
8572
8573
8574
8575
8576

test io-60.1 {writing illegal utf sequences} {fileevent testbytestring} {
    # This test will hang in older revisions of the core.

    set out [open $path(script) w]
    puts $out "catch {load $::tcltestlib Tcltest}"
    puts $out {
	puts [testbytestring \xE2]
	exit 1
    }
    proc readit {pipe} {
	variable x
	variable result
	if {[eof $pipe]} {
	    set x [catch {close $pipe} line]







|







8562
8563
8564
8565
8566
8567
8568
8569
8570
8571
8572
8573
8574
8575
8576

test io-60.1 {writing illegal utf sequences} {fileevent testbytestring} {
    # This test will hang in older revisions of the core.

    set out [open $path(script) w]
    puts $out "catch {load $::tcltestlib Tcltest}"
    puts $out {
	puts ABC[testbytestring \xE2]
	exit 1
    }
    proc readit {pipe} {
	variable x
	variable result
	if {[eof $pipe]} {
	    set x [catch {close $pipe} line]
8586
8587
8588
8589
8590
8591
8592
8593
8594
8595
8596
8597
8598
8599
8600
    variable x ""
    set result ""
    vwait [namespace which -variable x]

    # cut of the remainder of the error stack, especially the filename
    set result [lreplace $result 3 3 [lindex [split [lindex $result 3] \n] 0]]
    list $x $result
} {1 {gets {} catch {error writing "stdout": illegal byte sequence}}}

test io-61.1 {Reset eof state after changing the eof char} -setup {
    set datafile [makeFile {} eofchar]
    set f [open $datafile w]
    fconfigure $f -translation binary
    puts -nonewline $f [string repeat "Ho hum\n" 11]
    puts $f =







|







8586
8587
8588
8589
8590
8591
8592
8593
8594
8595
8596
8597
8598
8599
8600
    variable x ""
    set result ""
    vwait [namespace which -variable x]

    # cut of the remainder of the error stack, especially the filename
    set result [lreplace $result 3 3 [lindex [split [lindex $result 3] \n] 0]]
    list $x $result
} {1 {gets ABC catch {error writing "stdout": illegal byte sequence}}}

test io-61.1 {Reset eof state after changing the eof char} -setup {
    set datafile [makeFile {} eofchar]
    set f [open $datafile w]
    fconfigure $f -translation binary
    puts -nonewline $f [string repeat "Ho hum\n" 11]
    puts $f =
8947
8948
8949
8950
8951
8952
8953







































































































8954
8955
8956
8957
8958
8959
8960
8961
8962
8963
8964
8965
    catch {read [teststringobj get 1]}
    read [teststringobj get 2]
} -cleanup {
    interp delete child
    testobj freeallvars
    removeFile io-74.1
} -returnCodes error -match glob -result {can not find channel named "*"}








































































































# ### ### ### ######### ######### #########

# cleanup
foreach file [list fooBar longfile script script2 output test1 pipe my_script \
	test2 test3 cat stdout kyrillic.txt utf8-fcopy.txt utf8-rp.txt] {
    removeFile $file
}
cleanupTests
}
namespace delete ::tcl::test::io
return







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












8947
8948
8949
8950
8951
8952
8953
8954
8955
8956
8957
8958
8959
8960
8961
8962
8963
8964
8965
8966
8967
8968
8969
8970
8971
8972
8973
8974
8975
8976
8977
8978
8979
8980
8981
8982
8983
8984
8985
8986
8987
8988
8989
8990
8991
8992
8993
8994
8995
8996
8997
8998
8999
9000
9001
9002
9003
9004
9005
9006
9007
9008
9009
9010
9011
9012
9013
9014
9015
9016
9017
9018
9019
9020
9021
9022
9023
9024
9025
9026
9027
9028
9029
9030
9031
9032
9033
9034
9035
9036
9037
9038
9039
9040
9041
9042
9043
9044
9045
9046
9047
9048
9049
9050
9051
9052
9053
9054
9055
9056
9057
9058
9059
9060
9061
9062
9063
9064
9065
9066
9067
9068
    catch {read [teststringobj get 1]}
    read [teststringobj get 2]
} -cleanup {
    interp delete child
    testobj freeallvars
    removeFile io-74.1
} -returnCodes error -match glob -result {can not find channel named "*"}

# The following tests 75.1 to 75.5 exercise strict or tolerant channel
# encoding.
# They are left as a place-holder here. If TIP633 is voted, they will
# come back.
# Exercise strct channel encoding
test io-75.6 {multibyte encoding error read results in raw bytes} -setup {
    set fn [makeFile {} io-75.6]
    set f [open $fn w+]
    fconfigure $f -encoding binary
    # In UTF-8, a byte 0xCx starts a multibyte sequence and must be followed
    # by a byte > 0x7F. This is violated to get an invalid sequence.
    puts -nonewline $f "A\xC0\x40"
    flush $f
    seek $f 0
    fconfigure $f -encoding utf-8 -buffering none
} -constraints knownBug -body {
    set d [read $f]
    binary scan $d H* hd
    set hd
} -cleanup {
    close $f
    removeFile io-75.6
} -result "41"
# The current result cuts at the invalid sequence. IMHO, there should be an
# error thrown or the whole sequence should be returned as byte (compat mode).

test io-75.7 {unrepresentable character write passes and is replaced by ?} -setup {
    set fn [makeFile {} io-75.7]
    set f [open $fn w+]
    fconfigure $f -encoding iso8859-1
} -body {
    catch {puts -nonewline $f "A\u2022"} msg
    flush $f
    seek $f 0
    list [read $f] $msg
} -cleanup {
    close $f
    removeFile io-75.7
} -match glob -result [list {A} {error writing "*": illegal byte sequence}]

# Incomplete sequence test.
# This error may IMHO only be detected with the close.
# But the read already returns the incomplete sequence.
test io-75.8 {incomplete multibyte encoding read is ignored} -setup {
    set fn [makeFile {} io-75.8]
    set f [open $fn w+]
    fconfigure $f -encoding binary
    puts -nonewline $f "A\xC0"
    flush $f
    seek $f 0
    fconfigure $f -encoding utf-8 -buffering none
} -body {
    set d [read $f]
    close $f
    binary scan $d H* hd
    set hd
} -cleanup {
    removeFile io-75.8
} -result "41c0"
# The current result returns the orphan byte as byte.
# This may be expected due to special utf-8 handling.

# As utf-8 has a special treatment in multi-byte decoding, also test another
# one.
test io-75.9 {shiftjis encoding error read results in raw bytes} -setup {
    set fn [makeFile {} io-75.9]
    set f [open $fn w+]
    fconfigure $f -encoding binary
    # In shiftjis, \x81 starts a two-byte sequence.
    # But 2nd byte \xFF is not allowed
    puts -nonewline $f "A\x81\xFFA"
    flush $f
    seek $f 0
    fconfigure $f -encoding shiftjis -buffering none -eofchar "" -translation lf
} -constraints knownBug -body {
    set d [read $f]
    binary scan $d H* hd
    set hd
} -cleanup {
    close $f
    removeFile io-75.9
} -result "41"
# The current result cuts at the invalid sequence. IMHO, there should be an
# error thrown or the whole sequence should be returned as byte (compat mode).

test io-75.10 {incomplete shiftjis encoding read is ignored} -setup {
    set fn [makeFile {} io-75.10]
    set f [open $fn w+]
    fconfigure $f -encoding binary
    # \x81 announces a two byte sequence.
    puts -nonewline $f "A\x81"
    flush $f
    seek $f 0
    fconfigure $f -encoding utf-8 -buffering none -eofchar "" -translation lf
} -body {
    set d [read $f]
    close $f
    binary scan $d H* hd
    set hd
} -cleanup {
    removeFile io-75.10
} -result "4181"

# ### ### ### ######### ######### #########

# cleanup
foreach file [list fooBar longfile script script2 output test1 pipe my_script \
	test2 test3 cat stdout kyrillic.txt utf8-fcopy.txt utf8-rp.txt] {
    removeFile $file
}
cleanupTests
}
namespace delete ::tcl::test::io
return
Added tests/listRep.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
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
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
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
# This file contains tests that specifically exercise the internal representation
# of a list.
#
# Copyright © 2022 Ashok P. Nadkarni
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.

# Unlike the other files related to list commands which for the most part do
# black box testing focusing on functionality, this file does more of white box
# testing to exercise code paths that implement different list representations
# (with spans, leading free space etc., shared/unshared etc.) In addition to
# functional correctness, the tests also check for the expected internal
# representation as that pertains to performance heuristics. Generally speaking,
# combinations of the following need to be tested,
# - free space in front, back, neither, both of list representation
# - shared Tcl_Objs
# - shared internal reps (independent of shared Tcl_Objs)
# - byte-compiled vs non-compiled
#
# Being white box tests, they are sensitive to changes to further optimizations
# and changes in heuristics. That cannot be helped.

if {"::tcltest" ni [namespace children]} {
    package require tcltest 2.5
    namespace import -force ::tcltest::*
}

::tcltest::loadTestedCommands
catch [list package require -exact tcl::test [info patchlevel]]

testConstraint testlistrep [llength [info commands testlistrep]]

proc describe {l args} {dict get [testlistrep describe $l] {*}$args}

proc irange {first last} {
    set l {}
    while {$first <= $last} {
        lappend l $first
        incr first
    }
    return $l
}
proc leadSpace {l} {
    # Returns the leading space in a list store
    return [dict get [describe $l] store firstUsed]
}
proc tailSpace {l} {
    # Returns the trailing space in a list store
    array set rep [describe $l]
    dict with rep(store) {
        return [expr {$numAllocated - ($firstUsed + $numUsed)}]
    }
}
proc allocated {l} {
    # Returns the allocated space in a list store
    return [dict get [describe $l] store numAllocated]
}
proc repStoreRefCount {l} {
    # Returns the ref count for the list store
    return [dict get [describe $l] store refCount]
}
proc validate {l} {
    # Panics if internal listrep structures are not valid
    testlistrep validate $l
}
proc leadSpaceMore {l} {
    set leadSpace [leadSpace $l]
    expr {$leadSpace > 0 && $leadSpace >= 2*[tailSpace $l]}
}
proc tailSpaceMore {l} {
    set tailSpace [tailSpace $l]
    expr {$tailSpace > 0 && $tailSpace >= 2*[leadSpace $l]}
}
proc spaceEqual {l} {
    # 1 if lead and tail space shared (diff of 1 at most) and more than 0
    set leadSpace [leadSpace $l]
    set tailSpace [tailSpace $l]
    if {$leadSpace == 0 && $tailSpace == 0} {
        # At least one must be positive
        return 0
    }
    set diff [expr {$leadSpace - $tailSpace}]
    return [expr {$diff >= -1 && $diff <= 1}]
}
proc storeAddress {l} {
    return [describe $l store memoryAddress]
}
proc sameStore {l1 l2} {
    expr {[storeAddress $l1] == [storeAddress $l2]}
}
proc hasSpan {l args} {
    # Returns 1 if list has a span. If args are specified, they are checked with
    # span values (start and length)
    array set rep [describe $l]
    if {![info exists rep(span)]} {
        return 0
    }
    if {[llength $args] == 0} {
        return 1; # No need to check values
    }
    lassign $args start len
    if {[dict get $rep(span) spanStart] == $start &&
        [dict get $rep(span) spanLength] == $len} {
        return 1
    }
    return 0
}
proc checkListrep {l listLen numAllocated leadSpace tailSpace {refCount 0}} {
    # Checks if the internal representation of $l match
    # passed arguments. Return "" if yes, else error messages.
    array set rep [testlistrep describe $l]

    set rep(leadSpace) [dict get $rep(store) firstUsed]
    set rep(numAllocated) [dict get $rep(store) numAllocated]
    set rep(tailSpace) [expr {
                              $rep(numAllocated) - ($rep(leadSpace) + [dict get $rep(store) numUsed])
                          }]
    set rep(refCount) [dict get $rep(store) refCount]

    if {[info exists rep(span)]} {
        set rep(listLen) [dict get $rep(span) spanLength]
    } else {
        set rep(listLen) [dict get $rep(store) numUsed]
    }

    set errors [list]
    foreach arg {listLen numAllocated leadSpace tailSpace} {
        if {$rep($arg) != [set $arg]} {
            lappend errors "$arg in list representation ($rep($arg)) is not expected value ([set $arg])."
        }
    }
    # Check refCount only if caller has specified it as non-0
    if {$refCount && $refCount != $rep(refCount)} {
        lappend errors "refCount in list representation ($rep(refCount)) is not expected value ($refCount)."
    }
    return $errors
}

proc assertListrep {l listLen numAllocated leadSpace tailSpace {refCount 0}} {
    # Like check_listrep but raises error
    set errors [checkListrep $l $listLen $numAllocated $leadSpace $tailSpace $refCount]
    if {[llength $errors]} {
        error [join $errors \n]
    }
    return
}

# The default length should be large enough that doubling the allocation will
# clearly distinguish free space allocation difference between front and back.
# (difference in the two should at least be 2 else we cannot tell if front
# or back was favored appropriately)
proc freeSpaceNone {{len 8}} {return [testlistrep new $len 0 0]}
proc freeSpaceLead {{len 8} {lead 3}} {return [testlistrep new $len $lead 0]}
proc freeSpaceTail {{len 8} {tail 3}} {return [testlistrep new $len 0 $tail]}
proc freeSpaceBoth {{len 8} {lead 3} {tail 3}} {
    return [testlistrep new $len $lead $tail]
}
proc zombieSample {{len 1000} {leadzombies 100} {tailzombies 100}} {
    # returns an unshared listrep with zombies in front and back

    # don't combine freespacenone and lrange else zombies are freed
    set l [freeSpaceNone [expr {$len+$leadzombies+$tailzombies}]]
    return [lrange $l $leadzombies [expr {$leadzombies+$len-1}]]
}

# Just ensure above stubs return what's expected
if {[testConstraint testlistrep]} {
    assertListrep [freeSpaceNone] 8 8 0 0 1
    assertListrep [freeSpaceLead] 8 11 3 0 1
    assertListrep [freeSpaceTail] 8 11 0 3 1
    assertListrep [freeSpaceBoth] 8 14 3 3 1
    assertListrep [zombieSample] 1000 1200 0 0 1
    if {![hasSpan [zombieSample]] || [dict get [testlistrep describe [zombieSample]] span spanStart] == 0} {
        error "zombieSample span missing or span start is at 0."
    }
}

# Define some variables for some indices because the Tcl compiler will do some
# operations completely in byte code if indices are literals
set zero 0
set one 1
set two 2
set four 4
set end end

#
# Test sets:
# 1.* - unshared internal rep, no spans, with no free space
# 2.* - shared internal rep, no spans, with no free space
# 3.* - unshared internal rep, spanned
# 4.* - shared internal rep, spanned
# 5.* - shared Tcl_Obj
# 6.* - lists with zombie Tcl_Obj's

#
# listrep-1.* tests all operate on unshared listreps with no free space

test listrep-1.1 {
    Inserts in front of unshared list with no free space should reallocate with
    equal free space at front and back -- linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceNone] $zero 99]
    validate $l
    list $l [spaceEqual $l]
} -result [list {99 0 1 2 3 4 5 6 7} 1]

test listrep-1.1.1 {
    Inserts in front of unshared list with no free space should reallocate with
    equal free space at front and back -- lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceNone] $zero -1 99]
    validate $l
    list $l [spaceEqual $l]
} -result [list {99 0 1 2 3 4 5 6 7} 1]

test listrep-1.2 {
    Inserts at back of unshared list with no free space should allocate all
    space at back -- linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceNone] $end 99]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 5 6 7 99} 0 9]

test listrep-1.2.1 {
    Inserts at back of unshared list with no free space should allocate all
    space at back -- lset version
} -constraints testlistrep -body {
    set l [freeSpaceNone]
    lset l $end+1 99
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 5 6 7 99} 0 9]

test listrep-1.2.2 {
    Inserts at back of unshared list with no free space should allocate all
    space at back -- lappend version
} -constraints testlistrep -body {
    set l [freeSpaceNone]
    lappend l 99
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 5 6 7 99} 0 9]

test listrep-1.3 {
    Inserts in middle of unshared list with no free space should reallocate with
    equal free space at front and back - linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceNone] $four 99]
    validate $l
    list $l [spaceEqual $l]
} -result [list {0 1 2 3 99 4 5 6 7} 1]

test listrep-1.3.1 {
    Inserts in middle of unshared list with no free space should reallocate with
    equal free space at front and back - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceNone] $four $four-1 99]
    validate $l
    list $l [spaceEqual $l]
} -result [list {0 1 2 3 99 4 5 6 7} 1]

test listrep-1.4 {
    Deletes from front of small unshared list with no free space should
    just shift up leaving room at back - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceNone] $zero $zero]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {1 2 3 4 5 6 7} 0 1]

test listrep-1.4.1 {
    Deletes from front of small unshared list with no free space should
    just shift up leaving room at back - lassign version
} -constraints testlistrep -body {
    set l [lassign [freeSpaceNone] e]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l]
} -result [list 0 {1 2 3 4 5 6 7} 0 1]

test listrep-1.4.2 {
    Deletes from front of small unshared list with no free space should
    just shift up leaving room at back - lpop version
} -constraints testlistrep -body {
    set l [freeSpaceNone]
    set e [lpop l $zero]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l]
} -result [list 0 {1 2 3 4 5 6 7} 0 1]

test listrep-1.4.3 {
    Deletes from front of small unshared list with no free space should
    just shift up leaving room at back - lrange version
} -constraints testlistrep -body {
    set l [lrange [freeSpaceNone] $one $end]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {1 2 3 4 5 6 7} 0 1]

test listrep-1.4.4 {
    Deletes from front of small unshared list with no free space should
    just shift up leaving room at back - lremove version
} -constraints testlistrep -body {
    set l [lremove [freeSpaceNone] $zero]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {1 2 3 4 5 6 7} 0 1]

test listrep-1.5 {
    Deletes from front of large unshared list with no free space should
    create a span - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceNone 1000] $zero $one]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 2 998]
} -result [list [irange 2 999] 2 0 1]

test listrep-1.5.1 {
    Deletes from front of large unshared list with no free space should
    create a span - lassign version
} -constraints testlistrep -body {
    set l [lassign [freeSpaceNone 1000] e]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l] [hasSpan $l 1 999]
} -result [list 0 [irange 1 999] 1 0 1]

test listrep-1.5.2 {
    Deletes from front of large unshared list with no free space should
    create a span - lrange version
} -constraints testlistrep -body {
    set l [lrange [freeSpaceNone 1000] $two end]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 2 998]
} -result [list [irange 2 999] 2 0 1]

test listrep-1.5.3 {
    Deletes from front of large unshared list with no free space should
    create a span - lremove version
} -constraints testlistrep -body {
    set l [lremove [freeSpaceNone 1000] $zero]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 1 999]
} -result [list [irange 1 999] 1 0 1]

test listrep-1.5.4 {
    Deletes from front of large unshared list with no free space should
    create a span - lpop version
} -constraints testlistrep -body {
    set l [freeSpaceNone 1000]
    set e [lpop l 0]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l] [hasSpan $l 1 999]
} -result [list 0 [irange 1 999] 1 0 1]

test listrep-1.6 {
    Deletes closer to front of large list should move (smaller) front segment
    -- lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceNone 1000] $four $four]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 1 999]
} -result [list [concat [irange 0 3] [irange 5 999]] 1 0 1]

test listrep-1.6.1 {
    Deletes closer to front of large list should move (smaller) front segment
    -- lpop version
} -constraints testlistrep -body {
    set l [freeSpaceNone 1000]
    set e [lpop l $four]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l] [hasSpan $l 1 999]
} -result [list 4 [concat [irange 0 3] [irange 5 999]] 1 0 1]

test listrep-1.7 {
    Deletes closer to back of large list should move (smaller) back segment
    and will not need a span - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceNone 1000] end-$four end-$four]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list [concat [irange 0 994] [irange 996 999]] 0 1 0]

test listrep-1.7.1 {
    Deletes closer to back of large list should move (smaller) back segment
    and will not need a span - lpop version
} -constraints testlistrep -body {
    set l [freeSpaceNone 1000]
    set e [lpop l $end-4]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list 995 [concat [irange 0 994] [irange 996 999]] 0 1 0]

test listrep-1.8 {
    Deletes at back of small unshared list should not need a span - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceNone] end-$one end]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list {0 1 2 3 4 5} 0 2 0]

test listrep-1.8.1 {
    Deletes at back of small unshared list should not need a span - lrange version
} -constraints testlistrep -body {
    set l [lrange [freeSpaceNone] $zero end-$two]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list {0 1 2 3 4 5} 0 2 0]

test listrep-1.8.2 {
    Deletes at back of small unshared list should not need a span - lremove version
} -constraints testlistrep -body {
    set l [lremove [freeSpaceNone] $end-1 $end]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list {0 1 2 3 4 5} 0 2 0]

test listrep-1.8.3 {
    Deletes at back of small unshared list should not need a span - lpop version
} -constraints testlistrep -body {
    set l [freeSpaceNone]
    set e [lpop l $end]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list 7 {0 1 2 3 4 5 6} 0 1 0]

test listrep-1.9 {
    Deletes at back of large unshared list should not need a span - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceNone 1000] end-$four end]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list [irange 0 994] 0 5 0]

test listrep-1.9.1 {
    Deletes at back of large unshared list should not need a span - lrange version
} -constraints testlistrep -body {
    set l [lrange [freeSpaceNone 1000] 0 $end-5]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list [irange 0 994] 0 5 0]

test listrep-1.9.2 {
    Deletes at back of large unshared list should not need a span - lremove version
} -constraints testlistrep -body {
    set l [lremove [freeSpaceNone 1000] end-$four $end-3 end-$two $end-1 $end]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list [irange 0 994] 0 5 0]

test listrep-1.9.3 {
    Deletes at back of large unshared list should not need a span - lpop version
} -constraints testlistrep -body {
    set l [freeSpaceNone 1000]
    set e [lpop l $end]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list 999 [irange 0 998] 0 1 0]

test listrep-1.10 {
    no-op on unshared list should force a canonical list string - lreplace version
} -body {
    lreplace {    1 2   3 4   } $zero -1
} -result {1 2 3 4}

test listrep-1.10.1 {
    no-op on unshared list should force a canonical list string - lrange version
} -body {
    lrange {    1 2   3 4   } $zero $end
} -result {1 2 3 4}

test listrep-1.11 {
    Append elements to large unshared list is optimized as lappend
    so no free space in front - lreplace version
} -body {
    # Note $end, not end else byte code compiler short-cuts
    set l [lreplace [freeSpaceNone 1000] $end+1 $end+1 1000]
    validate $l
    list $l [leadSpace $l] [expr {[tailSpace $l] > 0}] [hasSpan $l]
} -result [list [irange 0 1000] 0 1 0]

test listrep-1.11.1 {
    Append elements to large unshared list is optimized as lappend
    so no free space in front - linsert version
} -body {
    # Note $end, not end else byte code compiler short-cuts
    set l [linsert [freeSpaceNone 1000] $end+1 1000]
    validate $l
    list $l [leadSpace $l] [expr {[tailSpace $l] > 0}] [hasSpan $l]
} -result [list [irange 0 1000] 0 1 0]

test listrep-1.11.2 {
    Append elements to large unshared list leaves no free space in front
    - lappend version
} -body {
    # Note $end, not end else byte code compiler short-cuts
    set l [freeSpaceNone 1000]
    lappend l 1000 1001
    validate $l
    list $l [leadSpace $l] [expr {[tailSpace $l] > 0}] [hasSpan $l]
} -result [list [irange 0 1001] 0 1 0]


test listrep-1.12 {
    Replacement of elements at front with same number elements in unshared list
    is in-place - lreplace version
} -body {
    set l [lreplace [freeSpaceNone] $zero $one 10 11]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {10 11 2 3 4 5 6 7} 0 0]

test listrep-1.12.1 {
    Replacement of elements at front with same number elements in unshared list
    is in-place - lset version
} -body {
    set l [freeSpaceNone]
    lset l 0 -1
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {-1 1 2 3 4 5 6 7} 0 0]

test listrep-1.13 {
    Replacement of elements at front with fewer elements in unshared list
    results in a spanned list with space only in front
} -body {
    set l [lreplace [freeSpaceNone] $zero $four 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {10 5 6 7} 4 0]

test listrep-1.14 {
    Replacement of elements at front with more elements in unshared list
    results in a reallocated spanned list with space at front and back
} -body {
    set l [lreplace [freeSpaceNone] $zero $one 10 11 12]
    validate $l
    list $l [spaceEqual $l]
} -result [list {10 11 12 2 3 4 5 6 7} 1]

test listrep-1.15 {
    Replacement of elements in middle with same number elements in unshared list
    is in-place - lreplace version
} -body {
    set l [lreplace [freeSpaceNone] $one $two 10 11]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 10 11 3 4 5 6 7} 0 0]

test listrep-1.15.1 {
    Replacement of elements in middle with same number elements in unshared list
    is in-place - lset version
} -body {
    set l [freeSpaceNone]
    lset l $two -1
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 -1 3 4 5 6 7} 0 0]

test listrep-1.16 {
    Replacement of elements in front half with fewer elements in unshared list
    results in a spanned list with space only in front since smaller segment moved
} -body {
    set l [lreplace [freeSpaceNone] $one $four 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 10 5 6 7} 3 0]

test listrep-1.17 {
    Replacement of elements in back half with fewer elements in unshared list
    results in a spanned list with space only at back
} -body {
    set l [lreplace [freeSpaceNone] end-$four end-$one 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 10 7} 0 3]

test listrep-1.18 {
    Replacement of elements in middle more elements in unshared list
    results in a reallocated spanned list with space at front and back
} -body {
    set l [lreplace [freeSpaceNone] $one $two 10 11 12]
    validate $l
    list $l [spaceEqual $l]
} -result [list {0 10 11 12 3 4 5 6 7} 1]

test listrep-1.19 {
    Replacement of elements at back with same number elements in unshared list
    is in-place - lreplace version
} -body {
    set l [lreplace [freeSpaceNone] $end-1 $end 10 11]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 5 10 11} 0 0]

test listrep-1.19.1 {
    Replacement of elements at back with same number elements in unshared list
    is in-place - lset version
} -body {
    set l [freeSpaceNone]
    lset l $end 10
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 5 6 10} 0 0]

test listrep-1.20 {
    Replacement of elements at back with fewer elements in unshared list
    is in-place with space only at the back
} -body {
    set l [lreplace [freeSpaceNone] $end-2 $end 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 10} 0 2]

test listrep-1.21 {
    Replacement of elements at back with more elements in unshared list
    allocates new representation with equal space at front and back
} -body {
    set l [lreplace [freeSpaceNone] $end-1 $end 10 11 12]
    validate $l
    list $l [spaceEqual $l]
} -result [list {0 1 2 3 4 5 10 11 12} 1]

#
# listrep-2.* tests all operate on shared list reps with no free space. Note the
# *list internal rep* must be shared, not only the Tcl_Obj so just assigning to
# another variable does not suffice. The lrange construct on an variable's value
# will do the needful.

test listrep-2.1 {
    Inserts in front of shared list with no free space should reallocate with
    more leading space in front - linsert version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [linsert $b $zero 99]
    validate $l
    list [repStoreRefCount $b] $l [leadSpaceMore $l] [repStoreRefCount $l]
} -result [list 2 {99 0 1 2 3 4 5 6 7} 1 1]

test listrep-2.1.1 {
    Inserts in front of shared list with no free space should reallocate with
    more leading space in front - lreplace version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b $zero -1 99]
    validate $l
    list [repStoreRefCount $b] $l [leadSpaceMore $l] [repStoreRefCount $l]
} -result [list 2 {99 0 1 2 3 4 5 6 7} 1 1]

test listrep-2.2 {
    Inserts at back of shared list with no free space should reallocate with
    more leading space in back - linsert version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [linsert $b $end 99]
    validate $l
    list [repStoreRefCount $b] $l [tailSpaceMore $l] [repStoreRefCount $l]
} -result [list 2 {0 1 2 3 4 5 6 7 99} 1 1]

test listrep-2.2.1 {
    Inserts at back of shared list with no free space should reallocate with
    more leading space in back - lreplace version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b $end+1 end+$one 99]
    validate $l
    list [repStoreRefCount $b] $l [tailSpaceMore $l] [repStoreRefCount $l]
} -result [list 2 {0 1 2 3 4 5 6 7 99} 1 1]

test listrep-2.2.2 {
    Inserts at back of shared list with no free space should reallocate with
    more leading space in back - lappend version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lappend b 99]
    validate $l
    list [repStoreRefCount $b] $l [tailSpaceMore $l] [repStoreRefCount $l]
} -result [list 1 {0 1 2 3 4 5 6 7 99} 1 1]

test listrep-2.2.3 {
    Inserts at back of shared list with no free space should reallocate with
    more leading space in back - lset version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lset b $end+1 99]
    validate $l
    list [repStoreRefCount $b] $l [tailSpaceMore $l] [repStoreRefCount $l]
} -result [list 1 {0 1 2 3 4 5 6 7 99} 1 1]

test listrep-2.3 {
    Inserts in middle of shared list with no free space should reallocate with
    equal spacing - linsert version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [linsert $b $four 99]
    validate $l
    list [repStoreRefCount $b] $l [spaceEqual $l] [repStoreRefCount $l]
} -result [list 2 {0 1 2 3 99 4 5 6 7} 1 1]

test listrep-2.3.1 {
    Inserts in middle of shared list with no free space should reallocate with
    equal spacing - lreplace version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b $four $four-1 99]
    validate $l
    list [repStoreRefCount $b] $l [spaceEqual $l] [repStoreRefCount $l]
} -result [list 2 {0 1 2 3 99 4 5 6 7} 1 1]

test listrep-2.4 {
    Deletes from front of small shared list with no free space should
    allocate new list of exact size - lreplace version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b $zero $zero]
    validate $l
    list [repStoreRefCount $b] $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 2 {1 2 3 4 5 6 7} 0 0 1]

test listrep-2.4.1 {
    Deletes from front of small shared list with no free space should
    allocate new list of exact size - lremove version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lremove $b $zero $one]
    validate $l
    list [repStoreRefCount $b] $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 2 {2 3 4 5 6 7} 0 0 1]

test listrep-2.4.2 {
    Deletes from front of small shared list with no free space should
    allocate new list of exact size - lrange version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lrange $b $one $end]
    validate $l
    list [repStoreRefCount $b] $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 2 {1 2 3 4 5 6 7} 0 0 1]

test listrep-2.4.3 {
    Deletes from front of small shared list with no free space should
    allocate new list of exact size - lassign version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lassign $b e]
    validate $l
    list $e [repStoreRefCount $b] $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 0 2 {1 2 3 4 5 6 7} 0 0 1]

test listrep-2.4.4 {
    Deletes from front of small shared list with no free space should
    allocate new list of exact size - lpop version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set l [lrange $a $zero end]; # Ensure shared listrep
    set e [lpop l $zero]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 0 {1 2 3 4 5 6 7} 0 0 1]

test listrep-2.5 {
    Deletes from front of large shared list with no free space should
    create span - lreplace version
} -constraints testlistrep -body {
    set a [freeSpaceNone 1000]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b $zero $zero]
    validate $l
    # The listrep store should be shared among a, b, l (3 refs)
    list [sameStore $b $l] [repStoreRefCount $b] $l [hasSpan $l] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 1 3 [irange 1 999] 1 0 0 3]

test listrep-2.5.1 {
    Deletes from front of large shared list with no free space should
    create span - lremove version
} -constraints testlistrep -body {
    set a [freeSpaceNone 1000]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lremove $b $zero $one]
    validate $l
    # The listrep store should be shared among a, b, l (3 refs)
    list [sameStore $b $l] [repStoreRefCount $b] $l [hasSpan $l] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 1 3 [irange 2 999] 1 0 0 3]

test listrep-2.5.2 {
    Deletes from front of large shared list with no free space should
    create span - lrange version
} -constraints testlistrep -body {
    set a [freeSpaceNone 1000]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lrange $b $two $end]
    validate $l
    # The listrep store should be shared among a, b, l (3 refs)
    list [sameStore $b $l] [repStoreRefCount $b] $l [hasSpan $l] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 1 3 [irange 2 999] 1 0 0 3]

test listrep-2.5.3 {
    Deletes from front of large shared list with no free space should
    create span - lassign version
} -constraints testlistrep -body {
    set a [freeSpaceNone 1000]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lassign $b e]
    validate $l
    # The listrep store should be shared among a, b, l (3 refs)
    list $e [sameStore $b $l] [repStoreRefCount $b] $l [hasSpan $l] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 0 1 3 [irange 1 999] 1 0 0 3]

test listrep-2.5.4 {
    Deletes from front of large shared list with no free space should
    create span - lpop version
} -constraints testlistrep -body {
    set a [freeSpaceNone 1000]
    set l [lrange $a $zero end]; # Ensure shared listrep
    set e [lpop l $zero]
    validate $l
    # The listrep store should be shared among a, b, l (3 refs)
    list $e $l [hasSpan $l] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 0 [irange 1 999] 1 0 0 2]

test listrep-2.6 {
    Deletes from back of small shared list with no free space should
    allocate new list of exact size - lreplace version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b $end $end]
    validate $l
    list [repStoreRefCount $b] $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 2 {0 1 2 3 4 5 6} 0 0 1]

test listrep-2.6.1 {
    Deletes from back of small shared list with no free space should
    allocate new list of exact size - lremove version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lremove $b $end $end-1]
    validate $l
    list [repStoreRefCount $b] $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 2 {0 1 2 3 4 5} 0 0 1]

test listrep-2.6.2 {
    Deletes from back of small shared list with no free space should
    allocate new list of exact size - lrange version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lrange $b $zero $end-1]
    validate $l
    list [repStoreRefCount $b] $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 2 {0 1 2 3 4 5 6} 0 0 1]

test listrep-2.6.3 {
    Deletes from back of small shared list with no free space should
    allocate new list of exact size - lpop version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set l [lrange $a $zero end]; # Ensure shared listrep
    set e [lpop l]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 7 {0 1 2 3 4 5 6} 0 0 1]

test listrep-2.7 {
    Deletes from back of large shared list with no free space should
    use a span - lreplace version
} -constraints testlistrep -body {
    set a [freeSpaceNone 1000]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b $end $end]
    validate $l
    # Note lead and tail space is 0 because original list store in a,b is used
    list [repStoreRefCount $b] $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 3 [irange 0 998] 0 0 3]

test listrep-2.7.1 {
    Deletes from back of large shared list with no free space should
    use a span - lremove version
} -constraints testlistrep -body {
    set a [freeSpaceNone 1000]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lremove $b $end-1 $end]
    validate $l
    # Note lead and tail space is 0 because original list store in a,b is used
    list [repStoreRefCount $b] $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 3 [irange 0 997] 0 0 3]

test listrep-2.7.2 {
    Deletes from back of large shared list with no free space should
    use a span - lrange version
} -constraints testlistrep -body {
    set a [freeSpaceNone 1000]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lrange $b $zero $end-1]
    validate $l
    # Note lead and tail space is 0 because original list store in a,b is used
    list [repStoreRefCount $b] $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 3 [irange 0 998] 0 0 3]

test listrep-2.7.3 {
    Deletes from back of large shared list with no free space should
    use a span - lpop version
} -constraints testlistrep -body {
    set a [freeSpaceNone 1000]
    set l [lrange $a $zero end]; # Ensure shared listrep
    set e [lpop l]
    validate $l
    # Note lead and tail space is 0 because original list store in a,b is used
    list $e $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 999 [irange 0 998] 0 0 2]

test listrep-2.8 {
    no-op on shared list should force a canonical list representation
    with original unchanged - lreplace version
} -body {
    set l {     1 2   3 4   }
    list [lreplace $l $zero -1] $l
} -result [list {1 2 3 4} {     1 2   3 4   }]

test listrep-2.8.1 {
    no-op on shared list should force a canonical list representation
    with original unchanged - lrange version
} -body {
    set l {     1 2   3 4   }
    list [lrange $l $zero end] $l
} -result [list {1 2 3 4} {     1 2   3 4   }]

test listrep-2.9 {
    Appends to back of large shared list with no free space allocates new
    list with space only at the back - lreplace version
} -constraints testlistrep -body {
    set a [freeSpaceNone 1000]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b $end+1 $end+1 1000]
    validate $l
    list [repStoreRefCount $b] $l [leadSpace $l] [expr {[tailSpace $l]>0}] [repStoreRefCount $l]
} -result [list 2 [irange 0 1000] 0 1 1]

test listrep-2.9.1 {
    Appends to back of large shared list with no free space allocates new
    list with space only at the back - linsert version
} -constraints testlistrep -body {
    set a [freeSpaceNone 1000]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [linsert $b $end+1 1000 1001]
    validate $l
    list [repStoreRefCount $b] $l [leadSpace $l] [expr {[tailSpace $l]>0}] [repStoreRefCount $l]
} -result [list 2 [irange 0 1001] 0 1 1]

test listrep-2.9.2 {
    Appends to back of large shared list with no free space allocates new
    list with space only at the back - lappend version
} -constraints testlistrep -body {
    set a [freeSpaceNone 1000]
    set l [lrange $a $zero end]; # Ensure shared listrep
    lappend l 1000
    validate $l
    list $l [leadSpace $l] [expr {[tailSpace $l]>0}] [repStoreRefCount $l]
} -result [list [irange 0 1000] 0 1 1]

test listrep-2.9.3 {
    Appends to back of large shared list with no free space allocates new
    list with space only at the back - lset version
} -constraints testlistrep -body {
    set a [freeSpaceNone 1000]
    set l [lrange $a $zero end]; # Ensure shared listrep
    lset l $end+1 1000
    validate $l
    list $l [leadSpace $l] [expr {[tailSpace $l]>0}] [repStoreRefCount $l]
} -result [list [irange 0 1000] 0 1 1]

test listrep-2.10 {
    Replacement of elements at front with same number in shared list results
    in a new list store with more space in front than back - lreplace version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b $zero $one 10 11]
    validate $l
    list [repStoreRefCount $b] $l [leadSpaceMore $l] [repStoreRefCount $l]
} -result [list 2 {10 11 2 3 4 5 6 7} 1 1]

test listrep-2.10.1 {
    Replacement of elements at front with same number in shared list results
    in a new list store with no extra space - lset version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set l [lrange $a $zero end]; # Ensure shared listrep
    lset l $zero 10
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {10 1 2 3 4 5 6 7} 0 0 1]

test listrep-2.11 {
    Replacement of elements at front with fewer elements in shared list
    results in a new list store with more space in front than back
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b $zero $four 10]
    validate $l
    list [repStoreRefCount $b] $l [leadSpaceMore $l] [repStoreRefCount $l]
} -result [list 2 {10 5 6 7} 1 1]

test listrep-2.12 {
    Replacement of elements at front with more elements in shared list
    results in a new spanned list with more space in front
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b $zero $one 10 11 12]
    validate $l
    list [repStoreRefCount $b] $l [leadSpaceMore $l] [repStoreRefCount $l]
} -result [list 2 {10 11 12 2 3 4 5 6 7} 1 1]

test listrep-2.13 {
    Replacement of elements in middle with same number in shared list results
    in a new list store with equal space in front and back - lreplace version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b $one $two 10 11]
    validate $l
    list [repStoreRefCount $b] $l [spaceEqual $l] [repStoreRefCount $l]
} -result [list 2 {0 10 11 3 4 5 6 7} 1 1]

test listrep-2.13.1 {
    Replacement of elements in middle with same number in shared list results
    in a new list store with exact allocation - lset version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set l [lrange $a $zero end]; # Ensure shared listrep
    lset l $one 10
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 10 2 3 4 5 6 7} 0 0 1]

test listrep-2.14 {
    Replacement of elements in middle with fewer elements in shared list
    results in a new list store with equal space
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b $one 5 10]
    validate $l
    list [repStoreRefCount $b] $l [spaceEqual $l] [repStoreRefCount $l]
} -result [list 2 {0 10 6 7} 1 1]

test listrep-2.15 {
    Replacement of elements in middle with more elements in shared list
    results in a new spanned list with space in front and back
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b $one $two 10 11 12]
    validate $l
    list [repStoreRefCount $b] $l [spaceEqual $l] [repStoreRefCount $l]
} -result [list 2 {0 10 11 12 3 4 5 6 7} 1 1]

test listrep-2.16 {
    Replacement of elements at back with same number in shared list results
    in a new list store with more space in back than front - lreplace version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b end-$one $end 10 11]
    validate $l
    list [repStoreRefCount $b] $l [tailSpaceMore $l] [repStoreRefCount $l]
} -result [list 2 {0 1 2 3 4 5 10 11} 1 1]

test listrep-2.16.1 {
    Replacement of elements at back with same number in shared list results
    in a new list store with no extra - lreplace version
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set l [lrange $a $zero end]; # Ensure shared listrep
    lset l $end 10
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 1 2 3 4 5 6 10} 0 0 1]

test listrep-2.17 {
    Replacement of elements at back with fewer elements in shared list
    results in a new list store with more space in back than front
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b end-$four $end 10]
    validate $l
    list [repStoreRefCount $b] $l [tailSpaceMore $l] [repStoreRefCount $l]
} -result [list 2 {0 1 2 10} 1 1]

test listrep-2.18 {
    Replacement of elements at back with more elements in shared list
    results in a new list store with more space in back than front
} -constraints testlistrep -body {
    set a [freeSpaceNone]
    set b [lrange $a $zero end]; # Ensure shared listrep
    set l [lreplace $b end-$four $end 10]
    validate $l
    list [repStoreRefCount $b] $l [tailSpaceMore $l] [repStoreRefCount $l]
} -result [list 2 {0 1 2 10} 1 1]

#
# listrep-3.* - tests on unshared spanned listreps

test listrep-3.1 {
    Inserts in front of unshared spanned list with room in front should just
    shrink the lead space - linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceBoth] $zero -2 -1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange -2 7] 1 3 1]

test listrep-3.1.1 {
    Inserts in front of unshared spanned list with room in front should just
    shrink the lead space - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth] $zero -1 -2 -1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange -2 7] 1 3 1]

test listrep-3.2 {
    Inserts in front of unshared spanned list with insufficient room in front
    but enough total freespace should redistribute free space - linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceBoth 8 1 10] $zero -2 -1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange -2 7] 5 4 1]

test listrep-3.2.1 {
    Inserts in front of unshared spanned list with insufficient room in front
    but enough total freespace should redistribute free space - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 8 1 10] $zero -1 -2 -1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange -2 7] 5 4 1]

test listrep-3.3 {
    Inserts in front of unshared spanned list with insufficient total freespace
    should reallocate with equal free space - linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceBoth 8 1 1] $zero -3 -2 -1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange -3 7] 6 5 1]

test listrep-3.3.1 {
    Inserts in front of unshared spanned list with insufficient total freespace
    should reallocate with equal free space - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 8 1 1] $zero -1 -3 -2 -1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange -3 7] 6 5 1]

test listrep-3.4 {
    Inserts at back of unshared spanned list with room at back should not
    reallocate - linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceBoth] $end 8]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 0 8] 3 2 1]

test listrep-3.4.1 {
    Inserts at back of unshared spanned list with room at back should not
    reallocate - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth] $end+1 $end+1 8 9]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 0 9] 3 1 1]

test listrep-3.4.2 {
    Inserts at back of unshared spanned list with room at back should not
    reallocate - lappend version
} -constraints testlistrep -body {
    set l [freeSpaceBoth]
    lappend l 8 9 10
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 0 10] 3 0 1]

test listrep-3.4.3 {
    Inserts at back of unshared spanned list with room at back should not
    reallocate - lset version
} -constraints testlistrep -body {
    set l [freeSpaceBoth]
    lset l $end+1 8
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 0 8] 3 2 1]

test listrep-3.5 {
    Inserts at back of unshared spanned list with insufficient room in back
    but enough total freespace should redistribute free space - linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceBoth 8 10 1] $end 8 9]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 0 9] 5 4 1]

test listrep-3.5.1 {
    Inserts at back of unshared spanned list with insufficient room in back
    but enough total freespace should redistribute free space - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 8 10 1] $end+1 $end+1 8 9]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 0 9] 5 4 1]

test listrep-3.5.2 {
    Inserts at back of unshared spanned list with insufficient room in back
    but enough total freespace should redistribute free space - lappend version
} -constraints testlistrep -body {
    set l [freeSpaceBoth 8 10 1]
    lappend l 8 9
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 0 9] 5 4 1]

test listrep-3.5.3 {
    Inserts at back of unshared spanned list with insufficient room in back
    but enough total freespace should redistribute free space - lset version
} -constraints testlistrep -body {
    set l [freeSpaceBoth 8 10 0]
    lset l $end+1 8
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 0 8] 5 4 1]

test listrep-3.6 {
    Inserts in back of unshared spanned list with insufficient total freespace
    should reallocate with all *additional* space at back. Note this differs
    from the insert in front case because here we realloc(). - linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceBoth 8 1 1] $end 8 9 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 0 10] 1 10 1]

test listrep-3.6.1 {
    Inserts in back of unshared spanned list with insufficient total freespace
    should reallocate with all *additional* space at back. Note this differs
    from the insert in front case because here we realloc() - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 8 1 1] $end+1 $end+1 8 9 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 0 10] 1 10 1]

test listrep-3.6.2 {
    Inserts in back of unshared spanned list with insufficient total freespace
    should reallocate with all *additional* space at back. Note this differs
    from the insert in front case because here we realloc() - lappend version
} -constraints testlistrep -body {
    set l [freeSpaceBoth 8 1 1]
    lappend l 8 9 10
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 0 10] 1 10 1]

test listrep-3.6.3 {
    Inserts in back of unshared spanned list with insufficient total freespace
    should reallocate with all *additional* space at back. Note this differs
    from the insert in front case because here we realloc() - lset version
} -constraints testlistrep -body {
    set l [freeSpaceNone]
    lset l $end+1 8
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 0 8] 0 9 1]

test listrep-3.7 {
    Inserts in front half of unshared spanned list with room in front should not
    reallocate and should move front segment
} -constraints testlistrep -body {
    set l [linsert [freeSpaceBoth] $one -2 -1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 -2 -1 1 2 3 4 5 6 7} 1 3 1]

test listrep-3.8 {
    Inserts in front half of unshared spanned list with insufficient leading
    space but with enough tail space - linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceBoth 8 1 5] $one -2 -1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 -2 -1 1 2 3 4 5 6 7} 1 3 1]

test listrep-3.8.1 {
    Inserts in front half of unshared spanned list with insufficient leading
    space but with enough tail space - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 8 1 5] $one -1 -2 -1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 -2 -1 1 2 3 4 5 6 7} 1 3 1]

test listrep-3.9 {
    Inserts in front half of unshared spanned list with sufficient total
    free space - linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceBoth 8 2 2] $one -3 -2 -1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 -3 -2 -1 1 2 3 4 5 6 7} 0 1 1]

test listrep-3.9.1 {
    Inserts in front half of unshared spanned list with sufficient total
    free space - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 8 2 2] $one -1 -3 -2 -1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 -3 -2 -1 1 2 3 4 5 6 7} 0 1 1]

test listrep-3.10 {
    Inserts in front half of unshared spanned list with insufficient total space.
    Note use of realloc() means new space will be at the back - linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceBoth 8 1 1] $one -3 -2 -1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 -3 -2 -1 1 2 3 4 5 6 7} 1 10 1]

test listrep-3.10.1 {
    Inserts in front half of unshared spanned list with insufficient total space.
    Note use of realloc() means new space will be at the back - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 8 1 1] $one -1 -3 -2 -1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 -3 -2 -1 1 2 3 4 5 6 7} 1 10 1]

test listrep-3.11 {
    Inserts in back half of unshared spanned list with room in back should not
    reallocate and should move back segment - linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceBoth] $end-$one 8 9]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 1 2 3 4 5 6 8 9 7} 3 1 1]

test listrep-3.11.1 {
    Inserts in back half of unshared spanned list with room in back should not
    reallocate and should move back segment - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth] $end -1 8 9]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 1 2 3 4 5 6 8 9 7} 3 1 1]

test listrep-3.12 {
    Inserts in back half of unshared spanned list with insufficient tail
    space but with enough leading space - linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceBoth 8 5 1] $end-$one 8 9]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 1 2 3 4 5 6 8 9 7} 3 1 1]

test listrep-3.12.1 {
    Inserts in back half of unshared spanned list with insufficient tail
    space but with enough leading space - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 8 5 1] $end -1 8 9]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 1 2 3 4 5 6 8 9 7} 3 1 1]

test listrep-3.13 {
    Inserts in back half of unshared spanned list with sufficient total
    free space - linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceBoth 8 2 2] $end-$one 8 9 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 1 2 3 4 5 6 8 9 10 7} 0 1 1]

test listrep-3.13.1 {
    Inserts in back half of unshared spanned list with sufficient total
    free space - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 8 2 2] $end -1 8 9 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 1 2 3 4 5 6 8 9 10 7} 0 1 1]

test listrep-3.14 {
    Inserts in back half of unshared spanned list with insufficient
    total space. Note use of realloc() means new space will be at the
    back - linsert version
} -constraints testlistrep -body {
    set l [linsert [freeSpaceBoth 8 1 1] $end-$one 8 9 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 1 2 3 4 5 6 8 9 10 7} 1 10 1]

test listrep-3.14.1 {
    Inserts in back half of unshared spanned list with insufficient
    total space. Note use of realloc() means new space will be at the
    back - lrepalce version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 8 1 1] $end -1 8 9 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 1 2 3 4 5 6 8 9 10 7} 1 10 1]

test listrep-3.15 {
    Deletes from front of small unshared span list results in elements
    moved up front and span removal - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth] $zero $zero]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list {1 2 3 4 5 6 7} 0 7 0]

test listrep-3.15.1 {
    Deletes from front of small unshared span list results in elements
    moved up front and span removal - lremove version
} -constraints testlistrep -body {
    set l [lremove [freeSpaceBoth] $zero $one]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list {2 3 4 5 6 7} 0 8 0]

test listrep-3.15.2 {
    Deletes from front of small unshared span list results in elements
    moved up front and span removal - lrange version
} -constraints testlistrep -body {
    set l [lrange [freeSpaceBoth] $one $end]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list {1 2 3 4 5 6 7} 0 7 0]

test listrep-3.15.3 {
    Deletes from front of small unshared span list results in elements
    moved up front and span removal - lassign version
} -constraints testlistrep -body {
    set l [lassign [freeSpaceBoth] e]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list 0 {1 2 3 4 5 6 7} 0 7 0]

test listrep-3.15.4 {
    Deletes from front of small unshared span list results in elements
    moved up front and span removal - lpop version
} -constraints testlistrep -body {
    set l [freeSpaceBoth]
    set e [lpop l $zero]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list {1 2 3 4 5 6 7} 0 7 0]

test listrep-3.16 {
    Deletes from front of large unshared span list results in another
    span - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 1000 10 10] $zero $one]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 12 998]
} -result [list [irange 2 999] 12 10 1]

test listrep-3.16.1 {
    Deletes from front of large unshared span list results in another
    span - lremove version
} -constraints testlistrep -body {
    set l [lremove [freeSpaceBoth 1000 10 10] $zero $one]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 12 998]
} -result [list [irange 2 999] 12 10 1]

test listrep-3.16.2 {
    Deletes from front of large unshared span list results in another
    span - lrange version
} -constraints testlistrep -body {
    set l [lrange [freeSpaceBoth 1000 10 10] $two $end]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 12 998]
} -result [list [irange 2 999] 12 10 1]

test listrep-3.16.3 {
    Deletes from front of large unshared span list results in another
    span - lassign version
} -constraints testlistrep -body {
    set l [lassign [freeSpaceBoth 1000 10 10] e]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l] [hasSpan $l 11 999]
} -result [list 0 [irange 1 999] 11 10 1]

test listrep-3.16.4 {
    Deletes from front of large unshared span list results in another
    span - lpop version
} -constraints testlistrep -body {
    set l [freeSpaceBoth 1000 10 10]
    set e [lpop l $zero]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l] [hasSpan $l 11 999]
} -result [list 0 [irange 1 999] 11 10 1]

test listrep-3.17 {
    Deletes from back of small unshared span list results in new store
    without span - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth] $end $end]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list {0 1 2 3 4 5 6} 0 7 0]

test listrep-3.17.1 {
    Deletes from back of small unshared span list results in new store
    without span - lremove version
} -constraints testlistrep -body {
    set l [lremove [freeSpaceBoth] $end]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list {0 1 2 3 4 5 6} 0 7 0]

test listrep-3.17.2 {
    Deletes from back of small unshared span list results in new store
    without span - lrange version
} -constraints testlistrep -body {
    set l [lrange [freeSpaceBoth] $zero $end-1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list {0 1 2 3 4 5 6} 0 7 0]

test listrep-3.17.3 {
    Deletes from back of small unshared span list results in new store
    without span - lpop version
} -constraints testlistrep -body {
    set l [freeSpaceBoth]
    set e [lpop l]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
} -result [list 7 {0 1 2 3 4 5 6} 0 7 0]

test listrep-3.18 {
    Deletes from back of large unshared span list results in another
    span - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 1000 10 10] $end-1 $end]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 10 998]
} -result [list [irange 0 997] 10 12 1]

test listrep-3.18.1 {
    Deletes from back of large unshared span list results in another
    span - lremove version
} -constraints testlistrep -body {
    set l [lremove [freeSpaceBoth 1000 10 10] $end-1 $end]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 10 998]
} -result [list [irange 0 997] 10 12 1]

test listrep-3.18.2 {
    Deletes from back of large unshared span list results in another
    span - lrange version
} -constraints testlistrep -body {
    set l [lrange [freeSpaceBoth 1000 10 10] $zero $end-2]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 10 998]
} -result [list [irange 0 997] 10 12 1]

test listrep-3.18.3 {
    Deletes from back of large unshared span list results in another
    span - lpop version
} -constraints testlistrep -body {
    set l [freeSpaceBoth 1000 10 10]
    set e [lpop l]
    validate $l
    list $e $l [leadSpace $l] [tailSpace $l] [hasSpan $l 10 999]
} -result [list 999 [irange 0 998] 10 11 1]

test listrep-3.19 {
    Deletes from front half of small unshared span list results in
    movement of smaller front segment - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth] $one $two]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 5 6]
} -result [list {0 3 4 5 6 7} 5 3 1]

test listrep-3.19.1 {
    Deletes from front half of small unshared span list results in
    movement of smaller front segment - lremove version
} -constraints testlistrep -body {
    set l [lremove [freeSpaceBoth] $one $two]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 5 6]
} -result [list {0 3 4 5 6 7} 5 3 1]

test listrep-3.20 {
    Deletes from front half of large unshared span list results in
    movement of smaller front segment - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 1000 10 10] $one $two]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 12 998]
} -result [list [list 0 {*}[irange 3 999]] 12 10 1]

test listrep-3.20.1 {
    Deletes from front half of large unshared span list results in
    movement of smaller front segment - lremove version
} -constraints testlistrep -body {
    set l [lremove [freeSpaceBoth 1000 10 10] $one $two]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 12 998]
} -result [list [list 0 {*}[irange 3 999]] 12 10 1]

test listrep-3.21 {
    Deletes from back half of small unshared span list results in
    movement of smaller back segment - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth] $end-2 $end-1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 3 6]
} -result [list {0 1 2 3 4 7} 3 5 1]

test listrep-3.21.1 {
    Deletes from back half of small unshared span list results in
    movement of smaller back segment - lremove version
} -constraints testlistrep -body {
    set l [lremove [freeSpaceBoth] $end-2 $end-1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 3 6]
} -result [list {0 1 2 3 4 7} 3 5 1]

test listrep-3.22 {
    Deletes from back half of large unshared span list results in
    movement of smaller back segment - lreplace version
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 1000 10 10] $end-2 $end-1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 10 998]
} -result [list [list {*}[irange 0 996] 999] 10 12 1]

test listrep-3.22.1 {
    Deletes from back half of large unshared span list results in
    movement of smaller back segment - lremove version
} -constraints testlistrep -body {
    set l [lremove [freeSpaceBoth 1000 10 10] $end-2 $end-1]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 10 998]
} -result [list [list {*}[irange 0 996] 999] 10 12 1]

test listrep-3.23 {
    Replacement of elements at front with same number elements in unshared
    spanned list is in-place - lreplace version
} -body {
    set l [lreplace [freeSpaceBoth] $zero $one 10 11]
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {10 11 2 3 4 5 6 7} 3 3]

test listrep-3.23.1 {
    Replacement of elements at front with same number elements in unshared
    spanned list is in-place - lset version
} -body {
    set l [freeSpaceBoth]
    lset l $zero 10
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {10 1 2 3 4 5 6 7} 3 3]

test listrep-3.24 {
    Replacement of elements at front with fewer elements in unshared
    spanned list expands leading space - lreplace version
} -body {
    set l [lreplace [freeSpaceBoth] $zero $four 10]
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {10 5 6 7} 7 3]

test listrep-3.25 {
    Replacement of elements at front with more elements in unshared
    spanned list with sufficient leading space shrinks leading space
} -body {
    set l [lreplace [freeSpaceBoth] $zero $one 10 11 12]
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {10 11 12 2 3 4 5 6 7} 2 3]

test listrep-3.26 {
    Replacement of elements at front with more elements in unshared
    spanned list with insufficient leading space but sufficient total
    free space
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 8 1 10] $zero $one 10 11 12 13]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {10 11 12 13 2 3 4 5 6 7} 5 4 1]

test listrep-3.27 {
    Replacement of elements at front in unshared spanned list with insufficient
    total freespace should reallocate with equal free space
} -constraints testlistrep -body {
    set l [lreplace [freeSpaceBoth 8 1 1] $zero $one 10 11 12 13 14]
    validate $l
    list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {10 11 12 13 14 2 3 4 5 6 7} 6 5 1]

test listrep-3.28 {
    Replacement of elements at back with same number of elements in unshared
    spanned list is in-place - lreplace version
} -body {
    set l [lreplace [freeSpaceBoth] $end-1 $end 10 11]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 5 10 11} 3 3]

test listrep-3.28.1 {
    Replacement of elements at back with same number of elements in unshared
    spanned list is in-place - lset version
} -body {
    set l [freeSpaceBoth]
    lset l $end 10
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 5 6 10} 3 3]

test listrep-3.29 {
    Replacement of elements at back with fewer elements in unshared
    spanned list expands tail space
} -body {
    set l [lreplace [freeSpaceBoth] $end-2 $end 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 10} 3 5]

test listrep-3.30 {
    Replacement of elements at back with more elements in unshared
    spanned list with sufficient tail space shrinks tailspace
} -body {
    set l [lreplace [freeSpaceBoth] $end-1 $end 10 11 12]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 5 10 11 12} 3 2]

test listrep-3.31 {
    Replacement of elements at back with more elements in unshared spanned list
    with insufficient tail space but enough total free space moves up the span
} -body {
    set l [lreplace [freeSpaceBoth 8 2 2] $end-1 $end 10 11 12 13 14]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 5 10 11 12 13 14} 0 1]

test listrep-3.32 {
    Replacement of elements at back with more elements in unshared spanned list
    with insufficient total space reallocates with more room in the tail because
    of realloc()
} -body {
    set l [lreplace [freeSpaceBoth 8 1 1] $end-1 $end 10 11 12 13 14]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 5 10 11 12 13 14} 1 10]

test listrep-3.33 {
    Replacement of elements in the middle in an unshared spanned list with
    the same number of elements - lreplace version
} -body {
    set l [lreplace [freeSpaceBoth] $two $four 10 11 12]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 10 11 12 5 6 7} 3 3]

test listrep-3.33.1 {
    Replacement of elements in the middle in an unshared spanned list with
    the same number of elements - lset version
} -body {
    set l [freeSpaceBoth]
    lset l $two 10
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 10 3 4 5 6 7} 3 3]

test listrep-3.34 {
    Replacement of elements in an unshared spanned list with fewer elements
    in the front half moves the front (smaller) segment
} -body {
    set l [lreplace [freeSpaceBoth] $two $four 10 11]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 10 11 5 6 7} 4 3]

test listrep-3.35 {
    Replacement of elements in an unshared spanned list with fewer elements
    in the back half moves the tail (smaller) segment
} -body {
    set l [lreplace [freeSpaceBoth] $end-2 $end-1 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 10 7} 3 4]

test listrep-3.36 {
    Replacement of elements in an unshared spanned list with more elements
    when both front and back have room should move the smaller segment
    (front case)
} -body {
    set l [lreplace [freeSpaceBoth] $one $two 8 9 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 8 9 10 3 4 5 6 7} 2 3]

test listrep-3.37 {
    Replacement of elements in an unshared spanned list with more elements
    when both front and back have room should move the smaller segment
    (back case)
} -body {
    set l [lreplace [freeSpaceBoth] $end-2 $end-1 8 9 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 8 9 10 7} 3 2]

test listrep-3.38 {
    Replacement of elements in an unshared spanned list with more elements
    when only front has room
} -body {
    set l [lreplace [freeSpaceBoth 8 3 1] $end-1 $end-1 8 9 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 1 2 3 4 5 8 9 10 7} 1 1]

test listrep-3.39 {
    Replacement of elements in an unshared spanned list with more elements
    when only back has room
} -body {
    set l [lreplace [freeSpaceBoth 8 1 3] $one $one 8 9 10]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 8 9 10 2 3 4 5 6 7} 1 1]

test listrep-3.40 {
    Replacement of elements in an unshared spanned list with more elements
    when neither send has enough room by itself
} -body {
    set l [lreplace [freeSpaceBoth] $one $one 8 9 10 11 12]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 8 9 10 11 12 2 3 4 5 6 7} 1 1]

test listrep-3.41 {
    Replacement of elements in an unshared spanned list with more elements
    when there is not enough free space results in new allocation. The back
    end has more space because of realloc()
} -body {
    set l [lreplace [freeSpaceBoth 8 1 1] $one $one 8 9 10 11 12]
    validate $l
    list $l [leadSpace $l] [tailSpace $l]
} -result [list {0 8 9 10 11 12 2 3 4 5 6 7} 1 11]

#
# 4.* - tests on shared spanned lists

test listrep-4.1 {
    Inserts in front of shared spanned list with used elements in lead space
    creates new list rep with more lead than tail space - linsert version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [linsert $spanl $zero -1]
    validate $l
    list $master $spanl $l [leadSpaceMore $l] [hasSpan $l] [repStoreRefCount $master] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 0 999] [irange 2 997] [list -1 {*}[irange 2 997]] 1 1 2 2 1]

test listrep-4.1.1 {
    Inserts in front of shared spanned list with used elements in lead space
    creates new list rep with more lead than tail space - lreplace version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lreplace $spanl $zero -1 -2]
    validate $l
    list $master $spanl $l [leadSpaceMore $l] [hasSpan $l] [repStoreRefCount $master] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 0 999] [irange 2 997] [list -2 {*}[irange 2 997]] 1 1 2 2 1]

test listrep-4.2 {
    Inserts in front of shared spanned list with orphaned leading elements
    allocate a new list rep with more lead than tail space - linsert version
    TODO - ideally this should garbage collect the orphans and reuse the lead space
    but that needs a "lprepend" command else the listrep operand is shared and hence
    orphans cannot be freed
} -constraints testlistrep -body {
    set master [freeSpaceLead 1000 100]
    set spanl [lrange $master $two $end-2]
    unset master; # So elements at 0, 1 are not used
    set l [linsert $spanl $zero -1]
    validate $l
    list $spanl $l [sameStore $spanl $l] [leadSpaceMore $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [list -1 {*}[irange 2 997]] 0 1 1 1 1]

test listrep-4.2.1 {
    Inserts in front of shared spanned list with orphaned leading elements
    allocate a new list rep with more lead than tail space - lreplace version
    TODO - ideally this should garbage collect the orphans and reuse the lead space
    but that needs a "lprepend" command else the listrep operand is shared and hence
    orphans cannot be freed
} -constraints testlistrep -body {
    set master [freeSpaceLead 1000 100]
    set spanl [lrange $master $two $end-2]
    unset master; # So elements at 0, 1 are not used
    set l [lreplace $spanl $zero -1 -2]
    validate $l
    list $spanl $l [sameStore $spanl $l] [leadSpaceMore $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [list -2 {*}[irange 2 997]] 0 1 1 1 1]

test listrep-4.3 {
    Inserts in front of shared spanned list where span is at front of used
    space reuses the same list store - linsert version
} -constraints testlistrep -body {
    set master [freeSpaceLead 1000 100]
    set spanl [lrange $master $zero $end-2]
    set l [linsert $spanl $zero -1]
    validate $l
    list $spanl $l [sameStore $spanl $l] [leadSpace $l] [tailSpace $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 0 997] [irange -1 997] 1 99 0 1 3 3]

test listrep-4.3.1 {
    Inserts in front of shared spanned list where span is at front of used
    space reuses the same list store - lreplace version
} -constraints testlistrep -body {
    set master [freeSpaceLead 1000 100]
    set spanl [lrange $master $zero $end-2]
    set l [lreplace $spanl $zero -1 -1]
    validate $l
    list $spanl $l [sameStore $spanl $l] [leadSpace $l] [tailSpace $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 0 997] [irange -1 997] 1 99 0 1 3 3]

test listrep-4.4 {
    Inserts in front of shared spanned list where span is at front of used
    space allocates new listrep if lead space insufficient even if total free space
    is sufficient. New listrep should have more lead space than tail space.
    - linsert version
} -constraints testlistrep -body {
    set master [freeSpaceBoth 1000 2]
    set spanl [lrange $master $zero $end-2]
    set l [linsert $spanl $zero -3 -2 -1]
    validate $l
    list $spanl $l [sameStore $spanl $l] [leadSpaceMore $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 0 997] [irange -3 997] 0 1 1 2 1]

test listrep-4.4.1 {
    Inserts in front of shared spanned list where span is at front of used
    space allocates new listrep if lead space insufficient even if total free space
    is sufficient. New listrep should have more lead space than tail space.
    - lreplace version
} -constraints testlistrep -body {
    set master [freeSpaceBoth 1000 2]
    set spanl [lrange $master $zero $end-2]
    set l [lreplace $spanl $zero -1 -3 -2 -1]
    validate $l
    list $spanl $l [sameStore $spanl $l] [leadSpaceMore $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 0 997] [irange -3 997] 0 1 1 2 1]

test listrep-4.5 {
    Inserts in back of shared spanned list where span is at end of used space
    still allocates a new listrep and trailing space is more than leading space
    - linsert version
} -constraints testlistrep -body {
    set master [freeSpaceBoth 1000 2]
    set spanl [lrange $master $two $end]
    set l [linsert $spanl $end 1000]
    validate $l
    list $spanl $l [sameStore $spanl $l] [tailSpaceMore $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 999] [irange 2 1000] 0 1 1 2 1]

test listrep-4.5.1 {
    Inserts in back of shared spanned list where span is at end of used space
    still allocates a new listrep and trailing space is more than leading space
    - lreplace version
} -constraints testlistrep -body {
    set master [freeSpaceBoth 1000 2]
    set spanl [lrange $master $two $end]
    set l [lreplace $spanl $end+1 $end+1 1000]
    validate $l
    list $spanl $l [sameStore $spanl $l] [tailSpaceMore $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 999] [irange 2 1000] 0 1 1 2 1]

test listrep-4.5.2 {
    Inserts in back of shared spanned list where span is at end of used space
    still allocates a new listrep and trailing space is more than leading space
    - lappend version
} -constraints testlistrep -body {
    set master [freeSpaceBoth 1000 2]
    set l [lrange $master $two $end]
    lappend l 1000
    validate $l
    list $l [sameStore $master $l] [tailSpaceMore $l] [hasSpan $l] [repStoreRefCount $l]
} -result [list  [irange 2 1000] 0 1 1 1]

test listrep-4.5.3 {
    Inserts in back of shared spanned list where span is at end of used space
    still allocates a new listrep and trailing space is more than leading space
    - lset version
} -constraints testlistrep -body {
    set master [freeSpaceBoth 1000 2]
    set l [lrange $master $two $end]
    lset l $end+1 1000
    validate $l
    list $l [sameStore $master $l] [tailSpaceMore $l] [hasSpan $l] [repStoreRefCount $l]
} -result [list  [irange 2 1000] 0 1 1 1]


test listrep-4.6 {
    Inserts in middle of shared spanned list allocates a new listrep with equal
    lead and tail space - linsert version
} -constraints testlistrep -body {
    set master [freeSpaceBoth 1000 2]
    set spanl [lrange $master $two $end-2]
    set i 200
    set l [linsert $spanl $i 1000]
    validate $l
    list $spanl $l [sameStore $spanl $l] [spaceEqual $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [concat [irange 2 201] 1000 [irange 202 997]] 0 1 1 2 1]

test listrep-4.6.1 {
    Inserts in middle of shared spanned list allocates a new listrep with equal
    lead and tail space - lreplace version
} -constraints testlistrep -body {
    set master [freeSpaceBoth 1000 2]
    set spanl [lrange $master $two $end-2]
    set i 200
    set l [lreplace $spanl $i -1 1000]
    validate $l
    list $spanl $l [sameStore $spanl $l] [spaceEqual $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [concat [irange 2 201] 1000 [irange 202 997]] 0 1 1 2 1]

test listrep-4.7 {
    Deletes from front of shared spanned list do not create a new allocation
    - lreplace version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lreplace $spanl $zero $one]
    validate $l
    list $spanl $l [sameStore $spanl $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [irange 4 997] 1 1 3 3]

test listrep-4.7.1 {
    Deletes from front of shared spanned list do not create a new allocation
    - lremove version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lremove $spanl $zero $one]
    validate $l
    list $spanl $l [sameStore $spanl $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [irange 4 997] 1 1 3 3]

test listrep-4.7.2 {
    Deletes from front of shared spanned list do not create a new allocation
    - lrange version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lrange $spanl $two $end]
    validate $l
    list $spanl $l [sameStore $spanl $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [irange 4 997] 1 1 3 3]

test listrep-4.7.3 {
    Deletes from front of shared spanned list do not create a new allocation
    - lassign version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lassign $spanl e]
    validate $l
    list $e $spanl $l [sameStore $spanl $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list 2 [irange 2 997] [irange 3 997] 1 1 3 3]

test listrep-4.7.4 {
    Deletes from front of shared spanned list do not create a new allocation
    - lpop version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set l [lrange $master $two $end-2]
    set e [lpop l $zero]
    validate $l
    list $e $l [sameStore $master $l] [hasSpan $l] [repStoreRefCount $l]
} -result [list 2 [irange 3 997] 1 1 2]

test listrep-4.8 {
    Deletes from end of shared spanned list do not create a new allocation
    - lreplace version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lreplace $spanl $end-1 $end]
    validate $l
    list $spanl $l [sameStore $spanl $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [irange 2 995] 1 1 3 3]

test listrep-4.8.1 {
    Deletes from end of shared spanned list do not create a new allocation
    - lremove version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lremove $spanl $end-1 $end]
    validate $l
    list $spanl $l [sameStore $spanl $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [irange 2 995] 1 1 3 3]

test listrep-4.8.2 {
    Deletes from end of shared spanned list do not create a new allocation
    - lrange version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lrange $spanl 0 $end-2]
    validate $l
    list $spanl $l [sameStore $spanl $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [irange 2 995] 1 1 3 3]

test listrep-4.8.3 {
    Deletes from end of shared spanned list do not create a new allocation
    - lpop version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set l [lrange $master $two $end-2]
    set e [lpop l]
    validate $l
    list $e $l [sameStore $master $l] [hasSpan $l] [repStoreRefCount $l]
} -result [list 997 [irange 2 996] 1 1 2]

test listrep-4.9 {
    Deletes from middle of shared spanned list creates a new allocation with
    equal free space at front and back - lreplace version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set i 500
    set l [lreplace $spanl $i $i]
    validate $l
    list $spanl $l [sameStore $spanl $l] [hasSpan $l] [spaceEqual $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [concat [irange 2 501] [irange 503 997]] 0 1 1 2 1]

test listrep-4.9.1 {
    Deletes from middle of shared spanned list creates a new allocation with
    equal free space at front and back - lremove version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set i 500
    set l [lremove $spanl $i $i]
    validate $l
    list $spanl $l [sameStore $spanl $l] [hasSpan $l] [spaceEqual $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [concat [irange 2 501] [irange 503 997]] 0 1 1 2 1]

test listrep-4.9.2 {
    Deletes from middle of shared spanned list creates a new allocation with
    equal free space at front and back - lpop version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set l [lrange $master $two $end-2]
    set i 500
    set e [lpop l $i]
    validate $l
    list $e $l [sameStore $master $l] [hasSpan $l] [spaceEqual $l] [repStoreRefCount $l]
} -result [list 502 [concat [irange 2 501] [irange 503 997]] 0 1 1 1]

test listrep-4.10 {
    Replacements with same number of elements at front of shared spanned list
    create a new allocation with more space in front - lreplace version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lreplace $spanl $zero $one -2 -1]
    validate $l
    list $spanl $l [sameStore $spanl $l] [leadSpaceMore $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [concat {-2 -1} [irange 4 997]] 0 1 1 2 1]

test listrep-4.10.1 {
    Replacements with same number of elements at front of shared spanned list
    create a new allocation with exact size
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set l [lrange $master $two $end-2]
    lset l $zero -1
    validate $l
    list $l [sameStore $master $l] [hasSpan $l] [repStoreRefCount $l]
} -result [list [concat {-1} [irange 3 997]] 0 0 1]

test listrep-4.11 {
    Replacements with fewer elements at front of shared spanned list
    create a new allocation with more space in front
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lreplace $spanl $zero $one -1]
    validate $l
    list $spanl $l [sameStore $spanl $l] [leadSpaceMore $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [concat {-1} [irange 4 997]] 0 1 1 2 1]

test listrep-4.12 {
    Replacements with more elements at front of shared spanned list
    create a new allocation with more space in front
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lreplace $spanl $zero $one -3 -2 -1]
    validate $l
    list $spanl $l [sameStore $spanl $l] [leadSpaceMore $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [concat {-3 -2 -1} [irange 4 997]] 0 1 1 2 1]

test listrep-4.13 {
    Replacements with same number of elements at back of shared spanned list
    create a new allocation with more space in back - lreplace version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lreplace $spanl $end-1 $end 1000 1001]
    validate $l
    list $spanl $l [sameStore $spanl $l] [tailSpaceMore $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [concat [irange 2 995] {1000 1001}] 0 1 1 2 1]

test listrep-4.13.1 {
    Replacements with same number of elements at back of shared spanned list
    create a new exact allocation with no span - lset version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set l [lrange $master $two $end-2]
    lset l $end 1000
    validate $l
    list $l [sameStore $master $l] [tailSpace $l] [hasSpan $l] [repStoreRefCount $l]
} -result [list [concat [irange 2 996] {1000}] 0 0 0 1]

test listrep-4.14 {
    Replacements with fewer elements at back of shared spanned list
    create a new allocation with more space in back
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lreplace $spanl $end-1 $end 1000]
    validate $l
    list $spanl $l [sameStore $spanl $l] [tailSpaceMore $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [concat [irange 2 995] {1000}] 0 1 1 2 1]

test listrep-4.15 {
    Replacements with more elements at back of shared spanned list
    create a new allocation with more space in back
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lreplace $spanl $end-1 $end 1000 1001 1002]
    validate $l
    list $spanl $l [sameStore $spanl $l] [tailSpaceMore $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [concat [irange 2 995] {1000 1001 1002}] 0 1 1 2 1]

test listrep-4.16 {
    Replacements with same number of elements in middle of shared spanned list
    create a new allocation with equal lead and tail sapce
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lreplace $spanl $one $two -2 -1]
    validate $l
    list $spanl $l [sameStore $spanl $l] [spaceEqual $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [concat {2 -2 -1} [irange 5 997]] 0 1 1 2 1]

test listrep-4.16.1 {
    Replacements with same number of elements in middle of shared spanned list
    create a new exact allocation - lset version
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set l [lrange $master $two $end-2]
    lset l $one -2
    validate $l
    list $l [sameStore $master $l] [hasSpan $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [concat {2 -2} [irange 4 997]] 0 0 0 1]

test listrep-4.17 {
    Replacements with fewer elements in middle of shared spanned list
    create a new allocation with equal lead and tail sapce
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lreplace $spanl $end-2 $end-1 1000]
    validate $l
    list $spanl $l [sameStore $spanl $l] [spaceEqual $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [concat [irange 2 994] {1000 997}] 0 1 1 2 1]

test listrep-4.18 {
    Replacements with more elements in middle of shared spanned list
    create a new allocation with equal lead and tail sapce
} -constraints testlistrep -body {
    set master [freeSpaceNone 1000]
    set spanl [lrange $master $two $end-2]
    set l [lreplace $spanl $end-2 $end-1 1000 1001 1002]
    validate $l
    list $spanl $l [sameStore $spanl $l] [spaceEqual $l] [hasSpan $l] [repStoreRefCount $spanl] [repStoreRefCount $l]
} -result [list [irange 2 997] [concat [irange 2 994] {1000 1001 1002 997}] 0 1 1 2 1]

# 5.* - tests on shared Tcl_Obj
# Tests when Tcl_Obj is shared but listrep is not. This is to ensure that
# checks for shared values check the Tcl_Obj reference counts in addition to
# the list internal representation reference counts. Probably some or all
# cases are already covered elsewhere but easier to just test than look.
test listrep-5.1 {
    Verify that operation on a shared Tcl_Obj with a single-ref, spanless
    list representation only modifies the target object - lappend version
} -constraints testlistrep -body {
    set l [freeSpaceNone]
    set l2 $l
    set same [sameStore $l $l2]
    lappend l 8
    list $same $l $l2 [sameStore $l $l2]
} -result [list 1 [irange 0 8] [irange 0 7] 0]

test listrep-5.1.1 {
    Verify that operation on a shared Tcl_Obj with a single-ref, spanless
    list representation only modifies the target object - lset version
} -constraints testlistrep -body {
    set l [freeSpaceNone]
    set l2 $l
    set same [sameStore $l $l2]
    lset l $end+1 8
    list $same $l $l2 [sameStore $l $l2]
} -result [list 1 [irange 0 8] [irange 0 7] 0]

test listrep-5.1.2 {
    Verify that operation on a shared Tcl_Obj with a single-ref, spanless
    list representation only modifies the target object - lpop version
} -constraints testlistrep -body {
    set l [freeSpaceNone]
    set l2 $l
    set same [sameStore $l $l2]
    lpop l
    list $same $l $l2 [sameStore $l $l2] [hasSpan $l]
} -result [list 1 [irange 0 6] [irange 0 7] 0 0]

test listrep-5.2 {
    Verify that operation on a shared Tcl_Obj with a single-ref, spanned
    list representation only modifies the target object - lappend version
} -constraints testlistrep -body {
    set l [freeSpaceBoth 1000 10 10]
    set l2 $l
    set same [sameStore $l $l2]
    lappend l 1000
    list $same $l $l2 [sameStore $l $l2] [hasSpan $l] [hasSpan $l2]
} -result [list 1 [irange 0 1000] [irange 0 999] 0 1 1]

test listrep-5.2.1 {
    Verify that operation on a shared Tcl_Obj with a single-ref, spanned
    list representation only modifies the target object - lset version
} -constraints testlistrep -body {
    set l [freeSpaceBoth 1000 10 10]
    set l2 $l
    set same [sameStore $l $l2]
    lset l $end+1 1000
    list $same $l $l2 [sameStore $l $l2] [hasSpan $l] [hasSpan $l2]
} -result [list 1 [irange 0 1000] [irange 0 999] 0 1 1]

test listrep-5.2.2 {
    Verify that operation on a shared Tcl_Obj with a single-ref, spanned
    list representation only modifies the target object - lpop version
} -constraints testlistrep -body {
    set l [freeSpaceNone 1000]
    set l2 $l
    set same [sameStore $l $l2]
    lpop l
    list $same $l $l2 [sameStore $l $l2] [hasSpan $l] [hasSpan $l2]
} -result [list 1 [irange 0 998] [irange 0 999] 1 1 0]

#
# 6.* - tests when lists contain zombies.
# The list implementation does lazy freeing in some cases so the list store
# contain Tcl_Obj's that are not actually referenced by any list (zombies).
# These are to be freed next time the list store is modified by a list
# operation as long as it is no longer shared.
test listrep-6.1 {
    Verify that zombies are freed up - linsert at front
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    # set l {} is for reference counts to drop to 1
    set l [linsert $l[set l {}] $zero -1]
    list $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [list -1 {*}[irange 10 209]] 1 9 10 1]

test listrep-6.1.1 {
    Verify that zombies are freed up - linsert in middle
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    # set l {} is for reference counts to drop to 1
    set l [linsert $l[set l {}] $one -1]
    list $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [list 10 -1 {*}[irange 11 209]] 1 9 10 1]

test listrep-6.1.2 {
    Verify that zombies are freed up - linsert at end
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    # set l {} is for reference counts to drop to 1
    set l [linsert $l[set l {}] $end 210]
    list $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 10 210] 1 10 9 1]

test listrep-6.2 {
    Verify that zombies are freed up - lrange version (whole)
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    # set l {} is for reference counts to drop to 1
    set l [lrange $l[set l {}] $zero $end]
    list $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 10 209] 1 10 10 1]

test listrep-6.2.1 {
    Verify that zombies are freed up - lrange version (subrange)
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    # set l {} is for reference counts to drop to 1
    set l [lrange $l[set l {}] $one $end-1]
    list $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 11 208] 1 11 11 1]

test listrep-6.3 {
    Verify that zombies are freed up - lassign version
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    # set l {} is for reference counts to drop to 1
    set l [lassign $l[set l {}] e]
    list $e $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 10 [irange 11 209] 1 11 10 1]

test listrep-6.4 {
    Verify that zombies are freed up - lremove version (front)
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    # set l {} is for reference counts to drop to 1
    set l [lremove $l[set l {}] $zero]
    list $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 11 209] 1 11 10 1]

test listrep-6.4.1 {
    Verify that zombies are freed up - lremove version (back)
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    # set l {} is for reference counts to drop to 1
    set l [lremove $l[set l {}] $end]
    list $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 10 208] 1 10 11 1]

test listrep-6.5 {
    Verify that zombies are freed up - lreplace at front
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    # set l {} is for reference counts to drop to 1
    set l [lreplace $l[set l {}] $zero $one -3 -2 -1]
    list $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [list -3 -2 -1 {*}[irange 12 209]] 1 9 10 1]

test listrep-6.5.1 {
    Verify that zombies are freed up - lreplace at back
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    # set l {} is for reference counts to drop to 1
    set l [lreplace $l[set l {}] $end-1 $end -1 -2 -3]
    list $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [list {*}[irange 10 207] -1 -2 -3] 1 10 9 1]

test listrep-6.6 {
    Verify that zombies are freed up - lappend
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    lappend l 210
    list $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 10 210] 1 10 9 1]

test listrep-6.7 {
    Verify that zombies are freed up - lpop version (front)
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    set e [lpop l $zero]
    list $e $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 10 [irange 11 209] 1 11 10 1]

test listrep-6.7.1 {
    Verify that zombies are freed up - lpop version (back)
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    set e [lpop l]
    list $e $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list 209 [irange 10 208] 1 10 11 1]

test listrep-6.8 {
    Verify that zombies are freed up - lset version
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    lset l $zero -1
    list $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [list -1 {*}[irange 11 209]] 1 10 10 1]

test listrep-6.8.1 {
    Verify that zombies are freed up - lset version (back)
} -constraints testlistrep -body {
    set l [zombieSample 200 10 10]
    set addr [storeAddress $l]
    lset l $end+1 210
    list $l [expr {$addr == [storeAddress $l]}] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list [irange 10 210] 1 10 9 1]


# All done
::tcltest::cleanupTests

return
Changes to tests/lrepeat.test.
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
}
test lrepeat-1.7 {Accept zero repetitions (TIP 323)} {
    -body {
	lrepeat 0 a b c
    }
    -result {}
}
test lrepeat-1.8 {Do not build enormous lists - Bug 2130992} -body {
     lrepeat 0x10000000 a b c d e f g h
} -returnCodes error -match glob -result *

## Okay
test lrepeat-2.1 {normal cases} {
    lrepeat 10 a
} {a a a a a a a a a a}







|







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
}
test lrepeat-1.7 {Accept zero repetitions (TIP 323)} {
    -body {
	lrepeat 0 a b c
    }
    -result {}
}
test lrepeat-1.8 {Do not build enormous lists - Bug 2130992} -constraints knownBug -body {
     lrepeat 0x10000000 a b c d e f g h
} -returnCodes error -match glob -result *

## Okay
test lrepeat-2.1 {normal cases} {
    lrepeat 10 a
} {a a a a a a a a a a}
Added tests/lseq.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
# Commands covered:  lseq
#
# 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.
#
# Copyright © 2003 Simon Geard.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.

if {"::tcltest" ni [namespace children]} {
    package require tcltest 2.5
    namespace import -force ::tcltest::*
}

testConstraint arithSeriesDouble 1
testConstraint arithSeriesShimmer 1
testConstraint arithSeriesShimmerOk 0

## Arg errors
test lseq-1.1 {error cases} -body {
    lseq
} \
    -returnCodes 1 \
    -result {wrong # args: should be "lseq n ??op? n ??by? n??"}


test lseq-1.2 {step magnitude} {
    lseq 10 .. 1 by -2 ;# or this could be an error - or not
} {10 8 6 4 2}

test lseq-1.3 {synergy between int and double} {
    set rl [lseq 25. to 5. by -5]
    set il [lseq 25  to 5  by -5]
    lmap r $rl i $il { if {$r ne "" && $i ne ""} {expr {int($r) == $i}} else {list $r $i} }
} {1 1 1 1 1}

test lseq-1.4 {integer decreasing} {
    lseq 10 .. 1
} {10 9 8 7 6 5 4 3 2 1}

test lseq-1.5 {integer increasing} {
    lseq 1 .. 10
} {1 2 3 4 5 6 7 8 9 10}

test lseq-1.6 {integer decreasing with step} {
    lseq 10 .. 1 by -2
} {10 8 6 4 2}

test lseq-1.7 {real increasing lseq} arithSeriesDouble {
    lseq 5.0 to 15.
} {5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0}

test lseq-1.8 {real increasing lseq with step} arithSeriesDouble {
    lseq 5.0 to 25. by 5
} {5.0 10.0 15.0 20.0 25.0}

test lseq-1.9 {real decreasing with step} arithSeriesDouble {
    lseq 25. to 5. by -5
} {25.0 20.0 15.0 10.0 5.0}

# note, 10 cannot be in such a list, but allowed
test lseq-1.10 {integer lseq with step} {
    lseq 1 to 10 by 2
} {1 3 5 7 9}

test lseq-1.11 {error case: increasing wrong step direction} {
    lseq 1 to 10 by -2
} {}

test lseq-1.12 {decreasing lseq with step} arithSeriesDouble {
    lseq 25. to -25. by -5
} {25.0 20.0 15.0 10.0 5.0 0.0 -5.0 -10.0 -15.0 -20.0 -25.0}

test lseq-1.13 {count operation} {
    -body {
	lseq 5 count 5
    }
    -result {5 6 7 8 9}
}

test lseq-1.14 {count with step} {
    -body {
	lseq 5 count 5 by 2
    }
    -result {5 7 9 11 13}
}

test lseq-1.15 {count with decreasing step} {
    -body {
	lseq 5 count 5 by -2
    }
    -result {5 3 1 -1 -3}
}

test lseq-1.16 {large numbers} {
    -body {
	lseq [expr {int(1e6)}] [expr {int(2e6)}] [expr {int(1e5)}]
    }
    -result {1000000 1100000 1200000 1300000 1400000 1500000 1600000 1700000 1800000 1900000 2000000}
}

test lseq-1.17 {too many arguments} -body {
    lseq 12 to 24 by 2 with feeling
} -returnCodes 1 -result {wrong # args: should be "lseq n ??op? n ??by? n??"}

test lseq-1.18 {too many arguments extra valid keyword} -body {
    lseq 12 to 24 by 2 count
} -returnCodes 1 -result {wrong # args: should be "lseq n ??op? n ??by? n??"}

test lseq-1.19 {too many arguments extra numeric value} -body {
    lseq 12 to 24 by 2 7
} -returnCodes 1 -result {wrong # args: should be "lseq n ??op? n ??by? n??"}

test lseq-1.20 {bug: wrong length computed} {
    lseq 1 to 10 -1
} {}

test lseq-1.21 {n n by n} {
    lseq 66 84 by 3
} {66 69 72 75 78 81 84}

test lseq-1.22 {n n by -n} {
    lseq 84 66 by -3
} {84 81 78 75 72 69 66}

#
# Short-hand use cases
#
test lseq-2.2 {step magnitude} {
    lseq 10 1 2 ;# this is an empty case since step has wrong sign
} {}

test lseq-2.3 {step wrong sign} arithSeriesDouble {
    lseq 25. 5. 5 ;# ditto - empty list
} {}

test lseq-2.4 {integer decreasing} {
    lseq 10 1
} {10 9 8 7 6 5 4 3 2 1}

test lseq-2.5 {integer increasing} {
    lseq 1 10
} {1 2 3 4 5 6 7 8 9 10}

test lseq-2.6 {integer decreasing with step} {
    lseq 10 1 by -2
} {10 8 6 4 2}

test lseq-2.7 {real increasing lseq} arithSeriesDouble {
    lseq 5.0 15.
} {5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0}


test lseq-2.8 {real increasing lseq with step} arithSeriesDouble {
    lseq 5.0 25. 5
} {5.0 10.0 15.0 20.0 25.0}


test lseq-2.9 {real decreasing with step} arithSeriesDouble {
    lseq 25. 5. -5
} {25.0 20.0 15.0 10.0 5.0}

test lseq-2.10 {integer lseq with step} {
    lseq 1 10 2
} {1 3 5 7 9}

test lseq-2.11 {error case: increasing wrong step direction} {
    lseq 1 10 -2
} {}

test lseq-2.12 {decreasing lseq with step} arithSeriesDouble {
    lseq 25. -25. -5
} {25.0 20.0 15.0 10.0 5.0 0.0 -5.0 -10.0 -15.0 -20.0 -25.0}

test lseq-2.13 {count only operation} {
    lseq 5
} {0 1 2 3 4}

test lseq-2.14 {count with step} {
    lseq 5 count 5 2
} {5 7 9 11 13}

test lseq-2.15 {count with decreasing step} {
    lseq 5 count 5 -2
} {5 3 1 -1 -3}

test lseq-2.16 {large numbers} {
    lseq 1e6 2e6 1e5
} {1000000.0 1100000.0 1200000.0 1300000.0 1400000.0 1500000.0 1600000.0 1700000.0 1800000.0 1900000.0 2000000.0}

test lseq-2.17 {large numbers} arithSeriesDouble {
    lseq 1e6 2e6 1e5
} {1000000.0 1100000.0 1200000.0 1300000.0 1400000.0 1500000.0 1600000.0 1700000.0 1800000.0 1900000.0 2000000.0}

# Covered: {10 1 2 } {1 10 2} {1 10 -2} {1 1 1} {1 1 1} {-5 17 3}
# Missing: {- - +} {- - -} {- + -} {+ - -} {- - +} {+ + -}
test lseq-2.18 {signs} {
    list [lseq -10 -1 2] \
	[lseq -10 -1 -1] \
	[lseq -10 1 -3] \
	[lseq 10 -1 -4] \
	[lseq -10 -1 3] \
	[lseq 10 1 -5]

} {{-10 -8 -6 -4 -2} {} {} {10 6 2} {-10 -7 -4 -1} {10 5}}

test lseq-3.1 {experiement} {
    set ans {}
    foreach factor [lseq 2.0 10.0] {
	set start 1
	set end 10
	for {set step 1} {$step < 1e8} {} {
	    set l [lseq $start to $end by $step]
	    if {[llength $l] != 10} {
		lappend ans $factor $step [llength $l] $l
	    }
	    set step [expr {$step * $factor}]
	    set end [expr {$end * $factor}]
	}
    }
    if {$ans eq {}} {
	set ans OK
    }
    set ans
} {OK}

test lseq-3.2 {error case} -body {
    lseq foo
} -returnCodes 1 -result {bad operation "foo": must be .., to, count, or by}

test lseq-3.3 {error case} -body {
    lseq 10 foo
} -returnCodes 1 -result {bad operation "foo": must be .., to, count, or by}

test lseq-3.4 {error case} -body {
    lseq 25 or 6
} -returnCodes 1 -result {bad operation "or": must be .., to, count, or by}

test lseq-3.5 {simple count and step arguments} {
    set s [lseq 25 by 6]
    list $s length=[llength $s]
} {{0 6 12 18 24 30 36 42 48 54 60 66 72 78 84 90 96 102 108 114 120 126 132 138 144} length=25}

test lseq-3.6 {error case} -body {
    lseq 1 7 or 3
} -returnCodes 1  -result {bad operation "or": must be .., to, count, or by}

test lseq-3.7 {lmap lseq} {
    lmap x [lseq 5] { expr {$x * $x} }
} {0 1 4 9 16}

test lseq-3.8 {lrange lseq} {
    set r [lrange [lseq 1 100] 10 20]
    lindex [tcl::unsupported::representation $r] 3
} {arithseries}

test lseq-3.9 {lassign lseq} arithSeriesShimmer {
    set r [lseq 15]
    set r2 [lassign $r a b]
    list [lindex [tcl::unsupported::representation $r] 3] $a $b \
	[lindex [tcl::unsupported::representation $r2] 3]
} {arithseries 0 1 arithseries}

test lseq-3.10 {lsearch lseq must shimmer?} arithSeriesShimmer {
    set r [lseq 15 0]
    set a [lsearch $r 9]
    list [lindex [tcl::unsupported::representation $r] 3] $a
} {arithseries 6}

test lseq-3.11 {lreverse lseq} {
    set r [lseq 15 0]
    set a [lreverse $r]
    join [list \
	      [lindex [tcl::unsupported::representation $r] 3] \
	      $r \
	      [lindex [tcl::unsupported::representation $a] 3] \
	      $a] \n
} {arithseries
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
arithseries
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15}

test lseq-3.12 {in operator} {
    set r [lseq 9]
    set i [expr {7 in $r}]
    set j [expr {10 ni $r}]
    set k [expr {-1 in $r}]
    set l [expr {4 ni $r}]
    list $i $j $k $l [lindex [tcl::unsupported::representation $r] 3]
} {1 1 0 0 arithseries}

test lseq-3.13 {lmap lseq shimmer} arithSeriesShimmer {
    set r [lseq 15]
    set rep-before [lindex [tcl::unsupported::representation $r] 3]
    set m [lmap i $r { expr {$i * 7} }]
    set rep-after [lindex [tcl::unsupported::representation $r] 3]
    set rep-m [lindex [tcl::unsupported::representation $m] 3]
    list $r ${rep-before} ${rep-after} ${rep-m} $m
} {{0 1 2 3 4 5 6 7 8 9 10 11 12 13 14} arithseries arithseries list {0 7 14 21 28 35 42 49 56 63 70 77 84 91 98}}

test lseq-3.14 {array for shimmer} arithSeriesShimmerOk {
    array set testarray {a Test for This great Function}
    set vars [lseq 2]
    set vars-rep [lindex [tcl::unsupported::representation $vars] 3]
    array for $vars testarray {
	lappend keys $0
	lappend vals $1
    }
    # Since hash order is not guaranteed, have to validate content ignoring order
    set valk [lmap k $keys {expr {$k in {a for great}}}]
    set valv [lmap v $vals {expr {$v in {Test This Function}}}]
    set vars-after [lindex [tcl::unsupported::representation $vars] 3]
    list ${vars-rep} $valk $valv ${vars-after}
} {arithseries {1 1 1} {1 1 1} arithseries}

test lseq-3.15 {join for shimmer} arithSeriesShimmer {
    set r [lseq 3]
    set rep-before [lindex [tcl::unsupported::representation $r] 3]
    set str [join $r :]
    set rep-after [lindex [tcl::unsupported::representation $r] 3]
    list ${rep-before} $str ${rep-after}
} {arithseries 0:1:2 arithseries}

test lseq-3.16 {error case} -body {
    lseq 16 to
} -returnCodes 1 -result {missing "to" value.}

test lseq-3.17 {error case} -body {
    lseq 17 to 13 by
} -returnCodes 1 -result {missing "by" value.}

test lseq-3.18 {error case} -body {
    lseq 18 count
} -returnCodes 1 -result {missing "count" value.}

test lseq-3.19 {edge case} -body {
    lseq 1 count 5 by 0
} -result {}
# 1 1 1 1 1

# My thought is that this is likely a user error, since they can always use lrepeat for this.

test lseq-3.20 {edge case} -body {
    lseq 1 to 1 by 0
} -result {}

# hmmm, I guess this is right, in a way, so...

test lseq-3.21 {edge case} {
    lseq 1 to 1 by 1
} {1}

test lseq-3.22 {edge case} {
    lseq 1 1 1
} {1}

test lseq-3.23 {edge case} {
    llength [lseq 1 1 1]
} {1}

test lseq-3.24 {edge case} {
    llength [lseq 1 to 1 1]
} {1}

test lseq-3.25 {edge case} {
    llength [lseq 1 to 1 by 1]
} {1}

test lseq-3.26 {lsort shimmer} arithSeriesShimmer {
    set r [lseq 15 0]
    set rep-before [lindex [tcl::unsupported::representation $r] 3]
    set lexical_sort [lsort $r]
    set rep-after [lindex [tcl::unsupported::representation $r] 3]
    list ${rep-before} $lexical_sort ${rep-after}
} {arithseries {0 1 10 11 12 13 14 15 2 3 4 5 6 7 8 9} arithseries}

test lseq-3.27 {lreplace shimmer} arithSeriesShimmer {
    set r [lseq 15 0]
    set rep-before [lindex [tcl::unsupported::representation $r] 3]
    set lexical_sort [lreplace $r 3 5 A B C]
    set rep-after [lindex [tcl::unsupported::representation $r] 3]
    list ${rep-before} $lexical_sort ${rep-after}
} {arithseries {15 14 13 A B C 9 8 7 6 5 4 3 2 1 0} arithseries}

test lseq-3.28 {lreverse bug in ArithSeries} {} {
    set r [lseq -5 17 3]
    set rr [lreverse $r]
    list $r $rr [string equal $r [lreverse $rr]]
} {{-5 -2 1 4 7 10 13 16} {16 13 10 7 4 1 -2 -5} 1}

test lseq-3.29 {edge case: negative count} {
    lseq -15
} {}

test lseq-3.30 {lreverse with double values} arithSeriesDouble {
    set r [lseq 3.5 18.5 1.5]
    set a [lreverse $r]
    join [list \
	      [lindex [tcl::unsupported::representation $r] 3] \
	      $r \
	      [lindex [tcl::unsupported::representation $a] 3] \
	      $a] \n
} {arithseries
3.5 5.0 6.5 8.0 9.5 11.0 12.5 14.0 15.5 17.0 18.5
arithseries
18.5 17.0 15.5 14.0 12.5 11.0 9.5 8.0 6.5 5.0 3.5}

test lseq-3.31 {lreverse inplace with doubles} arithSeriesDouble {
    lreverse [lseq 1.1 29.9 0.3]
} {29.9 29.599999999999998 29.299999999999997 29.0 28.7 28.4 28.099999999999998 27.799999999999997 27.5 27.2 26.9 26.599999999999998 26.299999999999997 26.0 25.7 25.4 25.099999999999998 24.799999999999997 24.5 24.2 23.9 23.599999999999998 23.299999999999997 23.0 22.7 22.4 22.099999999999998 21.799999999999997 21.5 21.2 20.9 20.6 20.299999999999997 20.0 19.7 19.4 19.1 18.799999999999997 18.5 18.2 17.9 17.6 17.299999999999997 17.0 16.7 16.4 16.1 15.799999999999999 15.5 15.2 14.899999999999999 14.6 14.299999999999999 14.0 13.7 13.399999999999999 13.099999999999998 12.8 12.5 12.2 11.899999999999999 11.599999999999998 11.3 11.0 10.7 10.399999999999999 10.099999999999998 9.8 9.5 9.2 8.899999999999999 8.599999999999998 8.3 8.0 7.699999999999999 7.399999999999999 7.099999999999998 6.800000000000001 6.5 6.199999999999999 5.899999999999999 5.599999999999998 5.300000000000001 5.0 4.699999999999999 4.399999999999999 4.099999999999998 3.8000000000000007 3.5 3.1999999999999993 2.8999999999999986 2.599999999999998 2.3000000000000007 2.0 1.6999999999999993 1.3999999999999986 1.1000000000000014}

test lseq-4.1 {end expressions} {
    set start 7
    lseq $start $start+11
} {7 8 9 10 11 12 13 14 15 16 17 18}

test lseq-4.2 {start expressions} {
    set base [clock seconds]
    set tl [lseq $base-60 $base 10]
    lmap t $tl {expr {$t - $base + 60}}
} {0 10 20 30 40 50 60}

##	lseq 1 to 10 by -2
##	# -> lseq: invalid step = -2 with a = 1 and b = 10

test lseq-4.3 {TIP examples} {
    set examples {# Examples from TIP-629
	# --- Begin ---
	lseq 10 .. 1
	# -> 10 9 8 7 6 5 4 3 2 1
	lseq 1 .. 10
	# -> 1 2 3 4 5 6 7 8 9 10
	lseq 10 .. 1 by 2
	# ->
	lseq 10 .. 1 by -2
	# -> 10 8 6 4 2
	lseq 5.0 to 15.
	# -> 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0
	lseq 5.0 to 25. by 5
	# -> 5.0 10.0 15.0 20.0 25.0
	lseq 25. to 5. by 5
	# ->
	lseq 25. to 5. by -5
	# -> 25.0 20.0 15.0 10.0 5.0
	lseq 1 to 10 by 2
	# -> 1 3 5 7 9
	lseq 25. to -25. by -5
	# -> 25.0 20.0 15.0 10.0 5.0 0.0 -5.0 -10.0 -15.0 -20.0 -25.0
	lseq 5 5
	# -> 5
	lseq 5 5 2
	# -> 5
	lseq 5 5 -2
	# -> 5
    }

    foreach {cmd expect} [split $examples \n] {
	if {[string trim $cmd] ne ""} {
	    set cmd [string trimleft $cmd]
	    if {[string match {\#*} $cmd]} continue
	    set status [catch $cmd ans]
	    lappend res $ans
	    if {[regexp {\# -> (.*)$} $expect -> expected]} {
		if {$expected ne $ans} {
		    lappend res [list Mismatch: $cmd -> $ans ne $expected]
		}
	    }
	}
    }
    set res
} {{10 9 8 7 6 5 4 3 2 1} {1 2 3 4 5 6 7 8 9 10} {} {10 8 6 4 2} {5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0} {5.0 10.0 15.0 20.0 25.0} {} {25.0 20.0 15.0 10.0 5.0} {1 3 5 7 9} {25.0 20.0 15.0 10.0 5.0 0.0 -5.0 -10.0 -15.0 -20.0 -25.0} 5 5 5}


# cleanup
::tcltest::cleanupTests
return

# Local Variables:
# mode: tcl
# End:
Changes to tests/proc.test.
407
408
409
410
411
412
413







414
415
416
417
418
419
420

test proc-7.5 {[631b4c45df] Crash in argument processing} {
    binary scan A c val
    proc foo [list  [list from $val]] {}
    rename foo {}
    unset -nocomplain val
} {}









# cleanup
catch {rename p ""}
catch {rename t ""}
::tcltest::cleanupTests
return







>
>
>
>
>
>
>







407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427

test proc-7.5 {[631b4c45df] Crash in argument processing} {
    binary scan A c val
    proc foo [list  [list from $val]] {}
    rename foo {}
    unset -nocomplain val
} {}

test proc-7.6 {[51d5f22997] Crash in argument processing} -cleanup {
    rename foo {}
} -body {
    proc foo {{x {}} {y {}} args} {}
    foo
} -result {}


# cleanup
catch {rename p ""}
catch {rename t ""}
::tcltest::cleanupTests
return
Changes to tests/result.test.
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
    set errorCode
} {{a b} c}

test result-6.0 {Bug 1209759} -constraints testreturn -body {
    # Might panic if bug is not fixed.
    proc foo {} {testreturn}
    foo
} -returnCodes ok  -result {}
test result-6.1 {Bug 1209759} -constraints testreturn -body {
    # Might panic if bug is not fixed.
    proc foo {} {catch {return -level 2}; testreturn}
    foo
} -cleanup {
    rename foo {}
} -returnCodes ok -result {}
test result-6.2 {Bug 1649062} -setup {
    proc foo {} {
        if {[catch {
            return -code error -errorinfo custom -errorcode CUSTOM foo
        } err]} {
            return [list $err $::errorCode $::errorInfo]
        }







|






|







105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
    set errorCode
} {{a b} c}

test result-6.0 {Bug 1209759} -constraints testreturn -body {
    # Might panic if bug is not fixed.
    proc foo {} {testreturn}
    foo
}  -result {}
test result-6.1 {Bug 1209759} -constraints testreturn -body {
    # Might panic if bug is not fixed.
    proc foo {} {catch {return -level 2}; testreturn}
    foo
} -cleanup {
    rename foo {}
} -result {}
test result-6.2 {Bug 1649062} -setup {
    proc foo {} {
        if {[catch {
            return -code error -errorinfo custom -errorcode CUSTOM foo
        } err]} {
            return [list $err $::errorCode $::errorInfo]
        }
Changes to tests/safe.test.
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
    safe::interpDelete $i
} -result {load of library for prefix Safepfx1 failed: can't use library in a safe interpreter: no Safepfx1_SafeInit procedure}
test safe-10.1.1 {testing statics loading} -constraints tcl::test -setup {
    set i [safe::interpCreate]
} -body {
    catch {interp eval $i {load {} Safepfx1}} m o
    dict get $o -errorinfo
} -returnCodes ok -cleanup {
    unset -nocomplain m o
    safe::interpDelete $i
} -result {load of library for prefix Safepfx1 failed: can't use library in a safe interpreter: no Safepfx1_SafeInit procedure
    invoked from within
"load {} Safepfx1"
    invoked from within
"interp eval $i {load {} Safepfx1}"}







|







1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
    safe::interpDelete $i
} -result {load of library for prefix Safepfx1 failed: can't use library in a safe interpreter: no Safepfx1_SafeInit procedure}
test safe-10.1.1 {testing statics loading} -constraints tcl::test -setup {
    set i [safe::interpCreate]
} -body {
    catch {interp eval $i {load {} Safepfx1}} m o
    dict get $o -errorinfo
} -cleanup {
    unset -nocomplain m o
    safe::interpDelete $i
} -result {load of library for prefix Safepfx1 failed: can't use library in a safe interpreter: no Safepfx1_SafeInit procedure
    invoked from within
"load {} Safepfx1"
    invoked from within
"interp eval $i {load {} Safepfx1}"}
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
} -returnCodes error -cleanup {
    safe::interpDelete $i
} -result {load of library for prefix Safepfx1 failed: can't use library in a safe interpreter: no Safepfx1_SafeInit procedure}
test safe-10.4.1 {testing nested statics loading / -nestedloadok} -constraints tcl::test -body {
    set i [safe::interpCreate -nestedloadok]
    catch {interp eval $i {interp create x; load {} Safepfx1 x}} m o
    dict get $o -errorinfo
} -returnCodes ok -cleanup {
    unset -nocomplain m o
    safe::interpDelete $i
} -result {load of library for prefix Safepfx1 failed: can't use library in a safe interpreter: no Safepfx1_SafeInit procedure
    invoked from within
"load {} Safepfx1 x"
    invoked from within
"interp eval $i {interp create x; load {} Safepfx1 x}"}







|







1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
} -returnCodes error -cleanup {
    safe::interpDelete $i
} -result {load of library for prefix Safepfx1 failed: can't use library in a safe interpreter: no Safepfx1_SafeInit procedure}
test safe-10.4.1 {testing nested statics loading / -nestedloadok} -constraints tcl::test -body {
    set i [safe::interpCreate -nestedloadok]
    catch {interp eval $i {interp create x; load {} Safepfx1 x}} m o
    dict get $o -errorinfo
} -cleanup {
    unset -nocomplain m o
    safe::interpDelete $i
} -result {load of library for prefix Safepfx1 failed: can't use library in a safe interpreter: no Safepfx1_SafeInit procedure
    invoked from within
"load {} Safepfx1 x"
    invoked from within
"interp eval $i {interp create x; load {} Safepfx1 x}"}
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
    safe::interpDelete $i
} -result {wrong # args: should be "encoding convertfrom ?-nocomplain? ?-failindex var? ?encoding? data"}
test safe-11.7.1 {testing safe encoding} -setup {
    set i [safe::interpCreate]
} -body {
    catch {interp eval $i encoding convertfrom} m o
    dict get $o -errorinfo
} -returnCodes ok -match glob -cleanup {
    unset -nocomplain m o
    safe::interpDelete $i
} -result {wrong # args: should be "encoding convertfrom ?-nocomplain? ?-failindex var? ?encoding? data"
    while executing
"encoding convertfrom"
    invoked from within
"encoding convertfrom"







|







1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
    safe::interpDelete $i
} -result {wrong # args: should be "encoding convertfrom ?-nocomplain? ?-failindex var? ?encoding? data"}
test safe-11.7.1 {testing safe encoding} -setup {
    set i [safe::interpCreate]
} -body {
    catch {interp eval $i encoding convertfrom} m o
    dict get $o -errorinfo
} -match glob -cleanup {
    unset -nocomplain m o
    safe::interpDelete $i
} -result {wrong # args: should be "encoding convertfrom ?-nocomplain? ?-failindex var? ?encoding? data"
    while executing
"encoding convertfrom"
    invoked from within
"encoding convertfrom"
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
    safe::interpDelete $i
} -result {wrong # args: should be "encoding convertto ?-nocomplain? ?-failindex var? ?encoding? data"}
test safe-11.8.1 {testing safe encoding} -setup {
    set i [safe::interpCreate]
} -body {
    catch {interp eval $i encoding convertto} m o
    dict get $o -errorinfo
} -returnCodes ok -match glob -cleanup {
    unset -nocomplain m o
    safe::interpDelete $i
} -result {wrong # args: should be "encoding convertto ?-nocomplain? ?-failindex var? ?encoding? data"
    while executing
"encoding convertto"
    invoked from within
"encoding convertto"







|







1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
    safe::interpDelete $i
} -result {wrong # args: should be "encoding convertto ?-nocomplain? ?-failindex var? ?encoding? data"}
test safe-11.8.1 {testing safe encoding} -setup {
    set i [safe::interpCreate]
} -body {
    catch {interp eval $i encoding convertto} m o
    dict get $o -errorinfo
} -match glob -cleanup {
    unset -nocomplain m o
    safe::interpDelete $i
} -result {wrong # args: should be "encoding convertto ?-nocomplain? ?-failindex var? ?encoding? data"
    while executing
"encoding convertto"
    invoked from within
"encoding convertto"
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
	set d [format %c 126]
	file join {$p(:0:)} $d
    }
} -cleanup {
    safe::interpDelete $i
    set env(HOME) $savedHOME
    unset savedHOME
} -result {~}
test safe-16.6 {Bug 3529949: defang ~ in paths used by AliasGlob (2)} -setup {
    set savedHOME $env(HOME)
    set env(HOME) /foo/bar
    set i [safe::interpCreate]
} -body {
    $i eval {
	set d [format %c 126]
	file join {$p(:0:)/foo/bar} $d
    }
} -cleanup {
    safe::interpDelete $i
    set env(HOME) $savedHOME
    unset savedHOME
} -result {~}
test safe-16.7 {Bug 3529949: defang ~user in paths used by AliasGlob (1)} -setup {
    set i [safe::interpCreate]
    set user $tcl_platform(user)
} -body {
    string map [list $user USER] [$i eval [list file join {$p(:0:)} ~$user]]
} -cleanup {
    safe::interpDelete $i
    unset user
} -result {~USER}
test safe-16.8 {Bug 3529949: defang ~user in paths used by AliasGlob (2)} -setup {
    set i [safe::interpCreate]
    set user $tcl_platform(user)
} -body {
    string map [list $user USER] [$i eval [list file join {$p(:0:)/foo/bar} ~$user]]
} -cleanup {
    safe::interpDelete $i
    unset user
} -result {~USER}

# cleanup
set ::auto_path $SaveAutoPath
unset SaveAutoPath TestsDir PathMapp
unset -nocomplain path
rename mapList {}
rename mapAndSortList {}
::tcltest::cleanupTests
return

# Local Variables:
# mode: tcl
# End:







|













|








|








|













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
	set d [format %c 126]
	file join {$p(:0:)} $d
    }
} -cleanup {
    safe::interpDelete $i
    set env(HOME) $savedHOME
    unset savedHOME
} -result {$p(:0:)/~}
test safe-16.6 {Bug 3529949: defang ~ in paths used by AliasGlob (2)} -setup {
    set savedHOME $env(HOME)
    set env(HOME) /foo/bar
    set i [safe::interpCreate]
} -body {
    $i eval {
	set d [format %c 126]
	file join {$p(:0:)/foo/bar} $d
    }
} -cleanup {
    safe::interpDelete $i
    set env(HOME) $savedHOME
    unset savedHOME
} -result {$p(:0:)/foo/bar/~}
test safe-16.7 {Bug 3529949: defang ~user in paths used by AliasGlob (1)} -setup {
    set i [safe::interpCreate]
    set user $tcl_platform(user)
} -body {
    string map [list $user USER] [$i eval [list file join {$p(:0:)} ~$user]]
} -cleanup {
    safe::interpDelete $i
    unset user
} -result {$p(:0:)/~USER}
test safe-16.8 {Bug 3529949: defang ~user in paths used by AliasGlob (2)} -setup {
    set i [safe::interpCreate]
    set user $tcl_platform(user)
} -body {
    string map [list $user USER] [$i eval [list file join {$p(:0:)/foo/bar} ~$user]]
} -cleanup {
    safe::interpDelete $i
    unset user
} -result {$p(:0:)/foo/bar/~USER}

# cleanup
set ::auto_path $SaveAutoPath
unset SaveAutoPath TestsDir PathMapp
unset -nocomplain path
rename mapList {}
rename mapAndSortList {}
::tcltest::cleanupTests
return

# Local Variables:
# mode: tcl
# End:
Changes to tests/switch.test.
741
742
743
744
745
746
747







748
749
750
751
752
753
754
	switch -regexp -- {} {
	    {[012]} {return yes}
	    .+ {return yes2}
	    default {return no}
	}
    }}
} no








test switch-15.1 {coroutine safety of non-bytecoded switch} {*}{
    -body {
	proc coro {} {
	    switch -glob a {
		a {yield ok1}
	    }







>
>
>
>
>
>
>







741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
	switch -regexp -- {} {
	    {[012]} {return yes}
	    .+ {return yes2}
	    default {return no}
	}
    }}
} no
test switch-14.17 {switch -regexp bug [c0bc269178]} {
    set result {}
    switch -regexp -matchvar m -indexvar i ac {
	{(a)(b)?(c)} {set result $m}
    }
    set result
} {ac a {} c}

test switch-15.1 {coroutine safety of non-bytecoded switch} {*}{
    -body {
	proc coro {} {
	    switch -glob a {
		a {yield ok1}
	    }
Changes to tests/winFile.test.
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
if {[testConstraint testvolumetype]} {
    testConstraint notNTFS [expr {[testvolumetype] eq "NTFS"}]
}
testConstraint notWine [expr {![info exists ::env(CI_USING_WINE)]}]

test winFile-1.1 {TclpGetUserHome} -constraints {win} -body {
    glob ~nosuchuser
} -returnCodes error -result {user "nosuchuser" doesn't exist}
test winFile-1.2 {TclpGetUserHome} -constraints {win nonPortable} -body {
    # The administrator account should always exist.
    glob ~administrator
} -match glob -result *
test winFile-1.4 {TclpGetUserHome} {win nonPortable} {
    catch {glob ~stanton@workgroup}
} {0}







|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
if {[testConstraint testvolumetype]} {
    testConstraint notNTFS [expr {[testvolumetype] eq "NTFS"}]
}
testConstraint notWine [expr {![info exists ::env(CI_USING_WINE)]}]

test winFile-1.1 {TclpGetUserHome} -constraints {win} -body {
    glob ~nosuchuser
} -returnCodes error -result {no files matched glob pattern "~nosuchuser"}
test winFile-1.2 {TclpGetUserHome} -constraints {win nonPortable} -body {
    # The administrator account should always exist.
    glob ~administrator
} -match glob -result *
test winFile-1.4 {TclpGetUserHome} {win nonPortable} {
    catch {glob ~stanton@workgroup}
} {0}
Changes to tools/tsdPerf.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <tcl.h>

extern DLLEXPORT Tcl_LibraryInitProc Tsdperf_Init;

static Tcl_ThreadDataKey key;

typedef struct {
    Tcl_WideInt value;
} TsdPerf;


static int
tsdPerfSetObjCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) {
    TsdPerf *perf = Tcl_GetThreadData(&key, sizeof(TsdPerf));
    Tcl_WideInt i;

    if (2 != objc) {
	Tcl_WrongNumArgs(interp, 1, objv, "value");
	return TCL_ERROR;
    }

    if (TCL_OK != Tcl_GetWideIntFromObj(interp, objv[1], &i)) {
	return TCL_ERROR;
    }

    perf->value = i;

    return TCL_OK;
}

static int
tsdPerfGetObjCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) {
    TsdPerf *perf = Tcl_GetThreadData(&key, sizeof(TsdPerf));


    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(perf->value));

    return TCL_OK;
}












|


















|







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
#include <tcl.h>

extern DLLEXPORT Tcl_LibraryInitProc Tsdperf_Init;

static Tcl_ThreadDataKey key;

typedef struct {
    Tcl_WideInt value;
} TsdPerf;


static int
tsdPerfSetObjCmd(void *cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) {
    TsdPerf *perf = Tcl_GetThreadData(&key, sizeof(TsdPerf));
    Tcl_WideInt i;

    if (2 != objc) {
	Tcl_WrongNumArgs(interp, 1, objv, "value");
	return TCL_ERROR;
    }

    if (TCL_OK != Tcl_GetWideIntFromObj(interp, objv[1], &i)) {
	return TCL_ERROR;
    }

    perf->value = i;

    return TCL_OK;
}

static int
tsdPerfGetObjCmd(void *cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) {
    TsdPerf *perf = Tcl_GetThreadData(&key, sizeof(TsdPerf));


    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(perf->value));

    return TCL_OK;
}
Changes to unix/Makefile.in.
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
TCLTEST_OBJS = tclTestInit.o tclTest.o tclTestObj.o tclTestProcBodyObj.o \
	tclThreadTest.o tclUnixTest.o

XTTEST_OBJS = xtTestInit.o tclTest.o tclTestObj.o tclTestProcBodyObj.o \
	tclThreadTest.o tclUnixTest.o tclXtNotify.o tclXtTest.o

GENERIC_OBJS = regcomp.o regexec.o regfree.o regerror.o tclAlloc.o \
	tclAssembly.o tclAsync.o tclBasic.o tclBinary.o tclCkalloc.o \
	tclClock.o tclCmdAH.o tclCmdIL.o tclCmdMZ.o \
	tclCompCmds.o tclCompCmdsGR.o tclCompCmdsSZ.o tclCompExpr.o \
	tclCompile.o tclConfig.o tclDate.o tclDictObj.o tclDisassemble.o \
	tclEncoding.o tclEnsemble.o \
	tclEnv.o tclEvent.o tclExecute.o tclFCmd.o tclFileName.o tclGet.o \
	tclHash.o tclHistory.o tclIndexObj.o tclInterp.o tclIO.o tclIOCmd.o \
	tclIORChan.o tclIORTrans.o tclIOGT.o tclIOSock.o tclIOUtil.o \
	tclLink.o tclListObj.o \







|
|







295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
TCLTEST_OBJS = tclTestInit.o tclTest.o tclTestObj.o tclTestProcBodyObj.o \
	tclThreadTest.o tclUnixTest.o

XTTEST_OBJS = xtTestInit.o tclTest.o tclTestObj.o tclTestProcBodyObj.o \
	tclThreadTest.o tclUnixTest.o tclXtNotify.o tclXtTest.o

GENERIC_OBJS = regcomp.o regexec.o regfree.o regerror.o tclAlloc.o \
	tclArithSeries.o tclAssembly.o tclAsync.o tclBasic.o tclBinary.o \
	tclCkalloc.o tclClock.o tclCmdAH.o tclCmdIL.o tclCmdMZ.o \
	tclCompCmds.o tclCompCmdsGR.o tclCompCmdsSZ.o tclCompExpr.o \
	tclCompile.o tclConfig.o tclDate.o tclDictObj.o tclDisassemble.o \
	tclEncoding.o tclEnsemble.o \
	tclEnv.o tclEvent.o tclExecute.o tclFCmd.o tclFileName.o tclGet.o \
	tclHash.o tclHistory.o tclIndexObj.o tclInterp.o tclIO.o tclIOCmd.o \
	tclIORChan.o tclIORTrans.o tclIOGT.o tclIOSock.o tclIOUtil.o \
	tclLink.o tclListObj.o \
391
392
393
394
395
396
397
398

399
400
401
402
403
404
405

406
407
408
409
410
411
412
	$(GENERIC_DIR)/tclOO.h \
	$(GENERIC_DIR)/tclOODecls.h \
	$(GENERIC_DIR)/tclOOInt.h \
	$(GENERIC_DIR)/tclOOIntDecls.h \
	$(GENERIC_DIR)/tclPatch.h \
	$(GENERIC_DIR)/tclPlatDecls.h \
	$(GENERIC_DIR)/tclPort.h \
	$(GENERIC_DIR)/tclRegexp.h


GENERIC_SRCS = \
	$(GENERIC_DIR)/regcomp.c \
	$(GENERIC_DIR)/regexec.c \
	$(GENERIC_DIR)/regfree.c \
	$(GENERIC_DIR)/regerror.c \
	$(GENERIC_DIR)/tclAlloc.c \

	$(GENERIC_DIR)/tclAssembly.c \
	$(GENERIC_DIR)/tclAsync.c \
	$(GENERIC_DIR)/tclBasic.c \
	$(GENERIC_DIR)/tclBinary.c \
	$(GENERIC_DIR)/tclCkalloc.c \
	$(GENERIC_DIR)/tclClock.c \
	$(GENERIC_DIR)/tclCmdAH.c \







|
>







>







391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
	$(GENERIC_DIR)/tclOO.h \
	$(GENERIC_DIR)/tclOODecls.h \
	$(GENERIC_DIR)/tclOOInt.h \
	$(GENERIC_DIR)/tclOOIntDecls.h \
	$(GENERIC_DIR)/tclPatch.h \
	$(GENERIC_DIR)/tclPlatDecls.h \
	$(GENERIC_DIR)/tclPort.h \
	$(GENERIC_DIR)/tclRegexp.h \
	$(GENERIC_DIR)/tclArithSeries.h

GENERIC_SRCS = \
	$(GENERIC_DIR)/regcomp.c \
	$(GENERIC_DIR)/regexec.c \
	$(GENERIC_DIR)/regfree.c \
	$(GENERIC_DIR)/regerror.c \
	$(GENERIC_DIR)/tclAlloc.c \
	$(GENERIC_DIR)/tclArithSeries.c \
	$(GENERIC_DIR)/tclAssembly.c \
	$(GENERIC_DIR)/tclAsync.c \
	$(GENERIC_DIR)/tclBasic.c \
	$(GENERIC_DIR)/tclBinary.c \
	$(GENERIC_DIR)/tclCkalloc.c \
	$(GENERIC_DIR)/tclClock.c \
	$(GENERIC_DIR)/tclCmdAH.c \
1249
1250
1251
1252
1253
1254
1255



1256
1257
1258
1259
1260
1261
1262
	$(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/regerror.c

tclAppInit.o: $(UNIX_DIR)/tclAppInit.c
	$(CC) -c $(APP_CC_SWITCHES) $(UNIX_DIR)/tclAppInit.c

tclAlloc.o: $(GENERIC_DIR)/tclAlloc.c
	$(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclAlloc.c




tclAssembly.o: $(GENERIC_DIR)/tclAssembly.c $(COMPILEHDR)
	$(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclAssembly.c

tclAsync.o: $(GENERIC_DIR)/tclAsync.c
	$(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclAsync.c








>
>
>







1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
	$(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/regerror.c

tclAppInit.o: $(UNIX_DIR)/tclAppInit.c
	$(CC) -c $(APP_CC_SWITCHES) $(UNIX_DIR)/tclAppInit.c

tclAlloc.o: $(GENERIC_DIR)/tclAlloc.c
	$(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclAlloc.c

tclArithSeries.o: $(GENERIC_DIR)/tclArithSeries.c $(COMPILEHDR)
	$(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclArithSeries.c

tclAssembly.o: $(GENERIC_DIR)/tclAssembly.c $(COMPILEHDR)
	$(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclAssembly.c

tclAsync.o: $(GENERIC_DIR)/tclAsync.c
	$(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclAsync.c

Changes to unix/tclEpollNotfy.c.
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
    int mask;			/* Mask of desired events: TCL_READABLE,
				 * etc. */
    int readyMask;		/* Mask of events that have been seen since
				 * the last time file handlers were invoked
				 * for this file. */
    Tcl_FileProc *proc;		/* Function to call, in the style of
				 * Tcl_CreateFileHandler. */
    ClientData clientData;	/* Argument to pass to proc. */
    struct FileHandler *nextPtr;/* Next in list of all files we care about. */
    LIST_ENTRY(FileHandler) readyNode;
				/* Next/previous in list of FileHandlers asso-
				 * ciated with regular files (S_IFREG) that are
				 * ready for I/O. */
    struct PlatformEventData *pedPtr;
				/* Pointer to PlatformEventData associating this







|







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
    int mask;			/* Mask of desired events: TCL_READABLE,
				 * etc. */
    int readyMask;		/* Mask of events that have been seen since
				 * the last time file handlers were invoked
				 * for this file. */
    Tcl_FileProc *proc;		/* Function to call, in the style of
				 * Tcl_CreateFileHandler. */
    void *clientData;	/* Argument to pass to proc. */
    struct FileHandler *nextPtr;/* Next in list of all files we care about. */
    LIST_ENTRY(FileHandler) readyNode;
				/* Next/previous in list of FileHandlers asso-
				 * ciated with regular files (S_IFREG) that are
				 * ready for I/O. */
    struct PlatformEventData *pedPtr;
				/* Pointer to PlatformEventData associating this
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
 * Side effects:
 *	If no initNotifierProc notifier hook exists, PlatformEventsInit is
 *	called.
 *
 *----------------------------------------------------------------------
 */

ClientData
TclpInitNotifier(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    PlatformEventsInit();
    return tsdPtr;
}







|







146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
 * Side effects:
 *	If no initNotifierProc notifier hook exists, PlatformEventsInit is
 *	called.
 *
 *----------------------------------------------------------------------
 */

void *
TclpInitNotifier(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    PlatformEventsInit();
    return tsdPtr;
}
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
 *	tsdPtr->notifierMutex is destroyed.
 *
 *----------------------------------------------------------------------
 */

void
TclpFinalizeNotifier(
    TCL_UNUSED(ClientData))
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    pthread_mutex_lock(&tsdPtr->notifierMutex);
#ifdef HAVE_EVENTFD
    if (tsdPtr->triggerEventFd) {
	close(tsdPtr->triggerEventFd);







|







271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
 *	tsdPtr->notifierMutex is destroyed.
 *
 *----------------------------------------------------------------------
 */

void
TclpFinalizeNotifier(
    TCL_UNUSED(void *))
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    pthread_mutex_lock(&tsdPtr->notifierMutex);
#ifdef HAVE_EVENTFD
    if (tsdPtr->triggerEventFd) {
	close(tsdPtr->triggerEventFd);
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
    int fd,			/* Handle of stream to watch. */
    int mask,			/* OR'ed combination of TCL_READABLE,
				 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
				 * conditions under which proc should be
				 * called. */
    Tcl_FileProc *proc,		/* Function to call for each selected
				 * event. */
    ClientData clientData)	/* Arbitrary data to pass to proc. */
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
    FileHandler *filePtr = LookUpFileHandler(tsdPtr, fd, NULL);
    int isNew = (filePtr == NULL);

    if (isNew) {
	filePtr = (FileHandler *) Tcl_Alloc(sizeof(FileHandler));







|







509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
    int fd,			/* Handle of stream to watch. */
    int mask,			/* OR'ed combination of TCL_READABLE,
				 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
				 * conditions under which proc should be
				 * called. */
    Tcl_FileProc *proc,		/* Function to call for each selected
				 * event. */
    void *clientData)	/* Arbitrary data to pass to proc. */
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
    FileHandler *filePtr = LookUpFileHandler(tsdPtr, fd, NULL);
    int isNew = (filePtr == NULL);

    if (isNew) {
	filePtr = (FileHandler *) Tcl_Alloc(sizeof(FileHandler));
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
 *----------------------------------------------------------------------
 */

int
TclAsyncNotifier(
    int sigNumber,		/* Signal number. */
    Tcl_ThreadId threadId,	/* Target thread. */
    ClientData clientData,	/* Notifier data. */
    int *flagPtr,		/* Flag to mark. */
    int value)			/* Value of mark. */
{
#if TCL_THREADS
    /*
     * WARNING:
     * This code most likely runs in a signal handler. Thus,







|







787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
 *----------------------------------------------------------------------
 */

int
TclAsyncNotifier(
    int sigNumber,		/* Signal number. */
    Tcl_ThreadId threadId,	/* Target thread. */
    void *clientData,	/* Notifier data. */
    int *flagPtr,		/* Flag to mark. */
    int value)			/* Value of mark. */
{
#if TCL_THREADS
    /*
     * WARNING:
     * This code most likely runs in a signal handler. Thus,
Changes to unix/tclKqueueNotfy.c.
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
    int mask;			/* Mask of desired events: TCL_READABLE,
				 * etc. */
    int readyMask;		/* Mask of events that have been seen since
				 * the last time file handlers were invoked
				 * for this file. */
    Tcl_FileProc *proc;		/* Function to call, in the style of
				 * Tcl_CreateFileHandler. */
    ClientData clientData;	/* Argument to pass to proc. */
    struct FileHandler *nextPtr;/* Next in list of all files we care about. */
    LIST_ENTRY(FileHandler) readyNode;
				/* Next/previous in list of FileHandlers asso-
				 * ciated with regular files (S_IFREG) that are
				 * ready for I/O. */
    struct PlatformEventData *pedPtr;
				/* Pointer to PlatformEventData associating this







|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
    int mask;			/* Mask of desired events: TCL_READABLE,
				 * etc. */
    int readyMask;		/* Mask of events that have been seen since
				 * the last time file handlers were invoked
				 * for this file. */
    Tcl_FileProc *proc;		/* Function to call, in the style of
				 * Tcl_CreateFileHandler. */
    void *clientData;	/* Argument to pass to proc. */
    struct FileHandler *nextPtr;/* Next in list of all files we care about. */
    LIST_ENTRY(FileHandler) readyNode;
				/* Next/previous in list of FileHandlers asso-
				 * ciated with regular files (S_IFREG) that are
				 * ready for I/O. */
    struct PlatformEventData *pedPtr;
				/* Pointer to PlatformEventData associating this
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
 *	tsdPtr->notifierMutex is destroyed.
 *
 *----------------------------------------------------------------------
 */

void
TclpFinalizeNotifier(
    TCL_UNUSED(ClientData))
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    pthread_mutex_lock(&tsdPtr->notifierMutex);
    if (tsdPtr->triggerPipe[0]) {
	close(tsdPtr->triggerPipe[0]);
	tsdPtr->triggerPipe[0] = -1;







|







270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
 *	tsdPtr->notifierMutex is destroyed.
 *
 *----------------------------------------------------------------------
 */

void
TclpFinalizeNotifier(
    TCL_UNUSED(void *))
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    pthread_mutex_lock(&tsdPtr->notifierMutex);
    if (tsdPtr->triggerPipe[0]) {
	close(tsdPtr->triggerPipe[0]);
	tsdPtr->triggerPipe[0] = -1;
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
 *	  fd(2), registering interest for TCL_READABLE on it via Platform-
 *	  EventsControl().
 *	- readyEvents and maxReadyEvents are initialised with 512 kevents.
 *
 *----------------------------------------------------------------------
 */

ClientData
TclpInitNotifier(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
    int i, fdFl;
    FileHandler *filePtr;

    errno = pthread_mutex_init(&tsdPtr->notifierMutex, NULL);







|







326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
 *	  fd(2), registering interest for TCL_READABLE on it via Platform-
 *	  EventsControl().
 *	- readyEvents and maxReadyEvents are initialised with 512 kevents.
 *
 *----------------------------------------------------------------------
 */

void *
TclpInitNotifier(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
    int i, fdFl;
    FileHandler *filePtr;

    errno = pthread_mutex_init(&tsdPtr->notifierMutex, NULL);
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
    int fd,			/* Handle of stream to watch. */
    int mask,			/* OR'ed combination of TCL_READABLE,
				 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
				 * conditions under which proc should be
				 * called. */
    Tcl_FileProc *proc,		/* Function to call for each selected
				 * event. */
    ClientData clientData)	/* Arbitrary data to pass to proc. */
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
    FileHandler *filePtr = LookUpFileHandler(tsdPtr, fd, NULL);
    int isNew = (filePtr == NULL);

    if (isNew) {
	filePtr = (FileHandler *) Tcl_Alloc(sizeof(FileHandler));







|







514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
    int fd,			/* Handle of stream to watch. */
    int mask,			/* OR'ed combination of TCL_READABLE,
				 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
				 * conditions under which proc should be
				 * called. */
    Tcl_FileProc *proc,		/* Function to call for each selected
				 * event. */
    void *clientData)	/* Arbitrary data to pass to proc. */
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
    FileHandler *filePtr = LookUpFileHandler(tsdPtr, fd, NULL);
    int isNew = (filePtr == NULL);

    if (isNew) {
	filePtr = (FileHandler *) Tcl_Alloc(sizeof(FileHandler));
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
 *----------------------------------------------------------------------
 */

int
TclAsyncNotifier(
    int sigNumber,		/* Signal number. */
    Tcl_ThreadId threadId,	/* Target thread. */
    ClientData clientData,	/* Notifier data. */
    int *flagPtr,		/* Flag to mark. */
    int value)			/* Value of mark. */
{
#if TCL_THREADS
    /*
     * WARNING:
     * This code most likely runs in a signal handler. Thus,







|







783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
 *----------------------------------------------------------------------
 */

int
TclAsyncNotifier(
    int sigNumber,		/* Signal number. */
    Tcl_ThreadId threadId,	/* Target thread. */
    void *clientData,	/* Notifier data. */
    int *flagPtr,		/* Flag to mark. */
    int value)			/* Value of mark. */
{
#if TCL_THREADS
    /*
     * WARNING:
     * This code most likely runs in a signal handler. Thus,
Changes to unix/tclSelectNotfy.c.
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
 *----------------------------------------------------------------------
 */

int
TclAsyncNotifier(
    int sigNumber,		/* Signal number. */
    TCL_UNUSED(Tcl_ThreadId),	/* Target thread. */
    TCL_UNUSED(ClientData),	/* Notifier data. */
    int *flagPtr,		/* Flag to mark. */
    int value)			/* Value of mark. */
{
#if TCL_THREADS
    /*
     * WARNING:
     * This code most likely runs in a signal handler. Thus,







|







917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
 *----------------------------------------------------------------------
 */

int
TclAsyncNotifier(
    int sigNumber,		/* Signal number. */
    TCL_UNUSED(Tcl_ThreadId),	/* Target thread. */
    TCL_UNUSED(void *),	/* Notifier data. */
    int *flagPtr,		/* Flag to mark. */
    int value)			/* Value of mark. */
{
#if TCL_THREADS
    /*
     * WARNING:
     * This code most likely runs in a signal handler. Thus,
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
 *
 *----------------------------------------------------------------------
 */

#if TCL_THREADS
static TCL_NORETURN void
NotifierThreadProc(
    TCL_UNUSED(ClientData))
{
    ThreadSpecificData *tsdPtr;
    fd_set readableMask;
    fd_set writableMask;
    fd_set exceptionMask;
    int i, fds[2], receivePipe, ret;
    long found;







|







982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
 *
 *----------------------------------------------------------------------
 */

#if TCL_THREADS
static TCL_NORETURN void
NotifierThreadProc(
    TCL_UNUSED(void *))
{
    ThreadSpecificData *tsdPtr;
    fd_set readableMask;
    fd_set writableMask;
    fd_set exceptionMask;
    int i, fds[2], receivePipe, ret;
    long found;
Changes to unix/tclUnixCompat.c.
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
static int		CopyHostent(struct hostent *tgtPtr, char *buf,
			    int buflen);
static int		CopyString(const char *src, char *buf, int buflen);

#endif

#ifdef NEED_PW_CLEANER
static void		FreePwBuf(ClientData dummy);
#endif
#ifdef NEED_GR_CLEANER
static void		FreeGrBuf(ClientData dummy);
#endif
#endif /* TCL_THREADS */

/*
 *---------------------------------------------------------------------------
 *
 * TclUnixSetBlockingMode --







|


|







112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
static int		CopyHostent(struct hostent *tgtPtr, char *buf,
			    int buflen);
static int		CopyString(const char *src, char *buf, int buflen);

#endif

#ifdef NEED_PW_CLEANER
static void		FreePwBuf(void *dummy);
#endif
#ifdef NEED_GR_CLEANER
static void		FreeGrBuf(void *dummy);
#endif
#endif /* TCL_THREADS */

/*
 *---------------------------------------------------------------------------
 *
 * TclUnixSetBlockingMode --
Changes to unix/tclUnixInit.c.
858
859
860
861
862
863
864












865
866
867
868
869
870
871
	Tcl_SetVar2(interp, "tcl_pkgPath", NULL, pkgPath,
		TCL_GLOBAL_ONLY | TCL_APPEND_VALUE);
    } else
#endif /* HAVE_COREFOUNDATION */
    {
	Tcl_SetVar2(interp, "tcl_pkgPath", NULL, pkgPath, TCL_GLOBAL_ONLY);
    }













#ifdef DJGPP
    Tcl_SetVar2(interp, "tcl_platform", "platform", "dos", TCL_GLOBAL_ONLY);
#else
    Tcl_SetVar2(interp, "tcl_platform", "platform", "unix", TCL_GLOBAL_ONLY);
#endif








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







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
	Tcl_SetVar2(interp, "tcl_pkgPath", NULL, pkgPath,
		TCL_GLOBAL_ONLY | TCL_APPEND_VALUE);
    } else
#endif /* HAVE_COREFOUNDATION */
    {
	Tcl_SetVar2(interp, "tcl_pkgPath", NULL, pkgPath, TCL_GLOBAL_ONLY);
    }

    {
        /* Some platforms build configure scripts expect ~ expansion so do that */
        Tcl_Obj *origPaths;
        Tcl_Obj *resolvedPaths;
        origPaths = Tcl_GetVar2Ex(interp, "tcl_pkgPath", NULL, TCL_GLOBAL_ONLY);
        resolvedPaths = TclResolveTildePathList(origPaths);
        if (resolvedPaths != origPaths && resolvedPaths != NULL) {
            Tcl_SetVar2Ex(interp, "tcl_pkgPath", NULL,
		resolvedPaths, TCL_GLOBAL_ONLY);
        }
    }

#ifdef DJGPP
    Tcl_SetVar2(interp, "tcl_platform", "platform", "dos", TCL_GLOBAL_ONLY);
#else
    Tcl_SetVar2(interp, "tcl_platform", "platform", "unix", TCL_GLOBAL_ONLY);
#endif

Changes to unix/tclUnixPipe.c.
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
 *	See the user documentation.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_PidObjCmd(
    TCL_UNUSED(ClientData),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const *objv)	/* Argument strings. */
{
    Tcl_Channel chan;
    PipeState *pipePtr;
    size_t i;







|







1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
 *	See the user documentation.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_PidObjCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const *objv)	/* Argument strings. */
{
    Tcl_Channel chan;
    PipeState *pipePtr;
    size_t i;
Changes to unix/tclUnixSock.c.
49
50
51
52
53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
    TcpState *statePtr;
    int fd;
    struct TcpFdList *next;
} TcpFdList;

struct TcpState {
    Tcl_Channel channel;	/* Channel associated with this file. */
    int testFlags;              /* bit field for tests. Is set by testsocket
                                 * test procedure */
    TcpFdList fds;		/* The file descriptors of the sockets. */
    int flags;			/* ORed combination of the bitfields defined
				 * below. */

    int interest;		/* Event types of interest */

    /*
     * Only needed for server sockets
     */

    Tcl_TcpAcceptProc *acceptProc;







<
<
<


>







49
50
51
52
53
54
55



56
57
58
59
60
61
62
63
64
65
    TcpState *statePtr;
    int fd;
    struct TcpFdList *next;
} TcpFdList;

struct TcpState {
    Tcl_Channel channel;	/* Channel associated with this file. */



    int flags;			/* ORed combination of the bitfields defined
				 * below. */
    TcpFdList fds;		/* The file descriptors of the sockets. */
    int interest;		/* Event types of interest */

    /*
     * Only needed for server sockets
     */

    Tcl_TcpAcceptProc *acceptProc;
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#define TCP_ASYNC_CONNECT	(1<<1)	/* Async connect in progress. */
#define TCP_ASYNC_PENDING	(1<<4)	/* TcpConnect was called to
					 * process an async connect. This
					 * flag indicates that reentry is
					 * still pending */
#define TCP_ASYNC_FAILED	(1<<5)	/* An async connect finally failed */

/*
 * These bits may be ORed together into the "testFlags" field of a TcpState
 * structure.
 */

#define TCP_ASYNC_TEST_MODE	(1<<0)	/* Async testing activated.  Do not
					 * automatically continue connection
					 * process. */

/*
 * The following defines the maximum length of the listen queue. This is the
 * number of outstanding yet-to-be-serviced requests for a connection on a
 * server socket, more than this number of outstanding requests and the







<
<
<
<
<
|







89
90
91
92
93
94
95





96
97
98
99
100
101
102
103
#define TCP_ASYNC_CONNECT	(1<<1)	/* Async connect in progress. */
#define TCP_ASYNC_PENDING	(1<<4)	/* TcpConnect was called to
					 * process an async connect. This
					 * flag indicates that reentry is
					 * still pending */
#define TCP_ASYNC_FAILED	(1<<5)	/* An async connect finally failed */






#define TCP_ASYNC_TEST_MODE	(1<<8)	/* Async testing activated.  Do not
					 * automatically continue connection
					 * process. */

/*
 * The following defines the maximum length of the listen queue. This is the
 * number of outstanding yet-to-be-serviced requests for a connection on a
 * server socket, more than this number of outstanding requests and the
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
 *
 * Results:
 * 	0 if the connection has completed, -1 if still in progress or there is
 * 	an error.
 *
 * Side effects:
 *	Processes socket events off the system queue. May process
 *	asynchroneous connects.
 *
 *----------------------------------------------------------------------
 */

static int
WaitForConnect(
    TcpState *statePtr,		/* State of the socket. */







|







420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
 *
 * Results:
 * 	0 if the connection has completed, -1 if still in progress or there is
 * 	an error.
 *
 * Side effects:
 *	Processes socket events off the system queue. May process
 *	asynchronous connects.
 *
 *----------------------------------------------------------------------
 */

static int
WaitForConnect(
    TcpState *statePtr,		/* State of the socket. */
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
    /*
     * In socket test mode do not continue with the connect.
     * Exceptions are:
     * - Call by recv/send and blocking socket
     *   (errorCodePtr != NULL && !GOT_BITS(flags, TCP_NONBLOCKING))
     */

    if (GOT_BITS(statePtr->testFlags, TCP_ASYNC_TEST_MODE)
            && !(errorCodePtr != NULL
                    && !GOT_BITS(statePtr->flags, TCP_NONBLOCKING))) {
	*errorCodePtr = EWOULDBLOCK;
	return -1;
    }

    if (errorCodePtr == NULL || GOT_BITS(statePtr->flags, TCP_NONBLOCKING)) {







|







457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
    /*
     * In socket test mode do not continue with the connect.
     * Exceptions are:
     * - Call by recv/send and blocking socket
     *   (errorCodePtr != NULL && !GOT_BITS(flags, TCP_NONBLOCKING))
     */

    if (GOT_BITS(statePtr->flags, TCP_ASYNC_TEST_MODE)
            && !(errorCodePtr != NULL
                    && !GOT_BITS(statePtr->flags, TCP_NONBLOCKING))) {
	*errorCodePtr = EWOULDBLOCK;
	return -1;
    }

    if (errorCodePtr == NULL || GOT_BITS(statePtr->flags, TCP_NONBLOCKING)) {
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
	    Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(errno), TCL_INDEX_NONE);
        }
	return TCL_OK;
    }

    if ((len > 1) && (optionName[1] == 'c') &&
	    (strncmp(optionName, "-connecting", len) == 0)) {
        Tcl_DStringAppend(dsPtr,
                GOT_BITS(statePtr->flags, TCP_ASYNC_CONNECT) ? "1" : "0", TCL_INDEX_NONE);
        return TCL_OK;
    }

    if ((len == 0) || ((len > 1) && (optionName[1] == 'p') &&
	    (strncmp(optionName, "-peername", len) == 0))) {
        address peername;
        socklen_t size = sizeof(peername);







|
|







866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
	    Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(errno), TCL_INDEX_NONE);
        }
	return TCL_OK;
    }

    if ((len > 1) && (optionName[1] == 'c') &&
	    (strncmp(optionName, "-connecting", len) == 0)) {
	Tcl_DStringAppend(dsPtr,
		GOT_BITS(statePtr->flags, TCP_ASYNC_CONNECT) ? "1" : "0", TCL_INDEX_NONE);
        return TCL_OK;
    }

    if ((len == 0) || ((len > 1) && (optionName[1] == 'p') &&
	    (strncmp(optionName, "-peername", len) == 0))) {
        address peername;
        socklen_t size = sizeof(peername);
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
        TclUnixSetBlockingMode(statePtr->fds.fd, statePtr->cachedBlocking);

        if (error != 0) {
            SET_BITS(statePtr->flags, TCP_ASYNC_FAILED);
        }

        /*
         * We need to forward the writable event that brought us here, bcasue
         * upon reading of getsockopt(SO_ERROR), at least some OSes clear the
         * writable state from the socket, and so a subsequent select() on
         * behalf of a script level [fileevent] would not fire. It doesn't
         * hurt that this is also called in the successful case and will save
         * the event mechanism one roundtrip through select().
         */








|







1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
        TclUnixSetBlockingMode(statePtr->fds.fd, statePtr->cachedBlocking);

        if (error != 0) {
            SET_BITS(statePtr->flags, TCP_ASYNC_FAILED);
        }

        /*
         * We need to forward the writable event that brought us here, because
         * upon reading of getsockopt(SO_ERROR), at least some OSes clear the
         * writable state from the socket, and so a subsequent select() on
         * behalf of a script level [fileevent] would not fire. It doesn't
         * hurt that this is also called in the successful case and will save
         * the event mechanism one roundtrip through select().
         */

Changes to unix/tclUnixTest.c.
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TestfilehandlerCmd(
    TCL_UNUSED(ClientData),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const *objv)	/* Argument strings. */
{
    Pipe *pipePtr;
    int i, mask, timeout;
    static int initialized = 0;







|







125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TestfilehandlerCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const *objv)	/* Argument strings. */
{
    Pipe *pipePtr;
    int i, mask, timeout;
    static int initialized = 0;
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
	return TCL_ERROR;
    }
    return TCL_OK;
}

static void
TestFileHandlerProc(
    ClientData clientData,	/* Points to a Pipe structure. */
    int mask)			/* Indicates which events happened:
				 * TCL_READABLE or TCL_WRITABLE. */
{
    Pipe *pipePtr = (Pipe *)clientData;

    if (mask & TCL_READABLE) {
	pipePtr->readCount++;







|







306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
	return TCL_ERROR;
    }
    return TCL_OK;
}

static void
TestFileHandlerProc(
    void *clientData,	/* Points to a Pipe structure. */
    int mask)			/* Indicates which events happened:
				 * TCL_READABLE or TCL_WRITABLE. */
{
    Pipe *pipePtr = (Pipe *)clientData;

    if (mask & TCL_READABLE) {
	pipePtr->readCount++;
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TestfilewaitCmd(
    TCL_UNUSED(ClientData),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const *objv)	/* Argument strings. */
{
    int mask, result, timeout;
    Tcl_Channel channel;
    int fd;
    ClientData data;

    if (objc != 4) {
	Tcl_WrongNumArgs(interp, 2, objv, "file readable|writable|both timeout");
	return TCL_ERROR;
    }
    channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL);
    if (channel == NULL) {







|







|







339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TestfilewaitCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const *objv)	/* Argument strings. */
{
    int mask, result, timeout;
    Tcl_Channel channel;
    int fd;
    void *data;

    if (objc != 4) {
	Tcl_WrongNumArgs(interp, 2, objv, "file readable|writable|both timeout");
	return TCL_ERROR;
    }
    channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL);
    if (channel == NULL) {
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
    } else {
	Tcl_AppendResult(interp, "bad argument \"", Tcl_GetString(objv[2]),
		"\": must be readable, writable, or both", NULL);
	return TCL_ERROR;
    }
    if (Tcl_GetChannelHandle(channel,
	    (mask & TCL_READABLE) ? TCL_READABLE : TCL_WRITABLE,
	    (ClientData*) &data) != TCL_OK) {
	Tcl_AppendResult(interp, "couldn't get channel file", NULL);
	return TCL_ERROR;
    }
    fd = PTR2INT(data);
    if (Tcl_GetIntFromObj(interp, objv[3], &timeout) != TCL_OK) {
	return TCL_ERROR;
    }







|







370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
    } else {
	Tcl_AppendResult(interp, "bad argument \"", Tcl_GetString(objv[2]),
		"\": must be readable, writable, or both", NULL);
	return TCL_ERROR;
    }
    if (Tcl_GetChannelHandle(channel,
	    (mask & TCL_READABLE) ? TCL_READABLE : TCL_WRITABLE,
	    (void **) &data) != TCL_OK) {
	Tcl_AppendResult(interp, "couldn't get channel file", NULL);
	return TCL_ERROR;
    }
    fd = PTR2INT(data);
    if (Tcl_GetIntFromObj(interp, objv[3], &timeout) != TCL_OK) {
	return TCL_ERROR;
    }
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TestfindexecutableCmd(
    TCL_UNUSED(ClientData),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const *objv)	/* Argument strings. */
{
    Tcl_Obj *saveName;

    if (objc != 2) {







|







407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TestfindexecutableCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const *objv)	/* Argument strings. */
{
    Tcl_Obj *saveName;

    if (objc != 2) {
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TestforkCmd(
    TCL_UNUSED(ClientData),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const *objv)	/* Argument strings. */
{
    pid_t pid;

    if (objc != 1) {







|







449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TestforkCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const *objv)	/* Argument strings. */
{
    pid_t pid;

    if (objc != 1) {
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
 *	Sets up an signal and async handlers.
 *
 *----------------------------------------------------------------------
 */

static int
TestalarmCmd(
    TCL_UNUSED(ClientData),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const *objv)	/* Argument strings. */
{
#ifdef SA_RESTART
    unsigned int sec = 1;
    struct sigaction action;







|







495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
 *	Sets up an signal and async handlers.
 *
 *----------------------------------------------------------------------
 */

static int
TestalarmCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const *objv)	/* Argument strings. */
{
#ifdef SA_RESTART
    unsigned int sec = 1;
    struct sigaction action;
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
 *	Resets the value of gotsig back to '0'.
 *
 *----------------------------------------------------------------------
 */

static int
TestgotsigCmd(
    TCL_UNUSED(ClientData),
    Tcl_Interp *interp,		/* Current interpreter. */
    TCL_UNUSED(int) /*objc*/,
    TCL_UNUSED(Tcl_Obj *const *))
{
    Tcl_AppendResult(interp, gotsig, NULL);
    gotsig = "0";
    return TCL_OK;







|







573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
 *	Resets the value of gotsig back to '0'.
 *
 *----------------------------------------------------------------------
 */

static int
TestgotsigCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    TCL_UNUSED(int) /*objc*/,
    TCL_UNUSED(Tcl_Obj *const *))
{
    Tcl_AppendResult(interp, gotsig, NULL);
    gotsig = "0";
    return TCL_OK;
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
 *	Changes permissions of specified files.
 *
 *---------------------------------------------------------------------------
 */

static int
TestchmodCmd(
    TCL_UNUSED(ClientData),
    Tcl_Interp *interp,			/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const *objv)		/* Argument strings. */
{
    int i, mode;

    if (objc < 2) {







|







604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
 *	Changes permissions of specified files.
 *
 *---------------------------------------------------------------------------
 */

static int
TestchmodCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,			/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const *objv)		/* Argument strings. */
{
    int i, mode;

    if (objc < 2) {
Changes to unix/tclXtNotify.c.
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
				 * time FileHandlerEventProc was called for
				 * this file. */
    XtInputId read;		/* Xt read callback handle. */
    XtInputId write;		/* Xt write callback handle. */
    XtInputId except;		/* Xt exception callback handle. */
    Tcl_FileProc *proc;		/* Procedure to call, in the style of
				 * Tcl_CreateFileHandler. */
    ClientData clientData;	/* Argument to pass to proc. */
    struct FileHandler *nextPtr;/* Next in list of all files we care about. */
} FileHandler;

/*
 * The following structure is what is added to the Tcl event queue when file
 * handlers are ready to fire.
 */







|







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
				 * time FileHandlerEventProc was called for
				 * this file. */
    XtInputId read;		/* Xt read callback handle. */
    XtInputId write;		/* Xt write callback handle. */
    XtInputId except;		/* Xt exception callback handle. */
    Tcl_FileProc *proc;		/* Procedure to call, in the style of
				 * Tcl_CreateFileHandler. */
    void *clientData;	/* Argument to pass to proc. */
    struct FileHandler *nextPtr;/* Next in list of all files we care about. */
} FileHandler;

/*
 * The following structure is what is added to the Tcl event queue when file
 * handlers are ready to fire.
 */
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*
 * Static routines defined in this file.
 */

static int		FileHandlerEventProc(Tcl_Event *evPtr, int flags);
static void		FileProc(XtPointer clientData, int *source,
			    XtInputId *id);
static void		NotifierExitHandler(ClientData clientData);
static void		TimerProc(XtPointer clientData, XtIntervalId *id);
static void		CreateFileHandler(int fd, int mask,
			    Tcl_FileProc *proc, ClientData clientData);
static void		DeleteFileHandler(int fd);
static void		SetTimer(const Tcl_Time * timePtr);
static int		WaitForEvent(const Tcl_Time * timePtr);

/*
 * Functions defined in this file for use by users of the Xt Notifier:
 */







|


|







75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*
 * Static routines defined in this file.
 */

static int		FileHandlerEventProc(Tcl_Event *evPtr, int flags);
static void		FileProc(XtPointer clientData, int *source,
			    XtInputId *id);
static void		NotifierExitHandler(void *clientData);
static void		TimerProc(XtPointer clientData, XtIntervalId *id);
static void		CreateFileHandler(int fd, int mask,
			    Tcl_FileProc *proc, void *clientData);
static void		DeleteFileHandler(int fd);
static void		SetTimer(const Tcl_Time * timePtr);
static int		WaitForEvent(const Tcl_Time * timePtr);

/*
 * Functions defined in this file for use by users of the Xt Notifier:
 */
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
 *	Destroys the notifier window.
 *
 *----------------------------------------------------------------------
 */

static void
NotifierExitHandler(
    TCL_UNUSED(ClientData))
{
    if (notifier.currentTimeout != 0) {
	XtRemoveTimeOut(notifier.currentTimeout);
    }
    for (; notifier.firstFileHandlerPtr != NULL; ) {
	Tcl_DeleteFileHandler(notifier.firstFileHandlerPtr->fd);
    }







|







225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
 *	Destroys the notifier window.
 *
 *----------------------------------------------------------------------
 */

static void
NotifierExitHandler(
    TCL_UNUSED(void *))
{
    if (notifier.currentTimeout != 0) {
	XtRemoveTimeOut(notifier.currentTimeout);
    }
    for (; notifier.firstFileHandlerPtr != NULL; ) {
	Tcl_DeleteFileHandler(notifier.firstFileHandlerPtr->fd);
    }
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
    int fd,			/* Handle of stream to watch. */
    int mask,			/* OR'ed combination of TCL_READABLE,
				 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
				 * conditions under which proc should be
				 * called. */
    Tcl_FileProc *proc,		/* Procedure to call for each selected
				 * event. */
    ClientData clientData)	/* Arbitrary data to pass to proc. */
{
    FileHandler *filePtr;

    if (!initialized) {
	InitNotifier();
    }








|







335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
    int fd,			/* Handle of stream to watch. */
    int mask,			/* OR'ed combination of TCL_READABLE,
				 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
				 * conditions under which proc should be
				 * called. */
    Tcl_FileProc *proc,		/* Procedure to call for each selected
				 * event. */
    void *clientData)	/* Arbitrary data to pass to proc. */
{
    FileHandler *filePtr;

    if (!initialized) {
	InitNotifier();
    }

Changes to unix/tclXtTest.c.
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TesteventloopCmd(
    TCL_UNUSED(ClientData),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    static int *framePtr = NULL;/* Pointer to integer on stack frame of
				 * innermost invocation of the "wait"
				 * subcommand. */







|







73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TesteventloopCmd(
    TCL_UNUSED(void *),
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    static int *framePtr = NULL;/* Pointer to integer on stack frame of
				 * innermost invocation of the "wait"
				 * subcommand. */
Changes to win/Makefile.in.
274
275
276
277
278
279
280

281
282
283
284
285
286
287

GENERIC_OBJS = \
	regcomp.$(OBJEXT) \
	regexec.$(OBJEXT) \
	regfree.$(OBJEXT) \
	regerror.$(OBJEXT) \
	tclAlloc.$(OBJEXT) \

	tclAssembly.$(OBJEXT) \
	tclAsync.$(OBJEXT) \
	tclBasic.$(OBJEXT) \
	tclBinary.$(OBJEXT) \
	tclCkalloc.$(OBJEXT) \
	tclClock.$(OBJEXT) \
	tclCmdAH.$(OBJEXT) \







>







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

GENERIC_OBJS = \
	regcomp.$(OBJEXT) \
	regexec.$(OBJEXT) \
	regfree.$(OBJEXT) \
	regerror.$(OBJEXT) \
	tclAlloc.$(OBJEXT) \
	tclArithSeries.$(OBJEXT) \
	tclAssembly.$(OBJEXT) \
	tclAsync.$(OBJEXT) \
	tclBasic.$(OBJEXT) \
	tclBinary.$(OBJEXT) \
	tclCkalloc.$(OBJEXT) \
	tclClock.$(OBJEXT) \
	tclCmdAH.$(OBJEXT) \
Changes to win/makefile.vc.
232
233
234
235
236
237
238

239
240
241
242
243
244
245

COREOBJS = \
	$(TMP_DIR)\regcomp.obj \
	$(TMP_DIR)\regerror.obj \
	$(TMP_DIR)\regexec.obj \
	$(TMP_DIR)\regfree.obj \
	$(TMP_DIR)\tclAlloc.obj \

	$(TMP_DIR)\tclAssembly.obj \
	$(TMP_DIR)\tclAsync.obj \
	$(TMP_DIR)\tclBasic.obj \
	$(TMP_DIR)\tclBinary.obj \
	$(TMP_DIR)\tclCkalloc.obj \
	$(TMP_DIR)\tclClock.obj \
	$(TMP_DIR)\tclCmdAH.obj \







>







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

COREOBJS = \
	$(TMP_DIR)\regcomp.obj \
	$(TMP_DIR)\regerror.obj \
	$(TMP_DIR)\regexec.obj \
	$(TMP_DIR)\regfree.obj \
	$(TMP_DIR)\tclAlloc.obj \
	$(TMP_DIR)\tclArithSeries.obj \
	$(TMP_DIR)\tclAssembly.obj \
	$(TMP_DIR)\tclAsync.obj \
	$(TMP_DIR)\tclBasic.obj \
	$(TMP_DIR)\tclBinary.obj \
	$(TMP_DIR)\tclCkalloc.obj \
	$(TMP_DIR)\tclClock.obj \
	$(TMP_DIR)\tclCmdAH.obj \
Changes to win/tclWinConsole.c.
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
				      * pointer. */
} ConsoleEvent;

/*
 * Declarations for functions used only in this file.
 */

static int	ConsoleBlockModeProc(ClientData instanceData, int mode);
static void	ConsoleCheckProc(ClientData clientData, int flags);
static int	ConsoleCloseProc(ClientData instanceData,
		    Tcl_Interp *interp, int flags);
static int	ConsoleEventProc(Tcl_Event *evPtr, int flags);
static void	ConsoleExitHandler(ClientData clientData);
static int	ConsoleGetHandleProc(ClientData instanceData,
		    int direction, ClientData *handlePtr);
static int	ConsoleGetOptionProc(ClientData instanceData,
		    Tcl_Interp *interp, const char *optionName,
		    Tcl_DString *dsPtr);
static void	ConsoleInit(void);
static int	ConsoleInputProc(ClientData instanceData, char *buf,
		    int toRead, int *errorCode);
static int	ConsoleOutputProc(ClientData instanceData,
		    const char *buf, int toWrite, int *errorCode);
static int	ConsoleSetOptionProc(ClientData instanceData,
		    Tcl_Interp *interp, const char *optionName,
		    const char *value);
static void	ConsoleSetupProc(ClientData clientData, int flags);
static void	ConsoleWatchProc(ClientData instanceData, int mask);
static void	ProcExitHandler(ClientData clientData);
static void	ConsoleThreadActionProc(ClientData instanceData, int action);
static DWORD	ReadConsoleChars(HANDLE hConsole, WCHAR *lpBuffer,
		    RingSizeT nChars, RingSizeT *nCharsReadPtr);
static DWORD	WriteConsoleChars(HANDLE hConsole,
		    const WCHAR *lpBuffer, RingSizeT nChars,
		    RingSizeT *nCharsWritten);
static void	RingBufferInit(RingBuffer *ringPtr, RingSizeT capacity);
static void	RingBufferClear(RingBuffer *ringPtr);







|
|
|


|
|
|
|



|

|

|


|
|
|
|







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
				      * pointer. */
} ConsoleEvent;

/*
 * Declarations for functions used only in this file.
 */

static int	ConsoleBlockModeProc(void *instanceData, int mode);
static void	ConsoleCheckProc(void *clientData, int flags);
static int	ConsoleCloseProc(void *instanceData,
		    Tcl_Interp *interp, int flags);
static int	ConsoleEventProc(Tcl_Event *evPtr, int flags);
static void	ConsoleExitHandler(void *clientData);
static int	ConsoleGetHandleProc(void *instanceData,
		    int direction, void **handlePtr);
static int	ConsoleGetOptionProc(void *instanceData,
		    Tcl_Interp *interp, const char *optionName,
		    Tcl_DString *dsPtr);
static void	ConsoleInit(void);
static int	ConsoleInputProc(void *instanceData, char *buf,
		    int toRead, int *errorCode);
static int	ConsoleOutputProc(void *instanceData,
		    const char *buf, int toWrite, int *errorCode);
static int	ConsoleSetOptionProc(void *instanceData,
		    Tcl_Interp *interp, const char *optionName,
		    const char *value);
static void	ConsoleSetupProc(void *clientData, int flags);
static void	ConsoleWatchProc(void *instanceData, int mask);
static void	ProcExitHandler(void *clientData);
static void	ConsoleThreadActionProc(void *instanceData, int action);
static DWORD	ReadConsoleChars(HANDLE hConsole, WCHAR *lpBuffer,
		    RingSizeT nChars, RingSizeT *nCharsReadPtr);
static DWORD	WriteConsoleChars(HANDLE hConsole,
		    const WCHAR *lpBuffer, RingSizeT nChars,
		    RingSizeT *nCharsWritten);
static void	RingBufferInit(RingBuffer *ringPtr, RingSizeT capacity);
static void	RingBufferClear(RingBuffer *ringPtr);
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
 *	Removes the console event source.
 *
 *----------------------------------------------------------------------
 */

static void
ConsoleExitHandler(
    TCL_UNUSED(ClientData))
{
    Tcl_DeleteEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
}

/*
 *----------------------------------------------------------------------
 *







|







666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
 *	Removes the console event source.
 *
 *----------------------------------------------------------------------
 */

static void
ConsoleExitHandler(
    TCL_UNUSED(void *))
{
    Tcl_DeleteEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
}

/*
 *----------------------------------------------------------------------
 *
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
 *	Resets the process list.
 *
 *----------------------------------------------------------------------
 */

static void
ProcExitHandler(
    TCL_UNUSED(ClientData))
{
    AcquireSRWLockExclusive(&gConsoleLock);
    gInitialized = 0;
    ReleaseSRWLockExclusive(&gConsoleLock);
}

/*







|







690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
 *	Resets the process list.
 *
 *----------------------------------------------------------------------
 */

static void
ProcExitHandler(
    TCL_UNUSED(void *))
{
    AcquireSRWLockExclusive(&gConsoleLock);
    gInitialized = 0;
    ReleaseSRWLockExclusive(&gConsoleLock);
}

/*
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
 *	Adjusts the block time if needed.
 *
 *----------------------------------------------------------------------
 */

void
ConsoleSetupProc(
    TCL_UNUSED(ClientData),
    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
{
    ConsoleChannelInfo *chanInfoPtr;
    Tcl_Time blockTime = { 0, 0 };
    int block = 1;

    if (!(flags & TCL_FILE_EVENTS)) {







|







755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
 *	Adjusts the block time if needed.
 *
 *----------------------------------------------------------------------
 */

void
ConsoleSetupProc(
    TCL_UNUSED(void *),
    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
{
    ConsoleChannelInfo *chanInfoPtr;
    Tcl_Time blockTime = { 0, 0 };
    int block = 1;

    if (!(flags & TCL_FILE_EVENTS)) {
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
 *	May queue an event.
 *
 *----------------------------------------------------------------------
 */

static void
ConsoleCheckProc(
    TCL_UNUSED(ClientData),
    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
{
    ConsoleChannelInfo *chanInfoPtr;
    Tcl_ThreadId me;
    int needEvent;

    if (!(flags & TCL_FILE_EVENTS)) {







|







820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
 *	May queue an event.
 *
 *----------------------------------------------------------------------
 */

static void
ConsoleCheckProc(
    TCL_UNUSED(void *),
    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
{
    ConsoleChannelInfo *chanInfoPtr;
    Tcl_ThreadId me;
    int needEvent;

    if (!(flags & TCL_FILE_EVENTS)) {
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
 *	Sets the device into blocking or non-blocking mode.
 *
 *----------------------------------------------------------------------
 */

static int
ConsoleBlockModeProc(
    ClientData instanceData,	/* Instance data for channel. */
    int mode)			/* TCL_MODE_BLOCKING or
				 * TCL_MODE_NONBLOCKING. */
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;

    /*
     * Consoles on Windows can not be switched between blocking and







|







920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
 *	Sets the device into blocking or non-blocking mode.
 *
 *----------------------------------------------------------------------
 */

static int
ConsoleBlockModeProc(
    void *instanceData,	/* Instance data for channel. */
    int mode)			/* TCL_MODE_BLOCKING or
				 * TCL_MODE_NONBLOCKING. */
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;

    /*
     * Consoles on Windows can not be switched between blocking and
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
 *	Closes the physical channel.
 *
 *----------------------------------------------------------------------
 */

static int
ConsoleCloseProc(
    ClientData instanceData,	/* Pointer to ConsoleChannelInfo structure. */
    TCL_UNUSED(Tcl_Interp *),
    int flags)
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
    ConsoleHandleInfo *handleInfoPtr;
    int errorCode = 0;
    ConsoleChannelInfo **nextPtrPtr;







|







960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
 *	Closes the physical channel.
 *
 *----------------------------------------------------------------------
 */

static int
ConsoleCloseProc(
    void *instanceData,	/* Pointer to ConsoleChannelInfo structure. */
    TCL_UNUSED(Tcl_Interp *),
    int flags)
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
    ConsoleHandleInfo *handleInfoPtr;
    int errorCode = 0;
    ConsoleChannelInfo **nextPtrPtr;
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
 * Side effects:
 *	Reads input from the actual channel.
 *
 *----------------------------------------------------------------------
 */
static int
ConsoleInputProc(
    ClientData instanceData,	/* Console state. */
    char *bufPtr,		/* Where to store data read. */
    int bufSize,		/* How much space is available in the
				 * buffer? */
    int *errorCode)		/* Where to store error code. */
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
    ConsoleHandleInfo *handleInfoPtr;







|







1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
 * Side effects:
 *	Reads input from the actual channel.
 *
 *----------------------------------------------------------------------
 */
static int
ConsoleInputProc(
    void *instanceData,	/* Console state. */
    char *bufPtr,		/* Where to store data read. */
    int bufSize,		/* How much space is available in the
				 * buffer? */
    int *errorCode)		/* Where to store error code. */
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
    ConsoleHandleInfo *handleInfoPtr;
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
 * Side effects:
 *	Writes output on the actual channel.
 *
 *----------------------------------------------------------------------
 */
static int
ConsoleOutputProc(
    ClientData instanceData,	/* Console state. */
    const char *buf,		/* The data buffer. */
    int toWrite,		/* How many bytes to write? */
    int *errorCode)		/* Where to store error code. */
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
    ConsoleHandleInfo *handleInfoPtr;
    RingSizeT numWritten;







|







1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
 * Side effects:
 *	Writes output on the actual channel.
 *
 *----------------------------------------------------------------------
 */
static int
ConsoleOutputProc(
    void *instanceData,	/* Console state. */
    const char *buf,		/* The data buffer. */
    int toWrite,		/* How many bytes to write? */
    int *errorCode)		/* Where to store error code. */
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
    ConsoleHandleInfo *handleInfoPtr;
    RingSizeT numWritten;
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
 *	None.
 *
 *----------------------------------------------------------------------
 */

static void
ConsoleWatchProc(
    ClientData instanceData,	/* Console state. */
    int newMask)		/* What events to watch for, one of
				 * of TCL_READABLE, TCL_WRITABLE
				 */
{
    ConsoleChannelInfo **nextPtrPtr, *ptr;
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
    int oldMask = chanInfoPtr->watchMask;







|







1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
 *	None.
 *
 *----------------------------------------------------------------------
 */

static void
ConsoleWatchProc(
    void *instanceData,	/* Console state. */
    int newMask)		/* What events to watch for, one of
				 * of TCL_READABLE, TCL_WRITABLE
				 */
{
    ConsoleChannelInfo **nextPtrPtr, *ptr;
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
    int oldMask = chanInfoPtr->watchMask;
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
ConsoleGetHandleProc(
    ClientData instanceData,	/* The console state. */
    TCL_UNUSED(int) /*direction*/,
    ClientData *handlePtr)	/* Where to store the handle. */
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;

    if (chanInfoPtr->handle == INVALID_HANDLE_VALUE) {
	return TCL_ERROR;
    } else {
	*handlePtr = chanInfoPtr->handle;







|

|







1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
ConsoleGetHandleProc(
    void *instanceData,	/* The console state. */
    TCL_UNUSED(int) /*direction*/,
    void **handlePtr)	/* Where to store the handle. */
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;

    if (chanInfoPtr->handle == INVALID_HANDLE_VALUE) {
	return TCL_ERROR;
    } else {
	*handlePtr = chanInfoPtr->handle;
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
 *	Changes thread local list of valid channels.
 *
 *----------------------------------------------------------------------
 */

static void
ConsoleThreadActionProc(
    ClientData instanceData,
    int action)
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;

    /* No need for any locks as no other thread will be writing to it */
    if (action == TCL_CHANNEL_THREAD_INSERT) {
	ConsoleInit(); /* Needed to set up event source handlers for this thread */







|







2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
 *	Changes thread local list of valid channels.
 *
 *----------------------------------------------------------------------
 */

static void
ConsoleThreadActionProc(
    void *instanceData,
    int action)
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;

    /* No need for any locks as no other thread will be writing to it */
    if (action == TCL_CHANNEL_THREAD_INSERT) {
	ConsoleInit(); /* Needed to set up event source handlers for this thread */
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
 *	May modify an option on a console. Sets Error message if needed (by
 *	calling Tcl_BadChannelOption).
 *
 *----------------------------------------------------------------------
 */
static int
ConsoleSetOptionProc(
    ClientData instanceData,	/* File state. */
    Tcl_Interp *interp,		/* For error reporting - can be NULL. */
    const char *optionName,	/* Which option to set? */
    const char *value)		/* New value for option. */
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
    int len = strlen(optionName);
    int vlen = strlen(value);







|







2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
 *	May modify an option on a console. Sets Error message if needed (by
 *	calling Tcl_BadChannelOption).
 *
 *----------------------------------------------------------------------
 */
static int
ConsoleSetOptionProc(
    void *instanceData,	/* File state. */
    Tcl_Interp *interp,		/* For error reporting - can be NULL. */
    const char *optionName,	/* Which option to set? */
    const char *value)		/* New value for option. */
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
    int len = strlen(optionName);
    int vlen = strlen(value);
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
 *	(by calling Tcl_BadChannelOption).
 *
 *----------------------------------------------------------------------
 */

static int
ConsoleGetOptionProc(
    ClientData instanceData,	/* File state. */
    Tcl_Interp *interp,		/* For error reporting - can be NULL. */
    const char *optionName,	/* Option to get. */
    Tcl_DString *dsPtr)		/* Where to store value(s). */
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
    int valid = 0;		/* Flag if valid option parsed. */
    unsigned int len;







|







2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
 *	(by calling Tcl_BadChannelOption).
 *
 *----------------------------------------------------------------------
 */

static int
ConsoleGetOptionProc(
    void *instanceData,	/* File state. */
    Tcl_Interp *interp,		/* For error reporting - can be NULL. */
    const char *optionName,	/* Option to get. */
    Tcl_DString *dsPtr)		/* Where to store value(s). */
{
    ConsoleChannelInfo *chanInfoPtr = (ConsoleChannelInfo *)instanceData;
    int valid = 0;		/* Flag if valid option parsed. */
    unsigned int len;
Changes to win/tclWinFCmd.c.
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
	     *	fprintf(stderr, "%d\n", ((WCHAR *) nativeName)[0]);
	     */

	    Tcl_DStringInit(&dsTemp);
	    Tcl_WCharToUtfDString(nativeName, TCL_INDEX_NONE, &dsTemp);
	    Tcl_DStringFree(&ds);

	    /*
	     * Deal with issues of tildes being absolute.
	     */

	    if (Tcl_DStringValue(&dsTemp)[0] == '~') {
		TclNewLiteralStringObj(tempPath, "./");
		Tcl_AppendToObj(tempPath, Tcl_DStringValue(&dsTemp),
			Tcl_DStringLength(&dsTemp));
		Tcl_DStringFree(&dsTemp);
	    } else {
		tempPath = TclDStringToObj(&dsTemp);
	    }
	    Tcl_ListObjReplace(NULL, splitPath, i, 1, 1, &tempPath);
	    FindClose(handle);
	}
    }

    *attributePtrPtr = Tcl_FSJoinPath(splitPath, -1);

    if (splitPath != NULL) {







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







1711
1712
1713
1714
1715
1716
1717










1718

1719
1720
1721
1722
1723
1724
1725
1726
	     *	fprintf(stderr, "%d\n", ((WCHAR *) nativeName)[0]);
	     */

	    Tcl_DStringInit(&dsTemp);
	    Tcl_WCharToUtfDString(nativeName, TCL_INDEX_NONE, &dsTemp);
	    Tcl_DStringFree(&ds);











            tempPath = TclDStringToObj(&dsTemp);

            Tcl_ListObjReplace(NULL, splitPath, i, 1, 1, &tempPath);
	    FindClose(handle);
	}
    }

    *attributePtrPtr = Tcl_FSJoinPath(splitPath, -1);

    if (splitPath != NULL) {
Changes to win/tclWinInit.c.
509
510
511
512
513
514
515






516

517
518
519
520
521
522
523
	if (ptr != NULL) {
	    Tcl_DStringAppend(&ds, ptr, TCL_INDEX_NONE);
	}
	if (Tcl_DStringLength(&ds) > 0) {
	    Tcl_SetVar2(interp, "env", "HOME", Tcl_DStringValue(&ds),
		    TCL_GLOBAL_ONLY);
	} else {






	    Tcl_SetVar2(interp, "env", "HOME", "c:\\", TCL_GLOBAL_ONLY);

	}
    }

    /*
     * Initialize the user name from the environment first, since this is much
     * faster than asking the system.
     * Note: cchUserNameLen is number of characters including nul terminator.







>
>
>
>
>
>
|
>







509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
	if (ptr != NULL) {
	    Tcl_DStringAppend(&ds, ptr, TCL_INDEX_NONE);
	}
	if (Tcl_DStringLength(&ds) > 0) {
	    Tcl_SetVar2(interp, "env", "HOME", Tcl_DStringValue(&ds),
		    TCL_GLOBAL_ONLY);
	} else {
            /* None of HOME, HOMEDRIVE, HOMEPATH exists. Try USERPROFILE */
            ptr = Tcl_GetVar2(interp, "env", "USERPROFILE", TCL_GLOBAL_ONLY);
            if (ptr != NULL && ptr[0]) {
                Tcl_SetVar2(interp, "env", "HOME", ptr, TCL_GLOBAL_ONLY);
            } else {
                /* Last resort */
                Tcl_SetVar2(interp, "env", "HOME", "c:\\", TCL_GLOBAL_ONLY);
            }
	}
    }

    /*
     * Initialize the user name from the environment first, since this is much
     * faster than asking the system.
     * Note: cchUserNameLen is number of characters including nul terminator.
Changes to win/tclWinSock.c.
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
    TcpState *statePtr;
    SOCKET fd;
    struct TcpFdList *next;
} TcpFdList;

struct TcpState {
    Tcl_Channel channel;	/* Channel associated with this socket. */
    int testFlags;              /* bit field for tests. Is set by testsocket
                                 * test procedure */
    struct TcpFdList *sockets;	/* Windows SOCKET handle. */
    int flags;			/* Bit field comprised of the flags described
				 * below. */

    int watchEvents;		/* OR'ed combination of FD_READ, FD_WRITE,
				 * FD_CLOSE, FD_ACCEPT and FD_CONNECT that
				 * indicate which events are interesting. */
    volatile int readyEvents;	/* OR'ed combination of FD_READ, FD_WRITE,
				 * FD_CLOSE, FD_ACCEPT and FD_CONNECT that
				 * indicate which events have occurred.
				 * Set by notifier thread, access must be
				 * protected by semaphore */
    int selectEvents;		/* OR'ed combination of FD_READ, FD_WRITE,
				 * FD_CLOSE, FD_ACCEPT and FD_CONNECT that
				 * indicate which events are currently being
				 * selected. */
    volatile int acceptEventCount;
				/* Count of the current number of FD_ACCEPTs
				 * that have arrived and not yet processed.
				 * Set by notifier thread, access must be
				 * protected by semaphore */
    Tcl_TcpAcceptProc *acceptProc;
				/* Proc to call on accept. */
    ClientData acceptProcData;	/* The data for the accept proc. */

    /*
     * Only needed for client sockets
     */

    struct addrinfo *addrlist;	/* Addresses to connect to. */
    struct addrinfo *addr;	/* Iterator over addrlist. */







<
<
<


>



















|







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
    TcpState *statePtr;
    SOCKET fd;
    struct TcpFdList *next;
} TcpFdList;

struct TcpState {
    Tcl_Channel channel;	/* Channel associated with this socket. */



    int flags;			/* Bit field comprised of the flags described
				 * below. */
    struct TcpFdList *sockets;	/* Windows SOCKET handle. */
    int watchEvents;		/* OR'ed combination of FD_READ, FD_WRITE,
				 * FD_CLOSE, FD_ACCEPT and FD_CONNECT that
				 * indicate which events are interesting. */
    volatile int readyEvents;	/* OR'ed combination of FD_READ, FD_WRITE,
				 * FD_CLOSE, FD_ACCEPT and FD_CONNECT that
				 * indicate which events have occurred.
				 * Set by notifier thread, access must be
				 * protected by semaphore */
    int selectEvents;		/* OR'ed combination of FD_READ, FD_WRITE,
				 * FD_CLOSE, FD_ACCEPT and FD_CONNECT that
				 * indicate which events are currently being
				 * selected. */
    volatile int acceptEventCount;
				/* Count of the current number of FD_ACCEPTs
				 * that have arrived and not yet processed.
				 * Set by notifier thread, access must be
				 * protected by semaphore */
    Tcl_TcpAcceptProc *acceptProc;
				/* Proc to call on accept. */
    void *acceptProcData;	/* The data for the accept proc. */

    /*
     * Only needed for client sockets
     */

    struct addrinfo *addrlist;	/* Addresses to connect to. */
    struct addrinfo *addr;	/* Iterator over addrlist. */
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
					 * socket */
#define TCP_ASYNC_PENDING	(1<<4)	/* TcpConnect was called to
					 * process an async connect. This
					 * flag indicates that reentry is
					 * still pending */
#define TCP_ASYNC_FAILED	(1<<5)	/* An async connect finally failed */

/*
 * These bits may be ORed together into the "testFlags" field of a TcpState
 * structure.
 */

#define TCP_ASYNC_TEST_MODE	(1<<0)	/* Async testing activated.  Do not
					 * automatically continue connection
					 * process */

/*
 * The following structure is what is added to the Tcl event queue when a
 * socket event occurs.
 */







<
<
<
<
<
|







180
181
182
183
184
185
186





187
188
189
190
191
192
193
194
					 * socket */
#define TCP_ASYNC_PENDING	(1<<4)	/* TcpConnect was called to
					 * process an async connect. This
					 * flag indicates that reentry is
					 * still pending */
#define TCP_ASYNC_FAILED	(1<<5)	/* An async connect finally failed */






#define TCP_ASYNC_TEST_MODE	(1<<8)	/* Async testing activated.  Do not
					 * automatically continue connection
					 * process */

/*
 * The following structure is what is added to the Tcl event queue when a
 * socket event occurs.
 */
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
 * Static routines for this file:
 */

static int		TcpConnect(Tcl_Interp *interp,
			    TcpState *state);
static void		InitSockets(void);
static TcpState *	NewSocketInfo(SOCKET socket);
static void		SocketExitHandler(ClientData clientData);
static LRESULT CALLBACK	SocketProc(HWND hwnd, UINT message, WPARAM wParam,
			    LPARAM lParam);
static int		SocketsEnabled(void);
static void		TcpAccept(TcpFdList *fds, SOCKET newSocket, address addr);
static int		WaitForConnect(TcpState *statePtr, int *errorCodePtr);
static int		WaitForSocketEvent(TcpState *statePtr, int events,
			    int *errorCodePtr);
static void		AddSocketInfoFd(TcpState *statePtr,  SOCKET socket);
static int		FindFDInList(TcpState *statePtr, SOCKET socket);
static DWORD WINAPI	SocketThread(LPVOID arg);
static void		TcpThreadActionProc(ClientData instanceData,
			    int action);
static int		TcpCloseProc(void *, Tcl_Interp *);

static Tcl_EventCheckProc	SocketCheckProc;
static Tcl_EventProc		SocketEventProc;
static Tcl_EventSetupProc	SocketSetupProc;
static Tcl_DriverBlockModeProc	TcpBlockModeProc;







|










|







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
 * Static routines for this file:
 */

static int		TcpConnect(Tcl_Interp *interp,
			    TcpState *state);
static void		InitSockets(void);
static TcpState *	NewSocketInfo(SOCKET socket);
static void		SocketExitHandler(void *clientData);
static LRESULT CALLBACK	SocketProc(HWND hwnd, UINT message, WPARAM wParam,
			    LPARAM lParam);
static int		SocketsEnabled(void);
static void		TcpAccept(TcpFdList *fds, SOCKET newSocket, address addr);
static int		WaitForConnect(TcpState *statePtr, int *errorCodePtr);
static int		WaitForSocketEvent(TcpState *statePtr, int events,
			    int *errorCodePtr);
static void		AddSocketInfoFd(TcpState *statePtr,  SOCKET socket);
static int		FindFDInList(TcpState *statePtr, SOCKET socket);
static DWORD WINAPI	SocketThread(LPVOID arg);
static void		TcpThreadActionProc(void *instanceData,
			    int action);
static int		TcpCloseProc(void *, Tcl_Interp *);

static Tcl_EventCheckProc	SocketCheckProc;
static Tcl_EventProc		SocketEventProc;
static Tcl_EventSetupProc	SocketSetupProc;
static Tcl_DriverBlockModeProc	TcpBlockModeProc;
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399

	    Tcl_DString inDs;

	    Tcl_DStringInit(&inDs);
	    Tcl_DStringSetLength(&inDs, 256);
	    if (gethostname(Tcl_DStringValue(&inDs),
		    Tcl_DStringLength(&inDs)) == 0) {
		Tcl_ExternalToUtfDStringEx(NULL, Tcl_DStringValue(&inDs), TCL_INDEX_NONE,
			TCL_ENCODING_NOCOMPLAIN, &ds);
	    }
	    Tcl_DStringFree(&inDs);
	}
    }

    *encodingPtr = Tcl_GetEncoding(NULL, "utf-8");
    *lengthPtr = Tcl_DStringLength(&ds);







|
|







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

	    Tcl_DString inDs;

	    Tcl_DStringInit(&inDs);
	    Tcl_DStringSetLength(&inDs, 256);
	    if (gethostname(Tcl_DStringValue(&inDs),
		    Tcl_DStringLength(&inDs)) == 0) {
		Tcl_ExternalToUtfDStringEx(NULL, Tcl_DStringValue(&inDs),
			TCL_INDEX_NONE, TCL_ENCODING_NOCOMPLAIN, &ds);
	    }
	    Tcl_DStringFree(&inDs);
	}
    }

    *encodingPtr = Tcl_GetEncoding(NULL, "utf-8");
    *lengthPtr = Tcl_DStringLength(&ds);
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
    Tcl_MutexUnlock(&socketMutex);

    if (SocketsEnabled()) {
	return TCL_OK;
    }
    if (interp != NULL) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"sockets are not available on this system", -1));
    }
    return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *







|







451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
    Tcl_MutexUnlock(&socketMutex);

    if (SocketsEnabled()) {
	return TCL_OK;
    }
    if (interp != NULL) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"sockets are not available on this system", TCL_INDEX_NONE));
    }
    return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *
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
 *	Sets the device into blocking or nonblocking mode.
 *
 *----------------------------------------------------------------------
 */

static int
TcpBlockModeProc(
    ClientData instanceData,	/* Socket state. */
    int mode)			/* The mode to set. Can be one of
				 * TCL_MODE_BLOCKING or
				 * TCL_MODE_NONBLOCKING. */
{
    TcpState *statePtr = (TcpState *)instanceData;

    if (mode == TCL_MODE_NONBLOCKING) {
	SET_BITS(statePtr->flags, TCP_NONBLOCKING);
    } else {
        CLEAR_BITS(statePtr->flags, TCP_NONBLOCKING);
    }
    return 0;
}

/*
 *----------------------------------------------------------------------
 *







|









|







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
 *	Sets the device into blocking or nonblocking mode.
 *
 *----------------------------------------------------------------------
 */

static int
TcpBlockModeProc(
    void *instanceData,	/* Socket state. */
    int mode)			/* The mode to set. Can be one of
				 * TCL_MODE_BLOCKING or
				 * TCL_MODE_NONBLOCKING. */
{
    TcpState *statePtr = (TcpState *)instanceData;

    if (mode == TCL_MODE_NONBLOCKING) {
	SET_BITS(statePtr->flags, TCP_NONBLOCKING);
    } else {
	CLEAR_BITS(statePtr->flags, TCP_NONBLOCKING);
    }
    return 0;
}

/*
 *----------------------------------------------------------------------
 *
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
 *
 * Results:
 * 	0 if the connection has completed, -1 if still in progress or there is
 * 	an error.
 *
 * Side effects:
 *	Processes socket events off the system queue. May process
 *	asynchroneous connect.
 *
 *----------------------------------------------------------------------
 */

static int
WaitForConnect(
    TcpState *statePtr,		/* State of the socket. */







|







574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
 *
 * Results:
 * 	0 if the connection has completed, -1 if still in progress or there is
 * 	an error.
 *
 * Side effects:
 *	Processes socket events off the system queue. May process
 *	asynchronous connect.
 *
 *----------------------------------------------------------------------
 */

static int
WaitForConnect(
    TcpState *statePtr,		/* State of the socket. */
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
     * In socket test mode do not continue with the connect
     * Exceptions are:
     * - Call by recv/send and blocking socket
     *   (errorCodePtr != NULL && !GOT_BITS(flags, TCP_NONBLOCKING))
     * - Call by the event queue (errorCodePtr == NULL)
     */

    if (GOT_BITS(statePtr->testFlags, TCP_ASYNC_TEST_MODE)
	    && errorCodePtr != NULL
            && GOT_BITS(statePtr->flags, TCP_NONBLOCKING)) {
	*errorCodePtr = EWOULDBLOCK;
	return -1;
    }

    /*







|







615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
     * In socket test mode do not continue with the connect
     * Exceptions are:
     * - Call by recv/send and blocking socket
     *   (errorCodePtr != NULL && !GOT_BITS(flags, TCP_NONBLOCKING))
     * - Call by the event queue (errorCodePtr == NULL)
     */

    if (GOT_BITS(statePtr->flags, TCP_ASYNC_TEST_MODE)
	    && errorCodePtr != NULL
            && GOT_BITS(statePtr->flags, TCP_NONBLOCKING)) {
	*errorCodePtr = EWOULDBLOCK;
	return -1;
    }

    /*
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
	 * Get the statePtr lock.
	 */

	tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
	WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

	/*
         * Check for connect event.
         */

	if (GOT_BITS(statePtr->readyEvents, FD_CONNECT)) {
	    /*
             * Consume the connect event.
             */

	    CLEAR_BITS(statePtr->readyEvents, FD_CONNECT);

	    /*
	     * For blocking sockets and foreground processing, disable async
	     * connect as we continue now synchoneously.
	     */

	    if (errorCodePtr != NULL &&
		    !GOT_BITS(statePtr->flags, TCP_NONBLOCKING)) {
		CLEAR_BITS(statePtr->flags, TCP_ASYNC_CONNECT);
	    }

	    /*
             * Free list lock.
             */

	    SetEvent(tsdPtr->socketListLock);

	    /*
	     * Continue connect. If switched to synchroneous connect, the
	     * connect is terminated.
	     */

	    result = TcpConnect(NULL, statePtr);

	    /*
             * Restore event service mode.
             */

	    (void) Tcl_SetServiceMode(oldMode);

	    /*
	     * Check for Succesfull connect or async connect restart
	     */








|
|



|
|














|
|











|
|







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
	 * Get the statePtr lock.
	 */

	tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
	WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

	/*
	 * Check for connect event.
	 */

	if (GOT_BITS(statePtr->readyEvents, FD_CONNECT)) {
	    /*
	     * Consume the connect event.
	     */

	    CLEAR_BITS(statePtr->readyEvents, FD_CONNECT);

	    /*
	     * For blocking sockets and foreground processing, disable async
	     * connect as we continue now synchoneously.
	     */

	    if (errorCodePtr != NULL &&
		    !GOT_BITS(statePtr->flags, TCP_NONBLOCKING)) {
		CLEAR_BITS(statePtr->flags, TCP_ASYNC_CONNECT);
	    }

	    /*
	     * Free list lock.
	     */

	    SetEvent(tsdPtr->socketListLock);

	    /*
	     * Continue connect. If switched to synchroneous connect, the
	     * connect is terminated.
	     */

	    result = TcpConnect(NULL, statePtr);

	    /*
	     * Restore event service mode.
	     */

	    (void) Tcl_SetServiceMode(oldMode);

	    /*
	     * Check for Succesfull connect or async connect restart
	     */

771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
 *	Reads input from the input device of the channel.
 *
 *----------------------------------------------------------------------
 */

static int
TcpInputProc(
    ClientData instanceData,	/* Socket state. */
    char *buf,			/* Where to store data read. */
    int bufSize,		/* How much space is available in the
				 * buffer? */
    int *errorCodePtr)		/* Where to store error code. */
{
    TcpState *statePtr = (TcpState *)instanceData;
    int bytesRead;







|







764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
 *	Reads input from the input device of the channel.
 *
 *----------------------------------------------------------------------
 */

static int
TcpInputProc(
    void *instanceData,	/* Socket state. */
    char *buf,			/* Where to store data read. */
    int bufSize,		/* How much space is available in the
				 * buffer? */
    int *errorCodePtr)		/* Where to store error code. */
{
    TcpState *statePtr = (TcpState *)instanceData;
    int bytesRead;
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
     * using non-blocking sockets.
     */

    while (1) {
	SendSelectMessage(tsdPtr, UNSELECT, statePtr);

	/*
         * Single fd operation: this proc is only called for a connected
         * socket.
         */

	bytesRead = recv(statePtr->sockets->fd, buf, bufSize, 0);
	CLEAR_BITS(statePtr->readyEvents, FD_READ);

	/*
	 * Check for end-of-file condition or successful read.
	 */







|
|
|







819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
     * using non-blocking sockets.
     */

    while (1) {
	SendSelectMessage(tsdPtr, UNSELECT, statePtr);

	/*
	 * Single fd operation: this proc is only called for a connected
	 * socket.
	 */

	bytesRead = recv(statePtr->sockets->fd, buf, bufSize, 0);
	CLEAR_BITS(statePtr->readyEvents, FD_READ);

	/*
	 * Check for end-of-file condition or successful read.
	 */
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
	}

	/*
	 * Check for error condition or underflow in non-blocking case.
	 */

	if (GOT_BITS(statePtr->flags, TCP_NONBLOCKING)
                || (error != WSAEWOULDBLOCK)) {
	    Tcl_WinConvertError(error);
	    *errorCodePtr = Tcl_GetErrno();
	    bytesRead = -1;
	    break;
	}

	/*







|







866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
	}

	/*
	 * Check for error condition or underflow in non-blocking case.
	 */

	if (GOT_BITS(statePtr->flags, TCP_NONBLOCKING)
	        || (error != WSAEWOULDBLOCK)) {
	    Tcl_WinConvertError(error);
	    *errorCodePtr = Tcl_GetErrno();
	    bytesRead = -1;
	    break;
	}

	/*
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
 *	Produces output on the socket.
 *
 *----------------------------------------------------------------------
 */

static int
TcpOutputProc(
    ClientData instanceData,	/* Socket state. */
    const char *buf,		/* The data buffer. */
    int toWrite,		/* How many bytes to write? */
    int *errorCodePtr)		/* Where to store error code. */
{
    TcpState *statePtr = (TcpState *)instanceData;
    int written;
    DWORD error;







|







908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
 *	Produces output on the socket.
 *
 *----------------------------------------------------------------------
 */

static int
TcpOutputProc(
    void *instanceData,	/* Socket state. */
    const char *buf,		/* The data buffer. */
    int toWrite,		/* How many bytes to write? */
    int *errorCodePtr)		/* Where to store error code. */
{
    TcpState *statePtr = (TcpState *)instanceData;
    int written;
    DWORD error;
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
	return -1;
    }

    while (1) {
	SendSelectMessage(tsdPtr, UNSELECT, statePtr);

	/*
         * Single fd operation: this proc is only called for a connected
         * socket.
         */

	written = send(statePtr->sockets->fd, buf, toWrite, 0);
	if (written != SOCKET_ERROR) {
	    /*
	     * Since Windows won't generate a new write event until we hit an
	     * overflow condition, we need to force the event loop to poll
	     * until the condition changes.







|
|
|







945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
	return -1;
    }

    while (1) {
	SendSelectMessage(tsdPtr, UNSELECT, statePtr);

	/*
	 * Single fd operation: this proc is only called for a connected
	 * socket.
	 */

	written = send(statePtr->sockets->fd, buf, toWrite, 0);
	if (written != SOCKET_ERROR) {
	    /*
	     * Since Windows won't generate a new write event until we hit an
	     * overflow condition, we need to force the event loop to poll
	     * until the condition changes.
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
 *	Closes the socket.
 *
 *----------------------------------------------------------------------
 */

static int
TcpCloseProc(
    ClientData instanceData,	/* The socket to close. */
    TCL_UNUSED(Tcl_Interp *))
{
    TcpState *statePtr = (TcpState *)instanceData;
    /* TIP #218 */
    int errorCode = 0;
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);








|







1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
 *	Closes the socket.
 *
 *----------------------------------------------------------------------
 */

static int
TcpCloseProc(
    void *instanceData,	/* The socket to close. */
    TCL_UNUSED(Tcl_Interp *))
{
    TcpState *statePtr = (TcpState *)instanceData;
    /* TIP #218 */
    int errorCode = 0;
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
     * This may be called, if an async socket connect fails or is closed
     * between connect and thread action callback.
     */

    if (tsdPtr->pendingTcpState != NULL
	    && tsdPtr->pendingTcpState == statePtr) {
	/*
         * Get infoPtr lock, because this concerns the notifier thread.
         */

	WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

	tsdPtr->pendingTcpState = NULL;

	/*
         * Free list lock.
         */

	SetEvent(tsdPtr->socketListLock);
    }

    /*
     * TIP #218. Removed the code removing the structure from the global
     * socket list. This is now done by the thread action callbacks, and only







|
|






|
|







1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
     * This may be called, if an async socket connect fails or is closed
     * between connect and thread action callback.
     */

    if (tsdPtr->pendingTcpState != NULL
	    && tsdPtr->pendingTcpState == statePtr) {
	/*
	 * Get infoPtr lock, because this concerns the notifier thread.
	 */

	WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

	tsdPtr->pendingTcpState = NULL;

	/*
	 * Free list lock.
	 */

	SetEvent(tsdPtr->socketListLock);
    }

    /*
     * TIP #218. Removed the code removing the structure from the global
     * socket list. This is now done by the thread action callbacks, and only
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
 *	Shuts down one side of the socket.
 *
 *----------------------------------------------------------------------
 */

static int
TcpClose2Proc(
    ClientData instanceData,	/* The socket to close. */
    Tcl_Interp *interp,		/* For error reporting. */
    int flags)			/* Flags that indicate which side to close. */
{
    TcpState *statePtr = (TcpState *)instanceData;
    int readError = 0;
    int writeError = 0;








|







1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
 *	Shuts down one side of the socket.
 *
 *----------------------------------------------------------------------
 */

static int
TcpClose2Proc(
    void *instanceData,	/* The socket to close. */
    Tcl_Interp *interp,		/* For error reporting. */
    int flags)			/* Flags that indicate which side to close. */
{
    TcpState *statePtr = (TcpState *)instanceData;
    int readError = 0;
    int writeError = 0;

1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
 *	Changes attributes of the socket at the system level.
 *
 *----------------------------------------------------------------------
 */

static int
TcpSetOptionProc(
    ClientData instanceData,	/* Socket state. */
    Tcl_Interp *interp,		/* For error reporting - can be NULL. */
    const char *optionName,	/* Name of the option to set. */
    TCL_UNUSED(const char *) /*value*/)		/* New value for option. */
{
#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
    TcpState *statePtr = instanceData;
    SOCKET sock;







|







1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
 *	Changes attributes of the socket at the system level.
 *
 *----------------------------------------------------------------------
 */

static int
TcpSetOptionProc(
    void *instanceData,	/* Socket state. */
    Tcl_Interp *interp,		/* For error reporting - can be NULL. */
    const char *optionName,	/* Name of the option to set. */
    TCL_UNUSED(const char *) /*value*/)		/* New value for option. */
{
#ifdef TCL_FEATURE_KEEPALIVE_NAGLE
    TcpState *statePtr = instanceData;
    SOCKET sock;
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TcpGetOptionProc(
    ClientData instanceData,	/* Socket state. */
    Tcl_Interp *interp,		/* For error reporting - can be NULL. */
    const char *optionName,	/* Name of the option to retrieve the value
				 * for, or NULL to get all options and their
				 * values. */
    Tcl_DString *dsPtr)		/* Where to store the computed value;
				 * initialized by caller. */
{







|







1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TcpGetOptionProc(
    void *instanceData,	/* Socket state. */
    Tcl_Interp *interp,		/* For error reporting - can be NULL. */
    const char *optionName,	/* Name of the option to retrieve the value
				 * for, or NULL to get all options and their
				 * values. */
    Tcl_DString *dsPtr)		/* Where to store the computed value;
				 * initialized by caller. */
{
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
    /*
     * Go one step in async connect
     *
     * If any error is thrown save it as backround error to report eventually
     * below.
     */

    if (!GOT_BITS(statePtr->testFlags, TCP_ASYNC_TEST_MODE)) {
	WaitForConnect(statePtr, NULL);
    }

    sock = statePtr->sockets->fd;
    if (optionName != NULL) {
	len = strlen(optionName);
    }

    if ((len > 1) && (optionName[1] == 'e') &&
	    (strncmp(optionName, "-error", len) == 0)) {
	/*
         * Do not return any errors if async connect is running.
         */

	if (!GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING)) {
	    if (GOT_BITS(statePtr->flags, TCP_ASYNC_FAILED)) {
		/*
		 * In case of a failed async connect, eventually report the
		 * connect error only once.  Do not report the system error,
		 * as this comes again and again.







|











|
|







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
    /*
     * Go one step in async connect
     *
     * If any error is thrown save it as backround error to report eventually
     * below.
     */

    if (!GOT_BITS(statePtr->flags, TCP_ASYNC_TEST_MODE)) {
	WaitForConnect(statePtr, NULL);
    }

    sock = statePtr->sockets->fd;
    if (optionName != NULL) {
	len = strlen(optionName);
    }

    if ((len > 1) && (optionName[1] == 'e') &&
	    (strncmp(optionName, "-error", len) == 0)) {
	/*
	 * Do not return any errors if async connect is running.
	 */

	if (!GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING)) {
	    if (GOT_BITS(statePtr->flags, TCP_ASYNC_FAILED)) {
		/*
		 * In case of a failed async connect, eventually report the
		 * connect error only once.  Do not report the system error,
		 * as this comes again and again.
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

		/*
		 * Return error message.
		 */

		if (err) {
		    Tcl_WinConvertError(err);
		    Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(Tcl_GetErrno()),
                            -1);
		}
	    }
	}
	return TCL_OK;
    }

    if ((len > 1) && (optionName[1] == 'c') &&
	    (strncmp(optionName, "-connecting", len) == 0)) {
	Tcl_DStringAppend(dsPtr,
		GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING)
		? "1" : "0", -1);
        return TCL_OK;
    }

    if (interp != NULL
            && Tcl_GetVar2(interp, SUPPRESS_RDNS_VAR, NULL, 0) != NULL) {
	reverseDNS = NI_NUMERICHOST;
    }

    if ((len == 0) || ((len > 1) && (optionName[1] == 'p') &&
	    (strncmp(optionName, "-peername", len) == 0))) {
	address peername;
	socklen_t size = sizeof(peername);

	if (GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING)) {
	    /*
	     * In async connect output an empty string
	     */

	    if (len == 0) {
		Tcl_DStringAppendElement(dsPtr, "-peername");
		Tcl_DStringAppendElement(dsPtr, "");
	    } else {
		return TCL_OK;
	    }
	} else if (getpeername(sock, (LPSOCKADDR) &(peername.sa),
                &size) == 0) {
	    /*
	     * Peername fetch succeeded - output list
	     */

	    if (len == 0) {
		Tcl_DStringAppendElement(dsPtr, "-peername");
		Tcl_DStringStartSublist(dsPtr);







|
<















|




















|







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

		/*
		 * Return error message.
		 */

		if (err) {
		    Tcl_WinConvertError(err);
		    Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(Tcl_GetErrno()), -1);

		}
	    }
	}
	return TCL_OK;
    }

    if ((len > 1) && (optionName[1] == 'c') &&
	    (strncmp(optionName, "-connecting", len) == 0)) {
	Tcl_DStringAppend(dsPtr,
		GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING)
		? "1" : "0", -1);
        return TCL_OK;
    }

    if (interp != NULL
	    && Tcl_GetVar(interp, SUPPRESS_RDNS_VAR, 0) != NULL) {
	reverseDNS = NI_NUMERICHOST;
    }

    if ((len == 0) || ((len > 1) && (optionName[1] == 'p') &&
	    (strncmp(optionName, "-peername", len) == 0))) {
	address peername;
	socklen_t size = sizeof(peername);

	if (GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING)) {
	    /*
	     * In async connect output an empty string
	     */

	    if (len == 0) {
		Tcl_DStringAppendElement(dsPtr, "-peername");
		Tcl_DStringAppendElement(dsPtr, "");
	    } else {
		return TCL_OK;
	    }
	} else if (getpeername(sock, (LPSOCKADDR) &(peername.sa),
		&size) == 0) {
	    /*
	     * Peername fetch succeeded - output list
	     */

	    if (len == 0) {
		Tcl_DStringAppendElement(dsPtr, "-peername");
		Tcl_DStringStartSublist(dsPtr);
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
	    Tcl_DStringStartSublist(dsPtr);
	}
	if (GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING)) {
	    /*
	     * In async connect output an empty string
	     */

            found = 1;
	} else {
	    for (fds = statePtr->sockets; fds != NULL; fds = fds->next) {
		sock = fds->fd;
		size = sizeof(sockname);
		if (getsockname(sock, &(sockname.sa), &size) >= 0) {
		    int flags = reverseDNS;








|







1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
	    Tcl_DStringStartSublist(dsPtr);
	}
	if (GOT_BITS(statePtr->flags, TCP_ASYNC_PENDING)) {
	    /*
	     * In async connect output an empty string
	     */

	    found = 1;
	} else {
	    for (fds = statePtr->sockets; fds != NULL; fds = fds->next) {
		sock = fds->fd;
		size = sizeof(sockname);
		if (getsockname(sock, &(sockname.sa), &size) >= 0) {
		    int flags = reverseDNS;

1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
 *	already true.
 *
 *----------------------------------------------------------------------
 */

static void
TcpWatchProc(
    ClientData instanceData,	/* The socket state. */
    int mask)			/* Events of interest; an OR-ed combination of
				 * TCL_READABLE, TCL_WRITABLE and
				 * TCL_EXCEPTION. */
{
    TcpState *statePtr = (TcpState *)instanceData;

    /*







|







1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
 *	already true.
 *
 *----------------------------------------------------------------------
 */

static void
TcpWatchProc(
    void *instanceData,	/* The socket state. */
    int mask)			/* Events of interest; an OR-ed combination of
				 * TCL_READABLE, TCL_WRITABLE and
				 * TCL_EXCEPTION. */
{
    TcpState *statePtr = (TcpState *)instanceData;

    /*
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TcpGetHandleProc(
    ClientData instanceData,	/* The socket state. */
    TCL_UNUSED(int) /*direction*/,
    ClientData *handlePtr)	/* Where to store the handle. */
{
    TcpState *statePtr = (TcpState *)instanceData;

    *handlePtr = INT2PTR(statePtr->sockets->fd);
    return TCL_OK;
}








|

|







1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TcpGetHandleProc(
    void *instanceData,	/* The socket state. */
    TCL_UNUSED(int) /*direction*/,
    void **handlePtr)	/* Where to store the handle. */
{
    TcpState *statePtr = (TcpState *)instanceData;

    *handlePtr = INT2PTR(statePtr->sockets->fd);
    return TCL_OK;
}

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

    if (async_callback) {
        goto reenter;
    }

    for (statePtr->addr = statePtr->addrlist; statePtr->addr != NULL;
	    statePtr->addr = statePtr->addr->ai_next) {
        for (statePtr->myaddr = statePtr->myaddrlist;
                statePtr->myaddr != NULL;
                statePtr->myaddr = statePtr->myaddr->ai_next) {
	    /*
	     * No need to try combinations of local and remote addresses
	     * of different families.
	     */

	    if (statePtr->myaddr->ai_family != statePtr->addr->ai_family) {
		continue;
	    }

            /*
             * Close the socket if it is still open from the last unsuccessful
             * iteration.
             */

	    if (statePtr->sockets->fd != INVALID_SOCKET) {
		closesocket(statePtr->sockets->fd);
	    }

	    /*
             * Get statePtr lock.
             */

	    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

	    /*
	     * Reset last error from last try
	     */

	    statePtr->notifierConnectError = 0;
	    Tcl_SetErrno(0);

	    statePtr->sockets->fd = socket(statePtr->myaddr->ai_family,
                    SOCK_STREAM, 0);

	    /*
             * Free list lock.
             */

	    SetEvent(tsdPtr->socketListLock);

	    /*
             * Continue on socket creation error.
             */

	    if (statePtr->sockets->fd == INVALID_SOCKET) {
		Tcl_WinConvertError((DWORD) WSAGetLastError());
		continue;
	    }

	    /*
	     * Win-NT has a misfeature that sockets are inherited in child
	     * processes by default. Turn off the inherit bit.
	     */

	    SetHandleInformation((HANDLE) statePtr->sockets->fd,
                    HANDLE_FLAG_INHERIT, 0);

	    /*
	     * Set kernel space buffering
	     */

	    TclSockMinimumBuffers((void *) statePtr->sockets->fd,
                    TCP_BUFFER_SIZE);

	    /*
	     * Try to bind to a local port.
	     */

	    if (bind(statePtr->sockets->fd, statePtr->myaddr->ai_addr,
		    statePtr->myaddr->ai_addrlen) == SOCKET_ERROR) {
		Tcl_WinConvertError((DWORD) WSAGetLastError());
		continue;
	    }

	    /*
	     * For asynchroneous connect set the socket in nonblocking mode
	     * and activate connect notification
	     */

	    if (async_connect) {
		TcpState *statePtr2;
		int in_socket_list = 0;

		/*
                 * Get statePtr lock.
                 */

		WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

		/*
		 * Bugfig for 336441ed59 to not ignore notifications until the
		 * infoPtr is in the list.
		 * Check if my statePtr is already in the tsdPtr->socketList







|
|
|



















|
|











|


|
|




|
|












|






|












|








|
|







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

    if (async_callback) {
        goto reenter;
    }

    for (statePtr->addr = statePtr->addrlist; statePtr->addr != NULL;
	    statePtr->addr = statePtr->addr->ai_next) {
	for (statePtr->myaddr = statePtr->myaddrlist;
		statePtr->myaddr != NULL;
		statePtr->myaddr = statePtr->myaddr->ai_next) {
	    /*
	     * No need to try combinations of local and remote addresses
	     * of different families.
	     */

	    if (statePtr->myaddr->ai_family != statePtr->addr->ai_family) {
		continue;
	    }

            /*
             * Close the socket if it is still open from the last unsuccessful
             * iteration.
             */

	    if (statePtr->sockets->fd != INVALID_SOCKET) {
		closesocket(statePtr->sockets->fd);
	    }

	    /*
	     * Get statePtr lock.
	     */

	    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

	    /*
	     * Reset last error from last try
	     */

	    statePtr->notifierConnectError = 0;
	    Tcl_SetErrno(0);

	    statePtr->sockets->fd = socket(statePtr->myaddr->ai_family,
		    SOCK_STREAM, 0);

	    /*
	     * Free list lock.
	     */

	    SetEvent(tsdPtr->socketListLock);

	    /*
	     * Continue on socket creation error.
	     */

	    if (statePtr->sockets->fd == INVALID_SOCKET) {
		Tcl_WinConvertError((DWORD) WSAGetLastError());
		continue;
	    }

	    /*
	     * Win-NT has a misfeature that sockets are inherited in child
	     * processes by default. Turn off the inherit bit.
	     */

	    SetHandleInformation((HANDLE) statePtr->sockets->fd,
		    HANDLE_FLAG_INHERIT, 0);

	    /*
	     * Set kernel space buffering
	     */

	    TclSockMinimumBuffers((void *) statePtr->sockets->fd,
		    TCP_BUFFER_SIZE);

	    /*
	     * Try to bind to a local port.
	     */

	    if (bind(statePtr->sockets->fd, statePtr->myaddr->ai_addr,
		    statePtr->myaddr->ai_addrlen) == SOCKET_ERROR) {
		Tcl_WinConvertError((DWORD) WSAGetLastError());
		continue;
	    }

	    /*
	     * For asynchronous connect set the socket in nonblocking mode
	     * and activate connect notification
	     */

	    if (async_connect) {
		TcpState *statePtr2;
		int in_socket_list = 0;

		/*
		 * Get statePtr lock.
		 */

		WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

		/*
		 * Bugfig for 336441ed59 to not ignore notifications until the
		 * infoPtr is in the list.
		 * Check if my statePtr is already in the tsdPtr->socketList
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
		}
		if (!in_socket_list) {
		    tsdPtr->pendingTcpState = statePtr;
		}

		/*
		 * Set connect mask to connect events
                 *
		 * This is activated by a SOCKET_SELECT message to the
		 * notifier thread.
		 */

		SET_BITS(statePtr->selectEvents, FD_CONNECT);

		/*
		 * Free list lock.
		 */

		SetEvent(tsdPtr->socketListLock);

    		/*
                 * Activate accept notification.
                 */

		SendSelectMessage(tsdPtr, SELECT, statePtr);
	    }

	    /*
	     * Attempt to connect to the remote socket.
	     */







|












|
|
|







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
		}
		if (!in_socket_list) {
		    tsdPtr->pendingTcpState = statePtr;
		}

		/*
		 * Set connect mask to connect events
		 *
		 * This is activated by a SOCKET_SELECT message to the
		 * notifier thread.
		 */

		SET_BITS(statePtr->selectEvents, FD_CONNECT);

		/*
		 * Free list lock.
		 */

		SetEvent(tsdPtr->socketListLock);

		/*
		 * Activate accept notification.
		 */

		SendSelectMessage(tsdPtr, SELECT, statePtr);
	    }

	    /*
	     * Attempt to connect to the remote socket.
	     */
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
		 *
		 * Clear the reenter flag
		 */

		CLEAR_BITS(statePtr->flags, TCP_ASYNC_PENDING);

		/*
                 * Get statePtr lock.
                 */

		WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

		/*
                 * Get signaled connect error.
                 */

		Tcl_WinConvertError((DWORD) statePtr->notifierConnectError);

		/*
                 * Clear eventual connect flag.
                 */

		CLEAR_BITS(statePtr->selectEvents, FD_CONNECT);

		/*
                 * Free list lock.
                 */

		SetEvent(tsdPtr->socketListLock);
	    }

	    /*
	     * Clear the tsd socket list pointer if we did not wait for
	     * the FD_CONNECT asynchroneously
	     */

	    tsdPtr->pendingTcpState = NULL;

	    if (Tcl_GetErrno() == 0) {
		goto out;
	    }







|
|




|
|




|
|




|
|






|







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
		 *
		 * Clear the reenter flag
		 */

		CLEAR_BITS(statePtr->flags, TCP_ASYNC_PENDING);

		/*
		 * Get statePtr lock.
		 */

		WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

		/*
		 * Get signaled connect error.
		 */

		Tcl_WinConvertError((DWORD) statePtr->notifierConnectError);

		/*
		 * Clear eventual connect flag.
		 */

		CLEAR_BITS(statePtr->selectEvents, FD_CONNECT);

		/*
		 * Free list lock.
		 */

		SetEvent(tsdPtr->socketListLock);
	    }

	    /*
	     * Clear the tsd socket list pointer if we did not wait for
	     * the FD_CONNECT asynchronously
	     */

	    tsdPtr->pendingTcpState = NULL;

	    if (Tcl_GetErrno() == 0) {
		goto out;
	    }
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
	    /*
	     * Set up the select mask for read/write events.
	     */

	    statePtr->selectEvents = FD_WRITE|FD_READ;

	    /*
             * Get statePtr lock.
             */

	    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

	    /*
             * Signal ready readable and writable events.
             */

	    SET_BITS(statePtr->readyEvents, FD_WRITE | FD_READ);

	    /*
             * Flag error to event routine.
             */

	    SET_BITS(statePtr->flags, TCP_ASYNC_FAILED);

	    /*
             * Save connect error to be reported by 'fconfigure -error'.
             */

	    statePtr->connectError = Tcl_GetErrno();

	    /*
             * Free list lock.
             */

	    SetEvent(tsdPtr->socketListLock);
	}

	/*
	 * Error message on synchroneous connect
	 */







|
|




|
|




|
|




|
|




|
|







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
	    /*
	     * Set up the select mask for read/write events.
	     */

	    statePtr->selectEvents = FD_WRITE|FD_READ;

	    /*
	     * Get statePtr lock.
	     */

	    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

	    /*
	     * Signal ready readable and writable events.
	     */

	    SET_BITS(statePtr->readyEvents, FD_WRITE | FD_READ);

	    /*
	     * Flag error to event routine.
	     */

	    SET_BITS(statePtr->flags, TCP_ASYNC_FAILED);

	    /*
	     * Save connect error to be reported by 'fconfigure -error'.
	     */

	    statePtr->connectError = Tcl_GetErrno();

	    /*
	     * Free list lock.
	     */

	    SetEvent(tsdPtr->socketListLock);
	}

	/*
	 * Error message on synchroneous connect
	 */
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
 *	None.
 *
 *----------------------------------------------------------------------
 */

Tcl_Channel
Tcl_MakeTcpClientChannel(
    ClientData sock)		/* The socket to wrap up into a channel. */
{
    TcpState *statePtr;
    char channelName[SOCK_CHAN_LENGTH];
    ThreadSpecificData *tsdPtr;

    if (TclpHasSockets(NULL) != TCL_OK) {
	return NULL;







|







2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
 *	None.
 *
 *----------------------------------------------------------------------
 */

Tcl_Channel
Tcl_MakeTcpClientChannel(
    void *sock)		/* The socket to wrap up into a channel. */
{
    TcpState *statePtr;
    char channelName[SOCK_CHAN_LENGTH];
    ThreadSpecificData *tsdPtr;

    if (TclpHasSockets(NULL) != TCL_OK) {
	return NULL;
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
    Tcl_Interp *interp,		/* For error reporting - may be NULL. */
    const char *service,	/* Port number to open. */
    const char *myHost,		/* Name of local host. */
    unsigned int flags,		/* Flags. */
    Tcl_TcpAcceptProc *acceptProc,
				/* Callback for accepting connections from new
				 * clients. */
    ClientData acceptProcData)	/* Data for the callback. */
{
    SOCKET sock = INVALID_SOCKET;
    unsigned short chosenport = 0;
    struct addrinfo *addrlist = NULL;
    struct addrinfo *addrPtr;	/* Socket address to listen on. */
    TcpState *statePtr = NULL;	/* The returned value. */
    char channelName[SOCK_CHAN_LENGTH];







|







2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
    Tcl_Interp *interp,		/* For error reporting - may be NULL. */
    const char *service,	/* Port number to open. */
    const char *myHost,		/* Name of local host. */
    unsigned int flags,		/* Flags. */
    Tcl_TcpAcceptProc *acceptProc,
				/* Callback for accepting connections from new
				 * clients. */
    void *acceptProcData)	/* Data for the callback. */
{
    SOCKET sock = INVALID_SOCKET;
    unsigned short chosenport = 0;
    struct addrinfo *addrlist = NULL;
    struct addrinfo *addrPtr;	/* Socket address to listen on. */
    TcpState *statePtr = NULL;	/* The returned value. */
    char channelName[SOCK_CHAN_LENGTH];
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235

    if (TclSockGetPort(interp, service, "tcp", &port) != TCL_OK) {
	errorMsg = "invalid port number";
	goto error;
    }

    if (!TclCreateSocketAddress(interp, &addrlist, myHost, port, 1,
            &errorMsg)) {
	goto error;
    }

    for (addrPtr = addrlist; addrPtr != NULL; addrPtr = addrPtr->ai_next) {
	sock = socket(addrPtr->ai_family, addrPtr->ai_socktype,
                addrPtr->ai_protocol);
	if (sock == INVALID_SOCKET) {







|







2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227

    if (TclSockGetPort(interp, service, "tcp", &port) != TCL_OK) {
	errorMsg = "invalid port number";
	goto error;
    }

    if (!TclCreateSocketAddress(interp, &addrlist, myHost, port, 1,
	    &errorMsg)) {
	goto error;
    }

    for (addrPtr = addrlist; addrPtr != NULL; addrPtr = addrPtr->ai_next) {
	sock = socket(addrPtr->ai_family, addrPtr->ai_socktype,
                addrPtr->ai_protocol);
	if (sock == INVALID_SOCKET) {
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
	 *
	 * Bind should not be affected by the socket having already been
	 * set into nonblocking mode. If there is trouble, this is one
	 * place to look for bugs.
	 */

	if (bind(sock, addrPtr->ai_addr,
                addrPtr->ai_addrlen) == SOCKET_ERROR) {
	    Tcl_WinConvertError((DWORD) WSAGetLastError());
	    closesocket(sock);
	    continue;
	}
	if (port == 0 && chosenport == 0) {
	    address sockname;
	    socklen_t namelen = sizeof(sockname);







|







2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
	 *
	 * Bind should not be affected by the socket having already been
	 * set into nonblocking mode. If there is trouble, this is one
	 * place to look for bugs.
	 */

	if (bind(sock, addrPtr->ai_addr,
		addrPtr->ai_addrlen) == SOCKET_ERROR) {
	    Tcl_WinConvertError((DWORD) WSAGetLastError());
	    closesocket(sock);
	    continue;
	}
	if (port == 0 && chosenport == 0) {
	    address sockname;
	    socklen_t namelen = sizeof(sockname);
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
 *	None.
 *
 *----------------------------------------------------------------------
 */

static void
SocketExitHandler(
    TCL_UNUSED(ClientData))
{
    Tcl_MutexLock(&socketMutex);

    /*
     * Make sure the socket event handling window is cleaned-up for, at
     * most, this thread.
     */







|







2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
 *	None.
 *
 *----------------------------------------------------------------------
 */

static void
SocketExitHandler(
    TCL_UNUSED(void *))
{
    Tcl_MutexLock(&socketMutex);

    /*
     * Make sure the socket event handling window is cleaned-up for, at
     * most, this thread.
     */
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
 *	Adjusts the block time if needed.
 *
 *----------------------------------------------------------------------
 */

void
SocketSetupProc(
    TCL_UNUSED(ClientData),
    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
{
    TcpState *statePtr;
    Tcl_Time blockTime = { 0, 0 };
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    if (!GOT_BITS(flags, TCL_FILE_EVENTS)) {
	return;
    }

    /*
     * Check to see if there is a ready socket.	 If so, poll.
     */
    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
    for (statePtr = tsdPtr->socketList; statePtr != NULL;
	    statePtr = statePtr->nextPtr) {
	if (GOT_BITS(statePtr->readyEvents,
                statePtr->watchEvents | FD_CONNECT | FD_ACCEPT)) {
	    Tcl_SetMaxBlockTime(&blockTime);
	    break;
	}
    }
    SetEvent(tsdPtr->socketListLock);
}








|

















|







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
 *	Adjusts the block time if needed.
 *
 *----------------------------------------------------------------------
 */

void
SocketSetupProc(
    TCL_UNUSED(void *),
    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
{
    TcpState *statePtr;
    Tcl_Time blockTime = { 0, 0 };
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    if (!GOT_BITS(flags, TCL_FILE_EVENTS)) {
	return;
    }

    /*
     * Check to see if there is a ready socket.	 If so, poll.
     */
    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
    for (statePtr = tsdPtr->socketList; statePtr != NULL;
	    statePtr = statePtr->nextPtr) {
	if (GOT_BITS(statePtr->readyEvents,
		statePtr->watchEvents | FD_CONNECT | FD_ACCEPT)) {
	    Tcl_SetMaxBlockTime(&blockTime);
	    break;
	}
    }
    SetEvent(tsdPtr->socketListLock);
}

2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
 *	May queue an event.
 *
 *----------------------------------------------------------------------
 */

static void
SocketCheckProc(
    TCL_UNUSED(ClientData),
    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
{
    TcpState *statePtr;
    SocketEvent *evPtr;
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    if (!GOT_BITS(flags, TCL_FILE_EVENTS)) {







|







2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
 *	May queue an event.
 *
 *----------------------------------------------------------------------
 */

static void
SocketCheckProc(
    TCL_UNUSED(void *),
    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
{
    TcpState *statePtr;
    SocketEvent *evPtr;
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    if (!GOT_BITS(flags, TCL_FILE_EVENTS)) {
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
    /*
     * Handle connection requests directly.
     */

    if (GOT_BITS(statePtr->readyEvents, FD_ACCEPT)) {
	for (fds = statePtr->sockets; fds != NULL; fds = fds->next) {
	    /*
             * Accept the incoming connection request.
             */

	    len = sizeof(address);
	    newSocket = accept(fds->fd, &(addr.sa), &len);

	    /*
             * On Tcl server sockets with multiple OS fds we loop over the fds
	     * trying an accept() on each, so we expect INVALID_SOCKET.  There
	     * are also other network stack conditions that can result in
	     * FD_ACCEPT but a subsequent failure on accept() by the time we
	     * get around to it.
             *
	     * Access to sockets (acceptEventCount, readyEvents) in socketList
	     * is still protected by the lock (prevents reintroduction of
	     * SF Tcl Bug 3056775.
	     */

	    if (newSocket == INVALID_SOCKET) {
		/* int err = WSAGetLastError(); */







|
|





|




|







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
    /*
     * Handle connection requests directly.
     */

    if (GOT_BITS(statePtr->readyEvents, FD_ACCEPT)) {
	for (fds = statePtr->sockets; fds != NULL; fds = fds->next) {
	    /*
	     * Accept the incoming connection request.
	     */

	    len = sizeof(address);
	    newSocket = accept(fds->fd, &(addr.sa), &len);

	    /*
	     * On Tcl server sockets with multiple OS fds we loop over the fds
	     * trying an accept() on each, so we expect INVALID_SOCKET.  There
	     * are also other network stack conditions that can result in
	     * FD_ACCEPT but a subsequent failure on accept() by the time we
	     * get around to it.
	     *
	     * Access to sockets (acceptEventCount, readyEvents) in socketList
	     * is still protected by the lock (prevents reintroduction of
	     * SF Tcl Bug 3056775.
	     */

	    if (newSocket == INVALID_SOCKET) {
		/* int err = WSAGetLastError(); */
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
	    if (statePtr->acceptEventCount <= 0) {
		CLEAR_BITS(statePtr->readyEvents, FD_ACCEPT);
	    }

	    SetEvent(tsdPtr->socketListLock);

	    /*
             * Caution: TcpAccept() has the side-effect of evaluating the
	     * server accept script (via AcceptCallbackProc() in tclIOCmd.c),
	     * which can close the server socket and invalidate statePtr and
	     * fds. If TcpAccept() accepts a socket we must return immediately
	     * and let SocketCheckProc queue additional FD_ACCEPT events.
	     */

	    TcpAccept(fds, newSocket, addr);
	    return 1;
	}

	/*
         * Loop terminated with no sockets accepted; clear the ready mask so
	 * we can detect the next connection request. Note that connection
	 * requests are level triggered, so if there is a request already
	 * pending, a new event will be generated.
	 */

	statePtr->acceptEventCount = 0;
	CLEAR_BITS(statePtr->readyEvents, FD_ACCEPT);







|











|







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
	    if (statePtr->acceptEventCount <= 0) {
		CLEAR_BITS(statePtr->readyEvents, FD_ACCEPT);
	    }

	    SetEvent(tsdPtr->socketListLock);

	    /*
	     * Caution: TcpAccept() has the side-effect of evaluating the
	     * server accept script (via AcceptCallbackProc() in tclIOCmd.c),
	     * which can close the server socket and invalidate statePtr and
	     * fds. If TcpAccept() accepts a socket we must return immediately
	     * and let SocketCheckProc queue additional FD_ACCEPT events.
	     */

	    TcpAccept(fds, newSocket, addr);
	    return 1;
	}

	/*
	 * Loop terminated with no sockets accepted; clear the ready mask so
	 * we can detect the next connection request. Note that connection
	 * requests are level triggered, so if there is a request already
	 * pending, a new event will be generated.
	 */

	statePtr->acceptEventCount = 0;
	CLEAR_BITS(statePtr->readyEvents, FD_ACCEPT);
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
    TcpState *statePtr,
    SOCKET socket)
{
    TcpFdList *fds = statePtr->sockets;

    if (fds == NULL) {
	/*
         * Add the first FD.
         */

	statePtr->sockets = (TcpFdList *)Tcl_Alloc(sizeof(TcpFdList));
	fds = statePtr->sockets;
    } else {
	/*
         * Find end of list and append FD.
         */

	while (fds->next != NULL) {
	    fds = fds->next;
	}

	fds->next = (TcpFdList *)Tcl_Alloc(sizeof(TcpFdList));
	fds = fds->next;







|
|





|
|







2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
    TcpState *statePtr,
    SOCKET socket)
{
    TcpFdList *fds = statePtr->sockets;

    if (fds == NULL) {
	/*
	 * Add the first FD.
	 */

	statePtr->sockets = (TcpFdList *)Tcl_Alloc(sizeof(TcpFdList));
	fds = statePtr->sockets;
    } else {
	/*
	 * Find end of list and append FD.
	 */

	while (fds->next != NULL) {
	    fds = fds->next;
	}

	fds->next = (TcpFdList *)Tcl_Alloc(sizeof(TcpFdList));
	fds = fds->next;
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
    SendSelectMessage(tsdPtr, UNSELECT, statePtr);
    SendSelectMessage(tsdPtr, SELECT, statePtr);

    while (1) {
	int event_found;

	/*
         * Get statePtr lock.
         */

	WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

	/*
         * Check if event occured.
         */

	event_found = GOT_BITS(statePtr->readyEvents, events);

	/*
         * Free list lock.
         */

	SetEvent(tsdPtr->socketListLock);

	/*
         * Exit loop if event occured.
         */

	if (event_found) {
	    break;
	}

	/*
         * Exit loop if event did not occur but this is a non-blocking channel
         */

	if (statePtr->flags & TCP_NONBLOCKING) {
	    *errorCodePtr = EWOULDBLOCK;
	    result = 0;
	    break;
	}








|
|




|
|




|
|




|
|






|
|







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
    SendSelectMessage(tsdPtr, UNSELECT, statePtr);
    SendSelectMessage(tsdPtr, SELECT, statePtr);

    while (1) {
	int event_found;

	/*
	 * Get statePtr lock.
	 */

	WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

	/*
	 * Check if event occured.
	 */

	event_found = GOT_BITS(statePtr->readyEvents, events);

	/*
	 * Free list lock.
	 */

	SetEvent(tsdPtr->socketListLock);

	/*
	 * Exit loop if event occured.
	 */

	if (event_found) {
	    break;
	}

	/*
	 * Exit loop if event did not occur but this is a non-blocking channel
	 */

	if (statePtr->flags & TCP_NONBLOCKING) {
	    *errorCodePtr = EWOULDBLOCK;
	    result = 0;
	    break;
	}

3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
 *	Changes thread local list of valid channels.
 *
 *----------------------------------------------------------------------
 */

static void
TcpThreadActionProc(
    ClientData instanceData,
    int action)
{
    ThreadSpecificData *tsdPtr;
    TcpState *statePtr = (TcpState *)instanceData;
    int notifyCmd;

    if (action == TCL_CHANNEL_THREAD_INSERT) {







|







3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
 *	Changes thread local list of valid channels.
 *
 *----------------------------------------------------------------------
 */

static void
TcpThreadActionProc(
    void *instanceData,
    int action)
{
    ThreadSpecificData *tsdPtr;
    TcpState *statePtr = (TcpState *)instanceData;
    int notifyCmd;

    if (action == TCL_CHANNEL_THREAD_INSERT) {
Changes to win/tclWinTime.c.
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
} wideClick = {0, 0, 0.0};


/*
 * Declarations for functions defined later in this file.
 */

static void		StopCalibration(ClientData clientData);
static DWORD WINAPI	CalibrationThread(LPVOID arg);
static void 		UpdateTimeEachSecond(void);
static void		ResetCounterSamples(unsigned long long fileTime,
			    long long perfCounter, long long perfFreq);
static long long	AccumulateSample(long long perfCounter,
			    unsigned long long fileTime);
static void		NativeScaleTime(Tcl_Time* timebuf,
			    ClientData clientData);
static long long	NativeGetMicroseconds(void);
static void		NativeGetTime(Tcl_Time* timebuf,
			    ClientData clientData);

/*
 * TIP #233 (Virtualized Time): Data for the time hooks, if any.
 */

Tcl_GetTimeProc *tclGetTimeProcPtr = NativeGetTime;
Tcl_ScaleTimeProc *tclScaleTimeProcPtr = NativeScaleTime;
ClientData tclTimeClientData = NULL;

/*
 * Inlined version of Tcl_GetTime.
 */

static inline void
GetTime(







|







|


|







|







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
} wideClick = {0, 0, 0.0};


/*
 * Declarations for functions defined later in this file.
 */

static void		StopCalibration(void *clientData);
static DWORD WINAPI	CalibrationThread(LPVOID arg);
static void 		UpdateTimeEachSecond(void);
static void		ResetCounterSamples(unsigned long long fileTime,
			    long long perfCounter, long long perfFreq);
static long long	AccumulateSample(long long perfCounter,
			    unsigned long long fileTime);
static void		NativeScaleTime(Tcl_Time* timebuf,
			    void *clientData);
static long long	NativeGetMicroseconds(void);
static void		NativeGetTime(Tcl_Time* timebuf,
			    void *clientData);

/*
 * TIP #233 (Virtualized Time): Data for the time hooks, if any.
 */

Tcl_GetTimeProc *tclGetTimeProcPtr = NativeGetTime;
Tcl_ScaleTimeProc *tclScaleTimeProcPtr = NativeScaleTime;
void *tclTimeClientData = NULL;

/*
 * Inlined version of Tcl_GetTime.
 */

static inline void
GetTime(
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
 *
 *----------------------------------------------------------------------
 */

static void
NativeScaleTime(
    TCL_UNUSED(Tcl_Time *),
    TCL_UNUSED(ClientData))
{
    /*
     * Native scale is 1:1. Nothing is done.
     */
}

/*







|







407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
 *
 *----------------------------------------------------------------------
 */

static void
NativeScaleTime(
    TCL_UNUSED(Tcl_Time *),
    TCL_UNUSED(void *))
{
    /*
     * Native scale is 1:1. Nothing is done.
     */
}

/*
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
 *
 *----------------------------------------------------------------------
 */

static void
NativeGetTime(
    Tcl_Time *timePtr,
    TCL_UNUSED(ClientData))
{
    long long usecSincePosixEpoch;

    /*
     * Try to use high resolution timer.
     */








|







673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
 *
 *----------------------------------------------------------------------
 */

static void
NativeGetTime(
    Tcl_Time *timePtr,
    TCL_UNUSED(void *))
{
    long long usecSincePosixEpoch;

    /*
     * Try to use high resolution timer.
     */

720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
 *----------------------------------------------------------------------
 */

void TclWinResetTimerResolution(void);

static void
StopCalibration(
    TCL_UNUSED(ClientData))
{
    SetEvent(timeInfo.exitEvent);

    /*
     * If Tcl_Finalize was called from DllMain, the calibration thread is in a
     * paused state so we need to timeout and continue.
     */







|







720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
 *----------------------------------------------------------------------
 */

void TclWinResetTimerResolution(void);

static void
StopCalibration(
    TCL_UNUSED(void *))
{
    SetEvent(timeInfo.exitEvent);

    /*
     * If Tcl_Finalize was called from DllMain, the calibration thread is in a
     * paused state so we need to timeout and continue.
     */
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
 *----------------------------------------------------------------------
 */

void
Tcl_SetTimeProc(
    Tcl_GetTimeProc *getProc,
    Tcl_ScaleTimeProc *scaleProc,
    ClientData clientData)
{
    tclGetTimeProcPtr = getProc;
    tclScaleTimeProcPtr = scaleProc;
    tclTimeClientData = clientData;
}

/*







|







1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
 *----------------------------------------------------------------------
 */

void
Tcl_SetTimeProc(
    Tcl_GetTimeProc *getProc,
    Tcl_ScaleTimeProc *scaleProc,
    void *clientData)
{
    tclGetTimeProcPtr = getProc;
    tclScaleTimeProcPtr = scaleProc;
    tclTimeClientData = clientData;
}

/*
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
 *----------------------------------------------------------------------
 */

void
Tcl_QueryTimeProc(
    Tcl_GetTimeProc **getProc,
    Tcl_ScaleTimeProc **scaleProc,
    ClientData *clientData)
{
    if (getProc) {
	*getProc = tclGetTimeProcPtr;
    }
    if (scaleProc) {
	*scaleProc = tclScaleTimeProcPtr;
    }







|







1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
 *----------------------------------------------------------------------
 */

void
Tcl_QueryTimeProc(
    Tcl_GetTimeProc **getProc,
    Tcl_ScaleTimeProc **scaleProc,
    void **clientData)
{
    if (getProc) {
	*getProc = tclGetTimeProcPtr;
    }
    if (scaleProc) {
	*scaleProc = tclScaleTimeProcPtr;
    }