diff --git a/ChangeLog b/ChangeLog index 5ad105ff7..ffe733c6c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,21 @@ +1999-03-09 Gordon Matzigkeit + + * shared_src/asm.S (biosdisk): Compute location of + disk_address_packet correctly. From OKUJI Yoshinori. + 1999-03-08 Gordon Matzigkeit + * docs/grub.texi: New Texinfo documentation. + + * shared_src/disk_io.c (set_device): First stab at interpreting + Mach-style partition naming. + + * shared_src/stage2.c (run_menu): Don't say it was a failure if + enter_cmdline returns nonzero... just wait for a key. + + * shared_src/cmdline.c (enter_cmdline): Return nonzero, and avoid + the fallback command if we did an install. + * shared_src/asm.S (_start): New explicit symbol to supress warnings. diff --git a/NEWS b/NEWS index 2808cfab8..6fda03621 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,9 @@ NEWS - list of user-visible changes between releases of GRUB New in XXX: -* The GRUB stage2 uses LBA mode and AWARD extensions if they are supported. +* Preliminary LBA and AWARD BIOS disk extension support. Please + contact bug-grub@gnu.org if you know you have a machine for which + this support would be useful, and would like to help in debugging. New in 0.5.90 - 1999-03-01, Gordon Matzigkeit: * Bug fixes. diff --git a/docs/Makefile.am b/docs/Makefile.am index 3a0d47f32..45b60a8b0 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -2,4 +2,5 @@ html = boot-proposal.html errors.html faq.html grub.html install.html \ mem64mb.html technical.html using.html txt = PC_partitioning.txt bios_mapping.txt commands.txt embedded_data.txt \ filesystem.txt +info_TEXINFOS = grub.texi EXTRA_DIST = BUGS $(txt) $(html) menu.lst diff --git a/docs/Makefile.in b/docs/Makefile.in index 13ce48fc4..ebaa6b40a 100644 --- a/docs/Makefile.in +++ b/docs/Makefile.in @@ -74,19 +74,27 @@ html = boot-proposal.html errors.html faq.html grub.html install.html \ mem64mb.html technical.html using.html txt = PC_partitioning.txt bios_mapping.txt commands.txt embedded_data.txt \ filesystem.txt +info_TEXINFOS = grub.texi EXTRA_DIST = BUGS $(txt) $(html) menu.lst mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_CLEAN_FILES = -DIST_COMMON = COPYING Makefile.am Makefile.in TODO +TEXI2DVI = texi2dvi +TEXINFO_TEX = $(srcdir)/texinfo.tex +INFO_DEPS = grub.info +DVIS = grub.dvi +TEXINFOS = grub.texi +DIST_COMMON = COPYING Makefile.am Makefile.in TODO mdate-sh stamp-vti \ +version.texi DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) TAR = tar GZIP = --best -all: Makefile +all: Makefile $(INFO_DEPS) .SUFFIXES: +.SUFFIXES: .dvi .info .ps .texi .texinfo .txi $(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) cd $(top_srcdir) && $(AUTOMAKE) --gnu docs/Makefile @@ -94,6 +102,140 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES) cd $(top_builddir) \ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + +version.texi: stamp-vti + cp $(srcdir)/stamp-vti $(srcdir)/version.texi + +stamp-vti: grub.texi $(top_srcdir)/configure.in + @echo "@set UPDATED `cd $(srcdir) \ + && $(SHELL) ./mdate-sh grub.texi`" > vti.tmp + @echo "@set EDITION $(VERSION)" >> vti.tmp + @echo "@set VERSION $(VERSION)" >> vti.tmp + @cmp -s vti.tmp $(srcdir)/stamp-vti \ + || (echo "Updating $(srcdir)/stamp-vti"; \ + cp vti.tmp $(srcdir)/stamp-vti) + -@rm -f vti.tmp + +mostlyclean-vti: + -rm -f vti.tmp + +clean-vti: + +distclean-vti: + +maintainer-clean-vti: + -rm -f stamp-vti version.texi + +grub.info: grub.texi version.texi +grub.dvi: grub.texi version.texi + + +DVIPS = dvips + +.texi.info: + @cd $(srcdir) && rm -f $@ $@-[0-9] $@-[0-9][0-9] + cd $(srcdir) \ + && $(MAKEINFO) `echo $< | sed 's,.*/,,'` + +.texi.dvi: + TEXINPUTS=$(srcdir):$$TEXINPUTS \ + MAKEINFO='$(MAKEINFO) -I $(srcdir)' $(TEXI2DVI) $< + +.texi: + @cd $(srcdir) && rm -f $@ $@-[0-9] $@-[0-9][0-9] + cd $(srcdir) \ + && $(MAKEINFO) `echo $< | sed 's,.*/,,'` + +.texinfo.info: + @cd $(srcdir) && rm -f $@ $@-[0-9] $@-[0-9][0-9] + cd $(srcdir) \ + && $(MAKEINFO) `echo $< | sed 's,.*/,,'` + +.texinfo: + @cd $(srcdir) && rm -f $@ $@-[0-9] $@-[0-9][0-9] + cd $(srcdir) \ + && $(MAKEINFO) `echo $< | sed 's,.*/,,'` + +.texinfo.dvi: + TEXINPUTS=$(srcdir):$$TEXINPUTS \ + MAKEINFO='$(MAKEINFO) -I $(srcdir)' $(TEXI2DVI) $< + +.txi.info: + @cd $(srcdir) && rm -f $@ $@-[0-9] $@-[0-9][0-9] + cd $(srcdir) \ + && $(MAKEINFO) `echo $< | sed 's,.*/,,'` + +.txi.dvi: + TEXINPUTS=$(srcdir):$$TEXINPUTS \ + MAKEINFO='$(MAKEINFO) -I $(srcdir)' $(TEXI2DVI) $< + +.txi: + @cd $(srcdir) && rm -f $@ $@-[0-9] $@-[0-9][0-9] + cd $(srcdir) \ + && $(MAKEINFO) `echo $< | sed 's,.*/,,'` +.dvi.ps: + $(DVIPS) $< -o $@ + +install-info-am: $(INFO_DEPS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(infodir) + @for file in $(INFO_DEPS); do \ + d=$(srcdir); \ + for ifile in `cd $$d && echo $$file $$file-[0-9] $$file-[0-9][0-9]`; do \ + if test -f $$d/$$ifile; then \ + echo " $(INSTALL_DATA) $$d/$$ifile $(DESTDIR)$(infodir)/$$ifile"; \ + $(INSTALL_DATA) $$d/$$ifile $(DESTDIR)$(infodir)/$$ifile; \ + else : ; fi; \ + done; \ + done + @$(POST_INSTALL) + @if $(SHELL) -c 'install-info --version | sed 1q | fgrep -s -v -i debian' >/dev/null 2>&1; then \ + for file in $(INFO_DEPS); do \ + echo " install-info --info-dir=$(DESTDIR)$(infodir) $(DESTDIR)$(infodir)/$$file";\ + install-info --info-dir=$(DESTDIR)$(infodir) $(DESTDIR)$(infodir)/$$file || :;\ + done; \ + else : ; fi + +uninstall-info: + $(PRE_UNINSTALL) + @if $(SHELL) -c 'install-info --version | sed 1q | fgrep -s -v -i debian' >/dev/null 2>&1; then \ + ii=yes; \ + else ii=; fi; \ + for file in $(INFO_DEPS); do \ + test -z "$ii" \ + || install-info --info-dir=$(DESTDIR)$(infodir) --remove $$file; \ + done + @$(NORMAL_UNINSTALL) + for file in $(INFO_DEPS); do \ + (cd $(DESTDIR)$(infodir) && rm -f $$file $$file-[0-9] $$file-[0-9][0-9]); \ + done + +dist-info: $(INFO_DEPS) + for base in $(INFO_DEPS); do \ + d=$(srcdir); \ + for file in `cd $$d && eval echo $$base*`; do \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file; \ + done; \ + done + +mostlyclean-aminfo: + -rm -f grub.aux grub.cp grub.cps grub.dvi grub.fn grub.fns grub.ky \ + grub.kys grub.ps grub.log grub.pg grub.toc grub.tp grub.tps \ + grub.vr grub.vrs grub.op grub.tr grub.cv grub.cn + +clean-aminfo: + +distclean-aminfo: + +maintainer-clean-aminfo: + for i in $(INFO_DEPS); do \ + rm -f $$i; \ + if test "`echo $$i-[0-9]*`" != "$$i-[0-9]*"; then \ + rm -f $$i-[0-9]*; \ + fi; \ + done tags: TAGS TAGS: @@ -114,25 +256,27 @@ distdir: $(DISTFILES) || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ || cp -p $$d/$$file $(distdir)/$$file; \ done -info: -dvi: + $(MAKE) top_distdir="$(top_distdir)" distdir="$(distdir)" dist-info +info: $(INFO_DEPS) +dvi: $(DVIS) check: all $(MAKE) installcheck: install-exec: @$(NORMAL_INSTALL) -install-data: +install-data: install-info-am @$(NORMAL_INSTALL) install: install-exec install-data all @: -uninstall: +uninstall: uninstall-info install-strip: $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' INSTALL_SCRIPT='$(INSTALL_PROGRAM)' install installdirs: + $(mkinstalldirs) $(DESTDIR)$(infodir) mostlyclean-generic: @@ -149,21 +293,24 @@ distclean-generic: maintainer-clean-generic: -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) -mostlyclean: mostlyclean-generic +mostlyclean: mostlyclean-vti mostlyclean-aminfo mostlyclean-generic -clean: clean-generic mostlyclean +clean: clean-vti clean-aminfo clean-generic mostlyclean -distclean: distclean-generic clean +distclean: distclean-vti distclean-aminfo distclean-generic clean -rm -f config.status -maintainer-clean: maintainer-clean-generic distclean +maintainer-clean: maintainer-clean-vti maintainer-clean-aminfo \ + maintainer-clean-generic distclean @echo "This command is intended for maintainers to use;" @echo "it deletes files that may require special tools to rebuild." -.PHONY: tags distdir info dvi installcheck install-exec install-data \ -install uninstall all installdirs mostlyclean-generic distclean-generic \ -clean-generic maintainer-clean-generic clean mostlyclean distclean \ -maintainer-clean +.PHONY: mostlyclean-vti distclean-vti clean-vti maintainer-clean-vti \ +install-info-am uninstall-info mostlyclean-aminfo distclean-aminfo \ +clean-aminfo maintainer-clean-aminfo tags distdir info dvi installcheck \ +install-exec install-data install uninstall all installdirs \ +mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/docs/grub.texi b/docs/grub.texi new file mode 100644 index 000000000..7a3fef877 --- /dev/null +++ b/docs/grub.texi @@ -0,0 +1,185 @@ +\input texinfo @c -*-texinfo-*- +@setfilename grub.info +@include version.texi + +@dircategory Kernel +@direntry +* GRUB: (grub). The GRand Unified Bootloader. +@end direntry + +@ifinfo +Copyright @copyright{} 1996 Erich Boleyn +Copyright @copyright{} 1999 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries a copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@end ignore + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that +the entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end ifinfo + +@c @setchapternewpage odd +@settitle GRUB Manual +@titlepage +@finalout +@title The GRUB Manual +@author Gordon Matzigkeit +@page + +@vskip 0pt plus 1filll +Copyright @copyright{} 1996 Erich Boleyn +Copyright @copyright{} 1999 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that +the entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end titlepage + +@node Top +@top GRUB + +This file documents GNU GRUB, the Grand Unified Bootloader. This +edition documents version @value{VERSION}. + +@menu +* Introduction:: Capturing the spirit of GRUB. + +@detailmenu + --- The Detailed Node Listing --- + +Introduction + +* History:: From maggot to house fly. +* Features:: How GRUB is different. +* Role of a bootloader:: Judging a system by its bootloader. + +@end detailmenu +@end menu + +@node Introduction +@chapter Introduction + +Briefly, a @dfn{bootloader} is the first software program that runs when +a computer starts. It is responsible for loading and transferring +control to the operating system @dfn{kernel} software (such as the Linux +or Hurd kernels). The kernel, in turn, initializes the rest of the +operating system (usually GNU). + +@menu +* History:: From maggot to house fly. +* Features:: How GRUB is different. +* Role of a bootloader:: Judging a system by its bootloader. +@end menu + + +@node History +@section History of GRUB + +GRUB originated in 1995 when Erich Boleyn was trying to boot the GNU +Hurd with the University of Utah's Mach 4 microkernel (now known as GNU +Mach). Erich and Brian Ford designed the Multiboot Standard (FIXME +xref), because they were determined not to add to the large number of +mutually-incompatible PC boot methods. + +Erich then began modifying the FreeBSD bootloader so that it would +understand Multiboot. He quickly realized that it would be a lot easier +to write his own bootloader from scratch than to keep working on the +FreeBSD bootloader, and so GRUB was born. + +Erich added many features to GRUB, but other priorities prevented him +from keeping up with the demands of its quickly-expanding user base. In +1999, Gordon Matzigkeit adopted GRUB as an official GNU package, and +opened its development by making the latest sources available via +anonymous CVS.@footnote{The repository is +@code{:pserver:anoncvs@@anoncvs.gnu.org:/gd/gnu/anoncvsroot}, module +@code{grub}. Just hit return when prompted for a password.} + + +@node Features +@section GRUB features +technical.html: why another bootloader? + + +@node Role of a bootloader +@section The role of a bootloader + +The following is a quotation from Gordon Matzigkeit, a GRUB fanatic: + +@quotation +Some people like to acknowlege both the operating system and kernel when +they talk about their computers, so they might say they use +``GNU/Linux'' or ``GNU/Hurd''. Other people seem to think that the +kernel is the most important part of the system, so they like to call +their GNU operating systems ``Linux systems.'' + +I, personally, believe that this is a grave injustice, because the +@emph{bootloader} is the most important software of all. So, I used to +refer to the above systems as either ``LILO''@footnote{The LInux LOader, +a bootloader that everybody uses, but nobody likes.} or ``GRUB'' +systems. + +Unfortunately, nobody ever understood what I was talking about; now I +just use the word ``GNU'' as a pseudonym for GRUB. + +So, if you ever hear people talking about their alleged ``GNU'' systems, +remember that they are actually paying homage to the best bootloader +around@dots{} GRUB! +@end quotation + +We, the GRUB maintainers, do not (usually) encourage Gordon's level of +fanaticism, but it helps to remember that bootloaders deserve careful +design. We hope at least that you enjoy using GNU GRUB as much as we +did writing it. + + +@chapter Using +@section using.html + + +@chapter Boot scripts +@section commands.txt + + +@chapter Disk partitions + + +@chapter Filesystems +@section filesystem.txt + + +@chapter Troubleshooting +@section errors.html + + +@chapter Multiboot +@section boot-proposal.html + + +@chapter Implementation +@section technical.html +@section embedded_data.txt +@section PC_patitioning.txt + +@bye diff --git a/docs/technical.html b/docs/technical.html index c2adff4db..b315d063c 100644 --- a/docs/technical.html +++ b/docs/technical.html @@ -61,7 +61,7 @@ only room for a very sparse functionality.

