Tk Source Code

Check-in [abca82f4]
Login

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

Overview
Comment:[Bug-1630262]: segfault when deleting lines with peer text widgets
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | bug-1630262
Files: files | file ages | folders
SHA1: abca82f488b9f1f85a98b24afee698ad1d2be6d9
User & Date: fvogel 2012-01-31 22:24:11
Context
2012-02-02
10:40
Better fix for bug-1630262, also fixing bug-1615425 check-in: 1cb7c1e0 user: fvogel tags: bug-1630262
2012-01-31
22:24
[Bug-1630262]: segfault when deleting lines with peer text widgets check-in: abca82f4 user: fvogel tags: bug-1630262
2012-01-30
19:44
ttk::combobox::Press: Don't take focus in disabled state [Bug 2925561]. check-in: dfb108b6 user: jenglish tags: core-8-5-branch
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ChangeLog.








1
2
3
4
5
6
7







2012-01-29  Jan Nijtmans  <[email protected]>

	* win/tkImgPhoto.c: [Bug 3480634]: PNG Images missing in menus on Mac

2012-01-27  Jan Nijtmans  <[email protected]>

	* win/tkWinDialog.c: [Bug 3480471]: tk_getOpenFile crashes on Win64
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
2012-01-??  Francois Vogel  <[email protected]>

	* generic/tkText.c:      [Bug-1630262]: segfault when deleting lines
	* generic/tkTextBTree.c  with peer text widgets
	* generic/tkTextDisp.c
	* tests/text.test

2012-01-29  Jan Nijtmans  <[email protected]>

	* win/tkImgPhoto.c: [Bug 3480634]: PNG Images missing in menus on Mac

2012-01-27  Jan Nijtmans  <[email protected]>

	* win/tkWinDialog.c: [Bug 3480471]: tk_getOpenFile crashes on Win64

Changes to generic/tkText.c.

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
		 * Deletion range starts on top line but after topIndex. Use
		 * the current topIndex as the new one.
		 */

		resetView = 1;
		line = line1;
		byteIndex = tPtr->topIndex.byteIndex;





	    }
	} else if (index2.linePtr == tPtr->topIndex.linePtr) {
	    /*
	     * Deletion range ends on top line but before topIndex. Figure out
	     * what will be the new character index for the character
	     * currently pointed to by topIndex.
	     */

	    resetView = 1;
	    line = line2;
	    byteIndex = tPtr->topIndex.byteIndex;
	    if (index1.linePtr != index2.linePtr) {
		byteIndex -= index2.byteIndex;
	    } else {
		byteIndex -= (index2.byteIndex - index1.byteIndex);
	    }





	}
	if (resetView) {
	    lineAndByteIndex[resetViewCount] = line;
	    lineAndByteIndex[resetViewCount+1] = byteIndex;
	} else {
	    lineAndByteIndex[resetViewCount] = -1;
	}







>
>
>
>
>
















>
>
>
>
>







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
		 * Deletion range starts on top line but after topIndex. Use
		 * the current topIndex as the new one.
		 */

		resetView = 1;
		line = line1;
		byteIndex = tPtr->topIndex.byteIndex;
            } else {
                /*
                 * Deletion range starts after the top line. This peers's view
                 * will not need to be reset. Nothing to do.
                 */
	    }
	} else if (index2.linePtr == tPtr->topIndex.linePtr) {
	    /*
	     * Deletion range ends on top line but before topIndex. Figure out
	     * what will be the new character index for the character
	     * currently pointed to by topIndex.
	     */

	    resetView = 1;
	    line = line2;
	    byteIndex = tPtr->topIndex.byteIndex;
	    if (index1.linePtr != index2.linePtr) {
		byteIndex -= index2.byteIndex;
	    } else {
		byteIndex -= (index2.byteIndex - index1.byteIndex);
	    }
        } else {
            /*
             * Deletion range ends before the top line. This peers's view
             * will not need to be reset. Nothing to do.
             */
	}
	if (resetView) {
	    lineAndByteIndex[resetViewCount] = line;
	    lineAndByteIndex[resetViewCount+1] = byteIndex;
	} else {
	    lineAndByteIndex[resetViewCount] = -1;
	}
