Actually exit loop on '\0' while printing a string.
[wine] / tools / wineconf
CommitLineData
21979019 1#!/usr/bin/perl -w
0e270f45
AJ
2
3# This program generates wine.conf files on STDOUT.
0799c1a7
AJ
4# Copyright (C) 1996 Stephen Simmons
5#
6# This library is free software; you can redistribute it and/or
7# modify it under the terms of the GNU Lesser General Public
8# License as published by the Free Software Foundation; either
9# version 2.1 of the License, or (at your option) any later version.
10#
11# This library is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14# Lesser General Public License for more details.
15#
16# You should have received a copy of the GNU Lesser General Public
17# License along with this library; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19#
20# NOTES:
21#
0e270f45
AJ
22# This program examines the contents of the DOS filesystems and
23# attempts to generate a sensible wine.conf file. This is output
24# to STDOUT.
f32f9181 25# It reads /etc/fstab to find mounting locations of the hard disk drives
0e270f45
AJ
26# It uses the correct algorithm for ordering DOS drives, with the
27# exception of the case of multiple drive controller types, where I don't
28# know what DOS's algorithm is.
29# It uses find to find all of the win.ini files on any DOS partition
30# and sorts them by age to guess which is part of the active Windows
31# installation.
32# It reads the autoexec.bat file (if found) and records all variable
33# settings. There are some inaccuracies in its determination.
34# First, while variables are interpolated properly, no control
35# structures are supported so calls and execs to other batch files are
36# ignored, and all variable settings take effect regardless of whether
37# they would in DOS (i,e., both if and else clauses are read).
38# This is used to determine the path and temp directories. Duplicate
39# path directories and path directories that don't exist are thrown
40# out.
41# On failing to find C:\AUTOEXEC.BAT, wineconf finds all executables
42# in the windows directory and subdirectories, and generates an
43# optimized path statement encompassing all the executables.
44# Then it also looks for \TEMP and \TMP on all drives taking the first
45# one it finds.
46# wineconf doesn't support floppy drives, network drives, printers,
47# and serial device configuration is hardcoded and not configured for
48# the machine it runs on. Similarly, spy parameters are hard coded.
49
696ff195 50# It would make sense to incorporate much of the heuristic code in
0e270f45
AJ
51# this program into a library to be shared with a dosemu configuration
52# program, because it seems that at least some of the same stuff will
53# be wanted. The program needs to be cleaned up still. A better tmp
54# search algorithm could be written. A fast option is planned. Less
55# Linux-dependence is desired. Should look for devices independent
56# of /etc/fstab; then sanity checks on /etc/fstab can be performed.
57
58use Getopt::Long;
59use File::Basename;
455414cf 60use strict;
0e270f45
AJ
61use Carp;
62
d654f94f 63GetOptions('windir=s', 'sysdir=s', 'thorough', 'debug:s', 'inifile=s') || &Usage;
0e270f45 64
696ff195
AM
65print "WINE REGISTRY Version 2\n";
66print ";; All keys relative to \\\\Machine\\\\Software\\\\Wine\\\\Wine\\\\Config\n\n";
0e270f45
AJ
67&ReadFSTAB();
68&FindWindowsDir();
69&ReadAutoexecBat();
70&StandardStuff();
71
72sub Usage {
73 print "Usage: $0 <options>\n";
74# print "-fstab <filename> Location of alternate fstab file\n";
75 print "-windir <filename> Location of windows dir in DOS space\n";
76 print "-thorough Do careful analysis (default)\n";
77 print "-sysdir <filename> Location of systems dir in DOS space\n";
d654f94f 78 print "-inifile <filename> Path to the wine.ini file (by default './wine.ini')\n";
0e270f45
AJ
79# print "-tmpdir <filename> Location of tmp directory\n";
80 print "Generates (to STDOUT) a wine configuration file based on\n";
81 print "/etc/fstab and searching around in DOS directories\n";
82 print "The options above can override certain values\n";
83 print "This should be considered ALPHA code\n";
84 exit(0);
85}
86
87sub ReadFSTAB {
455414cf
EP
88 $::opt_f = $::opt_f ? $::opt_f : '/etc/fstab';
89 open(FSTAB, $::opt_f) || die "Cannot read $::opt_f\n";
0e270f45
AJ
90 while(<FSTAB>) {
91 next if /^\s*\#/;
92 next if /^\s*$/;
455414cf
EP
93
94 my ($device, $mntpoint, $type, @rest) = split(' ', $_);
21979019 95 if ($device !~ m"^/dev/fd") {
4940c377
DN
96 if ($type eq "ntfs") {
97 push(@::FatDrives, [$device, $mntpoint, 'win95']);
98 }
99 elsif ($type eq "msdos" || $type eq "vfat") {
9bd682d8 100 push(@::FatDrives, [$device, $mntpoint, $type]);
21979019 101 }
9bd682d8 102 elsif ($type eq "iso9660" ||
4940c377 103 ($mntpoint eq "/cdrom" && ! $type eq 'supermount') ||
9bd682d8
OK
104 ($device eq '/dev/cdrom' && $type eq 'auto') ) {
105 push(@::CdromDrives, [$device, $mntpoint, 'win95']);
21979019 106 }
7cae558b 107 elsif ( ($mntpoint eq '/mnt/cdrom' || $mntpoint eq '/cdrom')
4940c377
DN
108 && $type eq 'supermount') {
109 push(@::CdromDrives, [ '/dev/cdrom', $mntpoint, 'win95']);
110 }
0e270f45
AJ
111 }
112 }
455414cf 113 if (!@::FatDrives) {
0e270f45
AJ
114 warn "ERROR ($0): Cannot find any MSDOS drives.\n";
115 warn "This does not mean you cannot run Wine, but $0\n";
116 warn "cannot help you (yet)\n";
117 exit(1);
118 }
9bd682d8
OK
119 push(@::UnixDrives, ['', '/tmp', 'hd']);
120 push(@::UnixDrives, ['', '${HOME}', 'network']);
455414cf
EP
121 my $MagicDrive = 'C';
122 @::FatDrives = sort byDriveOrder @::FatDrives;
123 @::CdromDrives = sort byCdOrder @::CdromDrives;
124 foreach my $FatDrive (@::FatDrives) {
0e270f45 125 print "[Drive $MagicDrive]\n";
455414cf 126 my $MntPoint = $FatDrive->[1];
9bd682d8 127 my $FileSys = $FatDrive->[2];
696ff195
AM
128 print "\"Path\" = \"$MntPoint\"\n";
129 print "\"Type\" = \"hd\"\n";
130 print "\"Filesystem\" = \"$FileSys\"\n";
0e270f45
AJ
131 print "\n";
132 &RegisterDrive($MagicDrive, $FatDrive);
133 if(!&IsMounted($FatDrive->[0])) {
7cae558b 134 warn "WARNING: DOS Drive $MagicDrive (" . $FatDrive->[0] .
0e270f45
AJ
135 ") is not mounted\n";
136 }
137 $MagicDrive++;
138 }
455414cf 139 foreach my $CdromDrive (@::CdromDrives) {
0e270f45 140 print "[Drive $MagicDrive]\n";
9bd682d8 141 my $Device = $CdromDrive->[0];
455414cf 142 my $MntPoint = $CdromDrive->[1];
9bd682d8 143 my $FileSys = $CdromDrive->[2];
696ff195
AM
144 print "\"Path\" = \"$MntPoint\"\n";
145 print "\"Type\" = \"cdrom\"\n";
146 print "\"Device\" = \"$Device\"\n";
147 print "\"Filesystem\" = \"$FileSys\"\n";
0e270f45
AJ
148 print "\n";
149 &RegisterDrive($MagicDrive, $CdromDrive);
150 $MagicDrive++;
151 }
9bd682d8
OK
152 foreach my $UnixDrive (@::UnixDrives) {
153 print "[Drive $MagicDrive]\n";
154 my $MntPoint = $UnixDrive->[1];
155 my $Type = $UnixDrive->[2];
696ff195
AM
156 print "\"Path\" = \"$MntPoint\"\n";
157 print "\"Type\" = \"$Type\"\n";
158 print "\"Filesystem\" = \"win95\"\n";
9bd682d8
OK
159 print "\n";
160 $MagicDrive++;
161 }
0e270f45
AJ
162}
163
164sub FindWindowsDir {
165 my($MagicDrive) = 'C';
455414cf 166 my(@FATD)=@::FatDrives;
0e270f45 167 my(@wininis) = ();
455414cf 168 my ($winini);
322d0873 169 my ($ThisDrive);
455414cf
EP
170
171 if (!$::opt_windir && !$::opt_fast && !$::opt_thorough) {
172 $::opt_thorough++;
0e270f45 173 }
455414cf
EP
174 if ($::opt_windir) {
175 $winini = &ToUnix($::opt_windir);
0e270f45
AJ
176 if (!-e $winini) {
177 die "ERROR: Specified winini file does not exist\n";
178 }
179 }
455414cf 180 elsif ($::opt_fast) {
0e270f45
AJ
181 die "-fast code can be implemented\n";
182 }
455414cf
EP
183 elsif ($::opt_thorough) {
184 if ($::opt_debug) { print STDERR "DEBUG: Num FATD = ", $#FATD+1, "\n"; }
322d0873 185 foreach $ThisDrive (@FATD) {
455414cf 186 my $MntPoint = $ThisDrive->[1];
6562310d 187 push(@wininis, `find $MntPoint -iname win.ini -print`);
0e270f45
AJ
188 }
189 foreach $winini (@wininis) {
190 chomp $winini;
191 }
455414cf 192 my ($winini_cnt) = $#wininis+1;
7cae558b 193 if ($::opt_debug) {
0e270f45
AJ
194 print STDERR "DEBUG: Num wininis found: $winini_cnt\n";}
195 if ($winini_cnt > 1) {
196 warn "$winini_cnt win.ini files found:\n";
197 @wininis = sort byFileAge @wininis;
198 warn join("\n", @wininis), "\n";
199 $winini = $wininis[0];
200 warn "Using most recent one: $winini\n";
201 }
202 elsif ($winini_cnt == 0) {
203 die "ERROR: No win.ini found in DOS partitions\n";
204 }
205 else {
206 $winini = $wininis[0];
207 }
208 }
209 else {
210 die "ERROR: None of -windir, -fast, or -thorough set\n";
211 }
455414cf 212 $::windir = &ToDos(dirname($winini));
0e270f45 213 print "[wine]\n";
1da4707f 214 print "\"windows\" = ", &marshall ($::windir), "\n";
455414cf 215 if ($::opt_sysdir) {
1da4707f 216 print "\"system\" = ", &marshall ($::opt_sysdir), "\n";
0e270f45
AJ
217 }
218 else {
1da4707f 219 print "\"system\" = ", &marshall ("$::windir\\SYSTEM"), "\n";
0e270f45
AJ
220 }
221}
222
223# Returns 1 if the device is mounted; -1 if mount check failed; 0 if not
224# mounted.
225# This code is Linux specific, and needs to be broadened.
226sub IsMounted {
227 my($Device) = @_;
228 if (-d "/proc") {
229 if (-e "/proc/mounts") {
7cae558b 230 open(MOUNTS, "/proc/mounts") ||
0e270f45
AJ
231 (warn "Cannot open /proc/mounts, although it exists\n" &&
232 return -1);
233 while(<MOUNTS>) {
7cae558b 234 if (/^$Device/) {
0e270f45
AJ
235 return 1; # Tested 1.4
236 }
237 }
238 return 0; # Tested 1.4
239 }
240 }
241 return -1;
242}
243
244sub RegisterDrive {
690142dc 245 my($DOSdrive, $Drive) = @_;
455414cf
EP
246 $::DOS2Unix{$DOSdrive} = $Drive;
247 $::Device2DOS{$Drive->[0]} = $DOSdrive;
248 $::MntPoint2DOS{$Drive->[1]} = $DOSdrive;
249 $::DOS2MntPoint{$DOSdrive} = $Drive->[1];
250 $::DOS2Device{$DOSdrive} = $Drive->[0];
0e270f45
AJ
251}
252
253sub ReadAutoexecBat {
455414cf
EP
254 if (!%::DOS2Unix) { &ReadFSTAB; }
255 my($DriveC) = $::DOS2MntPoint{"C"};
0e270f45
AJ
256 $DriveC =~ s%/$%%;
257 my($path);
7cae558b 258 if ($::opt_debug) {
0e270f45
AJ
259 print STDERR "DEBUG: Looking for $DriveC/autoexec.bat\n"; }
260 if (-e "$DriveC/autoexec.bat") {
261 # Tested 1.4
7cae558b 262 open(AUTOEXEC, "$DriveC/autoexec.bat") ||
0e270f45
AJ
263 die "Cannot read autoexec.bat\n";
264 while(<AUTOEXEC>) {
265 s/\015//;
266 if (/^\s*(set\s+)?(\w+)\s*[\s\=]\s*(.*)$/i) {
267 my($varname) = $2;
268 my($varvalue) = $3;
269 chomp($varvalue);
270 $varname =~ tr/A-Z/a-z/;
271 while ($varvalue =~ /%(\w+)%/) {
455414cf
EP
272 my $matchname = $1;
273 my $subname = $1;
0e270f45 274 $subname =~ tr/A-Z/a-z/;
696ff195 275 if (($::opt_debug) && ($::opt_debug =~ /path/i)) {
0e270f45
AJ
276 print STDERR "DEBUG: Found $matchname as $subname\n";
277 print STDERR "DEBUG: Old varvalue:\n$varvalue\n";
278 print STDERR "DEBUG: Old subname value:\n" .
455414cf 279 $::DOSenv{$subname} . "\n";
0e270f45 280 }
455414cf
EP
281 if ($::DOSenv{$subname}) {
282 $varvalue =~ s/\%$matchname\%/$::DOSenv{$subname}/;
0e270f45
AJ
283 }
284 else {
7cae558b 285 warn "DOS environment variable $subname not\n";
0e270f45
AJ
286 warn "defined in autoexec.bat. (Reading config.sys\n";
287 warn "is not implemented.) Using null value\n";
288 $varvalue =~ s/%$matchname%//;
289 }
696ff195 290 if (($::opt_debug) && ($::opt_debug =~ /path/i)) {
0e270f45
AJ
291 print STDERR "DEBUG: New varvalue:\n$varvalue\n";
292 }
293 }
455414cf 294 if ($::opt_debug) {
0e270f45
AJ
295 print STDERR "DEBUG: $varname = $varvalue\n";
296 }
455414cf 297 $::DOSenv{$varname} = $varvalue;
0e270f45
AJ
298 }
299 }
300 close(AUTOEXEC);
301 }
302 else {
303 # Tested 1.4
21979019 304 warn "WARNING: C:\\AUTOEXEC.BAT was not found.\n";
0e270f45
AJ
305 }
306
455414cf
EP
307 if ($::DOSenv{"path"}) {
308 my @pathdirs = split(/\s*;\s*/, $::DOSenv{"path"});
696ff195 309 if (($::opt_debug) && ($::opt_debug =~ /path/i)) {
0e270f45
AJ
310 print STDERR "DEBUG (path): @pathdirs\n";
311 }
455414cf 312 foreach my $pathdir (@pathdirs) {
0e270f45 313 if (-d &ToUnix($pathdir)) {
455414cf 314 if ($::DOSpathdir{$pathdir}++) {
0e270f45
AJ
315 warn "Ignoring duplicate DOS path entry $pathdir\n";
316 }
317 else {
696ff195 318 if (($::opt_debug) && ($::opt_debug =~ /path/i)) {
0e270f45
AJ
319 print STDERR "DEBUG (path): Found $pathdir\n";
320 }
455414cf 321 push(@::DOSpathlist, $pathdir);
0e270f45
AJ
322 }
323 }
324 else {
325 warn "Ignoring DOS path directory $pathdir, as it does not\n";
326 warn "exist\n";
327 }
328 }
1da4707f 329 print "\"path\" = ", &marshall (join (";", @::DOSpathlist)), "\n";
0e270f45
AJ
330 }
331 else {
332 # Code status: tested 1.4
333 warn "WARNING: Making assumptions for PATH\n";
334 warn "Will scan windows directory for executables and generate\n";
335 warn "path from that\n";
455414cf 336 my $shellcmd = 'find ' . &ToUnix($::windir) . " -iregex '" .
0e270f45 337 '.*\.\(exe\|bat\|com\|dll\)' . "' -print";
7cae558b 338 if ($::opt_debug) {
0e270f45
AJ
339 print STDERR "DEBUG: autoexec.bat search command:\n $shellcmd\n";
340 }
455414cf 341 push(@::DOScommand, `$shellcmd`);
7cae558b 342 if ($::opt_debug && $::opt_debug =~ /autoexec/i) {
455414cf 343 print STDERR "DEBUG: autoexec.bat search results:\n\@DOS::command\n";
0e270f45 344 }
455414cf 345 foreach my $command (@::DOScommand) {
0e270f45 346 $command =~ s%[^/]+$%%;
67019ae0 347 $::DOSexecdir{&ToDos($command)}++;
0e270f45 348 }
1da4707f 349 print "\"path\" = " .
7cae558b
AJ
350 &marshall (join(";",
351 grep(s%\\$%%,
1da4707f
MW
352 sort {$::DOSexecdir{$b} <=> $::DOSexecdir{$a}}
353 (keys %::DOSexecdir)))) . "\n";
0e270f45
AJ
354 }
355
455414cf 356 if ($::DOSenv{"temp"} && -d &ToUnix($::DOSenv{"temp"})) {
1da4707f 357 print "\"temp\" = ", &marshall ($::DOSenv{"temp"}), "\n";
0e270f45
AJ
358 }
359 else {
455414cf
EP
360 my $TheTemp;
361
0e270f45
AJ
362 warn "WARNING: Making assumptions for TEMP\n";
363 warn "Looking for \\TEMP and then \\TMP on every drive\n";
364 # Watch out .. might pick CDROM drive :-)
455414cf
EP
365 foreach my $DOSdrive (keys %::DOS2Unix) {
366 my $tmp = &ToUnix("$DOSdrive:\\temp");
0e270f45
AJ
367 if (-d $tmp) { $TheTemp = "$DOSdrive:\\temp"; last; }
368 $tmp = &ToUnix("$DOSdrive:\\tmp");
369 if (-d $tmp) { $TheTemp = "$DOSdrive:\\tmp"; last; }
370 }
455414cf 371 $TheTemp = '/tmp' if (!$TheTemp && -d '/tmp');
0e270f45
AJ
372 if ($TheTemp) {
373 warn "Using $TheTemp\n";
1da4707f 374 print "\"temp\" = ", &marshall ($TheTemp), "\n";
0e270f45
AJ
375 }
376 else {
377 warn "Using C:\\\n";
1da4707f 378 print "\"temp\" = ", &marshall ("C:\\"), "\n";
0e270f45
AJ
379 }
380 }
381 print "\n";
382}
383
384# FNunix = &ToUnix(FNdos);
385# Converts DOS filenames to Unix filenames, leaving Unix filenames
386# untouched.
387sub ToUnix {
388 my($FNdos) = @_;
389 my($FNunix);
390
391 # Initialize tables if necessary.
455414cf 392 if (!%::DOS2Unix) { &ReadFSTAB; }
0e270f45
AJ
393
394 # Determine which type of conversion is necessary
395 if ($FNdos =~ /^([A-Z])\:(.*)$/) { # DOS drive specified
455414cf 396 $FNunix = $::DOS2MntPoint{$1} . "/$2";
0e270f45
AJ
397 }
398 elsif ($FNdos =~ m%\\%) { # DOS drive not specified, C: is default
455414cf 399 $FNunix = $::DOS2MntPoint{"C"} . "/$FNdos";
0e270f45
AJ
400 }
401 else { # Unix filename
402 $FNunix = $FNdos;
403 }
404 1 while ($FNunix =~ s%\\%/%); # Convert \ to /
405 $FNunix =~ tr/A-Z/a-z/; # Translate to lower case
406 1 while ($FNunix =~ s%//%/%); # Translate double / to /
407 return $FNunix;
408}
409
410# FNdos = &ToDOS(FNunix)
411# Converts Unix filenames to DOS filenames
412sub ToDos {
413 my($FNunix) = @_;
455414cf
EP
414 my(@MntList) = keys %::MntPoint2DOS;
415 my ($TheMntPt, $FNdos);
416
417 foreach my $MntPt (@MntList) { # Scan mount point list to see if path matches
0e270f45
AJ
418 if ($FNunix =~ /^$MntPt/) {
419 $TheMntPt = $MntPt;
420 last;
421 }
422 }
423 if (!$TheMntPt) {
424 Carp("ERROR: $FNunix not found in DOS directories\n");
425 exit(1);
426 }
427 $FNdos = $FNunix;
428 $FNdos =~ s/^$TheMntPt//;
455414cf 429 $FNdos = $::MntPoint2DOS{$TheMntPt} . ":" . $FNdos;
0e270f45
AJ
430 1 while($FNdos =~ s%/%\\%);
431 return $FNdos;
432}
433
455414cf
EP
434sub InsertDefaultFile {
435 my ($fileName, $tag) = @_;
436 my $state = 0;
437
438 if (open(DEFFILE, "$fileName")) {
439 while (<DEFFILE>) {
440 $state = 0 if ($state == 1 && $_ =~ /^[ \t]*\#/o && index($_, "</$tag>") >= 0);
441 print $_ if ($state == 1);
442 $state = 1 if ($state == 0 && $_ =~ /^[ \t]*\#/o && index($_, "<$tag>" ) >= 0);
443 }
444 close(DEFFILE);
445 } else {
446 print STDERR "Cannot read $fileName\n";
447 }
448}
0e270f45 449
1da4707f
MW
450sub marshall {
451 my ($s) = @_;
452 $s =~ s/\\/\\\\/g;
453 return "\"$s\"";
454}
455
456
0e270f45 457sub StandardStuff {
d654f94f
LU
458 if (!$::opt_inifile) {
459 &InsertDefaultFile("./wine.ini", "wineconf");
460 } else {
461 &InsertDefaultFile($::opt_inifile, "wineconf");
462 }
0e270f45
AJ
463}
464
465sub byFileAge {
466 -M $a <=> -M $b;
467}
468
469sub byDriveOrder {
470 my($DeviceA) = $a->[0];
471 my($DeviceB) = $b->[0];
472
473 # Primary drives come first, logical drives last
474 # DOS User's Guide (version 6) p. 70, IBM version.
475 # If both drives are the same type, sort alphabetically
476 # This makes drive a come before b, etc.
477 # It also makes SCSI drives come before IDE drives;
478 # this may or may not be right :-(
479 my($Alogical, $Blogical);
480 if (substr($DeviceA, 3, 1) >= 5) { $Alogical++; }
481 if (substr($DeviceB, 3, 1) >= 5) { $Blogical++; }
482 if ($Alogical && !$Blogical) { return -1; }
483 elsif ($Blogical && !$Alogical) { return 1; }
484 else { return ($DeviceA cmp $DeviceB); }
485}
486
487sub byCdOrder {
488 my($DeviceA) = $a->[0];
489 my($DeviceB) = $b->[0];
490 $DeviceA cmp $DeviceB;
491}