First working version.
[wine.git] / tools / wineconf
blob83237a9e2723236705c63cf48b7ea9816c82e556
1 #!/usr/bin/perl -w
3 # This program generates wine.conf files on STDOUT.
4 # (C) 1996 Stephen Simmons
5 # Redistributable under Wine License
7 $RCS_ID = '$Id$ ';
9 # This program examines the contents of the DOS filesystems and
10 # attempts to generate a sensible wine.conf file. This is output
11 # to STDOUT.
12 # It reads /etc/FSTAB to find mounting locations of the hard disk drives
13 # It uses the correct algorithm for ordering DOS drives, with the
14 # exception of the case of multiple drive controller types, where I don't
15 # know what DOS's algorithm is.
16 # It uses find to find all of the win.ini files on any DOS partition
17 # and sorts them by age to guess which is part of the active Windows
18 # installation.
19 # It reads the autoexec.bat file (if found) and records all variable
20 # settings. There are some inaccuracies in its determination.
21 # First, while variables are interpolated properly, no control
22 # structures are supported so calls and execs to other batch files are
23 # ignored, and all variable settings take effect regardless of whether
24 # they would in DOS (i,e., both if and else clauses are read).
25 # This is used to determine the path and temp directories. Duplicate
26 # path directories and path directories that don't exist are thrown
27 # out.
28 # On failing to find C:\AUTOEXEC.BAT, wineconf finds all executables
29 # in the windows directory and subdirectories, and generates an
30 # optimized path statement encompassing all the executables.
31 # Then it also looks for \TEMP and \TMP on all drives taking the first
32 # one it finds.
33 # wineconf doesn't support floppy drives, network drives, printers,
34 # and serial device configuration is hardcoded and not configured for
35 # the machine it runs on. Similarly, spy parameters are hard coded.
37 # It would make sense to incorporate much of the hueristic code in
38 # this program into a library to be shared with a dosemu configuration
39 # program, because it seems that at least some of the same stuff will
40 # be wanted. The program needs to be cleaned up still. A better tmp
41 # search algorithm could be written. A fast option is planned. Less
42 # Linux-dependence is desired. Should look for devices independent
43 # of /etc/fstab; then sanity checks on /etc/fstab can be performed.
45 use Getopt::Long;
46 use File::Basename;
47 use strict;
48 use Carp;
50 GetOptions('windir=s', 'sysdir=s', 'thorough', 'debug:s') || &Usage;
52 &ReadFSTAB();
53 &FindWindowsDir();
54 &ReadAutoexecBat();
55 &StandardStuff();
57 sub Usage {
58 print "Usage: $0 <options>\n";
59 # print "-fstab <filename> Location of alternate fstab file\n";
60 print "-windir <filename> Location of windows dir in DOS space\n";
61 print "-thorough Do careful analysis (default)\n";
62 print "-sysdir <filename> Location of systems dir in DOS space\n";
63 # print "-tmpdir <filename> Location of tmp directory\n";
64 print "Generates (to STDOUT) a wine configuration file based on\n";
65 print "/etc/fstab and searching around in DOS directories\n";
66 print "The options above can override certain values\n";
67 print "This should be considered ALPHA code\n";
68 exit(0);
71 sub ReadFSTAB {
72 $::opt_f = $::opt_f ? $::opt_f : '/etc/fstab';
73 open(FSTAB, $::opt_f) || die "Cannot read $::opt_f\n";
74 while(<FSTAB>) {
75 next if /^\s*\#/;
76 next if /^\s*$/;
78 my ($device, $mntpoint, $type, @rest) = split(' ', $_);
79 if ($device !~ m"^/dev/fd") {
80 if ($type eq "msdos" || $type eq "vfat") {
81 push(@::FatDrives, [$device, $mntpoint]);
83 elsif ($type eq "iso9660" || ($device eq '/dev/cdrom' && $type eq 'auto') ) {
84 push(@::CdromDrives, [$device, $mntpoint]);
88 if (!@::FatDrives) {
89 warn "ERROR ($0): Cannot find any MSDOS drives.\n";
90 warn "This does not mean you cannot run Wine, but $0\n";
91 warn "cannot help you (yet)\n";
92 exit(1);
94 my $MagicDrive = 'C';
95 @::FatDrives = sort byDriveOrder @::FatDrives;
96 @::CdromDrives = sort byCdOrder @::CdromDrives;
97 foreach my $FatDrive (@::FatDrives) {
98 print "[Drive $MagicDrive]\n";
99 my $MntPoint = $FatDrive->[1];
100 print "Path=$MntPoint\n";
101 print "Type=hd\n";
102 print "\n";
103 &RegisterDrive($MagicDrive, $FatDrive);
104 if(!&IsMounted($FatDrive->[0])) {
105 warn "WARNING: DOS Drive $MagicDrive (" . $FatDrive->[0] .
106 ") is not mounted\n";
108 $MagicDrive++;
110 foreach my $CdromDrive (@::CdromDrives) {
111 print "[Drive $MagicDrive]\n";
112 my $MntPoint = $CdromDrive->[1];
113 print "Path=$MntPoint\n";
114 print "Type=cdrom\n";
115 print "\n";
116 &RegisterDrive($MagicDrive, $CdromDrive);
117 $MagicDrive++;
121 sub FindWindowsDir {
122 my($MagicDrive) = 'C';
123 my(@FATD)=@::FatDrives;
124 my(@wininis) = ();
125 my ($winini);
127 if (!$::opt_windir && !$::opt_fast && !$::opt_thorough) {
128 $::opt_thorough++;
130 if ($::opt_windir) {
131 $winini = &ToUnix($::opt_windir);
132 if (!-e $winini) {
133 die "ERROR: Specified winini file does not exist\n";
136 elsif ($::opt_fast) {
137 die "-fast code can be implemented\n";
139 elsif ($::opt_thorough) {
140 if ($::opt_debug) { print STDERR "DEBUG: Num FATD = ", $#FATD+1, "\n"; }
141 foreach(@FATD) {
142 my $ThisDrive = shift(@FATD);
143 my $MntPoint = $ThisDrive->[1];
144 push(@wininis, `find $MntPoint -name win.ini -print`);
146 foreach $winini (@wininis) {
147 chomp $winini;
149 my ($winini_cnt) = $#wininis+1;
150 if ($::opt_debug) {
151 print STDERR "DEBUG: Num wininis found: $winini_cnt\n";}
152 if ($winini_cnt > 1) {
153 warn "$winini_cnt win.ini files found:\n";
154 @wininis = sort byFileAge @wininis;
155 warn join("\n", @wininis), "\n";
156 $winini = $wininis[0];
157 warn "Using most recent one: $winini\n";
159 elsif ($winini_cnt == 0) {
160 die "ERROR: No win.ini found in DOS partitions\n";
162 else {
163 $winini = $wininis[0];
166 else {
167 die "ERROR: None of -windir, -fast, or -thorough set\n";
169 $::windir = &ToDos(dirname($winini));
170 print "[wine]\n";
171 print "windows=$::windir\n";
172 if ($::opt_sysdir) {
173 print "system=$::opt_sysdir\n";
175 else {
176 print "system=$::windir\\SYSTEM\n";
180 # Returns 1 if the device is mounted; -1 if mount check failed; 0 if not
181 # mounted.
182 # This code is Linux specific, and needs to be broadened.
183 sub IsMounted {
184 my($Device) = @_;
185 if (-d "/proc") {
186 if (-e "/proc/mounts") {
187 open(MOUNTS, "/proc/mounts") ||
188 (warn "Cannot open /proc/mounts, although it exists\n" &&
189 return -1);
190 while(<MOUNTS>) {
191 if (/^$Device/) {
192 return 1; # Tested 1.4
195 return 0; # Tested 1.4
198 return -1;
201 sub RegisterDrive {
202 my($DOSdrive, $Drive) = @_;
203 $::DOS2Unix{$DOSdrive} = $Drive;
204 $::Device2DOS{$Drive->[0]} = $DOSdrive;
205 $::MntPoint2DOS{$Drive->[1]} = $DOSdrive;
206 $::DOS2MntPoint{$DOSdrive} = $Drive->[1];
207 $::DOS2Device{$DOSdrive} = $Drive->[0];
210 sub ReadAutoexecBat {
211 if (!%::DOS2Unix) { &ReadFSTAB; }
212 my($DriveC) = $::DOS2MntPoint{"C"};
213 $DriveC =~ s%/$%%;
214 my($path);
215 if ($::opt_debug) {
216 print STDERR "DEBUG: Looking for $DriveC/autoexec.bat\n"; }
217 if (-e "$DriveC/autoexec.bat") {
218 # Tested 1.4
219 open(AUTOEXEC, "$DriveC/autoexec.bat") ||
220 die "Cannot read autoexec.bat\n";
221 while(<AUTOEXEC>) {
222 s/\015//;
223 if (/^\s*(set\s+)?(\w+)\s*[\s\=]\s*(.*)$/i) {
224 my($varname) = $2;
225 my($varvalue) = $3;
226 chomp($varvalue);
227 $varname =~ tr/A-Z/a-z/;
228 while ($varvalue =~ /%(\w+)%/) {
229 my $matchname = $1;
230 my $subname = $1;
231 $subname =~ tr/A-Z/a-z/;
232 if ($::opt_debug =~ /path/i) {
233 print STDERR "DEBUG: Found $matchname as $subname\n";
234 print STDERR "DEBUG: Old varvalue:\n$varvalue\n";
235 print STDERR "DEBUG: Old subname value:\n" .
236 $::DOSenv{$subname} . "\n";
238 if ($::DOSenv{$subname}) {
239 $varvalue =~ s/\%$matchname\%/$::DOSenv{$subname}/;
241 else {
242 warn "DOS environment variable $subname not\n";
243 warn "defined in autoexec.bat. (Reading config.sys\n";
244 warn "is not implemented.) Using null value\n";
245 $varvalue =~ s/%$matchname%//;
247 if ($::opt_debug =~ /path/i) {
248 print STDERR "DEBUG: New varvalue:\n$varvalue\n";
251 if ($::opt_debug) {
252 print STDERR "DEBUG: $varname = $varvalue\n";
254 $::DOSenv{$varname} = $varvalue;
257 close(AUTOEXEC);
259 else {
260 # Tested 1.4
261 warn "WARNING: C:\\AUTOEXEC.BAT was not found.\n";
264 if ($::DOSenv{"path"}) {
265 my @pathdirs = split(/\s*;\s*/, $::DOSenv{"path"});
266 if ($::opt_debug =~ /path/i) {
267 print STDERR "DEBUG (path): @pathdirs\n";
269 foreach my $pathdir (@pathdirs) {
270 if (-d &ToUnix($pathdir)) {
271 if ($::DOSpathdir{$pathdir}++) {
272 warn "Ignoring duplicate DOS path entry $pathdir\n";
274 else {
275 if ($::opt_debug =~ /path/i) {
276 print STDERR "DEBUG (path): Found $pathdir\n";
278 push(@::DOSpathlist, $pathdir);
281 else {
282 warn "Ignoring DOS path directory $pathdir, as it does not\n";
283 warn "exist\n";
286 print "path=" . join(";", @::DOSpathlist) . "\n";
288 else {
289 # Code status: tested 1.4
290 warn "WARNING: Making assumptions for PATH\n";
291 warn "Will scan windows directory for executables and generate\n";
292 warn "path from that\n";
293 my $shellcmd = 'find ' . &ToUnix($::windir) . " -iregex '" .
294 '.*\.\(exe\|bat\|com\|dll\)' . "' -print";
295 if ($::opt_debug) {
296 print STDERR "DEBUG: autoexec.bat search command:\n $shellcmd\n";
298 push(@::DOScommand, `$shellcmd`);
299 if ($::opt_debug && $::opt_debug =~ /autoexec/i) {
300 print STDERR "DEBUG: autoexec.bat search results:\n\@DOS::command\n";
302 foreach my $command (@::DOScommand) {
303 $command =~ s%[^/]+$%%;
304 $::DOSexecdir{$command}++;
306 print "path=" .
307 join(";",
308 grep(s%/$%%,
309 sort {$::DOSexecdir{$b} <=> $::DOSexecdir{$a}}
310 (keys %::DOSexecdir))) . "\n";
313 if ($::DOSenv{"temp"} && -d &ToUnix($::DOSenv{"temp"})) {
314 print "temp=" . $::DOSenv{"temp"} . "\n";
316 else {
317 my $TheTemp;
319 warn "WARNING: Making assumptions for TEMP\n";
320 warn "Looking for \\TEMP and then \\TMP on every drive\n";
321 # Watch out .. might pick CDROM drive :-)
322 foreach my $DOSdrive (keys %::DOS2Unix) {
323 my $tmp = &ToUnix("$DOSdrive:\\temp");
324 if (-d $tmp) { $TheTemp = "$DOSdrive:\\temp"; last; }
325 $tmp = &ToUnix("$DOSdrive:\\tmp");
326 if (-d $tmp) { $TheTemp = "$DOSdrive:\\tmp"; last; }
328 $TheTemp = '/tmp' if (!$TheTemp && -d '/tmp');
329 if ($TheTemp) {
330 warn "Using $TheTemp\n";
331 print "temp=$TheTemp\n";
333 else {
334 warn "Using C:\\\n";
335 print "temp=C:\\\n";
338 print "\n";
341 # FNunix = &ToUnix(FNdos);
342 # Converts DOS filenames to Unix filenames, leaving Unix filenames
343 # untouched.
344 sub ToUnix {
345 my($FNdos) = @_;
346 my($FNunix);
348 # Initialize tables if necessary.
349 if (!%::DOS2Unix) { &ReadFSTAB; }
351 # Determine which type of conversion is necessary
352 if ($FNdos =~ /^([A-Z])\:(.*)$/) { # DOS drive specified
353 $FNunix = $::DOS2MntPoint{$1} . "/$2";
355 elsif ($FNdos =~ m%\\%) { # DOS drive not specified, C: is default
356 $FNunix = $::DOS2MntPoint{"C"} . "/$FNdos";
358 else { # Unix filename
359 $FNunix = $FNdos;
361 1 while ($FNunix =~ s%\\%/%); # Convert \ to /
362 $FNunix =~ tr/A-Z/a-z/; # Translate to lower case
363 1 while ($FNunix =~ s%//%/%); # Translate double / to /
364 return $FNunix;
367 # FNdos = &ToDOS(FNunix)
368 # Converts Unix filenames to DOS filenames
369 sub ToDos {
370 my($FNunix) = @_;
371 my(@MntList) = keys %::MntPoint2DOS;
372 my ($TheMntPt, $FNdos);
374 foreach my $MntPt (@MntList) { # Scan mount point list to see if path matches
375 if ($FNunix =~ /^$MntPt/) {
376 $TheMntPt = $MntPt;
377 last;
380 if (!$TheMntPt) {
381 Carp("ERROR: $FNunix not found in DOS directories\n");
382 exit(1);
384 $FNdos = $FNunix;
385 $FNdos =~ s/^$TheMntPt//;
386 $FNdos = $::MntPoint2DOS{$TheMntPt} . ":" . $FNdos;
387 1 while($FNdos =~ s%/%\\%);
388 return $FNdos;
391 sub InsertDefaultFile {
392 my ($fileName, $tag) = @_;
393 my $state = 0;
395 if (open(DEFFILE, "$fileName")) {
396 while (<DEFFILE>) {
397 $state = 0 if ($state == 1 && $_ =~ /^[ \t]*\#/o && index($_, "</$tag>") >= 0);
398 print $_ if ($state == 1);
399 $state = 1 if ($state == 0 && $_ =~ /^[ \t]*\#/o && index($_, "<$tag>" ) >= 0);
401 close(DEFFILE);
402 } else {
403 print STDERR "Cannot read $fileName\n";
407 sub StandardStuff {
408 &InsertDefaultFile("./wine.ini", "wineconf");
411 sub byFileAge {
412 -M $a <=> -M $b;
415 sub byDriveOrder {
416 my($DeviceA) = $a->[0];
417 my($DeviceB) = $b->[0];
419 # Primary drives come first, logical drives last
420 # DOS User's Guide (version 6) p. 70, IBM version.
421 # If both drives are the same type, sort alphabetically
422 # This makes drive a come before b, etc.
423 # It also makes SCSI drives come before IDE drives;
424 # this may or may not be right :-(
425 my($Alogical, $Blogical);
426 if (substr($DeviceA, 3, 1) >= 5) { $Alogical++; }
427 if (substr($DeviceB, 3, 1) >= 5) { $Blogical++; }
428 if ($Alogical && !$Blogical) { return -1; }
429 elsif ($Blogical && !$Alogical) { return 1; }
430 else { return ($DeviceA cmp $DeviceB); }
433 sub byCdOrder {
434 my($DeviceA) = $a->[0];
435 my($DeviceB) = $b->[0];
436 $DeviceA cmp $DeviceB;