tkcon.tcl 161 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379
  1. #!/bin/sh
  2. # \
  3. exec wish "$0" ${1+"$@"}
  4. #
  5. ## tkcon.tcl
  6. ## Enhanced Tk Console, part of the VerTcl system
  7. ##
  8. ## Originally based off Brent Welch's Tcl Shell Widget
  9. ## (from "Practical Programming in Tcl and Tk")
  10. ##
  11. ## Thanks to the following (among many) for early bug reports & code ideas:
  12. ## Steven Wahl <steven@indra.com>, Jan Nijtmans <nijtmans@nici.kun.nl>
  13. ## Crimmins <markcrim@umich.edu>, Wart <wart@ugcs.caltech.edu>
  14. ##
  15. ## Copyright (c) 1995-2002 Jeffrey Hobbs
  16. ## Initiated: Thu Aug 17 15:36:47 PDT 1995
  17. ##
  18. ## jeff.hobbs@acm.org, jeff@hobbs.org
  19. ##
  20. ## source standard_disclaimer.tcl
  21. ## source bourbon_ware.tcl
  22. ##
  23. # Proxy support for retrieving the current version of Tkcon.
  24. #
  25. # Mon Jun 25 12:19:56 2001 - Pat Thoyts <Pat.Thoyts@bigfoot.com>
  26. #
  27. # In your tkcon.cfg or .tkconrc file put your proxy details into the
  28. # `proxy' member of the `PRIV' array. e.g.:
  29. #
  30. # set ::tkcon::PRIV(proxy) wwwproxy:8080
  31. #
  32. # If you want to be prompted for proxy authentication details (eg for
  33. # an NT proxy server) make the second element of this variable non-nil - eg:
  34. #
  35. # set ::tkcon::PRIV(proxy) {wwwproxy:8080 1}
  36. #
  37. # Or you can set the above variable from within tkcon by calling
  38. #
  39. # tkcon master set ::tkcon:PRIV(proxy) wwwproxy:8080
  40. #
  41. if {$tcl_version < 8.0} {
  42. return -code error "tkcon requires at least Tcl/Tk8"
  43. } else {
  44. #disabled, otherwise we get an error:
  45. #version conflict for package "Tk": have 8.6.12, need exactly 8.6
  46. #package require -exact Tk $tcl_version
  47. }
  48. catch {package require bogus-package-name}
  49. foreach pkg [info loaded {}] {
  50. set file [lindex $pkg 0]
  51. set name [lindex $pkg 1]
  52. if {![catch {set version [package require $name]}]} {
  53. if {[string match {} [package ifneeded $name $version]]} {
  54. package ifneeded $name $version [list load $file $name]
  55. }
  56. }
  57. }
  58. catch {unset pkg file name version}
  59. # Tk 8.4 makes previously exposed stuff private.
  60. # FIX: Update tkcon to not rely on the private Tk code.
  61. #
  62. if {![llength [info globals tkPriv]]} {
  63. ::tk::unsupported::ExposePrivateVariable tkPriv
  64. }
  65. foreach cmd {SetCursor UpDownLine Transpose ScrollPages} {
  66. if {![llength [info commands tkText$cmd]]} {
  67. ::tk::unsupported::ExposePrivateCommand tkText$cmd
  68. }
  69. }
  70. # Initialize the ::tkcon namespace
  71. #
  72. namespace eval ::tkcon {
  73. # when modifying this line, make sure that the auto-upgrade check
  74. # for version still works.
  75. variable VERSION "2.4"
  76. # The OPT variable is an array containing most of the optional
  77. # info to configure. COLOR has the color data.
  78. variable OPT
  79. variable COLOR
  80. # PRIV is used for internal data that only tkcon should fiddle with.
  81. variable PRIV
  82. set PRIV(WWW) [info exists embed_args]
  83. }
  84. ## ::tkcon::Init - inits tkcon
  85. #
  86. # Calls: ::tkcon::InitUI
  87. # Outputs: errors found in tkcon's resource file
  88. ##
  89. proc ::tkcon::Init {args} {
  90. variable VERSION
  91. variable OPT
  92. variable COLOR
  93. variable PRIV
  94. global tcl_platform env tcl_interactive errorInfo
  95. set tcl_interactive 1
  96. set argc [llength $args]
  97. ##
  98. ## When setting up all the default values, we always check for
  99. ## prior existence. This allows users who embed tkcon to modify
  100. ## the initial state before tkcon initializes itself.
  101. ##
  102. # bg == {} will get bg color from the main toplevel (in InitUI)
  103. foreach {key default} {
  104. bg {}
  105. blink \#FFFF00
  106. cursor \#000000
  107. disabled \#4D4D4D
  108. proc \#008800
  109. var \#FFC0D0
  110. prompt \#8F4433
  111. stdin \#000000
  112. stdout \#0000FF
  113. stderr \#FF0000
  114. } {
  115. if {![info exists COLOR($key)]} { set COLOR($key) $default }
  116. }
  117. foreach {key default} {
  118. autoload {}
  119. blinktime 500
  120. blinkrange 1
  121. buffer 512
  122. calcmode 0
  123. cols 80
  124. debugPrompt {(level \#$level) debug [history nextid] > }
  125. dead {}
  126. edit edit
  127. expandorder {Pathname Variable Procname}
  128. font {}
  129. history 48
  130. hoterrors 1
  131. library {}
  132. lightbrace 1
  133. lightcmd 1
  134. maineval {}
  135. maxmenu 15
  136. nontcl 0
  137. prompt1 {ignore this, it's set below}
  138. rows 20
  139. scrollypos right
  140. showmenu 1
  141. showmultiple 1
  142. showstatusbar 0
  143. slaveeval {}
  144. slaveexit close
  145. subhistory 1
  146. gc-delay 60000
  147. gets {congets}
  148. overrideexit 1
  149. usehistory 1
  150. exec slave
  151. } {
  152. if {![info exists OPT($key)]} { set OPT($key) $default }
  153. }
  154. foreach {key default} {
  155. app {}
  156. appname {}
  157. apptype slave
  158. namesp ::
  159. cmd {}
  160. cmdbuf {}
  161. cmdsave {}
  162. event 1
  163. deadapp 0
  164. deadsock 0
  165. debugging 0
  166. displayWin .
  167. histid 0
  168. find {}
  169. find,case 0
  170. find,reg 0
  171. errorInfo {}
  172. protocol exit
  173. showOnStartup 1
  174. slaveprocs {
  175. alias clear dir dump echo idebug lremove
  176. tkcon_puts tkcon_gets observe observe_var unalias which what
  177. }
  178. RCS {RCS: @(#) $Id: tkcon.tcl,v 1.1 2004/01/22 17:32:32 techenti Exp $}
  179. HEADURL {http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/tkcon/tkcon/tkcon.tcl?rev=HEAD}
  180. docs "http://tkcon.sourceforge.net/"
  181. email {jeff@hobbs.org}
  182. root .
  183. } {
  184. if {![info exists PRIV($key)]} { set PRIV($key) $default }
  185. }
  186. foreach {key default} {
  187. slavealias { $OPT(edit) more less tkcon }
  188. } {
  189. if {![info exists PRIV($key)]} { set PRIV($key) [subst $default] }
  190. }
  191. set PRIV(version) $VERSION
  192. if {[info exists PRIV(name)]} {
  193. set title $PRIV(name)
  194. } else {
  195. MainInit
  196. # some main initialization occurs later in this proc,
  197. # to go after the UI init
  198. set MainInit 1
  199. set title Main
  200. }
  201. ## NOTES FOR STAYING IN PRIMARY INTERPRETER:
  202. ##
  203. ## If you set ::tkcon::OPT(exec) to {}, then instead of a multiple
  204. ## interp model, you get tkcon operating in the main interp by default.
  205. ## This can be useful when attaching to programs that like to operate
  206. ## in the main interpter (for example, based on special wish'es).
  207. ## You can set this from the command line with -exec ""
  208. ## A side effect is that all tkcon command line args will be used
  209. ## by the first console only.
  210. #set OPT(exec) {}
  211. if {$PRIV(WWW)} {
  212. lappend PRIV(slavealias) history
  213. set OPT(prompt1) {[history nextid] % }
  214. } else {
  215. lappend PRIV(slaveprocs) tcl_unknown unknown
  216. set OPT(prompt1) {([file tail [pwd]]) [history nextid] % }
  217. }
  218. ## If we are using the default '.' toplevel, and there appear to be
  219. ## children of '.', then make sure we use a disassociated toplevel.
  220. if {$PRIV(root) == "." && [llength [winfo children .]]} {
  221. set PRIV(root) .tkcon
  222. }
  223. ## Do platform specific configuration here, other than defaults
  224. ### Use tkcon.cfg filename for resource filename on non-unix systems
  225. ### Determine what directory the resource file should be in
  226. switch $tcl_platform(platform) {
  227. macintosh {
  228. if {![interp issafe]} {cd [file dirname [info script]]}
  229. set envHome PREF_FOLDER
  230. set rcfile tkcon.cfg
  231. set histfile tkcon.hst
  232. catch {console hide}
  233. }
  234. windows {
  235. set envHome HOME
  236. set rcfile tkcon.cfg
  237. set histfile tkcon.hst
  238. }
  239. unix {
  240. set envHome HOME
  241. set rcfile .tkconrc
  242. set histfile .tkcon_history
  243. }
  244. }
  245. if {[info exists env($envHome)]} {
  246. set home $env($envHome)
  247. if {[file pathtype $home] == "volumerelative"} {
  248. # Convert 'C:' to 'C:/' if necessary, innocuous otherwise
  249. append home /
  250. }
  251. if {![info exists PRIV(rcfile)]} {
  252. set PRIV(rcfile) [file join $home $rcfile]
  253. }
  254. if {![info exists PRIV(histfile)]} {
  255. set PRIV(histfile) [file join $home $histfile]
  256. }
  257. }
  258. ## Handle command line arguments before sourcing resource file to
  259. ## find if resource file is being specified (let other args pass).
  260. if {[set i [lsearch -exact $args -rcfile]] != -1} {
  261. set PRIV(rcfile) [lindex $args [incr i]]
  262. }
  263. if {!$PRIV(WWW) && [file exists $PRIV(rcfile)]} {
  264. set code [catch {uplevel \#0 [list source $PRIV(rcfile)]} err]
  265. }
  266. if {[info exists env(TK_CON_LIBRARY)]} {
  267. lappend ::auto_path $env(TK_CON_LIBRARY)
  268. } else {
  269. lappend ::auto_path $OPT(library)
  270. }
  271. if {![info exists ::tcl_pkgPath]} {
  272. set dir [file join [file dirname [info nameofexec]] lib]
  273. if {[llength [info commands @scope]]} {
  274. set dir [file join $dir itcl]
  275. }
  276. catch {source [file join $dir pkgIndex.tcl]}
  277. }
  278. catch {tclPkgUnknown dummy-name dummy-version}
  279. ## Handle rest of command line arguments after sourcing resource file
  280. ## and slave is created, but before initializing UI or setting packages.
  281. set slaveargs {}
  282. set slavefiles {}
  283. set truth {^(1|yes|true|on)$}
  284. for {set i 0} {$i < $argc} {incr i} {
  285. set arg [lindex $args $i]
  286. if {[string match {-*} $arg]} {
  287. set val [lindex $args [incr i]]
  288. ## Handle arg based options
  289. switch -glob -- $arg {
  290. -- - -argv - -args {
  291. set argv [concat -- [lrange $argv $i end]]
  292. set argc [llength $argv]
  293. break
  294. }
  295. -color-* { set COLOR([string range $arg 7 end]) $val }
  296. -exec { set OPT(exec) $val }
  297. -main - -e - -eval { append OPT(maineval) \n$val\n }
  298. -package - -load { lappend OPT(autoload) $val }
  299. -slave { append OPT(slaveeval) \n$val\n }
  300. -nontcl { set OPT(nontcl) [regexp -nocase $truth $val]}
  301. -root { set PRIV(root) $val }
  302. -font { set OPT(font) $val }
  303. -rcfile {}
  304. default { lappend slaveargs $arg; incr i -1 }
  305. }
  306. } elseif {[file isfile $arg]} {
  307. lappend slavefiles $arg
  308. } else {
  309. lappend slaveargs $arg
  310. }
  311. }
  312. ## Create slave executable
  313. if {[string compare {} $OPT(exec)]} {
  314. uplevel \#0 ::tkcon::InitSlave $OPT(exec) $slaveargs
  315. } else {
  316. set argc [llength $slaveargs]
  317. set args $slaveargs
  318. uplevel \#0 $slaveargs
  319. }
  320. ## Attach to the slave, EvalAttached will then be effective
  321. Attach $PRIV(appname) $PRIV(apptype)
  322. InitUI $title
  323. ## swap puts and gets with the tkcon versions to make sure all
  324. ## input and output is handled by tkcon
  325. if {![catch {rename ::puts ::tkcon_tcl_puts}]} {
  326. interp alias {} ::puts {} ::tkcon_puts
  327. }
  328. if {($OPT(gets) != "") && ![catch {rename ::gets ::tkcon_tcl_gets}]} {
  329. interp alias {} ::gets {} ::tkcon_gets
  330. }
  331. EvalSlave history keep $OPT(history)
  332. if {[info exists MainInit]} {
  333. # Source history file only for the main console, as all slave
  334. # consoles will adopt from the main's history, but still
  335. # keep separate histories
  336. if {!$PRIV(WWW) && $OPT(usehistory) && [file exists $PRIV(histfile)]} {
  337. puts -nonewline "loading history file ... "
  338. # The history file is built to be loaded in and
  339. # understood by tkcon
  340. if {[catch {uplevel \#0 [list source $PRIV(histfile)]} herr]} {
  341. puts stderr "error:\n$herr"
  342. append PRIV(errorInfo) $errorInfo\n
  343. }
  344. set PRIV(event) [EvalSlave history nextid]
  345. puts "[expr {$PRIV(event)-1}] events added"
  346. }
  347. }
  348. ## Autoload specified packages in slave
  349. set pkgs [EvalSlave package names]
  350. foreach pkg $OPT(autoload) {
  351. puts -nonewline "autoloading package \"$pkg\" ... "
  352. if {[lsearch -exact $pkgs $pkg]>-1} {
  353. if {[catch {EvalSlave package require [list $pkg]} pkgerr]} {
  354. puts stderr "error:\n$pkgerr"
  355. append PRIV(errorInfo) $errorInfo\n
  356. } else { puts "OK" }
  357. } else {
  358. puts stderr "error: package does not exist"
  359. }
  360. }
  361. ## Evaluate maineval in slave
  362. if {[string compare {} $OPT(maineval)] && \
  363. [catch {uplevel \#0 $OPT(maineval)} merr]} {
  364. puts stderr "error in eval:\n$merr"
  365. append PRIV(errorInfo) $errorInfo\n
  366. }
  367. ## Source extra command line argument files into slave executable
  368. foreach fn $slavefiles {
  369. puts -nonewline "slave sourcing \"$fn\" ... "
  370. if {[catch {EvalSlave source [list $fn]} fnerr]} {
  371. puts stderr "error:\n$fnerr"
  372. append PRIV(errorInfo) $errorInfo\n
  373. } else { puts "OK" }
  374. }
  375. ## Evaluate slaveeval in slave
  376. if {[string compare {} $OPT(slaveeval)] && \
  377. [catch {interp eval $OPT(exec) $OPT(slaveeval)} serr]} {
  378. puts stderr "error in slave eval:\n$serr"
  379. append PRIV(errorInfo) $errorInfo\n
  380. }
  381. ## Output any error/output that may have been returned from rcfile
  382. if {[info exists code] && $code && [string compare {} $err]} {
  383. puts stderr "error in $PRIV(rcfile):\n$err"
  384. append PRIV(errorInfo) $errorInfo
  385. }
  386. if {[string compare {} $OPT(exec)]} {
  387. StateCheckpoint [concat $PRIV(name) $OPT(exec)] slave
  388. }
  389. StateCheckpoint $PRIV(name) slave
  390. Prompt "$title console display active (Tcl$::tcl_patchLevel / Tk$::tk_patchLevel)\n"
  391. }
  392. ## ::tkcon::InitSlave - inits the slave by placing key procs and aliases in it
  393. ## It's arg[cv] are based on passed in options, while argv0 is the same as
  394. ## the master. tcl_interactive is the same as the master as well.
  395. # ARGS: slave - name of slave to init. If it does not exist, it is created.
  396. # args - args to pass to a slave as argv/argc
  397. ##
  398. proc ::tkcon::InitSlave {slave args} {
  399. variable OPT
  400. variable COLOR
  401. variable PRIV
  402. global argv0 tcl_interactive tcl_library env auto_path
  403. if {[string match {} $slave]} {
  404. return -code error "Don't init the master interpreter, goofball"
  405. }
  406. if {![interp exists $slave]} { interp create $slave }
  407. if {[interp eval $slave info command source] == ""} {
  408. $slave alias source SafeSource $slave
  409. $slave alias load SafeLoad $slave
  410. $slave alias open SafeOpen $slave
  411. $slave alias file file
  412. interp eval $slave [dump var -nocomplain tcl_library auto_path env]
  413. interp eval $slave { catch {source [file join $tcl_library init.tcl]} }
  414. interp eval $slave { catch unknown }
  415. }
  416. $slave alias exit exit
  417. interp eval $slave {
  418. # Do package require before changing around puts/gets
  419. catch {package require bogus-package-name}
  420. catch {rename ::puts ::tkcon_tcl_puts}
  421. }
  422. foreach cmd $PRIV(slaveprocs) { $slave eval [dump proc $cmd] }
  423. foreach cmd $PRIV(slavealias) { $slave alias $cmd $cmd }
  424. interp alias $slave ::ls $slave ::dir -full
  425. interp alias $slave ::puts $slave ::tkcon_puts
  426. if {$OPT(gets) != ""} {
  427. interp eval $slave { catch {rename ::gets ::tkcon_tcl_gets} }
  428. interp alias $slave ::gets $slave ::tkcon_gets
  429. }
  430. if {[info exists argv0]} {interp eval $slave [list set argv0 $argv0]}
  431. interp eval $slave set tcl_interactive $tcl_interactive \; \
  432. set auto_path [list $auto_path] \; \
  433. set argc [llength $args] \; \
  434. set argv [list $args] \; {
  435. if {![llength [info command bgerror]]} {
  436. proc bgerror err {
  437. global errorInfo
  438. set body [info body bgerror]
  439. rename ::bgerror {}
  440. if {[auto_load bgerror]} { return [bgerror $err] }
  441. proc bgerror err $body
  442. tkcon bgerror $err $errorInfo
  443. }
  444. }
  445. }
  446. foreach pkg [lremove [package names] Tcl] {
  447. foreach v [package versions $pkg] {
  448. interp eval $slave [list package ifneeded $pkg $v \
  449. [package ifneeded $pkg $v]]
  450. }
  451. }
  452. }
  453. ## ::tkcon::InitInterp - inits an interpreter by placing key
  454. ## procs and aliases in it.
  455. # ARGS: name - interp name
  456. # type - interp type (slave|interp)
  457. ##
  458. proc ::tkcon::InitInterp {name type} {
  459. variable OPT
  460. variable PRIV
  461. ## Don't allow messing up a local master interpreter
  462. if {[string match namespace $type] || ([string match slave $type] && \
  463. [regexp {^([Mm]ain|Slave[0-9]+)$} $name])} return
  464. set old [Attach]
  465. set oldname $PRIV(namesp)
  466. catch {
  467. Attach $name $type
  468. EvalAttached { catch {rename ::puts ::tkcon_tcl_puts} }
  469. foreach cmd $PRIV(slaveprocs) { EvalAttached [dump proc $cmd] }
  470. switch -exact $type {
  471. slave {
  472. foreach cmd $PRIV(slavealias) {
  473. Main interp alias $name ::$cmd $PRIV(name) ::$cmd
  474. }
  475. }
  476. interp {
  477. set thistkcon [tk appname]
  478. foreach cmd $PRIV(slavealias) {
  479. EvalAttached "proc $cmd args { send [list $thistkcon] $cmd \$args }"
  480. }
  481. }
  482. }
  483. ## Catch in case it's a 7.4 (no 'interp alias') interp
  484. EvalAttached {
  485. catch {interp alias {} ::ls {} ::dir -full}
  486. if {[catch {interp alias {} ::puts {} ::tkcon_puts}]} {
  487. catch {rename ::tkcon_puts ::puts}
  488. }
  489. }
  490. if {$OPT(gets) != ""} {
  491. EvalAttached {
  492. catch {rename ::gets ::tkcon_tcl_gets}
  493. if {[catch {interp alias {} ::gets {} ::tkcon_gets}]} {
  494. catch {rename ::tkcon_gets ::gets}
  495. }
  496. }
  497. }
  498. return
  499. } {err}
  500. eval Attach $old
  501. AttachNamespace $oldname
  502. if {[string compare {} $err]} { return -code error $err }
  503. }
  504. ## ::tkcon::InitUI - inits UI portion (console) of tkcon
  505. ## Creates all elements of the console window and sets up the text tags
  506. # ARGS: root - widget pathname of the tkcon console root
  507. # title - title for the console root and main (.) windows
  508. # Calls: ::tkcon::InitMenus, ::tkcon::Prompt
  509. ##
  510. proc ::tkcon::InitUI {title} {
  511. variable OPT
  512. variable PRIV
  513. variable COLOR
  514. set root $PRIV(root)
  515. if {[string match . $root]} { set w {} } else { set w [toplevel $root] }
  516. if {!$PRIV(WWW)} {
  517. wm withdraw $root
  518. wm protocol $root WM_DELETE_WINDOW $PRIV(protocol)
  519. }
  520. set PRIV(base) $w
  521. ## Text Console
  522. set PRIV(console) [set con $w.text]
  523. text $con -wrap char -yscrollcommand [list $w.sy set] \
  524. -foreground $COLOR(stdin) \
  525. -insertbackground $COLOR(cursor)
  526. $con mark set output 1.0
  527. $con mark set limit 1.0
  528. if {[string compare {} $COLOR(bg)]} {
  529. $con configure -background $COLOR(bg)
  530. }
  531. set COLOR(bg) [$con cget -background]
  532. if {[string compare {} $OPT(font)]} {
  533. ## Set user-requested font, if any
  534. $con configure -font $OPT(font)
  535. } elseif {[string compare unix $::tcl_platform(platform)]} {
  536. ## otherwise make sure the font is monospace
  537. set font [$con cget -font]
  538. if {![font metrics $font -fixed]} {
  539. font create tkconfixed -family Courier -size 12
  540. $con configure -font tkconfixed
  541. }
  542. } else {
  543. $con configure -font fixed
  544. }
  545. set OPT(font) [$con cget -font]
  546. ## Scrollbar
  547. set PRIV(scrolly) [scrollbar $w.sy -takefocus 0 -bd 1 \
  548. -command [list $con yview]]
  549. if {!$PRIV(WWW)} {
  550. if {[string match "Windows CE" $::tcl_platform(os)]} {
  551. $w.sy configure -width 10
  552. catch {font create tkconfixed}
  553. font configure tkconfixed -family Tahoma -size 8
  554. $con configure -font tkconfixed -bd 0 -padx 0 -pady 0
  555. set cw [font measure tkconfixed "0"]
  556. set ch [font metrics tkconfixed -linespace]
  557. set sw [winfo screenwidth $con]
  558. set sh [winfo screenheight $con]
  559. # We need the magic hard offsets until I find a way to
  560. # correctly assume size
  561. if {$cw*($OPT(cols)+2) > $sw} {
  562. set OPT(cols) [expr {($sw / $cw) - 2}]
  563. }
  564. if {$ch*($OPT(rows)+3) > $sh} {
  565. set OPT(rows) [expr {($sh / $ch) - 3}]
  566. }
  567. # Place it so that the titlebar underlaps the CE titlebar
  568. wm geometry $root +0+0
  569. }
  570. $con configure -setgrid 1 -width $OPT(cols) -height $OPT(rows)
  571. }
  572. bindtags $con [list $con TkConsole TkConsolePost $root all]
  573. ## Menus
  574. ## catch against use in plugin
  575. if {[catch {menu $w.mbar} PRIV(menubar)]} {
  576. set PRIV(menubar) [frame $w.mbar -relief raised -bd 1]
  577. }
  578. InitMenus $PRIV(menubar) $title
  579. Bindings
  580. if {$OPT(showmenu)} {
  581. $root configure -menu $PRIV(menubar)
  582. }
  583. pack $w.sy -side $OPT(scrollypos) -fill y
  584. pack $con -fill both -expand 1
  585. set PRIV(statusbar) [set sbar [frame $w.sbar]]
  586. label $sbar.attach -relief sunken -bd 1 -anchor w \
  587. -textvariable ::tkcon::PRIV(StatusAttach)
  588. label $sbar.mode -relief sunken -bd 1 -anchor w \
  589. -textvariable ::tkcon::PRIV(StatusMode)
  590. label $sbar.cursor -relief sunken -bd 1 -anchor w -width 6 \
  591. -textvariable ::tkcon::PRIV(StatusCursor)
  592. set padx [expr {![string match "Windows CE" $::tcl_platform(os)]}]
  593. grid $sbar.attach $sbar.mode $sbar.cursor -sticky news -padx $padx
  594. grid columnconfigure $sbar 0 -weight 1
  595. grid columnconfigure $sbar 1 -weight 1
  596. grid columnconfigure $sbar 2 -weight 0
  597. if {$OPT(showstatusbar)} {
  598. pack $sbar -side bottom -fill x -before $::tkcon::PRIV(scrolly)
  599. }
  600. foreach col {prompt stdout stderr stdin proc} {
  601. $con tag configure $col -foreground $COLOR($col)
  602. }
  603. $con tag configure var -background $COLOR(var)
  604. $con tag raise sel
  605. $con tag configure blink -background $COLOR(blink)
  606. $con tag configure find -background $COLOR(blink)
  607. if {!$PRIV(WWW)} {
  608. wm title $root "tkcon $PRIV(version) $title"
  609. bind $con <Configure> {
  610. scan [wm geometry [winfo toplevel %W]] "%%dx%%d" \
  611. ::tkcon::OPT(cols) ::tkcon::OPT(rows)
  612. }
  613. if {$PRIV(showOnStartup)} { wm deiconify $root }
  614. }
  615. if {$PRIV(showOnStartup)} { focus -force $PRIV(console) }
  616. if {$OPT(gc-delay)} {
  617. after $OPT(gc-delay) ::tkcon::GarbageCollect
  618. }
  619. }
  620. ## ::tkcon::GarbageCollect - do various cleanup ops periodically to our setup
  621. ##
  622. proc ::tkcon::GarbageCollect {} {
  623. variable OPT
  624. variable PRIV
  625. set w $PRIV(console)
  626. if {[winfo exists $w]} {
  627. ## Remove error tags that no longer span anything
  628. ## Make sure the tag pattern matches the unique tag prefix
  629. foreach tag [$w tag names] {
  630. if {[string match _tag* $tag] && ![llength [$w tag ranges $tag]]} {
  631. $w tag delete $tag
  632. }
  633. }
  634. }
  635. if {$OPT(gc-delay)} {
  636. after $OPT(gc-delay) ::tkcon::GarbageCollect
  637. }
  638. }
  639. ## ::tkcon::Eval - evaluates commands input into console window
  640. ## This is the first stage of the evaluating commands in the console.
  641. ## They need to be broken up into consituent commands (by ::tkcon::CmdSep) in
  642. ## case a multiple commands were pasted in, then each is eval'ed (by
  643. ## ::tkcon::EvalCmd) in turn. Any uncompleted command will not be eval'ed.
  644. # ARGS: w - console text widget
  645. # Calls: ::tkcon::CmdGet, ::tkcon::CmdSep, ::tkcon::EvalCmd
  646. ##
  647. proc ::tkcon::Eval {w} {
  648. set incomplete [CmdSep [CmdGet $w] cmds last]
  649. $w mark set insert end-1c
  650. $w insert end \n
  651. if {[llength $cmds]} {
  652. foreach c $cmds {EvalCmd $w $c}
  653. $w insert insert $last {}
  654. } elseif {!$incomplete} {
  655. EvalCmd $w $last
  656. }
  657. $w see insert
  658. }
  659. ## ::tkcon::EvalCmd - evaluates a single command, adding it to history
  660. # ARGS: w - console text widget
  661. # cmd - the command to evaluate
  662. # Calls: ::tkcon::Prompt
  663. # Outputs: result of command to stdout (or stderr if error occured)
  664. # Returns: next event number
  665. ##
  666. proc ::tkcon::EvalCmd {w cmd} {
  667. variable OPT
  668. variable PRIV
  669. $w mark set output end
  670. if {[string compare {} $cmd]} {
  671. set code 0
  672. if {$OPT(subhistory)} {
  673. set ev [EvalSlave history nextid]
  674. incr ev -1
  675. if {[string match !! $cmd]} {
  676. set code [catch {EvalSlave history event $ev} cmd]
  677. if {!$code} {$w insert output $cmd\n stdin}
  678. } elseif {[regexp {^!(.+)$} $cmd dummy event]} {
  679. ## Check last event because history event is broken
  680. set code [catch {EvalSlave history event $ev} cmd]
  681. if {!$code && ![string match ${event}* $cmd]} {
  682. set code [catch {EvalSlave history event $event} cmd]
  683. }
  684. if {!$code} {$w insert output $cmd\n stdin}
  685. } elseif {[regexp {^\^([^^]*)\^([^^]*)\^?$} $cmd dummy old new]} {
  686. set code [catch {EvalSlave history event $ev} cmd]
  687. if {!$code} {
  688. regsub -all -- $old $cmd $new cmd
  689. $w insert output $cmd\n stdin
  690. }
  691. } elseif {$OPT(calcmode) && ![catch {expr $cmd} err]} {
  692. AddSlaveHistory $cmd
  693. set cmd $err
  694. set code -1
  695. }
  696. }
  697. if {$code} {
  698. $w insert output $cmd\n stderr
  699. } else {
  700. ## We are about to evaluate the command, so move the limit
  701. ## mark to ensure that further <Return>s don't cause double
  702. ## evaluation of this command - for cases like the command
  703. ## has a vwait or something in it
  704. $w mark set limit end
  705. if {$OPT(nontcl) && [string match interp $PRIV(apptype)]} {
  706. set code [catch {EvalSend $cmd} res]
  707. if {$code == 1} {
  708. set PRIV(errorInfo) "Non-Tcl errorInfo not available"
  709. }
  710. } elseif {[string match socket $PRIV(apptype)]} {
  711. set code [catch {EvalSocket $cmd} res]
  712. if {$code == 1} {
  713. set PRIV(errorInfo) "Socket-based errorInfo not available"
  714. }
  715. } else {
  716. set code [catch {EvalAttached $cmd} res]
  717. if {$code == 1} {
  718. if {[catch {EvalAttached [list set errorInfo]} err]} {
  719. set PRIV(errorInfo) "Error getting errorInfo:\n$err"
  720. } else {
  721. set PRIV(errorInfo) $err
  722. }
  723. }
  724. }
  725. AddSlaveHistory $cmd
  726. catch {EvalAttached [list set {} $res]}
  727. if {$code} {
  728. if {$OPT(hoterrors)} {
  729. set tag [UniqueTag $w]
  730. $w insert output $res [list stderr $tag] \n stderr
  731. $w tag bind $tag <Enter> \
  732. [list $w tag configure $tag -under 1]
  733. $w tag bind $tag <Leave> \
  734. [list $w tag configure $tag -under 0]
  735. $w tag bind $tag <ButtonRelease-1> \
  736. "if {!\[info exists tkPriv(mouseMoved)\] || !\$tkPriv(mouseMoved)} \
  737. {[list $OPT(edit) -attach [Attach] -type error -- $PRIV(errorInfo)]}"
  738. } else {
  739. $w insert output $res\n stderr
  740. }
  741. } elseif {[string compare {} $res]} {
  742. $w insert output $res\n stdout
  743. }
  744. }
  745. }
  746. Prompt
  747. set PRIV(event) [EvalSlave history nextid]
  748. }
  749. ## ::tkcon::EvalSlave - evaluates the args in the associated slave
  750. ## args should be passed to this procedure like they would be at
  751. ## the command line (not like to 'eval').
  752. # ARGS: args - the command and args to evaluate
  753. ##
  754. proc ::tkcon::EvalSlave args {
  755. interp eval $::tkcon::OPT(exec) $args
  756. }
  757. ## ::tkcon::EvalOther - evaluate a command in a foreign interp or slave
  758. ## without attaching to it. No check for existence is made.
  759. # ARGS: app - interp/slave name
  760. # type - (slave|interp)
  761. ##
  762. proc ::tkcon::EvalOther { app type args } {
  763. if {[string compare slave $type]==0} {
  764. return [Slave $app $args]
  765. } else {
  766. return [uplevel 1 send [list $app] $args]
  767. }
  768. }
  769. ## ::tkcon::AddSlaveHistory -
  770. ## Command is added to history only if different from previous command.
  771. ## This also doesn't cause the history id to be incremented, although the
  772. ## command will be evaluated.
  773. # ARGS: cmd - command to add
  774. ##
  775. proc ::tkcon::AddSlaveHistory cmd {
  776. set ev [EvalSlave history nextid]
  777. incr ev -1
  778. set code [catch {EvalSlave history event $ev} lastCmd]
  779. if {$code || [string compare $cmd $lastCmd]} {
  780. EvalSlave history add $cmd
  781. }
  782. }
  783. ## ::tkcon::EvalSend - sends the args to the attached interpreter
  784. ## Varies from 'send' by determining whether attachment is dead
  785. ## when an error is received
  786. # ARGS: cmd - the command string to send across
  787. # Returns: the result of the command
  788. ##
  789. proc ::tkcon::EvalSend cmd {
  790. variable OPT
  791. variable PRIV
  792. if {$PRIV(deadapp)} {
  793. if {[lsearch -exact [winfo interps] $PRIV(app)]<0} {
  794. return
  795. } else {
  796. set PRIV(appname) [string range $PRIV(appname) 5 end]
  797. set PRIV(deadapp) 0
  798. Prompt "\n\"$PRIV(app)\" alive\n" [CmdGet $PRIV(console)]
  799. }
  800. }
  801. set code [catch {send -displayof $PRIV(displayWin) $PRIV(app) $cmd} result]
  802. if {$code && [lsearch -exact [winfo interps] $PRIV(app)]<0} {
  803. ## Interpreter disappeared
  804. if {[string compare leave $OPT(dead)] && \
  805. ([string match ignore $OPT(dead)] || \
  806. [tk_messageBox -title "Dead Attachment" -type yesno \
  807. -icon info -message \
  808. "\"$PRIV(app)\" appears to have died.\
  809. \nReturn to primary slave interpreter?"]=="no")} {
  810. set PRIV(appname) "DEAD:$PRIV(appname)"
  811. set PRIV(deadapp) 1
  812. } else {
  813. set err "Attached Tk interpreter \"$PRIV(app)\" died."
  814. Attach {}
  815. set PRIV(deadapp) 0
  816. EvalSlave set errorInfo $err
  817. }
  818. Prompt \n [CmdGet $PRIV(console)]
  819. }
  820. return -code $code $result
  821. }
  822. ## ::tkcon::EvalSocket - sends the string to an interpreter attached via
  823. ## a tcp/ip socket
  824. ##
  825. ## In the EvalSocket case, ::tkcon::PRIV(app) is the socket id
  826. ##
  827. ## Must determine whether socket is dead when an error is received
  828. # ARGS: cmd - the data string to send across
  829. # Returns: the result of the command
  830. ##
  831. proc ::tkcon::EvalSocket cmd {
  832. variable OPT
  833. variable PRIV
  834. global tcl_version
  835. if {$PRIV(deadapp)} {
  836. if {![info exists PRIV(app)] || \
  837. [catch {eof $PRIV(app)} eof] || $eof} {
  838. return
  839. } else {
  840. set PRIV(appname) [string range $PRIV(appname) 5 end]
  841. set PRIV(deadapp) 0
  842. Prompt "\n\"$PRIV(app)\" alive\n" [CmdGet $PRIV(console)]
  843. }
  844. }
  845. # Sockets get \'s interpreted, so that users can
  846. # send things like \n\r or explicit hex values
  847. set cmd [subst -novariables -nocommands $cmd]
  848. #puts [list $PRIV(app) $cmd]
  849. set code [catch {puts $PRIV(app) $cmd ; flush $PRIV(app)} result]
  850. if {$code && [eof $PRIV(app)]} {
  851. ## Interpreter died or disappeared
  852. puts "$code eof [eof $PRIV(app)]"
  853. EvalSocketClosed
  854. }
  855. return -code $code $result
  856. }
  857. ## ::tkcon::EvalSocketEvent - fileevent command for an interpreter attached
  858. ## via a tcp/ip socket
  859. ## Must determine whether socket is dead when an error is received
  860. # ARGS: args - the args to send across
  861. # Returns: the result of the command
  862. ##
  863. proc ::tkcon::EvalSocketEvent {} {
  864. variable PRIV
  865. if {[gets $PRIV(app) line] == -1} {
  866. if {[eof $PRIV(app)]} {
  867. EvalSocketClosed
  868. }
  869. return
  870. }
  871. puts $line
  872. }
  873. ## ::tkcon::EvalSocketClosed - takes care of handling a closed eval socket
  874. ##
  875. # ARGS: args - the args to send across
  876. # Returns: the result of the command
  877. ##
  878. proc ::tkcon::EvalSocketClosed {} {
  879. variable OPT
  880. variable PRIV
  881. catch {close $PRIV(app)}
  882. if {[string compare leave $OPT(dead)] && \
  883. ([string match ignore $OPT(dead)] || \
  884. [tk_dialog $PRIV(base).dead "Dead Attachment" \
  885. "\"$PRIV(app)\" appears to have died.\
  886. \nReturn to primary slave interpreter?" questhead 0 OK No])} {
  887. set PRIV(appname) "DEAD:$PRIV(appname)"
  888. set PRIV(deadapp) 1
  889. } else {
  890. set err "Attached Tk interpreter \"$PRIV(app)\" died."
  891. Attach {}
  892. set PRIV(deadapp) 0
  893. EvalSlave set errorInfo $err
  894. }
  895. Prompt \n [CmdGet $PRIV(console)]
  896. }
  897. ## ::tkcon::EvalNamespace - evaluates the args in a particular namespace
  898. ## This is an override for ::tkcon::EvalAttached for when the user wants
  899. ## to attach to a particular namespace of the attached interp
  900. # ARGS: attached
  901. # namespace the namespace to evaluate in
  902. # args the args to evaluate
  903. # RETURNS: the result of the command
  904. ##
  905. proc ::tkcon::EvalNamespace { attached namespace args } {
  906. if {[llength $args]} {
  907. uplevel \#0 $attached \
  908. [list [concat [list namespace eval $namespace] $args]]
  909. }
  910. }
  911. ## ::tkcon::Namespaces - return all the namespaces descendent from $ns
  912. ##
  913. #
  914. ##
  915. proc ::tkcon::Namespaces {{ns ::} {l {}}} {
  916. if {[string compare {} $ns]} { lappend l $ns }
  917. foreach i [EvalAttached [list namespace children $ns]] {
  918. set l [Namespaces $i $l]
  919. }
  920. return $l
  921. }
  922. ## ::tkcon::CmdGet - gets the current command from the console widget
  923. # ARGS: w - console text widget
  924. # Returns: text which compromises current command line
  925. ##
  926. proc ::tkcon::CmdGet w {
  927. if {![llength [$w tag nextrange prompt limit end]]} {
  928. $w tag add stdin limit end-1c
  929. return [$w get limit end-1c]
  930. }
  931. }
  932. ## ::tkcon::CmdSep - separates multiple commands into a list and remainder
  933. # ARGS: cmd - (possible) multiple command to separate
  934. # list - varname for the list of commands that were separated.
  935. # last - varname of any remainder (like an incomplete final command).
  936. # If there is only one command, it's placed in this var.
  937. # Returns: constituent command info in varnames specified by list & rmd.
  938. ##
  939. proc ::tkcon::CmdSep {cmd list last} {
  940. upvar 1 $list cmds $last inc
  941. set inc {}
  942. set cmds {}
  943. foreach c [split [string trimleft $cmd] \n] {
  944. if {[string compare $inc {}]} {
  945. append inc \n$c
  946. } else {
  947. append inc [string trimleft $c]
  948. }
  949. if {[info complete $inc] && ![regexp {[^\\]\\$} $inc]} {
  950. if {[regexp "^\[^#\]" $inc]} {lappend cmds $inc}
  951. set inc {}
  952. }
  953. }
  954. set i [string compare $inc {}]
  955. if {!$i && [string compare $cmds {}] && ![string match *\n $cmd]} {
  956. set inc [lindex $cmds end]
  957. set cmds [lreplace $cmds end end]
  958. }
  959. return $i
  960. }
  961. ## ::tkcon::CmdSplit - splits multiple commands into a list
  962. # ARGS: cmd - (possible) multiple command to separate
  963. # Returns: constituent commands in a list
  964. ##
  965. proc ::tkcon::CmdSplit {cmd} {
  966. set inc {}
  967. set cmds {}
  968. foreach cmd [split [string trimleft $cmd] \n] {
  969. if {[string compare {} $inc]} {
  970. append inc \n$cmd
  971. } else {
  972. append inc [string trimleft $cmd]
  973. }
  974. if {[info complete $inc] && ![regexp {[^\\]\\$} $inc]} {
  975. #set inc [string trimright $inc]
  976. if {[regexp "^\[^#\]" $inc]} {lappend cmds $inc}
  977. set inc {}
  978. }
  979. }
  980. if {[regexp "^\[^#\]" $inc]} {lappend cmds $inc}
  981. return $cmds
  982. }
  983. ## ::tkcon::UniqueTag - creates a uniquely named tag, reusing names
  984. ## Called by ::tkcon::EvalCmd
  985. # ARGS: w - text widget
  986. # Outputs: tag name guaranteed unique in the widget
  987. ##
  988. proc ::tkcon::UniqueTag {w} {
  989. set tags [$w tag names]
  990. set idx 0
  991. while {[lsearch -exact $tags _tag[incr idx]] != -1} {}
  992. return _tag$idx
  993. }
  994. ## ::tkcon::ConstrainBuffer - This limits the amount of data in the text widget
  995. ## Called by ::tkcon::Prompt and in tkcon proc buffer/console switch cases
  996. # ARGS: w - console text widget
  997. # size - # of lines to constrain to
  998. # Outputs: may delete data in console widget
  999. ##
  1000. proc ::tkcon::ConstrainBuffer {w size} {
  1001. if {[$w index end] > $size} {
  1002. $w delete 1.0 [expr {int([$w index end])-$size}].0
  1003. }
  1004. }
  1005. ## ::tkcon::Prompt - displays the prompt in the console widget
  1006. # ARGS: w - console text widget
  1007. # Outputs: prompt (specified in ::tkcon::OPT(prompt1)) to console
  1008. ##
  1009. proc ::tkcon::Prompt {{pre {}} {post {}} {prompt {}}} {
  1010. variable OPT
  1011. variable PRIV
  1012. set w $PRIV(console)
  1013. if {[string compare {} $pre]} { $w insert end $pre stdout }
  1014. set i [$w index end-1c]
  1015. if {!$OPT(showstatusbar)} {
  1016. if {[string compare {} $PRIV(appname)]} {
  1017. $w insert end ">$PRIV(appname)< " prompt
  1018. }
  1019. if {[string compare :: $PRIV(namesp)]} {
  1020. $w insert end "<$PRIV(namesp)> " prompt
  1021. }
  1022. }
  1023. if {[string compare {} $prompt]} {
  1024. $w insert end $prompt prompt
  1025. } else {
  1026. $w insert end [EvalSlave subst $OPT(prompt1)] prompt
  1027. }
  1028. $w mark set output $i
  1029. $w mark set insert end
  1030. $w mark set limit insert
  1031. $w mark gravity limit left
  1032. if {[string compare {} $post]} { $w insert end $post stdin }
  1033. ConstrainBuffer $w $OPT(buffer)
  1034. set ::tkcon::PRIV(StatusCursor) [$w index insert]
  1035. $w see end
  1036. }
  1037. ## ::tkcon::About - gives about info for tkcon
  1038. ##
  1039. proc ::tkcon::About {} {
  1040. variable OPT
  1041. variable PRIV
  1042. variable COLOR
  1043. set w $PRIV(base).about
  1044. if {[winfo exists $w]} {
  1045. wm deiconify $w
  1046. } else {
  1047. global tk_patchLevel tcl_patchLevel tcl_version
  1048. toplevel $w
  1049. wm title $w "About tkcon v$PRIV(version)"
  1050. button $w.b -text Dismiss -command [list wm withdraw $w]
  1051. text $w.text -height 9 -bd 1 -width 60 \
  1052. -foreground $COLOR(stdin) \
  1053. -background $COLOR(bg) \
  1054. -font $OPT(font)
  1055. pack $w.b -fill x -side bottom
  1056. pack $w.text -fill both -side left -expand 1
  1057. $w.text tag config center -justify center
  1058. $w.text tag config title -justify center -font {Courier -18 bold}
  1059. # strip down the RCS info displayed in the about box
  1060. regexp {,v ([0-9\./: ]*)} $PRIV(RCS) -> RCS
  1061. $w.text insert 1.0 "About tkcon v$PRIV(version)" title \
  1062. "\n\nCopyright 1995-2002 Jeffrey Hobbs, $PRIV(email)\
  1063. \nRelease Info: v$PRIV(version), CVS v$RCS\
  1064. \nDocumentation available at:\n$PRIV(docs)\
  1065. \nUsing: Tcl v$tcl_patchLevel / Tk v$tk_patchLevel" center
  1066. $w.text config -state disabled
  1067. }
  1068. }
  1069. ## ::tkcon::InitMenus - inits the menubar and popup for the console
  1070. # ARGS: w - console text widget
  1071. ##
  1072. proc ::tkcon::InitMenus {w title} {
  1073. variable OPT
  1074. variable PRIV
  1075. variable COLOR
  1076. global tcl_platform
  1077. if {[catch {menu $w.pop -tearoff 0}]} {
  1078. label $w.label -text "Menus not available in plugin mode"
  1079. pack $w.label
  1080. return
  1081. }
  1082. menu $w.context -tearoff 0 -disabledforeground $COLOR(disabled)
  1083. set PRIV(context) $w.context
  1084. set PRIV(popup) $w.pop
  1085. proc MenuButton {w m l} {
  1086. $w add cascade -label $m -underline 0 -menu $w.$l
  1087. return $w.$l
  1088. }
  1089. foreach m [list File Console Edit Interp Prefs History Help] {
  1090. set l [string tolower $m]
  1091. MenuButton $w $m $l
  1092. $w.pop add cascade -label $m -underline 0 -menu $w.pop.$l
  1093. }
  1094. ## File Menu
  1095. ##
  1096. foreach m [list [menu $w.file -disabledforeground $COLOR(disabled)] \
  1097. [menu $w.pop.file -disabledforeground $COLOR(disabled)]] {
  1098. $m add command -label "Load File" -underline 0 -command ::tkcon::Load
  1099. $m add cascade -label "Save ..." -underline 0 -menu $m.save
  1100. $m add separator
  1101. $m add command -label "Quit" -underline 0 -accel Ctrl-q -command exit
  1102. ## Save Menu
  1103. ##
  1104. set s $m.save
  1105. menu $s -disabledforeground $COLOR(disabled) -tearoff 0
  1106. $s add command -label "All" -underline 0 \
  1107. -command {::tkcon::Save {} all}
  1108. $s add command -label "History" -underline 0 \
  1109. -command {::tkcon::Save {} history}
  1110. $s add command -label "Stdin" -underline 3 \
  1111. -command {::tkcon::Save {} stdin}
  1112. $s add command -label "Stdout" -underline 3 \
  1113. -command {::tkcon::Save {} stdout}
  1114. $s add command -label "Stderr" -underline 3 \
  1115. -command {::tkcon::Save {} stderr}
  1116. }
  1117. ## Console Menu
  1118. ##
  1119. foreach m [list [menu $w.console -disabledfore $COLOR(disabled)] \
  1120. [menu $w.pop.console -disabledfore $COLOR(disabled)]] {
  1121. $m add command -label "$title Console" -state disabled
  1122. $m add command -label "New Console" -underline 0 -accel Ctrl-N \
  1123. -command ::tkcon::New
  1124. $m add command -label "Close Console" -underline 0 -accel Ctrl-w \
  1125. -command ::tkcon::Destroy
  1126. $m add command -label "Clear Console" -underline 1 -accel Ctrl-l \
  1127. -command { clear; ::tkcon::Prompt }
  1128. if {[string match unix $tcl_platform(platform)]} {
  1129. $m add separator
  1130. $m add command -label "Make Xauth Secure" -und 5 \
  1131. -command ::tkcon::XauthSecure
  1132. }
  1133. $m add separator
  1134. $m add cascade -label "Attach To ..." -underline 0 -menu $m.attach
  1135. ## Attach Console Menu
  1136. ##
  1137. set sub [menu $m.attach -disabledforeground $COLOR(disabled)]
  1138. $sub add cascade -label "Interpreter" -underline 0 -menu $sub.apps
  1139. $sub add cascade -label "Namespace" -underline 1 -menu $sub.name
  1140. $sub add cascade -label "Socket" -underline 1 -menu $sub.sock \
  1141. -state [expr {([info tclversion] < 8.3)?"disabled":"normal"}]
  1142. ## Attach Console Menu
  1143. ##
  1144. menu $sub.apps -disabledforeground $COLOR(disabled) \
  1145. -postcommand [list ::tkcon::AttachMenu $sub.apps]
  1146. ## Attach Namespace Menu
  1147. ##
  1148. menu $sub.name -disabledforeground $COLOR(disabled) -tearoff 0 \
  1149. -postcommand [list ::tkcon::NamespaceMenu $sub.name]
  1150. if {$::tcl_version >= 8.3} {
  1151. # This uses [file channels] to create the menu, so we only
  1152. # want it for newer versions of Tcl.
  1153. ## Attach Socket Menu
  1154. ##
  1155. menu $sub.sock -disabledforeground $COLOR(disabled) -tearoff 0 \
  1156. -postcommand [list ::tkcon::SocketMenu $sub.sock]
  1157. }
  1158. ## Attach Display Menu
  1159. ##
  1160. if {![string compare "unix" $tcl_platform(platform)]} {
  1161. $sub add cascade -label "Display" -und 1 -menu $sub.disp
  1162. menu $sub.disp -disabledforeground $COLOR(disabled) \
  1163. -tearoff 0 \
  1164. -postcommand [list ::tkcon::DisplayMenu $sub.disp]
  1165. }
  1166. }
  1167. ## Edit Menu
  1168. ##
  1169. set text $PRIV(console)
  1170. foreach m [list [menu $w.edit] [menu $w.pop.edit]] {
  1171. $m add command -label "Cut" -underline 2 -accel Ctrl-x \
  1172. -command [list ::tkcon::Cut $text]
  1173. $m add command -label "Copy" -underline 0 -accel Ctrl-c \
  1174. -command [list ::tkcon::Copy $text]
  1175. $m add command -label "Paste" -underline 0 -accel Ctrl-v \
  1176. -command [list ::tkcon::Paste $text]
  1177. $m add separator
  1178. $m add command -label "Find" -underline 0 -accel Ctrl-F \
  1179. -command [list ::tkcon::FindBox $text]
  1180. }
  1181. ## Interp Menu
  1182. ##
  1183. foreach m [list $w.interp $w.pop.interp] {
  1184. menu $m -disabledforeground $COLOR(disabled) \
  1185. -postcommand [list ::tkcon::InterpMenu $m]
  1186. }
  1187. ## Prefs Menu
  1188. ##
  1189. foreach m [list [menu $w.prefs] [menu $w.pop.prefs]] {
  1190. $m add check -label "Brace Highlighting" \
  1191. -underline 0 -variable ::tkcon::OPT(lightbrace)
  1192. $m add check -label "Command Highlighting" \
  1193. -underline 0 -variable ::tkcon::OPT(lightcmd)
  1194. $m add check -label "History Substitution" \
  1195. -underline 0 -variable ::tkcon::OPT(subhistory)
  1196. $m add check -label "Hot Errors" \
  1197. -underline 0 -variable ::tkcon::OPT(hoterrors)
  1198. $m add check -label "Non-Tcl Attachments" \
  1199. -underline 0 -variable ::tkcon::OPT(nontcl)
  1200. $m add check -label "Calculator Mode" \
  1201. -underline 1 -variable ::tkcon::OPT(calcmode)
  1202. $m add check -label "Show Multiple Matches" \
  1203. -underline 0 -variable ::tkcon::OPT(showmultiple)
  1204. $m add check -label "Show Menubar" \
  1205. -underline 5 -variable ::tkcon::OPT(showmenu) \
  1206. -command {$::tkcon::PRIV(root) configure -menu [expr \
  1207. {$::tkcon::OPT(showmenu) ? $::tkcon::PRIV(menubar) : {}}]}
  1208. $m add check -label "Show Statusbar" \
  1209. -underline 5 -variable ::tkcon::OPT(showstatusbar) \
  1210. -command {
  1211. if {$::tkcon::OPT(showstatusbar)} {
  1212. pack $::tkcon::PRIV(statusbar) -side bottom -fill x \
  1213. -before $::tkcon::PRIV(scrolly)
  1214. } else { pack forget $::tkcon::PRIV(statusbar) }
  1215. }
  1216. $m add cascade -label "Scrollbar" -underline 2 -menu $m.scroll
  1217. ## Scrollbar Menu
  1218. ##
  1219. set m [menu $m.scroll -tearoff 0]
  1220. $m add radio -label "Left" -value left \
  1221. -variable ::tkcon::OPT(scrollypos) \
  1222. -command { pack config $::tkcon::PRIV(scrolly) -side left }
  1223. $m add radio -label "Right" -value right \
  1224. -variable ::tkcon::OPT(scrollypos) \
  1225. -command { pack config $::tkcon::PRIV(scrolly) -side right }
  1226. }
  1227. ## History Menu
  1228. ##
  1229. foreach m [list $w.history $w.pop.history] {
  1230. menu $m -disabledforeground $COLOR(disabled) \
  1231. -postcommand [list ::tkcon::HistoryMenu $m]
  1232. }
  1233. ## Help Menu
  1234. ##
  1235. foreach m [list [menu $w.help] [menu $w.pop.help]] {
  1236. $m add command -label "About " -underline 0 -accel Ctrl-A \
  1237. -command ::tkcon::About
  1238. $m add command -label "Retrieve Latest Version" -underline 0 \
  1239. -command ::tkcon::Retrieve
  1240. }
  1241. }
  1242. ## ::tkcon::HistoryMenu - dynamically build the menu for attached interpreters
  1243. ##
  1244. # ARGS: m - menu widget
  1245. ##
  1246. proc ::tkcon::HistoryMenu m {
  1247. variable PRIV
  1248. if {![winfo exists $m]} return
  1249. set id [EvalSlave history nextid]
  1250. if {$PRIV(histid)==$id} return
  1251. set PRIV(histid) $id
  1252. $m delete 0 end
  1253. while {($id>1) && ($id>$PRIV(histid)-10) && \
  1254. ![catch {EvalSlave history event [incr id -1]} tmp]} {
  1255. set lbl $tmp
  1256. if {[string len $lbl]>32} { set lbl [string range $tmp 0 28]... }
  1257. $m add command -label "$id: $lbl" -command "
  1258. $::tkcon::PRIV(console) delete limit end
  1259. $::tkcon::PRIV(console) insert limit [list $tmp]
  1260. $::tkcon::PRIV(console) see end
  1261. ::tkcon::Eval $::tkcon::PRIV(console)"
  1262. }
  1263. }
  1264. ## ::tkcon::InterpMenu - dynamically build the menu for attached interpreters
  1265. ##
  1266. # ARGS: w - menu widget
  1267. ##
  1268. proc ::tkcon::InterpMenu w {
  1269. variable OPT
  1270. variable PRIV
  1271. variable COLOR
  1272. if {![winfo exists $w]} return
  1273. $w delete 0 end
  1274. foreach {app type} [Attach] break
  1275. $w add command -label "[string toupper $type]: $app" -state disabled
  1276. if {($OPT(nontcl) && [string match interp $type]) || $PRIV(deadapp)} {
  1277. $w add separator
  1278. $w add command -state disabled -label "Communication disabled to"
  1279. $w add command -state disabled -label "dead or non-Tcl interps"
  1280. return
  1281. }
  1282. ## Show Last Error
  1283. ##
  1284. $w add separator
  1285. $w add command -label "Show Last Error" \
  1286. -command [list tkcon error $app $type]
  1287. ## Packages Cascaded Menu
  1288. ##
  1289. $w add separator
  1290. $w add cascade -label Packages -underline 0 -menu $w.pkg
  1291. set m $w.pkg
  1292. if {![winfo exists $m]} {
  1293. menu $m -tearoff no -disabledforeground $COLOR(disabled) \
  1294. -postcommand [list ::tkcon::PkgMenu $m $app $type]
  1295. }
  1296. ## State Checkpoint/Revert
  1297. ##
  1298. $w add separator
  1299. $w add command -label "Checkpoint State" \
  1300. -command [list ::tkcon::StateCheckpoint $app $type]
  1301. $w add command -label "Revert State" \
  1302. -command [list ::tkcon::StateRevert $app $type]
  1303. $w add command -label "View State Change" \
  1304. -command [list ::tkcon::StateCompare $app $type]
  1305. ## Init Interp
  1306. ##
  1307. $w add separator
  1308. $w add command -label "Send tkcon Commands" \
  1309. -command [list ::tkcon::InitInterp $app $type]
  1310. }
  1311. ## ::tkcon::PkgMenu - fill in in the applications sub-menu
  1312. ## with a list of all the applications that currently exist.
  1313. ##
  1314. proc ::tkcon::PkgMenu {m app type} {
  1315. # just in case stuff has been added to the auto_path
  1316. # we have to make sure that the errorInfo doesn't get screwed up
  1317. EvalAttached {
  1318. set __tkcon_error $errorInfo
  1319. catch {package require bogus-package-name}
  1320. set errorInfo ${__tkcon_error}
  1321. unset __tkcon_error
  1322. }
  1323. $m delete 0 end
  1324. foreach pkg [EvalAttached [list info loaded {}]] {
  1325. set loaded([lindex $pkg 1]) [package provide $pkg]
  1326. }
  1327. foreach pkg [lremove [EvalAttached {package names}] Tcl] {
  1328. set version [EvalAttached [list package provide $pkg]]
  1329. if {[string compare {} $version]} {
  1330. set loaded($pkg) $version
  1331. } elseif {![info exists loaded($pkg)]} {
  1332. set loadable($pkg) [list package require $pkg]
  1333. }
  1334. }
  1335. foreach pkg [EvalAttached {info loaded}] {
  1336. set pkg [lindex $pkg 1]
  1337. if {![info exists loaded($pkg)] && ![info exists loadable($pkg)]} {
  1338. set loadable($pkg) [list load {} $pkg]
  1339. }
  1340. }
  1341. set npkg 0
  1342. foreach pkg [lsort -dictionary [array names loadable]] {
  1343. foreach v [EvalAttached [list package version $pkg]] {
  1344. set brkcol [expr {([incr npkg]%23)==0}]
  1345. $m add command -label "Load $pkg ($v)" -command \
  1346. "::tkcon::EvalOther [list $app] $type $loadable($pkg) $v" \
  1347. -columnbreak $brkcol
  1348. }
  1349. }
  1350. if {[info exists loaded] && [info exists loadable]} {
  1351. $m add separator
  1352. }
  1353. foreach pkg [lsort -dictionary [array names loaded]] {
  1354. set brkcol [expr {([incr npkg]%23)==0}]
  1355. $m add command -label "${pkg}$loaded($pkg) Loaded" -state disabled \
  1356. -columnbreak $brkcol
  1357. }
  1358. }
  1359. ## ::tkcon::AttachMenu - fill in in the applications sub-menu
  1360. ## with a list of all the applications that currently exist.
  1361. ##
  1362. proc ::tkcon::AttachMenu m {
  1363. variable OPT
  1364. variable PRIV
  1365. array set interps [set tmp [Interps]]
  1366. foreach {i j} $tmp { set tknames($j) {} }
  1367. $m delete 0 end
  1368. set cmd {::tkcon::Prompt \n [::tkcon::CmdGet $::tkcon::PRIV(console)]}
  1369. $m add radio -label {None (use local slave) } -accel Ctrl-1 \
  1370. -variable ::tkcon::PRIV(app) \
  1371. -value [concat $::tkcon::PRIV(name) $::tkcon::OPT(exec)] \
  1372. -command "::tkcon::Attach {}; $cmd"
  1373. $m add separator
  1374. $m add command -label "Foreign Tk Interpreters" -state disabled
  1375. foreach i [lsort [lremove [winfo interps] [array names tknames]]] {
  1376. $m add radio -label $i -variable ::tkcon::PRIV(app) -value $i \
  1377. -command "::tkcon::Attach [list $i] interp; $cmd"
  1378. }
  1379. $m add separator
  1380. $m add command -label "tkcon Interpreters" -state disabled
  1381. foreach i [lsort [array names interps]] {
  1382. if {[string match {} $interps($i)]} { set interps($i) "no Tk" }
  1383. if {[regexp {^Slave[0-9]+} $i]} {
  1384. set opts [list -label "$i ($interps($i))" \
  1385. -variable ::tkcon::PRIV(app) -value $i \
  1386. -command "::tkcon::Attach [list $i] slave; $cmd"]
  1387. if {[string match $PRIV(name) $i]} {
  1388. append opts " -accel Ctrl-2"
  1389. }
  1390. eval $m add radio $opts
  1391. } else {
  1392. set name [concat Main $i]
  1393. if {[string match Main $name]} {
  1394. $m add radio -label "$name ($interps($i))" -accel Ctrl-3 \
  1395. -variable ::tkcon::PRIV(app) -value Main \
  1396. -command "::tkcon::Attach [list $name] slave; $cmd"
  1397. } else {
  1398. $m add radio -label "$name ($interps($i))" \
  1399. -variable ::tkcon::PRIV(app) -value $i \
  1400. -command "::tkcon::Attach [list $name] slave; $cmd"
  1401. }
  1402. }
  1403. }
  1404. }
  1405. ## Displays Cascaded Menu
  1406. ##
  1407. proc ::tkcon::DisplayMenu m {
  1408. $m delete 0 end
  1409. set cmd {::tkcon::Prompt \n [::tkcon::CmdGet $::tkcon::PRIV(console)]}
  1410. $m add command -label "New Display" -command ::tkcon::NewDisplay
  1411. foreach disp [Display] {
  1412. $m add separator
  1413. $m add command -label $disp -state disabled
  1414. set res [Display $disp]
  1415. set win [lindex $res 0]
  1416. foreach i [lsort [lindex $res 1]] {
  1417. $m add radio -label $i -variable ::tkcon::PRIV(app) -value $i \
  1418. -command "::tkcon::Attach [list $i] [list dpy:$win]; $cmd"
  1419. }
  1420. }
  1421. }
  1422. ## Sockets Cascaded Menu
  1423. ##
  1424. proc ::tkcon::SocketMenu m {
  1425. $m delete 0 end
  1426. set cmd {::tkcon::Prompt \n [::tkcon::CmdGet $::tkcon::PRIV(console)]}
  1427. $m add command -label "Create Connection" \
  1428. -command "::tkcon::NewSocket; $cmd"
  1429. foreach sock [file channels sock*] {
  1430. $m add radio -label $sock -variable ::tkcon::PRIV(app) -value $sock \
  1431. -command "::tkcon::Attach $sock socket; $cmd"
  1432. }
  1433. }
  1434. ## Namepaces Cascaded Menu
  1435. ##
  1436. proc ::tkcon::NamespaceMenu m {
  1437. variable PRIV
  1438. variable OPT
  1439. $m delete 0 end
  1440. if {($PRIV(deadapp) || [string match socket $PRIV(apptype)] || \
  1441. ($OPT(nontcl) && [string match interp $PRIV(apptype)]))} {
  1442. $m add command -label "No Namespaces" -state disabled
  1443. return
  1444. }
  1445. ## Same command as for ::tkcon::AttachMenu items
  1446. set cmd {::tkcon::Prompt \n [::tkcon::CmdGet $::tkcon::PRIV(console)]}
  1447. set names [lsort [Namespaces ::]]
  1448. if {[llength $names] > $OPT(maxmenu)} {
  1449. $m add command -label "Attached to $PRIV(namesp)" -state disabled
  1450. $m add command -label "List Namespaces" \
  1451. -command [list ::tkcon::NamespacesList $names]
  1452. } else {
  1453. foreach i $names {
  1454. if {[string match :: $i]} {
  1455. $m add radio -label "Main" -value $i \
  1456. -variable ::tkcon::PRIV(namesp) \
  1457. -command "::tkcon::AttachNamespace [list $i]; $cmd"
  1458. } else {
  1459. $m add radio -label $i -value $i \
  1460. -variable ::tkcon::PRIV(namesp) \
  1461. -command "::tkcon::AttachNamespace [list $i]; $cmd"
  1462. }
  1463. }
  1464. }
  1465. }
  1466. ## Namepaces List
  1467. ##
  1468. proc ::tkcon::NamespacesList {names} {
  1469. variable PRIV
  1470. set f $PRIV(base).namespaces
  1471. catch {destroy $f}
  1472. toplevel $f
  1473. listbox $f.names -width 30 -height 15 -selectmode single \
  1474. -yscrollcommand [list $f.scrollv set] \
  1475. -xscrollcommand [list $f.scrollh set]
  1476. scrollbar $f.scrollv -command [list $f.names yview]
  1477. scrollbar $f.scrollh -command [list $f.names xview] -orient horizontal
  1478. frame $f.buttons
  1479. button $f.cancel -text "Cancel" -command [list destroy $f]
  1480. grid $f.names $f.scrollv -sticky nesw
  1481. grid $f.scrollh -sticky ew
  1482. grid $f.buttons -sticky nesw
  1483. grid $f.cancel -in $f.buttons -pady 6
  1484. grid columnconfigure $f 0 -weight 1
  1485. grid rowconfigure $f 0 -weight 1
  1486. #fill the listbox
  1487. foreach i $names {
  1488. if {[string match :: $i]} {
  1489. $f.names insert 0 Main
  1490. } else {
  1491. $f.names insert end $i
  1492. }
  1493. }
  1494. #Bindings
  1495. bind $f.names <Double-1> {
  1496. ## Catch in case the namespace disappeared on us
  1497. catch { ::tkcon::AttachNamespace [%W get [%W nearest %y]] }
  1498. ::tkcon::Prompt "\n" [::tkcon::CmdGet $::tkcon::PRIV(console)]
  1499. destroy [winfo toplevel %W]
  1500. }
  1501. }
  1502. # ::tkcon::XauthSecure --
  1503. #
  1504. # This removes all the names in the xhost list, and secures
  1505. # the display for Tk send commands. Of course, this prevents
  1506. # what might have been otherwise allowable X connections
  1507. #
  1508. # Arguments:
  1509. # none
  1510. # Results:
  1511. # Returns nothing
  1512. #
  1513. proc ::tkcon::XauthSecure {} {
  1514. global tcl_platform
  1515. if {[string compare unix $tcl_platform(platform)]} {
  1516. # This makes no sense outside of Unix
  1517. return
  1518. }
  1519. set hosts [exec xhost]
  1520. # the first line is info only
  1521. foreach host [lrange [split $hosts \n] 1 end] {
  1522. exec xhost -$host
  1523. }
  1524. exec xhost -
  1525. tk_messageBox -title "Xhost secured" -message "Xhost secured" -icon info
  1526. }
  1527. ## ::tkcon::FindBox - creates minimal dialog interface to ::tkcon::Find
  1528. # ARGS: w - text widget
  1529. # str - optional seed string for ::tkcon::PRIV(find)
  1530. ##
  1531. proc ::tkcon::FindBox {w {str {}}} {
  1532. variable PRIV
  1533. set base $PRIV(base).find
  1534. if {![winfo exists $base]} {
  1535. toplevel $base
  1536. wm withdraw $base
  1537. wm title $base "tkcon Find"
  1538. pack [frame $base.f] -fill x -expand 1
  1539. label $base.f.l -text "Find:"
  1540. entry $base.f.e -textvariable ::tkcon::PRIV(find)
  1541. pack [frame $base.opt] -fill x
  1542. checkbutton $base.opt.c -text "Case Sensitive" \
  1543. -variable ::tkcon::PRIV(find,case)
  1544. checkbutton $base.opt.r -text "Use Regexp" -variable ::tkcon::PRIV(find,reg)
  1545. pack $base.f.l -side left
  1546. pack $base.f.e $base.opt.c $base.opt.r -side left -fill both -expand 1
  1547. pack [frame $base.sep -bd 2 -relief sunken -height 4] -fill x
  1548. pack [frame $base.btn] -fill both
  1549. button $base.btn.fnd -text "Find" -width 6
  1550. button $base.btn.clr -text "Clear" -width 6
  1551. button $base.btn.dis -text "Dismiss" -width 6
  1552. eval pack [winfo children $base.btn] -padx 4 -pady 2 \
  1553. -side left -fill both
  1554. focus $base.f.e
  1555. bind $base.f.e <Return> [list $base.btn.fnd invoke]
  1556. bind $base.f.e <Escape> [list $base.btn.dis invoke]
  1557. }
  1558. $base.btn.fnd config -command "::tkcon::Find [list $w] \$::tkcon::PRIV(find) \
  1559. -case \$::tkcon::PRIV(find,case) -reg \$::tkcon::PRIV(find,reg)"
  1560. $base.btn.clr config -command "
  1561. [list $w] tag remove find 1.0 end
  1562. set ::tkcon::PRIV(find) {}
  1563. "
  1564. $base.btn.dis config -command "
  1565. [list $w] tag remove find 1.0 end
  1566. wm withdraw [list $base]
  1567. "
  1568. if {[string compare {} $str]} {
  1569. set PRIV(find) $str
  1570. $base.btn.fnd invoke
  1571. }
  1572. if {[string compare normal [wm state $base]]} {
  1573. wm deiconify $base
  1574. } else { raise $base }
  1575. $base.f.e select range 0 end
  1576. }
  1577. ## ::tkcon::Find - searches in text widget $w for $str and highlights it
  1578. ## If $str is empty, it just deletes any highlighting
  1579. # ARGS: w - text widget
  1580. # str - string to search for
  1581. # -case TCL_BOOLEAN whether to be case sensitive DEFAULT: 0
  1582. # -regexp TCL_BOOLEAN whether to use $str as pattern DEFAULT: 0
  1583. ##
  1584. proc ::tkcon::Find {w str args} {
  1585. $w tag remove find 1.0 end
  1586. set truth {^(1|yes|true|on)$}
  1587. set opts {}
  1588. foreach {key val} $args {
  1589. switch -glob -- $key {
  1590. -c* { if {[regexp -nocase $truth $val]} { set case 1 } }
  1591. -r* { if {[regexp -nocase $truth $val]} { lappend opts -regexp } }
  1592. default { return -code error "Unknown option $key" }
  1593. }
  1594. }
  1595. if {![info exists case]} { lappend opts -nocase }
  1596. if {[string match {} $str]} return
  1597. $w mark set findmark 1.0
  1598. while {[string compare {} [set ix [eval $w search $opts -count numc -- \
  1599. [list $str] findmark end]]]} {
  1600. $w tag add find $ix ${ix}+${numc}c
  1601. $w mark set findmark ${ix}+1c
  1602. }
  1603. $w tag configure find -background $::tkcon::COLOR(blink)
  1604. catch {$w see find.first}
  1605. return [expr {[llength [$w tag ranges find]]/2}]
  1606. }
  1607. ## ::tkcon::Attach - called to attach tkcon to an interpreter
  1608. # ARGS: name - application name to which tkcon sends commands
  1609. # This is either a slave interperter name or tk appname.
  1610. # type - (slave|interp) type of interpreter we're attaching to
  1611. # slave means it's a tkcon interpreter
  1612. # interp means we'll need to 'send' to it.
  1613. # Results: ::tkcon::EvalAttached is recreated to evaluate in the
  1614. # appropriate interpreter
  1615. ##
  1616. proc ::tkcon::Attach {{name <NONE>} {type slave}} {
  1617. variable PRIV
  1618. variable OPT
  1619. if {[llength [info level 0]] == 1} {
  1620. # no args were specified, return the attach info instead
  1621. if {[string match {} $PRIV(appname)]} {
  1622. return [list [concat $PRIV(name) $OPT(exec)] $PRIV(apptype)]
  1623. } else {
  1624. return [list $PRIV(appname) $PRIV(apptype)]
  1625. }
  1626. }
  1627. set path [concat $PRIV(name) $OPT(exec)]
  1628. set PRIV(displayWin) .
  1629. if {[string match namespace $type]} {
  1630. return [uplevel 1 ::tkcon::AttachNamespace $name]
  1631. } elseif {[string match dpy:* $type]} {
  1632. set PRIV(displayWin) [string range $type 4 end]
  1633. } elseif {[string match sock* $type]} {
  1634. global tcl_version
  1635. if {[catch {eof $name} res]} {
  1636. return -code error "No known channel \"$name\""
  1637. } elseif {$res} {
  1638. catch {close $name}
  1639. return -code error "Channel \"$name\" returned EOF"
  1640. }
  1641. set app $name
  1642. set type socket
  1643. } elseif {[string compare {} $name]} {
  1644. array set interps [Interps]
  1645. if {[string match {[Mm]ain} [lindex $name 0]]} {
  1646. set name [lrange $name 1 end]
  1647. }
  1648. if {[string match $path $name]} {
  1649. set name {}
  1650. set app $path
  1651. set type slave
  1652. } elseif {[info exists interps($name)]} {
  1653. if {[string match {} $name]} { set name Main; set app Main }
  1654. set type slave
  1655. } elseif {[interp exists $name]} {
  1656. set name [concat $PRIV(name) $name]
  1657. set type slave
  1658. } elseif {[interp exists [concat $OPT(exec) $name]]} {
  1659. set name [concat $path $name]
  1660. set type slave
  1661. } elseif {[lsearch -exact [winfo interps] $name] > -1} {
  1662. if {[EvalSlave info exists tk_library] \
  1663. && [string match $name [EvalSlave tk appname]]} {
  1664. set name {}
  1665. set app $path
  1666. set type slave
  1667. } elseif {[set i [lsearch -exact \
  1668. [Main set ::tkcon::PRIV(interps)] $name]] != -1} {
  1669. set name [lindex [Main set ::tkcon::PRIV(slaves)] $i]
  1670. if {[string match {[Mm]ain} $name]} { set app Main }
  1671. set type slave
  1672. } else {
  1673. set type interp
  1674. }
  1675. } else {
  1676. return -code error "No known interpreter \"$name\""
  1677. }
  1678. } else {
  1679. set app $path
  1680. }
  1681. if {![info exists app]} { set app $name }
  1682. array set PRIV [list app $app appname $name apptype $type deadapp 0]
  1683. ## ::tkcon::EvalAttached - evaluates the args in the attached interp
  1684. ## args should be passed to this procedure as if they were being
  1685. ## passed to the 'eval' procedure. This procedure is dynamic to
  1686. ## ensure evaluation occurs in the right interp.
  1687. # ARGS: args - the command and args to evaluate
  1688. ##
  1689. switch -glob -- $type {
  1690. slave {
  1691. if {[string match {} $name]} {
  1692. interp alias {} ::tkcon::EvalAttached {} \
  1693. ::tkcon::EvalSlave uplevel \#0
  1694. } elseif {[string match Main $PRIV(app)]} {
  1695. interp alias {} ::tkcon::EvalAttached {} ::tkcon::Main
  1696. } elseif {[string match $PRIV(name) $PRIV(app)]} {
  1697. interp alias {} ::tkcon::EvalAttached {} uplevel \#0
  1698. } else {
  1699. interp alias {} ::tkcon::EvalAttached {} \
  1700. ::tkcon::Slave $::tkcon::PRIV(app)
  1701. }
  1702. }
  1703. sock* {
  1704. interp alias {} ::tkcon::EvalAttached {} \
  1705. ::tkcon::EvalSlave uplevel \#0
  1706. # The file event will just puts whatever data is found
  1707. # into the interpreter
  1708. fconfigure $name -buffering line -blocking 0
  1709. fileevent $name readable ::tkcon::EvalSocketEvent
  1710. }
  1711. dpy:* -
  1712. interp {
  1713. if {$OPT(nontcl)} {
  1714. interp alias {} ::tkcon::EvalAttached {} ::tkcon::EvalSlave
  1715. set PRIV(namesp) ::
  1716. } else {
  1717. interp alias {} ::tkcon::EvalAttached {} ::tkcon::EvalSend
  1718. }
  1719. }
  1720. default {
  1721. return -code error "[lindex [info level 0] 0] did not specify\
  1722. a valid type: must be slave or interp"
  1723. }
  1724. }
  1725. if {[string match slave $type] || \
  1726. (!$OPT(nontcl) && [regexp {^(interp|dpy)} $type])} {
  1727. set PRIV(namesp) ::
  1728. }
  1729. set PRIV(StatusAttach) "$PRIV(app) ($PRIV(apptype))"
  1730. return
  1731. }
  1732. ## ::tkcon::AttachNamespace - called to attach tkcon to a namespace
  1733. # ARGS: name - namespace name in which tkcon should eval commands
  1734. # Results: ::tkcon::EvalAttached will be modified
  1735. ##
  1736. proc ::tkcon::AttachNamespace { name } {
  1737. variable PRIV
  1738. variable OPT
  1739. if {($OPT(nontcl) && [string match interp $PRIV(apptype)]) \
  1740. || [string match socket $PRIV(apptype)] \
  1741. || $PRIV(deadapp)} {
  1742. return -code error "can't attach to namespace in attached environment"
  1743. }
  1744. if {[string match Main $name]} {set name ::}
  1745. if {[string compare {} $name] && \
  1746. [lsearch [Namespaces ::] $name] == -1} {
  1747. return -code error "No known namespace \"$name\""
  1748. }
  1749. if {[regexp {^(|::)$} $name]} {
  1750. ## If name=={} || ::, we want the primary namespace
  1751. set alias [interp alias {} ::tkcon::EvalAttached]
  1752. if {[string match ::tkcon::EvalNamespace* $alias]} {
  1753. eval [list interp alias {} ::tkcon::EvalAttached {}] \
  1754. [lindex $alias 1]
  1755. }
  1756. set name ::
  1757. } else {
  1758. interp alias {} ::tkcon::EvalAttached {} ::tkcon::EvalNamespace \
  1759. [interp alias {} ::tkcon::EvalAttached] [list $name]
  1760. }
  1761. set PRIV(namesp) $name
  1762. set PRIV(StatusAttach) "$PRIV(app) $PRIV(namesp) ($PRIV(apptype))"
  1763. }
  1764. ## ::tkcon::NewSocket - called to create a socket to connect to
  1765. # ARGS: none
  1766. # Results: It will create a socket, and attach if requested
  1767. ##
  1768. proc ::tkcon::NewSocket {} {
  1769. variable PRIV
  1770. set t $PRIV(base).newsock
  1771. if {![winfo exists $t]} {
  1772. toplevel $t
  1773. wm withdraw $t
  1774. wm title $t "tkcon Create Socket"
  1775. label $t.lhost -text "Host: "
  1776. entry $t.host -width 20
  1777. label $t.lport -text "Port: "
  1778. entry $t.port -width 4
  1779. button $t.ok -text "OK" -command {set ::tkcon::PRIV(grab) 1}
  1780. bind $t.host <Return> [list focus $t.port]
  1781. bind $t.port <Return> [list focus $t.ok]
  1782. bind $t.ok <Return> [list $t.ok invoke]
  1783. grid $t.lhost $t.host $t.lport $t.port -sticky ew
  1784. grid $t.ok - - - -sticky ew
  1785. grid columnconfig $t 1 -weight 1
  1786. grid rowconfigure $t 1 -weight 1
  1787. wm transient $t $PRIV(root)
  1788. wm geometry $t +[expr {([winfo screenwidth $t]-[winfo \
  1789. reqwidth $t]) / 2}]+[expr {([winfo \
  1790. screenheight $t]-[winfo reqheight $t]) / 2}]
  1791. }
  1792. #$t.host delete 0 end
  1793. #$t.port delete 0 end
  1794. wm deiconify $t
  1795. raise $t
  1796. grab $t
  1797. focus $t.host
  1798. vwait ::tkcon::PRIV(grab)
  1799. grab release $t
  1800. wm withdraw $t
  1801. set host [$t.host get]
  1802. set port [$t.port get]
  1803. if {$host == ""} { return }
  1804. if {[catch {
  1805. set sock [socket $host $port]
  1806. } err]} {
  1807. tk_messageBox -title "Socket Connection Error" \
  1808. -message "Unable to connect to \"$host:$port\":\n$err" \
  1809. -icon error -type ok
  1810. } else {
  1811. Attach $sock socket
  1812. }
  1813. }
  1814. ## ::tkcon::Load - sources a file into the console
  1815. ## The file is actually sourced in the currently attached's interp
  1816. # ARGS: fn - (optional) filename to source in
  1817. # Returns: selected filename ({} if nothing was selected)
  1818. ##
  1819. proc ::tkcon::Load { {fn ""} } {
  1820. set types {
  1821. {{Tcl Files} {.tcl .tk}}
  1822. {{Text Files} {.txt}}
  1823. {{All Files} *}
  1824. }
  1825. if {
  1826. [string match {} $fn] &&
  1827. ([catch {tk_getOpenFile -filetypes $types \
  1828. -title "Source File"} fn] || [string match {} $fn])
  1829. } { return }
  1830. EvalAttached [list source $fn]
  1831. }
  1832. ## ::tkcon::Save - saves the console or other widget buffer to a file
  1833. ## This does not eval in a slave because it's not necessary
  1834. # ARGS: w - console text widget
  1835. # fn - (optional) filename to save to
  1836. ##
  1837. proc ::tkcon::Save { {fn ""} {type ""} {opt ""} {mode w} } {
  1838. variable PRIV
  1839. if {![regexp -nocase {^(all|history|stdin|stdout|stderr|widget)$} $type]} {
  1840. array set s { 0 All 1 History 2 Stdin 3 Stdout 4 Stderr 5 Cancel }
  1841. ## Allow user to specify what kind of stuff to save
  1842. set type [tk_dialog $PRIV(base).savetype "Save Type" \
  1843. "What part of the text do you want to save?" \
  1844. questhead 0 $s(0) $s(1) $s(2) $s(3) $s(4) $s(5)]
  1845. if {$type == 5 || $type == -1} return
  1846. set type $s($type)
  1847. }
  1848. if {[string match {} $fn]} {
  1849. set types {
  1850. {{Tcl Files} {.tcl .tk}}
  1851. {{Text Files} {.txt}}
  1852. {{All Files} *}
  1853. }
  1854. if {[catch {tk_getSaveFile -defaultextension .tcl -filetypes $types \
  1855. -title "Save $type"} fn] || [string match {} $fn]} return
  1856. }
  1857. set type [string tolower $type]
  1858. switch $type {
  1859. stdin - stdout - stderr {
  1860. set data {}
  1861. foreach {first last} [$PRIV(console) tag ranges $type] {
  1862. lappend data [$PRIV(console) get $first $last]
  1863. }
  1864. set data [join $data \n]
  1865. }
  1866. history { set data [tkcon history] }
  1867. all - default { set data [$PRIV(console) get 1.0 end-1c] }
  1868. widget {
  1869. set data [$opt get 1.0 end-1c]
  1870. }
  1871. }
  1872. if {[catch {open $fn $mode} fid]} {
  1873. return -code error "Save Error: Unable to open '$fn' for writing\n$fid"
  1874. }
  1875. puts -nonewline $fid $data
  1876. close $fid
  1877. }
  1878. ## ::tkcon::MainInit
  1879. ## This is only called for the main interpreter to include certain procs
  1880. ## that we don't want to include (or rather, just alias) in slave interps.
  1881. ##
  1882. proc ::tkcon::MainInit {} {
  1883. variable PRIV
  1884. variable OPT
  1885. if {![info exists PRIV(slaves)]} {
  1886. array set PRIV [list slave 0 slaves Main name {} \
  1887. interps [list [tk appname]]]
  1888. }
  1889. interp alias {} ::tkcon::Main {} ::tkcon::InterpEval Main
  1890. interp alias {} ::tkcon::Slave {} ::tkcon::InterpEval
  1891. proc ::tkcon::GetSlaveNum {} {
  1892. set i -1
  1893. while {[interp exists Slave[incr i]]} {
  1894. # oh my god, an empty loop!
  1895. }
  1896. return $i
  1897. }
  1898. ## ::tkcon::New - create new console window
  1899. ## Creates a slave interpreter and sources in this script.
  1900. ## All other interpreters also get a command to eval function in the
  1901. ## new interpreter.
  1902. ##
  1903. proc ::tkcon::New {} {
  1904. variable PRIV
  1905. global argv0 argc argv
  1906. set tmp [interp create Slave[GetSlaveNum]]
  1907. lappend PRIV(slaves) $tmp
  1908. load {} Tk $tmp
  1909. # If we have tbcload, then that should be autoloaded into slaves.
  1910. set idx [lsearch [info loaded] "* Tbcload"]
  1911. if {$idx != -1} { catch {load {} Tbcload $tmp} }
  1912. lappend PRIV(interps) [$tmp eval [list tk appname \
  1913. "[tk appname] $tmp"]]
  1914. if {[info exist argv0]} {$tmp eval [list set argv0 $argv0]}
  1915. $tmp eval set argc $argc
  1916. $tmp eval [list set argv $argv]
  1917. $tmp eval [list namespace eval ::tkcon {}]
  1918. $tmp eval [list set ::tkcon::PRIV(name) $tmp]
  1919. $tmp eval [list set ::tkcon::PRIV(SCRIPT) $::tkcon::PRIV(SCRIPT)]
  1920. $tmp alias exit ::tkcon::Exit $tmp
  1921. $tmp alias ::tkcon::Destroy ::tkcon::Destroy $tmp
  1922. $tmp alias ::tkcon::New ::tkcon::New
  1923. $tmp alias ::tkcon::Main ::tkcon::InterpEval Main
  1924. $tmp alias ::tkcon::Slave ::tkcon::InterpEval
  1925. $tmp alias ::tkcon::Interps ::tkcon::Interps
  1926. $tmp alias ::tkcon::NewDisplay ::tkcon::NewDisplay
  1927. $tmp alias ::tkcon::Display ::tkcon::Display
  1928. $tmp alias ::tkcon::StateCheckpoint ::tkcon::StateCheckpoint
  1929. $tmp alias ::tkcon::StateCleanup ::tkcon::StateCleanup
  1930. $tmp alias ::tkcon::StateCompare ::tkcon::StateCompare
  1931. $tmp alias ::tkcon::StateRevert ::tkcon::StateRevert
  1932. $tmp eval {
  1933. if [catch {source -rsrc tkcon}] { source $::tkcon::PRIV(SCRIPT) }
  1934. }
  1935. return $tmp
  1936. }
  1937. ## ::tkcon::Exit - full exit OR destroy slave console
  1938. ## This proc should only be called in the main interpreter from a slave.
  1939. ## The master determines whether we do a full exit or just kill the slave.
  1940. ##
  1941. proc ::tkcon::Exit {slave args} {
  1942. variable PRIV
  1943. variable OPT
  1944. ## Slave interpreter exit request
  1945. if {[string match exit $OPT(slaveexit)]} {
  1946. ## Only exit if it specifically is stated to do so
  1947. uplevel 1 exit $args
  1948. }
  1949. ## Otherwise we will delete the slave interp and associated data
  1950. set name [InterpEval $slave]
  1951. set PRIV(interps) [lremove $PRIV(interps) [list $name]]
  1952. set PRIV(slaves) [lremove $PRIV(slaves) [list $slave]]
  1953. interp delete $slave
  1954. StateCleanup $slave
  1955. return
  1956. }
  1957. ## ::tkcon::Destroy - destroy console window
  1958. ## This proc should only be called by the main interpreter. If it is
  1959. ## called from there, it will ask before exiting tkcon. All others
  1960. ## (slaves) will just have their slave interpreter deleted, closing them.
  1961. ##
  1962. proc ::tkcon::Destroy {{slave {}}} {
  1963. variable PRIV
  1964. if {[string match {} $slave]} {
  1965. ## Main interpreter close request
  1966. if {[tk_dialog $PRIV(base).destroyme {Quit tkcon?} \
  1967. {Closing the Main console will quit tkcon} \
  1968. warning 0 "Don't Quit" "Quit tkcon"]} exit
  1969. } else {
  1970. ## Slave interpreter close request
  1971. set name [InterpEval $slave]
  1972. set PRIV(interps) [lremove $PRIV(interps) [list $name]]
  1973. set PRIV(slaves) [lremove $PRIV(slaves) [list $slave]]
  1974. interp delete $slave
  1975. }
  1976. StateCleanup $slave
  1977. return
  1978. }
  1979. if {$OPT(overrideexit)} {
  1980. ## We want to do a couple things before exiting...
  1981. if {[catch {rename ::exit ::tkcon::FinalExit} err]} {
  1982. puts stderr "tkcon might panic:\n$err"
  1983. }
  1984. proc ::exit args {
  1985. if {$::tkcon::OPT(usehistory)} {
  1986. if {[catch {open $::tkcon::PRIV(histfile) w} fid]} {
  1987. puts stderr "unable to save history file:\n$fid"
  1988. # pause a moment, because we are about to die finally...
  1989. after 1000
  1990. } else {
  1991. set max [::tkcon::EvalSlave history nextid]
  1992. set id [expr {$max - $::tkcon::OPT(history)}]
  1993. if {$id < 1} { set id 1 }
  1994. ## FIX: This puts history in backwards!!
  1995. while {($id < $max) && ![catch \
  1996. {::tkcon::EvalSlave history event $id} cmd]} {
  1997. if {[string compare {} $cmd]} {
  1998. puts $fid "::tkcon::EvalSlave\
  1999. history add [list $cmd]"
  2000. }
  2001. incr id
  2002. }
  2003. close $fid
  2004. }
  2005. }
  2006. uplevel 1 ::tkcon::FinalExit $args
  2007. }
  2008. }
  2009. ## ::tkcon::InterpEval - passes evaluation to another named interpreter
  2010. ## If the interpreter is named, but no args are given, it returns the
  2011. ## [tk appname] of that interps master (not the associated eval slave).
  2012. ##
  2013. proc ::tkcon::InterpEval {{slave {}} args} {
  2014. variable PRIV
  2015. if {[llength [info level 0]] == 1} {
  2016. # no args given
  2017. return $PRIV(slaves)
  2018. } elseif {[string match {[Mm]ain} $slave]} {
  2019. set slave {}
  2020. }
  2021. if {[llength $args]} {
  2022. return [interp eval $slave uplevel \#0 $args]
  2023. } else {
  2024. return [interp eval $slave tk appname]
  2025. }
  2026. }
  2027. proc ::tkcon::Interps {{ls {}} {interp {}}} {
  2028. if {[string match {} $interp]} {
  2029. lappend ls {} [tk appname]
  2030. }
  2031. foreach i [interp slaves $interp] {
  2032. if {[string compare {} $interp]} { set i "$interp $i" }
  2033. if {[string compare {} [interp eval $i package provide Tk]]} {
  2034. lappend ls $i [interp eval $i tk appname]
  2035. } else {
  2036. lappend ls $i {}
  2037. }
  2038. set ls [Interps $ls $i]
  2039. }
  2040. return $ls
  2041. }
  2042. proc ::tkcon::Display {{disp {}}} {
  2043. variable DISP
  2044. set res {}
  2045. if {$disp != ""} {
  2046. if {![info exists DISP($disp)]} { return }
  2047. return [list $DISP($disp) [winfo interps -displayof $DISP($disp)]]
  2048. }
  2049. return [lsort -dictionary [array names DISP]]
  2050. }
  2051. proc ::tkcon::NewDisplay {} {
  2052. variable PRIV
  2053. variable DISP
  2054. set t $PRIV(base).newdisp
  2055. if {![winfo exists $t]} {
  2056. toplevel $t
  2057. wm withdraw $t
  2058. wm title $t "tkcon Attach to Display"
  2059. label $t.gets -text "New Display: "
  2060. entry $t.data -width 32
  2061. button $t.ok -text "OK" -command {set ::tkcon::PRIV(grab) 1}
  2062. bind $t.data <Return> [list $t.ok invoke]
  2063. bind $t.ok <Return> [list $t.ok invoke]
  2064. grid $t.gets $t.data -sticky ew
  2065. grid $t.ok - -sticky ew
  2066. grid columnconfig $t 1 -weight 1
  2067. grid rowconfigure $t 1 -weight 1
  2068. wm transient $t $PRIV(root)
  2069. wm geometry $t +[expr {([winfo screenwidth $t]-[winfo \
  2070. reqwidth $t]) / 2}]+[expr {([winfo \
  2071. screenheight $t]-[winfo reqheight $t]) / 2}]
  2072. }
  2073. $t.data delete 0 end
  2074. wm deiconify $t
  2075. raise $t
  2076. grab $t
  2077. focus $t.data
  2078. vwait ::tkcon::PRIV(grab)
  2079. grab release $t
  2080. wm withdraw $t
  2081. set disp [$t.data get]
  2082. if {$disp == ""} { return }
  2083. regsub -all {\.} [string tolower $disp] ! dt
  2084. set dt $PRIV(base).$dt
  2085. destroy $dt
  2086. if {[catch {
  2087. toplevel $dt -screen $disp
  2088. set interps [winfo interps -displayof $dt]
  2089. if {![llength $interps]} {
  2090. error "No other Tk interpreters on $disp"
  2091. }
  2092. send -displayof $dt [lindex $interps 0] [list info tclversion]
  2093. } err]} {
  2094. global env
  2095. if {[info exists env(DISPLAY)]} {
  2096. set myd $env(DISPLAY)
  2097. } else {
  2098. set myd "myDisplay:0"
  2099. }
  2100. tk_messageBox -title "Display Connection Error" \
  2101. -message "Unable to connect to \"$disp\":\n$err\
  2102. \nMake sure you have xauth-based permissions\
  2103. (xauth add $myd . `mcookie`), and xhost is disabled\
  2104. (xhost -) on \"$disp\"" \
  2105. -icon error -type ok
  2106. destroy $dt
  2107. return
  2108. }
  2109. set DISP($disp) $dt
  2110. wm withdraw $dt
  2111. bind $dt <Destroy> [subst {catch {unset ::tkcon::DISP($disp)}}]
  2112. tk_messageBox -title "$disp Connection" \
  2113. -message "Connected to \"$disp\", found:\n[join $interps \n]" \
  2114. -type ok
  2115. }
  2116. ##
  2117. ## The following state checkpoint/revert procedures are very sketchy
  2118. ## and prone to problems. They do not track modifications to currently
  2119. ## existing procedures/variables, and they can really screw things up
  2120. ## if you load in libraries (especially Tk) between checkpoint and
  2121. ## revert. Only with this knowledge in mind should you use these.
  2122. ##
  2123. ## ::tkcon::StateCheckpoint - checkpoints the current state of the system
  2124. ## This allows you to return to this state with ::tkcon::StateRevert
  2125. # ARGS:
  2126. ##
  2127. proc ::tkcon::StateCheckpoint {app type} {
  2128. variable CPS
  2129. variable PRIV
  2130. if {[info exists CPS($type,$app,cmd)] && \
  2131. [tk_dialog $PRIV(base).warning "Overwrite Previous State?" \
  2132. "Are you sure you want to lose previously checkpointed\
  2133. state of $type \"$app\"?" questhead 1 "Do It" "Cancel"]} return
  2134. set CPS($type,$app,cmd) [EvalOther $app $type info commands *]
  2135. set CPS($type,$app,var) [EvalOther $app $type info vars *]
  2136. return
  2137. }
  2138. ## ::tkcon::StateCompare - compare two states and output difference
  2139. # ARGS:
  2140. ##
  2141. proc ::tkcon::StateCompare {app type {verbose 0}} {
  2142. variable CPS
  2143. variable PRIV
  2144. variable OPT
  2145. variable COLOR
  2146. if {![info exists CPS($type,$app,cmd)]} {
  2147. return -code error \
  2148. "No previously checkpointed state for $type \"$app\""
  2149. }
  2150. set w $PRIV(base).compare
  2151. if {[winfo exists $w]} {
  2152. $w.text config -state normal
  2153. $w.text delete 1.0 end
  2154. } else {
  2155. toplevel $w
  2156. frame $w.btn
  2157. scrollbar $w.sy -takefocus 0 -bd 1 -command [list $w.text yview]
  2158. text $w.text -yscrollcommand [list $w.sy set] -height 12 \
  2159. -foreground $COLOR(stdin) \
  2160. -background $COLOR(bg) \
  2161. -insertbackground $COLOR(cursor) \
  2162. -font $OPT(font)
  2163. pack $w.btn -side bottom -fill x
  2164. pack $w.sy -side right -fill y
  2165. pack $w.text -fill both -expand 1
  2166. button $w.btn.close -text "Dismiss" -width 11 \
  2167. -command [list destroy $w]
  2168. button $w.btn.check -text "Recheckpoint" -width 11
  2169. button $w.btn.revert -text "Revert" -width 11
  2170. button $w.btn.expand -text "Verbose" -width 11
  2171. button $w.btn.update -text "Update" -width 11
  2172. pack $w.btn.check $w.btn.revert $w.btn.expand $w.btn.update \
  2173. $w.btn.close -side left -fill x -padx 4 -pady 2 -expand 1
  2174. $w.text tag config red -foreground red
  2175. }
  2176. wm title $w "Compare State: $type [list $app]"
  2177. $w.btn.check config \
  2178. -command "::tkcon::StateCheckpoint [list $app] $type; \
  2179. ::tkcon::StateCompare [list $app] $type $verbose"
  2180. $w.btn.revert config \
  2181. -command "::tkcon::StateRevert [list $app] $type; \
  2182. ::tkcon::StateCompare [list $app] $type $verbose"
  2183. $w.btn.update config -command [info level 0]
  2184. if {$verbose} {
  2185. $w.btn.expand config -text Brief \
  2186. -command [list ::tkcon::StateCompare $app $type 0]
  2187. } else {
  2188. $w.btn.expand config -text Verbose \
  2189. -command [list ::tkcon::StateCompare $app $type 1]
  2190. }
  2191. ## Don't allow verbose mode unless 'dump' exists in $app
  2192. ## We're assuming this is tkcon's dump command
  2193. set hasdump [llength [EvalOther $app $type info commands dump]]
  2194. if {$hasdump} {
  2195. $w.btn.expand config -state normal
  2196. } else {
  2197. $w.btn.expand config -state disabled
  2198. }
  2199. set cmds [lremove [EvalOther $app $type info commands *] \
  2200. $CPS($type,$app,cmd)]
  2201. set vars [lremove [EvalOther $app $type info vars *] \
  2202. $CPS($type,$app,var)]
  2203. if {$hasdump && $verbose} {
  2204. set cmds [EvalOther $app $type eval dump c -nocomplain $cmds]
  2205. set vars [EvalOther $app $type eval dump v -nocomplain $vars]
  2206. }
  2207. $w.text insert 1.0 "NEW COMMANDS IN \"$app\":\n" red \
  2208. $cmds {} "\n\nNEW VARIABLES IN \"$app\":\n" red $vars {}
  2209. raise $w
  2210. $w.text config -state disabled
  2211. }
  2212. ## ::tkcon::StateRevert - reverts interpreter to previous state
  2213. # ARGS:
  2214. ##
  2215. proc ::tkcon::StateRevert {app type} {
  2216. variable CPS
  2217. variable PRIV
  2218. if {![info exists CPS($type,$app,cmd)]} {
  2219. return -code error \
  2220. "No previously checkpointed state for $type \"$app\""
  2221. }
  2222. if {![tk_dialog $PRIV(base).warning "Revert State?" \
  2223. "Are you sure you want to revert the state in $type \"$app\"?"\
  2224. questhead 1 "Do It" "Cancel"]} {
  2225. foreach i [lremove [EvalOther $app $type info commands *] \
  2226. $CPS($type,$app,cmd)] {
  2227. catch {EvalOther $app $type rename $i {}}
  2228. }
  2229. foreach i [lremove [EvalOther $app $type info vars *] \
  2230. $CPS($type,$app,var)] {
  2231. catch {EvalOther $app $type unset $i}
  2232. }
  2233. }
  2234. }
  2235. ## ::tkcon::StateCleanup - cleans up state information in master array
  2236. #
  2237. ##
  2238. proc ::tkcon::StateCleanup {args} {
  2239. variable CPS
  2240. if {![llength $args]} {
  2241. foreach state [array names CPS slave,*] {
  2242. if {![interp exists [string range $state 6 end]]} {
  2243. unset CPS($state)
  2244. }
  2245. }
  2246. } else {
  2247. set app [lindex $args 0]
  2248. set type [lindex $args 1]
  2249. if {[regexp {^(|slave)$} $type]} {
  2250. foreach state [array names CPS "slave,$app\[, \]*"] {
  2251. if {![interp exists [string range $state 6 end]]} {
  2252. unset CPS($state)
  2253. }
  2254. }
  2255. } else {
  2256. catch {unset CPS($type,$app)}
  2257. }
  2258. }
  2259. }
  2260. }
  2261. ## ::tkcon::Event - get history event, search if string != {}
  2262. ## look forward (next) if $int>0, otherwise look back (prev)
  2263. # ARGS: W - console widget
  2264. ##
  2265. proc ::tkcon::Event {int {str {}}} {
  2266. if {!$int} return
  2267. variable PRIV
  2268. set w $PRIV(console)
  2269. set nextid [EvalSlave history nextid]
  2270. if {[string compare {} $str]} {
  2271. ## String is not empty, do an event search
  2272. set event $PRIV(event)
  2273. if {$int < 0 && $event == $nextid} { set PRIV(cmdbuf) $str }
  2274. set len [string len $PRIV(cmdbuf)]
  2275. incr len -1
  2276. if {$int > 0} {
  2277. ## Search history forward
  2278. while {$event < $nextid} {
  2279. if {[incr event] == $nextid} {
  2280. $w delete limit end
  2281. $w insert limit $PRIV(cmdbuf)
  2282. break
  2283. } elseif {
  2284. ![catch {EvalSlave history event $event} res] &&
  2285. [set p [string first $PRIV(cmdbuf) $res]] > -1
  2286. } {
  2287. set p2 [expr {$p + [string length $PRIV(cmdbuf)]}]
  2288. $w delete limit end
  2289. $w insert limit $res
  2290. Blink $w "limit + $p c" "limit + $p2 c"
  2291. break
  2292. }
  2293. }
  2294. set PRIV(event) $event
  2295. } else {
  2296. ## Search history reverse
  2297. while {![catch {EvalSlave history event [incr event -1]} res]} {
  2298. if {[set p [string first $PRIV(cmdbuf) $res]] > -1} {
  2299. set p2 [expr {$p + [string length $PRIV(cmdbuf)]}]
  2300. $w delete limit end
  2301. $w insert limit $res
  2302. set PRIV(event) $event
  2303. Blink $w "limit + $p c" "limit + $p2 c"
  2304. break
  2305. }
  2306. }
  2307. }
  2308. } else {
  2309. ## String is empty, just get next/prev event
  2310. if {$int > 0} {
  2311. ## Goto next command in history
  2312. if {$PRIV(event) < $nextid} {
  2313. $w delete limit end
  2314. if {[incr PRIV(event)] == $nextid} {
  2315. $w insert limit $PRIV(cmdbuf)
  2316. } else {
  2317. $w insert limit [EvalSlave history event $PRIV(event)]
  2318. }
  2319. }
  2320. } else {
  2321. ## Goto previous command in history
  2322. if {$PRIV(event) == $nextid} {
  2323. set PRIV(cmdbuf) [CmdGet $w]
  2324. }
  2325. if {[catch {EvalSlave history event [incr PRIV(event) -1]} res]} {
  2326. incr PRIV(event)
  2327. } else {
  2328. $w delete limit end
  2329. $w insert limit $res
  2330. }
  2331. }
  2332. }
  2333. $w mark set insert end
  2334. $w see end
  2335. }
  2336. ## ::tkcon::ErrorHighlight - magic error highlighting
  2337. ## beware: voodoo included
  2338. # ARGS:
  2339. ##
  2340. proc ::tkcon::ErrorHighlight w {
  2341. variable COLOR
  2342. variable OPT
  2343. ## do voodoo here
  2344. set app [Attach]
  2345. # we have to pull the text out, because text regexps are screwed on \n's.
  2346. set info [$w get 1.0 end-1c]
  2347. # Check for specific line error in a proc
  2348. set exp(proc) "\"(\[^\"\]+)\"\n\[\t \]+\\\(procedure \"(\[^\"\]+)\""
  2349. # Check for too few args to a proc
  2350. set exp(param) "parameter \"(\[^\"\]+)\" to \"(\[^\"\]+)\""
  2351. set start 1.0
  2352. while {
  2353. [regexp -indices -- $exp(proc) $info junk what cmd] ||
  2354. [regexp -indices -- $exp(param) $info junk what cmd]
  2355. } {
  2356. foreach {w0 w1} $what {c0 c1} $cmd {break}
  2357. set what [string range $info $w0 $w1]
  2358. set cmd [string range $info $c0 $c1]
  2359. if {[string match *::* $cmd]} {
  2360. set res [uplevel 1 ::tkcon::EvalOther $app namespace eval \
  2361. [list [namespace qualifiers $cmd] \
  2362. [list info procs [namespace tail $cmd]]]]
  2363. } else {
  2364. set res [uplevel 1 ::tkcon::EvalOther $app info procs [list $cmd]]
  2365. }
  2366. if {[llength $res]==1} {
  2367. set tag [UniqueTag $w]
  2368. $w tag add $tag $start+${c0}c $start+1c+${c1}c
  2369. $w tag configure $tag -foreground $COLOR(stdout)
  2370. $w tag bind $tag <Enter> [list $w tag configure $tag -under 1]
  2371. $w tag bind $tag <Leave> [list $w tag configure $tag -under 0]
  2372. $w tag bind $tag <ButtonRelease-1> "if {!\$tkPriv(mouseMoved)} \
  2373. {[list $OPT(edit) -attach $app -type proc -find $what -- $cmd]}"
  2374. }
  2375. set info [string range $info $c1 end]
  2376. set start [$w index $start+${c1}c]
  2377. }
  2378. ## Next stage, check for procs that start a line
  2379. set start 1.0
  2380. set exp(cmd) "^\"\[^\" \t\n\]+"
  2381. while {
  2382. [string compare {} [set ix \
  2383. [$w search -regexp -count numc -- $exp(cmd) $start end]]]
  2384. } {
  2385. set start [$w index $ix+${numc}c]
  2386. # +1c to avoid the first quote
  2387. set cmd [$w get $ix+1c $start]
  2388. if {[string match *::* $cmd]} {
  2389. set res [uplevel 1 ::tkcon::EvalOther $app namespace eval \
  2390. [list [namespace qualifiers $cmd] \
  2391. [list info procs [namespace tail $cmd]]]]
  2392. } else {
  2393. set res [uplevel 1 ::tkcon::EvalOther $app info procs [list $cmd]]
  2394. }
  2395. if {[llength $res]==1} {
  2396. set tag [UniqueTag $w]
  2397. $w tag add $tag $ix+1c $start
  2398. $w tag configure $tag -foreground $COLOR(proc)
  2399. $w tag bind $tag <Enter> [list $w tag configure $tag -under 1]
  2400. $w tag bind $tag <Leave> [list $w tag configure $tag -under 0]
  2401. $w tag bind $tag <ButtonRelease-1> "if {!\$tkPriv(mouseMoved)} \
  2402. {[list $OPT(edit) -attach $app -type proc -- $cmd]}"
  2403. }
  2404. }
  2405. }
  2406. ## tkcon - command that allows control over the console
  2407. ## This always exists in the main interpreter, and is aliased into
  2408. ## other connected interpreters
  2409. # ARGS: totally variable, see internal comments
  2410. ##
  2411. proc tkcon {cmd args} {
  2412. variable ::tkcon::PRIV
  2413. variable ::tkcon::OPT
  2414. global errorInfo
  2415. switch -glob -- $cmd {
  2416. buf* {
  2417. ## 'buffer' Sets/Query the buffer size
  2418. if {[llength $args]} {
  2419. if {[regexp {^[1-9][0-9]*$} $args]} {
  2420. set OPT(buffer) $args
  2421. # catch in case the console doesn't exist yet
  2422. catch {::tkcon::ConstrainBuffer $PRIV(console) \
  2423. $OPT(buffer)}
  2424. } else {
  2425. return -code error "buffer must be a valid integer"
  2426. }
  2427. }
  2428. return $OPT(buffer)
  2429. }
  2430. bg* {
  2431. ## 'bgerror' Brings up an error dialog
  2432. set errorInfo [lindex $args 1]
  2433. bgerror [lindex $args 0]
  2434. }
  2435. cl* {
  2436. ## 'close' Closes the console
  2437. ::tkcon::Destroy
  2438. }
  2439. cons* {
  2440. ## 'console' - passes the args to the text widget of the console.
  2441. set result [uplevel 1 $PRIV(console) $args]
  2442. ::tkcon::ConstrainBuffer $PRIV(console) \
  2443. $OPT(buffer)
  2444. return $result
  2445. }
  2446. congets {
  2447. ## 'congets' a replacement for [gets stdin]
  2448. # Use the 'gets' alias of 'tkcon_gets' command instead of
  2449. # calling the *get* methods directly for best compatability
  2450. if {[llength $args]} {
  2451. return -code error "wrong # args: must be \"tkcon congets\""
  2452. }
  2453. tkcon show
  2454. set old [bind TkConsole <<TkCon_Eval>>]
  2455. bind TkConsole <<TkCon_Eval>> { set ::tkcon::PRIV(wait) 0 }
  2456. set w $PRIV(console)
  2457. # Make sure to move the limit to get the right data
  2458. $w mark set insert end
  2459. $w mark set limit insert
  2460. $w see end
  2461. vwait ::tkcon::PRIV(wait)
  2462. set line [::tkcon::CmdGet $w]
  2463. $w insert end \n
  2464. bind TkConsole <<TkCon_Eval>> $old
  2465. return $line
  2466. }
  2467. getc* {
  2468. ## 'getcommand' a replacement for [gets stdin]
  2469. ## This forces a complete command to be input though
  2470. if {[llength $args]} {
  2471. return -code error "wrong # args: must be \"tkcon getcommand\""
  2472. }
  2473. tkcon show
  2474. set old [bind TkConsole <<TkCon_Eval>>]
  2475. bind TkConsole <<TkCon_Eval>> { set ::tkcon::PRIV(wait) 0 }
  2476. set w $PRIV(console)
  2477. # Make sure to move the limit to get the right data
  2478. $w mark set insert end
  2479. $w mark set limit insert
  2480. $w see end
  2481. vwait ::tkcon::PRIV(wait)
  2482. set line [::tkcon::CmdGet $w]
  2483. $w insert end \n
  2484. while {![info complete $line] || [regexp {[^\\]\\$} $line]} {
  2485. vwait ::tkcon::PRIV(wait)
  2486. set line [::tkcon::CmdGet $w]
  2487. $w insert end \n
  2488. $w see end
  2489. }
  2490. bind TkConsole <<TkCon_Eval>> $old
  2491. return $line
  2492. }
  2493. get - gets {
  2494. ## 'gets' - a replacement for [gets stdin]
  2495. ## This pops up a text widget to be used for stdin (local grabbed)
  2496. if {[llength $args]} {
  2497. return -code error "wrong # args: should be \"tkcon gets\""
  2498. }
  2499. set t $PRIV(base).gets
  2500. if {![winfo exists $t]} {
  2501. toplevel $t
  2502. wm withdraw $t
  2503. wm title $t "tkcon gets stdin request"
  2504. label $t.gets -text "\"gets stdin\" request:"
  2505. text $t.data -width 32 -height 5 -wrap none \
  2506. -xscrollcommand [list $t.sx set] \
  2507. -yscrollcommand [list $t.sy set]
  2508. scrollbar $t.sx -orient h -takefocus 0 -highlightthick 0 \
  2509. -command [list $t.data xview]
  2510. scrollbar $t.sy -orient v -takefocus 0 -highlightthick 0 \
  2511. -command [list $t.data yview]
  2512. button $t.ok -text "OK" -command {set ::tkcon::PRIV(grab) 1}
  2513. bind $t.ok <Return> { %W invoke }
  2514. grid $t.gets - -sticky ew
  2515. grid $t.data $t.sy -sticky news
  2516. grid $t.sx -sticky ew
  2517. grid $t.ok - -sticky ew
  2518. grid columnconfig $t 0 -weight 1
  2519. grid rowconfig $t 1 -weight 1
  2520. wm transient $t $PRIV(root)
  2521. wm geometry $t +[expr {([winfo screenwidth $t]-[winfo \
  2522. reqwidth $t]) / 2}]+[expr {([winfo \
  2523. screenheight $t]-[winfo reqheight $t]) / 2}]
  2524. }
  2525. $t.data delete 1.0 end
  2526. wm deiconify $t
  2527. raise $t
  2528. grab $t
  2529. focus $t.data
  2530. vwait ::tkcon::PRIV(grab)
  2531. grab release $t
  2532. wm withdraw $t
  2533. return [$t.data get 1.0 end-1c]
  2534. }
  2535. err* {
  2536. ## Outputs stack caused by last error.
  2537. ## error handling with pizazz (but with pizza would be nice too)
  2538. if {[llength $args]==2} {
  2539. set app [lindex $args 0]
  2540. set type [lindex $args 1]
  2541. if {[catch {::tkcon::EvalOther $app $type set errorInfo} info]} {
  2542. set info "error getting info from $type $app:\n$info"
  2543. }
  2544. } else {
  2545. set info $PRIV(errorInfo)
  2546. }
  2547. if {[string match {} $info]} { set info "errorInfo empty" }
  2548. ## If args is empty, the -attach switch just ignores it
  2549. $OPT(edit) -attach $args -type error -- $info
  2550. }
  2551. fi* {
  2552. ## 'find' string
  2553. ::tkcon::Find $PRIV(console) $args
  2554. }
  2555. fo* {
  2556. ## 'font' ?fontname? - gets/sets the font of the console
  2557. if {[llength $args]} {
  2558. if {[info exists PRIV(console)] && \
  2559. [winfo exists $PRIV(console)]} {
  2560. $PRIV(console) config -font $args
  2561. set OPT(font) [$PRIV(console) cget -font]
  2562. } else {
  2563. set OPT(font) $args
  2564. }
  2565. }
  2566. return $OPT(font)
  2567. }
  2568. hid* - with* {
  2569. ## 'hide' 'withdraw' - hides the console.
  2570. if {[info exists PRIV(root)] && [winfo exists $PRIV(root)]} {
  2571. wm withdraw $PRIV(root)
  2572. }
  2573. }
  2574. his* {
  2575. ## 'history'
  2576. set sub {\2}
  2577. if {[string match -new* $args]} { append sub "\n"}
  2578. set h [::tkcon::EvalSlave history]
  2579. regsub -all "( *\[0-9\]+ |\t)(\[^\n\]*\n?)" $h $sub h
  2580. return $h
  2581. }
  2582. ico* {
  2583. ## 'iconify' - iconifies the console with 'iconify'.
  2584. if {[info exists PRIV(root)] && [winfo exists $PRIV(root)]} {
  2585. wm iconify $PRIV(root)
  2586. }
  2587. }
  2588. mas* - eval {
  2589. ## 'master' - evals contents in master interpreter
  2590. uplevel \#0 $args
  2591. }
  2592. set {
  2593. ## 'set' - set (or get, or unset) simple vars (not whole arrays)
  2594. ## from the master console interpreter
  2595. ## possible formats:
  2596. ## tkcon set <var>
  2597. ## tkcon set <var> <value>
  2598. ## tkcon set <var> <interp> <var1> <var2> w
  2599. ## tkcon set <var> <interp> <var1> <var2> u
  2600. ## tkcon set <var> <interp> <var1> <var2> r
  2601. if {[llength $args]==5} {
  2602. ## This is for use w/ 'tkcon upvar' and only works with slaves
  2603. foreach {var i var1 var2 op} $args break
  2604. if {[string compare {} $var2]} { append var1 "($var2)" }
  2605. switch $op {
  2606. u { uplevel \#0 [list unset $var] }
  2607. w {
  2608. return [uplevel \#0 [list set $var \
  2609. [interp eval $i [list set $var1]]]]
  2610. }
  2611. r {
  2612. return [interp eval $i [list set $var1 \
  2613. [uplevel \#0 [list set $var]]]]
  2614. }
  2615. }
  2616. } elseif {[llength $args] == 1} {
  2617. upvar \#0 [lindex $args 0] var
  2618. if {[array exists var]} {
  2619. return [array get var]
  2620. } else {
  2621. return $var
  2622. }
  2623. }
  2624. return [uplevel \#0 set $args]
  2625. }
  2626. append {
  2627. ## Modify a var in the master environment using append
  2628. return [uplevel \#0 append $args]
  2629. }
  2630. lappend {
  2631. ## Modify a var in the master environment using lappend
  2632. return [uplevel \#0 lappend $args]
  2633. }
  2634. sh* - dei* {
  2635. ## 'show|deiconify' - deiconifies the console.
  2636. if {![info exists PRIV(root)]} {
  2637. set PRIV(showOnStartup) 0
  2638. set PRIV(root) .tkcon
  2639. set OPT(exec) ""
  2640. }
  2641. if {![winfo exists $PRIV(root)]} {
  2642. ::tkcon::Init
  2643. }
  2644. wm deiconify $PRIV(root)
  2645. raise $PRIV(root)
  2646. focus -force $PRIV(console)
  2647. }
  2648. ti* {
  2649. ## 'title' ?title? - gets/sets the console's title
  2650. if {[llength $args]} {
  2651. return [wm title $PRIV(root) [join $args]]
  2652. } else {
  2653. return [wm title $PRIV(root)]
  2654. }
  2655. }
  2656. upv* {
  2657. ## 'upvar' masterVar slaveVar
  2658. ## link slave variable slaveVar to the master variable masterVar
  2659. ## only works masters<->slave
  2660. set masterVar [lindex $args 0]
  2661. set slaveVar [lindex $args 1]
  2662. if {[info exists $masterVar]} {
  2663. interp eval $OPT(exec) \
  2664. [list set $slaveVar [set $masterVar]]
  2665. } else {
  2666. catch {interp eval $OPT(exec) [list unset $slaveVar]}
  2667. }
  2668. interp eval $OPT(exec) \
  2669. [list trace variable $slaveVar rwu \
  2670. [list tkcon set $masterVar $OPT(exec)]]
  2671. return
  2672. }
  2673. v* {
  2674. return $PRIV(version)
  2675. }
  2676. default {
  2677. ## tries to determine if the command exists, otherwise throws error
  2678. set new ::tkcon::[string toupper \
  2679. [string index $cmd 0]][string range $cmd 1 end]
  2680. if {[llength [info command $new]]} {
  2681. uplevel \#0 $new $args
  2682. } else {
  2683. return -code error "bad option \"$cmd\": must be\
  2684. [join [lsort [list attach close console destroy \
  2685. font hide iconify load main master new save show \
  2686. slave deiconify version title bgerror]] {, }]"
  2687. }
  2688. }
  2689. }
  2690. }
  2691. ##
  2692. ## Some procedures to make up for lack of built-in shell commands
  2693. ##
  2694. ## tkcon_puts -
  2695. ## This allows me to capture all stdout/stderr to the console window
  2696. ## This will be renamed to 'puts' at the appropriate time during init
  2697. ##
  2698. # ARGS: same as usual
  2699. # Outputs: the string with a color-coded text tag
  2700. ##
  2701. proc tkcon_puts args {
  2702. set len [llength $args]
  2703. foreach {arg1 arg2 arg3} $args { break }
  2704. if {$len == 1} {
  2705. tkcon console insert output "$arg1\n" stdout
  2706. } elseif {$len == 2} {
  2707. if {![string compare $arg1 -nonewline]} {
  2708. tkcon console insert output $arg2 stdout
  2709. } elseif {![string compare $arg1 stdout] \
  2710. || ![string compare $arg1 stderr]} {
  2711. tkcon console insert output "$arg2\n" $arg1
  2712. } else {
  2713. set len 0
  2714. }
  2715. } elseif {$len == 3} {
  2716. if {![string compare $arg1 -nonewline] \
  2717. && (![string compare $arg2 stdout] \
  2718. || ![string compare $arg2 stderr])} {
  2719. tkcon console insert output $arg3 $arg2
  2720. } elseif {(![string compare $arg1 stdout] \
  2721. || ![string compare $arg1 stderr]) \
  2722. && ![string compare $arg3 nonewline]} {
  2723. tkcon console insert output $arg2 $arg1
  2724. } else {
  2725. set len 0
  2726. }
  2727. } else {
  2728. set len 0
  2729. }
  2730. ## $len == 0 means it wasn't handled by tkcon above.
  2731. ##
  2732. if {$len == 0} {
  2733. global errorCode errorInfo
  2734. if {[catch "tkcon_tcl_puts $args" msg]} {
  2735. regsub tkcon_tcl_puts $msg puts msg
  2736. regsub -all tkcon_tcl_puts $errorInfo puts errorInfo
  2737. return -code error $msg
  2738. }
  2739. return $msg
  2740. }
  2741. ## WARNING: This update should behave well because it uses idletasks,
  2742. ## however, if there are weird looping problems with events, or
  2743. ## hanging in waits, try commenting this out.
  2744. if {$len} {
  2745. tkcon console see output
  2746. update idletasks
  2747. }
  2748. }
  2749. ## tkcon_gets -
  2750. ## This allows me to capture all stdin input without needing to stdin
  2751. ## This will be renamed to 'gets' at the appropriate time during init
  2752. ##
  2753. # ARGS: same as gets
  2754. # Outputs: same as gets
  2755. ##
  2756. proc tkcon_gets args {
  2757. set len [llength $args]
  2758. if {$len != 1 && $len != 2} {
  2759. return -code error \
  2760. "wrong # args: should be \"gets channelId ?varName?\""
  2761. }
  2762. if {[string compare stdin [lindex $args 0]]} {
  2763. return [uplevel 1 tkcon_tcl_gets $args]
  2764. }
  2765. set gtype [tkcon set ::tkcon::OPT(gets)]
  2766. if {$gtype == ""} { set gtype congets }
  2767. set data [tkcon $gtype]
  2768. if {$len == 2} {
  2769. upvar 1 [lindex $args 1] var
  2770. set var $data
  2771. return [string length $data]
  2772. }
  2773. return $data
  2774. }
  2775. ## edit - opens a file/proc/var for reading/editing
  2776. ##
  2777. # Arguments:
  2778. # type proc/file/var
  2779. # what the actual name of the item
  2780. # Returns: nothing
  2781. ##
  2782. proc edit {args} {
  2783. array set opts {-find {} -type {} -attach {}}
  2784. while {[string match -* [lindex $args 0]]} {
  2785. switch -glob -- [lindex $args 0] {
  2786. -f* { set opts(-find) [lindex $args 1] }
  2787. -a* { set opts(-attach) [lindex $args 1] }
  2788. -t* { set opts(-type) [lindex $args 1] }
  2789. -- { set args [lreplace $args 0 0]; break }
  2790. default {return -code error "unknown option \"[lindex $args 0]\""}
  2791. }
  2792. set args [lreplace $args 0 1]
  2793. }
  2794. # determine who we are dealing with
  2795. if {[llength $opts(-attach)]} {
  2796. foreach {app type} $opts(-attach) {break}
  2797. } else {
  2798. foreach {app type} [tkcon attach] {break}
  2799. }
  2800. set word [lindex $args 0]
  2801. if {[string match {} $opts(-type)]} {
  2802. if {[llength [::tkcon::EvalOther $app $type info commands [list $word]]]} {
  2803. set opts(-type) "proc"
  2804. } elseif {[llength [::tkcon::EvalOther $app $type info vars [list $word]]]} {
  2805. set opts(-type) "var"
  2806. } elseif {[::tkcon::EvalOther $app $type file isfile [list $word]]} {
  2807. set opts(-type) "file"
  2808. }
  2809. }
  2810. if {[string compare $opts(-type) {}]} {
  2811. # Create unique edit window toplevel
  2812. set w $::tkcon::PRIV(base).__edit
  2813. set i 0
  2814. while {[winfo exists $w[incr i]]} {}
  2815. append w $i
  2816. toplevel $w
  2817. wm withdraw $w
  2818. if {[string length $word] > 20} {
  2819. wm title $w "[string range $word 0 16]... - tkcon Edit"
  2820. } else {
  2821. wm title $w "$word - tkcon Edit"
  2822. }
  2823. text $w.text -wrap none \
  2824. -xscrollcommand [list $w.sx set] \
  2825. -yscrollcommand [list $w.sy set] \
  2826. -foreground $::tkcon::COLOR(stdin) \
  2827. -background $::tkcon::COLOR(bg) \
  2828. -insertbackground $::tkcon::COLOR(cursor) \
  2829. -font $::tkcon::OPT(font)
  2830. scrollbar $w.sx -orient h -takefocus 0 -bd 1 \
  2831. -command [list $w.text xview]
  2832. scrollbar $w.sy -orient v -takefocus 0 -bd 1 \
  2833. -command [list $w.text yview]
  2834. set menu [menu $w.mbar]
  2835. $w configure -menu $menu
  2836. ## File Menu
  2837. ##
  2838. set m [menu [::tkcon::MenuButton $menu File file]]
  2839. $m add command -label "Save As..." -underline 0 \
  2840. -command [list ::tkcon::Save {} widget $w.text]
  2841. $m add command -label "Append To..." -underline 0 \
  2842. -command [list ::tkcon::Save {} widget $w.text a+]
  2843. $m add separator
  2844. $m add command -label "Dismiss" -underline 0 -accel "Ctrl-w" \
  2845. -command [list destroy $w]
  2846. bind $w <Control-w> [list destroy $w]
  2847. bind $w <$::tkcon::PRIV(meta)-w> [list destroy $w]
  2848. ## Edit Menu
  2849. ##
  2850. set text $w.text
  2851. set m [menu [::tkcon::MenuButton $menu Edit edit]]
  2852. $m add command -label "Cut" -under 2 \
  2853. -command [list tk_textCut $text]
  2854. $m add command -label "Copy" -under 0 \
  2855. -command [list tk_textCopy $text]
  2856. $m add command -label "Paste" -under 0 \
  2857. -command [list tk_textPaste $text]
  2858. $m add separator
  2859. $m add command -label "Find" -under 0 \
  2860. -command [list ::tkcon::FindBox $text]
  2861. ## Send To Menu
  2862. ##
  2863. set m [menu [::tkcon::MenuButton $menu "Send To..." send]]
  2864. $m add command -label "Send To $app" -underline 0 \
  2865. -command "::tkcon::EvalOther [list $app] $type \
  2866. eval \[$w.text get 1.0 end-1c\]"
  2867. set other [tkcon attach]
  2868. if {[string compare $other [list $app $type]]} {
  2869. $m add command -label "Send To [lindex $other 0]" \
  2870. -command "::tkcon::EvalOther $other \
  2871. eval \[$w.text get 1.0 end-1c\]"
  2872. }
  2873. grid $w.text - $w.sy -sticky news
  2874. grid $w.sx - -sticky ew
  2875. grid columnconfigure $w 0 -weight 1
  2876. grid columnconfigure $w 1 -weight 1
  2877. grid rowconfigure $w 0 -weight 1
  2878. } else {
  2879. return -code error "unrecognized type '$word'"
  2880. }
  2881. switch -glob -- $opts(-type) {
  2882. proc* {
  2883. $w.text insert 1.0 \
  2884. [::tkcon::EvalOther $app $type dump proc [list $word]]
  2885. }
  2886. var* {
  2887. $w.text insert 1.0 \
  2888. [::tkcon::EvalOther $app $type dump var [list $word]]
  2889. }
  2890. file {
  2891. $w.text insert 1.0 [::tkcon::EvalOther $app $type eval \
  2892. [subst -nocommands {
  2893. set __tkcon(fid) [open $word r]
  2894. set __tkcon(data) [read \$__tkcon(fid)]
  2895. close \$__tkcon(fid)
  2896. after 1000 unset __tkcon
  2897. return \$__tkcon(data)
  2898. }
  2899. ]]
  2900. }
  2901. error* {
  2902. $w.text insert 1.0 [join $args \n]
  2903. ::tkcon::ErrorHighlight $w.text
  2904. }
  2905. default {
  2906. $w.text insert 1.0 [join $args \n]
  2907. }
  2908. }
  2909. wm deiconify $w
  2910. focus $w.text
  2911. if {[string compare $opts(-find) {}]} {
  2912. ::tkcon::Find $w.text $opts(-find) -case 1
  2913. }
  2914. }
  2915. interp alias {} ::more {} ::edit
  2916. interp alias {} ::less {} ::edit
  2917. ## echo
  2918. ## Relaxes the one string restriction of 'puts'
  2919. # ARGS: any number of strings to output to stdout
  2920. ##
  2921. proc echo args { puts stdout [concat $args] }
  2922. ## clear - clears the buffer of the console (not the history though)
  2923. ## This is executed in the parent interpreter
  2924. ##
  2925. proc clear {{pcnt 100}} {
  2926. if {![regexp {^[0-9]*$} $pcnt] || $pcnt < 1 || $pcnt > 100} {
  2927. return -code error \
  2928. "invalid percentage to clear: must be 1-100 (100 default)"
  2929. } elseif {$pcnt == 100} {
  2930. tkcon console delete 1.0 end
  2931. } else {
  2932. set tmp [expr {$pcnt/100.0*[tkcon console index end]}]
  2933. tkcon console delete 1.0 "$tmp linestart"
  2934. }
  2935. }
  2936. ## alias - akin to the csh alias command
  2937. ## If called with no args, then it dumps out all current aliases
  2938. ## If called with one arg, returns the alias of that arg (or {} if none)
  2939. # ARGS: newcmd - (optional) command to bind alias to
  2940. # args - command and args being aliased
  2941. ##
  2942. proc alias {{newcmd {}} args} {
  2943. if {[string match {} $newcmd]} {
  2944. set res {}
  2945. foreach a [interp aliases] {
  2946. lappend res [list $a -> [interp alias {} $a]]
  2947. }
  2948. return [join $res \n]
  2949. } elseif {![llength $args]} {
  2950. interp alias {} $newcmd
  2951. } else {
  2952. eval interp alias [list {} $newcmd {}] $args
  2953. }
  2954. }
  2955. ## unalias - unaliases an alias'ed command
  2956. # ARGS: cmd - command to unbind as an alias
  2957. ##
  2958. proc unalias {cmd} {
  2959. interp alias {} $cmd {}
  2960. }
  2961. ## dump - outputs variables/procedure/widget info in source'able form.
  2962. ## Accepts glob style pattern matching for the names
  2963. #
  2964. # ARGS: type - type of thing to dump: must be variable, procedure, widget
  2965. #
  2966. # OPTS: -nocomplain
  2967. # don't complain if no items of the specified type are found
  2968. # -filter pattern
  2969. # specifies a glob filter pattern to be used by the variable
  2970. # method as an array filter pattern (it filters down for
  2971. # nested elements) and in the widget method as a config
  2972. # option filter pattern
  2973. # -- forcibly ends options recognition
  2974. #
  2975. # Returns: the values of the requested items in a 'source'able form
  2976. ##
  2977. proc dump {type args} {
  2978. set whine 1
  2979. set code ok
  2980. if {![llength $args]} {
  2981. ## If no args, assume they gave us something to dump and
  2982. ## we'll try anything
  2983. set args $type
  2984. set type any
  2985. }
  2986. while {[string match -* [lindex $args 0]]} {
  2987. switch -glob -- [lindex $args 0] {
  2988. -n* { set whine 0; set args [lreplace $args 0 0] }
  2989. -f* { set fltr [lindex $args 1]; set args [lreplace $args 0 1] }
  2990. -- { set args [lreplace $args 0 0]; break }
  2991. default {return -code error "unknown option \"[lindex $args 0]\""}
  2992. }
  2993. }
  2994. if {$whine && ![llength $args]} {
  2995. return -code error "wrong \# args: [lindex [info level 0] 0] type\
  2996. ?-nocomplain? ?-filter pattern? ?--? pattern ?pattern ...?"
  2997. }
  2998. set res {}
  2999. switch -glob -- $type {
  3000. c* {
  3001. # command
  3002. # outputs commands by figuring out, as well as possible, what it is
  3003. # this does not attempt to auto-load anything
  3004. foreach arg $args {
  3005. if {[llength [set cmds [info commands $arg]]]} {
  3006. foreach cmd [lsort $cmds] {
  3007. if {[lsearch -exact [interp aliases] $cmd] > -1} {
  3008. append res "\#\# ALIAS: $cmd =>\
  3009. [interp alias {} $cmd]\n"
  3010. } elseif {
  3011. [llength [info procs $cmd]] ||
  3012. ([string match *::* $cmd] &&
  3013. [llength [namespace eval [namespace qual $cmd] \
  3014. info procs [namespace tail $cmd]]])
  3015. } {
  3016. if {[catch {dump p -- $cmd} msg] && $whine} {
  3017. set code error
  3018. }
  3019. append res $msg\n
  3020. } else {
  3021. append res "\#\# COMMAND: $cmd\n"
  3022. }
  3023. }
  3024. } elseif {$whine} {
  3025. append res "\#\# No known command $arg\n"
  3026. set code error
  3027. }
  3028. }
  3029. }
  3030. v* {
  3031. # variable
  3032. # outputs variables value(s), whether array or simple.
  3033. if {![info exists fltr]} { set fltr * }
  3034. foreach arg $args {
  3035. if {![llength [set vars [uplevel 1 info vars [list $arg]]]]} {
  3036. if {[uplevel 1 info exists $arg]} {
  3037. set vars $arg
  3038. } elseif {$whine} {
  3039. append res "\#\# No known variable $arg\n"
  3040. set code error
  3041. continue
  3042. } else { continue }
  3043. }
  3044. foreach var [lsort $vars] {
  3045. if {[uplevel 1 [list info locals $var]] == ""} {
  3046. # use the proper scope of the var, but namespace which
  3047. # won't id locals or some upvar'ed vars correctly
  3048. set new [uplevel 1 \
  3049. [list namespace which -variable $var]]
  3050. if {$new != ""} {
  3051. set var $new
  3052. }
  3053. }
  3054. upvar 1 $var v
  3055. if {[array exists v] || [catch {string length $v}]} {
  3056. set nst {}
  3057. append res "array set [list $var] \{\n"
  3058. if {[array size v]} {
  3059. foreach i \
  3060. [lsort -dictionary [array names v $fltr]] {
  3061. upvar 0 v\($i\) __a
  3062. if {[array exists __a]} {
  3063. append nst "\#\# NESTED ARRAY ELEM: $i\n"
  3064. append nst "upvar 0 [list $var\($i\)] __a;\
  3065. [dump v -filter $fltr __a]\n"
  3066. } else {
  3067. append res " [list $i]\t[list $v($i)]\n"
  3068. }
  3069. }
  3070. } else {
  3071. ## empty array
  3072. append res " empty array\n"
  3073. if {$var == ""} {
  3074. append nst "unset (empty)\n"
  3075. } else {
  3076. append nst "unset [list $var](empty)\n"
  3077. }
  3078. }
  3079. append res "\}\n$nst"
  3080. } else {
  3081. append res [list set $var $v]\n
  3082. }
  3083. }
  3084. }
  3085. }
  3086. p* {
  3087. # procedure
  3088. foreach arg $args {
  3089. if {
  3090. ![llength [set procs [info proc $arg]]] &&
  3091. ([string match *::* $arg] &&
  3092. [llength [set ps [namespace eval \
  3093. [namespace qualifier $arg] \
  3094. info procs [namespace tail $arg]]]])
  3095. } {
  3096. set procs {}
  3097. set namesp [namespace qualifier $arg]
  3098. foreach p $ps {
  3099. lappend procs ${namesp}::$p
  3100. }
  3101. }
  3102. if {[llength $procs]} {
  3103. foreach p [lsort $procs] {
  3104. set as {}
  3105. foreach a [info args $p] {
  3106. if {[info default $p $a tmp]} {
  3107. lappend as [list $a $tmp]
  3108. } else {
  3109. lappend as $a
  3110. }
  3111. }
  3112. append res [list proc $p $as [info body $p]]\n
  3113. }
  3114. } elseif {$whine} {
  3115. append res "\#\# No known proc $arg\n"
  3116. set code error
  3117. }
  3118. }
  3119. }
  3120. w* {
  3121. # widget
  3122. ## The user should have Tk loaded
  3123. if {![llength [info command winfo]]} {
  3124. return -code error "winfo not present, cannot dump widgets"
  3125. }
  3126. if {![info exists fltr]} { set fltr .* }
  3127. foreach arg $args {
  3128. if {[llength [set ws [info command $arg]]]} {
  3129. foreach w [lsort $ws] {
  3130. if {[winfo exists $w]} {
  3131. if {[catch {$w configure} cfg]} {
  3132. append res "\#\# Widget $w\
  3133. does not support configure method"
  3134. set code error
  3135. } else {
  3136. append res "\#\# [winfo class $w]\
  3137. $w\n$w configure"
  3138. foreach c $cfg {
  3139. if {[llength $c] != 5} continue
  3140. ## Check to see that the option does
  3141. ## not match the default, then check
  3142. ## the item against the user filter
  3143. if {[string compare [lindex $c 3] \
  3144. [lindex $c 4]] && \
  3145. [regexp -nocase -- $fltr $c]} {
  3146. append res " \\\n\t[list [lindex $c 0]\
  3147. [lindex $c 4]]"
  3148. }
  3149. }
  3150. append res \n
  3151. }
  3152. }
  3153. }
  3154. } elseif {$whine} {
  3155. append res "\#\# No known widget $arg\n"
  3156. set code error
  3157. }
  3158. }
  3159. }
  3160. a* {
  3161. ## see if we recognize it, other complain
  3162. if {[regexp {(var|com|proc|widget)} \
  3163. [set types [uplevel 1 what $args]]]} {
  3164. foreach type $types {
  3165. if {[regexp {(var|com|proc|widget)} $type]} {
  3166. append res "[uplevel 1 dump $type $args]\n"
  3167. }
  3168. }
  3169. } else {
  3170. set res "dump was unable to resolve type for \"$args\""
  3171. set code error
  3172. }
  3173. }
  3174. default {
  3175. return -code error "bad [lindex [info level 0] 0] option\
  3176. \"$type\": must be variable, command, procedure,\
  3177. or widget"
  3178. }
  3179. }
  3180. return -code $code [string trimright $res \n]
  3181. }
  3182. ## idebug - interactive debugger
  3183. #
  3184. # idebug body ?level?
  3185. #
  3186. # Prints out the body of the command (if it is a procedure) at the
  3187. # specified level. <i>level</i> defaults to the current level.
  3188. #
  3189. # idebug break
  3190. #
  3191. # Creates a breakpoint within a procedure. This will only trigger
  3192. # if idebug is on and the id matches the pattern. If so, TkCon will
  3193. # pop to the front with the prompt changed to an idebug prompt. You
  3194. # are given the basic ability to observe the call stack an query/set
  3195. # variables or execute Tcl commands at any level. A separate history
  3196. # is maintained in debugging mode.
  3197. #
  3198. # idebug echo|{echo ?id?} ?args?
  3199. #
  3200. # Behaves just like "echo", but only triggers when idebug is on.
  3201. # You can specify an optional id to further restrict triggering.
  3202. # If no id is specified, it defaults to the name of the command
  3203. # in which the call was made.
  3204. #
  3205. # idebug id ?id?
  3206. #
  3207. # Query or set the idebug id. This id is used by other idebug
  3208. # methods to determine if they should trigger or not. The idebug
  3209. # id can be a glob pattern and defaults to *.
  3210. #
  3211. # idebug off
  3212. #
  3213. # Turns idebug off.
  3214. #
  3215. # idebug on ?id?
  3216. #
  3217. # Turns idebug on. If 'id' is specified, it sets the id to it.
  3218. #
  3219. # idebug puts|{puts ?id?} args
  3220. #
  3221. # Behaves just like "puts", but only triggers when idebug is on.
  3222. # You can specify an optional id to further restrict triggering.
  3223. # If no id is specified, it defaults to the name of the command
  3224. # in which the call was made.
  3225. #
  3226. # idebug show type ?level? ?VERBOSE?
  3227. #
  3228. # 'type' must be one of vars, locals or globals. This method
  3229. # will output the variables/locals/globals present in a particular
  3230. # level. If VERBOSE is added, then it actually 'dump's out the
  3231. # values as well. 'level' defaults to the level in which this
  3232. # method was called.
  3233. #
  3234. # idebug trace ?level?
  3235. #
  3236. # Prints out the stack trace from the specified level up to the top
  3237. # level. 'level' defaults to the current level.
  3238. #
  3239. ##
  3240. proc idebug {opt args} {
  3241. global IDEBUG
  3242. if {![info exists IDEBUG(on)]} {
  3243. array set IDEBUG { on 0 id * debugging 0 }
  3244. }
  3245. set level [expr {[info level]-1}]
  3246. switch -glob -- $opt {
  3247. on {
  3248. if {[llength $args]} { set IDEBUG(id) $args }
  3249. return [set IDEBUG(on) 1]
  3250. }
  3251. off { return [set IDEBUG(on) 0] }
  3252. id {
  3253. if {![llength $args]} {
  3254. return $IDEBUG(id)
  3255. } else { return [set IDEBUG(id) $args] }
  3256. }
  3257. break {
  3258. if {!$IDEBUG(on) || $IDEBUG(debugging) || \
  3259. ([llength $args] && \
  3260. ![string match $IDEBUG(id) $args]) || [info level]<1} {
  3261. return
  3262. }
  3263. set IDEBUG(debugging) 1
  3264. puts stderr "idebug at level \#$level: [lindex [info level -1] 0]"
  3265. set tkcon [llength [info command tkcon]]
  3266. if {$tkcon} {
  3267. tkcon master eval set ::tkcon::OPT(prompt2) \$::tkcon::OPT(prompt1)
  3268. tkcon master eval set ::tkcon::OPT(prompt1) \$::tkcon::OPT(debugPrompt)
  3269. set slave [tkcon set ::tkcon::OPT(exec)]
  3270. set event [tkcon set ::tkcon::PRIV(event)]
  3271. tkcon set ::tkcon::OPT(exec) [tkcon master interp create debugger]
  3272. tkcon set ::tkcon::PRIV(event) 1
  3273. }
  3274. set max $level
  3275. while 1 {
  3276. set err {}
  3277. if {$tkcon} {
  3278. # tkcon's overload of gets is advanced enough to not need
  3279. # this, but we get a little better control this way.
  3280. tkcon evalSlave set level $level
  3281. tkcon prompt
  3282. set line [tkcon getcommand]
  3283. tkcon console mark set output end
  3284. } else {
  3285. puts -nonewline stderr "(level \#$level) debug > "
  3286. gets stdin line
  3287. while {![info complete $line]} {
  3288. puts -nonewline "> "
  3289. append line "\n[gets stdin]"
  3290. }
  3291. }
  3292. if {[string match {} $line]} continue
  3293. set key [lindex $line 0]
  3294. if {![regexp {^([#-]?[0-9]+)} [lreplace $line 0 0] lvl]} {
  3295. set lvl \#$level
  3296. }
  3297. set res {}; set c 0
  3298. switch -- $key {
  3299. + {
  3300. ## Allow for jumping multiple levels
  3301. if {$level < $max} {
  3302. idebug trace [incr level] $level 0 VERBOSE
  3303. }
  3304. }
  3305. - {
  3306. ## Allow for jumping multiple levels
  3307. if {$level > 1} {
  3308. idebug trace [incr level -1] $level 0 VERBOSE
  3309. }
  3310. }
  3311. . { set c [catch {idebug trace $level $level 0 VERBOSE} res] }
  3312. v { set c [catch {idebug show vars $lvl } res] }
  3313. V { set c [catch {idebug show vars $lvl VERBOSE} res] }
  3314. l { set c [catch {idebug show locals $lvl } res] }
  3315. L { set c [catch {idebug show locals $lvl VERBOSE} res] }
  3316. g { set c [catch {idebug show globals $lvl } res] }
  3317. G { set c [catch {idebug show globals $lvl VERBOSE} res] }
  3318. t { set c [catch {idebug trace 1 $max $level } res] }
  3319. T { set c [catch {idebug trace 1 $max $level VERBOSE} res]}
  3320. b { set c [catch {idebug body $lvl} res] }
  3321. o { set res [set IDEBUG(on) [expr {!$IDEBUG(on)}]] }
  3322. h - ? {
  3323. puts stderr " + Move down in call stack
  3324. - Move up in call stack
  3325. . Show current proc name and params
  3326. v Show names of variables currently in scope
  3327. V Show names of variables currently in scope with values
  3328. l Show names of local (transient) variables
  3329. L Show names of local (transient) variables with values
  3330. g Show names of declared global variables
  3331. G Show names of declared global variables with values
  3332. t Show a stack trace
  3333. T Show a verbose stack trace
  3334. b Show body of current proc
  3335. o Toggle on/off any further debugging
  3336. c,q Continue regular execution (Quit debugger)
  3337. h,? Print this help
  3338. default Evaluate line at current level (\#$level)"
  3339. }
  3340. c - q break
  3341. default { set c [catch {uplevel \#$level $line} res] }
  3342. }
  3343. if {$tkcon} {
  3344. tkcon set ::tkcon::PRIV(event) \
  3345. [tkcon evalSlave eval history add [list $line]\
  3346. \; history nextid]
  3347. }
  3348. if {$c} {
  3349. puts stderr $res
  3350. } elseif {[string compare {} $res]} {
  3351. puts $res
  3352. }
  3353. }
  3354. set IDEBUG(debugging) 0
  3355. if {$tkcon} {
  3356. tkcon master interp delete debugger
  3357. tkcon master eval set ::tkcon::OPT(prompt1) \$::tkcon::OPT(prompt2)
  3358. tkcon set ::tkcon::OPT(exec) $slave
  3359. tkcon set ::tkcon::PRIV(event) $event
  3360. tkcon prompt
  3361. }
  3362. }
  3363. bo* {
  3364. if {[regexp {^([#-]?[0-9]+)} $args level]} {
  3365. return [uplevel $level {dump c -no [lindex [info level 0] 0]}]
  3366. }
  3367. }
  3368. t* {
  3369. if {[llength $args]<2} return
  3370. set min [set max [set lvl $level]]
  3371. set exp {^#?([0-9]+)? ?#?([0-9]+) ?#?([0-9]+)? ?(VERBOSE)?}
  3372. if {![regexp $exp $args junk min max lvl verbose]} return
  3373. for {set i $max} {
  3374. $i>=$min && ![catch {uplevel \#$i info level 0} info]
  3375. } {incr i -1} {
  3376. if {$i==$lvl} {
  3377. puts -nonewline stderr "* \#$i:\t"
  3378. } else {
  3379. puts -nonewline stderr " \#$i:\t"
  3380. }
  3381. set name [lindex $info 0]
  3382. if {[string compare VERBOSE $verbose] || \
  3383. ![llength [info procs $name]]} {
  3384. puts $info
  3385. } else {
  3386. puts "proc $name {[info args $name]} { ... }"
  3387. set idx 0
  3388. foreach arg [info args $name] {
  3389. if {[string match args $arg]} {
  3390. puts "\t$arg = [lrange $info [incr idx] end]"
  3391. break
  3392. } else {
  3393. puts "\t$arg = [lindex $info [incr idx]]"
  3394. }
  3395. }
  3396. }
  3397. }
  3398. }
  3399. s* {
  3400. #var, local, global
  3401. set level \#$level
  3402. if {![regexp {^([vgl][^ ]*) ?([#-]?[0-9]+)? ?(VERBOSE)?} \
  3403. $args junk type level verbose]} return
  3404. switch -glob -- $type {
  3405. v* { set vars [uplevel $level {lsort [info vars]}] }
  3406. l* { set vars [uplevel $level {lsort [info locals]}] }
  3407. g* { set vars [lremove [uplevel $level {info vars}] \
  3408. [uplevel $level {info locals}]] }
  3409. }
  3410. if {[string match VERBOSE $verbose]} {
  3411. return [uplevel $level dump var -nocomplain $vars]
  3412. } else {
  3413. return $vars
  3414. }
  3415. }
  3416. e* - pu* {
  3417. if {[llength $opt]==1 && [catch {lindex [info level -1] 0} id]} {
  3418. set id [lindex [info level 0] 0]
  3419. } else {
  3420. set id [lindex $opt 1]
  3421. }
  3422. if {$IDEBUG(on) && [string match $IDEBUG(id) $id]} {
  3423. if {[string match e* $opt]} {
  3424. puts [concat $args]
  3425. } else { eval puts $args }
  3426. }
  3427. }
  3428. default {
  3429. return -code error "bad [lindex [info level 0] 0] option \"$opt\",\
  3430. must be: [join [lsort [list on off id break print body\
  3431. trace show puts echo]] {, }]"
  3432. }
  3433. }
  3434. }
  3435. ## observe - like trace, but not
  3436. # ARGS: opt - option
  3437. # name - name of variable or command
  3438. ##
  3439. proc observe {opt name args} {
  3440. global tcl_observe
  3441. switch -glob -- $opt {
  3442. co* {
  3443. if {[regexp {^(catch|lreplace|set|puts|for|incr|info|uplevel)$} \
  3444. $name]} {
  3445. return -code error "cannot observe \"$name\":\
  3446. infinite eval loop will occur"
  3447. }
  3448. set old ${name}@
  3449. while {[llength [info command $old]]} { append old @ }
  3450. rename $name $old
  3451. set max 4
  3452. regexp {^[0-9]+} $args max
  3453. ## idebug trace could be used here
  3454. proc $name args "
  3455. for {set i \[info level\]; set max \[expr \[info level\]-$max\]} {
  3456. \$i>=\$max && !\[catch {uplevel \#\$i info level 0} info\]
  3457. } {incr i -1} {
  3458. puts -nonewline stderr \" \#\$i:\t\"
  3459. puts \$info
  3460. }
  3461. uplevel \[lreplace \[info level 0\] 0 0 $old\]
  3462. "
  3463. set tcl_observe($name) $old
  3464. }
  3465. cd* {
  3466. if {[info exists tcl_observe($name)] && [catch {
  3467. rename $name {}
  3468. rename $tcl_observe($name) $name
  3469. unset tcl_observe($name)
  3470. } err]} { return -code error $err }
  3471. }
  3472. ci* {
  3473. ## What a useless method...
  3474. if {[info exists tcl_observe($name)]} {
  3475. set i $tcl_observe($name)
  3476. set res "\"$name\" observes true command \"$i\""
  3477. while {[info exists tcl_observe($i)]} {
  3478. append res "\n\"$name\" observes true command \"$i\""
  3479. set i $tcl_observe($name)
  3480. }
  3481. return $res
  3482. }
  3483. }
  3484. va* - vd* {
  3485. set type [lindex $args 0]
  3486. set args [lrange $args 1 end]
  3487. if {![regexp {^[rwu]} $type type]} {
  3488. return -code error "bad [lindex [info level 0] 0] $opt type\
  3489. \"$type\", must be: read, write or unset"
  3490. }
  3491. if {![llength $args]} { set args observe_var }
  3492. foreach c [uplevel 1 [list trace vinfo $name]] {
  3493. # don't double up on the traces
  3494. if {[string equal [list $type $args] $c]} { return }
  3495. }
  3496. uplevel 1 [list trace $opt $name $type $args]
  3497. }
  3498. vi* {
  3499. uplevel 1 [list trace vinfo $name]
  3500. }
  3501. default {
  3502. return -code error "bad [lindex [info level 0] 0] option\
  3503. \"[lindex $args 0]\", must be: [join [lsort \
  3504. [list command cdelete cinfo variable vdelete vinfo]] {, }]"
  3505. }
  3506. }
  3507. }
  3508. ## observe_var - auxilary function for observing vars, called by trace
  3509. ## via observe
  3510. # ARGS: name - variable name
  3511. # el - array element name, if any
  3512. # op - operation type (rwu)
  3513. ##
  3514. proc observe_var {name el op} {
  3515. if {[string match u $op]} {
  3516. if {[string compare {} $el]} {
  3517. puts "unset \"${name}($el)\""
  3518. } else {
  3519. puts "unset \"$name\""
  3520. }
  3521. } else {
  3522. upvar 1 $name $name
  3523. if {[info exists ${name}($el)]} {
  3524. puts [dump v ${name}($el)]
  3525. } else {
  3526. puts [dump v $name]
  3527. }
  3528. }
  3529. }
  3530. ## which - tells you where a command is found
  3531. # ARGS: cmd - command name
  3532. # Returns: where command is found (internal / external / unknown)
  3533. ##
  3534. proc which cmd {
  3535. ## This tries to auto-load a command if not recognized
  3536. set types [uplevel 1 [list what $cmd 1]]
  3537. if {[llength $types]} {
  3538. set out {}
  3539. foreach type $types {
  3540. switch -- $type {
  3541. alias { set res "$cmd: aliased to [alias $cmd]" }
  3542. procedure { set res "$cmd: procedure" }
  3543. command { set res "$cmd: internal command" }
  3544. executable { lappend out [auto_execok $cmd] }
  3545. variable { lappend out "$cmd: $type" }
  3546. }
  3547. if {[info exists res]} {
  3548. global auto_index
  3549. if {[info exists auto_index($cmd)]} {
  3550. ## This tells you where the command MIGHT have come from -
  3551. ## not true if the command was redefined interactively or
  3552. ## existed before it had to be auto_loaded. This is just
  3553. ## provided as a hint at where it MAY have come from
  3554. append res " ($auto_index($cmd))"
  3555. }
  3556. lappend out $res
  3557. unset res
  3558. }
  3559. }
  3560. return [join $out \n]
  3561. } else {
  3562. return -code error "$cmd: command not found"
  3563. }
  3564. }
  3565. ## what - tells you what a string is recognized as
  3566. # ARGS: str - string to id
  3567. # Returns: id types of command as list
  3568. ##
  3569. proc what {str {autoload 0}} {
  3570. set types {}
  3571. if {[llength [info commands $str]] || ($autoload && \
  3572. [auto_load $str] && [llength [info commands $str]])} {
  3573. if {[lsearch -exact [interp aliases] $str] > -1} {
  3574. lappend types "alias"
  3575. } elseif {
  3576. [llength [info procs $str]] ||
  3577. ([string match *::* $str] &&
  3578. [llength [namespace eval [namespace qualifier $str] \
  3579. info procs [namespace tail $str]]])
  3580. } {
  3581. lappend types "procedure"
  3582. } else {
  3583. lappend types "command"
  3584. }
  3585. }
  3586. if {[llength [uplevel 1 info vars $str]]} {
  3587. upvar 1 $str var
  3588. if {[array exists var]} {
  3589. lappend types array variable
  3590. } else {
  3591. lappend types scalar variable
  3592. }
  3593. }
  3594. if {[file isdirectory $str]} {
  3595. lappend types "directory"
  3596. }
  3597. if {[file isfile $str]} {
  3598. lappend types "file"
  3599. }
  3600. if {[llength [info commands winfo]] && [winfo exists $str]} {
  3601. lappend types "widget"
  3602. }
  3603. if {[string compare {} [auto_execok $str]]} {
  3604. lappend types "executable"
  3605. }
  3606. return $types
  3607. }
  3608. ## dir - directory list
  3609. # ARGS: args - names/glob patterns of directories to list
  3610. # OPTS: -all - list hidden files as well (Unix dot files)
  3611. # -long - list in full format "permissions size date filename"
  3612. # -full - displays / after directories and link paths for links
  3613. # Returns: a directory listing
  3614. ##
  3615. proc dir {args} {
  3616. array set s {
  3617. all 0 full 0 long 0
  3618. 0 --- 1 --x 2 -w- 3 -wx 4 r-- 5 r-x 6 rw- 7 rwx
  3619. }
  3620. while {[string match \-* [lindex $args 0]]} {
  3621. set str [lindex $args 0]
  3622. set args [lreplace $args 0 0]
  3623. switch -glob -- $str {
  3624. -a* {set s(all) 1} -f* {set s(full) 1}
  3625. -l* {set s(long) 1} -- break
  3626. default {
  3627. return -code error "unknown option \"$str\",\
  3628. should be one of: -all, -full, -long"
  3629. }
  3630. }
  3631. }
  3632. set sep [string trim [file join . .] .]
  3633. if {![llength $args]} { set args . }
  3634. if {$::tcl_version >= 8.3} {
  3635. # Newer glob args allow safer dir processing. The user may still
  3636. # want glob chars, but really only for file matching.
  3637. foreach arg $args {
  3638. if {[file isdirectory $arg]} {
  3639. if {$s(all)} {
  3640. lappend out [list $arg [lsort \
  3641. [glob -nocomplain -directory $arg .* *]]]
  3642. } else {
  3643. lappend out [list $arg [lsort \
  3644. [glob -nocomplain -directory $arg *]]]
  3645. }
  3646. } else {
  3647. set dir [file dirname $arg]
  3648. lappend out [list $dir$sep [lsort \
  3649. [glob -nocomplain -directory $dir [file tail $arg]]]]
  3650. }
  3651. }
  3652. } else {
  3653. foreach arg $args {
  3654. if {[file isdirectory $arg]} {
  3655. set arg [string trimright $arg $sep]$sep
  3656. if {$s(all)} {
  3657. lappend out [list $arg [lsort [glob -nocomplain -- $arg.* $arg*]]]
  3658. } else {
  3659. lappend out [list $arg [lsort [glob -nocomplain -- $arg*]]]
  3660. }
  3661. } else {
  3662. lappend out [list [file dirname $arg]$sep \
  3663. [lsort [glob -nocomplain -- $arg]]]
  3664. }
  3665. }
  3666. }
  3667. if {$s(long)} {
  3668. set old [clock scan {1 year ago}]
  3669. set fmt "%s%9d %s %s\n"
  3670. foreach o $out {
  3671. set d [lindex $o 0]
  3672. append res $d:\n
  3673. foreach f [lindex $o 1] {
  3674. file lstat $f st
  3675. set f [file tail $f]
  3676. if {$s(full)} {
  3677. switch -glob $st(type) {
  3678. d* { append f $sep }
  3679. l* { append f "@ -> [file readlink $d$sep$f]" }
  3680. default { if {[file exec $d$sep$f]} { append f * } }
  3681. }
  3682. }
  3683. if {[string match file $st(type)]} {
  3684. set mode -
  3685. } else {
  3686. set mode [string index $st(type) 0]
  3687. }
  3688. foreach j [split [format %03o [expr {$st(mode)&0777}]] {}] {
  3689. append mode $s($j)
  3690. }
  3691. if {$st(mtime)>$old} {
  3692. set cfmt {%b %d %H:%M}
  3693. } else {
  3694. set cfmt {%b %d %Y}
  3695. }
  3696. append res [format $fmt $mode $st(size) \
  3697. [clock format $st(mtime) -format $cfmt] $f]
  3698. }
  3699. append res \n
  3700. }
  3701. } else {
  3702. foreach o $out {
  3703. set d [lindex $o 0]
  3704. append res "$d:\n"
  3705. set i 0
  3706. foreach f [lindex $o 1] {
  3707. if {[string len [file tail $f]] > $i} {
  3708. set i [string len [file tail $f]]
  3709. }
  3710. }
  3711. set i [expr {$i+2+$s(full)}]
  3712. set j 80
  3713. ## This gets the number of cols in the tkcon console widget
  3714. if {[llength [info commands tkcon]]} {
  3715. set j [expr {[tkcon master set ::tkcon::OPT(cols)]/$i}]
  3716. }
  3717. set k 0
  3718. foreach f [lindex $o 1] {
  3719. set f [file tail $f]
  3720. if {$s(full)} {
  3721. switch -glob [file type $d$sep$f] {
  3722. d* { append f $sep }
  3723. l* { append f @ }
  3724. default { if {[file exec $d$sep$f]} { append f * } }
  3725. }
  3726. }
  3727. append res [format "%-${i}s" $f]
  3728. if {$j == 0 || [incr k]%$j == 0} {
  3729. set res [string trimright $res]\n
  3730. }
  3731. }
  3732. append res \n\n
  3733. }
  3734. }
  3735. return [string trimright $res]
  3736. }
  3737. interp alias {} ::ls {} ::dir -full
  3738. ## lremove - remove items from a list
  3739. # OPTS:
  3740. # -all remove all instances of each item
  3741. # -glob remove all instances matching glob pattern
  3742. # -regexp remove all instances matching regexp pattern
  3743. # ARGS: l a list to remove items from
  3744. # args items to remove (these are 'join'ed together)
  3745. ##
  3746. proc lremove {args} {
  3747. array set opts {-all 0 pattern -exact}
  3748. while {[string match -* [lindex $args 0]]} {
  3749. switch -glob -- [lindex $args 0] {
  3750. -a* { set opts(-all) 1 }
  3751. -g* { set opts(pattern) -glob }
  3752. -r* { set opts(pattern) -regexp }
  3753. -- { set args [lreplace $args 0 0]; break }
  3754. default {return -code error "unknown option \"[lindex $args 0]\""}
  3755. }
  3756. set args [lreplace $args 0 0]
  3757. }
  3758. set l [lindex $args 0]
  3759. foreach i [join [lreplace $args 0 0]] {
  3760. if {[set ix [lsearch $opts(pattern) $l $i]] == -1} continue
  3761. set l [lreplace $l $ix $ix]
  3762. if {$opts(-all)} {
  3763. while {[set ix [lsearch $opts(pattern) $l $i]] != -1} {
  3764. set l [lreplace $l $ix $ix]
  3765. }
  3766. }
  3767. }
  3768. return $l
  3769. }
  3770. if {!$::tkcon::PRIV(WWW)} {;
  3771. ## Unknown changed to get output into tkcon window
  3772. # unknown:
  3773. # Invoked automatically whenever an unknown command is encountered.
  3774. # Works through a list of "unknown handlers" that have been registered
  3775. # to deal with unknown commands. Extensions can integrate their own
  3776. # handlers into the 'unknown' facility via 'unknown_handler'.
  3777. #
  3778. # If a handler exists that recognizes the command, then it will
  3779. # take care of the command action and return a valid result or a
  3780. # Tcl error. Otherwise, it should return "-code continue" (=2)
  3781. # and responsibility for the command is passed to the next handler.
  3782. #
  3783. # Arguments:
  3784. # args - A list whose elements are the words of the original
  3785. # command, including the command name.
  3786. proc unknown args {
  3787. global unknown_handler_order unknown_handlers errorInfo errorCode
  3788. #
  3789. # Be careful to save error info now, and restore it later
  3790. # for each handler. Some handlers generate their own errors
  3791. # and disrupt handling.
  3792. #
  3793. set savedErrorCode $errorCode
  3794. set savedErrorInfo $errorInfo
  3795. if {![info exists unknown_handler_order] || \
  3796. ![info exists unknown_handlers]} {
  3797. set unknown_handlers(tcl) tcl_unknown
  3798. set unknown_handler_order tcl
  3799. }
  3800. foreach handler $unknown_handler_order {
  3801. set status [catch {uplevel 1 $unknown_handlers($handler) $args} result]
  3802. if {$status == 1} {
  3803. #
  3804. # Strip the last five lines off the error stack (they're
  3805. # from the "uplevel" command).
  3806. #
  3807. set new [split $errorInfo \n]
  3808. set new [join [lrange $new 0 [expr {[llength $new]-6}]] \n]
  3809. return -code $status -errorcode $errorCode \
  3810. -errorinfo $new $result
  3811. } elseif {$status != 4} {
  3812. return -code $status $result
  3813. }
  3814. set errorCode $savedErrorCode
  3815. set errorInfo $savedErrorInfo
  3816. }
  3817. set name [lindex $args 0]
  3818. return -code error "invalid command name \"$name\""
  3819. }
  3820. # tcl_unknown:
  3821. # Invoked when a Tcl command is invoked that doesn't exist in the
  3822. # interpreter:
  3823. #
  3824. # 1. See if the autoload facility can locate the command in a
  3825. # Tcl script file. If so, load it and execute it.
  3826. # 2. If the command was invoked interactively at top-level:
  3827. # (a) see if the command exists as an executable UNIX program.
  3828. # If so, "exec" the command.
  3829. # (b) see if the command requests csh-like history substitution
  3830. # in one of the common forms !!, !<number>, or ^old^new. If
  3831. # so, emulate csh's history substitution.
  3832. # (c) see if the command is a unique abbreviation for another
  3833. # command. If so, invoke the command.
  3834. #
  3835. # Arguments:
  3836. # args - A list whose elements are the words of the original
  3837. # command, including the command name.
  3838. proc tcl_unknown args {
  3839. global auto_noexec auto_noload env unknown_pending tcl_interactive
  3840. global errorCode errorInfo
  3841. # If the command word has the form "namespace inscope ns cmd"
  3842. # then concatenate its arguments onto the end and evaluate it.
  3843. set cmd [lindex $args 0]
  3844. if {[regexp "^:*namespace\[ \t\n\]+inscope" $cmd] \
  3845. && [llength $cmd] == 4} {
  3846. set arglist [lrange $args 1 end]
  3847. set ret [catch {uplevel 1 $cmd $arglist} result]
  3848. if {$ret == 0} {
  3849. return $result
  3850. } else {
  3851. return -code $ret -errorcode $errorCode $result
  3852. }
  3853. }
  3854. # Save the values of errorCode and errorInfo variables, since they
  3855. # may get modified if caught errors occur below. The variables will
  3856. # be restored just before re-executing the missing command.
  3857. set savedErrorCode $errorCode
  3858. set savedErrorInfo $errorInfo
  3859. set name [lindex $args 0]
  3860. if {![info exists auto_noload]} {
  3861. #
  3862. # Make sure we're not trying to load the same proc twice.
  3863. #
  3864. if {[info exists unknown_pending($name)]} {
  3865. return -code error "self-referential recursion in \"unknown\" for command \"$name\""
  3866. }
  3867. set unknown_pending($name) pending
  3868. if {[llength [info args auto_load]]==1} {
  3869. set ret [catch {auto_load $name} msg]
  3870. } else {
  3871. set ret [catch {auto_load $name [uplevel 1 {namespace current}]} msg]
  3872. }
  3873. unset unknown_pending($name)
  3874. if {$ret} {
  3875. return -code $ret -errorcode $errorCode \
  3876. "error while autoloading \"$name\": $msg"
  3877. }
  3878. if {![array size unknown_pending]} { unset unknown_pending }
  3879. if {$msg} {
  3880. set errorCode $savedErrorCode
  3881. set errorInfo $savedErrorInfo
  3882. set code [catch {uplevel 1 $args} msg]
  3883. if {$code == 1} {
  3884. #
  3885. # Strip the last five lines off the error stack (they're
  3886. # from the "uplevel" command).
  3887. #
  3888. set new [split $errorInfo \n]
  3889. set new [join [lrange $new 0 [expr {[llength $new]-6}]] \n]
  3890. return -code error -errorcode $errorCode \
  3891. -errorinfo $new $msg
  3892. } else {
  3893. return -code $code $msg
  3894. }
  3895. }
  3896. }
  3897. if {[info level] == 1 && [string match {} [info script]] \
  3898. && [info exists tcl_interactive] && $tcl_interactive} {
  3899. if {![info exists auto_noexec]} {
  3900. set new [auto_execok $name]
  3901. if {[string compare {} $new]} {
  3902. set errorCode $savedErrorCode
  3903. set errorInfo $savedErrorInfo
  3904. return [uplevel 1 exec $new [lrange $args 1 end]]
  3905. #return [uplevel exec >&@stdout <@stdin $new [lrange $args 1 end]]
  3906. }
  3907. }
  3908. set errorCode $savedErrorCode
  3909. set errorInfo $savedErrorInfo
  3910. ##
  3911. ## History substitution moved into ::tkcon::EvalCmd
  3912. ##
  3913. if {[string compare $name "::"] == 0} {
  3914. set name ""
  3915. }
  3916. if {$ret != 0} {
  3917. return -code $ret -errorcode $errorCode \
  3918. "error in unknown while checking if \"$name\" is a unique command abbreviation: $msg"
  3919. }
  3920. set cmds [info commands $name*]
  3921. if {[llength $cmds] == 1} {
  3922. return [uplevel 1 [lreplace $args 0 0 $cmds]]
  3923. }
  3924. if {[llength $cmds]} {
  3925. if {$name == ""} {
  3926. return -code error "empty command name \"\""
  3927. } else {
  3928. return -code error \
  3929. "ambiguous command name \"$name\": [lsort $cmds]"
  3930. }
  3931. }
  3932. ## We've got nothing so far
  3933. ## Check and see if Tk wasn't loaded, but it appears to be a Tk cmd
  3934. if {![uplevel \#0 info exists tk_version]} {
  3935. lappend tkcmds bell bind bindtags button \
  3936. canvas checkbutton clipboard destroy \
  3937. entry event focus font frame grab grid image \
  3938. label labelframe listbox lower menu menubutton message \
  3939. option pack panedwindow place radiobutton raise \
  3940. scale scrollbar selection send spinbox \
  3941. text tk tkwait toplevel winfo wm
  3942. if {[lsearch -exact $tkcmds $name] >= 0 && \
  3943. [tkcon master tk_messageBox -icon question -parent . \
  3944. -title "Load Tk?" -type retrycancel -default retry \
  3945. -message "This appears to be a Tk command, but Tk\
  3946. has not yet been loaded. Shall I retry the command\
  3947. with loading Tk first?"] == "retry"} {
  3948. return [uplevel 1 "load {} Tk; $args"]
  3949. }
  3950. }
  3951. }
  3952. return -code continue
  3953. }
  3954. } ; # end exclusionary code for WWW
  3955. proc ::tkcon::Bindings {} {
  3956. variable PRIV
  3957. global tcl_platform tk_version
  3958. #-----------------------------------------------------------------------
  3959. # Elements of tkPriv that are used in this file:
  3960. #
  3961. # char - Character position on the line; kept in order
  3962. # to allow moving up or down past short lines while
  3963. # still remembering the desired position.
  3964. # mouseMoved - Non-zero means the mouse has moved a significant
  3965. # amount since the button went down (so, for example,
  3966. # start dragging out a selection).
  3967. # prevPos - Used when moving up or down lines via the keyboard.
  3968. # Keeps track of the previous insert position, so
  3969. # we can distinguish a series of ups and downs, all
  3970. # in a row, from a new up or down.
  3971. # selectMode - The style of selection currently underway:
  3972. # char, word, or line.
  3973. # x, y - Last known mouse coordinates for scanning
  3974. # and auto-scanning.
  3975. #-----------------------------------------------------------------------
  3976. switch -glob $tcl_platform(platform) {
  3977. win* { set PRIV(meta) Alt }
  3978. mac* { set PRIV(meta) Command }
  3979. default { set PRIV(meta) Meta }
  3980. }
  3981. ## Get all Text bindings into TkConsole
  3982. foreach ev [bind Text] { bind TkConsole $ev [bind Text $ev] }
  3983. ## We really didn't want the newline insertion
  3984. bind TkConsole <Control-Key-o> {}
  3985. ## Now make all our virtual event bindings
  3986. foreach {ev key} [subst -nocommand -noback {
  3987. <<TkCon_Exit>> <Control-q>
  3988. <<TkCon_New>> <Control-N>
  3989. <<TkCon_Close>> <Control-w>
  3990. <<TkCon_About>> <Control-A>
  3991. <<TkCon_Help>> <Control-H>
  3992. <<TkCon_Find>> <Control-F>
  3993. <<TkCon_Slave>> <Control-Key-1>
  3994. <<TkCon_Master>> <Control-Key-2>
  3995. <<TkCon_Main>> <Control-Key-3>
  3996. <<TkCon_Expand>> <Key-Tab>
  3997. <<TkCon_ExpandFile>> <Key-Escape>
  3998. <<TkCon_ExpandProc>> <Control-P>
  3999. <<TkCon_ExpandVar>> <Control-V>
  4000. <<TkCon_Tab>> <Control-i>
  4001. <<TkCon_Tab>> <$PRIV(meta)-i>
  4002. <<TkCon_Newline>> <Control-o>
  4003. <<TkCon_Newline>> <$PRIV(meta)-o>
  4004. <<TkCon_Newline>> <Control-Key-Return>
  4005. <<TkCon_Newline>> <Control-Key-KP_Enter>
  4006. <<TkCon_Eval>> <Return>
  4007. <<TkCon_Eval>> <KP_Enter>
  4008. <<TkCon_Clear>> <Control-l>
  4009. <<TkCon_Previous>> <Up>
  4010. <<TkCon_PreviousImmediate>> <Control-p>
  4011. <<TkCon_PreviousSearch>> <Control-r>
  4012. <<TkCon_Next>> <Down>
  4013. <<TkCon_NextImmediate>> <Control-n>
  4014. <<TkCon_NextSearch>> <Control-s>
  4015. <<TkCon_Transpose>> <Control-t>
  4016. <<TkCon_ClearLine>> <Control-u>
  4017. <<TkCon_SaveCommand>> <Control-z>
  4018. <<TkCon_Popup>> <Button-3>
  4019. }] {
  4020. event add $ev $key
  4021. ## Make sure the specific key won't be defined
  4022. bind TkConsole $key {}
  4023. }
  4024. ## Make the ROOT bindings
  4025. bind $PRIV(root) <<TkCon_Exit>> exit
  4026. bind $PRIV(root) <<TkCon_New>> { ::tkcon::New }
  4027. bind $PRIV(root) <<TkCon_Close>> { ::tkcon::Destroy }
  4028. bind $PRIV(root) <<TkCon_About>> { ::tkcon::About }
  4029. bind $PRIV(root) <<TkCon_Help>> { ::tkcon::Help }
  4030. bind $PRIV(root) <<TkCon_Find>> { ::tkcon::FindBox $::tkcon::PRIV(console) }
  4031. bind $PRIV(root) <<TkCon_Slave>> {
  4032. ::tkcon::Attach {}
  4033. ::tkcon::Prompt "\n" [::tkcon::CmdGet $::tkcon::PRIV(console)]
  4034. }
  4035. bind $PRIV(root) <<TkCon_Master>> {
  4036. if {[string compare {} $::tkcon::PRIV(name)]} {
  4037. ::tkcon::Attach $::tkcon::PRIV(name)
  4038. } else {
  4039. ::tkcon::Attach Main
  4040. }
  4041. ::tkcon::Prompt "\n" [::tkcon::CmdGet $::tkcon::PRIV(console)]
  4042. }
  4043. bind $PRIV(root) <<TkCon_Main>> {
  4044. ::tkcon::Attach Main
  4045. ::tkcon::Prompt "\n" [::tkcon::CmdGet $::tkcon::PRIV(console)]
  4046. }
  4047. bind $PRIV(root) <<TkCon_Popup>> {
  4048. ::tkcon::PopupMenu %X %Y
  4049. }
  4050. ## Menu items need null TkConsolePost bindings to avoid the TagProc
  4051. ##
  4052. foreach ev [bind $PRIV(root)] {
  4053. bind TkConsolePost $ev {
  4054. # empty
  4055. }
  4056. }
  4057. # ::tkcon::ClipboardKeysyms --
  4058. # This procedure is invoked to identify the keys that correspond to
  4059. # the copy, cut, and paste functions for the clipboard.
  4060. #
  4061. # Arguments:
  4062. # copy - Name of the key (keysym name plus modifiers, if any,
  4063. # such as "Meta-y") used for the copy operation.
  4064. # cut - Name of the key used for the cut operation.
  4065. # paste - Name of the key used for the paste operation.
  4066. proc ::tkcon::ClipboardKeysyms {copy cut paste} {
  4067. bind TkConsole <$copy> {::tkcon::Copy %W}
  4068. bind TkConsole <$cut> {::tkcon::Cut %W}
  4069. bind TkConsole <$paste> {::tkcon::Paste %W}
  4070. }
  4071. proc ::tkcon::GetSelection {w} {
  4072. if {
  4073. ![catch {selection get -displayof $w -type UTF8_STRING} txt] ||
  4074. ![catch {selection get -displayof $w} txt] ||
  4075. ![catch {selection get -displayof $w -selection CLIPBOARD} txt]
  4076. } {
  4077. return $txt
  4078. }
  4079. return -code error "could not find default selection"
  4080. }
  4081. proc ::tkcon::Cut w {
  4082. if {[string match $w [selection own -displayof $w]]} {
  4083. clipboard clear -displayof $w
  4084. catch {
  4085. set txt [selection get -displayof $w]
  4086. clipboard append -displayof $w $txt
  4087. if {[$w compare sel.first >= limit]} {
  4088. $w delete sel.first sel.last
  4089. }
  4090. }
  4091. }
  4092. }
  4093. proc ::tkcon::Copy w {
  4094. if {[string match $w [selection own -displayof $w]]} {
  4095. clipboard clear -displayof $w
  4096. catch {
  4097. set txt [selection get -displayof $w]
  4098. clipboard append -displayof $w $txt
  4099. }
  4100. }
  4101. }
  4102. proc ::tkcon::Paste w {
  4103. if {![catch {GetSelection $w} txt]} {
  4104. if {[$w compare insert < limit]} { $w mark set insert end }
  4105. $w insert insert $txt
  4106. $w see insert
  4107. if {[string match *\n* $txt]} { ::tkcon::Eval $w }
  4108. }
  4109. }
  4110. ## Redefine for TkConsole what we need
  4111. ##
  4112. event delete <<Paste>> <Control-V>
  4113. ::tkcon::ClipboardKeysyms <Copy> <Cut> <Paste>
  4114. bind TkConsole <Insert> {
  4115. catch { ::tkcon::Insert %W [::tkcon::GetSelection %W] }
  4116. }
  4117. bind TkConsole <Triple-1> {+
  4118. catch {
  4119. eval %W tag remove sel [%W tag nextrange prompt sel.first sel.last]
  4120. eval %W tag remove sel sel.last-1c
  4121. %W mark set insert sel.first
  4122. }
  4123. }
  4124. ## binding editor needed
  4125. ## binding <events> for .tkconrc
  4126. bind TkConsole <<TkCon_ExpandFile>> {
  4127. if {[%W compare insert > limit]} {::tkcon::Expand %W path}
  4128. break
  4129. }
  4130. bind TkConsole <<TkCon_ExpandProc>> {
  4131. if {[%W compare insert > limit]} {::tkcon::Expand %W proc}
  4132. }
  4133. bind TkConsole <<TkCon_ExpandVar>> {
  4134. if {[%W compare insert > limit]} {::tkcon::Expand %W var}
  4135. }
  4136. bind TkConsole <<TkCon_Expand>> {
  4137. if {[%W compare insert > limit]} {::tkcon::Expand %W}
  4138. }
  4139. bind TkConsole <<TkCon_Tab>> {
  4140. if {[%W compare insert >= limit]} {
  4141. ::tkcon::Insert %W \t
  4142. }
  4143. }
  4144. bind TkConsole <<TkCon_Newline>> {
  4145. if {[%W compare insert >= limit]} {
  4146. ::tkcon::Insert %W \n
  4147. }
  4148. }
  4149. bind TkConsole <<TkCon_Eval>> {
  4150. ::tkcon::Eval %W
  4151. }
  4152. bind TkConsole <Delete> {
  4153. if {[llength [%W tag nextrange sel 1.0 end]] \
  4154. && [%W compare sel.first >= limit]} {
  4155. %W delete sel.first sel.last
  4156. } elseif {[%W compare insert >= limit]} {
  4157. %W delete insert
  4158. %W see insert
  4159. }
  4160. }
  4161. bind TkConsole <BackSpace> {
  4162. if {[llength [%W tag nextrange sel 1.0 end]] \
  4163. && [%W compare sel.first >= limit]} {
  4164. %W delete sel.first sel.last
  4165. } elseif {[%W compare insert != 1.0] && [%W compare insert > limit]} {
  4166. %W delete insert-1c
  4167. %W see insert
  4168. }
  4169. }
  4170. bind TkConsole <Control-h> [bind TkConsole <BackSpace>]
  4171. bind TkConsole <KeyPress> {
  4172. ::tkcon::Insert %W %A
  4173. }
  4174. bind TkConsole <Control-a> {
  4175. if {[%W compare {limit linestart} == {insert linestart}]} {
  4176. tkTextSetCursor %W limit
  4177. } else {
  4178. tkTextSetCursor %W {insert linestart}
  4179. }
  4180. }
  4181. bind TkConsole <Key-Home> [bind TkConsole <Control-a>]
  4182. bind TkConsole <Control-d> {
  4183. if {[%W compare insert < limit]} break
  4184. %W delete insert
  4185. }
  4186. bind TkConsole <Control-k> {
  4187. if {[%W compare insert < limit]} break
  4188. if {[%W compare insert == {insert lineend}]} {
  4189. %W delete insert
  4190. } else {
  4191. %W delete insert {insert lineend}
  4192. }
  4193. }
  4194. bind TkConsole <<TkCon_Clear>> {
  4195. ## Clear console buffer, without losing current command line input
  4196. set ::tkcon::PRIV(tmp) [::tkcon::CmdGet %W]
  4197. clear
  4198. ::tkcon::Prompt {} $::tkcon::PRIV(tmp)
  4199. }
  4200. bind TkConsole <<TkCon_Previous>> {
  4201. if {[%W compare {insert linestart} != {limit linestart}]} {
  4202. tkTextSetCursor %W [tkTextUpDownLine %W -1]
  4203. } else {
  4204. ::tkcon::Event -1
  4205. }
  4206. }
  4207. bind TkConsole <<TkCon_Next>> {
  4208. if {[%W compare {insert linestart} != {end-1c linestart}]} {
  4209. tkTextSetCursor %W [tkTextUpDownLine %W 1]
  4210. } else {
  4211. ::tkcon::Event 1
  4212. }
  4213. }
  4214. bind TkConsole <<TkCon_NextImmediate>> { ::tkcon::Event 1 }
  4215. bind TkConsole <<TkCon_PreviousImmediate>> { ::tkcon::Event -1 }
  4216. bind TkConsole <<TkCon_PreviousSearch>> {
  4217. ::tkcon::Event -1 [::tkcon::CmdGet %W]
  4218. }
  4219. bind TkConsole <<TkCon_NextSearch>> {
  4220. ::tkcon::Event 1 [::tkcon::CmdGet %W]
  4221. }
  4222. bind TkConsole <<TkCon_Transpose>> {
  4223. ## Transpose current and previous chars
  4224. if {[%W compare insert > "limit+1c"]} { tkTextTranspose %W }
  4225. }
  4226. bind TkConsole <<TkCon_ClearLine>> {
  4227. ## Clear command line (Unix shell staple)
  4228. %W delete limit end
  4229. }
  4230. bind TkConsole <<TkCon_SaveCommand>> {
  4231. ## Save command buffer (swaps with current command)
  4232. set ::tkcon::PRIV(tmp) $::tkcon::PRIV(cmdsave)
  4233. set ::tkcon::PRIV(cmdsave) [::tkcon::CmdGet %W]
  4234. if {[string match {} $::tkcon::PRIV(cmdsave)]} {
  4235. set ::tkcon::PRIV(cmdsave) $::tkcon::PRIV(tmp)
  4236. } else {
  4237. %W delete limit end-1c
  4238. }
  4239. ::tkcon::Insert %W $::tkcon::PRIV(tmp)
  4240. %W see end
  4241. }
  4242. catch {bind TkConsole <Key-Page_Up> { tkTextScrollPages %W -1 }}
  4243. catch {bind TkConsole <Key-Prior> { tkTextScrollPages %W -1 }}
  4244. catch {bind TkConsole <Key-Page_Down> { tkTextScrollPages %W 1 }}
  4245. catch {bind TkConsole <Key-Next> { tkTextScrollPages %W 1 }}
  4246. bind TkConsole <$PRIV(meta)-d> {
  4247. if {[%W compare insert >= limit]} {
  4248. %W delete insert {insert wordend}
  4249. }
  4250. }
  4251. bind TkConsole <$PRIV(meta)-BackSpace> {
  4252. if {[%W compare {insert -1c wordstart} >= limit]} {
  4253. %W delete {insert -1c wordstart} insert
  4254. }
  4255. }
  4256. bind TkConsole <$PRIV(meta)-Delete> {
  4257. if {[%W compare insert >= limit]} {
  4258. %W delete insert {insert wordend}
  4259. }
  4260. }
  4261. bind TkConsole <ButtonRelease-2> {
  4262. if {
  4263. (!$tkPriv(mouseMoved) || $tk_strictMotif) &&
  4264. ![catch {::tkcon::GetSelection %W} ::tkcon::PRIV(tmp)]
  4265. } {
  4266. if {[%W compare @%x,%y < limit]} {
  4267. %W insert end $::tkcon::PRIV(tmp)
  4268. } else {
  4269. %W insert @%x,%y $::tkcon::PRIV(tmp)
  4270. }
  4271. if {[string match *\n* $::tkcon::PRIV(tmp)]} {::tkcon::Eval %W}
  4272. }
  4273. }
  4274. ##
  4275. ## End TkConsole bindings
  4276. ##
  4277. ##
  4278. ## Bindings for doing special things based on certain keys
  4279. ##
  4280. bind TkConsolePost <Key-parenright> {
  4281. if {$::tkcon::OPT(lightbrace) && $::tkcon::OPT(blinktime)>99 && \
  4282. [string compare \\ [%W get insert-2c]]} {
  4283. ::tkcon::MatchPair %W \( \) limit
  4284. }
  4285. set ::tkcon::PRIV(StatusCursor) [%W index insert]
  4286. }
  4287. bind TkConsolePost <Key-bracketright> {
  4288. if {$::tkcon::OPT(lightbrace) && $::tkcon::OPT(blinktime)>99 && \
  4289. [string compare \\ [%W get insert-2c]]} {
  4290. ::tkcon::MatchPair %W \[ \] limit
  4291. }
  4292. set ::tkcon::PRIV(StatusCursor) [%W index insert]
  4293. }
  4294. bind TkConsolePost <Key-braceright> {
  4295. if {$::tkcon::OPT(lightbrace) && $::tkcon::OPT(blinktime)>99 && \
  4296. [string compare \\ [%W get insert-2c]]} {
  4297. ::tkcon::MatchPair %W \{ \} limit
  4298. }
  4299. set ::tkcon::PRIV(StatusCursor) [%W index insert]
  4300. }
  4301. bind TkConsolePost <Key-quotedbl> {
  4302. if {$::tkcon::OPT(lightbrace) && $::tkcon::OPT(blinktime)>99 && \
  4303. [string compare \\ [%W get insert-2c]]} {
  4304. ::tkcon::MatchQuote %W limit
  4305. }
  4306. set ::tkcon::PRIV(StatusCursor) [%W index insert]
  4307. }
  4308. bind TkConsolePost <KeyPress> {
  4309. if {$::tkcon::OPT(lightcmd) && [string compare {} %A]} {
  4310. ::tkcon::TagProc %W
  4311. }
  4312. set ::tkcon::PRIV(StatusCursor) [%W index insert]
  4313. }
  4314. bind TkConsolePost <Button-1> {
  4315. set ::tkcon::PRIV(StatusCursor) [%W index insert]
  4316. }
  4317. bind TkConsolePost <B1-Motion> {
  4318. set ::tkcon::PRIV(StatusCursor) [%W index insert]
  4319. }
  4320. }
  4321. ##
  4322. # ::tkcon::PopupMenu - what to do when the popup menu is requested
  4323. ##
  4324. proc ::tkcon::PopupMenu {X Y} {
  4325. variable PRIV
  4326. variable OPT
  4327. set w $PRIV(console)
  4328. if {[string compare $w [winfo containing $X $Y]]} {
  4329. tk_popup $PRIV(popup) $X $Y
  4330. return
  4331. }
  4332. set x [expr {$X-[winfo rootx $w]}]
  4333. set y [expr {$Y-[winfo rooty $w]}]
  4334. if {[llength [set tags [$w tag names @$x,$y]]]} {
  4335. if {[lsearch -exact $tags "proc"] >= 0} {
  4336. lappend type "proc"
  4337. foreach {first last} [$w tag prevrange proc @$x,$y] {
  4338. set word [$w get $first $last]; break
  4339. }
  4340. }
  4341. if {[lsearch -exact $tags "var"] >= 0} {
  4342. lappend type "var"
  4343. foreach {first last} [$w tag prevrange var @$x,$y] {
  4344. set word [$w get $first $last]; break
  4345. }
  4346. }
  4347. }
  4348. if {![info exists type]} {
  4349. set exp "(^|\[^\\\\\]\[ \t\n\r\])"
  4350. set exp2 "\[\[\\\\\\?\\*\]"
  4351. set i [$w search -backwards -regexp $exp @$x,$y "@$x,$y linestart"]
  4352. if {[string compare {} $i]} {
  4353. if {![string match *.0 $i]} {append i +2c}
  4354. if {[string compare {} \
  4355. [set j [$w search -regexp $exp $i "$i lineend"]]]} {
  4356. append j +1c
  4357. } else {
  4358. set j "$i lineend"
  4359. }
  4360. regsub -all $exp2 [$w get $i $j] {\\\0} word
  4361. set word [string trim $word {\"$[]{}',?#*}]
  4362. if {[llength [EvalAttached [list info commands $word]]]} {
  4363. lappend type "proc"
  4364. }
  4365. if {[llength [EvalAttached [list info vars $word]]]} {
  4366. lappend type "var"
  4367. }
  4368. if {[EvalAttached [list file isfile $word]]} {
  4369. lappend type "file"
  4370. }
  4371. }
  4372. }
  4373. if {![info exists type] || ![info exists word]} {
  4374. tk_popup $PRIV(popup) $X $Y
  4375. return
  4376. }
  4377. $PRIV(context) delete 0 end
  4378. $PRIV(context) add command -label "$word" -state disabled
  4379. $PRIV(context) add separator
  4380. set app [Attach]
  4381. if {[lsearch $type proc] != -1} {
  4382. $PRIV(context) add command -label "View Procedure" \
  4383. -command [list $OPT(edit) -attach $app -type proc -- $word]
  4384. }
  4385. if {[lsearch $type var] != -1} {
  4386. $PRIV(context) add command -label "View Variable" \
  4387. -command [list $OPT(edit) -attach $app -type var -- $word]
  4388. }
  4389. if {[lsearch $type file] != -1} {
  4390. $PRIV(context) add command -label "View File" \
  4391. -command [list $OPT(edit) -attach $app -type file -- $word]
  4392. }
  4393. tk_popup $PRIV(context) $X $Y
  4394. }
  4395. ## ::tkcon::TagProc - tags a procedure in the console if it's recognized
  4396. ## This procedure is not perfect. However, making it perfect wastes
  4397. ## too much CPU time...
  4398. ##
  4399. proc ::tkcon::TagProc w {
  4400. set exp "\[^\\\\\]\[\[ \t\n\r\;{}\"\$\]"
  4401. set i [$w search -backwards -regexp $exp insert-1c limit-1c]
  4402. if {[string compare {} $i]} {append i +2c} else {set i limit}
  4403. regsub -all "\[\[\\\\\\?\\*\]" [$w get $i "insert-1c wordend"] {\\\0} c
  4404. if {[llength [EvalAttached [list info commands $c]]]} {
  4405. $w tag add proc $i "insert-1c wordend"
  4406. } else {
  4407. $w tag remove proc $i "insert-1c wordend"
  4408. }
  4409. if {[llength [EvalAttached [list info vars $c]]]} {
  4410. $w tag add var $i "insert-1c wordend"
  4411. } else {
  4412. $w tag remove var $i "insert-1c wordend"
  4413. }
  4414. }
  4415. ## ::tkcon::MatchPair - blinks a matching pair of characters
  4416. ## c2 is assumed to be at the text index 'insert'.
  4417. ## This proc is really loopy and took me an hour to figure out given
  4418. ## all possible combinations with escaping except for escaped \'s.
  4419. ## It doesn't take into account possible commenting... Oh well. If
  4420. ## anyone has something better, I'd like to see/use it. This is really
  4421. ## only efficient for small contexts.
  4422. # ARGS: w - console text widget
  4423. # c1 - first char of pair
  4424. # c2 - second char of pair
  4425. # Calls: ::tkcon::Blink
  4426. ##
  4427. proc ::tkcon::MatchPair {w c1 c2 {lim 1.0}} {
  4428. if {[string compare {} [set ix [$w search -back $c1 insert $lim]]]} {
  4429. while {
  4430. [string match {\\} [$w get $ix-1c]] &&
  4431. [string compare {} [set ix [$w search -back $c1 $ix-1c $lim]]]
  4432. } {}
  4433. set i1 insert-1c
  4434. while {[string compare {} $ix]} {
  4435. set i0 $ix
  4436. set j 0
  4437. while {[string compare {} [set i0 [$w search $c2 $i0 $i1]]]} {
  4438. append i0 +1c
  4439. if {[string match {\\} [$w get $i0-2c]]} continue
  4440. incr j
  4441. }
  4442. if {!$j} break
  4443. set i1 $ix
  4444. while {$j && [string compare {} \
  4445. [set ix [$w search -back $c1 $ix $lim]]]} {
  4446. if {[string match {\\} [$w get $ix-1c]]} continue
  4447. incr j -1
  4448. }
  4449. }
  4450. if {[string match {} $ix]} { set ix [$w index $lim] }
  4451. } else { set ix [$w index $lim] }
  4452. if {$::tkcon::OPT(blinkrange)} {
  4453. Blink $w $ix [$w index insert]
  4454. } else {
  4455. Blink $w $ix $ix+1c [$w index insert-1c] [$w index insert]
  4456. }
  4457. }
  4458. ## ::tkcon::MatchQuote - blinks between matching quotes.
  4459. ## Blinks just the quote if it's unmatched, otherwise blinks quoted string
  4460. ## The quote to match is assumed to be at the text index 'insert'.
  4461. # ARGS: w - console text widget
  4462. # Calls: ::tkcon::Blink
  4463. ##
  4464. proc ::tkcon::MatchQuote {w {lim 1.0}} {
  4465. set i insert-1c
  4466. set j 0
  4467. while {[string compare [set i [$w search -back \" $i $lim]] {}]} {
  4468. if {[string match {\\} [$w get $i-1c]]} continue
  4469. if {!$j} {set i0 $i}
  4470. incr j
  4471. }
  4472. if {$j&1} {
  4473. if {$::tkcon::OPT(blinkrange)} {
  4474. Blink $w $i0 [$w index insert]
  4475. } else {
  4476. Blink $w $i0 $i0+1c [$w index insert-1c] [$w index insert]
  4477. }
  4478. } else {
  4479. Blink $w [$w index insert-1c] [$w index insert]
  4480. }
  4481. }
  4482. ## ::tkcon::Blink - blinks between n index pairs for a specified duration.
  4483. # ARGS: w - console text widget
  4484. # i1 - start index to blink region
  4485. # i2 - end index of blink region
  4486. # dur - duration in usecs to blink for
  4487. # Outputs: blinks selected characters in $w
  4488. ##
  4489. proc ::tkcon::Blink {w args} {
  4490. eval [list $w tag add blink] $args
  4491. after $::tkcon::OPT(blinktime) [list $w] tag remove blink $args
  4492. return
  4493. }
  4494. ## ::tkcon::Insert
  4495. ## Insert a string into a text console at the point of the insertion cursor.
  4496. ## If there is a selection in the text, and it covers the point of the
  4497. ## insertion cursor, then delete the selection before inserting.
  4498. # ARGS: w - text window in which to insert the string
  4499. # s - string to insert (usually just a single char)
  4500. # Outputs: $s to text widget
  4501. ##
  4502. proc ::tkcon::Insert {w s} {
  4503. if {[string match {} $s] || [string match disabled [$w cget -state]]} {
  4504. return
  4505. }
  4506. if {[$w comp insert < limit]} {
  4507. $w mark set insert end
  4508. }
  4509. if {[llength [$w tag ranges sel]] && \
  4510. [$w comp sel.first <= insert] && [$w comp sel.last >= insert]} {
  4511. $w delete sel.first sel.last
  4512. }
  4513. $w insert insert $s
  4514. $w see insert
  4515. }
  4516. ## ::tkcon::Expand -
  4517. # ARGS: w - text widget in which to expand str
  4518. # type - type of expansion (path / proc / variable)
  4519. # Calls: ::tkcon::Expand(Pathname|Procname|Variable)
  4520. # Outputs: The string to match is expanded to the longest possible match.
  4521. # If ::tkcon::OPT(showmultiple) is non-zero and the user longest
  4522. # match equaled the string to expand, then all possible matches
  4523. # are output to stdout. Triggers bell if no matches are found.
  4524. # Returns: number of matches found
  4525. ##
  4526. proc ::tkcon::Expand {w {type ""}} {
  4527. set exp "\[^\\\\\]\[\[ \t\n\r\\\{\"$\]"
  4528. set tmp [$w search -backwards -regexp $exp insert-1c limit-1c]
  4529. if {[string compare {} $tmp]} {append tmp +2c} else {set tmp limit}
  4530. if {[$w compare $tmp >= insert]} return
  4531. set str [$w get $tmp insert]
  4532. switch -glob $type {
  4533. pa* { set res [ExpandPathname $str] }
  4534. pr* { set res [ExpandProcname $str] }
  4535. v* { set res [ExpandVariable $str] }
  4536. default {
  4537. set res {}
  4538. foreach t $::tkcon::OPT(expandorder) {
  4539. if {![catch {Expand$t $str} res] && \
  4540. [string compare {} $res]} break
  4541. }
  4542. }
  4543. }
  4544. set len [llength $res]
  4545. if {$len} {
  4546. $w delete $tmp insert
  4547. $w insert $tmp [lindex $res 0]
  4548. if {$len > 1} {
  4549. if {$::tkcon::OPT(showmultiple) && \
  4550. ![string compare [lindex $res 0] $str]} {
  4551. puts stdout [lsort [lreplace $res 0 0]]
  4552. }
  4553. }
  4554. } else { bell }
  4555. return [incr len -1]
  4556. }
  4557. ## ::tkcon::ExpandPathname - expand a file pathname based on $str
  4558. ## This is based on UNIX file name conventions
  4559. # ARGS: str - partial file pathname to expand
  4560. # Calls: ::tkcon::ExpandBestMatch
  4561. # Returns: list containing longest unique match followed by all the
  4562. # possible further matches
  4563. ##
  4564. proc ::tkcon::ExpandPathname str {
  4565. set pwd [EvalAttached pwd]
  4566. # Cause a string like {C:/Program\ Files/} to become "C:/Program Files/"
  4567. regsub -all {\\([][ ])} $str {\1} str
  4568. if {[catch {EvalAttached [list cd [file dirname $str]]} err]} {
  4569. return -code error $err
  4570. }
  4571. set dir [file tail $str]
  4572. ## Check to see if it was known to be a directory and keep the trailing
  4573. ## slash if so (file tail cuts it off)
  4574. if {[string match */ $str]} { append dir / }
  4575. # Create a safely glob-able name
  4576. regsub -all {([][])} $dir {\\\1} safedir
  4577. if {[catch {lsort [EvalAttached [list glob $safedir*]]} m]} {
  4578. set match {}
  4579. } else {
  4580. if {[llength $m] > 1} {
  4581. global tcl_platform
  4582. if {[string match windows $tcl_platform(platform)]} {
  4583. ## Windows is screwy because it's case insensitive
  4584. set tmp [ExpandBestMatch [string tolower $m] \
  4585. [string tolower $dir]]
  4586. ## Don't change case if we haven't changed the word
  4587. if {[string length $dir]==[string length $tmp]} {
  4588. set tmp $dir
  4589. }
  4590. } else {
  4591. set tmp [ExpandBestMatch $m $dir]
  4592. }
  4593. if {[string match */* $str]} {
  4594. set tmp [string trimright [file dirname $str] /]/$tmp
  4595. }
  4596. regsub -all {([^\\])([][ ])} $tmp {\1\\\2} tmp
  4597. set match [linsert $m 0 $tmp]
  4598. } else {
  4599. ## This may look goofy, but it handles spaces in path names
  4600. eval append match $m
  4601. if {[file isdirectory $match]} {append match /}
  4602. if {[string match */* $str]} {
  4603. set match [string trimright [file dirname $str] /]/$match
  4604. }
  4605. regsub -all {([^\\])([][ ])} $match {\1\\\2} match
  4606. ## Why is this one needed and the ones below aren't!!
  4607. set match [list $match]
  4608. }
  4609. }
  4610. EvalAttached [list cd $pwd]
  4611. return $match
  4612. }
  4613. ## ::tkcon::ExpandProcname - expand a tcl proc name based on $str
  4614. # ARGS: str - partial proc name to expand
  4615. # Calls: ::tkcon::ExpandBestMatch
  4616. # Returns: list containing longest unique match followed by all the
  4617. # possible further matches
  4618. ##
  4619. proc ::tkcon::ExpandProcname str {
  4620. set match [EvalAttached [list info commands $str*]]
  4621. if {[llength $match] == 0} {
  4622. set ns [EvalAttached \
  4623. "namespace children \[namespace current\] [list $str*]"]
  4624. if {[llength $ns]==1} {
  4625. set match [EvalAttached [list info commands ${ns}::*]]
  4626. } else {
  4627. set match $ns
  4628. }
  4629. }
  4630. if {[llength $match] > 1} {
  4631. regsub -all {([^\\]) } [ExpandBestMatch $match $str] {\1\\ } str
  4632. set match [linsert $match 0 $str]
  4633. } else {
  4634. regsub -all {([^\\]) } $match {\1\\ } match
  4635. }
  4636. return $match
  4637. }
  4638. ## ::tkcon::ExpandVariable - expand a tcl variable name based on $str
  4639. # ARGS: str - partial tcl var name to expand
  4640. # Calls: ::tkcon::ExpandBestMatch
  4641. # Returns: list containing longest unique match followed by all the
  4642. # possible further matches
  4643. ##
  4644. proc ::tkcon::ExpandVariable str {
  4645. if {[regexp {([^\(]*)\((.*)} $str junk ary str]} {
  4646. ## Looks like they're trying to expand an array.
  4647. set match [EvalAttached [list array names $ary $str*]]
  4648. if {[llength $match] > 1} {
  4649. set vars $ary\([ExpandBestMatch $match $str]
  4650. foreach var $match {lappend vars $ary\($var\)}
  4651. return $vars
  4652. } else {set match $ary\($match\)}
  4653. ## Space transformation avoided for array names.
  4654. } else {
  4655. set match [EvalAttached [list info vars $str*]]
  4656. if {[llength $match] > 1} {
  4657. regsub -all {([^\\]) } [ExpandBestMatch $match $str] {\1\\ } str
  4658. set match [linsert $match 0 $str]
  4659. } else {
  4660. regsub -all {([^\\]) } $match {\1\\ } match
  4661. }
  4662. }
  4663. return $match
  4664. }
  4665. ## ::tkcon::ExpandBestMatch2 - finds the best unique match in a list of names
  4666. ## Improves upon the speed of the below proc only when $l is small
  4667. ## or $e is {}. $e is extra for compatibility with proc below.
  4668. # ARGS: l - list to find best unique match in
  4669. # Returns: longest unique match in the list
  4670. ##
  4671. proc ::tkcon::ExpandBestMatch2 {l {e {}}} {
  4672. set s [lindex $l 0]
  4673. if {[llength $l]>1} {
  4674. set i [expr {[string length $s]-1}]
  4675. foreach l $l {
  4676. while {$i>=0 && [string first $s $l]} {
  4677. set s [string range $s 0 [incr i -1]]
  4678. }
  4679. }
  4680. }
  4681. return $s
  4682. }
  4683. ## ::tkcon::ExpandBestMatch - finds the best unique match in a list of names
  4684. ## The extra $e in this argument allows us to limit the innermost loop a
  4685. ## little further. This improves speed as $l becomes large or $e becomes long.
  4686. # ARGS: l - list to find best unique match in
  4687. # e - currently best known unique match
  4688. # Returns: longest unique match in the list
  4689. ##
  4690. proc ::tkcon::ExpandBestMatch {l {e {}}} {
  4691. set ec [lindex $l 0]
  4692. if {[llength $l]>1} {
  4693. set e [string length $e]; incr e -1
  4694. set ei [string length $ec]; incr ei -1
  4695. foreach l $l {
  4696. while {$ei>=$e && [string first $ec $l]} {
  4697. set ec [string range $ec 0 [incr ei -1]]
  4698. }
  4699. }
  4700. }
  4701. return $ec
  4702. }
  4703. # Here is a group of functions that is only used when Tkcon is
  4704. # executed in a safe interpreter. It provides safe versions of
  4705. # missing functions. For example:
  4706. #
  4707. # - "tk appname" returns "tkcon.tcl" but cannot be set
  4708. # - "toplevel" is equivalent to 'frame', only it is automatically
  4709. # packed.
  4710. # - The 'source', 'load', 'open', 'file' and 'exit' functions are
  4711. # mapped to corresponding functions in the parent interpreter.
  4712. #
  4713. # Further on, Tk cannot be really loaded. Still the safe 'load'
  4714. # provedes a speciall case. The Tk can be divided into 4 groups,
  4715. # that each has a safe handling procedure.
  4716. #
  4717. # - "::tkcon::SafeItem" handles commands like 'button', 'canvas' ......
  4718. # Each of these functions has the window name as first argument.
  4719. # - "::tkcon::SafeManage" handles commands like 'pack', 'place', 'grid',
  4720. # 'winfo', which can have multiple window names as arguments.
  4721. # - "::tkcon::SafeWindow" handles all windows, such as '.'. For every
  4722. # window created, a new alias is formed which also is handled by
  4723. # this function.
  4724. # - Other (e.g. bind, bindtag, image), which need their own function.
  4725. #
  4726. ## These functions courtesy Jan Nijtmans (nijtmans@nici.kun.nl)
  4727. ##
  4728. if {[string compare [info command tk] tk]} {
  4729. proc tk {option args} {
  4730. if {![string match app* $option]} {
  4731. error "wrong option \"$option\": should be appname"
  4732. }
  4733. return "tkcon.tcl"
  4734. }
  4735. }
  4736. if {[string compare [info command toplevel] toplevel]} {
  4737. proc toplevel {name args} {
  4738. eval frame $name $args
  4739. pack $name
  4740. }
  4741. }
  4742. proc ::tkcon::SafeSource {i f} {
  4743. set fd [open $f r]
  4744. set r [read $fd]
  4745. close $fd
  4746. if {[catch {interp eval $i $r} msg]} {
  4747. error $msg
  4748. }
  4749. }
  4750. proc ::tkcon::SafeOpen {i f {m r}} {
  4751. set fd [open $f $m]
  4752. interp transfer {} $fd $i
  4753. return $fd
  4754. }
  4755. proc ::tkcon::SafeLoad {i f p} {
  4756. global tk_version tk_patchLevel tk_library auto_path
  4757. if {[string compare $p Tk]} {
  4758. load $f $p $i
  4759. } else {
  4760. foreach command {button canvas checkbutton entry frame label
  4761. listbox message radiobutton scale scrollbar spinbox text toplevel} {
  4762. $i alias $command ::tkcon::SafeItem $i $command
  4763. }
  4764. $i alias image ::tkcon::SafeImage $i
  4765. foreach command {pack place grid destroy winfo} {
  4766. $i alias $command ::tkcon::SafeManage $i $command
  4767. }
  4768. if {[llength [info command event]]} {
  4769. $i alias event ::tkcon::SafeManage $i $command
  4770. }
  4771. frame .${i}_dot -width 300 -height 300 -relief raised
  4772. pack .${i}_dot -side left
  4773. $i alias tk tk
  4774. $i alias bind ::tkcon::SafeBind $i
  4775. $i alias bindtags ::tkcon::SafeBindtags $i
  4776. $i alias . ::tkcon::SafeWindow $i {}
  4777. foreach var {tk_version tk_patchLevel tk_library auto_path} {
  4778. $i eval set $var [list [set $var]]
  4779. }
  4780. $i eval {
  4781. package provide Tk $tk_version
  4782. if {[lsearch -exact $auto_path $tk_library] < 0} {
  4783. lappend auto_path $tk_library
  4784. }
  4785. }
  4786. return ""
  4787. }
  4788. }
  4789. proc ::tkcon::SafeSubst {i a} {
  4790. set arg1 ""
  4791. foreach {arg value} $a {
  4792. if {![string compare $arg -textvariable] ||
  4793. ![string compare $arg -variable]} {
  4794. set newvalue "[list $i] $value"
  4795. global $newvalue
  4796. if {[interp eval $i info exists $value]} {
  4797. set $newvalue [interp eval $i set $value]
  4798. } else {
  4799. catch {unset $newvalue}
  4800. }
  4801. $i eval trace variable $value rwu \{[list tkcon set $newvalue $i]\}
  4802. set value $newvalue
  4803. } elseif {![string compare $arg -command]} {
  4804. set value [list $i eval $value]
  4805. }
  4806. lappend arg1 $arg $value
  4807. }
  4808. return $arg1
  4809. }
  4810. proc ::tkcon::SafeItem {i command w args} {
  4811. set args [::tkcon::SafeSubst $i $args]
  4812. set code [catch "$command [list .${i}_dot$w] $args" msg]
  4813. $i alias $w ::tkcon::SafeWindow $i $w
  4814. regsub -all .${i}_dot $msg {} msg
  4815. return -code $code $msg
  4816. }
  4817. proc ::tkcon::SafeManage {i command args} {
  4818. set args1 ""
  4819. foreach arg $args {
  4820. if {[string match . $arg]} {
  4821. set arg .${i}_dot
  4822. } elseif {[string match .* $arg]} {
  4823. set arg ".${i}_dot$arg"
  4824. }
  4825. lappend args1 $arg
  4826. }
  4827. set code [catch "$command $args1" msg]
  4828. regsub -all .${i}_dot $msg {} msg
  4829. return -code $code $msg
  4830. }
  4831. #
  4832. # FIX: this function doesn't work yet if the binding starts with '+'.
  4833. #
  4834. proc ::tkcon::SafeBind {i w args} {
  4835. if {[string match . $w]} {
  4836. set w .${i}_dot
  4837. } elseif {[string match .* $w]} {
  4838. set w ".${i}_dot$w"
  4839. }
  4840. if {[llength $args] > 1} {
  4841. set args [list [lindex $args 0] \
  4842. "[list $i] eval [list [lindex $args 1]]"]
  4843. }
  4844. set code [catch "bind $w $args" msg]
  4845. if {[llength $args] <2 && $code == 0} {
  4846. set msg [lindex $msg 3]
  4847. }
  4848. return -code $code $msg
  4849. }
  4850. proc ::tkcon::SafeImage {i option args} {
  4851. set code [catch "image $option $args" msg]
  4852. if {[string match cr* $option]} {
  4853. $i alias $msg $msg
  4854. }
  4855. return -code $code $msg
  4856. }
  4857. proc ::tkcon::SafeBindtags {i w {tags {}}} {
  4858. if {[string match . $w]} {
  4859. set w .${i}_dot
  4860. } elseif {[string match .* $w]} {
  4861. set w ".${i}_dot$w"
  4862. }
  4863. set newtags {}
  4864. foreach tag $tags {
  4865. if {[string match . $tag]} {
  4866. lappend newtags .${i}_dot
  4867. } elseif {[string match .* $tag]} {
  4868. lappend newtags ".${i}_dot$tag"
  4869. } else {
  4870. lappend newtags $tag
  4871. }
  4872. }
  4873. if {[string match $tags {}]} {
  4874. set code [catch {bindtags $w} msg]
  4875. regsub -all \\.${i}_dot $msg {} msg
  4876. } else {
  4877. set code [catch {bindtags $w $newtags} msg]
  4878. }
  4879. return -code $code $msg
  4880. }
  4881. proc ::tkcon::SafeWindow {i w option args} {
  4882. if {[string match conf* $option] && [llength $args] > 1} {
  4883. set args [::tkcon::SafeSubst $i $args]
  4884. } elseif {[string match itemco* $option] && [llength $args] > 2} {
  4885. set args "[list [lindex $args 0]] [::tkcon::SafeSubst $i [lrange $args 1 end]]"
  4886. } elseif {[string match cr* $option]} {
  4887. if {[llength $args]%2} {
  4888. set args "[list [lindex $args 0]] [::tkcon::SafeSubst $i [lrange $args 1 end]]"
  4889. } else {
  4890. set args [::tkcon::SafeSubst $i $args]
  4891. }
  4892. } elseif {[string match bi* $option] && [llength $args] > 2} {
  4893. set args [list [lindex $args 0] [lindex $args 1] "[list $i] eval [list [lindex $args 2]]"]
  4894. }
  4895. set code [catch ".${i}_dot$w $option $args" msg]
  4896. if {$code} {
  4897. regsub -all .${i}_dot $msg {} msg
  4898. } elseif {[string match conf* $option] || [string match itemco* $option]} {
  4899. if {[llength $args] == 1} {
  4900. switch -- $args {
  4901. -textvariable - -variable {
  4902. set msg "[lrange $msg 0 3] [list [lrange [lindex $msg 4] 1 end]]"
  4903. }
  4904. -command - updatecommand {
  4905. set msg "[lrange $msg 0 3] [list [lindex [lindex $msg 4] 2]]"
  4906. }
  4907. }
  4908. } elseif {[llength $args] == 0} {
  4909. set args1 ""
  4910. foreach el $msg {
  4911. switch -- [lindex $el 0] {
  4912. -textvariable - -variable {
  4913. set el "[lrange $el 0 3] [list [lrange [lindex $el 4] 1 end]]"
  4914. }
  4915. -command - updatecommand {
  4916. set el "[lrange $el 0 3] [list [lindex [lindex $el 4] 2]]"
  4917. }
  4918. }
  4919. lappend args1 $el
  4920. }
  4921. set msg $args1
  4922. }
  4923. } elseif {[string match cg* $option] || [string match itemcg* $option]} {
  4924. switch -- $args {
  4925. -textvariable - -variable {
  4926. set msg [lrange $msg 1 end]
  4927. }
  4928. -command - updatecommand {
  4929. set msg [lindex $msg 2]
  4930. }
  4931. }
  4932. } elseif {[string match bi* $option]} {
  4933. if {[llength $args] == 2 && $code == 0} {
  4934. set msg [lindex $msg 2]
  4935. }
  4936. }
  4937. return -code $code $msg
  4938. }
  4939. proc ::tkcon::RetrieveFilter {host} {
  4940. variable PRIV
  4941. set result {}
  4942. if {[info exists PRIV(proxy)]} {
  4943. if {![regexp "^(localhost|127\.0\.0\.1)" $host]} {
  4944. set result [lrange [split [lindex $PRIV(proxy) 0] :] 0 1]
  4945. }
  4946. }
  4947. return $result
  4948. }
  4949. proc ::tkcon::RetrieveAuthentication {} {
  4950. package require Tk
  4951. if {[catch {package require base64}]} {
  4952. if {[catch {package require Trf}]} {
  4953. error "base64 support not available"
  4954. } else {
  4955. set local64 "base64 -mode enc"
  4956. }
  4957. } else {
  4958. set local64 "base64::encode"
  4959. }
  4960. set dlg [toplevel .auth]
  4961. wm title $dlg "Authenticating Proxy Configuration"
  4962. set f1 [frame ${dlg}.f1]
  4963. set f2 [frame ${dlg}.f2]
  4964. button $f2.b -text "OK" -command "destroy $dlg"
  4965. pack $f2.b -side right
  4966. label $f1.l2 -text "Username"
  4967. label $f1.l3 -text "Password"
  4968. entry $f1.e2 -textvariable "[namespace current]::conf_userid"
  4969. entry $f1.e3 -textvariable "[namespace current]::conf_passwd" -show *
  4970. grid $f1.l2 -column 0 -row 0 -sticky e
  4971. grid $f1.l3 -column 0 -row 1 -sticky e
  4972. grid $f1.e2 -column 1 -row 0 -sticky news
  4973. grid $f1.e3 -column 1 -row 1 -sticky news
  4974. grid columnconfigure $f1 1 -weight 1
  4975. pack $f2 -side bottom -fill x
  4976. pack $f1 -side top -anchor n -fill both -expand 1
  4977. tkwait window $dlg
  4978. set result {}
  4979. if {[info exists [namespace current]::conf_userid]} {
  4980. set data [subst $[namespace current]::conf_userid]
  4981. append data : [subst $[namespace current]::conf_passwd]
  4982. set data [$local64 $data]
  4983. set result [list "Proxy-Authorization" "Basic $data"]
  4984. }
  4985. unset [namespace current]::conf_passwd
  4986. return $result
  4987. }
  4988. proc ::tkcon::Retrieve {} {
  4989. # A little bit'o'magic to grab the latest tkcon from CVS and
  4990. # save it locally. It doesn't support proxies though...
  4991. variable PRIV
  4992. set defExt ""
  4993. if {[string match "windows" $::tcl_platform(platform)]} {
  4994. set defExt ".tcl"
  4995. }
  4996. set file [tk_getSaveFile -title "Save Latest tkcon to ..." \
  4997. -defaultextension $defExt \
  4998. -initialdir [file dirname $PRIV(SCRIPT)] \
  4999. -initialfile [file tail $PRIV(SCRIPT)] \
  5000. -parent $PRIV(root) \
  5001. -filetypes {{"Tcl Files" {.tcl .tk}} {"All Files" {*.*}}}]
  5002. if {[string compare $file ""]} {
  5003. package require http 2
  5004. set headers {}
  5005. if {[info exists PRIV(proxy)]} {
  5006. ::http::config -proxyfilter [namespace origin RetrieveFilter]
  5007. if {[lindex $PRIV(proxy) 1] != {}} {
  5008. set headers [RetrieveAuthentication]
  5009. }
  5010. }
  5011. set token [::http::geturl $PRIV(HEADURL) \
  5012. -headers $headers -timeout 30000]
  5013. set token [::http::geturl $PRIV(HEADURL) -timeout 30000]
  5014. ::http::wait $token
  5015. set code [catch {
  5016. if {[::http::status $token] == "ok"} {
  5017. set fid [open $file w]
  5018. # We don't want newline mode to change
  5019. fconfigure $fid -translation binary
  5020. set data [::http::data $token]
  5021. puts -nonewline $fid $data
  5022. close $fid
  5023. regexp {Id: tkcon.tcl,v (\d+\.\d+)} $data -> rcsVersion
  5024. regexp {VERSION\s+"(\d+\.\d+[^\"]*)"} $data -> tkconVersion
  5025. }
  5026. } err]
  5027. ::http::cleanup $token
  5028. if {$code} {
  5029. return -code error $err
  5030. } else {
  5031. if {![info exists rcsVersion]} { set rcsVersion "UNKNOWN" }
  5032. if {![info exists tkconVersion]} { set tkconVersion "UNKNOWN" }
  5033. if {[tk_messageBox -type yesno -icon info -parent $PRIV(root) \
  5034. -title "Retrieved tkcon v$tkconVersion, RCS $rcsVersion" \
  5035. -message "Successfully retrieved tkcon v$tkconVersion,\
  5036. RCS $rcsVersion. Shall I resource (not restart) this\
  5037. version now?"] == "yes"} {
  5038. set PRIV(SCRIPT) $file
  5039. set PRIV(version) $tkconVersion.$rcsVersion
  5040. ::tkcon::Resource
  5041. }
  5042. }
  5043. }
  5044. }
  5045. ## ::tkcon::Resource - re'source's this script into current console
  5046. ## Meant primarily for my development of this program. It follows
  5047. ## links until the ultimate source is found.
  5048. ##
  5049. set ::tkcon::PRIV(SCRIPT) [info script]
  5050. if {!$::tkcon::PRIV(WWW) && [string compare $::tkcon::PRIV(SCRIPT) {}]} {
  5051. # we use a catch here because some wrap apps choke on 'file type'
  5052. # because TclpLstat wasn't wrappable until 8.4.
  5053. catch {
  5054. while {[string match link [file type $::tkcon::PRIV(SCRIPT)]]} {
  5055. set link [file readlink $::tkcon::PRIV(SCRIPT)]
  5056. if {[string match relative [file pathtype $link]]} {
  5057. set ::tkcon::PRIV(SCRIPT) \
  5058. [file join [file dirname $::tkcon::PRIV(SCRIPT)] $link]
  5059. } else {
  5060. set ::tkcon::PRIV(SCRIPT) $link
  5061. }
  5062. }
  5063. catch {unset link}
  5064. if {[string match relative [file pathtype $::tkcon::PRIV(SCRIPT)]]} {
  5065. set ::tkcon::PRIV(SCRIPT) [file join [pwd] $::tkcon::PRIV(SCRIPT)]
  5066. }
  5067. }
  5068. }
  5069. if {$::tkcon::PRIV(WWW)} {
  5070. rename tk ::tkcon::_tk
  5071. proc tk {cmd args} {
  5072. if {$cmd == "appname"} {
  5073. return "tkcon/WWW"
  5074. } else {
  5075. return [uplevel 1 ::tkcon::_tk [list $cmd] $args]
  5076. }
  5077. }
  5078. }
  5079. proc ::tkcon::Resource {} {
  5080. uplevel \#0 {
  5081. if {[catch {source -rsrc tkcon}]} { source $::tkcon::PRIV(SCRIPT) }
  5082. }
  5083. Bindings
  5084. InitSlave $::tkcon::OPT(exec)
  5085. }
  5086. ## Initialize only if we haven't yet
  5087. ##
  5088. if {(![info exists ::tkcon::PRIV(root)] \
  5089. || ![winfo exists $::tkcon::PRIV(root)]) \
  5090. && (![info exists argv0] || [info script] == $argv0)} {
  5091. eval ::tkcon::Init $argv
  5092. }
  5093. package provide tkcon $::tkcon::VERSION