GRUB is very large compared to other bootloaders (typically 20 to 30 K). A mechanism is in place which can load all of it's components for different -installal methods. +installation methods. The feeling from the author is that this will not be a problem since most of the features pay for themselves both in terms of space (certainly the generic decompression @@ -387,4 +387,3 @@ available via a command from the menu interface).

- diff --git a/ffs_stage1_5/Makefile.am b/ffs_stage1_5/Makefile.am index 6d223e1c6..85f00e72b 100644 --- a/ffs_stage1_5/Makefile.am +++ b/ffs_stage1_5/Makefile.am @@ -1,4 +1,6 @@ -IMPORTANT_SIZE_LIMIT = 7168 +# On ext2fs, we can install into EXT2_BOOT_LOADER_INO, which gives us: +# MAX-STAGE1-BLOCK-LIST * MIN-EXT2-BLOCK-SIZE = 80 * 1024 = 81920 +IMPORTANT_SIZE_LIMIT = 81920 pkgdatadir = $(datadir)/$(PACKAGE)/$(host_cpu)-$(host_vendor) pkgdata_DATA = ffs_stage1_5 diff --git a/ffs_stage1_5/Makefile.in b/ffs_stage1_5/Makefile.in index ac50b7357..a445a639c 100644 --- a/ffs_stage1_5/Makefile.in +++ b/ffs_stage1_5/Makefile.in @@ -10,6 +10,9 @@ # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. +# On ext2fs, we can install into EXT2_BOOT_LOADER_INO, which gives us: +# MAX-STAGE1-BLOCK-LIST * MIN-EXT2-BLOCK-SIZE = 80 * 1024 = 81920 + SHELL = /bin/sh @@ -68,7 +71,7 @@ host_vendor = @host_vendor@ sbingrub = @sbingrub@ stage2debug = @stage2debug@ -IMPORTANT_SIZE_LIMIT = 7168 +IMPORTANT_SIZE_LIMIT = 81920 pkgdatadir = $(datadir)/$(PACKAGE)/$(host_cpu)-$(host_vendor) pkgdata_DATA = ffs_stage1_5 diff --git a/shared_src/asm.S b/shared_src/asm.S index 08a3d7c7a..fc9391e50 100644 --- a/shared_src/asm.S +++ b/shared_src/asm.S @@ -372,7 +372,12 @@ ENTRY(biosdisk) movw 0x1c(%ebp), %ax /* segment */ shll $4, %eax movl %eax, dap_buffer - movl $disk_address_packet, %esi + + /* compute the address of disk_address_packet */ + movl $disk_address_packet, %eax + movw %ax, %si + shrl $4, %eax + movw %ax, %cx /* save the segment to cx */ xorb %bl, %bl movb 0x8(%ebp), %bh /* read=0, write=1 */ @@ -383,6 +388,7 @@ ENTRY(biosdisk) call EXT_C(prot_to_real) /* enter real mode */ .code16 + movw %cx, %ds movw %bx, %ax int $0x13 /* do the operation */ @@ -446,7 +452,7 @@ disk_compute_args: .align 4 disk_address_packet: - .byte 0x20 /* length of packet */ + .byte 0x10 /* length of packet */ .byte 0 /* reserved */ dap_blocks: .word 0 /* number of blocks */ @@ -625,6 +631,7 @@ hard_drive: /* Wahoo! Got LBA! */ movb $0x1, %bh + data32 jmp 2f 1: xorb %bh, %bh /* Too bad, no LBA */ diff --git a/shared_src/char_io.c b/shared_src/char_io.c index e375a0cf0..37e258ddd 100644 --- a/shared_src/char_io.c +++ b/shared_src/char_io.c @@ -533,17 +533,17 @@ substring (char *s1, char *s2) { while (*s1 == *s2) { - /* The strings match, so return 0. */ - if (!*(s1++)) + /* The strings match exactly. */ + if (! *(s1++)) return 0; - s2++; + s2 ++; } - /* S1 is shorter than S2. */ + /* S1 is a substring of S2. */ if (*s1 == 0) return -1; - /* S1 is a substring of S2. */ + /* S1 isn't a substring. */ return 1; } diff --git a/shared_src/disk_io.c b/shared_src/disk_io.c index 019bfd27b..7a7b21361 100644 --- a/shared_src/disk_io.c +++ b/shared_src/disk_io.c @@ -1,6 +1,7 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 1996 Erich Boleyn + * Copyright (C) 1996 Erich Boleyn + * Copyright (C) 1999 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -593,16 +594,16 @@ set_device (char *device) part_choice = PART_CHOSEN; retval++; } - if (*device == ',') + else if (*device == ',') { /* Either an absolute PC or BSD partition. */ disk_choice = 0; - part_choice++; + part_choice ++; device++; if (*device >= '0' && *device <= '9') { - part_choice++; + part_choice ++; current_partition = 0; if (!(current_drive & 0x80) @@ -625,7 +626,7 @@ set_device (char *device) } else if (*device >= 'a' && *device <= 'h') { - part_choice++; + part_choice ++; current_partition = ((*(device++) - 'a') << 8) | 0xFF00FF; } @@ -634,13 +635,85 @@ set_device (char *device) if (part_choice == PART_DISK) { current_partition = saved_partition; - part_choice++; + part_choice ++; } retval++; } } } + else + { + char ch; + + /* A Mach-style absolute partition name. */ + ch = *device; + if (*device != 'f' && *device != 'h' || + (device += 2, (*(device - 1) != 'd'))) + errnum = ERR_DEV_FORMAT; + else + safe_parse_maxint (&device, (int *) ¤t_drive); + + disk_choice = 0; + if (ch != 'f') + current_drive += 0x80; + + if (errnum) + return 0; + + if (*device == '/') + { + part_choice = PART_CHOSEN; + retval ++; + } + else if (*device == 's') + { + /* An absolute PC partition. */ + disk_choice = 0; + part_choice ++; + device ++; + + if (*device >= '0' && *device <= '9') + { + part_choice ++; + current_partition = 0; + + if (!(current_drive & 0x80) || + !safe_parse_maxint (&device, (int *) ¤t_partition) || + (--current_partition) > 254) + { + errnum = ERR_DEV_FORMAT; + return 0; + } + + current_partition = (current_partition << 16) + 0xFFFF; + + if (*device >= 'a' && *device <= 'h') + { + /* A BSD partition within the slice. */ + current_partition = (((*(device ++) - 'a') << 8) + | (current_partition & 0xFF00FF)); + } + } + } + else if (*device >= 'a' && *device <= 'h') + { + /* An absolute BSD partition. */ + part_choice ++; + current_partition = ((*(device ++) - 'a') << 8) | 0xFF00FF; + } + + if (*device == '/') + { + if (part_choice == PART_DISK) + { + current_partition = saved_partition; + part_choice ++; + } + + retval ++; + } + } if (retval) retval = device + 1; @@ -711,6 +784,11 @@ set_bootdev (int hdbias) static char * setup_part (char *filename) { + /* FIXME: decide on syntax for blocklist vs. old-style vs. /dev/hd0s1 */ + /* Strip any leading /dev. */ + if (substring ("/dev/", filename) < 1) + filename += 5; + if (*filename == '(') { if ((filename = set_device (filename)) == 0)