1#  Init.tcl ---
2#
3#      This file is part of The Coccinella application.
4#      It sets up the global 'this' array for useful things.
5#
6#  Copyright (c) 2004-2008  Mats Bengtsson
7#
8#   This program is free software: you can redistribute it and/or modify
9#   it under the terms of the GNU General Public License as published by
10#   the Free Software Foundation, either version 3 of the License, or
11#   (at your option) any later version.
12#
13#   This program is distributed in the hope that it will be useful,
14#   but WITHOUT ANY WARRANTY; without even the implied warranty of
15#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16#   GNU General Public License for more details.
17#
18#   You should have received a copy of the GNU General Public License
19#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20#
21# $Id: Init.tcl,v 1.108 2008-08-19 07:56:57 matben Exp $
22
23namespace eval ::Init {
24
25    # Mutually exclusive.
26    set ::config(prefs,sameDrive)  0
27    set ::config(prefs,sameDir)    1
28}
29
30proc ::Init::SetThis {mainScript} {
31    global  this auto_path tcl_platform prefs config
32
33    # If we store the prefs file on a removable drive, use this folder name:
34    #    F:\CoccinellaPrefs  etc.
35    set this(prefsDriverDir) "CoccinellaPrefs"
36
37    # Collect paths in 'this' array.
38    set path                    [file dirname $mainScript]
39    set this(path)              $path
40    set this(script)            $mainScript
41
42    # Sub paths. Use relative source, prefs, or theme dirs.
43    # Keep generic path separator for theme engine.
44    set this(emoticons)         iconsets/emoticons
45    set this(images)            images
46    set this(resources)         resources
47    set this(sounds)            sounds
48    set this(themes)            themes
49
50    # Absolute paths.
51    set this(componentPath)     [file join $path components]
52    set this(docsPath)          [file join $path docs]
53    set this(emoticonsPath)     [file join $path iconsets emoticons]
54    set this(itemPath)          [file join $path items]
55    set this(msgcatPath)        [file join $path msgs]
56    set this(msgcatCompPath)    [file join $path msgs components]
57    set this(msgcatPostPath)    [file join $path msgs post]
58    set this(pluginsPath)       [file join $path plugins]
59    set this(appletsPath)       [file join $path plugins applets]
60    set this(resourcePath)      [file join $path resources]
61    set this(postPrefsPath)     [file join $path resources post]
62    set this(postPrefsFile)     [file join $path resources post prefs.rdb]
63    set this(prePrefsPath)      [file join $path resources pre]
64    set this(prePrefsFile)      [file join $path resources pre prefs.rdb]
65    set this(themesPath)        [file join $path themes]
66    set this(httpdRootPath)     $path
67    set this(isAppWrapped)      0
68    set this(appPath)           $path
69
70    # 'appPath' points to folder which contains:
71    #    Coccinella.tcl       if run from sources
72    #    Coccinella*.exe      on Windows
73    #    Coccinella*.bin      on Unix
74    #    Coccinella*.app      on Mac OS X
75
76    if {[info exists starkit::topdir]} {
77	set this(appPath) [file dirname [info nameofexecutable]]
78	set this(isAppWrapped) 1
79    } elseif {$this(platform) eq "macosx"} {
80
81	# If we have an application bundle we must get the .app folder.
82	# bundle typically 'Coccinella-0.96.0.app'
83	set psplit [file split $this(appPath)]
84	set bundle [lsearch -glob -inline $psplit *.app]
85	if {$bundle ne ""} {
86	    set appBundle [lsearch -glob -inline \
87	      [file split [info nameofexecutable]] *.app]
88	    if {$appBundle eq $bundle} {
89		set idx [lsearch -glob $psplit *.app]
90		incr idx -1
91		set this(appPath) [eval file join [lrange $psplit 0 $idx]]
92		set this(isAppWrapped) 1
93	    }
94	}
95    }
96
97    # Path where preferences etc are stored.
98    set this(prefsDefPath) [GetDefaultPrefsPath]
99    set this(prefsPath)    $this(prefsDefPath)
100    set this(prefsName)    "preferences.rdb"
101
102    # Old prefs name (changed 0.96.3)
103    switch -- $this(platform) {
104	macosx {
105	    set this(oldPrefsName) "Whiteboard Prefs"
106	}
107	unix {
108	    set this(oldPrefsName) "whiteboard"
109	}
110	windows {
111	    set this(oldPrefsName) "WBPREFS.TXT"
112	}
113    }
114
115    if {$config(prefs,sameDrive)} {
116
117	# Handle the situation where the app lives on a removable drive (USB stick).
118	# If it lives on a removable drive, and if we find an existing prefs
119	# file there, then we should relate all prefs related paths to it.
120	set this(prefsPathRemovable) 0
121	if {[IsAppOnRemovableDrive]} {
122	    set prefsPathDrive [GetAppDrivePrefsPath]
123	    set prefFile [file join $prefsPathDrive $this(prefsName)]
124	    set oldFile [file join $prefsPathDir $this(oldPrefsName)]
125	    if {([file exists $prefFile] && [file writable $prefFile]) || \
126	      ([file exists $oldFile] && [file writable $oldFile])} {
127		set this(prefsPathRemovable) 1
128		set this(prefsPath) $prefsPathDrive
129	    }
130	}
131    } elseif {$config(prefs,sameDir)} {
132
133	# Search for the prefs file in the applicatons folder first.
134	set this(prefsPathAppDir) 0
135	set prefsPathDir [GetAppDirPrefsPath]
136	set prefFile [file join $prefsPathDir $this(prefsName)]
137	set oldFile [file join $prefsPathDir $this(oldPrefsName)]
138	if {([file exists $prefFile] && [file writable $prefFile]) || \
139	  ([file exists $oldFile] && [file writable $oldFile])} {
140	    set this(prefsPathAppDir) 1
141	    set this(prefsPath) $prefsPathDir
142	}
143    }
144
145    # Import any old (pre 0.96.3) prefs file.
146    set oldPrefs [file join $this(prefsPath) $this(oldPrefsName)]
147    if {[file exists $oldPrefs]} {
148	set newPrefs [file join $this(prefsPath) $this(prefsName)]
149	file rename -force $oldPrefs $newPrefs
150    }
151
152    # Sets all paths that are dependent on this(prefsPath).
153    SetPrefsPaths
154
155    # Make sure all dirs exist.
156    MakePrefsDirs
157
158    # Need to rework this...
159    if {0 && [info exists starkit::topdir]} {
160	set this(httpdRootPath) $starkit::topdir
161	set this(httpdRelPath)  \
162	  [file join $::starkit::topdir lib app-Coccinella httpd]
163    }
164    set this(internalIPnum)  127.0.0.1
165    set this(internalIPname) "localhost"
166
167    # Set our IP number temporarily.
168    set this(ipnum) $this(internalIPnum)
169
170    # Need a tmp directory, typically in a StarKit when QuickTime movies are opened.
171    set MAX_INT 0x7FFFFFFF
172    set hex [format {%x} [expr {int($MAX_INT*rand())}]]
173    set tail coccinella[pid]-$hex
174    set this(tmpPath) [file join [TempDir] $tail]
175    if {![file isdirectory $this(tmpPath)]} {
176	file mkdir $this(tmpPath)
177    }
178
179    # This is where the "Batteries Included" binaries go. Empty if non.
180    if {$this(platform) eq "unix"} {
181	set machine $tcl_platform(machine)
182	if {[regexp {[2-9]86} $tcl_platform(machine)]} {
183	    set machine "i686"
184	} elseif {$tcl_platform(machine) eq "x86_64"} {
185	    if {[info exists ::starkit::topdir]} {
186	        set machine "i686"
187	    } else {
188	        set machine "x86_64"
189	    }
190	}
191	set machineSpecPath [file join $tcl_platform(os) $machine]
192    } elseif {$this(platform) eq "macosx"} {
193
194	# We keep universal builds for 8.5 in 'i386'.
195	if {[info tclversion] >= 8.5} {
196	    set machineSpecPath i386
197	} else {
198	    set machineSpecPath $tcl_platform(machine)
199	}
200    } else {
201	set machineSpecPath $tcl_platform(machine)
202    }
203
204    # Make cvs happy.
205    regsub -all " " $machineSpecPath "" machineSpecPath
206
207    set this(binLibPath) [file join $path bin library]
208    set this(binPath)    [file join $path bin $this(platform) $machineSpecPath]
209    if {[file exists $this(binPath)]} {
210	set auto_path [concat [list $this(binPath)] $auto_path]
211	set auto_path [concat [list $this(binLibPath)] $auto_path]
212    } else {
213	set this(binPath) {}
214    }
215
216    switch -- [tk windowingsystem] {
217	"aqua"  { set this(modkey) Command }
218	default { set this(modkey) Control }
219    }
220
221    # Find user name.
222    set this(username) [GetUserName]
223
224    # Write a pid file with our pid that gets deleted when quit.
225    # This is a way to detect if we are running.
226    set this(pidFile) [file join $this(prefsPath) coccinella.pid]
227    set fd [open $this(pidFile) w]
228    puts -nonewline $fd [pid]
229    close $fd
230
231    # Write a file to the prefs dir with our execution path.
232    if {[info exists ::starkit::topdir]} {
233	set exe [file nativename [info nameofexecutable]]
234    } else {
235	set exe "wish \"$mainScript\""
236    }
237    set this(execFile) [file join $this(prefsPath) launchCmd]
238    set fd [open $this(execFile) w]
239    puts -nonewline $fd $exe
240    close $fd
241}
242
243# Init::GetDefaultPrefsPath --
244#
245#       Finds the actual file path to our default prefs dir.
246
247proc ::Init::GetDefaultPrefsPath {} {
248    global  this
249
250    # Path where preferences etc are stored.
251    switch -- $this(platform) {
252	macosx {
253	    set prefsPath  \
254	      [file join [file nativename ~/Library/Preferences] Coccinella]
255	}
256	unix {
257	    set prefsPath [file nativename ~/.coccinella]
258	}
259	windows {
260
261	    # The default prefs dir is now obtained from the registry.
262	    # If any old be sure to copy to new.
263	    set appPath [GetWindowsAppPath]
264	    set prefsPath [file join $appPath Coccinella]
265	    if {![file isdirectory $prefsPath]} {
266		set oldPath [GetWindowsAdhocPrefsPath]
267		if {[file isdirectory $oldPath]} {
268		    file copy -force -- $oldPath $appPath
269		    file delete -force -- $oldPath
270		}
271	    }
272	}
273    }
274    return $prefsPath
275}
276
277proc ::Init::GetWindowsAppPath {} {
278
279    set appPath ""
280
281    catch {
282	package require registry
283	set shellFoldersKey \
284		 {HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders}
285	set appPath [registry get $shellFoldersKey AppData]
286	regsub -all {%([a-zA-Z]+)%} $appPath \$::env(\\1) appPath
287	set appPath [subst -nobackslashes -nocommands $appPath]
288	if {![file isdirectory $appPath]} {
289	    file mkdir $appPath
290	}
291    }
292    return $appPath
293}
294
295proc ::Init::GetWindowsAdhocPrefsPath {} {
296
297    set prefsPath ""
298
299    foreach key {USERPROFILE APPDATA HOME HOMEPATH \
300      ALLUSERSPROFILE CommonProgramFiles HOMEDRIVE} {
301	if {[info exists ::env($key)] && [file writable $::env($key)]} {
302	    set winPrefsDir $::env($key)
303	    break
304	}
305    }
306    if {![info exists winPrefsDir]} {
307	set vols [lsort [file volumes]]
308	set vols [lsearch -all -inline -glob -not $vols A:*]
309	set vols [lsearch -all -inline -glob -not $vols B:*]
310
311	# If none of the above are writable this is unlikely.
312	if {[file writable [lindex $vols 0]]} {
313	    set winPrefsDir [lindex $vols 0]
314	} else {
315	    if {[info exists starkit::topdir]} {
316		set dir [file dirname [info nameofexecutable]]
317	    } else {
318		set dir [file dirname [file dirname $this(script)]]
319	    }
320	    if {[file writable $dir]} {
321		set winPrefsDir $dir
322	    }
323	}
324    }
325    if {[info exists winPrefsDir]} {
326	set prefsPath [file join $winPrefsDir Coccinella]
327    }
328    return $prefsPath
329}
330
331proc ::Init::IsAppOnRemovableDrive {} {
332    global  this
333
334    set ans 0
335
336    if {$this(platform) eq "windows"} {
337	set prefsDrive [string tolower [string index $this(prefsDefPath) 0]]
338	set appDrive   [string tolower [string index $this(appPath) 0]]
339	if {$prefsDrive ne $appDrive} {
340	    set ans 1
341	}
342    } elseif {$this(platform) eq "macosx"} {
343
344	# Try to see if different drives. Ad hoc.
345	set lprefs [file split $this(prefsDefPath)]
346	set lapp   [file split $this(appPath)]
347	set prefs1 [lindex $lprefs 1]
348	set app1   [lindex $lapp 1]
349	if {($app1 ne $prefs1) && ($app1 eq "Volumes")} {
350	    set ans 1
351	}
352    } elseif {$this(platform) eq "unix"} {
353	# @@@ Don't know how to detect drives on unix in general.
354    }
355    return $ans
356}
357
358# Init::GetAppDrivePrefsPath --
359#
360#       Gets the prefs path for nonstandard drive.
361#       It doesn't check for its existence.
362#       You MUST have 'IsAppOnRemovableDrive' for this to make sense.
363
364proc ::Init::GetAppDrivePrefsPath {} {
365    global  this
366
367    set lapp [file split $this(appPath)]
368
369    if {$this(platform) eq "windows"} {
370	set path [file join [lindex $lapp 0] $this(prefsDriverDir)]
371    } elseif {$this(platform) eq "macosx"} {
372	set drive [lindex $lapp 2]
373	set path [file join [lindex $lapp 0] [lindex $lapp 1] [lindex $lapp 2]  \
374	  $this(prefsDriverDir)]
375    } elseif {$this(platform) eq "unix"} {
376	# @@@ Don't know how to detect drives on unix in general.
377	set path ""
378    }
379    return $path
380}
381
382#       One above this(appPath).
383
384proc ::Init::GetAppDirPrefsPath {} {
385    global  this
386    if {$this(isAppWrapped)} {
387	set path [file join $this(appPath) $this(prefsDriverDir)]
388    } else {
389	set psplit [file split $this(appPath)]
390	set path [eval file join [lrange $psplit 0 end-1] $this(prefsDriverDir)]
391    }
392    return $path
393}
394
395# Init::SetPrefsPaths --
396#
397#       Is supposed to set all standard paths that are dependent on
398#       this(prefsPath).
399
400proc ::Init::SetPrefsPaths {} {
401    global  this
402
403    set path $this(prefsPath)
404
405    set this(altItemPath)       [file join $path items]
406    set this(altEmoticonsPath)  [file join $path $this(emoticons)]
407    set this(altThemesPath)     [file join $path themes]
408    set this(inboxFile)         [file join $path Inbox.tcl]
409    set this(prefsAvatarPath)   [file join $path avatar]
410    set this(myAvatarPath)      [file join $path avatar my]
411    set this(cacheAvatarPath)   [file join $path avatar cache]
412    set this(recentAvatarPath)  [file join $path avatar recent]
413    set this(scriptsPath)       [file join $path scripts]
414    set this(backgroundsPath)   [file join $path backgrounds]
415    set this(certificatesPath)  [file join $path certificates]
416
417    set pname $this(prefsName)
418
419    switch -- $this(platform) {
420	unix {
421
422	    # On a central installation need to have local dirs for write access.
423	    set this(userPrefsFilePath) [file nativename [file join $path $pname]]
424	    set this(inboxCanvasPath) [file nativename [file join $path canvases]]
425	    set this(historyPath) [file nativename [file join $path history]]
426	}
427	macosx {
428	    set this(userPrefsFilePath) [file join $path $pname]
429	    set this(inboxCanvasPath) [file join $path Canvases]
430	    set this(historyPath) [file join $path History]
431	}
432	windows {
433	    set this(userPrefsFilePath) [file join $path $pname]
434	    set this(inboxCanvasPath) [file join $path Canvases]
435	    set this(historyPath) [file join $path History]
436	}
437    }
438}
439
440# Init::SetPrefsPathToDefault, SetPrefsPathToRemovable --
441#
442#       Helpers to allow switching prefs location.
443
444proc ::Init::SetPrefsPathToDefault {} {
445    global  this
446
447    set this(prefsPath) [GetDefaultPrefsPath]
448    set this(prefsPathRemovable) 0
449    set this(prefsPathAppDir) 0
450
451    SetPrefsPaths
452    MakePrefsDirs
453}
454
455proc ::Init::SetPrefsPathToRemovable {} {
456    global  this
457
458    set this(prefsPath) [GetAppDrivePrefsPath]
459    set this(prefsPathRemovable) 1
460    set this(prefsPathAppDir) 0
461
462    SetPrefsPaths
463    MakePrefsDirs
464}
465
466proc ::Init::SetPrefsPathToAppDir {} {
467    global  this
468
469    set this(prefsPath) [GetAppDirPrefsPath]
470    set this(prefsPathAppDir) 1
471
472    SetPrefsPaths
473    MakePrefsDirs
474}
475
476proc ::Init::GetUserName {} {
477    global  this
478
479    # Find user name.
480    if {[info exists ::env(USER)]} {
481	set username $::env(USER)
482    } elseif {[info exists ::env(LOGIN)]} {
483	set username $::env(LOGIN)
484    } elseif {[info exists ::env(USERNAME)]} {
485	set username $::env(USERNAME)
486    } elseif {[llength [set this(hostname) [info hostname]]]} {
487	set username $this(hostname)
488    } else {
489	set username "Unknown"
490    }
491    return $username
492}
493
494proc ::Init::SetThisVersion {} {
495    global  this
496
497    # The application major and minor version numbers; should only be written to
498    # default file, never read.
499    set this(vers,major)    0
500    set this(vers,minor)   96
501    set this(vers,release)  20
502
503    # NB: The 'minorRelease' number is only used for released versions and not
504    #     in cvs or so called daily builds. cvs always have odd 'release'
505    #     numbers and 'minorRelease' equal to 0.
506    #     A minor release has always a nonzero 'minorRelease' number and
507    #     an even 'release' number one minus the cvs 'release' number.
508    #     An example: if the cvs version id 0.96.5 we can create a series
509    #     of minor releases 0.96.4.1, 0.96.4.2, 0.96.4.3, ...
510    set this(vers,minorRelease) 0
511
512    set this(vers,full) $this(vers,major).$this(vers,minor).$this(vers,release)
513    if {$this(vers,minorRelease)} {
514	append this(vers,full) .$this(vers,minorRelease)
515    }
516
517    # This is used only to track upgrades.
518    set this(vers,previous) $this(vers,full)
519}
520
521proc ::Init::SetThisEmbedded {} {
522    global  this
523
524    # We may be embedded in another application, say an ActiveX component.
525    # The TclControl ActiveX package defines the browser namespace.
526    # So does the TclPlugin, at least version 2.0:
527    # http://www.tcl.tk/man/plugin2.0/pluginDoc/plugin.htm
528    if {[namespace exists ::browser]} {
529	set this(embedded) 1
530    } else {
531	set this(embedded) 0
532    }
533}
534
535proc ::Init::MakePrefsDirs {} {
536    global  this tcl_platform
537
538    foreach name {
539	prefsPath
540	inboxCanvasPath
541	historyPath
542	prefsAvatarPath
543	myAvatarPath
544	cacheAvatarPath
545	recentAvatarPath
546	altItemPath
547	altEmoticonsPath
548	altThemesPath
549	scriptsPath
550	backgroundsPath
551    } {
552	if {[file isfile $this($name)]} {
553	    file delete -force $this($name)
554	}
555	if {![file isdirectory $this($name)]} {
556	    file mkdir $this($name)
557	}
558    }
559
560    # Privacy!
561    # Make sure other have absolutely no access to our prefs.
562    # [Bug 243364] [NEW] umask needs to be set to 077 (?)
563    if {$tcl_platform(platform) eq "unix"} {
564	file attributes $this(prefsPath) -permissions 0700
565    }
566}
567
568proc ::Init::SetAutoPath {} {
569    global  auto_path this prefs
570
571    # Hack on MacOSX to avoid all old installed packages.
572    if {$this(platform) eq "macosx"} {
573	set auto_path [lsearch -all -not -inline $auto_path "/System/Library/Tcl"]
574	set auto_path [lsearch -all -not -inline $auto_path "/System/Library/Frameworks"]
575	set auto_path [lsearch -all -not -inline $auto_path "/Network/Library/Tcl"]
576	set auto_path [lsearch -all -not -inline $auto_path "/Network/Library/Frameworks"]
577	set auto_path [lsearch -all -not -inline $auto_path "~/Library/Tcl"]
578	set auto_path [lsearch -all -not -inline $auto_path "~/Library/Frameworks"]
579    }
580
581    # Add our lib and whiteboard directory to our search path.
582    lappend auto_path [file join $this(path) lib]
583    lappend auto_path [file join $this(path) whiteboard]
584
585    # Add the contrib directory which has things like widgets etc.
586    lappend auto_path [file join $this(path) contrib]
587
588    # Add the jabberlib directory which provides jabber support.
589    lappend auto_path [file join $this(path) jabberlib]
590
591    # Add the jabber directory which provides client specific jabber stuffs.
592    lappend auto_path [file join $this(path) jabber]
593
594    # Add the components directory since we may have packages used by components.
595    lappend auto_path [file join $this(path) components]
596
597    # Do we need TclXML. This is in its own app specific dir.
598    # Perhaps there can be a conflict if there is already an TclXML
599    # package installed in the standard 'auto_path'. Be sure to have it first!
600    set auto_path [concat [list [file join $this(path) TclXML]] $auto_path]
601}
602
603proc ::Init::Msgcat {} {
604    global  prefs this
605
606    package require msgcat
607
608    # The message catalog for language customization. Use 'en' as fallback.
609    if {$prefs(messageLocale) eq ""} {
610	if {[string match -nocase "c" [::msgcat::mclocale]]} {
611	    ::msgcat::mclocale en
612	}
613	set locale [::msgcat::mclocale]
614
615	# Avoid the mac encoding problems by rejecting certain locales.
616	if {[tk windowingsystem] eq "aqua"} {
617	    if {[regexp {ru} $locale]} {
618		set locale en
619		::msgcat::mclocale en
620	    }
621	}
622    } else {
623	set locale $prefs(messageLocale)
624	::msgcat::mclocale $locale
625    }
626
627    set this(systemLocale) $locale
628    set lang [lindex [split [file rootname $locale] _] 0]
629    set langs [glob -nocomplain -tails -directory $this(msgcatPath) -- *.msg]
630    set havecat 0
631    foreach f $langs {
632	set langcode [lindex [split [file rootname $f] _] 0]
633	if {[string match -nocase ${langcode}* $locale]} {
634	    set havecat 1
635	    break
636	}
637    }
638    if {!$havecat} {
639	::msgcat::mclocale en
640    }
641
642    # Windows is not utf-8 by default
643    encoding system utf-8
644
645    # Test here if you want a particular message catalog (en, nl, de, fr, sv,...).
646    #::msgcat::mclocale pl
647    uplevel #0 [list ::msgcat::mcload $this(msgcatPath)]
648
649    # This is a method to override default messages with custom ones for each
650    # language.
651    if {[file isdirectory $this(msgcatPostPath)]} {
652	uplevel #0 [list ::msgcat::mcload $this(msgcatPostPath)]
653    }
654    uplevel #0 namespace import ::msgcat::mc
655}
656
657# From tcllib (fileutil) + modifications:
658
659# ::Init::TempDir --
660#
661#	Return the correct directory to use for temporary files.
662#	Python attempts this sequence, which seems logical:
663#
664#       1. The directory named by the `TMPDIR' environment variable.
665#
666#       2. The directory named by the `TEMP' environment variable.
667#
668#       3. The directory named by the `TMP' environment variable.
669#
670#       4. A platform-specific location:
671#            * On Macintosh, the `Temporary Items' folder.
672#
673#            * On Windows, the directories `C:\\TEMP', `C:\\TMP',
674#              `\\TEMP', and `\\TMP', in that order.
675#
676#            * On all other platforms, the directories `/tmp',
677#              `/var/tmp', and `/usr/tmp', in that order.
678#
679#        5. As a last resort, the current working directory.
680#
681# Arguments:
682#	None.
683#
684# Side Effects:
685#	None.
686#
687# Results:
688#	The directory for temporary files.
689
690proc ::Init::TempDir {} {
691    global  tcl_platform env
692
693    set attempdirs [list]
694
695    foreach tmp {TMPDIR TEMP TMP} {
696	if {[info exists env($tmp)]} {
697	    lappend attempdirs $env($tmp)
698	}
699    }
700
701    switch $tcl_platform(platform) {
702	windows {
703	    lappend attempdirs "C:\\TEMP" "C:\\TMP" "\\TEMP" "\\TMP"
704	}
705	default {
706	    lappend attempdirs [file join / tmp] \
707		[file join / var tmp] [file join / usr tmp]
708	}
709    }
710
711    foreach tmp $attempdirs {
712	if {[file isdirectory $tmp] && [file writable $tmp]} {
713	    return [file normalize $tmp]
714	}
715    }
716
717    # If nothing else worked...
718    return [file normalize [pwd]]
719}
720
721proc ::Init::LoadTkPng {} {
722    global this
723
724    # tkpng is required for the gui.
725    if {[catch {package require tkpng 0.7}]} {
726	tk_messageBox -icon error -title [::msgcat::mc "Error"] \
727	  -message "The tkpng package is required for the GUI"
728	exit
729    }
730    set this(package,tkpng) 1
731}
732
733proc ::Init::LoadPackages {} {
734    global  this auto_path
735
736    # Take precautions and load only our own treectrl.
737
738    # tileqt has its own library support.
739    if {[tk windowingsystem] eq "x11"} {
740	namespace eval ::tileqt {}
741	set ::tileqt::library [file join $this(binLibPath) tileqt]
742    }
743
744    # treectrl is required.
745    ::Splash::SetMsg [mc "Looking for %s" treectrl]...
746    set ::treectrl_library [file join $this(binLibPath) treectrl]
747    if {[catch {package require treectrl 2.2} msg]} {
748	tk_messageBox -icon error -title [::msgcat::mc "Error"] \
749	  -message "This application requires the treectrl widget to work! $::errorInfo"
750	exit
751    }
752
753    # Other utility packages that can be platform specific.
754    # The 'Thread' package requires that the Tcl core has been built with support.
755    array set extraPacksArr {
756	macosx      {Itcl Tclapplescript tls Thread MacCarbonPrint carbon}
757	windows     {Itcl printer gdi tls Thread optcl tcom}
758	unix        {Itcl tls Thread}
759    }
760    foreach {platform packList} [array get extraPacksArr] {
761	foreach name $packList {
762	    set this(package,$name) 0
763	}
764    }
765    foreach name $extraPacksArr($this(platform)) {
766	::Splash::SetMsg [mc "Looking for %s" $name]...
767	if {![catch {package require $name} msg]} {
768	    set this(package,$name) 1
769	} else {
770	    ::Debug 2 "unable to load package: $name, reason: $msg"
771	}
772    }
773    if {$this(package,Itcl)} {
774	uplevel #0 {namespace import ::itcl::*}
775    }
776    if {!($this(package,printer) && $this(package,gdi))} {
777	set this(package,printer) 0
778    }
779
780    # Not ready for this yet.
781    set this(package,Thread) 0
782}
783
784# Init::Config --
785#
786#       Sets any 'config' array entries.
787#       The config array shall be used for hardcoded configuration settings
788#       that can be overriden only at build time by adding a config.tcl file
789#       in resources/.
790#       array set config {junk "The Junk" ...}
791
792proc ::Init::Config {} {
793    global  this config
794
795    set f [file join $this(resourcePath) config.tcl]
796    if {[file exists $f]} {
797	source $f
798    }
799
800    # Let any user defined config file override hard coded and build configs.
801    set f [file join $this(prefsPath) resources config.tcl]
802    if {[file exists $f]} {
803	source $f
804    }
805}
806
807proc ::Init::SetHostname {} {
808    global  this
809
810    set this(hostname) [info hostname]
811}
812
813#-------------------------------------------------------------------------------
814