Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Merge the reforms of dgp-trunk-read branch into trunk. (port of dgp-read-bytes) Large overhaul of I/O read operations - Protects integer overflow of buffers, reusing append machinery - Forces -buffersize changes to take place when commanded - Uses assertions to simplify code in "can't happen" situations - Eliminated duplication of -translation processing - Fixes bugs io-35.18b and io-35.20 |
|---|---|
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA1: |
d17b42d5cb7438ab9ed2212ae2ffce80 |
| User & Date: | dgp 2014-05-08 17:38:09.521 |
Context
|
2014-05-09
| ||
| 10:10 | Make Cygwin's "configure" work from another directory than /unix. (Not everything works this way!) check-in: b2c0b88e3a user: jan.nijtmans tags: trunk | |
|
2014-05-08
| ||
| 17:46 | merge trunk check-in: e34a130b6f user: dgp tags: dgp-refactor | |
| 17:38 | Merge the reforms of dgp-trunk-read branch into trunk. (port of dgp-read-bytes) Large overhaul ... check-in: d17b42d5cb user: dgp tags: trunk | |
| 17:30 | Merge the reforms of dgp-read-bytes branch into 8.5+ releases. Large overhaul of I/O read opera... check-in: ea760ac244 user: dgp tags: core-8-5-branch | |
| 16:21 | merge trunk Closed-Leaf check-in: 30ae8cb927 user: dgp tags: dgp-trunk-read | |
| 16:21 | silence compiler warning check-in: 1b033660ab user: dgp tags: trunk | |
Changes
Changes to generic/tclIO.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /* * tclIO.c -- * * This file provides the generic portions (those that are the same on * all platforms and for all channel types) of Tcl's IO facilities. * * Copyright (c) 1998-2000 Ajuba Solutions * Copyright (c) 1995-1997 Sun Microsystems, Inc. * * 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 "tclIO.h" | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /* * tclIO.c -- * * This file provides the generic portions (those that are the same on * all platforms and for all channel types) of Tcl's IO facilities. * * Copyright (c) 1998-2000 Ajuba Solutions * Copyright (c) 1995-1997 Sun Microsystems, Inc. * Contributions from Don Porter, NIST, 2014. (not subject to US copyright) * * 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 "tclIO.h" |
| ︙ | ︙ | |||
175 176 177 178 179 180 181 | Channel *chanPtr); static int CloseChannel(Tcl_Interp *interp, Channel *chanPtr, int errorCode); static int CloseChannelPart(Tcl_Interp *interp, Channel *chanPtr, int errorCode, int flags); static int CloseWrite(Tcl_Interp *interp, Channel *chanPtr); static void CommonGetsCleanup(Channel *chanPtr); | < < | > | | | | 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 | Channel *chanPtr); static int CloseChannel(Tcl_Interp *interp, Channel *chanPtr, int errorCode); static int CloseChannelPart(Tcl_Interp *interp, Channel *chanPtr, int errorCode, int flags); static int CloseWrite(Tcl_Interp *interp, Channel *chanPtr); static void CommonGetsCleanup(Channel *chanPtr); static int CopyBuffer(Channel *chanPtr, char *result, int space); static int CopyData(CopyState *csPtr, int mask); static void CopyEventProc(ClientData clientData, int mask); static void CreateScriptRecord(Tcl_Interp *interp, Channel *chanPtr, int mask, Tcl_Obj *scriptPtr); static void DeleteChannelTable(ClientData clientData, Tcl_Interp *interp); static void DeleteScriptRecord(Tcl_Interp *interp, Channel *chanPtr, int mask); static int DetachChannel(Tcl_Interp *interp, Tcl_Channel chan); static void DiscardInputQueued(ChannelState *statePtr, int discardSavedBuffers); static void DiscardOutputQueued(ChannelState *chanPtr); static int DoRead(Channel *chanPtr, char *dst, int bytesToRead, int allowShortReads); static int DoReadChars(Channel *chan, Tcl_Obj *objPtr, int toRead, int appendFlag); static int FilterInputBytes(Channel *chanPtr, GetsState *statePtr); static int FlushChannel(Tcl_Interp *interp, Channel *chanPtr, int calledFromAsyncFlush); static int TclGetsObjBinary(Tcl_Channel chan, Tcl_Obj *objPtr); static Tcl_Encoding GetBinaryEncoding(); static void FreeBinaryEncoding(ClientData clientData); static Tcl_HashTable * GetChannelTable(Tcl_Interp *interp); static int GetInput(Channel *chanPtr); static int HaveVersion(const Tcl_ChannelType *typePtr, Tcl_ChannelTypeVersion minimumVersion); static void PeekAhead(Channel *chanPtr, char **dstEndPtr, GetsState *gsPtr); static int ReadBytes(ChannelState *statePtr, Tcl_Obj *objPtr, int charsLeft); static int ReadChars(ChannelState *statePtr, Tcl_Obj *objPtr, int charsLeft, int *factorPtr); static void RecycleBuffer(ChannelState *statePtr, ChannelBuffer *bufPtr, int mustDiscard); static int StackSetBlockMode(Channel *chanPtr, int mode); static int SetBlockMode(Tcl_Interp *interp, Channel *chanPtr, int mode); static void StopCopy(CopyState *csPtr); static void TranslateInputEOL(ChannelState *statePtr, char *dst, const char *src, int *dstLenPtr, int *srcLenPtr); static void UpdateInterest(Channel *chanPtr); static int Write(Channel *chanPtr, const char *src, int srcLen, Tcl_Encoding encoding); static Tcl_Obj * FixLevelCode(Tcl_Obj *msg); static void SpliceChannel(Tcl_Channel chan); static void CutChannel(Tcl_Channel chan); |
| ︙ | ︙ | |||
1778 1779 1780 1781 1782 1783 1784 1785 |
* the channel itself. We use the buffers in the channel below the new
* transformation to hold the data. In the future this allows us to write
* transformations which pre-read data and push the unused part back when
* they are going away.
*/
if (((mask & TCL_READABLE) != 0) && (statePtr->inQueueHead != NULL)) {
/*
| > < | > | | | | < | 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 |
* the channel itself. We use the buffers in the channel below the new
* transformation to hold the data. In the future this allows us to write
* transformations which pre-read data and push the unused part back when
* they are going away.
*/
if (((mask & TCL_READABLE) != 0) && (statePtr->inQueueHead != NULL)) {
/*
* When statePtr->inQueueHead is not NULL, we know
* prevChanPtr->inQueueHead must be NULL.
*/
assert(prevChanPtr->inQueueHead == NULL);
assert(prevChanPtr->inQueueTail == NULL);
prevChanPtr->inQueueHead = statePtr->inQueueHead;
prevChanPtr->inQueueTail = statePtr->inQueueTail;
statePtr->inQueueHead = NULL;
statePtr->inQueueTail = NULL;
}
chanPtr = ckalloc(sizeof(Channel));
|
| ︙ | ︙ | |||
2369 2370 2371 2372 2373 2374 2375 |
if (mustDiscard) {
ReleaseChannelBuffer(bufPtr);
return;
}
/*
| | | | | 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 |
if (mustDiscard) {
ReleaseChannelBuffer(bufPtr);
return;
}
/*
* Only save buffers which have the requested buffersize for the
* channel. This is to honor dynamic changes of the buffersize
* made by the user.
*/
if ((bufPtr->bufLength - BUFFER_PADDING) != statePtr->bufSize) {
ReleaseChannelBuffer(bufPtr);
return;
}
/*
* Only save buffers for the input queue if the channel is readable.
*/
|
| ︙ | ︙ | |||
4172 4173 4174 4175 4176 4177 4178 | } result |= Tcl_UtfToExternal(NULL, encoding, nl, nlLen, statePtr->outputEncodingFlags, &statePtr->outputEncodingState, dst, dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL); | | < < | 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 | } result |= Tcl_UtfToExternal(NULL, encoding, nl, nlLen, statePtr->outputEncodingFlags, &statePtr->outputEncodingState, dst, dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL); assert (srcRead == nlLen); bufPtr->nextAdded += dstWrote; src++; srcLen--; total += dstWrote; dst += dstWrote; dstLen -= dstWrote; |
| ︙ | ︙ | |||
4979 4980 4981 4982 4983 4984 4985 |
FilterInputBytes(
Channel *chanPtr, /* Channel to read. */
GetsState *gsPtr) /* Current state of gets operation. */
{
ChannelState *statePtr = chanPtr->state;
/* State info for channel */
ChannelBuffer *bufPtr;
| | | 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 |
FilterInputBytes(
Channel *chanPtr, /* Channel to read. */
GetsState *gsPtr) /* Current state of gets operation. */
{
ChannelState *statePtr = chanPtr->state;
/* State info for channel */
ChannelBuffer *bufPtr;
char *raw, *dst;
int offset, toRead, dstNeeded, spaceLeft, result, rawLen;
Tcl_Obj *objPtr;
#define ENCODING_LINESIZE 20 /* Lower bound on how many bytes to convert at
* a time. Since we don't know a priori how
* many bytes of storage this many source
* bytes will use, we actually need at least
* ENCODING_LINESIZE * TCL_MAX_UTF bytes of
|
| ︙ | ︙ | |||
5041 5042 5043 5044 5045 5046 5047 |
/*
* Convert some of the bytes from the channel buffer to UTF-8. Space in
* objPtr's string rep is used to hold the UTF-8 characters. Grow the
* string rep if we need more space.
*/
| | < | 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 |
/*
* Convert some of the bytes from the channel buffer to UTF-8. Space in
* objPtr's string rep is used to hold the UTF-8 characters. Grow the
* string rep if we need more space.
*/
raw = RemovePoint(bufPtr);
rawLen = BytesLeft(bufPtr);
dst = *gsPtr->dstPtr;
offset = dst - objPtr->bytes;
toRead = ENCODING_LINESIZE;
if (toRead > rawLen) {
toRead = rawLen;
|
| ︙ | ︙ | |||
5395 5396 5397 5398 5399 5400 5401 |
if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
goto done;
}
ResetFlag(statePtr, CHANNEL_BLOCKED);
}
| < < < < < < < < < < < < < < < < < < < < < | | | | | | > | | | < < < < < < < < < < < < | 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 |
if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
goto done;
}
ResetFlag(statePtr, CHANNEL_BLOCKED);
}
/*
* Now go to the driver to get as much as is possible to
* fill the remaining request. Do all the error handling by
* ourselves. The code was stolen from 'GetInput' and
* slightly adapted (different return value here).
*
* The case of 'bytesToRead == 0' at this point cannot
* happen.
*/
nread = ChanRead(chanPtr, bufPtr + copied,
bytesToRead - copied, &result);
if (nread > 0) {
/*
* If we get a short read, signal up that we may be BLOCKED.
* We should avoid calling the driver because on some
* platforms we will block in the low level reading code even
* though the channel is set into nonblocking mode.
*/
if (nread < (bytesToRead - copied)) {
SetFlag(statePtr, CHANNEL_BLOCKED);
}
} else if (nread == 0) {
SetFlag(statePtr, CHANNEL_EOF);
statePtr->inputEncodingFlags |= TCL_ENCODING_END;
} else if (nread < 0) {
if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
if (copied > 0) {
|
| ︙ | ︙ | |||
5578 5579 5580 5581 5582 5583 5584 |
* will be appended to the object. Otherwise,
* the data will replace the existing contents
* of the object. */
{
ChannelState *statePtr = chanPtr->state;
/* State info for channel */
ChannelBuffer *bufPtr;
| | > > > > > | > > < < < < < < < | | | < | 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 |
* will be appended to the object. Otherwise,
* the data will replace the existing contents
* of the object. */
{
ChannelState *statePtr = chanPtr->state;
/* State info for channel */
ChannelBuffer *bufPtr;
int factor, copied, copiedNow, result;
Tcl_Encoding encoding;
int binaryMode;
#define UTF_EXPANSION_FACTOR 1024
/*
* This operation should occur at the top of a channel stack.
*/
chanPtr = statePtr->topChanPtr;
encoding = statePtr->encoding;
factor = UTF_EXPANSION_FACTOR;
Tcl_Preserve(chanPtr);
binaryMode = (encoding == NULL)
&& (statePtr->inputTranslation == TCL_TRANSLATE_LF)
&& (statePtr->inEofChar == '\0');
if (appendFlag == 0) {
if (binaryMode) {
Tcl_SetByteArrayLength(objPtr, 0);
} else {
Tcl_SetObjLength(objPtr, 0);
/*
* We're going to access objPtr->bytes directly, so we must ensure
* that this is actually a string object (otherwise it might have
* been pure Unicode).
*
* Probably not needed anymore.
*/
TclGetString(objPtr);
}
}
for (copied = 0; (unsigned) toRead > 0; ) {
copiedNow = -1;
if (statePtr->inQueueHead != NULL) {
if (binaryMode) {
copiedNow = ReadBytes(statePtr, objPtr, toRead);
} else {
copiedNow = ReadChars(statePtr, objPtr, toRead, &factor);
}
/*
* If the current buffer is empty recycle it.
*/
bufPtr = statePtr->inQueueHead;
|
| ︙ | ︙ | |||
5670 5671 5672 5673 5674 5675 5676 |
} else {
copied += copiedNow;
toRead -= copiedNow;
}
}
ResetFlag(statePtr, CHANNEL_BLOCKED);
| < < < < < | 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 |
} else {
copied += copiedNow;
toRead -= copiedNow;
}
}
ResetFlag(statePtr, CHANNEL_BLOCKED);
/*
* Update the notifier state so we don't block while there is still data
* in the buffers.
*/
done:
|
| ︙ | ︙ | |||
5710 5711 5712 5713 5714 5715 5716 | * are stored in objPtr as a ByteArray object. EOL and EOF translation * are done. * * 'bytesToRead' can safely be a very large number because space is only * allocated to hold data read from the channel as needed. * * Results: | | | < < < > | < < < < < | < < < < < < | < | < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < | < | | 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 |
* are stored in objPtr as a ByteArray object. EOL and EOF translation
* are done.
*
* 'bytesToRead' can safely be a very large number because space is only
* allocated to hold data read from the channel as needed.
*
* Results:
* The return value is the number of bytes appended to the object, or
* -1 to indicate that zero bytes were read due to an EOF.
*
* Side effects:
* The storage of bytes in objPtr can cause (re-)allocation of memory.
*
*---------------------------------------------------------------------------
*/
static int
ReadBytes(
ChannelState *statePtr, /* State of the channel to read. */
Tcl_Obj *objPtr, /* Input data is appended to this ByteArray
* object. Its length is how much space has
* been allocated to hold data, not how many
* bytes of data have been stored in the
* object. */
int bytesToRead) /* Maximum number of bytes to store, or < 0 to
* get all available bytes. Bytes are obtained
* from the first buffer in the queue - even
* if this number is larger than the number of
* bytes available in the first buffer, only
* the bytes from the first buffer are
* returned. */
{
ChannelBuffer *bufPtr = statePtr->inQueueHead;
int srcLen = BytesLeft(bufPtr);
int toRead = bytesToRead>srcLen || bytesToRead<0 ? srcLen : bytesToRead;
TclAppendBytesToByteArray(objPtr, (unsigned char *) RemovePoint(bufPtr),
toRead);
bufPtr->nextRemoved += toRead;
return toRead;
}
/*
*---------------------------------------------------------------------------
*
* ReadChars --
*
|
| ︙ | ︙ | |||
5838 5839 5840 5841 5842 5843 5844 |
int charsToRead, /* Maximum number of characters to store, or
* -1 to get all available characters.
* Characters are obtained from the first
* buffer in the queue -- even if this number
* is larger than the number of characters
* available in the first buffer, only the
* characters from the first buffer are
| | | | | | | < < | | < | < < | | | | > > > | < | > > | > | > | | > > > > > > > > | | | < > > > > > > > | < > > > > > | | | < | < < < < | > > > > | > | > > | < < | | > > > > > | > | > > > > > | | | | > | < | | < | | < < < < < < < | < > | > | > > > > > > | | < < < | < > > > | | < | < | > > | | < > | | < > > > | > > | > > > > | > > > > > > | > > > > > > | | | | < | > | | | > > | | > > > < > > > > > | | | < > | > | | > | > | > > | > > > > > > > > | | > | | > | | < < < | < < | > > > > > > > > > | > > > | < < > > | | < | | | > > > | < < > > > > > > | | | > > | < > > > | > > > > | | > | | | | | | | | | | | | | | | | | | | | | > | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | > | < | | < | < | | < | | | | < < > > | | > > > > > | | < < | | > | | | < | | | > > > > > | > > > > > > | < < | | | < < | | < < | | < > | < > | | | < | | | | < | | > | > > > > | > > > > | | < | < | < | < | | < < > > > > | | > | | | | < < < | | | < < | < > | | < > > | | < < < < | 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 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 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 |
int charsToRead, /* Maximum number of characters to store, or
* -1 to get all available characters.
* Characters are obtained from the first
* buffer in the queue -- even if this number
* is larger than the number of characters
* available in the first buffer, only the
* characters from the first buffer are
* returned. The execption is when there is
* not any complete character in the first
* buffer. In that case, a recursive call
* effectively obtains chars from the
* second buffer. */
int *factorPtr) /* On input, contains a guess of how many
* bytes need to be allocated to hold the
* result of converting N source bytes to
* UTF-8. On output, contains another guess
* based on the data seen so far. */
{
Tcl_Encoding encoding = statePtr->encoding? statePtr->encoding
: GetBinaryEncoding();
Tcl_EncodingState savedState = statePtr->inputEncodingState;
ChannelBuffer *bufPtr = statePtr->inQueueHead;
int savedIEFlags = statePtr->inputEncodingFlags;
int savedFlags = statePtr->flags;
char *dst, *src = RemovePoint(bufPtr);
int dstLimit, numBytes, srcLen = BytesLeft(bufPtr);
/*
* One src byte can yield at most one character. So when the
* number of src bytes we plan to read is less than the limit on
* character count to be read, clearly we will remain within that
* limit, and we can use the value of "srcLen" as a tighter limit
* for sizing receiving buffers.
*/
int toRead = ((unsigned) charsToRead > srcLen) ? srcLen : charsToRead;
/*
* 'factor' is how much we guess that the bytes in the source buffer will
* expand when converted to UTF-8 chars. This guess comes from analyzing
* how many characters were produced by the previous pass.
*/
int factor = *factorPtr;
int dstNeeded = TCL_UTF_MAX - 1 + toRead * factor / UTF_EXPANSION_FACTOR;
(void) TclGetStringFromObj(objPtr, &numBytes);
Tcl_AppendToObj(objPtr, NULL, dstNeeded);
if (toRead == srcLen) {
unsigned int size;
dst = TclGetStringStorage(objPtr, &size) + numBytes;
dstNeeded = size - numBytes;
} else {
dst = TclGetString(objPtr) + numBytes;
}
/*
* This routine is burdened with satisfying several constraints.
* It cannot append more than 'charsToRead` chars onto objPtr.
* This is measured after encoding and translation transformations
* are completed. There is no precise number of src bytes that can
* be associated with the limit. Yet, when we are done, we must know
* precisely the number of src bytes that were consumed to produce
* the appended chars, so that all subsequent bytes are left in
* the buffers for future read operations.
*
* The consequence is that we have no choice but to implement a
* "trial and error" approach, where in general we may need to
* perform transformations and copies multiple times to achieve
* a consistent set of results. This takes the shape of a loop.
*/
dstLimit = dstNeeded + 1;
while (1) {
int dstDecoded, dstRead, dstWrote, srcRead, numChars;
/*
* Perform the encoding transformation. Read no more than
* srcLen bytes, write no more than dstLimit bytes.
*/
int code = Tcl_ExternalToUtf(NULL, encoding, src, srcLen,
statePtr->inputEncodingFlags & (bufPtr->nextPtr
? ~0 : ~TCL_ENCODING_END), &statePtr->inputEncodingState,
dst, dstLimit, &srcRead, &dstDecoded, &numChars);
/*
* Perform the translation transformation in place. Read no more
* than the dstDecoded bytes the encoding transformation actually
* produced. Capture the number of bytes written in dstWrote.
* Capture the number of bytes actually consumed in dstRead.
*/
dstWrote = dstLimit;
dstRead = dstDecoded;
TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead);
if (dstRead < dstDecoded) {
/*
* The encoding transformation produced bytes that the
* translation transformation did not consume. Why did
* this happen?
*/
if (statePtr->inEofChar && dst[dstRead] == statePtr->inEofChar) {
/*
* 1) There's an eof char set on the channel, and
* we saw it and stopped translating at that point.
*
* NOTE the bizarre spec of TranslateInputEOL in this case.
* Clearly the eof char had to be read in order to account
* for the stopping, but the value of dstRead does not
* include it.
*
* Also rather bizarre, our caller can only notice an
* EOF condition if we return the value -1 as the number
* of chars read. This forces us to perform a 2-call
* dance where the first call can read all the chars
* up to the eof char, and the second call is solely
* for consuming the encoded eof char then pointed at
* by src so that we can return that magic -1 value.
* This seems really wasteful, especially since
* the first decoding pass of each call is likely to
* decode many bytes beyond that eof char that's all we
* care about.
*/
if (dstRead == 0) {
/*
* Curious choice in the eof char handling. We leave
* the eof char in the buffer. So, no need to compute
* a proper srcRead value. At this point, there
* are no chars before the eof char in the buffer.
*/
Tcl_SetObjLength(objPtr, numBytes);
return -1;
}
{
/*
* There are chars leading the buffer before the eof
* char. Adjust the dstLimit so we go back and read
* only those and do not encounter the eof char this
* time.
*/
dstLimit = dstRead + TCL_UTF_MAX;
statePtr->flags = savedFlags;
statePtr->inputEncodingFlags = savedIEFlags;
statePtr->inputEncodingState = savedState;
continue;
}
}
/*
* 2) The other way to read fewer bytes than are decoded
* is when the final byte is \r and we're in a CRLF
* translation mode so we cannot decide whether to
* record \r or \n yet.
*/
assert(dstRead + 1 == dstDecoded);
assert(dst[dstRead] == '\r');
assert(statePtr->inputTranslation == TCL_TRANSLATE_CRLF);
if (dstWrote > 0) {
/*
* There are chars we can read before we hit the bare cr.
* Go back with a smaller dstLimit so we get them in the
* next pass, compute a matching srcRead, and don't end
* up back here in this call.
*/
dstLimit = dstRead + TCL_UTF_MAX;
statePtr->flags = savedFlags;
statePtr->inputEncodingFlags = savedIEFlags;
statePtr->inputEncodingState = savedState;
continue;
}
assert(dstWrote == 0);
assert(dstRead == 0);
assert(dstDecoded == 1);
/*
* We decoded only the bare cr, and we cannot read a
* translated char from that alone. We have to know what's
* next. So why do we only have the one decoded char?
*/
if (code != TCL_OK) {
char buffer[TCL_UTF_MAX + 2];
int read, decoded, count;
/*
* Didn't get everything the buffer could offer
*/
statePtr->flags = savedFlags;
statePtr->inputEncodingFlags = savedIEFlags;
statePtr->inputEncodingState = savedState;
Tcl_ExternalToUtf(NULL, encoding, src, srcLen,
statePtr->inputEncodingFlags & (bufPtr->nextPtr
? ~0 : ~TCL_ENCODING_END), &statePtr->inputEncodingState,
buffer, TCL_UTF_MAX + 2, &read, &decoded, &count);
if (count == 2) {
if (buffer[1] == '\n') {
/* \r\n translate to \n */
dst[0] = '\n';
bufPtr->nextRemoved += read;
} else {
dst[0] = '\r';
bufPtr->nextRemoved += srcRead;
}
dst[1] = '\0';
statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
Tcl_SetObjLength(objPtr, numBytes + 1);
return 1;
}
} else if (statePtr->flags & CHANNEL_EOF) {
/*
* The bare \r is the only char and we will never read
* a subsequent char to make the determination.
*/
dst[0] = '\r';
bufPtr->nextRemoved = bufPtr->nextAdded;
Tcl_SetObjLength(objPtr, numBytes + 1);
return 1;
}
/* FALL THROUGH - get more data (dstWrote == 0) */
}
/*
* The translation transformation can only reduce the number
* of chars when it converts \r\n into \n. The reduction in
* the number of chars is the difference in bytes read and written.
*/
numChars -= (dstRead - dstWrote);
if (charsToRead > 0 && numChars > charsToRead) {
/*
* We read more chars than allowed. Reset limits to
* prevent that and try again.
*/
dstLimit = Tcl_UtfAtIndex(dst, charsToRead + 1) - dst;
statePtr->flags = savedFlags;
statePtr->inputEncodingFlags = savedIEFlags;
statePtr->inputEncodingState = savedState;
continue;
}
if (dstWrote == 0) {
/*
* We were not able to read any chars. Maybe there were
* not enough src bytes to decode into a char. Maybe
* a lone \r could not be translated (crlf mode). Need
* to combine any unused src bytes we have in the first
* buffer with subsequent bytes to try again.
*/
ChannelBuffer *nextPtr = bufPtr->nextPtr;
if (nextPtr == NULL) {
if (srcLen > 0) {
SetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
}
Tcl_SetObjLength(objPtr, numBytes);
return -1;
}
/*
* Space is made at the beginning of the buffer to copy the
* previous unused bytes there. Check first if the buffer we
* are using actually has enough space at its beginning for
* the data we are copying. Because if not we will write over
* the buffer management information, especially the 'nextPtr'.
*
* Note that the BUFFER_PADDING (See AllocChannelBuffer) is
* used to prevent exactly this situation. I.e. it should never
* happen. Therefore it is ok to panic should it happen despite
* the precautions.
*/
if (nextPtr->nextRemoved - srcLen < 0) {
Tcl_Panic("Buffer Underflow, BUFFER_PADDING not enough");
}
nextPtr->nextRemoved -= srcLen;
memcpy(RemovePoint(nextPtr), src, (size_t) srcLen);
RecycleBuffer(statePtr, bufPtr, 0);
statePtr->inQueueHead = nextPtr;
Tcl_SetObjLength(objPtr, numBytes);
return ReadChars(statePtr, objPtr, charsToRead, factorPtr);
}
statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
bufPtr->nextRemoved += srcRead;
if (dstWrote > srcRead + 1) {
*factorPtr = dstWrote * UTF_EXPANSION_FACTOR / srcRead;
}
Tcl_SetObjLength(objPtr, numBytes + dstWrote);
return numChars;
}
}
/*
*---------------------------------------------------------------------------
*
* TranslateInputEOL --
*
* Perform input EOL and EOF translation on the source buffer, leaving
* the translated result in the destination buffer.
*
* Results:
* The return value is 1 if the EOF character was found when copying
* bytes to the destination buffer, 0 otherwise.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
static void
TranslateInputEOL(
ChannelState *statePtr, /* Channel being read, for EOL translation and
* EOF character. */
char *dstStart, /* Output buffer filled with chars by applying
* appropriate EOL translation to source
* characters. */
const char *srcStart, /* Source characters. */
int *dstLenPtr, /* On entry, the maximum length of output
* buffer in bytes. On exit, the number of
* bytes actually used in output buffer. */
int *srcLenPtr) /* On entry, the length of source buffer. On
* exit, the number of bytes read from the
* source buffer. */
{
const char *eof = NULL;
int dstLen = *dstLenPtr;
int srcLen = *srcLenPtr;
int inEofChar = statePtr->inEofChar;
/*
* Depending on the translation mode in use, there's no need
* to scan more srcLen bytes at srcStart than can possibly transform
* to dstLen bytes. This keeps the scan for eof char below from
* being pointlessly long.
*/
switch (statePtr->inputTranslation) {
case TCL_TRANSLATE_LF:
case TCL_TRANSLATE_CR:
if (srcLen > dstLen) {
/* In these modes, each src byte become a dst byte. */
srcLen = dstLen;
}
break;
default:
/* In other modes, at most 2 src bytes become a dst byte. */
if (srcLen > 2 * dstLen) {
srcLen = 2 * dstLen;
}
break;
}
if (inEofChar != '\0') {
/*
* Make sure we do not read past any logical end of channel input
* created by the presence of the input eof char.
*/
if ((eof = memchr(srcStart, inEofChar, srcLen))) {
srcLen = eof - srcStart;
}
}
switch (statePtr->inputTranslation) {
case TCL_TRANSLATE_LF:
case TCL_TRANSLATE_CR:
if (dstStart != srcStart) {
memcpy(dstStart, srcStart, (size_t) srcLen);
}
if (statePtr->inputTranslation == TCL_TRANSLATE_CR) {
char *dst = dstStart;
char *dstEnd = dstStart + srcLen;
while ((dst = memchr(dst, '\r', dstEnd - dst))) {
*dst++ = '\n';
}
}
dstLen = srcLen;
break;
case TCL_TRANSLATE_CRLF: {
const char *crFound, *src = srcStart;
char *dst = dstStart;
int lesser = (dstLen < srcLen) ? dstLen : srcLen;
while ((crFound = memchr(src, '\r', lesser))) {
int numBytes = crFound - src;
memmove(dst, src, numBytes);
dst += numBytes; dstLen -= numBytes;
src += numBytes; srcLen -= numBytes;
if (srcLen == 1) {
/* valid src bytes end in \r */
if (eof) {
*dst++ = '\r';
src++; srcLen--;
} else {
lesser = 0;
break;
}
} else if (src[1] == '\n') {
*dst++ = '\n';
src += 2; srcLen -= 2;
} else {
*dst++ = '\r';
src++; srcLen--;
}
dstLen--;
lesser = (dstLen < srcLen) ? dstLen : srcLen;
}
memmove(dst, src, lesser);
srcLen = src + lesser - srcStart;
dstLen = dst + lesser - dstStart;
break;
}
case TCL_TRANSLATE_AUTO: {
const char *crFound, *src = srcStart;
char *dst = dstStart;
int lesser;
if ((statePtr->flags & INPUT_SAW_CR) && srcLen) {
if (*src == '\n') { src++; srcLen--; }
ResetFlag(statePtr, INPUT_SAW_CR);
}
lesser = (dstLen < srcLen) ? dstLen : srcLen;
while ((crFound = memchr(src, '\r', lesser))) {
int numBytes = crFound - src;
memmove(dst, src, numBytes);
dst[numBytes] = '\n';
dst += numBytes + 1; dstLen -= numBytes + 1;
src += numBytes + 1; srcLen -= numBytes + 1;
if (srcLen == 0) {
SetFlag(statePtr, INPUT_SAW_CR);
} else if (*src == '\n') {
src++; srcLen--;
}
lesser = (dstLen < srcLen) ? dstLen : srcLen;
}
memmove(dst, src, lesser);
srcLen = src + lesser - srcStart;
dstLen = dst + lesser - dstStart;
break;
}
default:
Tcl_Panic("unknown input translation %d", statePtr->inputTranslation);
}
*dstLenPtr = dstLen;
*srcLenPtr = srcLen;
if (srcStart + srcLen == eof) {
/*
* EOF character was seen in EOL translated range. Leave current file
* position pointing at the EOF character, but don't store the EOF
* character in the output string.
*/
SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF);
statePtr->inputEncodingFlags |= TCL_ENCODING_END;
ResetFlag(statePtr, INPUT_SAW_CR);
}
}
/*
*----------------------------------------------------------------------
*
* Tcl_Ungets --
*
|
| ︙ | ︙ | |||
6316 6317 6318 6319 6320 6321 6322 |
if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
len = -1;
goto done;
}
statePtr->flags = flags;
/*
| < < | < | < < | | 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 |
if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
len = -1;
goto done;
}
statePtr->flags = flags;
/*
* Clear the EOF flags, and clear the BLOCKED bit.
*/
ResetFlag(statePtr,
CHANNEL_BLOCKED | CHANNEL_STICKY_EOF | CHANNEL_EOF | INPUT_SAW_CR);
bufPtr = AllocChannelBuffer(len);
memcpy(InsertPoint(bufPtr), str, (size_t) len);
bufPtr->nextAdded += len;
if (statePtr->inQueueHead == NULL) {
bufPtr->nextPtr = NULL;
|
| ︙ | ︙ | |||
6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 | /* *--------------------------------------------------------------------------- * * GetInput -- * * Reads input data from a device into a channel buffer. * * Results: * The return value is the Posix error code if an error occurred while * reading from the file, or 0 otherwise. * * Side effects: * Reads from the underlying device. * | > > > | 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 | /* *--------------------------------------------------------------------------- * * GetInput -- * * Reads input data from a device into a channel buffer. * * IMPORTANT! This routine is only called on a chanPtr argument * that is the top channel of a stack! * * Results: * The return value is the Posix error code if an error occurred while * reading from the file, or 0 otherwise. * * Side effects: * Reads from the underlying device. * |
| ︙ | ︙ | |||
6497 6498 6499 6500 6501 6502 6503 |
}
/*
* First check for more buffers in the pushback area of the topmost
* channel in the stack and use them. They can be the result of a
* transformation which went away without reading all the information
* placed in the area when it was stacked.
| < < < < < < | > | 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 |
}
/*
* First check for more buffers in the pushback area of the topmost
* channel in the stack and use them. They can be the result of a
* transformation which went away without reading all the information
* placed in the area when it was stacked.
*/
if (chanPtr->inQueueHead != NULL) {
assert(statePtr->inQueueHead == NULL);
statePtr->inQueueHead = chanPtr->inQueueHead;
statePtr->inQueueTail = chanPtr->inQueueTail;
chanPtr->inQueueHead = NULL;
chanPtr->inQueueTail = NULL;
return 0;
}
|
| ︙ | ︙ | |||
6535 6536 6537 6538 6539 6540 6541 |
toRead = SpaceLeft(bufPtr);
} else {
bufPtr = statePtr->saveInBufPtr;
statePtr->saveInBufPtr = NULL;
/*
* Check the actual buffersize against the requested buffersize.
| | | < < < < < < < < < < < < < < > > < < < < < < < < < < < < < < < < < < < < | | < < < < < < < < < < < < | 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 |
toRead = SpaceLeft(bufPtr);
} else {
bufPtr = statePtr->saveInBufPtr;
statePtr->saveInBufPtr = NULL;
/*
* Check the actual buffersize against the requested buffersize.
* Saved buffers of the wrong size are squashed. This is done
* to honor dynamic changes of the buffersize made by the user.
*/
if ((bufPtr != NULL)
&& (bufPtr->bufLength - BUFFER_PADDING != statePtr->bufSize)) {
ReleaseChannelBuffer(bufPtr);
bufPtr = NULL;
}
if (bufPtr == NULL) {
bufPtr = AllocChannelBuffer(statePtr->bufSize);
}
bufPtr->nextPtr = NULL;
toRead = SpaceLeft(bufPtr);
assert(toRead == statePtr->bufSize);
if (statePtr->inQueueTail == NULL) {
statePtr->inQueueHead = bufPtr;
} else {
statePtr->inQueueTail->nextPtr = bufPtr;
}
statePtr->inQueueTail = bufPtr;
}
/*
* TODO - consider escape before buffer alloc
* If EOF is set, we should avoid calling the driver because on some
* platforms it is impossible to read from a device after EOF.
*/
if (GotFlag(statePtr, CHANNEL_EOF)) {
return 0;
}
PreserveChannelBuffer(bufPtr);
nread = ChanRead(chanPtr, InsertPoint(bufPtr), toRead, &result);
if (nread > 0) {
result = 0;
bufPtr->nextAdded += nread;
/*
* If we get a short read, signal up that we may be BLOCKED. We should
* avoid calling the driver because on some platforms we will block in
* the low level reading code even though the channel is set into
* nonblocking mode.
*/
if (nread < toRead) {
SetFlag(statePtr, CHANNEL_BLOCKED);
}
} else if (nread == 0) {
result = 0;
SetFlag(statePtr, CHANNEL_EOF);
statePtr->inputEncodingFlags |= TCL_ENCODING_END;
} else if (nread < 0) {
if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
SetFlag(statePtr, CHANNEL_BLOCKED);
|
| ︙ | ︙ | |||
7104 7105 7106 7107 7108 7109 7110 |
if (BUSY_STATE(statePtr, flags) && ((flags & CHANNEL_RAW_MODE) == 0)) {
Tcl_SetErrno(EBUSY);
return -1;
}
if (direction == TCL_READABLE) {
/*
| < < | | < < < | 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 |
if (BUSY_STATE(statePtr, flags) && ((flags & CHANNEL_RAW_MODE) == 0)) {
Tcl_SetErrno(EBUSY);
return -1;
}
if (direction == TCL_READABLE) {
/*
* Clear the BLOCKED bit. We want to discover this condition
* anew in each operation.
*/
ResetFlag(statePtr, CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA);
}
return 0;
}
/*
|
| ︙ | ︙ | |||
7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343 |
if (sz < 1) {
sz = 1;
} else if (sz > MAX_CHANNEL_BUFFER_SIZE) {
sz = MAX_CHANNEL_BUFFER_SIZE;
}
statePtr = ((Channel *) chan)->state;
statePtr->bufSize = sz;
}
/*
*----------------------------------------------------------------------
*
* Tcl_GetChannelBufferSize --
*
| > > > > > > > > > > > > > > > > > > > > | 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269 7270 |
if (sz < 1) {
sz = 1;
} else if (sz > MAX_CHANNEL_BUFFER_SIZE) {
sz = MAX_CHANNEL_BUFFER_SIZE;
}
statePtr = ((Channel *) chan)->state;
if (statePtr->bufSize == sz) {
return;
}
statePtr->bufSize = sz;
/*
* If bufsize changes, need to get rid of old utility buffer.
*/
if (statePtr->saveInBufPtr != NULL) {
RecycleBuffer(statePtr, statePtr->saveInBufPtr, 1);
statePtr->saveInBufPtr = NULL;
}
if ((statePtr->inQueueHead != NULL)
&& (statePtr->inQueueHead->nextPtr == NULL)
&& IsBufferEmpty(statePtr->inQueueHead)) {
RecycleBuffer(statePtr, statePtr->inQueueHead, 1);
statePtr->inQueueHead = NULL;
statePtr->inQueueTail = NULL;
}
}
/*
*----------------------------------------------------------------------
*
* Tcl_GetChannelBufferSize --
*
|
| ︙ | ︙ | |||
7764 7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 |
} else if (HaveOpt(7, "-buffersize")) {
int newBufferSize;
if (Tcl_GetInt(interp, newValue, &newBufferSize) == TCL_ERROR) {
return TCL_ERROR;
}
Tcl_SetChannelBufferSize(chan, newBufferSize);
} else if (HaveOpt(2, "-encoding")) {
Tcl_Encoding encoding;
if ((newValue[0] == '\0') || (strcmp(newValue, "binary") == 0)) {
encoding = NULL;
} else {
encoding = Tcl_GetEncoding(interp, newValue);
| > | 7691 7692 7693 7694 7695 7696 7697 7698 7699 7700 7701 7702 7703 7704 7705 |
} else if (HaveOpt(7, "-buffersize")) {
int newBufferSize;
if (Tcl_GetInt(interp, newValue, &newBufferSize) == TCL_ERROR) {
return TCL_ERROR;
}
Tcl_SetChannelBufferSize(chan, newBufferSize);
return TCL_OK;
} else if (HaveOpt(2, "-encoding")) {
Tcl_Encoding encoding;
if ((newValue[0] == '\0') || (strcmp(newValue, "binary") == 0)) {
encoding = NULL;
} else {
encoding = Tcl_GetEncoding(interp, newValue);
|
| ︙ | ︙ | |||
7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 7806 7807 7808 |
statePtr->encoding = encoding;
statePtr->inputEncodingState = NULL;
statePtr->inputEncodingFlags = TCL_ENCODING_START;
statePtr->outputEncodingState = NULL;
statePtr->outputEncodingFlags = TCL_ENCODING_START;
ResetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
UpdateInterest(chanPtr);
} else if (HaveOpt(2, "-eofchar")) {
if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
return TCL_ERROR;
}
if (argc == 0) {
statePtr->inEofChar = 0;
statePtr->outEofChar = 0;
| > | 7723 7724 7725 7726 7727 7728 7729 7730 7731 7732 7733 7734 7735 7736 7737 |
statePtr->encoding = encoding;
statePtr->inputEncodingState = NULL;
statePtr->inputEncodingFlags = TCL_ENCODING_START;
statePtr->outputEncodingState = NULL;
statePtr->outputEncodingFlags = TCL_ENCODING_START;
ResetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
UpdateInterest(chanPtr);
return TCL_OK;
} else if (HaveOpt(2, "-eofchar")) {
if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
return TCL_ERROR;
}
if (argc == 0) {
statePtr->inEofChar = 0;
statePtr->outEofChar = 0;
|
| ︙ | ︙ | |||
7955 7956 7957 7958 7959 7960 7961 |
} else if (chanPtr->typePtr->setOptionProc != NULL) {
return chanPtr->typePtr->setOptionProc(chanPtr->instanceData, interp,
optionName, newValue);
} else {
return Tcl_BadChannelOption(interp, optionName, NULL);
}
| < < < < < < < < < < < < < < < < | 7884 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897 |
} else if (chanPtr->typePtr->setOptionProc != NULL) {
return chanPtr->typePtr->setOptionProc(chanPtr->instanceData, interp,
optionName, newValue);
} else {
return Tcl_BadChannelOption(interp, optionName, NULL);
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* CleanupChannelHandlers --
|
| ︙ | ︙ | |||
8062 8063 8064 8065 8066 8067 8068 |
/* State info for channel */
ChannelHandler *chPtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
NextChannelHandler nh;
Channel *upChanPtr;
const Tcl_ChannelType *upTypePtr;
| < < < < < < < < < < < < < < < | 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988 |
/* State info for channel */
ChannelHandler *chPtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
NextChannelHandler nh;
Channel *upChanPtr;
const Tcl_ChannelType *upTypePtr;
/*
* In contrast to the other API functions this procedure walks towards the
* top of a stack and not down from it.
*
* The channel calling this procedure is the one who generated the event,
* and thus does not take part in handling it. IOW, its HandlerProc is not
* called, instead we begin with the channel above it.
|
| ︙ | ︙ | |||
8317 8318 8319 8320 8321 8322 8323 |
/*
* Restart the timer in case a channel handler reenters the event loop
* before UpdateInterest gets called by Tcl_NotifyChannel.
*/
statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME,
ChannelTimerProc,chanPtr);
| < < < < < < < < < < < < < < < < < < < < < | 8215 8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 |
/*
* Restart the timer in case a channel handler reenters the event loop
* before UpdateInterest gets called by Tcl_NotifyChannel.
*/
statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME,
ChannelTimerProc,chanPtr);
Tcl_Preserve(statePtr);
Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE);
Tcl_Release(statePtr);
} else {
statePtr->timer = NULL;
UpdateInterest(chanPtr);
}
}
|
| ︙ | ︙ | |||
9279 9280 9281 9282 9283 9284 9285 | } /* *---------------------------------------------------------------------- * * DoRead -- * | > > | | > | > > > > > > > > | | | < < < < < > > | < < > | | < < > | | < | < < < < < > | < < < < < < < < > < < < | < < | < > | | | | < < | | < > | | | < | | < < < < < < < < < < < < | > > | < < < < | < < < < < < | < < < < | < | > > > | | < < < | < | < < | < < | < < < < < | | < < < | < < | | > | < < < < | | < | | | < < > > > > | | < < < < < | < < < < < | | < < < | < > | < | < < | | < | < | > | > | < | < | < | < < | < < < < < < < < < < | | < < < < < < | < < < < < | | | | < < < | < < < < | < | | < < < < | < | > | < < > | < < | | < < | < < > > | | < < < < < | < < < < < < | < | < < < < < < < | | | | | | | < > | < | < | > > > | | 9156 9157 9158 9159 9160 9161 9162 9163 9164 9165 9166 9167 9168 9169 9170 9171 9172 9173 9174 9175 9176 9177 9178 9179 9180 9181 9182 9183 9184 9185 9186 9187 9188 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218 9219 9220 9221 9222 9223 9224 9225 9226 9227 9228 9229 9230 9231 9232 9233 9234 9235 9236 9237 9238 9239 9240 9241 9242 9243 9244 9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264 9265 9266 9267 9268 9269 9270 9271 9272 9273 9274 9275 9276 9277 9278 9279 9280 9281 9282 9283 9284 9285 9286 9287 9288 9289 9290 9291 9292 9293 9294 9295 9296 9297 9298 9299 9300 9301 9302 9303 9304 9305 9306 9307 9308 9309 9310 9311 9312 9313 9314 9315 9316 9317 9318 9319 9320 9321 9322 9323 9324 9325 9326 9327 9328 9329 9330 9331 9332 9333 9334 9335 9336 9337 9338 9339 9340 |
}
/*
*----------------------------------------------------------------------
*
* DoRead --
*
* Stores up to "bytesToRead" bytes in memory pointed to by "dst".
* These bytes come from reading the channel "chanPtr" and
* performing the configured translations. No encoding conversions
* are applied to the bytes being read.
*
* Results:
* The number of bytes actually stored (<= bytesToRead),
* or -1 if there is an error in reading the channel. Use
* Tcl_GetErrno() to retrieve the error code for the error
* that occurred.
*
* The number of bytes stored can be less than the number
* requested when
* - EOF is reached on the channel; or
* - the channel is non-blocking, and we've read all we can
* without blocking.
* - a channel reading error occurs (and we return -1)
*
* Side effects:
* May cause input to be buffered.
*
*----------------------------------------------------------------------
*/
static int
DoRead(
Channel *chanPtr, /* The channel from which to read. */
char *dst, /* Where to store input read. */
int bytesToRead, /* Maximum number of bytes to read. */
int allowShortReads) /* Allow half-blocking (pipes,sockets) */
{
ChannelState *statePtr = chanPtr->state;
char *p = dst;
Tcl_Preserve(chanPtr);
while (bytesToRead) {
/*
* Each pass through the loop is intended to process up to
* one channel buffer.
*/
int bytesRead, bytesWritten;
ChannelBuffer *bufPtr = statePtr->inQueueHead;
/*
* When there's no buffered data to read, and we're at EOF,
* escape to the caller.
*/
if (statePtr->flags & CHANNEL_EOF
&& (bufPtr == NULL || IsBufferEmpty(bufPtr))) {
break;
}
/* If there is no full buffer, attempt to create and/or fill one. */
while (bufPtr == NULL || !IsBufferFull(bufPtr)) {
int code;
ResetFlag(statePtr, CHANNEL_BLOCKED);
moreData:
code = GetInput(chanPtr);
bufPtr = statePtr->inQueueHead;
assert (bufPtr != NULL);
if (statePtr->flags & (CHANNEL_EOF|CHANNEL_BLOCKED)) {
/* Further reads cannot do any more */
break;
}
if (code) {
/* Read error */
UpdateInterest(chanPtr);
Tcl_Release(chanPtr);
return -1;
}
assert (IsBufferFull(bufPtr));
}
assert (bufPtr != NULL);
bytesRead = BytesLeft(bufPtr);
bytesWritten = bytesToRead;
TranslateInputEOL(statePtr, p, RemovePoint(bufPtr),
&bytesWritten, &bytesRead);
bufPtr->nextRemoved += bytesRead;
p += bytesWritten;
bytesToRead -= bytesWritten;
if (!IsBufferEmpty(bufPtr)) {
/*
* Buffer is not empty. How can that be?
*
* 0) We stopped early because we got all the bytes
* we were seeking. That's fine.
*/
if (bytesToRead == 0) {
UpdateInterest(chanPtr);
break;
}
/*
* 1) We're @EOF because we saw eof char.
*/
if (statePtr->inEofChar
&& RemovePoint(bufPtr)[0] == statePtr->inEofChar) {
UpdateInterest(chanPtr);
break;
}
/*
* 2) The buffer holds a \r while in CRLF translation,
* followed by the end of the buffer.
*/
assert(statePtr->inputTranslation == TCL_TRANSLATE_CRLF);
assert(RemovePoint(bufPtr)[0] == '\r');
assert(BytesLeft(bufPtr) == 1);
if (bufPtr->nextPtr == NULL) {
/* There's no more buffered data.... */
if (statePtr->flags & CHANNEL_EOF) {
/* ...and there never will be. */
*p++ = '\r';
bytesToRead--;
bufPtr->nextRemoved++;
} else if (statePtr->flags & CHANNEL_BLOCKED) {
/* ...and we cannot get more now. */
SetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
UpdateInterest(chanPtr);
break;
} else {
/* ... so we need to get some. */
goto moreData;
}
}
if (bufPtr->nextPtr) {
/* There's a next buffer. Shift orphan \r to it. */
ChannelBuffer *nextPtr = bufPtr->nextPtr;
nextPtr->nextRemoved -= 1;
RemovePoint(nextPtr)[0] = '\r';
bufPtr->nextRemoved++;
}
}
if (IsBufferEmpty(bufPtr)) {
statePtr->inQueueHead = bufPtr->nextPtr;
if (statePtr->inQueueHead == NULL) {
statePtr->inQueueTail = NULL;
}
RecycleBuffer(statePtr, bufPtr, 0);
}
if ((statePtr->flags & CHANNEL_NONBLOCKING || allowShortReads)
&& statePtr->flags & CHANNEL_BLOCKED) {
break;
}
}
Tcl_Release(chanPtr);
return (int)(p - dst);
}
/*
*----------------------------------------------------------------------
*
* CopyBuffer --
*
|
| ︙ | ︙ | |||
10939 10940 10941 10942 10943 10944 10945 10946 10947 10948 10949 10950 10951 10952 |
ChannelState *statePtr;
if (interp == NULL) {
return TCL_ERROR;
}
if (objPtr->typePtr == &chanObjType) {
/*
* The channel is valid until any call to DetachChannel occurs.
* Ensure consistency checks are done.
*/
statePtr = GET_CHANNELSTATE(objPtr);
if (GotFlag(statePtr, CHANNEL_TAINTED|CHANNEL_CLOSED)) {
ResetFlag(statePtr, CHANNEL_TAINTED);
| > | 10683 10684 10685 10686 10687 10688 10689 10690 10691 10692 10693 10694 10695 10696 10697 |
ChannelState *statePtr;
if (interp == NULL) {
return TCL_ERROR;
}
if (objPtr->typePtr == &chanObjType) {
/*
* TODO: TAINT Flag and dup'd channel values?
* The channel is valid until any call to DetachChannel occurs.
* Ensure consistency checks are done.
*/
statePtr = GET_CHANNELSTATE(objPtr);
if (GotFlag(statePtr, CHANNEL_TAINTED|CHANNEL_CLOSED)) {
ResetFlag(statePtr, CHANNEL_TAINTED);
|
| ︙ | ︙ | |||
11022 11023 11024 11025 11026 11027 11028 |
ChanFlag('R', BUFFER_READY);
ChanFlag('F', BG_FLUSH_SCHEDULED);
ChanFlag('c', CHANNEL_CLOSED);
ChanFlag('E', CHANNEL_EOF);
ChanFlag('S', CHANNEL_STICKY_EOF);
ChanFlag('B', CHANNEL_BLOCKED);
ChanFlag('/', INPUT_SAW_CR);
| < < < < < | 10767 10768 10769 10770 10771 10772 10773 10774 10775 10776 10777 10778 10779 10780 10781 10782 |
ChanFlag('R', BUFFER_READY);
ChanFlag('F', BG_FLUSH_SCHEDULED);
ChanFlag('c', CHANNEL_CLOSED);
ChanFlag('E', CHANNEL_EOF);
ChanFlag('S', CHANNEL_STICKY_EOF);
ChanFlag('B', CHANNEL_BLOCKED);
ChanFlag('/', INPUT_SAW_CR);
ChanFlag('D', CHANNEL_DEAD);
ChanFlag('R', CHANNEL_RAW_MODE);
ChanFlag('x', CHANNEL_INCLOSE);
buf[i] ='\0';
fprintf(stderr, "%s: %s\n", str, buf);
return 0;
}
|
| ︙ | ︙ |
Changes to generic/tclIO.h.
| ︙ | ︙ | |||
249 250 251 252 253 254 255 | #define CHANNEL_BLOCKED (1<<11) /* EWOULDBLOCK or EAGAIN occurred on * this channel. This bit is cleared * before every input or output * operation. */ #define INPUT_SAW_CR (1<<12) /* Channel is in CRLF eol input * translation mode and the last byte * seen was a "\r". */ | < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | #define CHANNEL_BLOCKED (1<<11) /* EWOULDBLOCK or EAGAIN occurred on * this channel. This bit is cleared * before every input or output * operation. */ #define INPUT_SAW_CR (1<<12) /* Channel is in CRLF eol input * translation mode and the last byte * seen was a "\r". */ #define CHANNEL_DEAD (1<<13) /* The channel has been closed by the * exit handler (on exit) but not * deallocated. When any IO operation * sees this flag on a channel, it * does not call driver level * functions to avoid referring to * deallocated data. */ #define CHANNEL_NEED_MORE_DATA (1<<14) /* The last input operation failed * because there was not enough data * to complete the operation. This * flag is set when gets fails to get * a complete line or when read fails * to get a complete character. When * set, file events will not be * delivered for buffered data until * the state of the channel * changes. */ #define CHANNEL_RAW_MODE (1<<16) /* When set, notes that the Raw API is * being used. */ #define CHANNEL_INCLOSE (1<<19) /* Channel is currently being closed. * Its structures are still live and * usable, but it may not be closed * again from within the close * handler. */ #define CHANNEL_TAINTED (1<<20) /* Channel stack structure has changed. |
| ︙ | ︙ |
Changes to generic/tclInt.h.
| ︙ | ︙ | |||
2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 | int *typePtr); MODULE_SCOPE int TclGetOpenModeEx(Tcl_Interp *interp, const char *modeString, int *seekFlagPtr, int *binaryPtr); MODULE_SCOPE Tcl_Obj * TclGetProcessGlobalValue(ProcessGlobalValue *pgvPtr); MODULE_SCOPE Tcl_Obj * TclGetSourceFromFrame(CmdFrame *cfPtr, int objc, Tcl_Obj *const objv[]); MODULE_SCOPE int TclGlob(Tcl_Interp *interp, char *pattern, Tcl_Obj *unquotedPrefix, int globFlags, Tcl_GlobTypeData *types); MODULE_SCOPE int TclIncrObj(Tcl_Interp *interp, Tcl_Obj *valuePtr, Tcl_Obj *incrPtr); MODULE_SCOPE Tcl_Obj * TclIncrObjVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *incrPtr, int flags); | > > | 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 | int *typePtr); MODULE_SCOPE int TclGetOpenModeEx(Tcl_Interp *interp, const char *modeString, int *seekFlagPtr, int *binaryPtr); MODULE_SCOPE Tcl_Obj * TclGetProcessGlobalValue(ProcessGlobalValue *pgvPtr); MODULE_SCOPE Tcl_Obj * TclGetSourceFromFrame(CmdFrame *cfPtr, int objc, Tcl_Obj *const objv[]); MODULE_SCOPE char * TclGetStringStorage(Tcl_Obj *objPtr, unsigned int *sizePtr); MODULE_SCOPE int TclGlob(Tcl_Interp *interp, char *pattern, Tcl_Obj *unquotedPrefix, int globFlags, Tcl_GlobTypeData *types); MODULE_SCOPE int TclIncrObj(Tcl_Interp *interp, Tcl_Obj *valuePtr, Tcl_Obj *incrPtr); MODULE_SCOPE Tcl_Obj * TclIncrObjVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *incrPtr, int flags); |
| ︙ | ︙ |
Changes to generic/tclStringObj.c.
| ︙ | ︙ | |||
1119 1120 1121 1122 1123 1124 1125 |
if (length <= limit) {
toCopy = length;
} else {
if (ellipsis == NULL) {
ellipsis = "...";
}
| > | | 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 |
if (length <= limit) {
toCopy = length;
} else {
if (ellipsis == NULL) {
ellipsis = "...";
}
toCopy = (bytes == NULL) ? limit
: Tcl_UtfPrev(bytes+limit+1-strlen(ellipsis), bytes) - bytes;
}
/*
* If objPtr has a valid Unicode rep, then append the Unicode conversion
* of "bytes" to the objPtr's Unicode rep, otherwise append "bytes" to
* objPtr's string rep.
*/
|
| ︙ | ︙ | |||
1431 1432 1433 1434 1435 1436 1437 | /* * Protect against case where unicode points into the existing * stringPtr->unicode array. Force it to follow any relocations due to * the reallocs below. */ | | | 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 |
/*
* Protect against case where unicode points into the existing
* stringPtr->unicode array. Force it to follow any relocations due to
* the reallocs below.
*/
if (unicode && unicode >= stringPtr->unicode
&& unicode <= stringPtr->unicode + stringPtr->maxChars) {
offset = unicode - stringPtr->unicode;
}
GrowUnicodeBuffer(objPtr, numChars);
stringPtr = GET_STRING(objPtr);
|
| ︙ | ︙ | |||
1453 1454 1455 1456 1457 1458 1459 |
}
/*
* Copy the new string onto the end of the old string, then add the
* trailing null.
*/
| > | | > | 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 |
}
/*
* Copy the new string onto the end of the old string, then add the
* trailing null.
*/
if (unicode) {
memmove(stringPtr->unicode + stringPtr->numChars, unicode,
appendNumChars * sizeof(Tcl_UniChar));
}
stringPtr->unicode[numChars] = 0;
stringPtr->numChars = numChars;
stringPtr->allocated = 0;
TclInvalidateStringRep(objPtr);
}
|
| ︙ | ︙ | |||
1593 1594 1595 1596 1597 1598 1599 | /* * Protect against case where unicode points into the existing * stringPtr->unicode array. Force it to follow any relocations due to * the reallocs below. */ | | | 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 |
/*
* Protect against case where unicode points into the existing
* stringPtr->unicode array. Force it to follow any relocations due to
* the reallocs below.
*/
if (bytes && bytes >= objPtr->bytes
&& bytes <= objPtr->bytes + objPtr->length) {
offset = bytes - objPtr->bytes;
}
/*
* TODO: consider passing flag=1: no overalloc on first append. This
* would make test stringObj-8.1 fail.
|
| ︙ | ︙ | |||
1621 1622 1623 1624 1625 1626 1627 |
/*
* Invalidate the unicode data.
*/
stringPtr->numChars = -1;
stringPtr->hasUnicode = 0;
| > | > | 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 |
/*
* Invalidate the unicode data.
*/
stringPtr->numChars = -1;
stringPtr->hasUnicode = 0;
if (bytes) {
memmove(objPtr->bytes + oldLength, bytes, numBytes);
}
objPtr->bytes[newLength] = 0;
objPtr->length = newLength;
}
/*
*----------------------------------------------------------------------
*
|
| ︙ | ︙ | |||
2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 |
va_end(argList);
return objPtr;
}
/*
*---------------------------------------------------------------------------
*
* TclStringObjReverse --
*
* Implements the [string reverse] operation.
*
* Results:
* An unshared Tcl value which is the [string reverse] of the argument
* supplied. When sharing rules permit, the returned value might be the
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
va_end(argList);
return objPtr;
}
/*
*---------------------------------------------------------------------------
*
* TclGetStringStorage --
*
* Returns the string storage space of a Tcl_Obj.
*
* Results:
* The pointer value objPtr->bytes is returned and the number of bytes
* allocated there is written to *sizePtr (if known).
*
* Side effects:
* May set objPtr->bytes.
*
*---------------------------------------------------------------------------
*/
char *
TclGetStringStorage(
Tcl_Obj *objPtr,
unsigned int *sizePtr)
{
String *stringPtr;
if (objPtr->typePtr != &tclStringType || objPtr->bytes == NULL) {
return TclGetStringFromObj(objPtr, (int *)sizePtr);
}
stringPtr = GET_STRING(objPtr);
*sizePtr = stringPtr->allocated;
return objPtr->bytes;
}
/*
*---------------------------------------------------------------------------
*
* TclStringObjReverse --
*
* Implements the [string reverse] operation.
*
* Results:
* An unshared Tcl value which is the [string reverse] of the argument
* supplied. When sharing rules permit, the returned value might be the
|
| ︙ | ︙ | |||
2844 2845 2846 2847 2848 2849 2850 |
if (needed > stringPtr->maxChars) {
GrowUnicodeBuffer(objPtr, needed);
stringPtr = GET_STRING(objPtr);
}
stringPtr->hasUnicode = 1;
| > | > > > | 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 |
if (needed > stringPtr->maxChars) {
GrowUnicodeBuffer(objPtr, needed);
stringPtr = GET_STRING(objPtr);
}
stringPtr->hasUnicode = 1;
if (bytes) {
stringPtr->numChars = needed;
} else {
numAppendChars = 0;
}
for (dst=stringPtr->unicode + numOrigChars; numAppendChars-- > 0; dst++) {
bytes += TclUtfToUniChar(bytes, dst);
}
*dst = 0;
}
/*
|
| ︙ | ︙ |
Changes to tests/io.test.
| ︙ | ︙ | |||
4749 4750 4751 4752 4753 4754 4755 |
set f [open $path(test1) r]
fconfigure $f -translation crlf -eofchar \x1a
set l [string length [set in [read $f]]]
set e [eof $f]
close $f
list $s $l $e [scan [string index $in end] %c]
} -result {9 8 1 13}
| | | 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 |
set f [open $path(test1) r]
fconfigure $f -translation crlf -eofchar \x1a
set l [string length [set in [read $f]]]
set e [eof $f]
close $f
list $s $l $e [scan [string index $in end] %c]
} -result {9 8 1 13}
test io-35.18b {Tcl_Eof, eof char, cr write, crlf read} -body {
file delete $path(test1)
set f [open $path(test1) w]
fconfigure $f -translation cr -eofchar \x1a
puts $f {}
close $f
set s [file size $path(test1)]
set f [open $path(test1) r]
|
| ︙ | ︙ | |||
4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 |
set f [open $path(test1) r]
fconfigure $f -translation crlf -eofchar \x1a
set l [string length [set in [read $f]]]
set e [eof $f]
close $f
list $c $l $e [scan [string index $in end] %c]
} -result {17 8 1 13}
# Test Tcl_InputBlocked
test io-36.1 {Tcl_InputBlocked on nonblocking pipe} {stdio openpipe} {
set f1 [open "|[list [interpreter]]" r+]
puts $f1 {puts hello_from_pipe}
flush $f1
| > > > > > > > > > > > > > > > | 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 |
set f [open $path(test1) r]
fconfigure $f -translation crlf -eofchar \x1a
set l [string length [set in [read $f]]]
set e [eof $f]
close $f
list $c $l $e [scan [string index $in end] %c]
} -result {17 8 1 13}
test io-35.20 {Tcl_Eof, eof char in middle, cr write, crlf read} {
file delete $path(test1)
set f [open $path(test1) w]
fconfigure $f -translation cr -eofchar {}
set i [format \n%cqrsuvw 26]
puts $f $i
close $f
set c [file size $path(test1)]
set f [open $path(test1) r]
fconfigure $f -translation crlf -eofchar \x1a
set l [string length [set in [read $f]]]
set e [eof $f]
close $f
list $c $l $e [scan [string index $in end] %c]
} {9 1 1 13}
# Test Tcl_InputBlocked
test io-36.1 {Tcl_InputBlocked on nonblocking pipe} {stdio openpipe} {
set f1 [open "|[list [interpreter]]" r+]
puts $f1 {puts hello_from_pipe}
flush $f1
|
| ︙ | ︙ | |||
6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 |
fcopy $in $out
close $in
close $out
file size $path(kyrillic.txt)
} 3
test io-53.1 {CopyData} {fcopy} {
file delete $path(test1)
set f1 [open $thisScript]
set f2 [open $path(test1) w]
fconfigure $f1 -translation lf -blocking 0
fconfigure $f2 -translation cr -blocking 0
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 |
fcopy $in $out
close $in
close $out
file size $path(kyrillic.txt)
} 3
test io-52.12 {coverage of -translation auto} {
file delete $path(test1) $path(test2)
set out [open $path(test1) wb]
chan configure $out -translation lf
puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
close $out
set in [open $path(test1)]
chan configure $in -buffersize 8
set out [open $path(test2) w]
fcopy $in $out
close $in
close $out
file size $path(test2)
} 29
test io-52.13 {coverage of -translation cr} {
file delete $path(test1) $path(test2)
set out [open $path(test1) wb]
chan configure $out -translation lf
puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
close $out
set in [open $path(test1)]
chan configure $in -buffersize 8 -translation cr
set out [open $path(test2) w]
fcopy $in $out
close $in
close $out
file size $path(test2)
} 30
test io-52.14 {coverage of -translation crlf} {
file delete $path(test1) $path(test2)
set out [open $path(test1) wb]
chan configure $out -translation lf
puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
close $out
set in [open $path(test1)]
chan configure $in -buffersize 8 -translation crlf
set out [open $path(test2) w]
fcopy $in $out
close $in
close $out
file size $path(test2)
} 29
test io-52.14.1 {coverage of -translation crlf} {
file delete $path(test1) $path(test2)
set out [open $path(test1) wb]
chan configure $out -translation lf
puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
close $out
set in [open $path(test1)]
chan configure $in -buffersize 8 -translation crlf
set out [open $path(test2) w]
fcopy $in $out -size 2
close $in
close $out
file size $path(test2)
} 2
test io-52.14.2 {coverage of -translation crlf} {
file delete $path(test1) $path(test2)
set out [open $path(test1) wb]
chan configure $out -translation lf
puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
close $out
set in [open $path(test1)]
chan configure $in -translation crlf
set out [open $path(test2) w]
fcopy $in $out -size 9
close $in
close $out
file size $path(test2)
} 9
test io-52.15 {coverage of -translation crlf} {
file delete $path(test1) $path(test2)
set out [open $path(test1) wb]
chan configure $out -translation lf
puts -nonewline $out abcdefg\r
close $out
set in [open $path(test1)]
chan configure $in -buffersize 8 -translation crlf
set out [open $path(test2) w]
fcopy $in $out
close $in
close $out
file size $path(test2)
} 8
test io-52.16 {coverage of eofChar handling} {
file delete $path(test1) $path(test2)
set out [open $path(test1) wb]
chan configure $out -translation lf
puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
close $out
set in [open $path(test1)]
chan configure $in -buffersize 8 -translation lf -eofchar a
set out [open $path(test2) w]
fcopy $in $out
close $in
close $out
file size $path(test2)
} 0
test io-52.17 {coverage of eofChar handling} {
file delete $path(test1) $path(test2)
set out [open $path(test1) wb]
chan configure $out -translation lf
puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
close $out
set in [open $path(test1)]
chan configure $in -buffersize 8 -translation lf -eofchar d
set out [open $path(test2) w]
fcopy $in $out
close $in
close $out
file size $path(test2)
} 3
test io-52.18 {coverage of eofChar handling} {
file delete $path(test1) $path(test2)
set out [open $path(test1) wb]
chan configure $out -translation lf
puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
close $out
set in [open $path(test1)]
chan configure $in -buffersize 8 -translation crlf -eofchar h
set out [open $path(test2) w]
fcopy $in $out
close $in
close $out
file size $path(test2)
} 8
test io-52.19 {coverage of eofChar handling} {
file delete $path(test1) $path(test2)
set out [open $path(test1) wb]
chan configure $out -translation lf
puts -nonewline $out abcdefg\rhijklmn\nopqrstu\r\nvwxyz
close $out
set in [open $path(test1)]
chan configure $in -buffersize 10 -translation crlf -eofchar h
set out [open $path(test2) w]
fcopy $in $out
close $in
close $out
file size $path(test2)
} 8
test io-53.1 {CopyData} {fcopy} {
file delete $path(test1)
set f1 [open $thisScript]
set f2 [open $path(test1) w]
fconfigure $f1 -translation lf -blocking 0
fconfigure $f2 -translation cr -blocking 0
|
| ︙ | ︙ | |||
7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 |
set done
} -cleanup {
close $outChan
close $inChan
removeFile out
removeFile in
} -result {40 bytes copied}
test io-54.1 {Recursive channel events} {socket fileevent} {
# This test checks to see if file events are delivered during recursive
# event loops when there is buffered data on the channel.
proc accept {s a p} {
variable as
| > > > > > > > > > > > > > > > > > > > | 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 7460 7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 |
set done
} -cleanup {
close $outChan
close $inChan
removeFile out
removeFile in
} -result {40 bytes copied}
test io-53.12 {CopyData: foreground short reads, aka bug 3096275} {stdio unix openpipe fcopy} {
file delete $path(pipe)
set f1 [open $path(pipe) w]
puts -nonewline $f1 {
fconfigure stdin -translation binary -blocking 0
fconfigure stdout -buffering none -translation binary
fcopy stdin stdout
}
close $f1
set f1 [open "|[list [interpreter] $path(pipe)]" r+]
fconfigure $f1 -translation binary -buffering none
puts -nonewline $f1 A
after 2000 {set ::done timeout}
fileevent $f1 readable {set ::done ok}
vwait ::done
set ch [read $f1 1]
close $f1
list $::done $ch
} {ok A}
test io-54.1 {Recursive channel events} {socket fileevent} {
# This test checks to see if file events are delivered during recursive
# event loops when there is buffered data on the channel.
proc accept {s a p} {
variable as
|
| ︙ | ︙ |
Changes to tests/ioCmd.test.
| ︙ | ︙ | |||
2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 |
LOG MAIN_WAITING
vwait forever
LOG MAIN_DONE
set res
} -cleanup {
rename LOG {}
rename POST {}
rename HANDLER {}
| > | | 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 |
LOG MAIN_WAITING
vwait forever
LOG MAIN_DONE
set res
} -cleanup {
after cancel $::timer
rename LOG {}
rename POST {}
rename HANDLER {}
unset beat drive data forever res tid ch timer
} -match glob \
-result {{initialize rc* read} {watch rc* read} {read rc* 4096} {watch rc* {}} {watch rc* read} {read rc* 4096} {watch rc* {}} {finalize rc*}}
# --- === *** ###########################
# method cgetall
test iocmd.tf-25.1 {chan configure, cgetall, standard options} -match glob -body {
|
| ︙ | ︙ |