#!/usr/bin/wish

# oslc password locker

package require sqlite3
package require md5
package require platform

global pos
set pos [lindex $tcl_platform(os) 0]
set version "0.99.7b"
set ez 0

proc makenewpw {unum choices} {
	upvar putp putp

	set counter 0
	set builder ""
	while {$counter < $unum} {
    	set r [expr {int(rand()*84)}]
	    set tpass [lindex $choices $r]
    	set builder $builder$tpass
	    incr counter
	}
	set putp $builder

	return
}

proc endapp {} {
	set isDB [file isfile "./oslcpwl"]
	if {$isDB > 0} {
		file delete -force "./oslcpwl"
	}
	exit
}

proc decipher_db {ndb key} {
	if {[catch {exec openssl enc -sm4-ofb -d -in $ndb.oslc -out $ndb -pbkdf2 -pass pass:$key} msg]} {
		tk_messageBox -message "Error\n$msg"
       	return
	}
}

proc encrypt_db {ndb key} {
	if {[catch {exec openssl enc -sm4-ofb -in $ndb -out $ndb.oslc -pbkdf2 -pass pass:$key} msg]} {
		tk_messageBox -message "Error\n$msg"
       	return
	}
}

proc wipe_db {ndb} {
	if {[catch {exec rm $ndb} msg]} {
		tk_messageBox -message "Error\n$msg"
	}
}

proc makedb {ndb key} {
	upvar ez ez
	set isDB [file isfile $ndb.oslc]
	if { $isDB == 0} {
		sqlite3 init $ndb
		init eval {CREATE TABLE pwd_locker(record_no INTEGER PRIMARY KEY AUTOINCREMENT, description char(40) NOT NULL UNIQUE, login text, password text NOT NULL, notes text)}
		init close
		encrypt_db $ndb $key
		wipe_db $ndb
		set ofile [open "./sem3" "w"]
		close $ofile
		tk_messageBox -message "New database \"$ndb\" created"
	} else {
		decipher_db $ndb $key
		sqlite3 pw $ndb
		if {[catch {pw eval {ALTER TABLE pwd_locker ADD COLUMN notes text}} msg]} {
			pw close
			wipe_db $ndb
			tk_messageBox -message "Modify database error, probably a password problem"
			set isDB [file isfile "./oslcpwl"]
			if {$isDB > 0} {
				file delete -force "./oslcpwl"
			}
			set ez 1
			return
		}
		pw eval {ALTER TABLE pwd_locker RENAME TO _pwd_locker_old}
		pw eval {CREATE TABLE pwd_locker(record_no INTEGER PRIMARY KEY AUTOINCREMENT, description char(40) NOT NULL UNIQUE, login text, password text NOT NULL, notes text)}
		pw eval {INSERT INTO pwd_locker (record_no, description, login, password) SELECT record_no, description, login, password FROM _pwd_locker_old}
		pw eval {DROP TABLE _pwd_locker_old}
		pw close
		encrypt_db $ndb $key
		wipe_db $ndb
		set ofile [open "./sem3" "w"]
		close $ofile
		tk_messageBox -message "Database \"$ndb\" upgraded to v3.x"
	}
	return
}

proc addrecord {pwdesc logindata pwdata ndb} {
	sqlite3 pw $ndb
	if {[catch {pw eval {INSERT INTO pwd_locker (description, login, password) VALUES ($pwdesc,$logindata,$pwdata)}} msg]} {
		tk_messageBox -message $msg
		pw close
        return 1
	}
	tk_messageBox -message "$pwdesc record written to database"
	pw close
	return
}

proc modifyrecord {pwdesc logindata pwdata notes ndb} {
	upvar mod mod
	sqlite3 pw $ndb
	set rn [pw eval {SELECT record_no FROM pwd_locker WHERE description = $pwdesc}]
	if {$rn < 1} {
		tk_messageBox -message "no record matching \"description = $pwdesc\", returning"
		incr mod
		return
	}
	if {[catch {pw eval {UPDATE pwd_locker SET notes = $notes, login = $logindata, password = $pwdata WHERE description = $pwdesc}} msg]} {
		tk_messageBox -message $msg
		pw close
        return 1
	}
	tk_messageBox -message "$pwdesc Record modified"
	pw close
	return
}

