]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/testsuite/lib/tuiterm.exp
Update copyright year range in header of all files managed by GDB
[thirdparty/binutils-gdb.git] / gdb / testsuite / lib / tuiterm.exp
1 # Copyright 2019-2023 Free Software Foundation, Inc.
2
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 3 of the License, or
6 # (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16 # An ANSI terminal emulator for expect.
17
18 namespace eval Term {
19 # Size of the terminal.
20 variable _rows
21 variable _cols
22
23 # Buffer / contents of the terminal.
24 variable _chars
25
26 # Position of the cursor.
27 variable _cur_col
28 variable _cur_row
29
30 variable _attrs
31
32 variable _last_char
33
34 variable _resize_count
35
36 proc _log { what } {
37 verbose "+++ $what"
38 }
39
40 # Call BODY, then log WHAT along with the original and new cursor position.
41 proc _log_cur { what body } {
42 variable _cur_row
43 variable _cur_col
44
45 set orig_cur_row $_cur_row
46 set orig_cur_col $_cur_col
47
48 uplevel $body
49
50 _log "$what, cursor: ($orig_cur_row, $orig_cur_col) -> ($_cur_row, $_cur_col)"
51 }
52
53 # If ARG is empty, return DEF: otherwise ARG. This is useful for
54 # defaulting arguments in CSIs.
55 proc _default {arg def} {
56 if {$arg == ""} {
57 return $def
58 }
59 return $arg
60 }
61
62 # Erase in the line Y from SX to just before EX.
63 proc _clear_in_line {sx ex y} {
64 variable _attrs
65 variable _chars
66 set lattr [array get _attrs]
67 while {$sx < $ex} {
68 set _chars($sx,$y) [list " " $lattr]
69 incr sx
70 }
71 }
72
73 # Erase the lines from SY to just before EY.
74 proc _clear_lines {sy ey} {
75 variable _cols
76 while {$sy < $ey} {
77 _clear_in_line 0 $_cols $sy
78 incr sy
79 }
80 }
81
82 # Beep.
83 proc _ctl_0x07 {} {
84 }
85
86 # Backspace.
87 proc _ctl_0x08 {} {
88 _log_cur "Backspace" {
89 variable _cur_col
90
91 if {$_cur_col > 0} {
92 incr _cur_col -1
93 }
94 }
95 }
96
97 # Linefeed.
98 proc _ctl_0x0a {} {
99 _log_cur "Line feed" {
100 variable _cur_row
101 variable _rows
102
103 incr _cur_row 1
104 if {$_cur_row >= $_rows} {
105 error "FIXME scroll"
106 }
107 }
108 }
109
110 # Carriage return.
111 proc _ctl_0x0d {} {
112 _log_cur "Carriage return" {
113 variable _cur_col
114
115 set _cur_col 0
116 }
117 }
118
119 # Insert Character.
120 #
121 # https://vt100.net/docs/vt510-rm/ICH.html
122 proc _csi_@ {args} {
123 set n [_default [lindex $args 0] 1]
124
125 _log_cur "Insert Character ($n)" {
126 variable _cur_col
127 variable _cur_row
128 variable _cols
129 variable _chars
130
131 # Move characters right of the cursor right by N positions,
132 # starting with the rightmost one.
133 for {set in_col [expr $_cols - $n - 1]} {$in_col >= $_cur_col} {incr in_col -1} {
134 set out_col [expr $in_col + $n]
135 set _chars($out_col,$_cur_row) $_chars($in_col,$_cur_row)
136 }
137
138 # Write N blank spaces starting from the cursor.
139 _clear_in_line $_cur_col [expr $_cur_col + $n] $_cur_row
140 }
141 }
142
143 # Cursor Up.
144 #
145 # https://vt100.net/docs/vt510-rm/CUU.html
146 proc _csi_A {args} {
147 set arg [_default [lindex $args 0] 1]
148
149 _log_cur "Cursor Up ($arg)" {
150 variable _cur_row
151
152 set _cur_row [expr {max ($_cur_row - $arg, 0)}]
153 }
154 }
155
156 # Cursor Down.
157 #
158 # https://vt100.net/docs/vt510-rm/CUD.html
159 proc _csi_B {args} {
160 set arg [_default [lindex $args 0] 1]
161
162 _log_cur "Cursor Down ($arg)" {
163 variable _cur_row
164 variable _rows
165
166 set _cur_row [expr {min ($_cur_row + $arg, $_rows - 1)}]
167 }
168 }
169
170 # Cursor Forward.
171 #
172 # https://vt100.net/docs/vt510-rm/CUF.html
173 proc _csi_C {args} {
174 set arg [_default [lindex $args 0] 1]
175
176 _log_cur "Cursor Forward ($arg)" {
177 variable _cur_col
178 variable _cols
179
180 set _cur_col [expr {min ($_cur_col + $arg, $_cols - 1)}]
181 }
182 }
183
184 # Cursor Backward.
185 #
186 # https://vt100.net/docs/vt510-rm/CUB.html
187 proc _csi_D {args} {
188 set arg [_default [lindex $args 0] 1]
189
190 _log_cur "Cursor Backward ($arg)" {
191 variable _cur_col
192
193 set _cur_col [expr {max ($_cur_col - $arg, 0)}]
194 }
195 }
196
197 # Cursor Next Line.
198 #
199 # https://vt100.net/docs/vt510-rm/CNL.html
200 proc _csi_E {args} {
201 set arg [_default [lindex $args 0] 1]
202
203 _log_cur "Cursor Next Line ($arg)" {
204 variable _cur_col
205 variable _cur_row
206 variable _rows
207
208 set _cur_col 0
209 set _cur_row [expr {min ($_cur_row + $arg, $_rows - 1)}]
210 }
211 }
212
213 # Cursor Previous Line.
214 #
215 # https://vt100.net/docs/vt510-rm/CPL.html
216 proc _csi_F {args} {
217 set arg [_default [lindex $args 0] 1]
218
219 _log_cur "Cursor Previous Line ($arg)" {
220 variable _cur_col
221 variable _cur_row
222 variable _rows
223
224 set _cur_col 0
225 set _cur_row [expr {max ($_cur_row - $arg, 0)}]
226 }
227 }
228
229 # Cursor Horizontal Absolute.
230 #
231 # https://vt100.net/docs/vt510-rm/CHA.html
232 proc _csi_G {args} {
233 set arg [_default [lindex $args 0] 1]
234
235 _log_cur "Cursor Horizontal Absolute ($arg)" {
236 variable _cur_col
237 variable _cols
238
239 set _cur_col [expr {min ($arg - 1, $_cols)}]
240 }
241 }
242
243 # Cursor Position.
244 #
245 # https://vt100.net/docs/vt510-rm/CUP.html
246 proc _csi_H {args} {
247 set row [_default [lindex $args 0] 1]
248 set col [_default [lindex $args 1] 1]
249
250 _log_cur "Cursor Position ($row, $col)" {
251 variable _cur_col
252 variable _cur_row
253
254 set _cur_row [expr {$row - 1}]
255 set _cur_col [expr {$col - 1}]
256 }
257 }
258
259 # Cursor Horizontal Forward Tabulation.
260 #
261 # https://vt100.net/docs/vt510-rm/CHT.html
262 proc _csi_I {args} {
263 set n [_default [lindex $args 0] 1]
264
265 _log_cur "Cursor Horizontal Forward Tabulation ($n)" {
266 variable _cur_col
267 variable _cols
268
269 incr _cur_col [expr {$n * 8 - $_cur_col % 8}]
270 if {$_cur_col >= $_cols} {
271 set _cur_col [expr {$_cols - 1}]
272 }
273 }
274 }
275
276 # Erase in Display.
277 #
278 # https://vt100.net/docs/vt510-rm/ED.html
279 proc _csi_J {args} {
280 set arg [_default [lindex $args 0] 0]
281
282 _log_cur "Erase in Display ($arg)" {
283 variable _cur_col
284 variable _cur_row
285 variable _rows
286 variable _cols
287
288 if {$arg == 0} {
289 # Cursor (inclusive) to end of display.
290 _clear_in_line $_cur_col $_cols $_cur_row
291 _clear_lines [expr {$_cur_row + 1}] $_rows
292 } elseif {$arg == 1} {
293 # Beginning of display to cursor (inclusive).
294 _clear_lines 0 $_cur_row
295 _clear_in_line 0 [expr $_cur_col + 1] $_cur_row
296 } elseif {$arg == 2} {
297 # Entire display.
298 _clear_lines 0 $_rows
299 }
300 }
301 }
302
303 # Erase in Line.
304 #
305 # https://vt100.net/docs/vt510-rm/EL.html
306 proc _csi_K {args} {
307 set arg [_default [lindex $args 0] 0]
308
309 _log_cur "Erase in Line ($arg)" {
310 variable _cur_col
311 variable _cur_row
312 variable _cols
313
314 if {$arg == 0} {
315 # Cursor (inclusive) to end of line.
316 _clear_in_line $_cur_col $_cols $_cur_row
317 } elseif {$arg == 1} {
318 # Beginning of line to cursor (inclusive).
319 _clear_in_line 0 [expr $_cur_col + 1] $_cur_row
320 } elseif {$arg == 2} {
321 # Entire line.
322 _clear_in_line 0 $_cols $_cur_row
323 }
324 }
325 }
326
327 # Insert Line
328 #
329 # https://vt100.net/docs/vt510-rm/IL.html
330 proc _csi_L {args} {
331 set arg [_default [lindex $args 0] 1]
332
333 _log_cur "Insert Line ($arg)" {
334 variable _cur_col
335 variable _cur_row
336 variable _rows
337 variable _cols
338 variable _chars
339
340 set y [expr $_rows - 2]
341 set next_y [expr $y + $arg]
342 while {$y >= $_cur_row} {
343 for {set x 0} {$x < $_cols} {incr x} {
344 set _chars($x,$next_y) $_chars($x,$y)
345 }
346 incr y -1
347 incr next_y -1
348 }
349
350 _clear_lines $_cur_row [expr $_cur_row + $arg]
351 }
352 }
353
354 # Delete line.
355 #
356 # https://vt100.net/docs/vt510-rm/DL.html
357 proc _csi_M {args} {
358 set count [_default [lindex $args 0] 1]
359
360 _log_cur "Delete line ($count)" {
361 variable _cur_row
362 variable _rows
363 variable _cols
364 variable _chars
365
366 set y $_cur_row
367 set next_y [expr {$y + $count}]
368 while {$next_y < $_rows} {
369 for {set x 0} {$x < $_cols} {incr x} {
370 set _chars($x,$y) $_chars($x,$next_y)
371 }
372 incr y
373 incr next_y
374 }
375 _clear_lines $y $_rows
376 }
377 }
378
379 # Delete Character.
380 #
381 # https://vt100.net/docs/vt510-rm/DCH.html
382 proc _csi_P {args} {
383 set count [_default [lindex $args 0] 1]
384
385 _log_cur "Delete character ($count)" {
386 variable _cur_row
387 variable _cur_col
388 variable _chars
389 variable _cols
390
391 # Move all characters right of the cursor N positions left.
392 set out_col [expr $_cur_col]
393 set in_col [expr $_cur_col + $count]
394
395 while {$in_col < $_cols} {
396 set _chars($out_col,$_cur_row) $_chars($in_col,$_cur_row)
397 incr in_col
398 incr out_col
399 }
400
401 # Clear the rest of the line.
402 _clear_in_line $out_col $_cols $_cur_row
403 }
404 }
405
406 # Pan Down
407 #
408 # https://vt100.net/docs/vt510-rm/SU.html
409 proc _csi_S {args} {
410 set count [_default [lindex $args 0] 1]
411
412 _log_cur "Pan Down ($count)" {
413 variable _cur_col
414 variable _cur_row
415 variable _cols
416 variable _rows
417 variable _chars
418
419 # The following code is written without consideration for
420 # the scroll margins. At this time this comment was
421 # written the tuiterm library doesn't support the scroll
422 # margins. If/when that changes, then the following will
423 # need to be updated.
424
425 set dy 0
426 set y $count
427
428 while {$y < $_rows} {
429 for {set x 0} {$x < $_cols} {incr x} {
430 set _chars($x,$dy) $_chars($x,$y)
431 }
432 incr y 1
433 incr dy 1
434 }
435
436 _clear_lines $dy $_rows
437 }
438 }
439
440 # Pan Up
441 #
442 # https://vt100.net/docs/vt510-rm/SD.html
443 proc _csi_T {args} {
444 set count [_default [lindex $args 0] 1]
445
446 _log_cur "Pan Up ($count)" {
447 variable _cur_col
448 variable _cur_row
449 variable _cols
450 variable _rows
451 variable _chars
452
453 # The following code is written without consideration for
454 # the scroll margins. At this time this comment was
455 # written the tuiterm library doesn't support the scroll
456 # margins. If/when that changes, then the following will
457 # need to be updated.
458
459 set y [expr $_rows - $count]
460 set dy $_rows
461
462 while {$dy >= $count} {
463 for {set x 0} {$x < $_cols} {incr x} {
464 set _chars($x,$dy) $_chars($x,$y)
465 }
466 incr y -1
467 incr dy -1
468 }
469
470 _clear_lines 0 $count
471 }
472 }
473
474 # Erase chars.
475 #
476 # https://vt100.net/docs/vt510-rm/ECH.html
477 proc _csi_X {args} {
478 set n [_default [lindex $args 0] 1]
479
480 _log_cur "Erase chars ($n)" {
481 # Erase characters but don't move cursor.
482 variable _cur_col
483 variable _cur_row
484 variable _attrs
485 variable _chars
486
487 set lattr [array get _attrs]
488 set x $_cur_col
489 for {set i 0} {$i < $n} {incr i} {
490 set _chars($x,$_cur_row) [list " " $lattr]
491 incr x
492 }
493 }
494 }
495
496 # Cursor Backward Tabulation.
497 #
498 # https://vt100.net/docs/vt510-rm/CBT.html
499 proc _csi_Z {args} {
500 set n [_default [lindex $args 0] 1]
501
502 _log_cur "Cursor Backward Tabulation ($n)" {
503 variable _cur_col
504
505 set _cur_col [expr {max (int (($_cur_col - 1) / 8) * 8 - ($n - 1) * 8, 0)}]
506 }
507 }
508
509 # Repeat.
510 #
511 # https://www.xfree86.org/current/ctlseqs.html (See `(REP)`)
512 proc _csi_b {args} {
513 set n [_default [lindex $args 0] 1]
514
515 _log_cur "Repeat ($n)" {
516 variable _last_char
517
518 _insert [string repeat $_last_char $n]
519 }
520 }
521
522 # Vertical Line Position Absolute.
523 #
524 # https://vt100.net/docs/vt510-rm/VPA.html
525 proc _csi_d {args} {
526 set row [_default [lindex $args 0] 1]
527
528 _log_cur "Vertical Line Position Absolute ($row)" {
529 variable _cur_row
530 variable _rows
531
532 set _cur_row [expr min ($row - 1, $_rows - 1)]
533 }
534 }
535
536 # Select Graphic Rendition.
537 #
538 # https://vt100.net/docs/vt510-rm/SGR.html
539 proc _csi_m {args} {
540 _log_cur "Select Graphic Rendition ([join $args {, }])" {
541 variable _attrs
542
543 foreach item $args {
544 switch -exact -- $item {
545 "" - 0 {
546 set _attrs(intensity) normal
547 set _attrs(fg) default
548 set _attrs(bg) default
549 set _attrs(underline) 0
550 set _attrs(reverse) 0
551 }
552 1 {
553 set _attrs(intensity) bold
554 }
555 2 {
556 set _attrs(intensity) dim
557 }
558 4 {
559 set _attrs(underline) 1
560 }
561 7 {
562 set _attrs(reverse) 1
563 }
564 22 {
565 set _attrs(intensity) normal
566 }
567 24 {
568 set _attrs(underline) 0
569 }
570 27 {
571 set _attrs(reverse) 1
572 }
573 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 {
574 set _attrs(fg) $item
575 }
576 39 {
577 set _attrs(fg) default
578 }
579 40 - 41 - 42 - 43 - 44 - 45 - 46 - 47 {
580 set _attrs(bg) $item
581 }
582 49 {
583 set _attrs(bg) default
584 }
585 }
586 }
587 }
588 }
589
590 # Insert string at the cursor location.
591 proc _insert {str} {
592 _log_cur "Inserted string '$str'" {
593 _log "Inserting string '$str'"
594
595 variable _cur_col
596 variable _cur_row
597 variable _rows
598 variable _cols
599 variable _attrs
600 variable _chars
601 set lattr [array get _attrs]
602 foreach char [split $str {}] {
603 _log_cur " Inserted char '$char'" {
604 set _chars($_cur_col,$_cur_row) [list $char $lattr]
605 incr _cur_col
606 if {$_cur_col >= $_cols} {
607 set _cur_col 0
608 incr _cur_row
609 if {$_cur_row >= $_rows} {
610 error "FIXME scroll"
611 }
612 }
613 }
614 }
615 }
616 }
617
618 # Move the cursor to the (0-based) COL and ROW positions.
619 proc _move_cursor { col row } {
620 variable _cols
621 variable _rows
622 variable _cur_col
623 variable _cur_row
624
625 if { $col < 0 || $col >= $_cols } {
626 error "_move_cursor: invalid col value: $col"
627 }
628
629 if { $row < 0 || $row >= $_rows } {
630 error "_move_cursor: invalid row value: $row"
631 }
632
633
634 set _cur_col $col
635 set _cur_row $row
636 }
637
638 # Initialize.
639 proc _setup {rows cols} {
640 global stty_init
641 set stty_init "rows $rows columns $cols"
642
643 variable _rows
644 variable _cols
645 variable _cur_col
646 variable _cur_row
647 variable _attrs
648 variable _resize_count
649
650 set _rows $rows
651 set _cols $cols
652 set _cur_col 0
653 set _cur_row 0
654 set _resize_count 0
655 array set _attrs {
656 intensity normal
657 fg default
658 bg default
659 underline 0
660 reverse 0
661 }
662
663 _clear_lines 0 $_rows
664 }
665
666 # Accept some output from gdb and update the screen.
667 # Return 1 if successful, or 0 if a timeout occurred.
668 proc accept_gdb_output { } {
669 global expect_out
670 gdb_expect {
671 -re "^\[\x07\x08\x0a\x0d\]" {
672 scan $expect_out(0,string) %c val
673 set hexval [format "%02x" $val]
674 _log "wait_for: _ctl_0x${hexval}"
675 _ctl_0x${hexval}
676 }
677 -re "^\x1b(\[0-9a-zA-Z\])" {
678 _log "wait_for: unsupported escape"
679 error "unsupported escape"
680 }
681 -re "^\x1b\\\[(\[0-9;\]*)(\[a-zA-Z@\])" {
682 set cmd $expect_out(2,string)
683 set params [split $expect_out(1,string) ";"]
684 _log "wait_for: _csi_$cmd <<<$expect_out(1,string)>>>"
685 eval _csi_$cmd $params
686 }
687 -re "^\[^\x07\x08\x0a\x0d\x1b\]+" {
688 _insert $expect_out(0,string)
689 variable _last_char
690 set _last_char [string index $expect_out(0,string) end]
691 }
692
693 timeout {
694 # Assume a timeout means we somehow missed the
695 # expected result, and carry on.
696 return 0
697 }
698 }
699
700 return 1
701 }
702
703 # Accept some output from gdb and update the screen. WAIT_FOR is
704 # a regexp matching the line to wait for. Return 0 on timeout, 1
705 # on success.
706 proc wait_for {wait_for} {
707 global gdb_prompt
708 variable _cur_col
709 variable _cur_row
710
711 set prompt_wait_for "$gdb_prompt \$"
712
713 while 1 {
714 if { [accept_gdb_output] == 0 } {
715 return 0
716 }
717
718 # If the cursor appears just after the prompt, return. It
719 # isn't reliable to check this only after an insertion,
720 # because curses may make "unusual" redrawing decisions.
721 if {$wait_for == "$prompt_wait_for"} {
722 set prev [get_line $_cur_row $_cur_col]
723 } else {
724 set prev [get_line $_cur_row]
725 }
726 if {[regexp -- $wait_for $prev]} {
727 if {$wait_for == "$prompt_wait_for"} {
728 break
729 }
730 set wait_for $prompt_wait_for
731 }
732 }
733
734 return 1
735 }
736
737 # Accept some output from gdb and update the screen. Wait for the screen
738 # region X/Y/WIDTH/HEIGTH to matches REGEXP. Return 0 on timeout, 1 on
739 # success.
740 proc wait_for_region_contents {x y width height regexp} {
741 while 1 {
742 if { [accept_gdb_output] == 0 } {
743 return 0
744 }
745
746 if { [check_region_contents_p $x $y $width $height $regexp] } {
747 break
748 }
749 }
750
751 return 1
752 }
753
754 # Like ::clean_restart, but ensures that gdb starts in an
755 # environment where the TUI can work. ROWS and COLS are the size
756 # of the terminal. EXECUTABLE, if given, is passed to
757 # clean_restart.
758 proc clean_restart {rows cols {executable {}}} {
759 global env stty_init
760 save_vars {env(TERM) stty_init ::GDBFLAGS} {
761 setenv TERM ansi
762 _setup $rows $cols
763
764 # Make GDB not print the directory names. Use this setting to
765 # remove the differences in test runs due to varying directory
766 # names.
767 append ::GDBFLAGS " -ex \"set filename-display basename\""
768
769 if {$executable == ""} {
770 ::clean_restart
771 } else {
772 ::clean_restart $executable
773 }
774 ::gdb_test_no_output "set pagination off"
775 }
776 }
777
778 # Setup ready for starting the tui, but don't actually start it.
779 # Returns 1 on success, 0 if TUI tests should be skipped.
780 proc prepare_for_tui {} {
781 if {[skip_tui_tests]} {
782 return 0
783 }
784
785 gdb_test_no_output "set tui border-kind ascii"
786 gdb_test_no_output "maint set tui-resize-message on"
787 return 1
788 }
789
790 # Start the TUI. Returns 1 on success, 0 if TUI tests should be
791 # skipped.
792 proc enter_tui {} {
793 if {![prepare_for_tui]} {
794 return 0
795 }
796
797 command_no_prompt_prefix "tui enable"
798 return 1
799 }
800
801 # Send the command CMD to gdb, then wait for a gdb prompt to be
802 # seen in the TUI. CMD should not end with a newline -- that will
803 # be supplied by this function.
804 proc command {cmd} {
805 global gdb_prompt
806 send_gdb "$cmd\n"
807 set str [string_to_regexp $cmd]
808 set str "^$gdb_prompt $str"
809 wait_for $str
810 }
811
812 # As proc command, but don't wait for a initial prompt. This is used for
813 # inital terminal commands, where there's no prompt yet.
814 proc command_no_prompt_prefix {cmd} {
815 send_gdb "$cmd\n"
816 set str [string_to_regexp $cmd]
817 wait_for "^$str"
818 }
819
820 # Return the text of screen line N, without attributes. Lines are
821 # 0-based. If C is given, stop before column C. Columns are also
822 # zero-based.
823 proc get_line {n {c ""}} {
824 variable _rows
825 # This can happen during resizing, if the cursor seems to
826 # temporarily be off-screen.
827 if {$n >= $_rows} {
828 return ""
829 }
830
831 set result ""
832 variable _cols
833 variable _chars
834 set c [_default $c $_cols]
835 set x 0
836 while {$x < $c} {
837 append result [lindex $_chars($x,$n) 0]
838 incr x
839 }
840 return $result
841 }
842
843 # Get just the character at (X, Y).
844 proc get_char {x y} {
845 variable _chars
846 return [lindex $_chars($x,$y) 0]
847 }
848
849 # Get the entire screen as a string.
850 proc get_all_lines {} {
851 variable _rows
852 variable _cols
853 variable _chars
854
855 set result ""
856 for {set y 0} {$y < $_rows} {incr y} {
857 for {set x 0} {$x < $_cols} {incr x} {
858 append result [lindex $_chars($x,$y) 0]
859 }
860 append result "\n"
861 }
862
863 return $result
864 }
865
866 # Get the text just before the cursor.
867 proc get_current_line {} {
868 variable _cur_col
869 variable _cur_row
870 return [get_line $_cur_row $_cur_col]
871 }
872
873 # Helper function for check_box. Returns empty string if the box
874 # is found, description of why not otherwise.
875 proc _check_box {x y width height} {
876 set x2 [expr {$x + $width - 1}]
877 set y2 [expr {$y + $height - 1}]
878
879 verbose -log "_check_box x=$x, y=$y, x2=$x2, y2=$y2, width=$width, height=$height"
880
881 set c [get_char $x $y]
882 if {$c != "+"} {
883 return "ul corner is $c, not +"
884 }
885
886 set c [get_char $x $y2]
887 if {$c != "+"} {
888 return "ll corner is $c, not +"
889 }
890
891 set c [get_char $x2 $y]
892 if {$c != "+"} {
893 return "ur corner is $c, not +"
894 }
895
896 set c [get_char $x2 $y2]
897 if {$c != "+"} {
898 return "lr corner is $c, not +"
899 }
900
901 # Note we do not check the full horizonal borders of the box.
902 # The top will contain a title, and the bottom may as well, if
903 # it is overlapped by some other border. However, at most a
904 # title should appear as '+-VERY LONG TITLE-+', so we can
905 # check for the '+-' on the left, and '-+' on the right.
906 set c [get_char [expr {$x + 1}] $y]
907 if {$c != "-"} {
908 return "ul title padding is $c, not -"
909 }
910
911 set c [get_char [expr {$x2 - 1}] $y]
912 if {$c != "-"} {
913 return "ul title padding is $c, not -"
914 }
915
916 # Now check the vertical borders.
917 for {set i [expr {$y + 1}]} {$i < $y2 - 1} {incr i} {
918 set c [get_char $x $i]
919 if {$c != "|"} {
920 return "left side $i is $c, not |"
921 }
922
923 set c [get_char $x2 $i]
924 if {$c != "|"} {
925 return "right side $i is $c, not |"
926 }
927 }
928
929 return ""
930 }
931
932 # Check for a box at the given coordinates.
933 proc check_box {test_name x y width height} {
934 dump_box $x $y $width $height
935 set why [_check_box $x $y $width $height]
936 if {$why == ""} {
937 pass $test_name
938 } else {
939 fail "$test_name ($why)"
940 }
941 }
942
943 # Check whether the text contents of the terminal match the
944 # regular expression. Note that text styling is not considered.
945 proc check_contents {test_name regexp} {
946 dump_screen
947 set contents [get_all_lines]
948 gdb_assert {[regexp -- $regexp $contents]} $test_name
949 }
950
951 # Get the region of the screen described by X, Y, WIDTH,
952 # and HEIGHT, and separate the lines using SEP.
953 proc get_region { x y width height sep } {
954 variable _chars
955
956 # Grab the contents of the box, join each line together
957 # using $sep.
958 set result ""
959 for {set yy $y} {$yy < [expr {$y + $height}]} {incr yy} {
960 if {$yy > $y} {
961 # Add the end of line sequence only if this isn't the
962 # first line.
963 append result $sep
964 }
965 for {set xx $x} {$xx < [expr {$x + $width}]} {incr xx} {
966 append result [lindex $_chars($xx,$yy) 0]
967 }
968 }
969 return $result
970 }
971
972 # Check that the region of the screen described by X, Y, WIDTH,
973 # and HEIGHT match REGEXP. This is like check_contents except
974 # only part of the screen is checked. This can be used to check
975 # the contents within a box (though check_box_contents is a better
976 # choice for boxes with a border). Return 1 if check succeeded.
977 proc check_region_contents_p { x y width height regexp } {
978 variable _chars
979 dump_box $x $y $width $height
980
981 # Now grab the contents of the box, join each line together
982 # with a '\r\n' sequence and match against REGEXP.
983 set result [get_region $x $y $width $height "\r\n"]
984 return [regexp -- $regexp $result]
985 }
986
987 # Check that the region of the screen described by X, Y, WIDTH,
988 # and HEIGHT match REGEXP. As check_region_contents_p, but produce
989 # a pass/fail message.
990 proc check_region_contents { test_name x y width height regexp } {
991 set ok [check_region_contents_p $x $y $width $height $regexp]
992 gdb_assert {$ok} $test_name
993 }
994
995 # Check the contents of a box on the screen. This is a little
996 # like check_contents, but doens't check the whole screen
997 # contents, only the contents of a single box. This procedure
998 # includes (effectively) a call to check_box to ensure there is a
999 # box where expected, if there is then the contents of the box are
1000 # matched against REGEXP.
1001 proc check_box_contents {test_name x y width height regexp} {
1002 variable _chars
1003
1004 dump_box $x $y $width $height
1005 set why [_check_box $x $y $width $height]
1006 if {$why != ""} {
1007 fail "$test_name (box check: $why)"
1008 return
1009 }
1010
1011 check_region_contents $test_name [expr {$x + 1}] [expr {$y + 1}] \
1012 [expr {$width - 2}] [expr {$height - 2}] $regexp
1013 }
1014
1015 # A debugging function to dump the current screen, with line
1016 # numbers.
1017 proc dump_screen {} {
1018 variable _rows
1019 variable _cols
1020 variable _cur_row
1021 variable _cur_col
1022
1023 verbose -log "Screen Dump (size $_cols columns x $_rows rows, cursor at column $_cur_col, row $_cur_row):"
1024
1025 for {set y 0} {$y < $_rows} {incr y} {
1026 set fmt [format %5d $y]
1027 verbose -log "$fmt [get_line $y]"
1028 }
1029 }
1030
1031 # A debugging function to dump a box from the current screen, with line
1032 # numbers.
1033 proc dump_box { x y width height } {
1034 verbose -log "Box Dump ($width x $height) @ ($x, $y):"
1035 set region [get_region $x $y $width $height "\n"]
1036 set lines [split $region "\n"]
1037 set nr $y
1038 foreach line $lines {
1039 set fmt [format %5d $nr]
1040 verbose -log "$fmt $line"
1041 incr nr
1042 }
1043 }
1044
1045 # Resize the terminal.
1046 proc _do_resize {rows cols} {
1047 variable _chars
1048 variable _rows
1049 variable _cols
1050
1051 set old_rows [expr {min ($_rows, $rows)}]
1052 set old_cols [expr {min ($_cols, $cols)}]
1053
1054 # Copy locally.
1055 array set local_chars [array get _chars]
1056 unset _chars
1057
1058 set _rows $rows
1059 set _cols $cols
1060 _clear_lines 0 $_rows
1061
1062 for {set x 0} {$x < $old_cols} {incr x} {
1063 for {set y 0} {$y < $old_rows} {incr y} {
1064 set _chars($x,$y) $local_chars($x,$y)
1065 }
1066 }
1067 }
1068
1069 proc resize {rows cols} {
1070 variable _rows
1071 variable _cols
1072 variable _resize_count
1073
1074 # expect handles each argument to stty separately. This means
1075 # that gdb will see SIGWINCH twice. Rather than rely on this
1076 # behavior (which, after all, could be changed), we make it
1077 # explicit here. This also simplifies waiting for the redraw.
1078 _do_resize $rows $_cols
1079 stty rows $_rows < $::gdb_tty_name
1080 # Due to the strange column resizing behavior, and because we
1081 # don't care about this intermediate resize, we don't check
1082 # the size here.
1083 wait_for "@@ resize done $_resize_count"
1084 incr _resize_count
1085 # Somehow the number of columns transmitted to gdb is one less
1086 # than what we request from expect. We hide this weird
1087 # details from the caller.
1088 _do_resize $_rows $cols
1089 stty columns [expr {$_cols + 1}] < $::gdb_tty_name
1090 wait_for "@@ resize done $_resize_count, size = ${_cols}x${rows}"
1091 incr _resize_count
1092 }
1093 }