Some checks failed
		
		
	
	Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
				
			
		
			
				
	
	
		
			1368 lines
		
	
	
		
			71 KiB
		
	
	
	
		
			Tcl
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1368 lines
		
	
	
		
			71 KiB
		
	
	
	
		
			Tcl
		
	
	
		
			Executable File
		
	
	
	
	
| #!/bin/sh  
 | |
| #-*-tcl-*-  
 | |
| # the next line restarts using wish \
 | |
| exec wish8.6 "$0" -- ${1+"$@"}
 | |
| 
 | |
| 
 | |
| set version 3.0
 | |
| 
 | |
| ###############################################################################################
 | |
| #
 | |
| # VisualREGEXP -- A graphical front-end to wirte/debug regular expression
 | |
| # (c) 2000-2002 Laurent Riesterer
 | |
| #
 | |
| # VisualREGEXP Home Page: http://laurent.riesterer.free.fr/regexp
 | |
| #
 | |
| #----------------------------------------------------------------------------------------------
 | |
| #
 | |
| # Usage: tkregexp <sampleFile>
 | |
| #
 | |
| #----------------------------------------------------------------------------------------------
 | |
| #
 | |
| # This program is free software; you can redistribute it and/or modify  
 | |
| # it under the terms of the GNU General Public License as published by  
 | |
| # the Free Software Foundation; either version 2 of the License, or  
 | |
| # (at your option) any later version.  
 | |
| #  
 | |
| # This program is distributed in the hope that it will be useful,  
 | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of  
 | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
 | |
| # GNU General Public License for more details.  
 | |
| #  
 | |
| # You should have received a copy of the GNU General Public License  
 | |
| # along with this program; if not, write to the Free Software  
 | |
| # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA  
 | |
| #
 | |
| ###############################################################################################
 | |
| 
 | |
| 
 | |
| 
 | |
| #----------------------------------------------------------------------------------------------
 | |
| # SOME CUSTOMIZATION CAN BE DONE BY MODIFYING VARIABLES BELOW
 | |
| #----------------------------------------------------------------------------------------------
 | |
| 
 | |
| # default geometry
 | |
| set geometry            800x600+0+0
 | |
| # main font used to display the text
 | |
| if {$tcl_platform(platform) == "windows"} {
 | |
|         set font_regexp     {Courier 10}
 | |
|         set font_replace    {Courier 10}
 | |
|         set font_sample     {Courier 10}
 | |
| } else {
 | |
|         set font_regexp                9x15
 | |
|         set font_replace        9x15
 | |
|         set font_sample                9x15
 | |
| }
 | |
| # the font used in the popup menu (use ---- to get a separator, else format is {font size ?bold?}
 | |
| set fonts                                 {{Courier 8} {Courier 9} {Courier 10} {Courier 11} {Courier 12}
 | |
|                                                  ----
 | |
|                                              {Arial 8} {Arial 9} {Arial 10} {Arial 11} {Arial 12}
 | |
|                                              ----
 | |
|                                              8x13 8x13bold 9x15 9x15bold 10x20}
 | |
| # the colors for the different matching groups
 | |