proc deleterecord {pwdesc logindata pwdata ndb} {
	sqlite3 pw $ndb
	if {[catch {pw eval {DELETE from pwd_locker WHERE description=$pwdesc}} msg]} {
		tk_messageBox -message $msg
		pw close
        return 1
	}
	pw close
	return
}

proc about {} {
	upvar version version
	tk_messageBox -message "Password Locker version $version, written in TCL by Dana Booth"
}

proc clearall {} {
	.top.desc_cb delete 0 end
	.top.pass_text delete 0 end
	.top.login_text delete 0 end
	.bot.desc_text delete 0 end
	.bot.pass_text delete 0 end
	.bot.login_text delete 0 end
	.notesb.text delete 1.0 end
	.top.desc_cb insert 0 "pull down to choose"
}

proc changepw {ndb key} {
	if {[catch {exec openssl enc -sm4-ofb -d -in $ndb.oslc -out $ndb -pbkdf2 -pass pass:$key} msg]} {
		tk_messageBox -message "Error\n$msg"
       	return
	}
	toplevel .password
	.password configure -width 575 -height 190
	wm title .password "Password Dialog"
	wm resizable .password 0 0
	
	frame .password.one -relief ridge -borderwidth 2 -padx 3 -pady 1 -width 480 -height 48
	place .password.one -x 10 -y 3

	grid [label .password.one.tryonelabel -text "New pwd:"]
	grid [entry .password.one.tryoneentry -show "*" -textvariable pwdone -background white -width 42]
	.password.one.tryoneentry delete 0 end
	
	place .password.one.tryonelabel -x 4 -y 10
	place .password.one.tryoneentry -x 100 -y 12

	frame .password.two -relief ridge -borderwidth 2 -padx 3 -pady 1 -width 480 -height 48
	place .password.two -x 10 -y 55
	
	grid [label .password.two.trytwolabel -text "Repeat pwd:"]
	grid [entry .password.two.trytwoentry -show "*"  -textvariable pwdtwo -background white -width 42]
	.password.two.trytwoentry delete 0 end
	
	place .password.two.trytwolabel -x 4 -y 10
	place .password.two.trytwoentry -x 100 -y 12

	frame .password.button -borderwidth 2 -padx 3 -pady 2 -width 540 -height 48
	place .password.button -x 10 -y 140 ; # was 120
	grid [button .password.button.cancel -background grey85 -text "Cancel" -pady 3 -width 9 -command {
		destroy .password
		unset pwdone
		if {[info exists pwdtwo] > 0} {
			unset pwdtwo
		}		
		return
	}]

	grid [button .password.button.b  -background grey85 -text "Continue" -pady 3 -width 9 -command {
		destroy .password
		if {$pwv < 1} {
			if {$pwdone != $pwdtwo} {
				tk_messageBox -message "Passwords don't match, returning"
				unset pwdone
				unset pwdtwo
				return
			}
		}
		set q $pwdone
		unset pwdone
		if {$pwv < 1} {
			unset pwdtwo
		}
		set tpi {! @ # $ % & - _ = +}

		set tpic 0
		set tpc -1
		set tp " "
		while {$tp != ""} {
			incr tpc
			set tp [string index $q $tpc]
			if {$tp == " "} {
				set tp [lindex $tpi $tpic]
				incr tpic
				if {$tpic > 9} {
					set tpic 0
				}
			set q [string replace $q $tpc $tpc $tp]
			}
		}

		set tem $q
		set q [md5::md5 -hex $q]
		set passlengthsem 0
		set passlength [string length $tem]
		while {$passlengthsem < $passlength} {
			set qz [md5::md5 -hex $q]
			set q "$q$qz"
			incr passlengthsem
		}
		set q [md5::md5 -hex $q]
		set key $q
		destroy .password
		sqlite3 pw $ndb
		if {[catch {set pwlist [pw eval {SELECT description FROM pwd_locker ORDER BY description}]} msg]} {
			tk_messageBox -message "Error: $msg probably a password issue"
			set z 1
			pw close
			wipe_db $ndb
		} else {
			pw close
			set wipendb $ndb.oslc
			wipe_db $wipendb
			encrypt_db $ndb $key
			wipe_db $ndb
		}
		if {$z == 0} {
			tk_messageBox -message "Password successfully changed"
			set loggedin "yes"
			clearall
			.top.desc_cb configure -values $pwlist
		}
	}]
	place .password.button.b -x 0
	place .password.button.cancel -x 100

	focus -force .password.one.tryoneentry
	bind .password.one.tryoneentry <Return> ".password.button.b invoke"
	bind .password.one.tryoneentry <KP_Enter> ".password.button.b invoke"
}

set ndb "./oslcpwl"
set key ""
set pwlist ""
	
. configure -width 750 -height 415
wm title . "oslc password locker version $version"
wm resizable . 0 0
wm protocol . WM_DELETE_WINDOW {endapp}
focus -force .

menu .mbar -backgroun grey90
. configure -menu .mbar

menu .mbar.fl -tearoff 0
menu .mbar.he -tearoff 0
.mbar add cascade -menu .mbar.fl -label File -underline 0
.mbar add cascade -menu .mbar.he -label Help -underline 0

.mbar.fl add command -label Exit -command {endapp}
.mbar.fl add command -label "Change Master Password" -command {
	set cpw 0
	if {[info exists key] < 1} {
		tk_messageBox -message "Login with your current password first"
		set cpw 1
	}
	if {$cpw == 0} {
		if {$key == ""} {
			tk_messageBox -message "Login with your current password first"
			set cpw 1
		}
	}
	if {$cpw == 0} {
		changepw $ndb $key
	}
	unset cpw
}
.mbar.he add command -label "View readme.txt" -command {readme}
.mbar.he add command -label About -command {about}
# top frame:
frame .top -borderwidth 1 -padx 3 -pady 4 -height 120 -width 575
place .top -x 8 -y 0

grid [label .top.desc_label -text "Lookup Description"]
grid [ttk::combobox .top.desc_cb -textvariable est -values $pwlist -background white -width 32 -state normal]
.top.desc_cb insert 0 "please log in"
bind .top.desc_cb <<ComboboxSelected>> {
	set pq ""
	decipher_db $ndb $key
	sqlite3 pw $ndb
	set pq [pw eval {SELECT password FROM pwd_locker WHERE description=$est}]
	set pq [string trimleft $pq "\{"]
	set pq [string trimright $pq "\}"]
	set lq [pw eval {SELECT login FROM pwd_locker WHERE description=$est}]
	set lq [string trimleft $lq "\{"]
	set lq [string trimright $lq "\}"]
	set nt [pw eval {SELECT notes FROM pwd_locker WHERE description=$est}]
	set nt [string trimleft $nt "\{"]
	set nt [string trimright $nt "\}"]
	pw close
	encrypt_db $ndb $key
	wipe_db $ndb
	.top.pass_text delete 0 end
	.top.pass_text insert 0 $pq
	.top.login_text delete 0 end
	.top.login_text insert 0 $lq
	.notesb.text delete 1.0 end
	.notesb.text insert 1.0 $nt
}

place .top.desc_label -x 0 -y 0
place .top.desc_cb -x 0 -y 20

grid [label .top.login_label -text "Login"]
grid [entry .top.login_text -background white -relief sunken -width 48]
place .top.login_label -x 275 -y 0
place .top.login_text -x 275 -y 20

grid [label .top.pass_label -text "Password"]
grid [entry .top.pass_text -background white -relief sunken -width 48]
place .top.pass_label -x 275 -y 50
place .top.pass_text -x 275 -y 70

grid [button .top.delete -background grey65 -text "Delete This Record" -width 29 -command {
	set pwdesc [.top.desc_cb get]
	set pwdata [.top.pass_text get]
	set logindata [.top.login_text get]
	if {$pwdata == ""} {
		tk_messageBox -message "Can't delete, there is no record currently loaded."
		return
	}
	set adialog [tk_dialog .deleteYesNo "Confirm Delete" "Confirm you want to delete this record" questhead 1 "Delete" "Cancel"]
	if {$adialog == 0} {
		decipher_db $ndb $key
		deleterecord $pwdesc $logindata $pwdata $ndb
		sqlite3 pw $ndb
		set pwlist [pw eval {SELECT description FROM pwd_locker ORDER BY description}]
		pw close
		encrypt_db $ndb $key
		wipe_db $ndb
		tk_messageBox -message "$pwdesc record deleted from database"
		.top.desc_cb configure -values $pwlist
		clearall
	} else {
		tk_messageBox -message "Cancelled"
	}
}]
place .top.delete -x 0 -y 80

grid [button .top.modify -background grey65 -text "Commit Edits - This Record" -width 29 -command {
	set mod 0
	set pwdesc [.top.desc_cb get]
	set pwdata [.top.pass_text get]
	set logindata [.top.login_text get]
	set notes [.notesb.text get 1.0 end]
	if {$pwdata == ""} {
		tk_messageBox -message "Can't modify, there is no record currently loaded."
		return
	}
	set adialog [tk_dialog .deleteYesNo "Confirm modify" "Confirm you want to modify this record" questhead 1 "Modify" "Cancel"]
	if {$adialog == 0} {
		decipher_db $ndb $key
		modifyrecord $pwdesc $logindata $pwdata $notes $ndb
		if {$mod == 0} {
			sqlite3 pw $ndb
			set pwlist [pw eval {SELECT description FROM pwd_locker ORDER BY description}]
			pw close
			encrypt_db $ndb $key
			wipe_db $ndb
			.top.desc_cb configure -values $pwlist
			clearall
		}
	} else {
		tk_messageBox -message "Cancelled"
	}
}]
place .top.modify -x 0 -y 50

# new frame for the notes box
frame .notes -height 25 -width 725
place .notes -x 8 -y 130
grid [label .notes.label -text "Type or paste any notes or comments, \"Commit Edits\" to save"]
place .notes.label -x 3 -y 0

frame .notesb -height 65 -width 725
place .notesb -x 8 -y 156
grid [text .notesb.text -wrap word -pady 1 -background white -width 88 -height 4 -yscrollcommand ".notesb.sbv set"]
grid [scrollbar .notesb.sbv -command ".notesb.text yview" -orient vertical -background white] -column 1 -row 0 -sticky ns

# top right frame:
frame .top_right -height 120 -width 150
place .top_right -x 590 -y 0

grid [button .top_right.copylogin -background grey65 -text "< Copy to Clipboard" -width 17 -command {
	set logindata [.top.login_text get]
	if {[string length $logindata] < 1} {
		tk_messageBox -message "nothing to copy, returning"
		return
	}
	clipboard clear
	clipboard append "$logindata"
	tk_messageBox -message "$logindata copied to clipboard"
}]
place .top_right.copylogin -x 10 -y 20

grid [button .top_right.copypw -background grey65 -text "< Copy to Clipboard" -width 17 -command {
	set pwdata [.top.pass_text get]
	if {[string length $pwdata] < 1} {
		tk_messageBox -message "nothing to copy, returning"
		return
	}
	clipboard clear
	clipboard append "$pwdata"
	tk_messageBox -message "$pwdata copied to clipboard"
}]
place .top_right.copypw -x 10 -y 71


# use a frame to make a solid line
frame .line -height 2 -width 730 -relief solid -borderwidth 1
place .line -x 3 -y 230

# this is the lower stuff, adding a new password
# top frame:
frame .bot -relief solid -borderwidth 1 -padx 3 -pady 4 -height 110 -width 575
place .bot -x 8 -y 250

grid [label .bot.desc_label -text "New Record Description"]
grid [entry .bot.desc_text -background white -relief sunken -width 32]
place .bot.desc_label -x 0 -y 0
place .bot.desc_text -x 0 -y 20

grid [label .bot.login_label -text "New Login"]
grid [entry .bot.login_text -background white -relief sunken -width 48]
place .bot.login_label -x 275 -y 0
place .bot.login_text -x 275 -y 20

grid [label .bot.pass_label -text "New Password"]
grid [entry .bot.pass_text -background white -relief sunken -width 94]
place .bot.pass_label -x 0 -y 50
place .bot.pass_text -x 0 -y 70

# bottom right frame:
frame .bot_right -height 200 -width 150
place .bot_right -x 590 -y 250
grid [button .bot_right.copypw -background grey65 -text "Add Record" -width 17 -command {
	if {[info exists loggedin]} {
		set pwdesc [.bot.desc_text get]
		set pwdata [.bot.pass_text get]
		set logindata [.bot.login_text get]
		if {[string length $pwdesc] < 1} {
			tk_messageBox -message "no description entered, returning"
			return
		}
		decipher_db $ndb $key
		addrecord $pwdesc $logindata $pwdata $ndb
		.bot.desc_text delete 0 end
		.bot.pass_text delete 0 end
		sqlite3 pw $ndb
		set pwlist [pw eval {SELECT description FROM pwd_locker ORDER BY description}]
		pw close
		encrypt_db $ndb $key
		wipe_db $ndb
		.top.desc_cb configure -values $pwlist
		clearall
	} else {
		tk_messageBox -message "You have to log in first"
	}
}]
place .bot_right.copypw -x 10 -y 20

grid [button .bot_right.makepw -background grey65 -text "Password Maker" -width 17 -command {
	makenewpw $unum $choices
	.bot.pass_text delete 0 end
	.bot.pass_text insert 0 $putp
}]
place .bot_right.makepw -x 10 -y 71
set numlist {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64}
grid [ttk::combobox .bot_right.num -textvariable unum -values $numlist -background white -width 3 -state normal]
.bot_right.num insert 0 7
place .bot_right.num -x 10 -y 110
grid [label .bot_right.cblabel -text "# of Characters"]
place .bot_right.cblabel -x 55 -y 110

frame .last -relief sunken -height 60 -width 500
place .last -x 8 -y 370
grid [button .last.login -background grey65 -text "Login" -width 15 -command {

	set pwv 2; # 2 = up to date, 1 = need to update v2 to v3, 0 = first time running
	if {[file exists "./sem3"] < 1} {
		if {[file exists "./oslcpwl.oslc"] < 1} {
			set pwv 0
			tk_messageBox -message "This is a first run, you'll need to provide a master password (that can be changed later)"
		} else {
			set pwv 1
			tk_messageBox -message "First time running v3.x, we'll need to upgrade the database now"
		}
	}
	if {[info exists loggedin]} {
		tk_messageBox -message "You're already logged in"
	} else {
		set z 0
		toplevel .password
		.password configure -width 575 -height 190
		wm title .password "Password Dialog"
		wm resizable .password 0 0
	
		frame .password.one -relief ridge -borderwidth 2 -padx 3 -pady 1 -width 480 -height 48
		place .password.one -x 10 -y 3

		grid [label .password.one.tryonelabel -text "Password:"]
		grid [entry .password.one.tryoneentry -show "*" -textvariable pwdone -background white -width 42]
	
		place .password.one.tryonelabel -x 4 -y 10
		place .password.one.tryoneentry -x 100 -y 12

		if {$pwv == 0} {
			frame .password.two -relief ridge -borderwidth 2 -padx 3 -pady 1 -width 480 -height 48
			place .password.two -x 10 -y 55
	
			grid [label .password.two.trytwolabel -text "Repeat pwd:"]
			grid [entry .password.two.trytwoentry -show "*"  -textvariable pwdtwo -background white -width 42]
	
			place .password.two.trytwolabel -x 4 -y 10
			place .password.two.trytwoentry -x 100 -y 12
		}
		frame .password.button -borderwidth 2 -padx 3 -pady 2 -width 540 -height 48
		place .password.button -x 10 -y 140 ; # was 120
		grid [button .password.button.cancel -background grey85 -text "Cancel" -pady 3 -width 9 -command {
			destroy .password
			unset pwdone
			if {[info exists pwdtwo] > 0} {
				unset pwdtwo
			}		
			return
		}]
		grid [button .password.button.b  -background grey85 -text "Continue" -pady 3 -width 9 -command {
			destroy .password
			if {$pwv < 1} {
				if {$pwdone != $pwdtwo} {
					tk_messageBox -message "Passwords don't match, returning"
					unset pwdone
					unset pwdtwo
					return
				}
			}
			set q $pwdone
			unset pwdone
			if {$pwv < 1} {
				unset pwdtwo
			}
			set tpi {! @ # $ % & - _ = +}
			set tpic 0
			set tpc -1
			set tp " "
			while {$tp != ""} {
				incr tpc
				set tp [string index $q $tpc]
				if {$tp == " "} {
					set tp [lindex $tpi $tpic]
					incr tpic
					if {$tpic > 9} {
						set tpic 0
					}
				set q [string replace $q $tpc $tpc $tp]
				}
			}

			set tem $q
			set q [md5::md5 -hex $q]
			set passlengthsem 0
			set passlength [string length $tem]
			while {$passlengthsem < $passlength} {
				set qz [md5::md5 -hex $q]
				set q "$q$qz"
				incr passlengthsem
			}
			set q [md5::md5 -hex $q]
			set key $q
			destroy .password
			if {$pwv == 0 || $pwv == 1} {
				makedb $ndb $key
			}
			if {$ez == 0} {
				decipher_db $ndb $key
				sqlite3 pw $ndb
				if {[catch {set pwlist [pw eval {SELECT description FROM pwd_locker ORDER BY description}]} msg]} {
					tk_messageBox -message "Error: $msg probably a password issue"
					set z 1
					pw close
					wipe_db $ndb
				} else {
					pw close
					encrypt_db $ndb $key
					wipe_db $ndb
				}
				if {$z == 0} {
					tk_messageBox -message "Successfully logged in"
					set loggedin "yes"
					clearall
					.top.desc_cb configure -values $pwlist
				}
			}
			set ez 0
		}]
		place .password.button.b -x 0
		place .password.button.cancel -x 100	

		focus -force .password.one.tryoneentry
		bind .password.one.tryoneentry <Return> ".password.button.b invoke"
		bind .password.one.tryoneentry <KP_Enter> ".password.button.b invoke"
	}
}]
place .last.login -x 10 -y 1

grid [button .last.logout -background grey65 -text "Logout" -width 15 -command {
	set pwlist {}
	.top.desc_cb configure -values $pwlist
	if {[info exists loggedin] < 1} {
		tk_messageBox -message "You're already logged out"
	} else {
		unset loggedin
		unset key
		clearall
		.top.desc_cb delete 0 end
		.top.desc_cb insert 0 "please log in"
	}
}]
place .last.logout -x 135 -y 1

grid [button .last.clear -background grey65 -text "Clear Text" -width 15 -command {
	clearall
}]
place .last.clear -x 260 -y 1
	
grid [button .last.exit -background grey65 -text "Exit App" -width 15 -command {endapp}]
place .last.exit -x 385 -y 1
set isDB [file isfile $ndb]
if {$isDB > 0} {
	file delete -force $ndb
}

set choices {
	1
	2
	3
	4
	5
	6
	7
	8
	9
	0
    a
    b
    c
    d
    e
    f
    g
    h
    i
    j
    k
    l
    m
    n
    o
    p
    q
    r
    s
    t
    u
    v
    w
    x
    y
    z
    A
    B
    C
    D
    E
    F
    G
    H
    I
    J
    K
    L
    M
    N
    O
    P
    Q
    R
    S
    T
    U
    V
    W
    X
    Y
    Z
    !
    @
    #
    $
    %
    ^
    &
    *
    (
    )
    -
    _
    =
    +
    ;
    :
    '
    ?
    .
    >
    ,
    <
    |
}
set putp ""

proc copypaste {cpmenu xx yy} {
	incr yy 110
    set x [expr [winfo rootx .]+$xx]
    set y [expr [winfo rooty .]+$yy]
    tk_popup $cpmenu $x $y
}

menu .cpmenu -tearoff 0
.cpmenu add command -label "Copy" -command {
	set vt [.notesb.text tag ranges sel]
	if {$vt == ""} {
		return
	}
	set vtl [split $vt " "]
	set vttag [.notesb.text get [lindex $vtl 0] [lindex $vtl 1]]
	clipboard clear
	clipboard append $vttag
}
.cpmenu add command -label "Paste" -command {
	if {[catch {set vp [clipboard get]} msg]} {
		tk_messageBox -message "Error $msg, Returning"
		return
	}
	set vp [string trim $vp]	
	set vt [.notesb.text tag ranges sel]
	if {$vt != ""} {
		set vtl [split $vt " "]
		set vttag [.notesb.text delete [lindex $vtl 0] [lindex $vtl 1]]
	}
	.notesb.text insert insert $vp
}
bind . <3> {copypaste .cpmenu %x %y}

proc readme {} {
	upvar pos pos
	if {[file isfile "./readme.txt"] < 1} {
		tk_messageBox -message "Missing reame.txt file, returning"
		return
	}
	if {$pos == "Linux"} {
		catch {exec xed ./readme.txt}
	} else {
		exec {*}[auto_execok start] notepad.exe "./readme.txt"
	}
	return
}