3153
3154
3155
3156
3157
3158
3159
3160








3161
3162
3163
3164
3165
3166
3167





















3168
3169
3170
3171
3172
3173
3174
	int line = lineAndByteIndex[resetViewCount];

	if (line != -1) {
	    int byteIndex = lineAndByteIndex[resetViewCount+1];
	    TkTextIndex indexTmp;

	    if (tPtr == textPtr) {
		if (viewUpdate) {








		    TkTextMakeByteIndex(sharedTextPtr->tree, textPtr, line,
			    byteIndex, &indexTmp);
		    TkTextSetYView(tPtr, &indexTmp, 0);
		}
	    } else {
		TkTextMakeByteIndex(sharedTextPtr->tree, NULL, line,
			byteIndex, &indexTmp);





















		TkTextSetYView(tPtr, &indexTmp, 0);
	    }
	}
	resetViewCount += 2;
    }
    if (sharedTextPtr->refCount > PIXEL_CLIENTS) {
	ckfree((char *) lineAndByteIndex);







|
>
>
>
>
>
>
>
>





|

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







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
	int line = lineAndByteIndex[resetViewCount];

	if (line != -1) {
	    int byteIndex = lineAndByteIndex[resetViewCount+1];
	    TkTextIndex indexTmp;

	    if (tPtr == textPtr) {
                if (viewUpdate) {
                    /*
                     * line cannot be before -startline of textPtr because
                     * this line corresponds to an index which is necessarily
                     * between "1.0" and "end" relative to textPtr.
                     * Therefore no need to clamp line to the -start/-end
                     * range.
                     */

		    TkTextMakeByteIndex(sharedTextPtr->tree, textPtr, line,
			    byteIndex, &indexTmp);
		    TkTextSetYView(tPtr, &indexTmp, 0);
		}
	    } else {
                TkTextMakeByteIndex(sharedTextPtr->tree, tPtr, line,
			byteIndex, &indexTmp);
                /*
                 * line may be before -startline of tPtr and must be
                 * clamped to -startline before providing it to
                 * TkTextSetYView otherwise lines before -startline
                 * would be displayed.
                 * There is no need to worry about -endline however,
                 * because the view will only be reset if the deletion
                 * involves the TOP line of the screen
                 */

                if (tPtr->start != NULL) {
                    int start;
                    TkTextIndex indexStart;

                    start = TkBTreeLinesTo(NULL, tPtr->start);
                    TkTextMakeByteIndex(sharedTextPtr->tree, NULL, start,
			    0, &indexStart);
                    if (TkTextIndexCmp(&indexTmp, &indexStart) < 0) {
                        indexTmp = indexStart;
                    }
                }
		TkTextSetYView(tPtr, &indexTmp, 0);
	    }
	}
	resetViewCount += 2;
    }
    if (sharedTextPtr->refCount > PIXEL_CLIENTS) {
	ckfree((char *) lineAndByteIndex);

Changes to generic/tkTextBTree.c.

658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
	treePtr->startEndRef = (TkText **)
		ckrealloc((char *) treePtr->startEndRef,
		sizeof(TkText *) * count);

	if (textPtr->start != NULL) {
	    count--;
	    treePtr->startEnd[count] = textPtr->start;
	    treePtr->startEndRef[count] = treePtr->sharedTextPtr->peers;
	}
	if (textPtr->end != NULL) {
	    count--;
	    treePtr->startEnd[count] = textPtr->end;
	    treePtr->startEndRef[count] = treePtr->sharedTextPtr->peers;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *







|




|







658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
	treePtr->startEndRef = (TkText **)
		ckrealloc((char *) treePtr->startEndRef,
		sizeof(TkText *) * count);

	if (textPtr->start != NULL) {
	    count--;
	    treePtr->startEnd[count] = textPtr->start;
	    treePtr->startEndRef[count] = textPtr;
	}
	if (textPtr->end != NULL) {
	    count--;
	    treePtr->startEnd[count] = textPtr->end;
	    treePtr->startEndRef[count] = textPtr;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619

    nodePtr = treePtr->rootPtr;
    if ((line < 0) || (line >= nodePtr->numLines)) {
	return NULL;
    }

    /*
     * Check for the any start/end offset for this text widget.
     */

    if (textPtr != NULL) {
	if (textPtr->start != NULL) {
	    line += TkBTreeLinesTo(NULL, textPtr->start);
	    if (line >= nodePtr->numLines) {
		return NULL;







|







1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619

    nodePtr = treePtr->rootPtr;
    if ((line < 0) || (line >= nodePtr->numLines)) {
	return NULL;
    }

    /*
     * Check for any start/end offset for this text widget.
     */

    if (textPtr != NULL) {
	if (textPtr->start != NULL) {
	    line += TkBTreeLinesTo(NULL, textPtr->start);
	    if (line >= nodePtr->numLines) {
		return NULL;
1989
1990
1991
1992
1993
1994
1995





1996
1997
1998
1999
2000
2001
2002
		Tcl_Panic("TkBTreeLinesTo couldn't find node");
	    }
	    index += nodePtr2->numLines;
	}
    }
    if (textPtr != NULL && textPtr->start != NULL) {
	index -= TkBTreeLinesTo(NULL, textPtr->start);





    }
    return index;
}

/*
 *----------------------------------------------------------------------
 *







>
>
>
>
>







1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
		Tcl_Panic("TkBTreeLinesTo couldn't find node");
	    }
	    index += nodePtr2->numLines;
	}
    }
    if (textPtr != NULL && textPtr->start != NULL) {
	index -= TkBTreeLinesTo(NULL, textPtr->start);
        if (index < 0) {
            /* One should panic here!
            Tcl_Panic("TkBTreeLinesTo: linePtr comes before -startline");
             */
        }
    }
    return index;
}

/*
 *----------------------------------------------------------------------
 *

Changes to generic/tkTextDisp.c.

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
    TkTextLine *linePtr,	/* Invalidation starts from this line. */
    int lineCount,		/* And includes this many following lines. */
    int action)			/* Indicates what type of invalidation
				 * occurred (insert, delete, or simple). */
{
    int fromLine;
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;





























    if (linePtr != NULL) {
	int counter = lineCount;

	fromLine = TkBTreeLinesTo(textPtr, linePtr);

	/*
	 * Invalidate the height calculations of each line in the given range.
	 */

	TkBTreeLinePixelEpoch(textPtr, linePtr) = 0;
	while (counter > 0 && linePtr != 0) {
	    linePtr = TkBTreeNextLine(textPtr, linePtr);
	    if (linePtr != NULL) {
		TkBTreeLinePixelEpoch(textPtr, linePtr) = 0;
	    }
	    counter--;
	}

	/*
	 * Now schedule an examination of each line in the union of the old
	 * and new update ranges, including the (possibly empty) range in
	 * between. If that between range is not-empty, then we are examining
	 * more lines than is strictly necessary (but the examination of the
	 * extra lines should be quick, since their pixelCalculationEpoch will
	 * be up to date). However, to keep track of that would require more
	 * complex record-keeping that what we have.
	 */

	if (dInfoPtr->lineUpdateTimer == NULL) {
	    dInfoPtr->currentMetricUpdateLine = fromLine;
	    if (action == TK_TEXT_INVALIDATE_DELETE) {
		lineCount = 0;
	    }







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











|














|







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
    TkTextLine *linePtr,	/* Invalidation starts from this line. */
    int lineCount,		/* And includes this many following lines. */
    int action)			/* Indicates what type of invalidation
				 * occurred (insert, delete, or simple). */
{
    int fromLine;
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;

    /*
     * All lines to invalidate must be inside the -startline/-endline range.
     */

    if (linePtr != NULL) {
        int start;
        TkTextLine *toLinePtr;
        if (textPtr->start != NULL) {
            fromLine = TkBTreeLinesTo(NULL, linePtr);
            start = TkBTreeLinesTo(NULL, textPtr->start);
            if (fromLine < start) {
                lineCount -= start - fromLine;
                linePtr = textPtr->start;
            }
        }
        if (textPtr->end != NULL) {
            int count = 0;
            toLinePtr = linePtr;
            while (count < lineCount && toLinePtr != NULL) {
                toLinePtr = TkBTreeNextLine(textPtr, toLinePtr);
                count++;
            }
            if (toLinePtr == NULL) {
                lineCount = count;
            }
        }
    }

    if (linePtr != NULL) {
	int counter = lineCount;

	fromLine = TkBTreeLinesTo(textPtr, linePtr);

	/*
	 * Invalidate the height calculations of each line in the given range.
	 */

	TkBTreeLinePixelEpoch(textPtr, linePtr) = 0;
	while (counter > 0 && linePtr != NULL) {
	    linePtr = TkBTreeNextLine(textPtr, linePtr);
	    if (linePtr != NULL) {
		TkBTreeLinePixelEpoch(textPtr, linePtr) = 0;
	    }
	    counter--;
	}

	/*
	 * Now schedule an examination of each line in the union of the old
	 * and new update ranges, including the (possibly empty) range in
	 * between. If that between range is not-empty, then we are examining
	 * more lines than is strictly necessary (but the examination of the
	 * extra lines should be quick, since their pixelCalculationEpoch will
	 * be up to date). However, to keep track of that would require more
	 * complex record-keeping than what we have.
	 */

	if (dInfoPtr->lineUpdateTimer == NULL) {
	    dInfoPtr->currentMetricUpdateLine = fromLine;
	    if (action == TK_TEXT_INVALIDATE_DELETE) {
		lineCount = 0;
	    }

Changes to tests/text.test.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# This file is a Tcl script to test the code in the file tkText.c.
# This file is organized in the standard fashion for Tcl tests.
#
# Copyright (c) 1992-1994 The Regents of the University of California.
# Copyright (c) 1994-1996 Sun Microsystems, Inc.
# Copyright (c) 1998-1999 by Scriptics Corporation.
# All rights reserved.

package require tcltest 2.1
eval tcltest::configure $argv
tcltest::loadTestedCommands
namespace import -force tcltest::test

# Create entries in the option database to be sure that geometry options
# like border width have predictable values.

option add *Text.borderWidth 2
option add *Text.highlightThickness 2
option add *Text.font {Courier -12}

text .t -width 20 -height 10













|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# This file is a Tcl script to test the code in the file tkText.c.
# This file is organized in the standard fashion for Tcl tests.
#
# Copyright (c) 1992-1994 The Regents of the University of California.
# Copyright (c) 1994-1996 Sun Microsystems, Inc.
# Copyright (c) 1998-1999 by Scriptics Corporation.
# All rights reserved.

package require tcltest 2.1
eval tcltest::configure $argv
tcltest::loadTestedCommands
namespace import -force tcltest::test

# Create entries in the odeption database to be sure that geometry options
# like border width have predictable values.

option add *Text.borderWidth 2
option add *Text.highlightThickness 2
option add *Text.font {Courier -12}

text .t -width 20 -height 10
3617
3618
3619
3620
3621
3622
3623






























































































3624
3625
3626
3627
3628
3629
3630
    lappend res [.t tag ranges sel]
    .t configure -start 25 -end 30
    lappend res [.t tag ranges sel]
    .t configure -start {} -end {}
    lappend res [.t tag ranges sel]
    set res
} {{10.0 20.0} {6.0 16.0} {6.0 11.0} {1.0 6.0} {1.0 2.0} {} {10.0 20.0}}































































































test text-33.1 {widget dump -command alters tags} {
    .t delete 1.0 end
    .t insert end "abc\n" a "---" {} "def" b "   \n" {} "ghi\n" c
    .t tag configure b -background red
    proc Dumpy {key value index} {
      #puts "KK: $key, $value"







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







3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
    lappend res [.t tag ranges sel]
    .t configure -start 25 -end 30
    lappend res [.t tag ranges sel]
    .t configure -start {} -end {}
    lappend res [.t tag ranges sel]
    set res
} {{10.0 20.0} {6.0 16.0} {6.0 11.0} {1.0 6.0} {1.0 2.0} {} {10.0 20.0}}

test text-32.2 {peer widget -start, -end and deletion (bug 1630262)} -setup {
    destroy .t .pt
    set res {}
} -body {
    text .t
    .t peer create .pt
    for {set i 1} {$i < 100} {incr i} {
      .t insert end "Line $i\n"
    }
    .t configure -startline 5
    # none of the following delete shall crash
    # (all did before fixing bug 1630262)
    # 1. delete on the same line: line1 == line2 in DeleteIndexRange,
    #    and resetView is true neither for .t not for .pt
    .pt delete 2.0 2.2
    # 2. delete just one line: line1 < line2 in DeleteIndexRange,
    #    and resetView is true only for .t, not for .pt
    .pt delete 2.0 3.0
    # 3. delete several lines: line1 < line2 in DeleteIndexRange,
    #    and resetView is true only for .t, not for .pt
    .pt delete 2.0 5.0
    # 4. delete to the end line: line1 < line2 in DeleteIndexRange,
    #    and resetView is true only for .t, not for .pt
    .pt delete 2.0 end
    # this test succeeds provided there is no crash
    set res 1
} -cleanup {
    destroy .pt
} -result {1}

test text-32.3 {peer widget -start, -end and deletion (bug 1630262)} -setup {
    destroy .t .pt
    set res {}
} -body {
    text .t
    .t peer create .pt
    for {set i 1} {$i < 100} {incr i} {
      .t insert end "Line $i\n"
    }
    .t configure -startline 5
    .pt configure -startline 3
    # the following delete shall not crash
    # (it did before fixing bug 1630262)
    .pt delete 2.0 3.0
    # moreover -startline shall be correct
    # (was wrong before fixing bug 1630262)
    lappend res [.t cget -start] [.pt cget -start]
} -cleanup {
    destroy .pt
} -result {4 3}

test text-32.4 {peer widget -start, -end and deletion (bug 1630262)} -setup {
    destroy .t .pt
    set res {}
} -body {
    text .t
    .t peer create .pt
    for {set i 1} {$i < 100} {incr i} {
      .t insert end "Line $i\n"
    }
    .t configure -startline 5 -endline 15
    .pt configure -startline 8 -endline 12
    # .pt now shows a range entirely inside the range of .pt
    # from .t, delete lines located after [.pt cget -end]
    .t delete 9.0 10.0
    # from .t, delete lines straddling [.pt cget -end]
    .t delete 6.0 9.0
    lappend res [.t cget -start] [.t cget -end] [.pt cget -start] [.pt cget -end]
    .t configure -startline 5 -endline 12
    .pt configure -startline 8 -endline 12
    # .pt now shows again a range entirely inside the range of .pt
    # from .t, delete lines located before [.pt cget -start]
    .t delete 2.0 3.0
    # from .t, delete lines straddling [.pt cget -start]
    .t delete 2.0 5.0
    lappend res [.t cget -start] [.t cget -end] [.pt cget -start] [.pt cget -end]
    .t configure -startline 22 -endline 31
    .pt configure -startline 42 -endline 51
    # .t now shows a range entirely before the range of .pt
    # from .t, delete some lines, then do it from .pt
    .t delete 2.0 3.0
    .t delete 2.0 5.0
    .pt delete 2.0 5.0
    lappend res [.t cget -start] [.t cget -end] [.pt cget -start] [.pt cget -end]
    .t configure -startline 55 -endline 75
    .pt configure -startline 60 -endline 70
    # .pt now shows a range entirely inside the range of .t
    # from .t, delete a range straddling the entire range of .pt
    .t delete 3.0 18.0
    lappend res [.t cget -start] [.t cget -end] [.pt cget -start] [.pt cget -end]
} -cleanup {
    destroy .pt
} -result {5 11 8 10 5 8 6 8 22 27 38 44 55 60 57 57}

test text-33.1 {widget dump -command alters tags} {
    .t delete 1.0 end
    .t insert end "abc\n" a "---" {} "def" b "   \n" {} "ghi\n" c
    .t tag configure b -background red
    proc Dumpy {key value index} {
      #puts "KK: $key, $value"