| set colors              {#ff0000 #0000ff darkgreen violetred #ff9000 #537db9 #e4c500     firebrick darkgoldenrod hotpink}
 | |
| set bgcolors            {#ffe6e6 #e6e6ff #e6ffe6   #efd5e1   #fef3e5 #d6dce5 lightyellow white    white        white}
 | |
| # use background color in sample by default ? (1 use, 0 do not use)
 | |
| set background                        0
 | |
| # background color to visualize the non-reporting group (?:...)
 | |
| set color_noreport      #fffdc4
 | |
| # background color to visualize the lookhead group (?=...) and (?!...)
 | |
| set color_lookahead     wheat
 | |
| # show/hide help about control characters in regexp
 | |
| set show_help                        0
 | |
| # show/hide history windows on startup
 | |
| set history                                0
 | |
| # mode to use on startup (select/concat = raw, select/insert new lines = nl, replace = replace)
 | |
| set mode                                replace
 | |
| # database of some regexp to appear in the "Insert regexp" menu
 | |
| set regexp_db {
 | |
|         "URL"                        {(?:^|")(http|ftp|mailto):(?://)?(\w+(?:[\.:@]\w+)*?)(?:/|@)([^"\?]*?)(?:\?([^\?"]*?))?(?:$|")}
 | |
|         "IP numbers"         {[12]?[0-9]?[0-9](\.[12]?[0-9]?[0-9]){3}}
 | |
|         "HTML tags"                {<[^<>]+>}
 | |
|         "HTML tag content" {<(\w+)[^>]*?>(.*?)</\1>}
 | |
|         "vars and arrays (PHP)" {\$[^0-9 ]{1}[a-zA-Z0-9_]*((?:\[[a-zA-Z0-9_'\"]+\])*)}
 | |
|         "dd/mm/yyyy"        {(0[1-9]|[12][0-9]|3[01])(/|-)(0[1-9]|1[12])(/|-)[12][0-9]{3}}
 | |
|         "mm/dd/yyyy"        {(0[1-9]|1[12])(/|-)(0[1-9]|[12][0-9]|3[01])(/|-)[12][0-9]{3}}
 | |
|         "hh:mm"                        {([01][0-9]|2[0-3]):[0-5][0-9]}
 | |
|         "user@domain.net" {[A-Za-z0-9_.-]+@([A-Za-z0-9_]+\.)+[A-Za-z]{2,4}}
 | |
| }
 | |
| 
 | |
| 
 | |
| #----------------------------------------------------------------------------------------------
 | |
| # DO NOT MODIFY BELOW THIS POINT
 | |
| #----------------------------------------------------------------------------------------------
 | |
| 
 | |
| namespace eval regexp {} {
 | |
|         set data(v:undo:index) 0
 | |
|         set data(v:undo:sample) ""
 | |
|         set data(v:dir) "."
 | |
|         set data(v:file) "untitled.txt"
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------------------------
 | |
| #        Main GUI
 | |
| #----------------------------------------------------------------------------------------------
 | |
| 
 | |
| proc regexp::gui {} {
 | |
| variable data
 | |
| global colors bgcolors color_noreport color_lookahead geometry show_help regexp_db history
 | |
| global tcl_platform
 | |
| 
 | |
| 
 | |
|         set top ""
 | |
| 
 | |
|         # frame for regexp
 | |
|         set w [frame $top.regexp -bd 2 -relief groove]
 | |
|                 # options
 | |
|                 set fo [frame $w.options]
 | |
|                 set sep 0
 | |
|                 foreach option {nocase all - line lineanchor linestop - inline} \
 | |
|                                 label {nocase all - line "lineanchor (k)" "linestop (m)" - inline} \
 | |
|                                 underline {0 0 - 0 12 10 - 0} {
 | |
|                         if {$option != "-"} {
 | |
|                                 checkbutton $fo.$option -text $label -bd 1 -underline $underline \
 | |
|                                                         -variable regexp::data(v:$option) \
 | |
|                                                         -offvalue "" -onvalue "-$option"
 | |
|                                 set data(v:$option) ""
 | |
|                                 pack $fo.$option -side left
 | |
|                         } else {
 | |
|                                 pack [frame $fo.[incr sep] -width 40] -side left
 | |
|                         }
 | |
|                 }
 | |
|                 # text for regexp entry
 | |
|                 set data(w:regexp) [text $w.regexp -wrap char -bg white -font $::font_regexp \
 | |
|                                                                         -selectbackground lightblue -selectborderwidth 0 \
 | |
|                                                                         -width 1 -height 3 -bd 1]
 | |
|                 if {$tcl_platform(platform) == "windows"} {
 | |
|                         set sfont {Courier 8}
 | |
|                         set sbfont {Courier 8 bold}
 | |
|                 } else {
 | |
|                         set sfont 6x13
 | |
|                         set sbfont 6x13bold
 | |
|                 }
 | |
|                 set data(w:help) [text $w.help -font $sfont -bd 0 -height 9 -wrap none -bg [$w cget -bg]]
 | |
|                 $w.help insert 1.0 "\n\n\n\n\n\n\n\n"
 | |
|                 $w.help insert 1.0 {\a  alert              \n     newline     \0    char 0       \d [[:digit:]]    \A beginning of the string }
 | |
|                 $w.help insert 2.0 {\b  backspace          \r     carriage    \xyz  octal code   \D [^[:digit:]]   \Z end of string }
 | |
|                 $w.help insert 3.0 {\B  synomyn for \      \t     tab                            \s [[:space:]]    \m beginning of a word}
 | |
|                 $w.help insert 4.0 {\cX same as X & 0x1F   \uwxyz unicode     \x    backref      \S [^[:space:]]   \M end of a word}
 | |
|                 $w.help insert 5.0 {\e  ESC                \v     vert tab                       \w [[:alnum:]_]   \y beginning or end of a word}
 | |
|                 $w.help insert 6.0 {\f  form feed          \xhhh  hexa code                      \W [^[:alnum:]_]  \Y not beginning or end of a word}
 | |
|                 $w.help insert 7.0 {----------------------------------------------------------------------------------------------------------------}
 | |
|                 $w.help insert 8.0 {    ungreedy:          ?? single optional *? zero-many       +? at least one   {n,m}? ungreedy quantifiers}
 | |
|                 $w.help insert 9.0 {(?:) ghost group       (?=) lookahead     (?!) neg. lookahead}
 | |
|                 $w.help tag configure bold -font $sbfont
 | |
|                 foreach line {1 2 3 4 5 6} {
 | |
|                         foreach {min max} {0 2 23 25 42 44 61 63 79 82} {
 | |
|                                 $w.help tag add bold $line.$min $line.$max
 | |
|                         }
 | |
|                 }
 | |
|                 $w.help tag remove bold 2.43 2.44 4.43 4.44
 | |
|                 # buttons & selection of match
 | |
|                 set fb [frame $w.b]
 | |
|                 button $fb.go -text "Go" -underline 0 -command "regexp::go" -bd 1 -width 5
 | |
|                 button $fb.clear -text "Clear (z)" -underline 7 -command "regexp::clear" -bd 1 -width 5
 | |
|                 pack $fb.go [frame $fb.00 -width 10] $fb.clear -side left -pady 5
 | |
| 
 | |
|                 # selection - buttons for match level
 | |
|                 label $fb.sep
 | |
|                 label $fb.l -text "Select:"
 | |
|                 pack $fb.sep -side left -fill x -expand true
 | |
|                 pack $fb.l -side left -padx 5 -pady 5
 | |
|                 set i 0
 | |
|                 foreach c $colors t {match 1 2 3 4 5 6 7 8 9} {
 | |
|                         button $fb.$i -text $t -fg $c -bd 1 -padx 0 -width 6 -command "regexp::select $i"
 | |
|                         pack $fb.$i -side left -fill y -pady 5
 | |
|                         incr i
 | |
|                 }
 | |
|                 # text for replace
 | |
|                 set f [frame $w.replace]
 | |
|                         set data(w:replace) [text $f.replace -wrap char -bg white -font $::font_replace \
 | |
|                                                                                 -selectbackground lightblue -selectborderwidth 0 \
 | |
|                                                                                 -width 1 -height 2 -bd 1]
 | |
|                         button $f.do -text "Replace" -underline 0 -bd 1 -width 9 -command "regexp::replace"
 | |
|                         label $f.nb -textvariable regexp::data(v:nbreplace) -width 12 -anchor e
 | |
|                         pack $data(w:replace) -side left -expand true -fill both -pady 5 -padx 5
 | |
|                         pack $f.do -side left -pady 5
 | |
|                         pack $f.nb -side left -pady 5 -padx 5
 | |
|                 # layout
 | |
|                 pack [frame $w.0 -height 5] $data(w:regexp) -side top -anchor w -padx 5 -expand true -fill both
 | |
|                 pack $fo $fb -side top -anchor w -padx 5 -expand true -fill both
 | |
|                 pack $fb -side top -anchor w -padx 5 -expand true -fill both
 | |
|                 set data(w:allreplace) $f
 | |
| 
 | |
|         # frame for sample
 | |
|         set w [frame $top.sample -bd 2 -relief groove]
 | |
|                 set w [frame $w.inner]
 | |
|                 pack $top.sample.inner -padx 5 -pady 5 -fill both -expand true
 | |
|                 # text for sample highlighting
 | |
|                 set data(w:sample) [text $w.sample  -bg white -font $::font_sample -bd 1 -width 1 -height 1 \
 | |
|                                                                         -selectbackground lightblue -selectborderwidth 0 \
 | |
|                                                                         -yscrollcommand "$w.sy set" -xscrollcommand "$w.sx set"]
 | |
|                 scrollbar $w.sy -command "$w.sample yview" -orient vertical -bd 1
 | |
|                 scrollbar $w.sx -command "$w.sample xview" -orient horizontal -bd 1
 | |
|                 # set tags for colors & special
 | |
|                 set data(v:levels) {e0 e1 e2 e3 e4 e5 e6 e7 e8 e9}
 | |
|                 foreach level $data(v:levels) \
 | |
|                                 color $colors {
 | |
|                         $data(w:regexp) tag configure $level -foreground $color
 | |
|                         $data(w:history) tag configure $level -foreground $color
 | |
|                         $data(w:sample) tag configure $level -foreground $color
 | |
|                 }
 | |
|                 $data(w:regexp) tag configure lookahead -background $color_lookahead
 | |
|                 $data(w:regexp) tag configure noreport -background $color_noreport
 | |
|                 $data(w:history) tag configure lookahead -background $color_lookahead
 | |
|                 $data(w:history) tag configure noreport -background $color_noreport
 | |
|                 # options
 | |
|                 set f [frame $w.matches]
 | |
|                         label $f.nb -textvariable regexp::data(v:nbmatches) -anchor w
 | |
|                         set regexp::data(v:nbmatches) "0 matches"
 | |
|                         # button for navigation
 | |
|                         button $f.p -text "Previous" -bd 1 -pady 2 -width 8 -command "regexp::sample:move -1"
 | |
|                         button $f.n -text "Next" -bd 1 -pady 2 -width 8 -command "regexp::sample:move +1"
 | |
|                         set data(v:positions) [list ]
 | |
|                         set data(v:position) 0
 | |
|                         # layout
 | |
|                         pack $f.nb [frame $f.0 -width 15] $f.p $f.n -padx 5 -side left
 | |
|                 
 | |
|                 # layout
 | |
|                 grid $w.sample        $w.sy        -sticky news
 | |
|                 grid $w.sx                x                -sticky news
 | |
|                 grid $w.matches        -                -sticky news
 | |
|                 grid rowconfigure $w {0} -weight 1
 | |
|                 grid columnconfigure $w {0} -weight 1
 | |
| 
 | |
|         # main layout
 | |
|         pack $top.regexp -side top -anchor w -padx 5 -pady 5 -fill x
 | |
|         pack $top.sample -side top -anchor w -padx 5 -pady 5 -expand true -fill both
 | |
|         wm geometry . $geometry
 | |
|         wm title . "Visual REGEXP $::version"
 | |
|         focus $data(w:regexp)
 | |
| 
 | |
|         # main menu
 | |
|         . configure -menu .menubar
 | |
|         set m [menu .menubar -tearoff 0 -bd 1 -activeborderwidth 1]
 | |
|           # file
 | |
|           $m add cascade -menu $m.file -label "File" -underline 0
 | |
|           set mm [menu $m.file -tearoff 0 -bd 1 -activeborderwidth 1]
 | |
|                 $mm add command -label "Load regexp ..." -command "regexp::regexp:load"
 | |
|                 $mm add command -label "Load sample ..." -command "regexp::sample:load" -accelerator "Alt-O"
 | |
|                 $mm add separator
 | |
|                 $mm add command -label "Save sample (auto) ..." -command "regexp::sample:save auto" -accelerator "Alt-S"
 | |
|                 $mm add command -label "Save sample Unix (lf) ..." -command "regexp::sample:save lf"
 | |
|                 $mm add command -label "Save sample Windows (crlf) ..." -command "regexp::sample:save crlf"
 | |
|                 $mm add command -label "Save sample Mac (cr) ..." -command "regexp::sample:save cr"
 | |
|                 $mm add separator
 | |
|                 $mm add command -label "Quit" -underline 0 -command "exit" -accelerator "Alt-Q"
 | |
|           # edit
 | |
|           $m add cascade -menu $m.edit -label "Edit" -underline 0
 | |
|           set mm [menu $m.edit -tearoff 0 -bd 1 -activeborderwidth 1]
 | |
|                 $mm add command -label "Copy regexp to clipboard" -command "regexp::dump" -accelerator "Alt-C"
 | |
|                 $mm add separator
 | |
|                 $mm add command -label "Undo" -command "regexp::unredo:regexp -1" -accelerator "Control-Z"
 | |
|                 $mm add command -label "Redo" -command "regexp::unredo:regexp +1" -accelerator "Control-R"
 | |
|           # view
 | |
|           $m add cascade -menu $m.view -label "View" -underline 0
 | |
|           set mm [menu $m.view -tearoff 0 -bd 1 -activeborderwidth 1]
 | |
|                 set regexp::data(v:background) $::background
 | |
|                 regexp::sample:background
 | |
|                 $mm add checkbutton -label "Show background for matches" -command "regexp::sample:background" \
 | |
|                                 -variable regexp::data(v:background)
 | |
|                 $mm add checkbutton -label "Show regexp help" -command "regexp::regexp:help:toggle" \
 | |
|                                 -variable regexp::data(v:help)
 | |
|                 set regexp::data(v:help) $show_help
 | |
|                 $mm add checkbutton -label "Wrap lines in sample" -variable regexp::data(v:wrap) \
 | |
|                                                 -command "$data(w:sample) configure -wrap \$regexp::data(v:wrap)" \
 | |
|                                                 -offvalue "none" -onvalue "char"
 | |
|                 set regexp::data(v:history) $history
 | |
|                 $mm add checkbutton -label "History of Regexp" -variable regexp::data(v:history) \
 | |
|                                                 -command "if {\$regexp::data(v:history)} {wm deiconify .history} else {wm iconify .history}"
 | |
|           # select mode
 | |
|           $m add cascade -menu $m.select -label "Select mode" -underline 5
 | |
|           set mm [menu $m.select -tearoff 0 -bd 1 -activeborderwidth 1]
 | |
|                 $mm add radiobutton -label "select / concat raw matches" \
 | |
|                                 -variable regexp::data(v:mode) -value "raw" -command regexp::replace:toggle
 | |
|                 $mm add radiobutton -label "select / insert new line between matches" \
 | |
|                                 -variable regexp::data(v:mode) -value "nl" -command regexp::replace:toggle
 | |
|                 $mm add radiobutton -label "replace widget" \
 | |
|                                 -variable regexp::data(v:mode) -value "replace" -command regexp::replace:toggle
 | |
|           # insert well know regexp
 | |
|           $m add cascade -menu $m.insert -label "Insert regexp" -underline 11
 | |
|           set mm [menu $m.insert -tearoff 0 -bd 1 -activeborderwidth 1]
 | |
|                 $mm add command -label "Make regexp ..." -command "regexp::make-regexp"
 | |
|                 $mm add separator
 | |
|                 $mm add command -label "Load patterns ..." -command "regexp::pattern:load"
 | |
|                 $mm add separator
 | |
|                 foreach {n e} $regexp_db {
 | |
|                         $mm add command -label "$n" -command "regexp::regexp:insert [list $e]"
 | |
|                 }
 | |
|                 set data(w:menu) $mm
 | |
|           # help
 | |
|           $m add cascade -menu $m.help -label "Help" -underline 0
 | |
|           set mm [menu $m.help -tearoff 0 -bd 1 -activeborderwidth 1]
 | |
|                 $mm add command -label "Help" -command "regexp::help"
 | |
| 
 | |
| 
 | |
|         # key binding
 | |
|         bind all <Alt-q> "exit"
 | |
|         bind all <Alt-g> "regexp::go"
 | |
|         bind $data(w:regexp) <Return> "regexp::go; break"
 | |
|         bind all <Alt-c> "regexp::dump"
 | |
|         bind all <Alt-r> "regexp::replace"
 | |
|         bind all <Alt-o> "regexp::sample:load"
 | |
|         bind all <Alt-s> "regexp::sample:save auto"
 | |
| 
 | |
|         bind $data(w:regexp) <Control-z> "regexp::unredo:regexp -1"
 | |
|         bind $data(w:regexp) <Control-r> "regexp::unredo:regexp +1"
 | |
| 
 | |
|         bind $data(w:replace) <Control-z> "regexp::undo:sample"
 | |
|         bind $data(w:sample) <Control-z> "regexp::undo:sample"
 | |
| 
 | |
|         bind all <Alt-a> "$fo.all toggle"
 | |
|         bind all <Alt-n> "$fo.nocase toggle"
 | |
|         bind all <Alt-l> "$fo.line toggle"
 | |
|         bind all <Alt-k> "$fo.lineanchor toggle"
 | |
|         bind all <Alt-m> "$fo.linestop toggle"
 | |
|         bind all <Alt-i> "$fo.inline toggle"
 | |
|         bind all <Alt-z> "regexp::clear"
 | |
| 
 | |
|         bind $data(w:regexp) <Control-Key> { # nothing }
 | |
|         bind $data(w:regexp) <Alt-Key> { # nothing }
 | |
|         bind $data(w:regexp) <Meta-Key> { # nothing }
 | |
|         bind $data(w:regexp) <Mod1-Key> { # nothing }
 | |
|         bind $data(w:regexp) <Key> "regexp::undo:regexp:compute %W %K %A"
 | |
| 
 | |
|         bind $data(w:replace) <Control-Tab> "$data(w:replace) insert insert {\\t}; break;"
 | |
|         bind $data(w:sample) <Control-Tab> "$data(w:sample) insert insert {\t}; break;"
 | |
|         # special for regexp Ctrl+letter = \<letter>
 | |
|         bind $data(w:regexp) <Control-Tab> "$data(w:regexp) insert insert {\\t}; break;"
 | |
|         foreach key {a b B e f n r t v u x 0 d D s S w W A Z m M y Y} {
 | |
|                 bind $data(w:regexp) <Control-$key> "$data(w:regexp) insert insert {\\$key}; break;"
 | |
|         }
 | |
|         foreach key {a b B e f n r t v u x 0} {
 | |
|                 bind $data(w:replace) <Control-$key> "$data(w:replace) insert insert {\\$key}; break;"
 | |
|         }
 | |
| 
 | |
|         bind Text <Control-v> {}
 | |
| 
 | |
|         # font selection popup
 | |
|         foreach w {regexp replace sample} {
 | |
|                 set m [menu .fonts_$w -tearoff 0]
 | |
|                 foreach f $::fonts {
 | |
|                         if {$f == "----"} {
 | |
|                                 $m add separator
 | |
|                         } else {
 | |
|                                 $m add command -label $f -command "$data(w:$w) configure -font [list $f]"
 | |
|                         }
 | |
|                 }
 | |
|                 bind $data(w:$w) <3> "tk_popup $m %X %Y"
 | |
|         }
 | |
| 
 | |
|         # some init
 | |
|         set data(v:nocase) "-nocase"
 | |
|         set data(v:all) "-all"
 | |
|         set data(v:wrap) "char"
 | |
|         set regexp::data(v:mode) $::mode
 | |
|         replace:toggle                ;# set bindings
 | |
|         regexp:help:toggle
 | |
| }
 | |
| 
 | |
| proc regexp::pattern:load {{file ""}} {
 | |
| variable data
 | |
| 
 | |
|         # get filename
 | |
|         if {$file == ""} {
 | |
|                 set types [list [list "All" *]]
 | |
|                 set file [tk_getOpenFile -filetypes $types -parent .]
 | |
|             if {$file == ""} {
 | |
|                         return
 | |
|                 }
 | |
|         }
 | |
|         # do it
 | |
|         set in [open $file "r"]
 | |
|         $data(w:menu) delete [expr 4+[llength $::regexp_db]/2] end
 | |
|         while {![eof $in]} {
 | |
|                 set name [gets $in]
 | |
|                 while {$name == ""} {
 | |
|                         set name [gets $in]
 | |
|                 }
 | |
|                 set pattern [gets $in]
 | |
|                 while {$pattern == ""} {
 | |
|                         set pattern [gets $in]
 | |
|                 }
 | |
|                 $data(w:menu) add command -label $name -command "regexp::regexp:insert [list $pattern]"
 | |
|         }
 | |
|         close $in
 | |
| }
 | |
| 
 | |
| 
 | |
| #----------------------------------------------------------------------------------------------
 | |
| #        Main toplevel commands
 | |
| #----------------------------------------------------------------------------------------------
 | |
| 
 | |
| proc regexp::go {} {
 | |
| variable data
 | |
| 
 | |
|         set exp [$data(w:regexp) get 1.0 end-1char]
 | |
|         # check if regexp is OK
 | |
|         if {[catch { regexp -- $exp dummy } errMsg]} {
 | |
|                 tk_messageBox -type ok -icon error -message "Malformed regexp: $errMsg"
 | |
|                 return
 | |
|         }
 | |
|         regexp::regexp:colorize
 | |
|         regexp::sample:colorize
 | |
|         regexp::history:add
 | |
| }
 | |
| 
 | |
| proc regexp::clear {} {
 | |
| variable data
 | |
| 
 | |
|         regexp::history:add
 | |
|         $data(w:regexp) delete 1.0 end
 | |
|         regexp::go
 | |
| }
 | |
| 
 | |
| proc regexp::dump {} {
 | |
| variable data
 | |
| 
 | |
|         # update display
 | |
|         go
 | |
|         # built list of options
 | |
|         set dump "regexp"
 | |
|         foreach option {nocase all   line lineanchor linestop   inline} {
 | |
|                 if {$data(v:$option) != ""} {
 | |
|                         append dump " $data(v:$option)"
 | |
|                 }
 | |
|         }
 | |
|         # build expression
 | |
|         set exp [$data(w:regexp) get 1.0 end-1char]
 | |
|         append dump " -- {$exp} string"
 | |
|         # add variables if needed
 | |
|         if {$data(v:inline) == ""} {
 | |
|                 append dump " match"
 | |
|                 for {set i 1} {$i < $data(v:nblevels)} {incr i} {
 | |
|                         append dump " v$i"
 | |
|                 }
 | |
|         }
 | |
|         # put dump into the clipboard (by creating a hidden entry ... anyone has a better solution?)
 | |
|         destroy .e
 | |
|         entry .e
 | |
|         .e insert 0 $dump
 | |
|         .e selection range 0 end
 | |
|         puts "$dump"
 | |
| }
 | |
| 
 | |
| proc regexp::select {level} {
 | |
| variable data
 | |
| 
 | |
|         # update
 | |
|         go
 | |
|         if {[llength $data(v:result)] == 0} {
 | |
|                 bell
 | |
|                 return
 | |
|         }
 | |
|         # puts regexp
 | |
|         dump
 | |
|         # extract matching parts in sample
 | |
|         set i 0
 | |
|         set newsample ""
 | |
|         foreach match $data(v:result) {
 | |
|                 if {($i % $data(v:nblevels)) == $level} {
 | |
|                         set text [$data(w:sample) get \
 | |
|                                                         [$data(w:sample) index "1.0+[lindex $match 0]chars"] \
 | |
|                                                         [$data(w:sample) index "1.0+[expr [lindex $match 1]+1]chars"]]
 | |
|                         append newsample $text
 | |
|                         if {$data(v:mode) == "nl"} {
 | |
|                                 append newsample "\n"
 | |
|                         }
 | |
|                 }
 | |
|                 incr i
 | |
|         }
 | |
|         $data(w:sample) delete 1.0 end
 | |
|         $data(w:sample) insert 1.0 $newsample
 | |
|         # update with regexp
 | |
|         go
 | |
| }
 | |
| 
 | |
| proc regexp::help {} {
 | |
| global tcl_platform
 | |
| 
 | |
|         toplevel .help
 | |
|         wm title .help "Help"
 | |
|         # logo
 | |
|         label .help.l -image logo
 | |
|         pack .help.l -side top -padx 10 -pady 10
 | |
|         # help text
 | |
|         if {$tcl_platform(platform) == "windows"} {
 | |
|                 text .help.t -bd 2 -relief groove -font {Courier 10}
 | |
|         } else {
 | |
|                 text .help.t -bd 2 -relief groove
 | |
|         }
 | |
|         pack .help.t -side top -padx 20
 | |
|         .help.t tag configure bold -font "[.help.t cget -font] bold"
 | |
|         .help.t insert 1.0 "Version:" bold " $::version
 | |
| 
 | |
| " normal "Usage:" bold " tkregexp <sampleFile>
 | |
| 
 | |
| " normal "Key bindings:" bold " Alt-q               exit
 | |
|               Alt-a               toggle 'all' flag
 | |
|               Alt-n               toggle 'nocase' flag
 | |
|               Alt-l               toggle 'line' flag
 | |
|               Alt-k               toggle 'lineanchor' flag
 | |
|               Alt-m               toggle 'linestop' flag
 | |
|               Alt-i               toggle 'inline' flag
 | |
|               Alt-g               do the highlighting
 | |
|               Return (in regexp)  do the highlighting
 | |
| 
 | |
| " normal "To clipboard:" bold " Put the 'regexp' command with its arguments to the clipboard
 | |
| 
 | |
| " normal "Tips:" bold " 1) To set the sample, either put a filename on the command line,
 | |
|          or just copy & paste it in the sample text window.
 | |
|       2) You can change the default colors or windows size by editing the
 | |
|          first lines of the program file.
 | |
|       3) when typing your regexp you can use Control-Z/Control-R
 | |
|          to undo/redo the last typing.
 | |
|       4) When using the replace function, using Control-Z restore the value
 | |
|          of the sample before the replacement : you try, retry, reretry, ..."
 | |
|         # ok button
 | |
|         button .help.ok -text "Ok" -width 10 -default active -command "destroy .help"
 | |
|         pack .help.ok -side bottom -pady 10
 | |
| }
 | |
| 
 | |
| proc regexp::regexp:help:toggle {} {
 | |
| variable data
 | |
| 
 | |
|         if {$data(v:help) == 0} {
 | |
|                 pack forget $data(w:help)
 | |
|         } else {
 | |
|                 pack $data(w:help) -before $data(w:regexp) -fill x -padx 5
 | |
|         }
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------------------------
 | |
| #        Undo/redo (quick and dirty UNDO/REDO support)
 | |
| #----------------------------------------------------------------------------------------------
 | |
| 
 | |
| proc regexp::undo:sample {} {
 | |
| variable data
 | |
| 
 | |
|         # display result
 | |
|         $data(w:sample) delete 1.0 end
 | |
|         $data(w:sample) insert 1.0 $data(v:undo:sample)
 | |
|         # colorize
 | |
|         go
 | |
| }
 | |
| 
 | |
| proc regexp::unredo:regexp {dir} {
 | |
| variable data
 | |
| 
 | |
|         set index [expr ($data(v:undo:index)+$dir) % 100]
 | |
|         if {![info exists data(v:undo:r$index)]} {
 | |
|                 return
 | |
|         }
 | |
|         set data(v:undo:index) $index
 | |
| 
 | |
|         set t $data(w:regexp)
 | |
|         $t delete 1.0 end
 | |
|         $t insert 1.0 [lindex $data(v:undo:r$index) 1]
 | |
|         $t mark set insert [lindex $data(v:undo:r$index) 0]
 | |
| }
 | |
| 
 | |
| proc regexp::undo:regexp:compute {w k a} {
 | |
| variable data
 | |
| 
 | |
|         if {[string match -nocase "*control*" $k]
 | |
|                         || [string match -nocase "*shift*" $k]
 | |
|                         || [string match -nocase "*alt*" $k]} {
 | |
|                 return
 | |
|         }
 | |
| 
 | |
|         set data(v:undo:r$data(v:undo:index)) [list [$w index insert] [$w get 1.0 end-1char]]
 | |
|         set data(v:undo:index) [expr ($data(v:undo:index)+1) % 100]
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------------------------
 | |
| #        Replace
 | |
| #----------------------------------------------------------------------------------------------
 | |
| 
 | |
| proc regexp::replace {} {
 | |
| variable data
 | |
| 
 | |
|         set exp [$data(w:regexp) get 1.0 end-1char]
 | |
|         set subst [$data(w:replace) get 1.0 end-1char]
 | |
|         if {$exp == ""} {
 | |
|                 set regexp::data(v:nbreplace) "empty regexp"
 | |
|                 return
 | |
|         }
 | |
| 
 | |
|         # get sample & store it for undo
 | |
|         set sample [$data(w:sample) get 1.0 end]
 | |
|         set data(v:undo:sample) $sample
 | |
|         set result [eval regsub $data(v:all) \
 | |
|                                                 $data(v:line) $data(v:lineanchor) $data(v:linestop) \
 | |
|                                                 $data(v:nocase) -- \
 | |
|                                                 [list $exp] [list $sample] [list [subst -nocommands -novariables $subst]] sample]
 | |
|         set regexp::data(v:nbreplace) "$result replaced"
 | |
|         # display result
 | |
|         $data(w:sample) delete 1.0 end
 | |
|         $data(w:sample) insert 1.0 $sample
 | |
| }
 | |
| 
 | |
| proc regexp::replace:toggle {} {
 | |
| variable data
 | |
| 
 | |
|         if {$regexp::data(v:mode) == "replace"} {
 | |
|                 bind $data(w:regexp) <Tab> "focus $data(w:replace); break;"
 | |
|                 bind $data(w:regexp) <Shift-Tab> "focus $data(w:sample); break;"
 | |
|                 catch { bind $data(w:regexp) <ISO_Left_Tab> "focus $data(w:sample); break;" }
 | |
| 
 | |
|                 bind $data(w:replace) <Tab> "focus $data(w:sample); break;"
 | |
|                 bind $data(w:replace) <Shift-Tab> "focus $data(w:regexp); break;"
 | |
|                 catch { bind $data(w:replace) <ISO_Left_Tab> "focus $data(w:regexp); break;" }
 | |
| 
 | |
|                 bind $data(w:sample) <Tab> "focus $data(w:regexp); break;"
 | |
|                 bind $data(w:sample) <Shift-Tab> "focus $data(w:replace); break;"
 | |
|                 catch { bind $data(w:sample) <ISO_Left_Tab> "focus $data(w:replace); break;" }
 | |
| 
 | |
|                 pack $data(w:allreplace) -side top -fill both
 | |
| 
 | |
|         } else {
 | |
|                 bind $data(w:regexp) <Tab> "focus $data(w:sample); break;"
 | |
|                 catch { bind $data(w:regexp) <ISO_Left_Tab> "focus $data(w:sample); break;" }
 | |
| 
 | |
|                 bind $data(w:sample) <Tab> "focus $data(w:regexp); break;"
 | |
|                 catch { bind $data(w:sample) <ISO_Left_Tab> "focus $data(w:regexp); break;" }
 | |
| 
 | |
|                 pack forget $data(w:allreplace)
 | |
|         }
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------------------------
 | |
| #        Manage REGEXP
 | |
| #----------------------------------------------------------------------------------------------
 | |
| 
 | |
| proc regexp::regexp:set {text} {
 | |
| variable data
 | |
| 
 | |
|         $data(w:regexp) delete 1.0 end
 | |
|         $data(w:regexp) insert 1.0 $text
 | |
| }
 | |
| 
 | |
| proc regexp::regexp:colorize {} {
 | |
| variable data
 | |
| 
 | |
|         set exp [$data(w:regexp) get 1.0 end-1char]
 | |
|         set max [string length $exp]
 | |
|         set stack {}
 | |
|         # list format : min max min max ...
 | |
|         set indices [list "report" 0 [string length $exp]]
 | |
|         # search the groups in the regexp
 | |
|         set data(v:nblevels) 1
 | |
|         for {set i 0} {$i < $max} {incr i} {
 | |
|                 set c [string index $exp $i]
 | |
|                 if {$c == "\\"} {
 | |
|                         incr i
 | |
|                         continue
 | |
|                 } elseif {$c == "("} {
 | |
|                         set c [string index $exp [expr $i+1]]
 | |
|                         set what [string index $exp [expr $i+2]]
 | |
|                         # test for escape with (?...)
 | |
|                         if {$c == "?"} {
 | |
|                                 if {$what != ":"} {
 | |
|                                         lappend indices "lookahead"
 | |
|                                 } else {
 | |
|                                         lappend indices "noreport"
 | |
|                                 }
 | |
|                         } else {
 | |
|                                 lappend indices "report"
 | |
|                                 incr data(v:nblevels)
 | |
|                         }
 | |
|                         lappend indices $i
 | |
|                         set stack "[llength $indices] $stack"
 | |
|                         lappend indices 0
 | |
| 
 | |
|                 } elseif {$c == ")"} {
 | |
|                         set idx [lindex $stack 0]
 | |
|                         if {$idx == ""} {
 | |
|                                 continue
 | |
|                         }
 | |
|                         set stack [lrange $stack 1 end]
 | |
|                         set indices [lreplace $indices $idx $idx $i]
 | |
|                 }
 | |
|         }
 | |
| 
 | |
|         # remove old colors
 | |
|         foreach level $data(v:levels) {
 | |
|                 $data(w:regexp) tag remove $level 1.0 end
 | |
|         }
 | |
|         $data(w:regexp) tag remove "lookahead" 1.0 end
 | |
|         $data(w:regexp) tag remove "noreport" 1.0 end
 | |
|         # colorize the regexp
 | |
|         set i 0
 | |
|         foreach {type min max} $indices {
 | |
|                 if {$type != "report"} {
 | |
|                         continue
 | |
|                 }
 | |
|                 $data(w:regexp) tag add [lindex $data(v:levels) $i] \
 | |
|                                 [$data(w:regexp) index "1.0+${min}chars"] \
 | |
|                                 [$data(w:regexp) index "1.0+[expr $max+1]chars"]
 | |
|                 incr i
 | |
|         }
 | |
|         # apply special item
 | |
|         foreach {type min max} $indices {
 | |
|                 if {$type == "report"} {
 | |
|                         continue
 | |
|                 }
 | |
|                 $data(w:regexp) tag add $type \
 | |
|                                 [$data(w:regexp) index "1.0+${min}chars"] \
 | |
|                                 [$data(w:regexp) index "1.0+[expr $max+1]chars"]
 | |
|         }
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------------------------
 | |
| 
 | |
| proc regexp::regexp:load {} {
 | |
| variable data
 | |
| 
 | |
|         # get filename
 | |
|         set types [list [list "All" *]]
 | |
|         set file [tk_getOpenFile -filetypes $types -parent .]
 | |
|         if {$file == ""} {
 | |
|                 return
 | |
|         }
 | |
|         # do it
 | |
|         set in [open $file "r"]
 | |
|         regexp:set [read $in [file size $file]]
 | |
|         close $in
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------------------------
 | |
| 
 | |
| proc regexp::regexp:insert {what} {
 | |
| variable data
 | |
| 
 | |
|         set w $data(w:regexp)
 | |
|         # prepare undo/redo
 | |
|         set data(v:undo:r$data(v:undo:index)) [list [$w index insert] [$w get 1.0 end-1char]]
 | |
|         set data(v:undo:index) [expr ($data(v:undo:index)+1) % 100]
 | |
|         # do it
 | |
|         $w insert insert $what
 | |
|         # prepare undo/redo
 | |
|         set data(v:undo:r$data(v:undo:index)) [list [$w index insert] [$w get 1.0 end-1char]]
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------------------------
 | |
| # History window to memorize already typed regexp
 | |
| 
 | |
| proc regexp::history:init {} {
 | |
| variable data
 | |
| global font
 | |
| 
 | |
|         set w [toplevel .history]
 | |
|         wm title $w "Visual REGEXP $::version -- REGEXP History"
 | |
|         wm geometry $w 640x480
 | |
|         wm protocol $w WM_DELETE_WINDOW "set regexp::data(v:history) 0; wm withdraw $w"
 | |
| 
 | |
|         # text zone        
 | |
|         set tf [frame $w.t]
 | |
|         pack $tf -side top -expand true -fill both
 | |
|         set t [text $tf.t -xscrollcommand "$tf.x set" -yscrollcommand "$tf.y set" \
 | |
|                                         -bg white -font $::font_regexp -width 5 -height 1 \
 | |
|                                         -selectbackground lightblue -selectborderwidth 0]
 | |
|         set data(w:history) $t
 | |
|         $t tag configure spacing -font {Helvetica 6}
 | |
|         set tx [scrollbar $tf.x -bd 1 -orient horizontal -command "$t xview"]
 | |
|         set ty [scrollbar $tf.y -bd 1 -orient vertical -command "$t yview"]
 | |
|         bindtags $t "$t all"
 | |
|         grid $t  $ty -sticky news
 | |
|         grid $tx x   -sticky news
 | |
|         grid columnconfigure $tf {0} -weight 1
 | |
|         grid columnconfigure $tf {1} -weight 0
 | |
|         grid rowconfigure $tf {0} -weight 1
 | |
|         grid rowconfigure $tf {1} -weight 0
 | |
| 
 | |
|         # buttons
 | |
|         set bf [frame $w.f]
 | |
|         pack $bf -side bottom -padx 5 -pady 5
 | |
| 
 | |
|         set b1 [button $bf.1 -bd 1 -text "Hide" -command "wm withdraw $w; set ::regexp::data(v:history) 0"]
 | |
|         set b2 [button $bf.2 -bd 1 -text "Save ..." -command "regexp::history:save"]
 | |
|         pack $b2 $b1 -side left -anchor c
 | |
| 
 | |
|         wm withdraw $w
 | |
| }
 | |
| 
 | |
| set last ""
 | |
| set counter 0
 | |
| 
 | |
| proc regexp::history:add {} {
 | |
| variable data
 | |
| 
 | |
|         if {$::inReplay} {
 | |
|                 # avoid to put the same expression again when replaying it
 | |
|                 set ::inReplay 0
 | |
|                 return
 | |
|         }
 | |
| 
 | |
|         set exp [$data(w:regexp) get 1.0 end-1char]
 | |
|         if {$exp != "" && $exp != $::last} {
 | |
|                 # memorize position
 | |
|                 set start [$data(w:history) index insert]
 | |
|                 # add text
 | |
|                 $data(w:history) insert end "$exp\n"
 | |
|                 set end [$data(w:history) index insert]
 | |
|                 $data(w:history) insert end "\n" {spacing}
 | |
|                 set ::last $exp
 | |
|                 $data(w:history) yview moveto 1.0
 | |
|                 # do the binding
 | |
|                 set tag "t$::counter"
 | |
|                 incr ::counter
 | |
|             $data(w:history) tag bind $tag <Any-Enter> "$data(w:history) tag configure $tag -background lightblue"
 | |
|             $data(w:history) tag bind $tag <Any-Leave> "$data(w:history) tag configure $tag -background {}"
 | |
|                 $data(w:history) tag bind $tag <1> "regexp::history:replay [list $exp]"
 | |
|                 $data(w:history) tag add $tag $start $end
 | |
| 
 | |
|                 # colorize the expression in history
 | |
|                 scan $start "%d.%d" sl sc
 | |
|                 incr sl -1
 | |
|                 foreach tag {e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 lookahead noreport} {
 | |
|                         foreach {start end} [$data(w:regexp) tag ranges $tag] {
 | |
|                                 set start [$data(w:history) index "$start + $sc chars + $sl lines"]
 | |
|                                 set end [$data(w:history) index "$end + $sc chars + $sl lines"]
 | |
|                                 $data(w:history) tag add $tag $start $end
 | |
|                         }
 | |
|                 }
 | |
|         }
 | |
| }
 | |
| 
 | |
| set inReplay 0
 | |
| 
 | |
| proc regexp::history:replay {text} {
 | |
| variable data
 | |
| 
 | |
|         set ::inReplay 1
 | |
|         regexp:set $text
 | |
|         go
 | |
| }
 | |
| 
 | |
| proc regexp::history:save {} {
 | |
| variable data
 | |
| 
 | |
|         set file [tk_getSaveFile -defaultextension .txt]
 | |
|         if {$file != ""} {
 | |
|                 set out [open $file "w"]
 | |
|                 puts -nonewline $out [$data(w:history) get 1.0 end]
 | |
|                 close $out
 | |
|         }
 | |
| }
 | |
| 
 | |
| 
 | |
| #----------------------------------------------------------------------------------------------
 | |
| #        Manage SAMPLE
 | |
| #----------------------------------------------------------------------------------------------
 | |
| 
 | |
| proc regexp::sample:set {text} {
 | |
| variable data
 | |
| 
 | |
|         $data(w:sample) delete 1.0 end
 | |
|         $data(w:sample) insert 1.0 $text
 | |
|         set data(v:undo:sample) $text
 | |
| }
 | |
| 
 | |
| proc regexp::sample:colorize {} {
 | |
| variable data
 | |
| 
 | |
|         # remove old tags
 | |
|         foreach level $data(v:levels) {
 | |
|                 $data(w:sample) tag remove $level 1.0 end
 | |
|         }
 | |
|         set data(v:position) 0
 | |
|         set data(v:positions) [list ]
 | |
| 
 | |
|         # set new tags
 | |
|         set exp [$data(w:regexp) get 1.0 end-1char]
 | |
|         if {$exp == ""} {
 | |
|                 set data(v:result) {}
 | |
|                 return
 | |
|         }
 | |
|         set result [eval regexp -inline -indices $data(v:all) \
 | |
|                                                 $data(v:line) $data(v:lineanchor) $data(v:linestop) \
 | |
|                                                 $data(v:nocase) -- \
 | |
|                                                 [list $exp] [list [$data(w:sample) get 1.0 end]]]
 | |
|         set data(v:result) $result
 | |
|         set i 0
 | |
|         foreach match $result {
 | |
|                 set start [$data(w:sample) index "1.0+[lindex $match 0]chars"]
 | |
|                 $data(w:sample) tag add e[expr $i % $data(v:nblevels)] \
 | |
|                                 $start [$data(w:sample) index "1.0+[expr [lindex $match 1]+1]chars"]
 | |
|                 lappend data(v:positions) $start
 | |
|                 if {$i == 0} {
 | |
|                         $data(w:sample) see $start
 | |
|                 }
 | |
|                 incr i
 | |
|         }
 | |
|         # set nb of matches
 | |
|         if {$data(v:nblevels)} {
 | |
|                 set nb 0
 | |
|                 foreach item $result {
 | |
|                         if {[lindex $item 0] <= [lindex $item 1]} {
 | |
|                                 incr nb
 | |
|                         }
 | |
|                 }
 | |
|                 set regexp::data(v:nbmatches) "[expr $nb/$data(v:nblevels)] matches"
 | |
|         } else {
 | |
|                 set regexp::data(v:nbmatches) "? matches"
 | |
|         }
 | |
| }
 | |
| 
 | |
| proc regexp::sample:background {} {
 | |
| variable data
 | |
| 
 | |
|         foreach level $data(v:levels) color $::colors bgcolor $::bgcolors {
 | |
|                 if {$data(v:background)} {
 | |
|                         $data(w:sample) tag configure $level -foreground $color -background $bgcolor
 | |
|                 } else {
 | |
|                         $data(w:sample) tag configure $level -foreground $color -background {}
 | |
|                 }
 | |
|         }
 | |
| }
 | |
| 
 | |
| proc regexp::sample:move {amount} {
 | |
| variable data
 | |
| 
 | |
|         if {$amount == -1} {
 | |
|                 if {$data(v:position) > 0} {
 | |
|                         incr data(v:position) -1
 | |
|                 }
 | |
|         } else {
 | |
|                 if {$data(v:position) < [llength $data(v:positions)]-1} {
 | |
|                         incr data(v:position) +1
 | |
|                 }
 | |
|         }
 | |
|         set where [lindex $data(v:positions) $data(v:position)]
 | |
|         if {$where != ""} {
 | |
|                 $data(w:sample) see $where
 | |
|                 $data(w:sample) mark set insert $where
 | |
|                 focus $data(w:sample)
 | |
|         }
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------------------------
 | |
| 
 | |
| proc regexp::sample:load {} {
 | |
| variable data
 | |
| 
 | |
|         # get filename
 | |
|         set types [list [list "All" *]]
 | |
|         set file [tk_getOpenFile -initialdir $data(v:dir) -filetypes $types -parent .]
 | |
|     if {$file == ""} {
 | |
|                 return
 | |
|         }
 | |
|         # memorize location
 | |
|         set data(v:dir) [file dirname $file]
 | |
|         set data(v:file) [file tail $file]
 | |
|         # do it
 | |
|         set in [open $file "r"]
 | |
|         sample:set [read $in [file size $file]]
 | |
|         close $in
 | |
| }
 | |
| 
 | |
| proc regexp::sample:save {mode} {
 | |
| variable data
 | |
| 
 | |
|         # get filename
 | |
|         set types [list [list "All" *]]
 | |
|         set file [tk_getSaveFile -initialdir $data(v:dir) -initialfile $data(v:file) \
 | |
|                                                          -filetypes $types -parent .]
 | |
|     if {$file == ""} {
 | |
|                 return
 | |
|         }
 | |
|         # memorize location
 | |
|         set data(v:dir) [file dirname $file]
 | |
|         set data(v:file) [file tail $file]
 | |
|         # do it
 | |
|         set out [open $file "w"]
 | |
|         fconfigure $out -translation $mode
 | |
|         puts $out [$data(w:sample) get 1.0 end]
 | |
|         close $out
 | |
| }
 | |
| 
 | |
| 
 | |
| #----------------------------------------------------------------------------------------------
 | |
| #        Main toplevel commands
 | |
| #----------------------------------------------------------------------------------------------
 | |
| 
 | |
| proc regexp::make-regexp {} {
 | |
| variable data
 | |
| 
 | |
|         # new dialog
 | |
|         catch { destroy .mkregexp }
 | |
|         set w [toplevel .mkregexp]
 | |
|         wm title $w "Make regexp"
 | |
|         wm geometry $w 640x480
 | |
|         # widgets
 | |
|         set f [frame $w.top]
 | |
|                 # area to input words
 | |
|                 label $f.l1 -text "Words list:"
 | |
|                 set list [text $f.list -wrap char -bg white -font $::font_regexp \
 | |
|                                                         -selectbackground lightblue -selectborderwidth 0 \
 | |
|                                                         -width 1 -height 10 -bd 1 -yscrollcommand "$f.sy1 set"]
 | |
|                 scrollbar $f.sy1 -command "$list yview" -orient vertical -bd 1
 | |
|                 # button to compute the regexp
 | |
|                 set doit [button $f.doit -text "Compute" -width 15 -bd 1 -command "regexp::make-regexp:compute"]
 | |
|                 # display result
 | |
|                 label $f.l2 -text "Regexp:"
 | |
|                 set output [text $f.output -wrap char -bg white -font $::font_regexp \
 | |
|                                                         -selectbackground lightblue -selectborderwidth 0 \
 | |
|                                                         -width 1 -height 4 -bd 1 -yscrollcommand "$f.sy2 set"]
 | |
|                 bindtags $output "$output all"
 | |
|                 scrollbar $f.sy2 -command "$output yview" -orient vertical -bd 1
 | |
|                 # layout
 | |
|                 grid $f.l1        $list                $f.sy1                -sticky news
 | |
|                 grid $doit        -                        -                        -sticky ns -pady 2
 | |
|                 grid $f.l2        $output        $f.sy2                -sticky news
 | |
|                 grid columnconfigure $f {1} -weight 1
 | |
|                 grid rowconfigure $f {0 2} -weight 1
 | |
|                 # init
 | |
|                 set data(w:make:list) $list
 | |
|                 set data(w:make:output) $output
 | |
|         # button OK / CANCEL
 | |
|         set ff [frame $w.bottom]
 | |
|                 set ok [button $ff.ok -text "Insert into regexp" -width 20 -bd 1 -command "regexp::make-regexp:ok $w"]
 | |
|                 set cancel [button $ff.cancel -text "Cancel" -width 20 -bd 1 -command "destroy $w"]
 | |
|                 pack $ok $cancel -side left -fill both -padx 10 -pady 10
 | |
|         # layout
 | |
|         pack $f -side top -expand true -fill both
 | |
|         pack $ff -side bottom -anchor c
 | |
| }
 | |
| 
 | |
| proc regexp::make-regexp:compute {} {
 | |
| variable data
 | |
| 
 | |
|         set words [$data(w:make:list) get 1.0 end-1c]
 | |
|         $data(w:make:output) delete 1.0 end
 | |
|         $data(w:make:output) insert 1.0 [make-regexp::make-regexp $words]
 | |
| }
 | |
| 
 | |
| proc regexp::make-regexp:ok {w} {
 | |
| variable data
 | |
| 
 | |
|         set words [$data(w:make:list) get 1.0 end-1c]
 | |
| 
 | |
|         $data(w:regexp) insert insert "([make-regexp::make-regexp $words])"
 | |
|         destroy $w
 | |
| }
 | |
| 
 | |
| 
 | |
| #==============================================================================================
 | |
| #        Main entry point
 | |
| #==============================================================================================
 | |
| 
 | |
| # try to get customization from 'visual_regexp.ini'
 | |
| puts "[file exists visual_regexp.ini]"
 | |
| set filename [file dirname [info nameofexecutable]]/visual_regexp.ini
 | |
| if {[file exists $filename]} {
 | |
|         source $filename
 | |
| } elseif {[file exists visual_regexp.ini]} {
 | |
|         source visual_regexp.ini
 | |
| }
 | |
| 
 | |
| # try to auto user patterns
 | |
| set filename [file dirname [info nameofexecutable]]/regexp.txt
 | |
| if {[file exists $filename]} {
 | |
|         regexp::pattern:load $filename
 | |
| } elseif {[file exists regexp.txt]} {
 | |
|         regexp::pattern:load regexp.txt
 | |
| }
 | |
| 
 | |
| # buld the GUI
 | |
| regexp::history:init
 | |
| regexp::gui
 | |
| regexp::go
 | |
| 
 | |
| if {$argc > 1} {
 | |
|         puts "Usage: $argv0 <sampleFile>"
 | |
| } elseif {$argc == 1} {
 | |
|         set filename [lindex $argv 0]
 | |
|         if [file exists $filename] {
 | |
|             set file [open $filename]
 | |
|             set data [read $file [file size $filename]]
 | |
|             close $file
 | |
|             # memorize location
 | |
|             set regexp::data(v:dir) [file dirname $filename]
 | |
|             set regexp::data(v:file) [file tail $filename]
 | |
|         } else {
 | |
|             set data [lindex $argv 0]
 | |
|         }
 | |
|         regexp::sample:set $data
 | |
|         unset data
 | |
| }
 | |
| 
 | |
| 
 | |
| #----------------------------------------------------------------------------------------------
 | |
| 
 | |
| image create photo logo -data {R0lGODlhLAFxAMYAAAICAhcXFzw8WFtbb4+Njq2ssioqNMfGxkJCSgYCtcYtJrjOuEpGVs3Y0FJOYr1JNb53Yt/g4BsXq+i9yspGHOjQ08adlebm5rm57unH0NwjGPY4EsJaIgYCwqOjp2hmet6+wurq6uTYvvcuE77Ovh4altZLE9C0uioqjrljT76OevtCDt7Hx7q5u8LC7tzZ2PsbFnJyhvsqEspWGvxaCgYC0+jh3cLSwh4akujY2Ll8Z5qZm8rK6tDQ6PxODNHQ0f///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////yH5BAEKAEAALAAAAAAsAXAAAAf+gBEvBy2FBz8XiYqLixGEhT8vjJOUjD+PLZGVm5yCl4WghpEREZymlBE/hqWnio8HrbGngoUvrLKEP7eym4+SnS0HpaqEB8bHFyHKF57GycrQ0RcNhAWQsSEvl6O8lJ+Qv92TIY6hoIcvts/im9mfwrLlBYjs4uTF4uXq9YwRwfSbUmUKwYxUMVu7Uh2DF60hwXKgAJoiV5DfIlWgGoSzmA2UtQMaSVk8RW1VvIgjeQ06sDGWvxYiU3bUxYmcqgbP7mVKVCrVJWOacjpcBjEYw6HQeApCiFSaIJFJFYqK0LRqw0QYjeoqtayr1a/RbAyCZAPsQ1AvzKqtOo3mWlr+wtZWFUTiV9Wxkogay/twoUaCrSKQOAev1U+QLVEdtmuzUAGQuyxSO3QLsOWUi2w+kkgSVORZ2j6DxsmrceFJpERTUvb01E8bi14Ewwn4ZwPYOSf6PGcrGgsLDzQIF67gQYqPuppeSBfqRdm+r5JbtTBCgwIIz5M+TJ35qlyrWDcLVR5C1WOqase24AuWGY9gadUuxzdUYNyvzIxxRWpwYDKeGq3T2n8ETiRWA6HQdIEKCsDg4IMQOqiACjyolog2oTRAVTJFsWThMzrIIKIMDwA2zn8mEpgiZvkZQlorIbzy4SQI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}
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #==============================================================================================
 | |
| #        Make Regexp
 | |
| #==============================================================================================
 | |
| namespace eval make-regexp {
 | |
| }
 | |
| #        Takes a list of words, returns a list "prefix <recurse>   prefix <recurse>  ..."
 | |
| #        after grouping by first common letter.
 | |
| proc make-regexp::prefix {words} {
 | |
|         # init
 | |
|         set result {}
 | |
|         lappend words ""                ;# to force last completion
 | |
|         # group by first letter
 | |
|         set prefix [string range [lindex $words 0] 0 0]
 | |
|         set subwords [list [string range [lindex $words 0] 1 end]]
 | |
|         foreach word [lrange $words 1 end] {
 | |
|                 set char [string range $word 0 0]
 | |
|                 if {$char == $prefix} {
 | |
|                         lappend subwords [string range $word 1 end]
 | |
|                 } else {
 | |
|                         # compute prefixes recursively
 | |
|                         set recurse [prefix $subwords]
 | |
|                         if {[llength $recurse] == 2} {
 | |
|                                 # only one prefix, so concat with previous prefix
 | |
|                                 append prefix [lindex $recurse 0]
 | |
|                                 set recurse [lindex $recurse 1]
 | |
|                         }
 | |
|                         append result " [verify [list $prefix $recurse]]"
 | |
|                         set prefix $char
 | |
|                         set subwords [list [string range $word 1 end]]
 | |
|                 }
 | |
|         }
 | |
|         # return
 | |
|         set result
 | |
| }
 | |
| #        Verification of regexp.
 | |
| #        After searching common suffixes, some patterns grouped by parenthesis or conditional exps
 | |
| #        may be broken. We need to fix them.
 | |
| proc make-regexp::verify {exp} {
 | |
|         set orphans [isOrphans $exp]
 | |
|         set result {}
 | |
|         foreach {prefix recurse} $exp {
 | |
|                 if {![isBalanced $prefix]} {
 | |
|                         if {[llength $recurse]} {
 | |
|                                 foreach {pp rr} $recurse {
 | |
|                                         lappend result "$prefix$pp" $rr
 | |
|                                 }
 | |
|                                 if {![isBalanced $prefix] && $orphans} {
 | |
|                                         set result [verify $result]
 | |
|                                 }
 | |
|                         } else {
 | |
|                                 lappend result "$prefix" ""
 | |
|                         }
 | |
|                 } else {
 | |
|                         lappend result $prefix $recurse
 | |
|                 }
 | |
|         }
 | |
|         # return result after fixing
 | |
|         set result
 | |
| }
 | |
| #        Check for orphan grouping ('|' lost in lower level)
 | |
| proc make-regexp::isOrphans {exp} {
 | |
|         set orphan 0
 | |
|         foreach {prefix recurse} $exp {
 | |
|                 if {[string index $prefix 0] == "|"} {
 | |
|                         set orphan 1
 | |
|                         break
 | |
|                 }
 | |
|                 if {[isOrphans $recurse]} {
 | |
|                         set orphan 1
 | |
|                         break
 | |
|                 }
 | |
|         }
 | |
|         set orphan
 | |
| }
 | |
| #==============================================================================================
 | |
| #        Check if parenthesis in 'str' after balanced.
 | |
| proc make-regexp::isBalanced {str} {
 | |
|         # if start with '?' skip it
 | |
|         if {[string index $str 0] == "?"} {
 | |
|                 return 0
 | |
|         }
 | |
|         # must start with a ')'
 | |
|         if {[string index $str 0] != ")"} {
 | |
|                 return 1
 | |
|         }
 | |
|         # try to balanced each ')' with an appropriate '('
 | |
|         set depth 0
 | |
|         foreach c [split $str {}] {
 | |
|                 if {$c == "("} {
 | |
|                         incr depth -1
 | |
|                 } elseif {$c == ")"} {
 | |
|                         incr depth +1
 | |
|                 }
 | |
|         }
 | |
|         return [expr $depth == 0]
 | |
| }
 | |
| #        Check if 'str' contains a first level grouping
 | |
| proc make-regexp::firstLevelGroup {str} {
 | |
|         set depth 0
 | |
|         foreach c [split $str {}] {
 | |
|                 if {$c == "("} {
 | |
|                         incr depth -1
 | |
|                 } elseif {$c == ")"} {
 | |
|                         incr depth +1
 | |
|                 } elseif {$depth == 0 && $c == "|"} {
 | |
|                         return 1
 | |
|                 }
 | |
|         }
 | |
|         return 0
 | |
| }
 | |
| #==============================================================================================
 | |
| #        After having found common prefixes, try to find common suffixes in expression
 | |
| proc make-regexp::suffix {list} {
 | |
|         # end of recursion if empty list
 | |
|         if {[llength $list] == 0} {
 | |
|                 return ""
 | |
|         }
 | |
|         set newlist {}
 | |
|         foreach {prefix recurse} $list {
 | |
|                 set result [suffix $recurse]
 | |
|                 lappend newlist $prefix [lindex $result 0]
 | |
|         }
 | |
|         # compute longest common suffixes
 | |
|         set words {}
 | |
|         foreach {prefix tail} $newlist {
 | |
|                 if {[firstLevelGroup $tail]} {
 | |
|                         set tail "($tail)"
 | |
|                 }
 | |
|                 lappend words [reverse $prefix$tail]
 | |
|         }
 | |
|         set words [lsort -unique $words]
 | |
|         set reverse [prefix $words]
 | |
|         # compute regexp from precomputed reverse list
 | |
|         set regexp [build "" $reverse]
 | |
|         # returns computed regexp
 | |
|         set regexp
 | |
| }
 | |
| proc make-regexp::build {mainstem reverse} {
 | |
|         # flag to indicate need for '?' (optional group)
 | |
|         set addQuestionMark 0
 | |
|         set regexp ""
 | |
|         foreach {prefix recurse} $reverse {
 | |
|                 set stem "[reverse $prefix]$mainstem"
 | |
|                 if {[llength $recurse]} {
 | |
|                         set fromlower [build $stem $recurse]
 | |
|                 } else {
 | |
|                         set fromlower ""
 | |
|                 }
 | |
|                 # build regexp
 | |
|                 if {$prefix == ""} {
 | |
|                         set addQuestionMark 1
 | |
|                 } else {
 | |
|                         if {[string length $fromlower] > 1 && [string index $fromlower end] != "?"} {
 | |
|                                 set fromlower "($fromlower)"
 | |
|                         }
 | |
|                         append regexp "$fromlower[reverse $prefix]|"
 | |
|                 }
 | |
|         }
 | |
|         # remove last trailing '|'
 | |
|         set regexp "[string range $regexp 0 end-1]"
 | |
|         # add '?' if needed
 | |
|         if {$addQuestionMark} {
 | |
|                 if {[string length $regexp] == 1} {
 | |
|                         set regexp "$regexp?"
 | |
|                 } else {
 | |
|                         set regexp "($regexp)?"
 | |
|                 }
 | |
|         }
 | |
|         # result
 | |
|         set regexp
 | |
| }
 | |
| #----------------------------------------------------------------------------------------------
 | |
| #        Last pass for grouping '(x|y|z|...)' into char range '[xyz...]'
 | |
| proc make-regexp::optimize:charset {regexp} {
 | |
|         set optimized ""
 | |
|         set memory ""
 | |
|         set ok 1
 | |
|         set charset ""
 | |
|         # examine char one by one
 | |
|         set len [string length $regexp]
 | |
|         for {set i 0} {$i < $len} {incr i} {
 | |
|                 set char [string index $regexp $i]
 | |
|                 append memory $char
 | |
|                 if {$char =="("} {
 | |
|                         # start of group
 | |
|                         if {$ok} {
 | |
|                                 append optimized [string range $memory 0 end-1]
 | |
|                         }
 | |
|                         incr i
 | |
|                         set result [optimize:charset [string range $regexp $i end]]
 | |
|                         append optimized "[lindex $result 2][lindex $result 0][lindex $result 3]"
 | |
|                         set memory ""
 | |
|                         set ok 0
 | |
|                         incr i [expr [lindex $result 1]]
 | |
|                         continue
 | |
|                 } elseif {$char ==")"} {
 | |
|                         # end of group
 | |
|                         if {$ok} {
 | |
|                                 set optimized "\[$charset\]"
 | |
|                                 return [list $optimized $i "" ""]
 | |
|                         } else {
 | |
|                                 return [list $optimized $i "(" ")"]
 | |
|                         }
 | |
|                 }
 | |
|                 if {$ok} {
 | |
|                         if {$i & 1} {
 | |
|                                 if {$char != "|"} {
 | |
|                                         set ok 0
 | |
|                                         append optimized $memory
 | |
|                                 }
 | |
|                         } else {
 | |
|                                 append charset $char
 | |
|                         }
 | |
|                 } else {
 | |
|                         append optimized $char
 | |
|                 }
 | |
|         }
 | |
|         # return result
 | |
|         list $optimized $i "(" ")"
 | |
| }
 | |
| #==============================================================================================
 | |
| #        Compute string in reverse order
 | |
| proc make-regexp::reverse {string} {
 | |
|         set result ""
 | |
|         for {set i [expr [string length $string]-1]} {$i >= 0} {incr i -1} {
 | |
|                 append result [string index $string $i]
 | |
|         }
 | |
|         set result
 | |
| }
 | |
| #==============================================================================================
 | |
| proc make-regexp::make-regexp {words} {
 | |
|         set words [lsort -unique $words]
 | |
|         # escape special chars used to form regexp
 | |
|         regsub -all -- {\|} $words "\x01" words
 | |
|         regsub -all -- {\(} $words "\x02" words
 | |
|         regsub -all -- {\)} $words "\x03" words
 | |
|         regsub -all -- {\?} $words "\x04" words
 | |
|         regsub -all -- {\[} $words "\x07" words
 | |
|         regsub -all -- {\]} $words "\x08" words
 | |
|         # do it
 | |
|         set list [prefix $words]
 | |
|         set regexp [suffix $list]
 | |
|         # returns regexp
 | |
|           set regexp [lindex [optimize:charset $regexp] 0]
 | |
|         # un-escape special chars used to form regexp
 | |
|         regsub -all -- "\x01" $regexp "\\|" regexp
 | |
|         regsub -all -- "\x02" $regexp "\\(" regexp
 | |
|         regsub -all -- "\x03" $regexp "\\)" regexp
 | |
|         regsub -all -- "\x04" $regexp "\\?" regexp
 | |
|         regsub -all -- "\x07" $regexp "\\\[" regexp
 | |
|         regsub -all -- "\x08" $regexp "\\\]" regexp
 | |
|         regsub -all -- "\\*" $regexp "\\*" regexp
 | |
|         regsub -all -- "\\+" $regexp "\\+" regexp
 | |
|         regsub -all -- "\\\$" $regexp "\$" regexp
 | |
|         regsub -all -- "\\\^" $regexp "\\\^" regexp
 | |
|         # returns result
 | |
|         set regexp
 | |
| }
 | |
| #==============================================================================================
 |