diff --git a/.bzrignore b/.bzrignore index 8cdc9d12d..46e8637b6 100644 --- a/.bzrignore +++ b/.bzrignore @@ -25,6 +25,8 @@ docs/version.texi *.exec genkernsyms.sh gensymlist.sh +gentrigtables +grub-bin2h grub-dumpbios grub-editenv grub-emu @@ -40,8 +42,13 @@ grub-pe2elf grub-probe grub_probe_init.c grub_probe_init.h +grub-reboot +grub-script-check +grub_script_check_init.c +grub_script_check_init.h grub_script.tab.c grub_script.tab.h +grub-set-default grub-setup grub_setup_init.c grub_setup_init.h @@ -62,4 +69,5 @@ stamp-h stamp-h1 stamp-h.in symlist.c +trigtables.c update-grub_lib diff --git a/ChangeLog b/ChangeLog index 64aa9d593..92c8dc53c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,2068 @@ +2010-04-24 Christian Franke + + * Makefile.in (TARGET_LDFLAGS): Add -static-libgcc. + (kernel_img_LDFLAGS): Remove -static-libgcc. + +2010-04-24 Christian Franke + + * configure.ac: Do not CHECK_BSS_START_SYMBOL + and CHECK_END_SYMBOL if grub-emu is built. + Unset TARGET_OBJ2ELF if grub-emu is built + without module support. + +2010-04-24 Jiro SEKIBA + + Nilfs2 support. + + * conf/common.rmk (grub_probe_SOURCES): Add fs/nilfs2.c. + (grub_fstest_SOURCES): Likewise. + (pkglib_MODULES): Add nilfs2.mod. + (nilfs2_mod_SOURCES): New variable. + (nilfs2_mod_CFLAGS): Likewise. + (nilfs2_mod_LDFLAGS): Likewise. + * conf/i386-pc.rmk (grub_setup_SOURCES): Add fs/nilfs2.c. + * conf/sparc64-ieee1275.rmk (grub_setup_SOURCES): Add fs/nilfs2.c. + * fs/nilfs2.c: New file. + +2010-04-21 Vladimir Serbinenko + + * configure.ac: Refuse to compile for x86_64-efi is mcmodel=large + is not supported. + +2010-04-19 Grégoire Sutre + + Add grub-mkconfig support for NetBSD. + + * util/grub.d/10_netbsd.in: grub-mkconfig helper script for NetBSD. + * util/grub-mkconfig.in: export new NetBSD specific variables. + * po/POTFILES-shell: added 10_netbsd.in. + * util/grub-mkconfig_lib.in: check for gettext binary, default to echo. + +2010-04-19 BVK Chaitanya + + Fix emu build with grub-emu-pci and grub-emu-modules. + + * include/grub/util/misc.h: Export grub_util_{info,error,warn} + functions. + * include/grub/libpciaccess.h: New file. + * conf/any-emu.rmk: Update kernel headers for emu build. + +2010-04-19 Vladimir Serbinenko + + * fs/udf.c (grub_udf_iterate_dir): Silence a spurious warning. + +2010-04-19 Vladimir Serbinenko + + * fs/udf.c (grub_udf_iterate_dir): Decode the Unicode filenames. + +2010-04-18 Vladimir Serbinenko + + * boot/sparc64/ieee1275/boot.S: Various size-reducing changes. + Retrieve chosen/bootpath if bootpath isn't hardcoded. + * conf/sparc64-ieee1275.rmk (grub_setup_SOURCES): Add + util/ieee1275/ofpath.c. + * util/sparc64/ieee1275/grub-ofpathname.c: Renamed to ... + * util/ieee1275/grub-ofpathname.c: ... this. All users updated + * include/grub/sparc64/ieee1275/boot.h + (GRUB_BOOT_MACHINE_KERNEL_SECTOR): Renamed to ... + (GRUB_BOOT_MACHINE_KERNEL_BYTE): ...this. Moved 8 bytes lower. + * util/hostdisk.c (grub_util_biosdisk_get_osdev): New function. + * util/ieee1275/ofpath.c (grub_util_devname_to_ofpath): Make argument + const char *. + * util/sparc64/ieee1275/grub-setup.c (compute_dest_ofpath): Removed. + (setup): Use KERNEL_BYTE instead of KERNEL_SECTOR. + Use grub_util_devname_to_ofpath. Zero-fill boot_devpath on same disk + install. + +2010-04-18 Grégoire Sutre + + * util/grub-mkconfig.in: Corrected two == equality tests. + Set grub_prefix as in grub-install for NetBSD and OpenBSD. + * configure.ac: All definitions and uses of TARGET_IMG_LDFLAGS_AC now + expect a number appended to it. + * acinclude.m4 (grub_PROG_OBJCOPY_ABSOLUTE): ${TARGET_IMG_LDFLAGS_AC} + expects a number appended to it. + +2010-04-18 Vladimir Serbinenko + + * po/POTFILES: Renamed multiboot_loader.c to multiboot.c + +2010-04-18 Vladimir Serbinenko + + * util/hostdisk.c (make_device_name): Change to new partition naming. + +2010-04-17 Vladimir Serbinenko + + * disk/lvm.c (grub_lvm_memberlist): Issue an error if pv->disk = 0. + +2010-04-17 Christian Franke + + * Makefile.in: Add missing localedir setting. + +2010-04-14 Colin Watson + + Restore TEXTDOMAINDIR correction from r1889, lost apparently by + mistake in r2156. Noticed by Anthony Fok. + + * util/grub.d/10_kfreebsd.in (TEXTDOMAINDIR): Set to lowercased + @localedir@. + * util/grub.d/10_linux.in (TEXTDOMAINDIR): Likewise. + +2010-04-14 BVK Chaitanya + + Fix a spurious, uninitialized variable warning. + + * loader/i386/bsdXX.c (grub_freebsd_load_elfmodule_obj): + Initialize variable, shdr. + (grub_freebsd_load_elfmodule): Likewise. + (grub_freebsd_load_elf_meta): Likewise. + +2010-04-13 BVK Chaitanya + + Fix for escaped dollar in double quoted strings. + + * script/yylex.l: Updated flex rules. + * conf/tests.rmk: Rule for new testcase. + * tests/grub_script_dollar.in: New testcase. + +2010-04-13 Carles Pina i Estany +2010-04-13 Colin Watson + + Enclose all translated strings in grub.cfg in single quotes, and + escape them appropriately (Ubuntu bug #552921). + + * util/grub-mkconfig_lib.in (gettext_quoted): New function. + * util/grub.d/10_hurd.in: Use it. + * util/grub.d/10_kfreebsd.in (kfreebsd_entry): Likewise. + * util/grub.d/10_linux.in (linux_entry): Likewise. + +2010-04-11 Vladimir Serbinenko + + Fix cygwin compilation. + + * configure.ac: Define NEED_REGISTER_FRAME_INFO. + * include/grub/misc.h (__register_frame_info) + [NEED_REGISTER_FRAME_INFO && !UTIL]: New export. + (__deregister_frame_info) [NEED_REGISTER_FRAME_INFO && !UTIL]: Likewise. + * kern/misc.c (__register_frame_info) + [NEED_REGISTER_FRAME_INFO && !UTIL]: New empty function. + (__deregister_frame_info) [NEED_REGISTER_FRAME_INFO && !UTIL]: Likewise. + +2010-04-11 Vladimir Serbinenko + + * configure.ac: Respect grub_cv_asm_uscore when defining dummy symbols. + +2010-04-11 Vladimir Serbinenko + + Unify libgcc processing. + + * Makefile.in (kernel_img_LDFLAGS): New variable. + * conf/common.rmk (kernel_img_HEADERS): Add libgcc.h. + * conf/i386-coreboot.rmk (kernel_img_LDFLAGS): Append instead of + overwriting. + * conf/i386-ieee1275.rmk (kernel_img_LDFLAGS): Likewise. + * conf/i386-pc.rmk (kernel_img_LDFLAGS): Likewise. + * conf/i386-qemu.rmk (kernel_img_LDFLAGS): Likewise. + * conf/x86-efi.rmk (kernel_img_LDFLAGS): Likewise. + * conf/mips-qemu-mips.rmk (kernel_img_LDFLAGS): Append instead of + overwriting. Remove -lgcc and -static-libgcc + * conf/mips-yeeloong.rmk (kernel_img_LDFLAGS): Likewise. + * conf/mips.rmk (kernel_img_HEADERS): Remove cpu/libgcc.h + * conf/powerpc-ieee1275.rmk (kernel_img_HEADERS): Remove cpu/libgcc.h + (kernel_img_LDFLAGS): Append instead of overwriting. + Remove -lgcc and -static-libgcc + * conf/sparc64-ieee1275.rmk: Likewise. + * include/grub/powerpc/libgcc.h: Move to ... + * include/grub/libgcc.h: .. this. + * include/grub/libgcc.h: Don't export most of the function on x86. + (__bswapsi2): New export. + (__bswapdi2): Likewise. + * include/grub/mips/libgcc.h: Removed. + * include/grub/sparc64/libgcc.h: Likewise. + +2010-04-10 Vladimir Serbinenko + + * util/hostdisk.c (grub_util_biosdisk_get_grub_dev): Remove + disk_info_msg (conflicts with gettexting into languages with cases). + +2010-04-10 Grégoire Sutre + + Add grub-probe support for NetBSD. + + * util/getroot.c (find_root_device): Convert block device to + character device on NetBSD. + * util/probe.c (probe): Require character device on NetBSD. + * util/hostdisk.c: NetBSD specific headers. + (configure_device_driver): new function to tune device driver + parameters (currently only for NetBSD floppy driver). + (grub_util_biosdisk_open): NetBSD specific code (get disk size + via disklabel ioctl). + (open_device): call configure_device_driver on NetBSD. + (convert_system_partition_to_system_disk): NetBSD specific code. + (device_is_wholedisk): Likewise. + (grub_util_biosdisk_get_grub_dev): Likewise. + (make_device_name): Fixed a typo in bsd_part_str. + * configure.ac: check for opendisk() and getrawpartition() on + NetBSD and set LIBUTIL. + * Makefile.in: add LIBUTIL to LIBS. + +2010-04-10 BVK Chaitanya + + Documentation fix. + + * util/grub-script-check.c: Better help message. + +2010-04-10 BVK Chaitanya + + Fix FreeBSD build. + + * configure.ac: Flex version check. + * conf/common.rmk: Add -Wno-error to sh.mod. + * script/yylex.l: Remove all #pragma. + +2010-04-10 Vladimir Serbinenko + + * include/grub/util/misc.h (canonicalise_file_name): Add missing + prototype. + Reported by: Seth Goldberg. + +2010-04-10 Vladimir Serbinenko + + * loader/multiboot.c (GRUB_MOD_INIT) [GRUB_USE_MULTIBOOT2]: + Rename "module" to "module2". + Reported by: Seth Goldberg. + +2010-04-10 Vladimir Serbinenko + + * include/grub/efi/memory.h (grub_machine_mmap_iterate): Remove + EXPORT_FUNC. + Reported by: Seth Goldberg. + +2010-04-10 Vladimir Serbinenko + + * lib/posix_wrap/locale.h: Add missing file. + Reported by: Seth Goldberg. + +2010-04-10 Vladimir Serbinenko + + grub-emu module load support. + + * Makefile.in (TARGET_NO_MODULES): New variable. All users of + NO_DYNAMIC_MODULES switched to this. + (TARGET_CFLAGS): Add -DGRUB_TARGET_NO_MODULES=1 if applicable. + (CFLAGS): Likewise. + * conf/any-emu.rmk: Generate symlist. + (kernel_img_HEADERS): Add util/datetime.h. + (kernel_img_HEADERS) [sdl]: Add sdl.h. + (kernel_img_HEADERS) [libusb]: Add libusb.h. + (kernel_img_SOURCES) [TARGET_NO_MODULES = no && !x86]: Add + kern/$(target_cpu)/cache.S. + * configure.ac (grub-emu-modules): New option. + * genmk.rb: Handle multiple source lists. + * include/grub/sdl.h: New file. + * include/grub/libusb.h: Likewise. + * util/grub-emu.c (main): Hanle (host) root. + * util/hostdisk.c (grub_util_biosdisk_get_grub_dev): Error with + GRUB_ERR_UNKNOWN_DEVICE. + * util/misc.c: Move mm functions to ... + * util/mm.c: ... here. All users updated. + +2010-04-09 Vladimir Serbinenko + + * Makefile.in (RMKFILES): Search in srcdir and not current directory. + (MAINTAINER_CLEANFILES): Don't add $(srcdir) to MKFILES. Add few + missing files. + (maintainer-clean): Remove libgcrypt-grub. + +2010-04-09 Vladimir Serbinenko + + * term/efi/console.c (grub_console_checkkey): Macroify key contants. + +2010-04-09 EFI Coder + + * normal/menu_text.c (print_message): Clean up the message and show + the Fn information when on EFI + * term/efi/console.c (grub_console_checkkey): Add F4 support. + +2010-04-09 Vladimir Serbinenko + + * normal/autofs.c (read_fs_list): New parameter 'prefix'. + All users updated. + * normal/crypto.c (read_crypto_list): Likewise. + * normal/dyncmd.c (read_command_list): Likewise. + * normal/term.c (read_terminal_list): Likewise. + * normal/main.c (read_lists): Use explicit prefix. + (read_lists_hook): Use read_lists. + (grub_normal_execute): Likewise. + +2010-04-09 Vladimir Serbinenko + + * util/grub-mkrescue.in: Fix incorrect path in coreboot part. + Reported by: Thomas Schmitt. + Add -no-emul-boot to grub-mkisofs parameters. + +2010-04-09 Vladimir Serbinenko + + * font/font.c: Indented. + +2010-04-09 BVK Chaitanya + + Elif support to GRUB script (by Deepak Vankadaru). + + * tests/grub_script_if.in: New testcase. + * conf/tests.rmk: Rule for new testcase. + * script/parser.y: Grammar rules for elif. + +2010-04-09 BVK Chaitanya + + While and until loops support to GRUB script. + + * include/grub/script_sh.h (grub_script_cmdwhile): New struct. + (grub_script_create_cmdwhile): New function prototype. + (grub_script_execute_cmdwhile): New function prototype. + * script/execute.c (grub_script_execute_cmdwhile): New function. + * script/parser.y (command): New commands. + (whilecmd): New grammar rule. + (untilcmd): New grammar rule. + * script/script.c (grub_script_create_cmdwhile): New function. + * util/grub-script-check.c (grub_script_execute_cmdwhile): New + function. + + * tests/grub_script_while1.in: New testcase. + * conf/tests.rmk: Rule for new testcase. + +2010-04-09 Vladimir Serbinenko + + * util/grub.d/00_header.in: Add few missing quotes. Recognise *.jpeg + as *.jpg. + +2010-04-09 Mario Vazquez + + GRUB_BACKGROUND support. + + * util/grub-mkconfig.in: Export GRUB_BACKGROUND. + * util/grub.d/00_header.in: Parse GRUB_BACKGROUND. + +2010-04-09 Vladimir Serbinenko + + Load fonts and modules for gfxmenu in grub-mkconfig. + Idea by: Mario Vazquez + + * util/grub.d/00_header.in: Load pf2 and image modules. + +2010-04-09 Vladimir Serbinenko + + grub-mkconfig multiple terminal support. + + * util/grub-mkconfig.in: Handle multiple terminals correctly. + * util/grub.d/00_header.in: Likewise. + +2010-04-09 Vladimir Serbinenko + + * Makefile.in: Specify files explicitly instead of using $< and $@ since + we use cd $(srcdir). + +2010-04-08 Colin Watson + + * util/grub.d/10_linux.in: Only use the first word of + GRUB_DISTRIBUTOR for --class, to avoid problems if somebody puts + spaces in GRUB_DISTRIBUTOR. + * util/grub.d/10_kfreebsd.in: Likewise. + * util/grub.d/10_hurd.in: Likewise. + +2010-04-06 BVK Chaitanya + + Fix unit testing framework for Qemu 0.12. + + * tests/util/grub-shell.in: Remove -serial stdio option. + +2010-04-06 Vladimir Serbinenko + + POSIX header file wrappers. + + * lib/posix_wrap/assert.h: New file. Wrapper for its POSIX + equivalents. + * lib/posix_wrap/ctype.h: Likewise. + * lib/posix_wrap/errno.h: Likewise. + * lib/posix_wrap/langinfo.h: Likewise. + * lib/posix_wrap/limits.h: Likewise. + * lib/posix_wrap/localcharset.h: Likewise. + * lib/posix_wrap/stdint.h: Likewise. + * lib/posix_wrap/stdio.h: Likewise. + * lib/posix_wrap/stdlib.h: Likewise. + * lib/posix_wrap/string.h: Likewise. + * lib/posix_wrap/sys/types.h: Likewise. + * lib/posix_wrap/unistd.h: Likewise. + * lib/posix_wrap/wchar.h: Likewise. + * lib/posix_wrap/wctype.h: Likewise. + * conf/common.rmk (grub_script.yy.c): Remove #include elimination. + (grub_script.yy.h): Likewise. + * script/yylex.l: Remove POSIX emulation #defines. + * Makefile.in (POSIX_CFLAGS): New variable. + (GNULIB_UTIL_CFLAGS): Likewise. + + Regexp support. + + * conf/common.rmk (pkglib_MODULES): Add regexp.mod. + (regexp_mod_SOURCES): New variable. + (regexp_mod_CFLAGS): Likewise. + (regexp_mod_LDFLAGS): Likewise. + * commands/regexp.c: New file. + * gnulib/regcomp.c: New file. Imported from gnulib. + * gnulib/regex.c: Likewise. + * gnulib/regex_internal.c: Likewise. + * gnulib/regex_internal.h: Likewise. + * gnulib/regexec.c: Likewise. + * gnulib/regex.h: Likewise. + +2010-04-05 Vladimir Serbinenko + + * loader/i386/multiboot_mbi.c (grub_multiboot_load): Correctly report + unsupported video mode types. + +2010-04-05 Vladimir Serbinenko + + * kern/i386/pc/startup.S (grub_getrtsecs): Removed (dead code). + +2010-04-05 Vladimir Serbinenko + + * include/grub/i386/pc/init.h (grub_get_mmap_entry): Don't export. + * conf/i386-pc.rmk (kernel_img_HEADERS): Remove machine/init.h. + +2010-04-04 Vladimir Serbinenko + + Remove unused grub_vga_get_font. + + * kern/i386/pc/startup.S (grub_vga_get_font): Removed. + * include/grub/i386/pc/vga.h (grub_vga_get_font): Likewise. + +2010-04-03 Grégoire Sutre + + * kern/misc.c: Disable the __enable_execute_stack hack for utilities. + * include/grub/misc.h: Likewise. + +2010-04-03 Grégoire Sutre + + * util/grub-install.in: Add `|| exit 1' to all grub-probe calls + for which failure is fatal. + +2010-04-03 Grégoire Sutre + + * util/grub-install.in: Use mkdir -p to create grub directory. + * util/i386/efi/grub-install.in: Likewise. + * util/ieee1275/grub-install.in: Likewise. + +2010-04-03 Grégoire Sutre + + * Makefile.in (LEX): new variable. + +2010-04-03 Grégoire Sutre + + * util/i386/efi/grub-dumpdevtree: replaced the non-portable `==' by + `=' and added double quotes on operands of this equality test. + +2010-04-03 Vladimir Serbinenko + + * Makefile.in (uninstall): Remove a leftover debug echo. + Reported by: Grégoire Sutre + +2010-04-03 Vladimir Serbinenko + + MIPS multiboot2 support. + + * conf/mips.rmk (pkglib_MODULES): Add multiboot2.mod. + (multiboot2_mod_SOURCES): New variable. + (multiboot2_mod_CFLAGS): Likewise. + (multiboot2_mod_LDFLAGS): Likewise. + (multiboot2_mod_ASFLAGS): Likewise. + * include/grub/i386/multiboot.h (MULTIBOOT_INITIAL_STATE): New + definition. + (MULTIBOOT_ENTRY_REGISTER): Likewise. + (MULTIBOOT_MBI_REGISTER): Likewise. + (MULTIBOOT_ARCHITECTURE_CURRENT): Likewise. + (MULTIBOOT_ELF32_MACHINE): Likewise. + (MULTIBOOT_ELF64_MACHINE): Likewise. + * include/grub/mips/multiboot.h: New file. + * include/grub/video.h (grub_video_driver_id): New type + GRUB_VIDEO_DRIVER_SM712. + (grub_video_get_info_and_fini): Export. + (grub_video_get_palette): Likewise. + (grub_video_get_driver_id): Likewise. + * include/multiboot2.h: Resynced with spec. + * loader/i386/multiboot.c: Moved from here ... + * loader/multiboot.c: ... here. All users updated. + (grub_multiboot_boot): Use platform-specific macros. + * loader/i386/multiboot_elfxx.c: Moved from here ... + * loader/multiboot_elfxx.c: ... here. All users updated. + (E_MACHINE): Use MULTIBOOT_ELF32_MACHINE and MULTIBOOT_ELF64_MACHINE. + * loader/i386/multiboot_mbi2.c (grub_multiboot_load): Check arcitecture. + * video/sm712.c (grub_video_sm712_adapter): Add missing id field. + +2010-04-02 Vladimir Serbinenko + + Import gnulib argp module. + + * gnulib/argp-ba.c: New file. + * gnulib/argp-eexst.c: Likewise. + * gnulib/argp-fmtstream.c: Likewise. + * gnulib/argp-fmtstream.h: Likewise. + * gnulib/argp-fs-xinl.c: Likewise. + * gnulib/argp-help.c: Likewise. + * gnulib/argp-namefrob.h: Likewise. + * gnulib/argp-parse.c: Likewise. + * gnulib/argp-pin.c: Likewise. + * gnulib/argp-pv.c: Likewise. + * gnulib/argp-pvh.c: Likewise. + * gnulib/argp-version-etc.c: Likewise. + * gnulib/argp-version-etc.h: Likewise. + * gnulib/argp-xinl.c: Likewise. + * gnulib/argp.h: Likewise. + +2010-03-31 Vladimir Serbinenko + + * kern/device.c (grub_device_iterate): Clear errors after failed + opening device. + +2010-03-31 Vladimir Serbinenko + + * kern/ieee1275/openfw.c (grub_children_iterate): Skip device itself if + returned by firmware. + +2010-03-30 Vladimir Serbinenko + + * loader/i386/multiboot_mbi2.c (retrieve_video_parameters): Fix + compilation on coreboot and qemu + +2010-03-28 Vladimir Serbinenko + + * include/multiboot2.h: Resync with spec. + +2010-03-28 Vladimir Serbinenko + + Multiboot2 tag support + + * conf/i386.rmk (multiboot2_mod_SOURCES): Replace + loader/i386/multiboot_mbi.c with loader/i386/multiboot_mbi2.c. + Remove loader/multiboot_loader.c. + * include/grub/i386/multiboot.h (grub_multiboot_real_boot): Removed. + (grub_multiboot2_real_boot): Likewise. + * include/grub/multiboot.h (grub_multiboot_set_accepts_video): Removed. + (grub_get_multiboot_mmap_count): New proto. + (grub_fill_multiboot_mmap): Likewise. + (grub_multiboot_set_video_mode): Likewise. + (grub_multiboot_set_console): Likewise. + (grub_multiboot_load): Likewise. + (grub_multiboot_load_elf): Likewise. + (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT): New definition. + (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER): Likewise. + * include/multiboot.h: Resynced with specification. + * include/multiboot2.h: Resynced with specification. + * loader/i386/multiboot_mbi.c (DEFAULT_VIDEO_MODE): Moved from here... + * loader/i386/multiboot.c (DEFAULT_VIDEO_MODE): ... here. + * loader/i386/multiboot_mbi.c (HAS_VGA_TEXT): Moved from here .. + * include/grub/multiboot.h (GRUB_MACHINE_HAS_VGA_TEXT): ... here. All + users updated. + * loader/i386/multiboot_mbi.c (accepts_video): Moved from here... + * loader/i386/multiboot.c (accepts_video): ... here. All users updated. + * loader/i386/multiboot_mbi.c (grub_multiboot_set_accepts_video): + Removed. + * loader/i386/multiboot_mbi.c (grub_get_multiboot_mmap_len): + Moved from here... + * loader/i386/multiboot.c (grub_get_multiboot_mmap_len): ... here. + * loader/i386/multiboot_mbi.c (grub_fill_multiboot_mmap): + Moved from here... + * loader/i386/multiboot.c (grub_fill_multiboot_mmap): ... here. + * loader/i386/multiboot_mbi.c (set_video_mode): Moved from here... + * loader/i386/multiboot.c (grub_multiboot_set_video_mode): ... here. + All users updated. + * loader/i386/multiboot_mbi2.c: New file. + +2010-03-27 Vladimir Serbinenko + + Resync with gnulib. + + * Makefile.in (GNULIB_CFLAGS): New variable. + * conf/common.rmk (grub_mkisofs_CFLAGS): Add GNULIB_CFLAGS. + (grub_script_check_CFLAGS): New variable. + * gnulib/alloca.h: Resync with gnulib. + * gnulib/error.c: Likewise. + * gnulib/error.h: Likewise. + * gnulib/fnmatch.c: Likewise. + * gnulib/fnmatch_loop.c: Likewise. + * gnulib/getdelim.c: Likewise. + * gnulib/getline.c: Likewise. + * gnulib/getopt.c: Likewise. + * gnulib/getopt1.c: Likewise. + * gnulib/getopt_int.h: Likewise. + * gnulib/gettext.h: Likewise. + * gnulib/progname.c: Likewise. + * gnulib/progname.h: Likewise. + +2010-03-27 Grégoire Sutre + + Fix a build failure (-Wundef -Werror) when ENABLE_NLS is not defined, + which is the case with --disabled-nls. + + * include/grub/i18n.h: Use (defined(ENABLE_NLS) + && ENABLE_NLS) instead of ENABLE_NLS in all #if preprocessor macros. + * util/misc.c: Likewise. + * util/mkisofs/mkisofs.c: Likewise. + * util/mkisofs/mkisofs.h: Likewise. + +2010-03-27 Vladimir Serbinenko + + Simplify Apple CC support. + + * commands/i386/pc/drivemap_int13h.S: Use LOCAL when possible. + Add 0 byte at the end not to have a symbol with empty target. + * mmap/i386/pc/mmap_helper.S: Likewise. + * genmk.rb: Ignore errors 2030 and 2050. + * kern/i386/pc/startup.S: Use LOCAL when possible. + +2010-03-26 BVK Chaitanya + + Testcase and the fix for final semicolon on cmdline. + + * tests/grub_script_final_semicolon.in: New testcase. + * conf/tests.rmk: Rules for the new testcase. + * script/parser.y: Grammar fix. + +2010-03-26 BVK Chaitanya + + Blank lines testcase for GRUB script. + + * tests/grub_script_blanklines.in: New testcase. + * conf/tests.rmk: Rules for the new testcase. + +2010-03-26 Vladimir Serbinenko + + Don't use __FILE__. + + * genmk.rb: Add -DGRUB_FILE to all C targets. + * fs/reiserfs.c: Replace __FILE__ with GRUB_FILE. + * include/grub/list.h: Likewise. + * include/grub/misc.h: Likewise. + * include/grub/mm.h: Likewise. + * include/grub/test.h: Likewise. + * kern/mm.c: Likewise. + * lib/libgcrypt_wrap/cipher_wrap.h: Likewise. + +2010-03-26 Vladimir Serbinenko + + Sunpc partitions support. + + * conf/common.rmk (grub_probe_SOURCES): Add partmap/sunpc.c. + (grub_fstest_SOURCES): Likewise. + (pkglib_MODULES): Add part_sunpc.mod. + (part_sunpc_mod_SOURCES): New variable. + (part_sunpc_mod_CFLAGS): Likewise. + (part_sunpc_mod_LDFLAGS): Likewise. + * conf/i386-pc.rmk (grub_setup_SOURCES): Add partmap/sunpc.c. + * partmap/sunpc.c: New file. + +2010-03-26 BVK Chaitanya + + For loop support to GRUB script. + + * include/grub/script_sh.h (grub_script_cmdfor): New struct. + (grub_script_create_cmdfor): New function prototype. + (grub_script_execute_cmdfor): New function prototype. + * script/execute.c (grub_script_execute_cmdfor): New function. + * script/parser.y (command): New for command. + (forcmd): New grammar rule. + * script/script.c (grub_script_create_cmdfor): New function. + * util/grub-script-check.c (grub_script_execute_cmdfor): New + function. + * tests/grub_script_for1.in: New testcase. + * conf/tests.rmk: Rules for new testcase. + +2010-03-26 Vladimir Serbinenko + + Nested partitions + + * commands/blocklist.c (grub_cmd_blocklist): Don't check whether + 'partition' is NULL, grub_partition_get_start already does that. + * commands/loadenv.c (check_blocklists): Likewise. + (write_blocklists): Likewise. + * conf/common.rmk (grub_probe_SOURCES): Add partmap/bsdlabel.c. + (grub_fstest_SOURCES): Likewise. + (pkglib_MODULES): Add part_bsd.mod. + (part_bsd_mod_SOURCES): New variable. + (part_bsd_mod_CFLAGS): Likewise. + (part_bsd_mod_LDFLAGS): Likewise. + * conf/i386-pc.rmk (grub_setup_SOURCES): Add partmap/bsdlabel.c. + (grub_emu_SOURCES): Likewise. + * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Likewise. + * include/grub/bsdlabel.h: New file. + * include/grub/partition.h (grub_partition_map): Remove 'probe' and + 'get_name'. + (grub_partition): Add 'parent' and 'number'. Remove 'data'. + (grub_partition_map_list): New variable. + (grub_partition_map_register): Inline. + (grub_partition_map_unregister): Likewise. + (FOR_PARTITION_MAPS): New macro. + (grub_partition_map_iterate): Removed. + (grub_partition_get_start): Handle nested partitions. + * include/grub/msdos_partition.h: Remove bsd-related entries. + (grub_pc_partition): Remove. + * kern/disk.c (grub_disk_close): Free partition data. + (grub_disk_adjust_range): Handle nested partitions. + * kern/partition.c (grub_partition_map_probe): New function. + (grub_partition_probe): Parse name to number, handle subpartitions. + (get_partmap): New function. + (grub_partition_iterate): Handle subpartitions. + (grub_partition_get_name): Likewise. + * loader/i386/pc/bsd.c (grub_bsd_get_device): Likewise. + * loader/i386/multiboot.c (grub_multiboot_get_bootdev): Likewise. + * loader/i386/pc/chainloader.c (grub_chainloader_cmd): Likewise. + * partmap/acorn.c (acorn_partition_map_iterate): Don't force raw access. + Set 'number'. + (acorn_partition_map_probe): Remove. + (acorn_partition_map_get_name): Likewise. + * partmap/amiga.c (amiga_partition_map_iterate): Don't force raw access. + Set 'number'. + Set 'index' to 0 since there can be only one partition entry per sector. + (amiga_partition_map_probe): Remove. + (amiga_partition_map_get_name): Likewise. + * partmap/apple.c (apple_partition_map_iterate): Don't force raw access. + Set 'number'. + Set 'offset' and 'index' to real positions of partitions. + (apple_partition_map_probe): Remove. + (apple_partition_map_get_name): Likewise. + * partmap/bsdlabel.c: New file. + * partmap/gpt.c (gpt_partition_map_iterate): Don't force raw access. + Set 'number'. + Allocate 'data' so it can be correctly freed. + Set 'index' to offset inside sector. + (gpt_partition_map_probe): Remove. + (gpt_partition_map_get_name): Likewise. + * partmap/msdos.c (grub_partition_parse): Remove. + (pc_partition_map_iterate): Don't force raw access. + Set 'number'. + Make 'ext_offset' a local variable. + (pc_partition_map_probe): Remove. + (pc_partition_map_get_name): Remove. + * partmap/sun.c (sun_partition_map_iterate): Don't force raw access. + Set 'number'. + (sun_partition_map_probe): Remove. + (sun_partition_map_get_name): Likewise. + * parttool/msdospart.c (grub_pcpart_boot): Handle nested partitions. + (grub_pcpart_type): Likewise. + * util/hostdisk.c (open_device): Handle new numbering scheme. + (grub_util_biosdisk_get_grub_dev): Handle nested partitions. + * util/i386/pc/grub-setup.c (setup): Handle new numbering scheme. + * util/grub-probe.c (probe_partmap): Handle nested paritions. + * util/grub-install.in: Insert all subpartition modules. + * util/ieee1275/grub-install.in: Likewise. + +2010-03-24 Adrian Glaubitz + + * kern/dl.c (grub_dl_resolve_symbols): Improve error message + grammar. + +2010-03-24 Colin Watson + + * .bzrignore: Add grub-bin2h, grub-reboot, and grub-set-default. + +2010-03-21 Colin Watson + + * util/grub-install.in: Copy .mo files from @datadir@/locale, to + match where 'make install' puts them. + * util/i386/efi/grub-install.in: Likewise. + +2010-03-19 Colin Watson + + * .bzrignore: Add gentrigtables, grub-script-check, + grub_script_check_init.c, grub_script_check_init.h, and + trigtables.c. + +2010-03-18 Vladimir Serbinenko + + * kern/parser.c: Indented. + +2010-03-17 Vladimir Serbinenko + + * term/i386/pc/vesafb.c: Removed (orphaned, deprecated and broken). + +2010-03-17 Vladimir Serbinenko + + * video/fb/fbblit.c (grub_video_fbblit_blend_XXXA8888_1bit): Handle + alpha_mask_size == 0 case. + +2010-03-14 BVK Chaitanya + + GRUB shell lexer and parser improvements. + + * conf/any-emu.rmk: Build rule updates. + * conf/common.rmk: Likewise. + * conf/i386-coreboot.rmk: Likewise. + * conf/i386-efi.rmk: Likewise. + * conf/i386-ieee1275.rmk: Likewise. + * conf/i386-pc.rmk: Likewise. + * conf/powerpc-ieee1275.rmk: Likewise. + * conf/x86_64-efi.rmk: Likewise. + + * configure.ac: Configure check for flex. + + * include/grub/script_sh.h (grub_script_arg_type_t): More argument + types. + (grub_lexer_param): Struct member updates. + (grub_parser_param): Likewise. + (GRUB_LEXER_TOKEN_MAX): Maximum token size. + (GRUB_LEXER_RECORD_INCREMENT): Memory increments' size. + (grub_script_lexer_init): Prototype update. + (grub_script_lexer_record_start): Likewise. + (grub_script_lexer_record_stop): Likewise. + (grub_script_lexer_yywrap): New function prototype. + (grub_script_lexer_fini): Likewise. + (grub_script_execute_argument_to_string): Removed by... + (grub_script_execute_argument_to_argv): ...better version. + + * script/execute.c (ROUND_UPTO): New macro. + (grub_script_execute_cmdline): Out of memory fixes. + (grub_script_execute_menuentry): Likewise. + (grub_script_execute_argument_to_string): Removed. Update all + users by... + (grub_script_execute_argument_to_argv): ...better version. + * script/function.c (grub_script_function_create): Use + grub_script_execute_argument_to_argv instead of + grub_script_execute_argument_to_string. + + * script/lexer.c (check_varstate): Removed. + (check_textstate): Removed. + (grub_script_lexer_record_start): Likewise. + (grub_script_lexer_record_stop): Likewise. + (recordchar): Replaced with... + (grub_script_lexer_record): ...new function. + (nextchar): Removed. + (grub_script_lexer_init): Rewritten. + (grub_script_yylex): Rewritten. + (append_newline): New function. + (grub_script_lexer_yywrap): New function. + (grub_script_lexer_fini): New function. + (grub_script_yyerror): Sets error flag. + + * script/yylex.l: New file. + (grub_lexer_yyfree): Wrapper for flex yyffre. + (grub_lexer_yyalloc): Likewise. + (grub_lexer_yyrealloc): Likewise. + * script/parser.y: Refactored. + + * script/script.c (grub_script_arg_add): Out of memory fixes. + (grub_script_add_arglist): Likewise. + (grub_script_create_cmdline): Likewise. + (grub_script_create_cmdmenu): Likewise. + (grub_script_add_cmd): Likewise. + (grub_script_parse): Use grub_script_lexer_fini to deallocated. + * util/grub-script-check.c (grub_script_execute_menuentry): Remove + unnecessary code. + + * tests/grub_script_echo1.in: New testcase. + * tests/grub_script_vars1.in: New testcase. + * tests/grub_script_echo_keywords.in: New testcase. + +2010-03-14 Vladimir Serbinenko + + Remove some redundancy in build system. + + * Makefile.in (TARGET_CFLAGS): Add -ffreestanding. + (TARGET_ASFLAGS): Add -nostdinc -fno-builtin. + (TARGET_LDFLAGS): Add -nostdlib. + (TARGET_IMG_LDFLAGS): Likewise. + * commands/lsmmap.c (grub_cmd_lsmmap) [GRUB_MACHINE_EMU]: Don't do + anything since mmap isn't available. + * conf/any-emu.rmk (kernel_img_SOURCES): Remove commands/boot.c. + Add util/time.c. + (pkglib_MODULES): Remove reboot.mod. + (reboot_mod_SOURCES): Removed. + (reboot_mod_CFLAGS): Likewise. + (reboot_mod_LDFLAGS): Likewise. + * conf/common.rmk (script/lexer.c_DEPENDENCIES): New variable. + (MOSTLYCLEANFILES): Add symlist.c kernel_syms.lst. + (DEFSYMFILES): Add kernel_syms.lst. + (kernel_img_HEADERS): Add common headers. + (symlist.c): New target. + (kernel_syms.lst): Likewise. + (pkglib_MODULES): Add memdisk.mod. + (memdisk_mod_SOURCES): New variable. + (memdisk_mod_CFLAGS): Likewise. + (memdisk_mod_LDFLAGS): Likewise. + (pkglib_MODULES): Add reboot.mod. + (reboot_mod_SOURCES): New variable. + (reboot_mod_CFLAGS): Likewise. + (reboot_mod_LDFLAGS): Likewise. + (pkglib_MODULES): Add date.mod. + (date_mod_SOURCES): New variable. + (date_mod_CFLAGS): Likewise. + (date_mod_LDFLAGS): Likewise. + (pkglib_MODULES): Add datehook.mod. + (datehook_mod_SOURCES): New variable. + (datehook_mod_CFLAGS): Likewise. + (datehook_mod_LDFLAGS): Likewise. + (pkglib_MODULES): Add lsmmap.mod. + (lsmmap_mod_SOURCES): New variable. + (lsmmap_mod_CFLAGS): Likewise. + (lsmmap_mod_LDFLAGS): Likewise. + (pkglib_MODULES): Add boot.mod. + (boot_mod_SOURCES): New variable. + (boot_mod_CFLAGS): Likewise. + (boot_mod_LDFLAGS): Likewise. + * conf/i386-coreboot.rmk: Removed redundant parts. + * conf/i386-ieee1275.rmk: Likewise. + * conf/i386-pc.rmk: Likewise. + * conf/mips-yeeloong.rmk: Likewise. + * conf/mips.rmk: Likewise. + * conf/powerpc-ieee1275.rmk: Likewise. + * conf/sparc64-ieee1275.rmk: Likewise. + * conf/x86_64-efi.rmk: Likewise. + * conf/i386-coreboot.rmk: Moved qemu parts .. + * conf/i386-qemu.rmk: ... here + * conf/i386-efi.rmk: Moved common parts to... + * conf/x86-efi.rmk: ... here. + * conf/i386.rmk: Added modules common to all x86 variants. + * configure.ac: Add -m32/-m64 to TARGET_ASFLAGS. + * disk/memdisk.c: Remove grub/machine/kernel.h. + * gensymlist.sh.in: Include symbol.h. + * hook/datehook.c: Correct module name. + * include/grub/datetime.h (grub_get_datetime) [GRUB_MACHINE_EMU]: Export. + (grub_set_datetime) [GRUB_MACHINE_EMU]: Likewise. + * include/grub/i386/efi/serial.h: New file. + * include/grub/x86_64/efi/serial.h: Likewise. + * util/time.c: Likewise. + * video/ieee1275.c (grub_video_ieee1275_setup): Handle 64-bit void *. + +2010-03-14 Colin King +2010-03-14 Colin Watson + + Shrink the pre-partition-table part of boot.img by eight bytes. + + * boot/i386/pc/boot.S (ERR): New macro. + (chs_mode): Use ERR. + (geometry_error): Likewise. + (hd_probe_error): Remove. This is only used once, so we wrwite + it inline instead. + (read_error): Instead of printing read_error_string, just set up + %si and fall through to ... + (error_message): ... this new function, also used by ERR. + +2010-03-14 Colin Watson + + Speed up consecutive hostdisk operations on the same device. + + * util/hostdisk.c (struct grub_util_biosdisk_data): New structure. + (grub_util_biosdisk_open): Initialise disk->data. + (struct linux_partition_cache): New structure. + (linux_find_partition): Cache partition start positions; these are + expensive to compute on every read and write. + (open_device): Cache open file descriptor in disk->data, so that we + don't have to reopen it and flush the buffer cache for consecutive + operations on the same device. + (grub_util_biosdisk_close): New function. + (grub_util_biosdisk_dev): Set `close' member. + + * conf/common.rmk (grub_probe_SOURCES): Add kern/list.c. + * conf/i386-efi.rmk (grub_setup_SOURCES): Likewise. + * conf/i386-pc.rmk (grub_setup_SOURCES): Likewise. + * conf/sparc64-ieee1275.rmk (grub_setup_SOURCES): Likewise. + * conf/x86_64-efi.rmk (grub_setup_SOURCES): Likewise. + +2010-03-14 Vladimir Serbinenko + + Compile parts of grub-emu as modules. + + * Makefile.in (TARGET_CPPFLAGS) [emu]: Remove -nostdinc -isystem. + (pkglib_DATA) [emu]: Remove moddep.lst command.lst fs.lst + partmap.lst parttool.lst handler.lst video.lst crypto.lst terminal.lst. + (all-local): Add $(GRUB_EMU). + (install-local): Install $(GRUB_EMU). + (uninstall): Uninstall $(GRUB_EMU). + * commands/parttool.c: Replace GRUB_UTIL with GRUB_NO_MODULES. + * kern/dl.c: Likewise. + * commands/sleep.c: Not include machine/time.h. + * conf/any-emu.rmk (COMMON_LDFLAGS): New variable. + (COMMON_CFLAGS): Likewise. + (sbin_UTILITIES): Remove grub-emu. + (grub_emu_SOURCES): Removed. + (kernel_img_RELOCATABLE): New variable. + (pkglib_PROGRAMS): Add kernel.img. + (kernel_img_SOURCES): New variable + (kernel_img_CFLAGS): Likewise. + (kernel_img_LDFLAGS): Likewise. + (TARGET_NO_STRIP): Likewise. + (TARGET_NO_DYNAMIC_MODULES): Likewise. + (pkglib_MODULES): Add progname.mod, hostfs.mod, host.mod, reboot.mod, + halt.mod, cpuid.mod, usb.mod, sdl.mod and pci.mod. + (grub-emu): New target. + (GRUB_EMU): New variable. + * configure.ac: Whitelist -emu as possible x86_64 architecture. + * efiemu/main.c: Replace GRUB_UTIL with GRUB_MACHINE_EMU. + * loader/xnu.c: Likewise. + * include/grub/pci.h: Likewise. + * genemuinit.sh: New file. + * genemuinitheader.sh: Likewise. + * genmk.rb: Don't strip if TARGET_NO_STRIP is yes. + Support TARGET_NO_DYNAMIC_MODULES. + * include/grub/dl.h (GRUB_NO_MODULES): New variable. + * commands/search.c: Fix GRUB_MOD_INIT and GRUB_MOD_FINI arguments. + * disk/loopback.c: Likewise. + * font/font_cmd.c: Likewise. + * partmap/acorn.c: Likewise. + * partmap/amiga.c: Likewise. + * partmap/apple.c: Likewise. + * partmap/gpt.c: Likewise. + * partmap/msdos.c: Likewise. + * partmap/sun.c: Likewise. + * parttool/msdospart.c: Likewise. + * term/gfxterm.c: Likewise. + * video/bitmap.c: Likewise. + * video/readers/jpeg.c: Likewise. + * video/readers/png.c: Likewise. + * video/readers/tga.c: Likewise. + * video/video.c: Likewise. + * util/grub-emu.c (read_command_list): Removed. + (main): Don't call util_init_nls. + * util/misc.c (grub_err_printf) [!GRUB_UTIL]: Removed. + (grub_util_init_nls) [!GRUB_UTIL]: Likewise. + +2010-03-14 Vladimir Serbinenko + + * conf/powerpc-ieee1275.rmk (pkglib_MODULES): Add datetime.mod, + date.mod, datehook.mod. + (datetime_mod_SOURCES): New variable. + (datetime_mod_CFLAGS): Likewise. + (datetime_mod_LDFLAGS): Likewise. + (date_mod_SOURCES): Likewise. + (date_mod_CFLAGS): Likewise. + (date_mod_LDFLAGS): Likewise. + (datehook_mod_SOURCES): Likewise. + (datehook_mod_CFLAGS): Likewise. + (datehook_mod_LDFLAGS): Likewise. + * conf/sparc64-ieee1275.rmk: Likewise. + * lib/ieee1275/datetime.c: New file. + +2010-03-14 Vladimir Serbinenko + + * conf/powerpc-ieee1275.rmk (pkglib_MODULES): Add ieee1275_fb.mod. + (ieee1275_fb_mod_SOURCES): New variable. + (ieee1275_fb_mod_CFLAGS): Likewise. + (ieee1275_fb_mod_LDFLAGS): Likewise. + * include/grub/ieee1275/ieee1275.h (grub_ieee1275_devices_iterate): + New proto. + * kern/ieee1275/init.c (HEAP_MAX_SIZE): Increased. + (HEAP_MAX_ADDR): Likewise. + * kern/ieee1275/openfw.c (grub_children_iterate): Don't skip empty + type. + Correct stop condition. + (grub_ieee1275_devices_iterate): New function. + * video/ieee1275.c: New file. + +2010-03-14 Vladimir Serbinenko + + Merge sparc grub-mkimage into generic grub-mkimage and a.out support. + + * boot/sparc64/ieee1275/boot.S (boot_continue): Use SCRATCH_PAD_BOOT + as scratch. + * boot/sparc64/ieee1275/diskboot.S (after_info_block): Use + SCRATCH_PAD_DISKBOOT as scratch. + (bootit): Pass Openfirmware pointer in %o4. + * conf/sparc64-ieee1275.rmk (kernel_img_LDFLAGS): Link at 0x4400 instead + of 0x200000. + (grub_mkimage_SOURCES): Replace util/sparc64/ieee1275/grub-mkimage.c + with util/grub-mkrawimage.c. + * configure.ac: Handle GRUB_MACHINE_SPARC64 and GRUB_MACHINE_MIPS. + * include/grub/aout.h (AOUT_MID_SUN): New definition. + (grub_aout_get_type) [GRUB_UTIL]: Removed. + (grub_aout_load) [GRUB_UTIL]: Likewise. + * include/grub/kernel.h (grub_modules_get_end): New proto. + * include/grub/sparc64/ieee1275/boot.h (SCRATCH_PAD): Removed. + (SCRATCH_PAD_BOOT): New definition. + (SCRATCH_PAD_DISKBOOT): Likewise. + (GRUB_BOOT_MACHINE_IMAGE_ADDRESS): Set to 0x4400. + * include/grub/sparc64/ieee1275/ieee1275.h + (grub_ieee1275_original_stack): New variable + * include/grub/sparc64/ieee1275/kernel.h (GRUB_KERNEL_MACHINE_RAW_SIZE): + New definition + (GRUB_KERNEL_MACHINE_STACK_SIZE): Likewise. + (GRUB_PLATFORM_IMAGE_FORMATS): Likewise. + (GRUB_PLATFORM_IMAGE_DEFAULT_FORMAT): Likewise. + (GRUB_PLATFORM_IMAGE_DEFAULT): Likewise. + (GRUB_PLATFORM_IMAGE_RAW): Likewise. + (GRUB_PLATFORM_IMAGE_AOUT): Likewise. + (grub_platform_image_format_t): New type. + * kern/mips/yeeloong/init.c (grub_modules_get_end): Move from here ... + * kern/main.c (grub_modules_get_end) + [GRUB_MACHINE_MIPS_YEELOONG || GRUB_MACHINE_SPARC64]: ... here. + * kern/sparc64/ieee1275/crt0.S: Store firmware entry point in %o0. + (codestart): Switch stacks. + * kern/sparc64/ieee1275/init.c (grub_ieee1275_original_stack): New + variable. + (grub_heap_init): Use grub_modules_get_end. + * loader/sparc64/ieee1275/linux.c (grub_linux_boot): Restore original + stack. + * util/grub-mkrawimage.c (generate_image): Support sparc64. + (main): Likewise. + * util/sparc64/ieee1275/grub-mkimage.c: Removed. + +2010-03-14 Thorsten Glaser + + * util/grub-mkrescue.in: Base ISO UUID on UTC. + +2010-03-08 Matt Kraai + + * util/i386/pc/grub-setup.c (setup): Fix a grammatical error (Debian + bug #559005). + +2010-03-07 Vladimir Serbinenko + + * genmoddep.awk: Output all missing symbols and not only first. + +2010-03-06 Vladimir Serbinenko + + * NEWS: Put the date of 1.98 release. + +2010-03-06 Vladimir Serbinenko + + * configure.ac: Update CPPFLAGS and not CFLAGS when checking for + ft2build.h. + +2010-03-06 Vladimir Serbinenko + + * normal/cmdline.c (grub_cmdline_get): Fix gabled line after + completition in the middle of string. + +2010-03-06 Vladimir Serbinenko + + * util/grub-mkrescue.in: Use mktemp with explicit template. + +2010-03-06 Vladimir Serbinenko + + * loader/i386/bsd.c (grub_bsd_get_device): Fix a memory leak. + +2010-03-06 Vladimir Serbinenko + + * loader/i386/multiboot_mbi.c (grub_multiboot_set_bootdev): Free the + right pointer. + +2010-03-05 Vladimir Serbinenko + + Fix FreeBSD compilation. + + * Makefile.in (TARGET_CPPFLAGS): Remove -nostdinc -isystem. + * configure.ac: Add -nostdinc -isystem to TARGET_CPPFLAGS if it works. + +2010-03-05 Vladimir Serbinenko + + * util/import_gcry.py: Add autogenerated files to MAINTAINER_CLEANFILES. + +2010-03-04 Vladimir Serbinenko + + * gettext/gettext.c (grub_gettext_init_ext): Fix a memory leak. + +2010-03-04 Vladimir Serbinenko + + * disk/scsi.c (grub_scsi_iterate): Fix a memory leak. + +2010-03-04 Robert Millan + + Support relative image path in theme file. + + * gfxmenu/gui_image.c (grub_gui_image): New member theme_dir. + (image_set_property): Handle theme_dir and relative path. + +2010-03-04 Vladimir Serbinenko + + * configure.ac: Alias amd64 to x86_64. + +2010-03-04 Vladimir Serbinenko + + * NEWS: mention multiboot on EFI. + +2010-03-04 Vladimir Serbinenko + + * kern/main.c (grub_load_modules): Handle errors from init functions of + embeded modules. + +2010-03-04 Vladimir Serbinenko + + * normal/autofs.c (autoload_fs_module): Handle errors. + +2010-03-04 Vladimir Serbinenko + + Disable linux.mod on qemu-mips since it's not functional and leads + to compilation failure. + + * conf/mips.rmk (pkglib_MODULES): Remove linux.mod. + * conf/mips-yeeloong.rmk (pkglib_MODULES): Add linux.mod. + * conf/mips.rmk (linux_mod_SOURCES): Move from here ... + * conf/mips-yeeloong.rmk (linux_mod_SOURCES): ... here + * conf/mips.rmk (linux_mod_CFLAGS): Move from here ... + * conf/mips-yeeloong.rmk (linux_mod_CFLAGS): ... here + * conf/mips.rmk (linux_mod_ASFLAGS): Move from here ... + * conf/mips-yeeloong.rmk (linux_mod_ASFLAGS): ... here + * conf/mips.rmk (linux_mod_LDFLAGS): Move from here ... + * conf/mips-yeeloong.rmk (linux_mod_LDFLAGS): ... here + Reported by: BVK Chaitanya + +2010-03-04 Jordan Uggla + + * INSTALL: Add gettext as a dependency and add qemu to a new section + "Prerequisites for make-check". + +2010-03-04 Christian Franke + + * util/grub-pe2elf.c: Add missing include "progname.h". + +2010-03-04 Vladimir Serbinenko + + * normal/crypto.c (read_crypto_list): Fix a typo. + Reported by: Seth Goldberg. + +2010-03-04 Vladimir Serbinenko + + * Makefile.in (DISTCLEANFILES): Add stamp-h1. + Reported by: Seth Goldberg. + +2010-03-04 Vladimir Serbinenko + + * Makefile.in (CLEANFILES) [FONT_SOURCE && grub_mkfont]: Add + ascii.bitmaps. + +2010-03-04 Vladimir Serbinenko + + * genmk.rb: Remove terminal*.lst in make clean. + Reported by: Seth Goldberg. + +2010-03-04 Vladimir Serbinenko + + * util/i386/efi/grub-install.in: Copy gettext files. + +2010-03-01 Vladimir Serbinenko + + * fs/ext2.c (grub_ext2_read_block): Fix an integer overflow. + +2010-03-01 Vladimir Serbinenko + + Wait for user entry basing on presence of output rather than on errors. + + * include/grub/normal.h (grub_normal_get_line_counter): New proto. + (grub_install_newline_hook): Likewise. + * normal/main.c (GRUB_MOD_INIT): Call grub_install_newline_hook. + * normal/menu.c (show_menu): Check line_counter to determine presence + of output. + * normal/term.c (grub_normal_line_counter): New variable. + (grub_normal_get_line_counter): New function. + (grub_install_newline_hook): Likewise. + +2010-03-01 Vladimir Serbinenko + + * commands/cat.c (grub_cmd_cat): Propagate grub_gzfile_open error. + +2010-03-01 Vladimir Serbinenko + + * configure.ac: Update version to 1.98. + +2010-02-26 Vladimir Serbinenko + + * util/grub.d/10_linux.in (linux_entry): Don't default to + gfxpayload=keep if Linux doesn't support video handover. + +2010-02-25 Vladimir Serbinenko + + Don't compile video modules on yeeloong since video subsystem is part + of kernel. + + * conf/common.rmk (pkglib_MODULES) [yeeloong]: Remove video.mod, + video_fb.mod, bitmap.mod, font.mod, gfxterm.mod and bufio.mod + * conf/mips-yeeloong.rmk (kernel_img_HEADERS): Add bitmap.h, + video.h, gfxterm.h, font.h, bitmap_scale.h and bufio.h. + * conf/mips.rmk (kernel_img_HEADERS): Add values instead of overwriting. + * include/grub/bitmap.h: Add EXPORT_FUNC and EXPORT_VAR. + * include/grub/bitmap_scale.h: Likewise. + * include/grub/bufio.h: Likewise. + * include/grub/font.h: Likewise. + * include/grub/gfxterm.h: Likewise. + * include/grub/video.h: Likewise. + * include/grub/vbe.h: Don't include video_fb.h. + * video/i386/pc/vbe.c: Include video_fb.h. + * commands/i386/pc/vbetest.c: Include video.h. + +2010-02-25 Jordan Uggla + + * util/grub-mkconfig.in (GRUB_SAVEDEFAULT): Export new variable. + * util/grub-mkconfig_lib.in (save_default_entry): Only save a new + default entry if GRUB_SAVEDEFAULT=true. This allows using + GRUB_DEFAULT=saved on its own to let grub-reboot work, without + saving a new default on every boot. + +2010-02-24 Vladimir Serbinenko + + * normal/crypto.c (read_crypto_list): Fix a memory leak. + * normal/term.c (read_terminal_list): Likewise. + * normal/main.c (grub_normal_init_page): Likewise. + (grub_normal_read_line_real): Likewise. + +2010-02-24 Vladimir Serbinenko + + * loader/i386/multiboot_mbi.c (grub_multiboot_set_bootdev): Fix a + memory leak. + Reported by: Seth Goldberg. + +2010-02-24 Joey Korkames + + * term/ieee1275/ofconsole.c (grub_ofconsole_readkey): Remove + duplicate declaration of `start'. + +2010-02-20 Vladimir Serbinenko + + * fs/iso9660.c (grub_iso9660_iterate_dir): Strip version from joliet + filename. + Reported by: Georgy Buranov + +2010-02-20 Carles Pina i Estany + + * util/grub-mkrawimage.c (usage): Change string formatting to + improve gettext. + +2010-02-20 Manoel Rebelo Abranches + + * term/ieee1275/ofconsole.c (grub_ofconsole_readkey): Add delete and + backspace keys. + +2010-02-20 Vladimir Serbinenko + + * video/fb/video_fb.c (grub_video_fb_scroll): Fix a pixel size bug. + Reported by: Michael Suchanek. + +2010-02-18 Samuel Thibault + + * util/grub-mkconfig.in: Export GRUB_INIT_TUNE. + * util/grub.d/00_header.in: Handle GRUB_INIT_TUNE. + +2010-02-16 Vladimir Serbinenko + + Remove any reference to non-free fonts. + + * commands/videotest.c (grub_cmd_videotest): Use unifont by default. + * docs/gfxmenu-theme-example.txt: Removed. It's both outdated and + uses non-free components. + * font/font.c (grub_font_get_name): Remove example name. + * gfxmenu/gui_label.c (grub_gui_label_new): Use unifont by default. + * gfxmenu/gui_list.c (grub_gui_list_new): Likewise. + * gfxmenu/gui_progress_bar.c (grub_gui_progress_bar_new): Likewise. + * gfxmenu/view.c (grub_gfxmenu_view_new): Likewise. + +2010-02-16 Georgy Buranov + + * disk/efi/efidisk.c (grub_efidisk_get_device_name): Fix a typo. + +2010-02-15 Vladimir Serbinenko + + * term/serial.c (serial_get_divisor) [GRUB_MACHINE_MIPS_YEELOONG]: + Double divisor. + (serial_hw_init) [GRUB_MACHINE_MIPS_YEELOONG]: Don't enable advanced + features. + (GRUB_MOD_INIT) [GRUB_MACHINE_MIPS_YEELOONG]: Default to 115200. + +2010-02-15 Vladimir Serbinenko + + * gensymlist.sh.in: Use TARGET_CC instead of CC. + +2010-02-14 Samuel Thibault + + * commands/i386/pc/play.c (GRUB_MOD_INIT(play)): Fix help. + * docs/grub.texi (Command-line and menu entry commands): Document play + command. + +2010-02-14 Samuel Thibault + + * commands/i386/pc/play.c (grub_cmd_play): If grub_file_open fails, + parse arguments as inline tempo and notes. Move code for playing notes + to... + (play): ... new function. + +2010-02-14 Samuel Thibault + + * commands/i386/pc/play.c (T_REST, T_FINE, struct note, beep_on): Use + grub_uint16_t instead of short. + (grub_cmd_play): Use grub_uint32_t instead of int, convert data from + disk from little endian to cpu endianness. + +2010-02-07 Samuel Thibault + + * commands/i386/pc/play.c (BASE_TEMPO): Set to 60 * + GRUB_TICKS_PER_SECOND instead of 120. + +2010-02-14 Vladimir Serbinenko + + * term/ieee1275/ofconsole.c (grub_ofconsole_readkey): Wait for possible + escape sequence after \e. + +2010-02-14 Vladimir Serbinenko + + * term/ieee1275/ofconsole.c (grub_ofconsole_putchar): Don't output + non-ASCII characters. + +2010-02-14 Vladimir Serbinenko + + * util/grub-mkconfig_lib.in (prepare_grub_to_access_device): Enclose + set root in single quotes to prevent \, from being unescaped. + +2010-02-14 Vladimir Serbinenko + + Prevent unknown commands from stopping menuentry execution. + + * script/execute.c (grub_script_execute_cmdline): Print error after + unknown command. + +2010-02-14 Vladimir Serbinenko + + * fs/i386/pc/pxe.c (GRUB_MOD_INIT): Fix typo. + Reported by: Pavel Pisa. + +2010-02-13 Vladimir Serbinenko + + * io/gzio.c (grub_gzio_open): Use grub_zalloc. + +2010-02-13 Vladimir Serbinenko + + Merge grub_ieee1275_map_physical into grub_map and rename to + grub_ieee1275_map + + * include/grub/ieee1275/ieee1275.h (grub_ieee1275_map): New proto. + * include/grub/sparc64/ieee1275/ieee1275.h (grub_ieee1275_map_physical): + Remove. + * kern/ieee1275/openfw.c (grub_map): Rename to ... + (grub_ieee1275_map): ... this. All users updated. Add phys_lo when + necessary. + * kern/sparc64/ieee1275/ieee1275.c (grub_ieee1275_map_physical): Remove. + +2010-02-13 Vladimir Serbinenko + + * disk/ieee1275/ofdisk.c (grub_ofdisk_open): Check device type before + opening and not after. + +2010-02-13 Vladimir Serbinenko + + * term/ieee1275/ofconsole.c (grub_ofconsole_readkey): Macroify + constants. + +2010-02-13 Vladimir Serbinenko + + * loader/sparc64/ieee1275/linux.c (align_addr): Remove. + (alloc_phys): Use ALIGN_UP instead of align_addr. + +2010-02-13 Vladimir Serbinenko + + * loader/sparc64/ieee1275/linux.c (alloc_phys): Correct bounds checking. + +2010-02-13 Vladimir Serbinenko + + * kern/sparc64/ieee1275/crt0.S (codestart): Move modules backwards. + +2010-02-13 Vladimir Serbinenko + + * disk/ieee1275/ofdisk.c (grub_ofdisk_read): Remove excessively + verbose dprintf. + +2010-02-13 Vladimir Serbinenko + + Fix over-4GiB seek on sparc64. + + * include/grub/ieee1275/ieee1275.h (grub_ieee1275_seek): + Replace pos_i and pos_lo with pos. All users updated. + * include/grub/powerpc/ieee1275/ieee1275.h (GRUB_IEEE1275_CELL_SIZEOF): + New constant. + * include/grub/sparc64/ieee1275/ieee1275.h (GRUB_IEEE1275_CELL_SIZEOF): + Likewise. + * kern/ieee1275/ieee1275.c (grub_ieee1275_seek): Split pos into pos_hi + and pos_lo. + +2010-02-13 Vladimir Serbinenko + + * util/grub-mkrawimage.c (main): Call set_program_name. + +2010-02-13 Vladimir Serbinenko + + Properly align 64-bit targets. + + * util/grub-mkrawimage.c (ALIGN_ADDR): New macro. + (generate_image): Use ALIGN_ADDR. + +2010-02-13 Vladimir Serbinenko + + Properly create cross-endian images. + + * include/grub/types.h (grub_host_to_target_addr): New macro + * util/grub-mkrawimage.c (generate_image): Add missing host_to_target. + +2010-02-13 Vladimir Serbinenko + + * util/grub-mkrawimage.c (generate_image): Add forgotten ALIGN_UP. + +2010-02-10 Vladimir Serbinenko + + Pass SIMPLE framebuffer size in bytes and not 64K blocks. + + * loader/i386/efi/linux.c (grub_linux_setup_video): Don't divide by 64K. + * loader/i386/linux.c (grub_linux_setup_video): Likewise. + (grub_linux_boot): Divide by 64K when on VESA. + +2010-02-10 Vladimir Serbinenko + + Support GRUB_GFXPAYLOAD_LINUX. + + * util/grub-mkconfig.in: Export GRUB_GFXPAYLOAD_LINUX. + * util/grub.d/10_linux.in (linux_entry): Handle GRUB_GFXPAYLOAD_LINUX. + +2010-02-10 Vladimir Serbinenko + + * script/execute.c (grub_script_execute_cmdline): Use grub_print_error + to show messages instead of discarding them. + Process errors after executing command and not before. Keep old method + too as precaution. + +2010-02-09 Vladimir Serbinenko + + * configure.ac: Check for ft2build.h. + +2010-02-07 Vladimir Serbinenko + + * kern/ieee1275/openfw.c (grub_halt): Try executing "poweroff". + +2010-02-07 Vladimir Serbinenko + + * genkernsyms.sh.in: Use TARGET_CC. + +2010-02-07 Colin Watson + + * NEWS: Update. + +2010-02-07 Vladimir Serbinenko + + * include/grub/multiboot2.h: Remove leftover file. + * include/grub/normal.h [GRUB_UTIL]: Remove leftover declarations. + * include/grub/partition.h [GRUB_UTIL]: Likewise. + +2010-02-07 Yves Blusseau + + * gnulib/getdelim.c: add missing header (type ssize_t must be defined). + +2010-02-07 Vladimir Serbinenko + + Fix warnings in grub-emu when compiling with maximum warning options. + + * util/grub-emu.c (ENABLE_RELOCATABLE): New definition. + (grub_arch_modules_addr): Return 0 and not NULL. + * util/misc.c (ENABLE_RELOCATABLE): New definition. + (xstrdup): Use newstr instead of dup. + * util/hostdisk.c (grub_util_biosdisk_get_grub_dev): Rename one instance + of disk to dsk to avoid shadowing. + (find_free_slot): Fix prototype. + * util/getroot.c (grub_util_is_dmraid): Make static. + * include/grub/time.h (grub_get_rtc) [GRUB_MACHINE_EMU || GRUB_UTIL]: + Add missing prototype. + * util/sdl.c (grub_video_sdl_set_viewport): Remove. + +2010-02-07 Vladimir Serbinenko + + * loader/i386/linux.c (grub_linux_setup_video): Handle error + appropriately. + +2010-02-07 Vladimir Serbinenko + + * fs/reiserfs.c (grub_reiserfs_read): Use #if 0 instead of commenting + code out. + +2010-02-07 Vladimir Serbinenko + + * include/grub/cache.h (grub_arch_sync_caches) [i386 || x86_64]: Inline. + * kern/i386/coreboot/init.c (grub_arch_sync_caches): Remove. + * kern/i386/efi/init.c (grub_arch_sync_caches): Likewise. + * kern/i386/ieee1275/init.c (grub_arch_sync_caches): Likewise. + * kern/i386/pc/init.c (grub_arch_sync_caches): Likewise. + * util/misc.c (grub_arch_sync_caches) [i386 || x86_64]: Likewise. + +2010-02-07 Vladimir Serbinenko + + * include/grub/err.h (grub_err_printf): Don't export. + +2010-02-07 Vladimir Serbinenko + + * include/grub/dl.h (grub_dl_register_symbol): Don't export. + +2010-02-07 Vladimir Serbinenko + + * include/grub/i18n.h (grub_gettext_dummy): Removed. + * kern/misc.c (grub_gettext_dummy): Make static. + +2010-02-06 Vladimir Serbinenko + + * kern/misc.c (grub_utf8_to_ucs4): Don't eat valid characters preceeded + by non-valid ones. + * kern/term.c (grub_putchar): Likewise. + +2010-02-06 Vladimir Serbinenko + + * partmap/sun.c (sun_partition_map_iterate): Restructure flow to fix + buggy hook call and memory leak. + +2010-02-06 Vladimir Serbinenko + + * commands/ls.c (grub_ls_list_files): Free pathname on exit. + +2010-02-06 Vladimir Serbinenko + + * fs/fat.c (grub_fat_iterate_dir): Free unibuf at exit. + +2010-02-06 Vladimir Serbinenko + + * loader/i386/pc/xnu.c (grub_xnu_set_video): Add const qualifier to + modevar. + Return grub_errno on allocation error. + +2010-02-06 Vladimir Serbinenko + + * disk/ieee1275/ofdisk.c (grub_ofdisk_read): Correct error handling. + +2010-02-06 Yves Blusseau + + * conf/common.rmk (grub_script_check_SOURCES): add missing dependencies. + (grub_mkpasswd_pbkdf2_SOURCES): Likewise. + +2010-02-06 Vladimir Serbinenko + + * fs/i386/pc/pxe.c (grub_pxefs_dir): Return with failure on + non-pxe disk. + (grub_pxefs_open): Likewise. + +2010-02-06 Robert Millan + + * util/grub.d/10_hurd.in: Add --class information to menuentries. + * util/grub.d/10_kfreebsd.in: Likewise. + * util/grub.d/10_linux.in: Likewise. + +2010-02-06 Colin D Bennett + + * conf/common.rmk (pkglib_MODULES): Add gfxmenu.mod. + (gfxmenu_mod_SOURCES): New variable. + (gfxmenu_mod_CFLAGS): Likewise. + (gfxmenu_mod_LDFLAGS): Likewise. + * include/grub/term.h (grub_term_set_current_output): Declare + argument as const. + * docs/gfxmenu-theme-example.txt: New file. + * gfxmenu/gfxmenu.c: Likewise. + * gfxmenu/gui_box.c: Likewise. + * gfxmenu/gui_canvas.c: Likewise. + * gfxmenu/gui_circular_progress.c: Likewise. + * gfxmenu/gui_image.c: Likewise. + * gfxmenu/gui_label.c: Likewise. + * gfxmenu/gui_list.c: Likewise. + * gfxmenu/gui_progress_bar.c: Likewise. + * gfxmenu/gui_string_util.c: Likewise. + * gfxmenu/gui_util.c: Likewise. + * gfxmenu/icon_manager.c: Likewise. + * gfxmenu/model.c: Likewise. + * gfxmenu/named_colors.c: Likewise. + * gfxmenu/theme_loader.c: Likewise. + * gfxmenu/view.c: Likewise. + * gfxmenu/widget-box.c: Likewise. + * include/grub/gfxmenu_model.h: Likewise. + * include/grub/gfxmenu_view.h: Likewise. + * include/grub/gfxwidgets.h: Likewise. + * include/grub/gui.h: Likewise. + * include/grub/gui_string_util.h: Likewise. + * include/grub/icon_manager.h: Likewise. + +2010-02-06 Vladimir Serbinenko + + Agglomerate scrolling in gfxterm. + + * term/gfxterm.c (grub_virtual_screen): New member 'total_screen'. + (grub_virtual_screen_setup): Initialise 'total_screen'. + (write_char): Split to ... + (paint_char): ... this ... + (write_char): ... and this. + (paint_char): Handle delayed scrolling. + (draw_cursor): Likewise. + (scroll_up): Split to ... + (real_scroll): ... this ... + (scroll_up): ... and this. + (real_scroll): Handle multi-line scroll and draw below-the-bottom + characters. + (grub_gfxterm_refresh): Call real_scroll. + +2010-02-06 Colin D Bennett + + * include/grub/misc.h (grub_iscntrl): New inline function. + (grub_isalnum): Likewise. + (grub_strtol): Likewise. + +2010-02-06 Colin D Bennett + + * normal/menu_text.c (get_entry_number): Move from here ... + * normal/menu.c (get_entry_number): ... moved here. + * include/grub/menu.h (grub_menu_get_default_entry_index): + New prototype. + * normal/menu.c (grub_menu_get_default_entry_index): New function. + * normal/menu_text.c (run_menu): Use grub_menu_get_default_entry_index. + * include/grub/menu_viewer.h (grub_menu_viewer_init): New prototype. + (grub_menu_viewer_should_return): Likewise. + * normal/main.c (GRUB_MOD_INIT (normal)): Call grub_menu_viewer_init. + * normal/menu_text.c (run_menu): Enable menu switching. + * normal/menu_viewer.c (should_return): New variable. + (menu_viewer_changed): Likewise. + (grub_menu_viewer_show_menu): Handle menu viewer changes. + (grub_menu_viewer_should_return): New function. + (menuviewer_write_hook): Likewise. + (grub_menu_viewer_init): Likewise. + +2010-02-06 Colin D Bennet +2010-02-06 Vladimir Serbinenko + + Support for gfxterm in a window. + + * include/grub/gfxterm.h: New file. + * include/grub/video.h (struct grub_video_rect): New declaration. + (grub_video_rect_t): Likewise. + * term/gfxterm.c (struct grub_gfxterm_window): New type. + (refcount): New variable. + (render_target): Likewise. + (window): Likewise. + (repaint_callback): Likewise. + (grub_virtual_screen_setup): Use 'render_target'. + (init_window): New function. + (grub_gfxterm_init_window): Likewise. + (grub_gfxterm_init): Check reference counter. + Use init_window. + (destroy_window): New function. + (grub_gfxterm_destroy_window): Likewise. + (grub_gfxterm_fini): Check reference counter. + Use destroy_window. + (redraw_screen_rect): Restore viewport. + Use 'render_target' and 'window'. + Call 'repaint_callback'. + (write_char): Use 'render_target'. + (draw_cursor): Likewise. + (scroll_up): Restore viewport. + Use 'render_target' and 'window'. + Call 'repaint_callback'. + (grub_gfxterm_cls): Likewise. + (grub_gfxterm_refresh): Use 'window'. + (grub_gfxterm_set_repaint_callback): New function. + (grub_gfxterm_background_image_cmd): Use 'window'. + (grub_gfxterm_get_term): New function. + (GRUB_MOD_INIT(term_gfxterm)): Set 'refcount' to 0. + +2010-02-06 Colin D Bennett + + Bitmap scaling support. + + * conf/common.rmk (pkglib_MODULES): Add bitmap_scale.mod. + (bitmap_scale_mod_SOURCES): New variable. + (bitmap_scale_mod_CFLAGS): Likewise. + (bitmap_scale_mod_LDFLAGS): Likewise. + * include/grub/bitmap_scale.h: New file. + * term/gfxterm.c (BACKGROUND_CMD_ARGINDEX_MODE): New definiton. + (background_image_cmd_options): New variable. + (grub_gfxterm_background_image_cmd): Support bitmap stretching. + (cmd): Rename and change type to ... + (background_image_cmd_handle): ... this. All users updated. + (GRUB_MOD_INIT(term_gfxterm)): Make background_image extended command. + * video/bitmap_scale.c: New file. + +2010-02-06 Vladimir Serbinenko + + SDL support. + + * Makefile.in (LIBSDL): New variable. + (enable_grub_emu_sdl): Likewise. + * conf/i386-pc.rmk (grub_emu_SOURCES): Add video files. + (grub_emu_SOURCES) [enable_grub_emu_sdl]: Add util/sdl.c. + (grub_emu_LDFLAGS) [enable_grub_emu_sdl]: Add $(LIBSDL). + * configure.ac: Detect SDL availability and add --enable-grub-emu-sdl + * util/sdl.c: New file. + +2010-02-06 Colin D Bennett +2010-02-06 Vladimir Serbinenko + + Double buffering support. + + * commands/i386/pc/videotest.c (grub_cmd_videotest): Swap doublebuffers. + * include/grub/video.h: Update comment. + * include/grub/video_fb.h (grub_video_fb_doublebuf_update_screen_t): + New type. + (grub_video_fb_doublebuf_blit_init): New prototype. + * term/gfxterm.c (scroll_up): Support double buffering. + (grub_gfxterm_refresh): Likewise. + * video/fb/video_fb.c (doublebuf_blit_update_screen): New function. + (grub_video_fb_doublebuf_blit_init): Likewise. + * video/i386/pc/vbe.c (framebuffer): Remove 'render_target'. Add + 'front_target', 'back_target', 'offscreen_buffer', 'page_size', + 'displayed_page', 'render_page' and 'update_screen'. + (grub_video_vbe_fini): Free offscreen buffer. + (doublebuf_pageflipping_commit): New function. + (doublebuf_pageflipping_update_screen): Likewise. + (doublebuf_pageflipping_init): Likewise. + (double_buffering_init): Likewise. + (grub_video_vbe_setup): Enable doublebuffering. + (grub_video_vbe_swap_buffers): Implement. + (grub_video_vbe_set_active_render_target): Handle double buffering. + (grub_video_vbe_get_active_render_target): Likewise. + (grub_video_vbe_get_info_and_fini): Likewise. Free offscreen_buffer. + (grub_video_vbe_adapter): Use grub_video_vbe_get_active_render_target. + (grub_video_vbe_enable_double_buffering): Likewise. + (grub_video_vbe_swap_buffers): Use update_screen. + (grub_video_set_mode): Use double buffering. + +2010-02-06 Robert Millan + + * maintainance/gentrigtables.py: Remove. + * lib/trig.c: Likewise. + + * gentrigtables.c: New file. C rewrite of gentrigtables.py. + + * conf/common.rmk (trig_mod_SOURCES): Replace `lib/trig.c' with + `trigtables.c'. + (trigtables.c): New rule. + (gentrigtables): Likewise. + (DISTCLEANFILES): Add `trigtables.c' and `gentrigtables'. + +2010-02-06 Robert Millan + + * maintainance/gentrigtables.py: Avoid duplicate hardcoding of + integer constants. + +2010-02-06 Colin D Bennet + + Trigonometry support. + + * include/grub/trig.h: New file. + * lib/trig.c: Likewise. + * maintainance/gentrigtables.py: Likewise. + * conf/common.rmk (pkglib_MODULES): Add trig.mod. + (trig_mod_SOURCES): New variable. + (trig_mod_CFLAGS): Likewise. + (trig_mod_LDFLAGS): Likewise. + +2010-02-06 Vladimir Serbinenko + + * kern/ieee1275/openfw.c (grub_ieee1275_encode_devname): Support whole + disk devices. + +2010-02-06 Vladimir Serbinenko + + * kern/ieee1275/openfw.c (grub_devalias_iterate): Stop iterating on + error. + +2010-02-03 Vladimir Serbinenko + + * util/hostdisk.c (open_device): Don't use partition device when reading + before the partition. + (grub_util_biosdisk_read): Don't read from partition and before the + partition in single operation. + (grub_util_biosdisk_write): Don't write to partition and before the + partition in single operation. + +2010-02-03 Torsten Landschoff + + * kern/disk.c (grub_disk_read): Fix offset computation when reading + last sectors. + +2010-02-03 Vladimir Serbinenko + + * disk/i386/pc/biosdisk.c (grub_biosdisk_read): Handle non-2048 aligned + CDROM reads. + (grub_biosdisk_write): Refuse to write to CDROM. + +2010-01-31 Vladimir Serbinenko + + * disk/ieee1275/ofdisk.c (grub_ofdisk_iterate): Fix off-by-one error. + +2010-01-31 Vladimir Serbinenko + + * font/font.c (find_glyph): Check that bmp_idx is available before + using it. + (grub_font_get_string_width): Never call grub_font_get_glyph_internal + with (font == NULL). + +2010-01-28 Christian Schmitt + + * util/ieee1275/grub-install.in: Fix nvsetenv arguments. + +2010-01-28 BVK Chaitanya + + * include/grub/script_sh.h (sourcecode): Add const qualifier. + * util/grub-script-check.c (getline): Fix empty lines case. + +2010-01-28 Robert Millan + + * Makefile.in (check): Exit with fail status when one of the tests + fails. + * tests/example_functional_test.c (example_test): Fix reversed assert. + * tests/example_unit_test.c (example_test): Likewise. + +2010-01-28 Colin Watson + + * util/grub.d/10_linux.in: This script does not use any of the + contents of gettext.sh, only the external command `gettext', so stop + sourcing it. (Moreover, gettext.sh isn't necessarily installed in + the same prefix as GRUB.) + * util/grub.d/10_kfreebsd.in: Likewise. + +2010-01-27 Vladimir Serbinenko + + * normal/cmdline.c (grub_cmdline_get): Fix completion in the middle + of the line. + +2010-01-27 Vladimir Serbinenko + + * kern/disk.c (grub_disk_read): Fix offset computation when reading + last sectors. + +2010-01-27 Vladimir Serbinenko + + * commands/hashsum.c (hash_file): Avoid possible stack overflow by + having a 4KiB and not 32KiB buffer size. + +2010-01-27 Robert Millan + + * util/hostfs.c: Include `'. + (grub_hostfs_read): Handle errors from fseeko() and fread(). + +2010-01-27 Robert Millan + + * kern/disk.c (grub_disk_read): Fix bug that would cause infinite + loop when using read hooks on files whose size isn't sector-aligned. + +2010-01-27 Robert Millan + + Remove unused parameter. + + * fs/iso9660.c (struct grub_iso9660_data): Remove `length' parameter. + (grub_iso9660_open): Remove initialization of `data->length'. + +2010-01-27 Robert Millan + + * util/grub-fstest.c (fstest): Rewrite allocation, fixing a few + memleak conditions. + +2010-01-27 Carles Pina i Estany + + * util/lvm.c: New macro LVM_DEV_MAPPER_STRING. + (grub_util_lvm_isvolume): Use LVM_DEV_MAPPER_STRING. + +2010-01-26 Carles Pina i Estany + + * util/bin2h.c (usage): Fix warning (space after backslash). + +2010-01-26 Carles Pina i Estany + + * font/font.c: Include `grub/fontformat.h. + Remove font file format constants. + (grub_font_load): Use the new macros. + * include/grub/fontformat.h: New file. + * util/grub-mkfont.c: Include `grub/fontformat.c'. + (write_font_pf2): Use the new macros. + +2010-01-26 Robert Millan + + * util/bin2h.c (usage): Make --help actually explain what `grub-bin2h' + does. + +2010-01-26 Robert Millan + + * include/grub/i386/pc/boot.h (GRUB_BOOT_MACHINE_PXE_DL): New macro. + + * boot/i386/pc/pxeboot.S: Include `'. + (_start): Macroify `0x7F'. + + * kern/i386/pc/init.c: Include `'. + (make_install_device): Use "(pxe)" as fallback prefix when booting + via PXE. + +2010-01-26 Vladimir Serbinenko + + * configure.ac: Reset LIBS after check for libgcc symbols. + +2010-01-25 Colin Watson + + * util/hostdisk.c (open_device): Add trailing newline to debug + message. + +2010-01-25 Grégoire Sutre + + * configure.ac: Check for `limits.h'. + * util/misc.c: Include `' (for PATH_MAX). + +2010-01-24 Robert Millan + + * loader/mips/linux.c (grub_cmd_linux, grub_cmd_initrd): Don't + capitalize error strings. + +2010-01-24 Samuel Thibault + + * util/grub.d/10_hurd.in: Add a recovery mode. + +2010-01-23 Vladimir Serbinenko + + * configure.ac: Check for libgcc symbols with -nostdlib. + +2010-01-23 BVK Chaitanya + + * acinclude.m4: Quote underquoted AC_DEFUN parameters. + +2010-01-22 Vladimir Serbinenko + + * term/ieee1275/ofconsole.c (grub_ofconsole_setcolorstate): Allocate on + stack since heap may be unavailable at that point. + (grub_ofconsole_gotoxy): Likewise. + +2010-01-22 Vladimir Serbinenko + + * configure.ac: Check for _restgpr_14_x. + * include/grub/powerpc/libgcc.h [HAVE__RESTGPR_14_X]: Add _restgpr_*_x + and _savegpr_* prototypes. + +2010-01-22 Robert Millan + + Use generic grub_reboot() for i386-efi. + + * kern/efi/efi.c [__i386__] (grub_reboot): Remove. + * kern/i386/efi/startup.S: Include `"../realmode.S"'. + * kern/i386/realmode.S: Include `'. + +2010-01-22 Vladimir Serbinenko + + * kern/ieee1275/init.c (grub_machine_set_prefix): Don't check for + presence of "prefix" variable as it breaks when normal.mod is + embedded. + +2010-01-21 Vladimir Serbinenko + + * term/ieee1275/ofconsole.c (grub_ofconsole_dimensions): Allocate on + stack since heap is unavailable at that point. + +2010-01-21 Vladimir Serbinenko + + * include/grub/i386/bsd.h (FREEBSD_N_BIOS_GEOM): Removed. + (grub_freebsd_bootinfo): Rewritten. + * loader/i386/bsd.c (grub_freebsd_boot): Use new grub_freebsd_bootinfo. + +2010-01-21 Vladimir Serbinenko + + * util/misc.c (make_system_path_relative_to_its_root): Fix typo. + +2010-01-21 Robert Millan + + * po/POTFILES: Remove mkisofs-related files. They have their own TLP + domain now. + +2010-01-20 Felix Zielcke + + * util/misc.c (make_system_path_relative_to_its_root): Change the work + around for handling "/" to the correct fix. Fix a memory leak. Use + xstrdup instead of strdup. + 2010-01-20 Vladimir Serbinenko * conf/mips.rmk (kernel_img_HEADERS): Add env_private.h @@ -23764,7 +25829,7 @@ * genmk.rb (PModule#rule): Make sure to get only symbol names from the output of nm. - Reported by Robert Millan . + Reported by Robert Millan . 2003-09-25 Yoshinori K. Okuji diff --git a/INSTALL b/INSTALL index cfade2026..0dd408bcc 100644 --- a/INSTALL +++ b/INSTALL @@ -14,6 +14,7 @@ configuring the GRUB. * GCC 4.1.3 or later * GNU Make * GNU Bison 2.3 or later +* GNU gettext 0.17 or later * GNU binutils 2.9.1.0.23 or later * Other standard GNU/Unix tools @@ -25,6 +26,10 @@ need the following. * Autoconf 2.60 or later * Automake 1.10.1 or later +Prerequisites for make-check: + +* qemu, specifically the binary 'qemu-system-i386' + Configuring the GRUB ==================== diff --git a/Makefile.in b/Makefile.in index d0bbe3b25..3df8b1b72 100644 --- a/Makefile.in +++ b/Makefile.in @@ -35,6 +35,7 @@ sysconfdir = @sysconfdir@ sharedstatedir = @sharedstatedir@ localstatedir = @localstatedir@ libdir = @libdir@ +localedir = @localedir@ infodir = @infodir@ mandir = @mandir@ includedir = @includedir@ @@ -43,6 +44,10 @@ pkglibdir = $(libdir)/`echo @PACKAGE_TARNAME@/$(target_cpu)-$(platform) | sed ' # Internationalization library. LIBINTL = @LIBINTL@ +TARGET_NO_MODULES = @TARGET_NO_MODULES@ + +# Util library. +LIBUTIL = @LIBUTIL@ XGETTEXT = @XGETTEXT@ MSGMERGE = @MSGMERGE@ @@ -75,27 +80,31 @@ MKDIR_P = @MKDIR_P@ mkinstalldirs = $(srcdir)/mkinstalldirs -LIBS = @LIBS@ $(LIBINTL) +LIBS = @LIBS@ $(LIBINTL) $(LIBUTIL) CC = @CC@ CFLAGS = @CFLAGS@ +POSIX_CFLAGS = -I$(srcdir)/lib/posix_wrap +GNULIB_UTIL_CFLAGS = -Wno-undef -Wno-sign-compare -Wno-unused -D_GL_UNUSED="__attribute__ ((unused))" -I$(srcdir)/gnulib +GNULIB_CFLAGS = $(GNULIB_UTIL_CFLAGS) $(POSIX_CFLAGS) ASFLAGS = @ASFLAGS@ LDFLAGS = @LDFLAGS@ $(LIBS) CPPFLAGS = @CPPFLAGS@ -I$(builddir) -I$(builddir)/include -I$(srcdir)/gnulib -I$(srcdir)/include -Wall -W \ - -DGRUB_LIBDIR=\"$(pkglibdir)\" -DLOCALEDIR=\"$(localedir)\" + -DGRUB_LIBDIR=\"$(libdir)\" -DLOCALEDIR=\"$(localedir)\" TARGET_CC = @TARGET_CC@ -TARGET_CFLAGS = @TARGET_CFLAGS@ -TARGET_ASFLAGS = @TARGET_ASFLAGS@ +TARGET_CFLAGS = -ffreestanding @TARGET_CFLAGS@ +TARGET_ASFLAGS = -nostdinc -fno-builtin @TARGET_ASFLAGS@ TARGET_MODULE_FORMAT = @TARGET_MODULE_FORMAT@ TARGET_APPLE_CC = @TARGET_APPLE_CC@ OBJCONV = @OBJCONV@ -TARGET_CPPFLAGS = @TARGET_CPPFLAGS@ -nostdinc -isystem $(shell $(TARGET_CC) -print-file-name=include) -I$(srcdir)/include -I$(builddir) -I$(builddir)/include \ +TARGET_CPPFLAGS = @TARGET_CPPFLAGS@ -I$(srcdir)/include -I$(builddir) -I$(builddir)/include \ -Wall -W -TARGET_LDFLAGS = @TARGET_LDFLAGS@ +TARGET_LDFLAGS = -nostdlib -static-libgcc @TARGET_LDFLAGS@ TARGET_IMG_LDSCRIPT = @TARGET_IMG_LDSCRIPT@ -TARGET_IMG_LDFLAGS = @TARGET_IMG_LDFLAGS@ +TARGET_IMG_LDFLAGS = -nostdlib @TARGET_IMG_LDFLAGS@ TARGET_IMG_CFLAGS = @TARGET_IMG_CFLAGS@ TARGET_OBJ2ELF = @TARGET_OBJ2ELF@ +kernel_img_LDFLAGS = -lgcc EXEEXT = @EXEEXT@ OBJCOPY = @OBJCOPY@ STRIP = @STRIP@ @@ -114,12 +123,15 @@ endif AWK = @AWK@ LIBCURSES = @LIBCURSES@ LIBUSB = @LIBUSB@ +LIBSDL = @LIBSDL@ LIBPCIACCESS = @LIBPCIACCESS@ +LEX = @LEX@ YACC = @YACC@ FONT_SOURCE = @FONT_SOURCE@ # Options. enable_grub_emu_usb = @enable_grub_emu_usb@ +enable_grub_emu_sdl = @enable_grub_emu_sdl@ enable_grub_emu_pci = @enable_grub_emu_pci@ enable_grub_fstest = @enable_grub_fstest@ enable_grub_pe2elf = @enable_grub_pe2elf@ @@ -130,7 +142,7 @@ enable_efiemu = @enable_efiemu@ ### General variables. -RMKFILES = $(wildcard conf/*.rmk) +RMKFILES = $(wildcard $(srcdir)/conf/*.rmk) MKFILES = $(patsubst %.rmk,%.mk,$(RMKFILES)) @@ -145,11 +157,13 @@ INFOS = $(info_INFOS) CLEANFILES = MOSTLYCLEANFILES = DISTCLEANFILES = config.status config.cache config.log config.h \ - Makefile stamp-h include/grub/cpu include/grub/machine \ + Makefile stamp-h stamp-h1 include/grub/cpu include/grub/machine \ gensymlist.sh genkernsyms.sh build_env.mk \ docs/grub.info docs/version.texi docs/stamp-vti -MAINTAINER_CLEANFILES = $(srcdir)/configure $(addprefix $(srcdir)/,$(MKFILES)) \ +MAINTAINER_CLEANFILES = $(srcdir)/configure $(srcdir)/aclocal.m4 \ + $(MKFILES) $(srcdir)/config.guess \ + $(srcdir)/config.sub $(srcdir)/install-sh $(srcdir)/missing \ $(srcdir)/DISTLIST $(srcdir)/config.h.in $(srcdir)/stamp-h.in $(INFOS) # The default target. @@ -173,10 +187,17 @@ include $(srcdir)/conf/tests.mk -include $(wildcard $(GRUB_CONTRIB)/*/conf/common.mk) endif +ifeq ($(TARGET_NO_MODULES), yes) + TARGET_CFLAGS += -DGRUB_TARGET_NO_MODULES=1 + CFLAGS += -DGRUB_TARGET_NO_MODULES=1 +endif + ### General targets. CLEANFILES += $(pkglib_DATA) $(pkgdata_DATA) po/*.mo +ifneq ($(TARGET_NO_MODULES), yes) pkglib_DATA += moddep.lst command.lst fs.lst partmap.lst parttool.lst handler.lst video.lst crypto.lst terminal.lst +endif moddep.lst: $(DEFSYMFILES) $(UNDSYMFILES) genmoddep.awk cat $(DEFSYMFILES) /dev/null \ | $(AWK) -f $(srcdir)/genmoddep.awk $(UNDSYMFILES) > $@ \ @@ -237,6 +258,7 @@ else ifeq ($(enable_grub_mkfont),yes) pkgdata_DATA += unicode.pf2 ascii.pf2 ascii.h +CLEANFILES += ascii.bitmaps # Arrows and lines are needed to draw the menu, so we always include them UNICODE_ARROWS=0x2190-0x2193 @@ -265,7 +287,7 @@ build_env.mk: Makefile echo "TARGET_CC=$(TARGET_CC)" ; \ echo "TARGET_CFLAGS=$(TARGET_CFLAGS)" ; \ echo "TARGET_ASFLAGS=$(TARGET_ASFLAGS)" ; \ - echo "TARGET_CPPFLAGS=$(TARGET_CPPFLAGS) -I$(pkglibdir) -I$(includedir)" ; \ + echo "TARGET_CPPFLAGS=$(TARGET_CPPFLAGS) -I$(libdir) -I$(includedir)" ; \ echo "STRIP=$(STRIP)" ; \ echo "OBJCONV=$(OBJCONV)" ; \ echo "TARGET_MODULE_FORMAT=$(TARGET_MODULE_FORMAT)" ; \ @@ -276,7 +298,7 @@ build_env.mk: Makefile ) > $@ pkglib_BUILDDIR += config.h grub_script.tab.h -all-local: $(PROGRAMS) $(PKGLIB) $(PKGDATA) $(SCRIPTS) $(INFOS) $(MKFILES) $(foreach lang, $(LINGUAS), po/$(lang).mo) +all-local: $(PROGRAMS) $(GRUB_EMU) $(PKGLIB) $(PKGDATA) $(SCRIPTS) $(INFOS) $(MKFILES) $(foreach lang, $(LINGUAS), po/$(lang).mo) install: install-local @@ -297,7 +319,7 @@ install-local: all $(INSTALL_DATA) $$dir$$file $(DESTDIR)$(pkgdatadir)/$$dest; \ done $(SHELL) $(mkinstalldirs) $(DESTDIR)$(bindir) $(DESTDIR)$(mandir)/man1 - @list='$(bin_UTILITIES)'; for file in $$list; do \ + @list='$(bin_UTILITIES) $(GRUB_EMU)'; for file in $$list; do \ if test -f "$$file"; then dir=; else dir="$(srcdir)/"; fi; \ dest="`echo $$file | sed 's,.*/,,' | sed '$(transform)'`"; \ $(INSTALL_PROGRAM) $$dir$$file $(DESTDIR)$(bindir)/$$dest; \ @@ -373,7 +395,7 @@ uninstall: dest="`echo $$file | sed 's,.*/,,'`"; \ rm -f $(DESTDIR)$(pkgdatadir)/$$dest; \ done - @list='$(bin_UTILITIES) $(bin_SCRIPTS)'; for file in $$list; do \ + @list='$(bin_UTILITIES) $(bin_SCRIPTS) $(GRUB_EMU)'; for file in $$list; do \ dest="`echo $$file | sed 's,.*/,,' | sed '$(transform)'`"; \ rm -f $(DESTDIR)$(bindir)/$$dest; \ rm -f $(DESTDIR)$(mandir)/man1/$$dest.1; \ @@ -390,7 +412,6 @@ uninstall: @list='$(lib_SCRIPTS)'; \ for file in $$list; do \ dest="`echo $$file | sed 's,.*/,,'`"; \ - echo rm -f $(DESTDIR)$(libdir)/$$dest; \ rm -f $(DESTDIR)$(libdir)/grub/$$dest; \ done @list='$(info_INFOS)'; \ @@ -419,6 +440,8 @@ distclean: mostlyclean maintainer-clean: distclean -test -z "$(MAINTAINER_CLEANFILES)" || rm -f $(MAINTAINER_CLEANFILES) + -rmdir $(srcdir)/lib/libgcrypt-grub/cipher + -rmdir $(srcdir)/lib/libgcrypt-grub info: @@ -473,23 +496,21 @@ distcheck: dist check: all $(UNIT_TESTS) $(FUNCTIONAL_TESTS) $(SCRIPTED_TESTS) @list="$(UNIT_TESTS)"; \ + set -e; \ for file in $$list; do \ $(builddir)/$$file; \ done @list="$(FUNCTIONAL_TESTS)"; \ + set -e; \ for file in $$list; do \ mod=`basename $$file .mod`; \ echo "insmod functional_test; insmod $$mod; functional_test" \ | $(builddir)/grub-shell; \ done @list="$(SCRIPTED_TESTS)"; \ + set -e; \ for file in $$list; do \ - echo "$$file:"; \ - if $(builddir)/$$file; then \ - echo "$$file: PASS"; \ - else \ - echo "$$file: FAIL"; \ - fi; \ + $(builddir)/$$file; \ done .SUFFIX: @@ -524,8 +545,8 @@ genkernsyms.sh: genkernsyms.sh.in config.status $(SHELL) ./config.status $(srcdir)/po/$(PACKAGE).pot: po/POTFILES po/POTFILES-shell - cd $(srcdir) && $(XGETTEXT) -ctranslate --from-code=utf-8 -o $@ -f $< --keyword=_ --keyword=N_ - cd $(srcdir) && $(XGETTEXT) -ctranslate --from-code=utf-8 -o $@ -f po/POTFILES-shell -j --language=Shell + cd $(srcdir) && $(XGETTEXT) -ctranslate --from-code=utf-8 -o po/$(PACKAGE).pot -f po/POTFILES --keyword=_ --keyword=N_ + cd $(srcdir) && $(XGETTEXT) -ctranslate --from-code=utf-8 -o po/$(PACKAGE).pot -f po/POTFILES-shell -j --language=Shell $(foreach lang, $(LINGUAS), $(srcdir)/po/$(lang).po): po/$(PACKAGE).pot $(MSGMERGE) -U $@ $^ diff --git a/NEWS b/NEWS index cc725fd3b..1e3334f18 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,21 @@ -New in 1.98: +New in 1.98 - 2010-03-06: + +* Multiboot on EFI support. + +* Graphical menu support. + +* MIPS support. + +* Saved default menu entry support, with new utilities `grub-reboot' and + `grub-set-default'. + +* Unit testing framework. + +* Support for multiple terminals. + +* Encrypted password support, with a new utility `grub-mkpasswd-pbkdf2'. + +* `grub-mkfloppy' removed; use `grub-mkrescue' to create floppy images. * Add grub-probe support for GNU/Hurd. diff --git a/acinclude.m4 b/acinclude.m4 index 36a3b3e08..72483b5d0 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -14,7 +14,7 @@ $2 dnl Check whether target compiler is working -AC_DEFUN(grub_PROG_TARGET_CC, +AC_DEFUN([grub_PROG_TARGET_CC], [AC_MSG_CHECKING([whether target compiler is working]) AC_CACHE_VAL(grub_cv_prog_target_cc, [AC_LINK_IFELSE([AC_LANG_PROGRAM([[ @@ -36,7 +36,7 @@ dnl grub_ASM_USCORE checks if C symbols get an underscore after dnl compiling to assembler. dnl Written by Pavel Roskin. Based on grub_ASM_EXT_C written by dnl Erich Boleyn and modified by Yoshinori K. Okuji. -AC_DEFUN(grub_ASM_USCORE, +AC_DEFUN([grub_ASM_USCORE], [AC_REQUIRE([AC_PROG_CC]) AC_MSG_CHECKING([if C symbols get an underscore after compilation]) AC_CACHE_VAL(grub_cv_asm_uscore, @@ -75,7 +75,7 @@ AC_MSG_RESULT([$grub_cv_asm_uscore]) dnl Some versions of `objcopy -O binary' vary their output depending dnl on the link address. -AC_DEFUN(grub_PROG_OBJCOPY_ABSOLUTE, +AC_DEFUN([grub_PROG_OBJCOPY_ABSOLUTE], [AC_MSG_CHECKING([whether ${OBJCOPY} works for absolute addresses]) AC_CACHE_VAL(grub_cv_prog_objcopy_absolute, [cat > conftest.c <<\EOF @@ -93,7 +93,7 @@ else fi grub_cv_prog_objcopy_absolute=yes for link_addr in 0x2000 0x8000 0x7C00; do - if AC_TRY_COMMAND([${CC-cc} ${CFLAGS} -nostdlib ${TARGET_IMG_LDFLAGS_AC} -Wl,-Ttext -Wl,$link_addr conftest.o -o conftest.exec]); then : + if AC_TRY_COMMAND([${CC-cc} ${CFLAGS} -nostdlib ${TARGET_IMG_LDFLAGS_AC}$link_addr conftest.o -o conftest.exec]); then : else AC_MSG_ERROR([${CC-cc} cannot link at address $link_addr]) fi @@ -119,7 +119,7 @@ fi dnl Supply --build-id=none to ld if building modules. dnl This suppresses warnings from ld on some systems -AC_DEFUN(grub_PROG_LD_BUILD_ID_NONE, +AC_DEFUN([grub_PROG_LD_BUILD_ID_NONE], [AC_MSG_CHECKING([whether linker accepts --build-id=none]) AC_CACHE_VAL(grub_cv_prog_ld_build_id_none, [save_LDFLAGS="$LDFLAGS" @@ -150,7 +150,7 @@ dnl dnl We only support the newer versions, because the old versions cause dnl major pain, by requiring manual assembly to get 16-bit instructions into dnl asm files. -AC_DEFUN(grub_I386_ASM_ADDR32, +AC_DEFUN([grub_I386_ASM_ADDR32], [AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([grub_I386_ASM_PREFIX_REQUIREMENT]) AC_MSG_CHECKING([for .code16 addr32 assembler support]) @@ -178,7 +178,7 @@ AC_MSG_RESULT([$grub_cv_i386_asm_addr32])]) dnl check if our compiler is apple cc dnl because it requires numerous workarounds -AC_DEFUN(grub_apple_cc, +AC_DEFUN([grub_apple_cc], [AC_REQUIRE([AC_PROG_CC]) AC_MSG_CHECKING([whether our compiler is apple cc]) AC_CACHE_VAL(grub_cv_apple_cc, @@ -193,7 +193,7 @@ AC_MSG_RESULT([$grub_cv_apple_cc])]) dnl check if our target compiler is apple cc dnl because it requires numerous workarounds -AC_DEFUN(grub_apple_target_cc, +AC_DEFUN([grub_apple_target_cc], [AC_REQUIRE([AC_PROG_CC]) AC_MSG_CHECKING([whether our target compiler is apple cc]) AC_CACHE_VAL(grub_cv_apple_target_cc, @@ -210,7 +210,7 @@ AC_MSG_RESULT([$grub_cv_apple_target_cc])]) dnl Later versions of GAS requires that addr32 and data32 prefixes dnl appear in the same lines as the instructions they modify, while dnl earlier versions requires that they appear in separate lines. -AC_DEFUN(grub_I386_ASM_PREFIX_REQUIREMENT, +AC_DEFUN([grub_I386_ASM_PREFIX_REQUIREMENT], [AC_REQUIRE([AC_PROG_CC]) AC_MSG_CHECKING(dnl [whether addr32 must be in the same line as the instruction]) @@ -246,7 +246,7 @@ AC_MSG_RESULT([$grub_cv_i386_asm_prefix_requirement])]) dnl Older versions of GAS require that absolute indirect calls/jumps are dnl not prefixed with `*', while later versions warn if not prefixed. -AC_DEFUN(grub_I386_ASM_ABSOLUTE_WITHOUT_ASTERISK, +AC_DEFUN([grub_I386_ASM_ABSOLUTE_WITHOUT_ASTERISK], [AC_REQUIRE([AC_PROG_CC]) AC_MSG_CHECKING(dnl [whether an absolute indirect call/jump must not be prefixed with an asterisk]) @@ -276,7 +276,7 @@ AC_MSG_RESULT([$grub_cv_i386_asm_absolute_without_asterisk])]) dnl Check what symbol is defined as a bss start symbol. dnl Written by Michael Hohmoth and Yoshinori K. Okuji. -AC_DEFUN(grub_CHECK_BSS_START_SYMBOL, +AC_DEFUN([grub_CHECK_BSS_START_SYMBOL], [AC_REQUIRE([AC_PROG_CC]) AC_MSG_CHECKING([if __bss_start is defined by the compiler]) AC_CACHE_VAL(grub_cv_check_uscore_uscore_bss_start_symbol, @@ -320,7 +320,7 @@ fi dnl Check what symbol is defined as an end symbol. dnl Written by Yoshinori K. Okuji. -AC_DEFUN(grub_CHECK_END_SYMBOL, +AC_DEFUN([grub_CHECK_END_SYMBOL], [AC_REQUIRE([AC_PROG_CC]) AC_MSG_CHECKING([if end is defined by the compiler]) AC_CACHE_VAL(grub_cv_check_end_symbol, @@ -352,7 +352,7 @@ fi ]) dnl Check if the C compiler generates calls to `__enable_execute_stack()'. -AC_DEFUN(grub_CHECK_ENABLE_EXECUTE_STACK,[ +AC_DEFUN([grub_CHECK_ENABLE_EXECUTE_STACK],[ AC_MSG_CHECKING([whether `$CC' generates calls to `__enable_execute_stack()']) AC_LANG_CONFTEST([[ void f (int (*p) (void)); @@ -379,7 +379,7 @@ rm -f conftest* dnl Check if the C compiler supports `-fstack-protector'. -AC_DEFUN(grub_CHECK_STACK_PROTECTOR,[ +AC_DEFUN([grub_CHECK_STACK_PROTECTOR],[ [# Smashing stack protector. ssp_possible=yes] AC_MSG_CHECKING([whether `$CC' accepts `-fstack-protector']) @@ -398,7 +398,7 @@ else ]) dnl Check if the C compiler supports `-mstack-arg-probe' (Cygwin). -AC_DEFUN(grub_CHECK_STACK_ARG_PROBE,[ +AC_DEFUN([grub_CHECK_STACK_ARG_PROBE],[ [# Smashing stack arg probe. sap_possible=yes] AC_MSG_CHECKING([whether `$CC' accepts `-mstack-arg-probe']) @@ -414,7 +414,7 @@ else ]) dnl Check if ln can handle directories properly (mingw). -AC_DEFUN(grub_CHECK_LINK_DIR,[ +AC_DEFUN([grub_CHECK_LINK_DIR],[ AC_MSG_CHECKING([whether ln can handle directories properly]) [mkdir testdir 2>/dev/null case $srcdir in @@ -432,7 +432,7 @@ rm -rf testdir] ]) dnl Check if the C compiler supports `-fPIE'. -AC_DEFUN(grub_CHECK_PIE,[ +AC_DEFUN([grub_CHECK_PIE],[ [# Position independent executable. pie_possible=yes] AC_MSG_CHECKING([whether `$CC' has `-fPIE' as default]) diff --git a/boot/i386/pc/boot.S b/boot/i386/pc/boot.S index 257f9044e..4afc57349 100644 --- a/boot/i386/pc/boot.S +++ b/boot/i386/pc/boot.S @@ -27,6 +27,7 @@ /* Print message string */ #define MSG(x) movw $x, %si; call LOCAL(message) +#define ERR(x) movw $x, %si; jmp LOCAL(error_message) .file "boot.S" @@ -233,7 +234,7 @@ LOCAL(chs_mode): jz LOCAL(floppy_probe) /* Nope, we definitely have a hard disk, and we're screwed. */ - jmp LOCAL(hd_probe_error) + ERR(hd_probe_error_string) LOCAL(final_init): /* set the mode to zero */ @@ -360,22 +361,15 @@ LOCAL(copy_buffer): * BIOS Geometry translation error (past the end of the disk geometry!). */ LOCAL(geometry_error): - MSG(geometry_error_string) - jmp LOCAL(general_error) - -/* - * Disk probe failure. - */ -LOCAL(hd_probe_error): - MSG(hd_probe_error_string) - jmp LOCAL(general_error) + ERR(geometry_error_string) /* * Read error on the disk. */ LOCAL(read_error): - MSG(read_error_string) - + movw $read_error_string, %si +LOCAL(error_message): + call LOCAL(message) LOCAL(general_error): MSG(general_error_string) diff --git a/boot/i386/pc/pxeboot.S b/boot/i386/pc/pxeboot.S index 28c90e29b..446bfc781 100644 --- a/boot/i386/pc/pxeboot.S +++ b/boot/i386/pc/pxeboot.S @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2000,2005,2007,2008,2009 Free Software Foundation, Inc. + * Copyright (C) 2000,2005,2007,2008,2009,2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,6 +16,8 @@ * along with GRUB. If not, see . */ +#include + .file "pxeboot.S" .text @@ -28,7 +30,7 @@ _start: start: /* Use drive number 0x7F for PXE */ - movb $0x7F, %dl + movb $GRUB_BOOT_MACHINE_PXE_DL, %dl /* Jump to the real world */ ljmp $0, $0x8200 diff --git a/boot/i386/qemu/boot.S b/boot/i386/qemu/boot.S index a93fe3943..51039dd69 100644 --- a/boot/i386/qemu/boot.S +++ b/boot/i386/qemu/boot.S @@ -31,7 +31,7 @@ _start: jmp 1f - . = _start + GRUB_BOOT_MACHINE_CORE_ENTRY_ADDR + . = _start + GRUB_BOOT_I386_QEMU_CORE_ENTRY_ADDR VARIABLE(grub_core_entry_addr) .long 0 1: diff --git a/boot/sparc64/ieee1275/boot.S b/boot/sparc64/ieee1275/boot.S index 74f4ee014..f08258f47 100644 --- a/boot/sparc64/ieee1275/boot.S +++ b/boot/sparc64/ieee1275/boot.S @@ -45,8 +45,9 @@ boot_version: .byte GRUB_BOOT_VERSION_MAJOR, GRUB_BOOT_VERSION_MINOR * load address plus the size of the prepended A.OUT header (32 bytes). */ boot_path: - . = _start + GRUB_BOOT_MACHINE_KERNEL_SECTOR -kernel_sector: .xword 2 + . = _start + GRUB_BOOT_MACHINE_KERNEL_BYTE +boot_path_end: +kernel_byte: .xword (2 << 9) kernel_address: .word GRUB_BOOT_MACHINE_KERNEL_ADDR prom_finddev_name: .asciz "finddevice" @@ -77,11 +78,23 @@ prom_error: /* %o0: OF call name * %o1: input arg 1 */ -prom_call_1_1: - mov 1, %g1 - ba prom_call - mov 1, %o5 +prom_call_1_1_o2: + clr %o2 + ba prom_call_x_1 + mov 1, %g1 +prom_call_getprop: + mov 4, %g1 + stx %g1, [%l1 + 256] + mov CHOSEN_NODE_REG, %o1 + ba prom_call_x_1 + GET_ABS(prom_getprop_name, %o0) + +prom_call_3_1_o1: + ba prom_call_3_1 + mov BOOTDEV_REG, %o1 + + /* %o2: message string * %o3: message length */ @@ -95,8 +108,9 @@ console_write: * %o2: input arg 2 * %o3: input arg 3 */ -prom_call_3_1: +prom_call_3_1: mov 3, %g1 +prom_call_x_1: mov 1, %o5 /* fallthru */ @@ -118,7 +132,7 @@ prom_call: boot_continue: mov %o7, PIC_REG /* PIC base */ - sethi %hi(SCRATCH_PAD), %l1 /* OF argument slots */ + sethi %hi(SCRATCH_PAD_BOOT), %l1 /* OF argument slots */ /* Find the /chosen node so we can fetch the stdout handle, * and thus perform console output. @@ -126,23 +140,17 @@ boot_continue: * chosen_node = prom_finddevice("/chosen") */ GET_ABS(prom_finddev_name, %o0) - GET_ABS(prom_chosen_path, %o1) - call prom_call_1_1 - clr %o2 + call prom_call_1_1_o2 + GET_ABS(prom_chosen_path, %o1) ldx [%l1 + 0x20], CHOSEN_NODE_REG brz CHOSEN_NODE_REG, prom_error /* getprop(chosen_node, "stdout", &buffer, buffer_size) */ - GET_ABS(prom_getprop_name, %o0) - mov 4, %g1 - mov 1, %o5 - mov CHOSEN_NODE_REG, %o1 - GET_ABS(prom_stdout_name, %o2) + GET_ABS(prom_stdout_name, %o2) add %l1, 256, %o3 - mov 1024, %o4 - call prom_call - stx %g1, [%l1 + 256] + call prom_call_getprop + mov 1024, %o4 lduw [%l1 + 256], STDOUT_NODE_REG brz,pn STDOUT_NODE_REG, prom_error @@ -152,15 +160,25 @@ boot_continue: call console_write mov GRUB_NAME_LEN, %o3 + GET_ABS(boot_path, %o3) + ldub [%o3], %o1 + brnz,pn %o1, bootpath_known + + /* getprop(chosen_node, "bootpath", &buffer, buffer_size) */ + GET_ABS(prom_bootpath_name, %o2) + call prom_call_getprop + mov (boot_path_end - boot_path), %o4 + +bootpath_known: + /* Open up the boot_path, and use that handle to read the * first block of the GRUB kernel image. * * bootdev_handle = open(boot_path) */ GET_ABS(prom_open_name, %o0) - GET_ABS(boot_path, %o1) - call prom_call_1_1 - clr %o2 + call prom_call_1_1_o2 + GET_ABS(boot_path, %o1) ldx [%l1 + 0x20], BOOTDEV_REG brz,pn BOOTDEV_REG, prom_open_error @@ -168,28 +186,23 @@ boot_continue: /* Since we have 64-bit cells, the high cell of the seek offset * is zero and the low cell is the entire value. * - * seek(bootdev, 0, *kernel_sector << 9) + * seek(bootdev, 0, *kernel_byte) */ GET_ABS(prom_seek_name, %o0) - mov BOOTDEV_REG, %o1 clr %o2 - LDX_ABS(kernel_sector, 0x00, %o3) - call prom_call_3_1 - sllx %o3, 9, %o3 + call prom_call_3_1_o1 + LDX_ABS(kernel_byte, 0x00, %o3) /* read(bootdev, *kernel_address, 512) */ GET_ABS(prom_read_name, %o0) - mov BOOTDEV_REG, %o1 LDUW_ABS(kernel_address, 0x00, %o2) - call prom_call_3_1 + call prom_call_3_1_o1 mov 512, %o3 LDUW_ABS(kernel_address, 0x00, %o2) jmpl %o2, %o7 nop -1: ba,a 1b - . = _start + GRUB_BOOT_MACHINE_CODE_END /* the last 4 bytes in the sector 0 contain the signature */ diff --git a/boot/sparc64/ieee1275/diskboot.S b/boot/sparc64/ieee1275/diskboot.S index 68ed0eee0..83dfee098 100644 --- a/boot/sparc64/ieee1275/diskboot.S +++ b/boot/sparc64/ieee1275/diskboot.S @@ -19,6 +19,7 @@ #include #include +#include .text .align 4 @@ -81,14 +82,14 @@ prom_call: after_info_block: - sethi %hi(SCRATCH_PAD), %l1 /* OF argument slots */ + sethi %hi(SCRATCH_PAD_DISKBOOT), %l1 /* OF argument slots */ GET_ABS(notification_string, %o2) call console_write mov NOTIFICATION_STRING_LEN, %o3 - GET_ABS(firstlist - GRUB_BOOT_MACHINE_LIST_SIZE, %l2) - set GRUB_BOOT_MACHINE_IMAGE_ADDRESS, %l3 + GET_ABS(firstlist - GRUB_BOOT_SPARC64_IEEE1275_LIST_SIZE, %l2) + set GRUB_BOOT_SPARC64_IEEE1275_IMAGE_ADDRESS, %l3 bootloop: lduw [%l2 + 0x08], %o0 brz %o0, bootit @@ -115,7 +116,7 @@ bootloop: mov NOTIFICATION_STEP_LEN, %o3 ba bootloop - sub %l2, GRUB_BOOT_MACHINE_LIST_SIZE, %l2 + sub %l2, GRUB_BOOT_SPARC64_IEEE1275_LIST_SIZE, %l2 bootit: GET_ABS(prom_close_name, %o0) @@ -127,16 +128,16 @@ bootit: GET_ABS(notification_done, %o2) call console_write mov NOTIFICATION_DONE_LEN, %o3 - sethi %hi(GRUB_BOOT_MACHINE_IMAGE_ADDRESS), %o2 - jmpl %o2 + %lo(GRUB_BOOT_MACHINE_IMAGE_ADDRESS), %o7 - mov CIF_REG, %o0 + sethi %hi(GRUB_BOOT_SPARC64_IEEE1275_IMAGE_ADDRESS), %o2 + jmpl %o2 + %lo(GRUB_BOOT_SPARC64_IEEE1275_IMAGE_ADDRESS), %o7 + mov CIF_REG, %o4 1: ba,a 1b lastlist: .word 0 .word 0 - . = _start + (0x200 - GRUB_BOOT_MACHINE_LIST_SIZE) + . = _start + (0x200 - GRUB_BOOT_SPARC64_IEEE1275_LIST_SIZE) blocklist_default_start: .word 0 .word 2 diff --git a/commands/blocklist.c b/commands/blocklist.c index fec59a828..cace31113 100644 --- a/commands/blocklist.c +++ b/commands/blocklist.c @@ -90,8 +90,7 @@ grub_cmd_blocklist (grub_command_t cmd __attribute__ ((unused)), return grub_error (GRUB_ERR_BAD_DEVICE, "this command is available only for disk devices"); - if (file->device->disk->partition) - part_start = grub_partition_get_start (file->device->disk->partition); + part_start = grub_partition_get_start (file->device->disk->partition); file->read_hook = read_blocklist; diff --git a/commands/cat.c b/commands/cat.c index 844034777..3bdafc4c6 100644 --- a/commands/cat.c +++ b/commands/cat.c @@ -41,7 +41,7 @@ grub_cmd_cat (grub_command_t cmd __attribute__ ((unused)), file = grub_gzfile_open (args[0], 1); if (! file) - return 0; + return grub_errno; while ((size = grub_file_read (file, buf, sizeof (buf))) > 0 && key != GRUB_TERM_ESC) diff --git a/commands/hashsum.c b/commands/hashsum.c index a4e71b844..951479fa7 100644 --- a/commands/hashsum.c +++ b/commands/hashsum.c @@ -57,7 +57,7 @@ static grub_err_t hash_file (grub_file_t file, const gcry_md_spec_t *hash, void *result) { grub_uint8_t context[hash->contextsize]; - char *readbuf[4096]; + grub_uint8_t readbuf[4096]; grub_memset (context, 0, sizeof (context)); hash->init (context); diff --git a/commands/i386/pc/drivemap_int13h.S b/commands/i386/pc/drivemap_int13h.S index 440349685..b460cd7b5 100644 --- a/commands/i386/pc/drivemap_int13h.S +++ b/commands/i386/pc/drivemap_int13h.S @@ -19,7 +19,7 @@ #include -#define INT13H_OFFSET(x) ((x) - EXT_C(grub_drivemap_handler)) +#define INT13H_OFFSET(x) ((x) - LOCAL (base)) .code16 @@ -27,6 +27,7 @@ /* The replacement int13 handler. Preserve all registers. */ FUNCTION(grub_drivemap_handler) +LOCAL (base): /* Save %dx for future restore. */ push %dx /* Push flags. Used to simulate interrupt with original flags. */ @@ -35,12 +36,7 @@ FUNCTION(grub_drivemap_handler) /* Map the drive number (always in DL). */ push %ax push %bx -#ifdef APPLE_CC - grub_drivemap_mapstart_ofs = INT13H_OFFSET(EXT_C(grub_drivemap_mapstart)) - movw $grub_drivemap_mapstart_ofs, %bx -#else - movw $INT13H_OFFSET(EXT_C(grub_drivemap_mapstart)), %bx -#endif + movw $INT13H_OFFSET(LOCAL (mapstart)), %bx more_remaining: movw %cs:(%bx), %ax @@ -66,12 +62,7 @@ not_found: popf pushf -#ifdef APPLE_CC - grub_drivemap_oldhandler_ofs = INT13H_OFFSET (EXT_C (grub_drivemap_oldhandler)) - lcall *%cs:grub_drivemap_oldhandler_ofs -#else - lcall *%cs:INT13H_OFFSET (EXT_C (grub_drivemap_oldhandler)) -#endif + lcall *%cs:INT13H_OFFSET (LOCAL (oldhandler)) push %bp mov %sp, %bp @@ -94,11 +85,7 @@ norestore: popf pushf -#ifdef APPLE_CC - lcall *%cs:grub_drivemap_oldhandler_ofs -#else - lcall *%cs:INT13H_OFFSET (EXT_C (grub_drivemap_oldhandler)) -#endif + lcall *%cs:INT13H_OFFSET (LOCAL (oldhandler)) push %bp mov %sp, %bp @@ -111,9 +98,13 @@ norestore: /* Far pointer to the old handler. Stored as a CS:IP in the style of real-mode IVT entries (thus PI:SC in mem). */ VARIABLE(grub_drivemap_oldhandler) +LOCAL (oldhandler): .word 0x0, 0x0 /* This label MUST be at the end of the copied block, since the installer code reserves additional space for mappings at runtime and copies them over it. */ -.align 2 + .align 2 + VARIABLE(grub_drivemap_mapstart) +LOCAL (mapstart): + .byte 0 diff --git a/commands/i386/pc/play.c b/commands/i386/pc/play.c index 1151dddf4..44d98a1f0 100644 --- a/commands/i386/pc/play.c +++ b/commands/i386/pc/play.c @@ -29,7 +29,7 @@ #include #include -#define BASE_TEMPO 120 +#define BASE_TEMPO (60 * GRUB_TICKS_PER_SECOND) /* The speaker port. */ #define SPEAKER 0x61 @@ -101,13 +101,13 @@ #define PIT_CTRL_COUNT_BINARY 0x00 /* 16-bit binary counter. */ #define PIT_CTRL_COUNT_BCD 0x01 /* 4-decade BCD counter. */ -#define T_REST ((short) 0) -#define T_FINE ((short) -1) +#define T_REST ((grub_uint16_t) 0) +#define T_FINE ((grub_uint16_t) -1) struct note { - short pitch; - short duration; + grub_uint16_t pitch; + grub_uint16_t duration; }; static void @@ -120,7 +120,7 @@ beep_off (void) } static void -beep_on (short pitch) +beep_on (grub_uint16_t pitch) { unsigned char status; unsigned int counter; @@ -143,60 +143,111 @@ beep_on (short pitch) grub_outb (status | SPEAKER_TMR2 | SPEAKER_DATA, SPEAKER); } +/* Returns whether playing should continue. */ +static int +play (unsigned tempo, struct note *note) +{ + unsigned int to; + + if (note->pitch == T_FINE || grub_checkkey () >= 0) + return 1; + + grub_dprintf ("play", "pitch = %d, duration = %d\n", note->pitch, + note->duration); + + switch (note->pitch) + { + case T_REST: + beep_off (); + break; + + default: + beep_on (note->pitch); + break; + } + + to = grub_get_rtc () + BASE_TEMPO * note->duration / tempo; + while (((unsigned int) grub_get_rtc () <= to) && (grub_checkkey () < 0)) + ; + + return 0; +} + static grub_err_t grub_cmd_play (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) { grub_file_t file; - struct note buf; - int tempo; - unsigned int to; - if (argc != 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required"); + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name or tempo and notes required"); file = grub_file_open (args[0]); - if (! file) - return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); - - if (grub_file_read (file, &tempo, sizeof(tempo)) != sizeof(tempo)) + if (file) { - grub_file_close (file); - return grub_error (GRUB_ERR_FILE_READ_ERROR, - "file doesn't even contains a full tempo record"); - } + struct note buf; + grub_uint32_t tempo; - grub_dprintf ("play","tempo = %d\n", tempo); - - while (grub_file_read (file, &buf, - sizeof (struct note)) == sizeof (struct note) - && buf.pitch != T_FINE && grub_checkkey () < 0) - { - - grub_dprintf ("play", "pitch = %d, duration = %d\n", buf.pitch, - buf.duration); - - switch (buf.pitch) + if (grub_file_read (file, &tempo, sizeof (tempo)) != sizeof (tempo)) { - case T_REST: - beep_off (); - break; + grub_file_close (file); + return grub_error (GRUB_ERR_FILE_READ_ERROR, + "file doesn't even contains a full tempo record"); + } - default: - beep_on (buf.pitch); + tempo = grub_le_to_cpu32 (tempo); + grub_dprintf ("play","tempo = %d\n", tempo); + + while (grub_file_read (file, &buf, + sizeof (struct note)) == sizeof (struct note)) + { + buf.pitch = grub_le_to_cpu16 (buf.pitch); + buf.duration = grub_le_to_cpu16 (buf.duration); + + if (play (tempo, &buf)) break; } - to = grub_get_rtc () + BASE_TEMPO * buf.duration / tempo; - while (((unsigned int) grub_get_rtc () <= to) && (grub_checkkey () < 0)) - ; + grub_file_close (file); + } + else + { + char *end; + unsigned tempo; + struct note note; + int i; + tempo = grub_strtoul (args[0], &end, 0); + + if (*end) + /* Was not a number either, assume it was supposed to be a file name. */ + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); + + grub_dprintf ("play","tempo = %d\n", tempo); + + for (i = 1; i + 1 < argc; i += 2) + { + note.pitch = grub_strtoul (args[i], &end, 0); + if (*end) + { + grub_error (GRUB_ERR_BAD_NUMBER, "bogus pitch number"); + break; + } + + note.duration = grub_strtoul (args[i + 1], &end, 0); + if (*end) + { + grub_error (GRUB_ERR_BAD_NUMBER, "bogus duration number"); + break; + } + + if (play (tempo, ¬e)) + break; + } } beep_off (); - grub_file_close (file); - while (grub_checkkey () > 0) grub_getkey (); @@ -208,7 +259,8 @@ static grub_command_t cmd; GRUB_MOD_INIT(play) { cmd = grub_register_command ("play", grub_cmd_play, - N_("FILE"), N_("Play a tune.")); + N_("FILE | TEMPO [PITCH1 DURATION1] [PITCH2 DURATION2] ... "), + N_("Play a tune.")); } GRUB_MOD_FINI(play) diff --git a/commands/i386/pc/vbetest.c b/commands/i386/pc/vbetest.c index d97323087..d2921c09d 100644 --- a/commands/i386/pc/vbetest.c +++ b/commands/i386/pc/vbetest.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include diff --git a/commands/loadenv.c b/commands/loadenv.c index 51b88cbc9..d763b2d5e 100644 --- a/commands/loadenv.c +++ b/commands/loadenv.c @@ -235,10 +235,8 @@ check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists, /* One more sanity check. Re-read all sectors by blocklists, and compare those with the data read via a file. */ disk = file->device->disk; - if (disk->partition) - part_start = grub_partition_get_start (disk->partition); - else - part_start = 0; + + part_start = grub_partition_get_start (disk->partition); buf = grub_envblk_buffer (envblk); for (p = blocklists, index = 0; p; index += p->length, p = p->next) @@ -268,10 +266,7 @@ write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists, buf = grub_envblk_buffer (envblk); disk = file->device->disk; - if (disk->partition) - part_start = grub_partition_get_start (disk->partition); - else - part_start = 0; + part_start = grub_partition_get_start (disk->partition); index = 0; for (p = blocklists; p; index += p->length, p = p->next) diff --git a/commands/ls.c b/commands/ls.c index 8a8319ac8..eb1049617 100644 --- a/commands/ls.c +++ b/commands/ls.c @@ -87,14 +87,13 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) int print_files_long (const char *filename, const struct grub_dirhook_info *info) { - char *pathname; - if ((! all) && (filename[0] == '.')) return 0; if (! info->dir) { grub_file_t file; + char *pathname; if (dirname[grub_strlen (dirname) - 1] == '/') pathname = grub_xasprintf ("%s%s", dirname, filename); @@ -110,6 +109,7 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) if (! file) { grub_errno = 0; + grub_free (pathname); return 0; } @@ -144,6 +144,7 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) } grub_file_close (file); + grub_free (pathname); } else grub_printf ("%-12s", "DIR"); diff --git a/commands/lsmmap.c b/commands/lsmmap.c index d5eef1ce9..2755df9c4 100644 --- a/commands/lsmmap.c +++ b/commands/lsmmap.c @@ -16,7 +16,9 @@ * along with GRUB. If not, see . */ +#ifndef GRUB_MACHINE_EMU #include +#endif #include #include #include @@ -34,7 +36,9 @@ grub_cmd_lsmmap (grub_command_t cmd __attribute__ ((unused)), (long long) addr, (long long) size, type); return 0; } +#ifndef GRUB_MACHINE_EMU grub_machine_mmap_iterate (hook); +#endif return 0; } diff --git a/commands/minicmd.c b/commands/minicmd.c index 72e855faf..4ea9efead 100644 --- a/commands/minicmd.c +++ b/commands/minicmd.c @@ -316,7 +316,6 @@ grub_mini_cmd_lsmod (struct grub_command *cmd __attribute__ ((unused)), grub_printf ("%s", dep->mod->name); } grub_putchar ('\n'); - grub_refresh (); return 0; } diff --git a/commands/parttool.c b/commands/parttool.c index 5ad6133ca..0850c5e1d 100644 --- a/commands/parttool.c +++ b/commands/parttool.c @@ -175,7 +175,7 @@ grub_cmd_parttool (grub_command_t cmd __attribute__ ((unused)), } /* Load modules. */ -#ifndef GRUB_UTIL +#if GRUB_NO_MODULES { const char *prefix; prefix = grub_env_get ("prefix"); diff --git a/commands/regexp.c b/commands/regexp.c new file mode 100644 index 000000000..910bb9082 --- /dev/null +++ b/commands/regexp.c @@ -0,0 +1,79 @@ +/* regexp.c -- The regexp command. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +static grub_err_t +grub_cmd_regexp (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + int argn = 0; + int matches = 0; + regex_t regex; + int ret; + grub_size_t s; + char *comperr; + grub_err_t err; + + if (argc != 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "2 arguments expected"); + + ret = regcomp (®ex, args[0], RE_SYNTAX_GNU_AWK); + if (ret) + goto fail; + + ret = regexec (®ex, args[1], 0, 0, 0); + if (!ret) + { + regfree (®ex); + return GRUB_ERR_NONE; + } + + fail: + s = regerror (ret, ®ex, 0, 0); + comperr = grub_malloc (s); + if (!comperr) + { + regfree (®ex); + return grub_errno; + } + regerror (ret, ®ex, comperr, s); + err = grub_error (GRUB_ERR_TEST_FAILURE, "%s", comperr); + regfree (®ex); + grub_free (comperr); + return err; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(regexp) +{ + cmd = grub_register_command ("regexp", grub_cmd_regexp, + "REGEXP STRING", + "Test if REGEXP matches STRING."); +} + +GRUB_MOD_FINI(regexp) +{ + grub_unregister_command (cmd); +} diff --git a/commands/search.c b/commands/search.c index afb2e98e8..71267b117 100644 --- a/commands/search.c +++ b/commands/search.c @@ -149,11 +149,11 @@ grub_cmd_do_search (grub_command_t cmd __attribute__ ((unused)), int argc, static grub_command_t cmd; #ifdef DO_SEARCH_FILE -GRUB_MOD_INIT(search_file) +GRUB_MOD_INIT(search_fs_file) #elif defined (DO_SEARCH_FS_UUID) GRUB_MOD_INIT(search_fs_uuid) #else -GRUB_MOD_INIT(search_fs_label) +GRUB_MOD_INIT(search_label) #endif { cmd = @@ -163,11 +163,11 @@ GRUB_MOD_INIT(search_fs_label) } #ifdef DO_SEARCH_FILE -GRUB_MOD_FINI(search_file) +GRUB_MOD_FINI(search_fs_file) #elif defined (DO_SEARCH_FS_UUID) GRUB_MOD_FINI(search_fs_uuid) #else -GRUB_MOD_FINI(search_fs_label) +GRUB_MOD_FINI(search_label) #endif { grub_unregister_command (cmd); diff --git a/commands/sleep.c b/commands/sleep.c index 012181fa2..ead279506 100644 --- a/commands/sleep.c +++ b/commands/sleep.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include diff --git a/commands/videotest.c b/commands/videotest.c index 1730a2031..390811a71 100644 --- a/commands/videotest.c +++ b/commands/videotest.c @@ -69,9 +69,9 @@ grub_cmd_videotest (grub_command_t cmd __attribute__ ((unused)), color = grub_video_map_rgb (0, 255, 255); grub_video_fill_rect (color, 100, 100, 100, 100); - sansbig = grub_font_get ("Helvetica Bold 24"); - sans = grub_font_get ("Helvetica Bold 14"); - sanssmall = grub_font_get ("Helvetica 8"); + sansbig = grub_font_get ("Unknown Regular 16"); + sans = grub_font_get ("Unknown Regular 16"); + sanssmall = grub_font_get ("Unknown Regular 16"); fixed = grub_font_get ("Fixed 20"); if (! sansbig || ! sans || ! sanssmall || ! fixed) return grub_error (GRUB_ERR_BAD_FONT, "no font loaded"); @@ -126,11 +126,6 @@ grub_cmd_videotest (grub_command_t cmd __attribute__ ((unused)), grub_font_draw_string (str, fixed, color, 16, texty); texty += grub_font_get_descent (fixed) + grub_font_get_leading (fixed); - /* Some character don't exist in the Helvetica font, so the font engine - will fall back to using glyphs from another font that does contain them. - TODO The font engine should be smart about selecting a replacement font - and prioritize fonts with similar sizes. */ - texty += grub_font_get_ascent(sansbig); grub_font_draw_string (str, sansbig, color, 16, texty); texty += grub_font_get_descent (sansbig) + grub_font_get_leading (sansbig); diff --git a/conf/any-emu.rmk b/conf/any-emu.rmk index 1277af791..d1e5754dc 100644 --- a/conf/any-emu.rmk +++ b/conf/any-emu.rmk @@ -1,103 +1,113 @@ # -*- makefile -*- -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h +COMMON_CFLAGS += -nostdinc -isystem $(shell $(TARGET_CC) -print-file-name=include) -sbin_UTILITIES += grub-emu -util/grub-emu.c_DEPENDENCIES = grub_emu_init.h -grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ - commands/configfile.c commands/echo.c commands/help.c \ - commands/handler.c commands/ls.c commands/test.c \ - commands/search_wrap.c commands/search_file.c \ - commands/search_label.c commands/search_uuid.c \ - commands/blocklist.c commands/hexdump.c \ - lib/hexdump.c commands/halt.c commands/reboot.c \ - lib/envblk.c commands/loadenv.c \ - commands/gptsync.c commands/probe.c commands/xnu_uuid.c \ - commands/password.c commands/keystatus.c \ - disk/host.c disk/loopback.c disk/scsi.c \ - fs/fshelp.c \ - \ - io/gzio.c \ - kern/device.c kern/disk.c kern/dl.c kern/elf.c kern/env.c \ - kern/err.c kern/list.c kern/handler.c \ - kern/command.c kern/corecmd.c commands/extcmd.c kern/file.c \ - kern/fs.c commands/boot.c kern/main.c kern/misc.c kern/parser.c \ - kern/partition.c kern/term.c \ +kernel_img_RELOCATABLE = yes +pkglib_PROGRAMS = kernel.img +kernel_img_SOURCES = kern/device.c kern/disk.c kern/dl.c kern/env.c \ + kern/err.c kern/list.c kern/handler.c kern/command.c \ + kern/corecmd.c kern/file.c kern/fs.c kern/main.c kern/misc.c \ + kern/parser.c kern/partition.c kern/term.c \ kern/rescue_reader.c kern/rescue_parser.c \ - lib/arg.c normal/cmdline.c normal/datetime.c normal/misc.c \ - normal/handler.c normal/auth.c lib/crypto.c normal/autofs.c \ - normal/completion.c normal/main.c normal/color.c \ - normal/menu.c normal/menu_entry.c \ - normal/menu_text.c normal/crypto.c normal/term.c \ - commands/terminal.c normal/context.c lib/charset.c \ - script/main.c script/execute.c script/function.c \ - script/lexer.c script/script.c grub_script.tab.c \ - partmap/amiga.c partmap/apple.c partmap/msdos.c partmap/sun.c \ - partmap/acorn.c partmap/gpt.c \ \ - fs/affs.c fs/cpio.c fs/fat.c fs/ext2.c fs/hfs.c \ - fs/hfsplus.c fs/iso9660.c fs/udf.c fs/jfs.c fs/minix.c \ - fs/ntfs.c fs/ntfscomp.c fs/reiserfs.c fs/sfs.c \ - fs/ufs.c fs/ufs2.c fs/xfs.c fs/afs.c fs/afs_be.c \ - fs/befs.c fs/befs_be.c fs/tar.c \ + util/console.c util/grub-emu.c util/misc.c \ + util/hostdisk.c util/getroot.c util/mm.c util/time.c \ \ - util/console.c util/hostfs.c util/grub-emu.c util/misc.c \ - util/hostdisk.c util/getroot.c \ - \ - disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ - disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ - commands/parttool.c parttool/msdospart.c \ - lib/libgcrypt-grub/cipher/md5.c \ - grub_emu_init.c gnulib/progname.c -grub_emu_CFLAGS += -Wno-missing-field-initializers -Wno-error -I$(srcdir)/lib/libgcrypt_wrap + gnulib/progname.c util/hostfs.c disk/host.c +kernel_img_HEADERS += datetime.h util/misc.h +kernel_img_CFLAGS = $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) -Wno-undef -I$(srcdir)/gnulib +kernel_img_LDFLAGS = $(COMMON_LDFLAGS) +TARGET_NO_STRIP = yes +ifneq ($(TARGET_NO_MODULES), yes) +kernel_img_SOURCES += symlist.c kern/$(target_cpu)/dl.c +ifneq ($(target_cpu), i386) +ifneq ($(target_cpu), x86_64) +kernel_img_SOURCES += kern/$(target_cpu)/cache.S +endif +endif +else +kernel_img_SOURCES += grub_emu_init.c +endif + +# For halt.mod. +pkglib_MODULES += halt.mod +halt_mod_SOURCES = commands/halt.c +halt_mod_CFLAGS = $(COMMON_CFLAGS) +halt_mod_LDFLAGS = $(COMMON_LDFLAGS) ifeq ($(target_cpu), i386) -grub_emu_SOURCES += commands/i386/cpuid.c +pkglib_MODULES += cpuid.mod +cpuid_mod_SOURCES = commands/i386/cpuid.c +cpuid_mod_CFLAGS = $(COMMON_CFLAGS) +cpuid_mod_LDFLAGS = $(COMMON_LDFLAGS) endif grub_emu_LDFLAGS = $(LIBCURSES) ifeq ($(enable_grub_emu_usb), yes) -grub_emu_SOURCES += disk/usbms.c util/usb.c bus/usb/usb.c \ - commands/usbtest.c -grub_emu_LDFLAGS += $(LIBCURSES) $(LIBUSB) +kernel_img_HEADERS += libusb.h + +pkglib_MODULES += libusb.mod +libusb_mod_SOURCES = util/usb.c +libusb_mod_CFLAGS = +libusb_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For usb.mod +pkglib_MODULES += usb.mod +usb_mod_SOURCES = bus/usb/usb.c +usb_mod_CFLAGS = $(COMMON_CFLAGS) +usb_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For usbtest.mod +pkglib_MODULES += usbtest.mod +usbtest_mod_SOURCES = commands/usbtest.c +usbtest_mod_CFLAGS = $(COMMON_CFLAGS) +usbtest_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For usbms.mod +pkglib_MODULES += usbms.mod +usbms_mod_SOURCES = disk/usbms.c +usbms_mod_CFLAGS = $(COMMON_CFLAGS) +usbms_mod_LDFLAGS = $(COMMON_LDFLAGS) + +grub_emu_LDFLAGS += $(LIBUSB) +endif + +ifeq ($(enable_grub_emu_sdl), yes) +pkglib_MODULES += sdl.mod +sdl_mod_SOURCES = util/sdl.c +sdl_mod_CFLAGS = +sdl_mod_LDFLAGS = $(COMMON_LDFLAGS) +grub_emu_LDFLAGS += $(LIBSDL) +kernel_img_HEADERS += sdl.h endif ifeq ($(enable_grub_emu_pci), yes) -grub_emu_SOURCES += util/pci.c commands/lspci.c +pkglib_MODULES += pci.mod +pci_mod_SOURCES = util/pci.c commands/lspci.c +pci_mod_LDFLAGS = $(COMMON_LDFLAGS) grub_emu_LDFLAGS += $(LIBPCIACCESS) +kernel_img_HEADERS += libpciaccess.h endif -grub_emu_init.lst: geninit.sh $(filter-out grub_emu_init.c,$(grub_emu_SOURCES)) - rm -f $@; grep GRUB_MOD_INIT $(filter %.c,$^) /dev/null > $@ -DISTCLEANFILES += grub_emu_init.lst +include $(srcdir)/conf/common.mk -grub_emu_init.h: grub_emu_init.lst $(filter-out grub_emu_init.c,$(grub_emu_SOURCES)) geninitheader.sh - rm -f $@; sh $(srcdir)/geninitheader.sh $< > $@ +grub_emu_init.h: genemuinitheader.sh $(pkglib_MODULES) + rm -f $@; echo $(pkglib_MODULES) | sh $(srcdir)/genemuinitheader.sh $(NM) > $@ DISTCLEANFILES += grub_emu_init.h -grub_emu_init.c: grub_emu_init.lst $(filter-out grub_emu_init.c,$(grub_emu_SOURCES)) geninit.sh grub_emu_init.h - rm -f $@; sh $(srcdir)/geninit.sh $< $(filter %.c,$^) > $@ +grub_emu_init.c: genemuinit.sh $(pkglib_MODULES) grub_emu_init.h + rm -f $@; echo $(pkglib_MODULES) | sh $(srcdir)/genemuinit.sh $(NM) > $@ DISTCLEANFILES += grub_emu_init.c - - - -# FIXME: this could be shared with common.rmk - -# For grub-mkfont. -ifeq ($(enable_grub_mkfont), yes) -bin_UTILITIES += grub-mkfont -grub_mkfont_SOURCES = gnulib/progname.c util/grub-mkfont.c util/misc.c -grub_mkfont_CFLAGS = $(freetype_cflags) -grub_mkfont_LDFLAGS = $(freetype_libs) +CLEANFILES += grub-emu +ifneq ($(TARGET_NO_MODULES), yes) +grub-emu: $(pkglib_PROGRAMS) + $(CC) -o $@ $(pkglib_PROGRAMS) $(grub_emu_LDFLAGS) $(LDFLAGS) +else +grub-emu: $(pkglib_MODULES) $(pkglib_PROGRAMS) + $(CC) -o $@ $(pkglib_MODULES) $(pkglib_PROGRAMS) $(grub_emu_LDFLAGS) $(LDFLAGS) endif +GRUB_EMU=grub-emu -grub_script.tab.c grub_script.tab.h: script/parser.y - $(YACC) -d -p grub_script_yy -b grub_script $(srcdir)/script/parser.y -DISTCLEANFILES += grub_script.tab.c grub_script.tab.h - -bin_UTILITIES += grub-bin2h -grub_bin2h_SOURCES = gnulib/progname.c util/bin2h.c diff --git a/conf/common.rmk b/conf/common.rmk index 538ba9832..683aa2e66 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -1,5 +1,8 @@ # -*- makefile -*- +# Used by various components. These rules need to precede them. +script/lexer.c_DEPENDENCIES = grub_script.tab.h + sbin_UTILITIES += grub-mkdevicemap grub_mkdevicemap_SOURCES = gnulib/progname.c util/grub-mkdevicemap.c \ util/deviceiter.c \ @@ -11,28 +14,28 @@ else grub_mkdevicemap_SOURCES += util/devicemap.c endif -# For grub-mkelfimage. -bin_UTILITIES += grub-mkelfimage -grub_mkelfimage_SOURCES = gnulib/progname.c \ - util/elf/grub-mkimage.c util/misc.c \ - util/resolve.c -util/elf/grub-mkimage.c_DEPENDENCIES = Makefile +# For grub-mkimage. +bin_UTILITIES += grub-mkimage +grub_mkimage_SOURCES = gnulib/progname.c util/grub-mkimage.c util/misc.c \ + util/resolve.c lib/LzmaEnc.c lib/LzFind.c +util/grub-mkimage.c_DEPENDENCIES = Makefile # For grub-probe. sbin_UTILITIES += grub-probe util/grub-probe.c_DEPENDENCIES = grub_probe_init.h grub_probe_SOURCES = gnulib/progname.c util/grub-probe.c \ - util/hostdisk.c util/misc.c util/getroot.c \ + util/hostdisk.c util/misc.c util/getroot.c util/mm.c \ kern/device.c kern/disk.c kern/err.c kern/misc.c \ - kern/parser.c kern/partition.c kern/file.c \ + kern/parser.c kern/partition.c kern/file.c kern/list.c \ \ fs/affs.c fs/cpio.c fs/fat.c fs/ext2.c fs/hfs.c \ fs/hfsplus.c fs/iso9660.c fs/udf.c fs/jfs.c fs/minix.c \ - fs/ntfs.c fs/ntfscomp.c fs/reiserfs.c fs/sfs.c \ - fs/ufs.c fs/ufs2.c fs/xfs.c fs/afs.c fs/afs_be.c \ - fs/befs.c fs/befs_be.c fs/tar.c \ + fs/nilfs2.c fs/ntfs.c fs/ntfscomp.c fs/reiserfs.c \ + fs/sfs.c fs/ufs.c fs/ufs2.c fs/xfs.c fs/afs.c \ + fs/afs_be.c fs/befs.c fs/befs_be.c fs/tar.c \ \ - partmap/msdos.c partmap/apple.c partmap/sun.c partmap/gpt.c\ + partmap/msdos.c partmap/bsdlabel.c partmap/apple.c \ + partmap/sun.c partmap/sunpc.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c \ disk/raid.c disk/mdraid_linux.c disk/lvm.c grub_probe_init.c @@ -52,12 +55,12 @@ grub_mkisofs_SOURCES = util/mkisofs/eltorito.c \ gnulib/error.c gnulib/progname.c grub_mkisofs_CFLAGS = -D_FILE_OFFSET_BITS=64 \ -I$(srcdir)/util/mkisofs/include \ - -Wno-all -Werror + -Wno-all -Werror $(GNULIB_UTIL_CFLAGS) # For grub-fstest. util/grub-fstest.c_DEPENDENCIES = grub_fstest_init.h grub_fstest_SOURCES = gnulib/progname.c util/grub-fstest.c util/hostfs.c \ - util/misc.c \ + util/misc.c util/mm.c \ kern/file.c kern/device.c kern/disk.c kern/err.c kern/misc.c \ disk/host.c disk/loopback.c kern/list.c kern/command.c \ lib/arg.c commands/extcmd.c normal/datetime.c normal/misc.c \ @@ -65,12 +68,12 @@ grub_fstest_SOURCES = gnulib/progname.c util/grub-fstest.c util/hostfs.c \ \ fs/affs.c fs/cpio.c fs/fat.c fs/ext2.c fs/hfs.c \ fs/hfsplus.c fs/iso9660.c fs/udf.c fs/jfs.c fs/minix.c \ - fs/ntfs.c fs/ntfscomp.c fs/reiserfs.c fs/sfs.c \ + fs/nilfs2.c fs/ntfs.c fs/ntfscomp.c fs/reiserfs.c fs/sfs.c \ fs/ufs.c fs/ufs2.c fs/xfs.c fs/afs.c fs/afs_be.c fs/befs.c \ fs/befs_be.c fs/tar.c \ \ - kern/partition.c partmap/msdos.c partmap/apple.c partmap/sun.c \ - partmap/gpt.c \ + kern/partition.c partmap/msdos.c partmap/bsdlabel.c \ + partmap/apple.c partmap/sun.c partmap/sunpc.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c disk/raid.c \ disk/raid5_recover.c disk/raid6_recover.c \ disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ @@ -91,13 +94,38 @@ grub_mkrelpath_SOURCES = gnulib/progname.c util/grub-mkrelpath.c util/misc.c bin_UTILITIES += grub-bin2h grub_bin2h_SOURCES = gnulib/progname.c util/bin2h.c +# For the lexer. +grub_script.yy.c grub_script.yy.h: script/yylex.l + $(LEX) -o grub_script.yy.c --header-file=grub_script.yy.h $(srcdir)/script/yylex.l +DISTCLEANFILES += grub_script.yy.c grub_script.yy.h + # For grub-script-check. bin_UTILITIES += grub-script-check util/grub-script-check.c_DEPENDENCIES = grub_script_check_init.h -grub_script_check_SOURCES = gnulib/progname.c util/grub-script-check.c util/misc.c \ +grub_script_check_SOURCES = gnulib/progname.c gnulib/getdelim.c gnulib/getline.c \ + util/grub-script-check.c util/misc.c util/mm.c \ script/main.c script/script.c script/function.c script/lexer.c \ kern/handler.c kern/err.c kern/parser.c kern/list.c \ - kern/misc.c kern/env.c grub_script_check_init.c grub_script.tab.c + kern/misc.c kern/env.c grub_script_check_init.c grub_script.tab.c \ + grub_script.yy.c +grub_script_check_CFLAGS = $(GNULIB_UTIL_CFLAGS) +MOSTLYCLEANFILES += symlist.c kernel_syms.lst +DEFSYMFILES += kernel_syms.lst + +kernel_img_HEADERS += boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ + env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ + partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ + list.h handler.h command.h i18n.h env_private.h libgcc.h + +ifneq ($(platform), emu) +kernel_img_HEADERS += machine/memory.h machine/loader.h +endif + +symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh + /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) + +kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh + /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) # For the parser. grub_script.tab.c grub_script.tab.h: script/parser.y @@ -158,7 +186,7 @@ DISTCLEANFILES += grub_fstest_init.c # for grub-editenv bin_UTILITIES += grub-editenv -grub_editenv_SOURCES = gnulib/progname.c util/grub-editenv.c lib/envblk.c util/misc.c kern/misc.c kern/err.c +grub_editenv_SOURCES = gnulib/progname.c util/grub-editenv.c lib/envblk.c util/misc.c util/mm.c kern/misc.c kern/err.c CLEANFILES += grub-editenv # Needed for genmk.rb to work @@ -269,6 +297,12 @@ minix_mod_SOURCES = fs/minix.c minix_mod_CFLAGS = $(COMMON_CFLAGS) minix_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For nilfs2.mod. +pkglib_MODULES += nilfs2.mod +nilfs2_mod_SOURCES = fs/nilfs2.c +nilfs2_mod_CFLAGS = $(COMMON_CFLAGS) +nilfs2_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For hfs.mod. hfs_mod_SOURCES = fs/hfs.c hfs_mod_CFLAGS = $(COMMON_CFLAGS) @@ -376,6 +410,16 @@ part_gpt_mod_SOURCES = partmap/gpt.c part_gpt_mod_CFLAGS = $(COMMON_CFLAGS) part_gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) +pkglib_MODULES += part_bsd.mod +part_bsd_mod_SOURCES = partmap/bsdlabel.c +part_bsd_mod_CFLAGS = $(COMMON_CFLAGS) +part_bsd_mod_LDFLAGS = $(COMMON_LDFLAGS) + +pkglib_MODULES += part_sunpc.mod +part_sunpc_mod_SOURCES = partmap/sunpc.c +part_sunpc_mod_CFLAGS = $(COMMON_CFLAGS) +part_sunpc_mod_LDFLAGS = $(COMMON_LDFLAGS) + # Special disk structures and generic drivers pkglib_MODULES += raid.mod raid5rec.mod raid6rec.mod mdraid.mod dm_nv.mod \ @@ -451,6 +495,28 @@ hello_mod_SOURCES = hello/hello.c hello_mod_CFLAGS = $(COMMON_CFLAGS) hello_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For gfxmenu.mod. +pkglib_MODULES += gfxmenu.mod +gfxmenu_mod_SOURCES = \ + gfxmenu/gfxmenu.c \ + gfxmenu/model.c \ + gfxmenu/view.c \ + gfxmenu/icon_manager.c \ + gfxmenu/theme_loader.c \ + gfxmenu/widget-box.c \ + gfxmenu/gui_canvas.c \ + gfxmenu/gui_circular_progress.c \ + gfxmenu/gui_box.c \ + gfxmenu/gui_label.c \ + gfxmenu/gui_list.c \ + gfxmenu/gui_image.c \ + gfxmenu/gui_progress_bar.c \ + gfxmenu/gui_util.c \ + gfxmenu/gui_string_util.c \ + gfxmenu/named_colors.c +gfxmenu_mod_CFLAGS = $(COMMON_CFLAGS) +gfxmenu_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For parttool.mod. parttool_mod_SOURCES = commands/parttool.c parttool_mod_CFLAGS = $(COMMON_CFLAGS) @@ -594,64 +660,82 @@ normal_mod_LDFLAGS = $(COMMON_LDFLAGS) # For sh.mod. sh_mod_SOURCES = script/main.c script/script.c script/execute.c \ - script/function.c script/lexer.c grub_script.tab.c -sh_mod_CFLAGS = $(COMMON_CFLAGS) + script/function.c script/lexer.c grub_script.tab.c grub_script.yy.c +sh_mod_CFLAGS = $(COMMON_CFLAGS) $(POSIX_CFLAGS) -Wno-error sh_mod_LDFLAGS = $(COMMON_LDFLAGS) +ifneq (, $(FONT_SOURCE)) +font/font.c_DEPENDENCIES = ascii.h +endif + # Common Video Subsystem specific modules. -pkglib_MODULES += video.mod videotest.mod bitmap.mod tga.mod jpeg.mod \ - png.mod gfxterm.mod video_fb.mod +# On Yeeloong it's part of kernel +ifneq ($(platform), yeeloong) # For video.mod. +pkglib_MODULES += video.mod video_mod_SOURCES = video/video.c video_mod_CFLAGS = $(COMMON_CFLAGS) video_mod_LDFLAGS = $(COMMON_LDFLAGS) +pkglib_MODULES += video_fb.mod video_fb_mod_SOURCES = video/fb/video_fb.c video/fb/fbblit.c \ video/fb/fbfill.c video/fb/fbutil.c video_fb_mod_CFLAGS = $(COMMON_CFLAGS) video_fb_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For videotest.mod. -videotest_mod_SOURCES = commands/videotest.c -videotest_mod_CFLAGS = $(COMMON_CFLAGS) -videotest_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For bitmap.mod +pkglib_MODULES += bitmap.mod bitmap_mod_SOURCES = video/bitmap.c bitmap_mod_CFLAGS = $(COMMON_CFLAGS) bitmap_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For tga.mod -tga_mod_SOURCES = video/readers/tga.c -tga_mod_CFLAGS = $(COMMON_CFLAGS) -tga_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For jpeg.mod. -jpeg_mod_SOURCES = video/readers/jpeg.c -jpeg_mod_CFLAGS = $(COMMON_CFLAGS) -jpeg_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For png.mod. -png_mod_SOURCES = video/readers/png.c -png_mod_CFLAGS = $(COMMON_CFLAGS) -png_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For bitmap_scale.mod +pkglib_MODULES += bitmap_scale.mod +bitmap_scale_mod_SOURCES = video/bitmap_scale.c +bitmap_scale_mod_CFLAGS = $(COMMON_CFLAGS) +bitmap_scale_mod_LDFLAGS = $(COMMON_LDFLAGS) pkglib_MODULES += font.mod -ifneq (, $(FONT_SOURCE)) -font/font.c_DEPENDENCIES = ascii.h -endif font_mod_SOURCES = font/font_cmd.c font/font.c font_mod_CFLAGS = $(COMMON_CFLAGS) font_mod_LDFLAGS = $(COMMON_LDFLAGS) # For gfxterm.mod. +pkglib_MODULES += gfxterm.mod gfxterm_mod_SOURCES = term/gfxterm.c gfxterm_mod_CFLAGS = $(COMMON_CFLAGS) gfxterm_mod_LDFLAGS = $(COMMON_LDFLAGS) +endif + +# For videotest.mod. +pkglib_MODULES += videotest.mod +videotest_mod_SOURCES = commands/videotest.c +videotest_mod_CFLAGS = $(COMMON_CFLAGS) +videotest_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For tga.mod +pkglib_MODULES += tga.mod +tga_mod_SOURCES = video/readers/tga.c +tga_mod_CFLAGS = $(COMMON_CFLAGS) +tga_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For jpeg.mod. +pkglib_MODULES += jpeg.mod +jpeg_mod_SOURCES = video/readers/jpeg.c +jpeg_mod_CFLAGS = $(COMMON_CFLAGS) +jpeg_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For png.mod. +pkglib_MODULES += png.mod +png_mod_SOURCES = video/readers/png.c +png_mod_CFLAGS = $(COMMON_CFLAGS) +png_mod_LDFLAGS = $(COMMON_LDFLAGS) + + # Misc. -pkglib_MODULES += gzio.mod bufio.mod elf.mod +pkglib_MODULES += gzio.mod elf.mod # For elf.mod. elf_mod_SOURCES = kern/elf.c @@ -663,10 +747,14 @@ gzio_mod_SOURCES = io/gzio.c gzio_mod_CFLAGS = $(COMMON_CFLAGS) gzio_mod_LDFLAGS = $(COMMON_LDFLAGS) +# On Yeeloong it's part of kernel +ifneq ($(platform), yeeloong) # For bufio.mod. +pkglib_MODULES += bufio.mod bufio_mod_SOURCES = io/bufio.c bufio_mod_CFLAGS = $(COMMON_CFLAGS) bufio_mod_LDFLAGS = $(COMMON_LDFLAGS) +endif # For gettext.mod. pkglib_MODULES += gettext.mod @@ -682,6 +770,18 @@ xnu_uuid_mod_SOURCES = commands/xnu_uuid.c xnu_uuid_mod_CFLAGS = $(COMMON_CFLAGS) xnu_uuid_mod_LDFLAGS = $(COMMON_LDFLAGS) +pkglib_MODULES += trig.mod +trig_mod_SOURCES = trigtables.c +trig_mod_CFLAGS = $(COMMON_CFLAGS) +trig_mod_LDFLAGS = $(COMMON_LDFLAGS) + +trigtables.c: gentrigtables + ./gentrigtables > $@ +DISTCLEANFILES += trigtables.c +gentrigtables: gentrigtables.c + $(CC) -o $@ $^ $(CPPFLAGS) -lm +DISTCLEANFILES += gentrigtables + pkglib_MODULES += setjmp.mod setjmp_mod_SOURCES = lib/$(target_cpu)/setjmp.S setjmp_mod_ASFLAGS = $(COMMON_ASFLAGS) @@ -692,6 +792,11 @@ charset_mod_SOURCES = lib/charset.c charset_mod_CFLAGS = $(COMMON_CFLAGS) charset_mod_LDFLAGS = $(COMMON_LDFLAGS) +pkglib_MODULES += regexp.mod +regexp_mod_SOURCES = gnulib/regex.c commands/regexp.c +regexp_mod_CFLAGS = $(COMMON_CFLAGS) $(GNULIB_CFLAGS) +regexp_mod_LDFLAGS = $(COMMON_LDFLAGS) + pkglib_MODULES += terminal.mod terminal_mod_SOURCES = commands/terminal.c terminal_mod_CFLAGS = $(COMMON_CFLAGS) @@ -718,8 +823,44 @@ password_pbkdf2_mod_SOURCES = commands/password_pbkdf2.c password_pbkdf2_mod_CFLAGS = $(COMMON_CFLAGS) password_pbkdf2_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For memdisk.mod. +pkglib_MODULES += memdisk.mod +memdisk_mod_SOURCES = disk/memdisk.c +memdisk_mod_CFLAGS = $(COMMON_CFLAGS) +memdisk_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For reboot.mod. +pkglib_MODULES += reboot.mod +reboot_mod_SOURCES = commands/reboot.c +reboot_mod_CFLAGS = $(COMMON_CFLAGS) +reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For date.mod +pkglib_MODULES += date.mod +date_mod_SOURCES = commands/date.c +date_mod_CFLAGS = $(COMMON_CFLAGS) +date_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For datehook.mod +pkglib_MODULES += datehook.mod +datehook_mod_SOURCES = hook/datehook.c +datehook_mod_CFLAGS = $(COMMON_CFLAGS) +datehook_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For lsmmap.mod +pkglib_MODULES += lsmmap.mod +lsmmap_mod_SOURCES = commands/lsmmap.c +lsmmap_mod_CFLAGS = $(COMMON_CFLAGS) +lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For boot.mod. +pkglib_MODULES += boot.mod +boot_mod_SOURCES = commands/boot.c lib/i386/pc/biosnum.c +boot_mod_CFLAGS = $(COMMON_CFLAGS) +boot_mod_LDFLAGS = $(COMMON_LDFLAGS) + bin_UTILITIES += grub-mkpasswd-pbkdf2 -grub_mkpasswd_pbkdf2_SOURCES = gnulib/progname.c util/grub-mkpasswd-pbkdf2.c lib/crypto.c lib/libgcrypt-grub/cipher/sha512.c lib/pbkdf2.c util/misc.c kern/err.c +grub_mkpasswd_pbkdf2_SOURCES = gnulib/progname.c gnulib/getdelim.c gnulib/getline.c util/grub-mkpasswd-pbkdf2.c lib/crypto.c lib/libgcrypt-grub/cipher/sha512.c lib/pbkdf2.c util/misc.c util/mm.c kern/err.c grub_mkpasswd_pbkdf2_CFLAGS += -Wno-missing-field-initializers -Wno-error -I$(srcdir)/lib/libgcrypt_wrap -DGRUB_MKPASSWD=1 # Randomly generated diff --git a/conf/i386-coreboot.rmk b/conf/i386-coreboot.rmk index 9563c0b2b..11c3abd2a 100644 --- a/conf/i386-coreboot.rmk +++ b/conf/i386-coreboot.rmk @@ -1,18 +1,9 @@ # -*- makefile -*- -COMMON_ASFLAGS = -nostdinc -fno-builtin -m32 -COMMON_CFLAGS = -fno-builtin -mrtd -mregparm=3 -m32 -COMMON_LDFLAGS = -m32 -nostdlib - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h +COMMON_CFLAGS = -mrtd -mregparm=3 # Images. -GRUB_KERNEL_MACHINE_LINK_ADDR = 0x8200 - -ifeq ($(platform), coreboot) - pkglib_PROGRAMS += kernel.img kernel_img_SOURCES = kern/i386/coreboot/startup.S \ kern/i386/misc.S \ @@ -31,73 +22,9 @@ kernel_img_SOURCES = kern/i386/coreboot/startup.S \ kern/env.c \ term/i386/pc/vga_text.c term/i386/vga_common.c \ symlist.c -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ - partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ - machine/boot.h machine/console.h machine/init.h \ - machine/memory.h machine/loader.h list.h handler.h command.h i18n.h \ - env_private.h kernel_img_CFLAGS = $(COMMON_CFLAGS) kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) -Wl,-N,-S,-Ttext,$(GRUB_KERNEL_MACHINE_LINK_ADDR),-Bstatic - -endif - -ifeq ($(platform), qemu) - -GRUB_BOOT_MACHINE_LINK_ADDR = 0xffe00 - -pkglib_IMAGES += boot.img -boot_img_SOURCES = boot/i386/qemu/boot.S -boot_img_ASFLAGS = $(COMMON_ASFLAGS) -DGRUB_BOOT_MACHINE_LINK_ADDR=$(GRUB_BOOT_MACHINE_LINK_ADDR) -boot_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)$(GRUB_BOOT_MACHINE_LINK_ADDR) -boot_img_FORMAT = binary - -bin_UTILITIES += grub-mkimage -grub_mkimage_SOURCES = util/grub-mkrawimage.c util/misc.c \ - util/resolve.c gnulib/progname.c -grub_mkimage_CFLAGS = -DGRUB_KERNEL_MACHINE_LINK_ADDR=$(GRUB_KERNEL_MACHINE_LINK_ADDR) -util/grub-mkrawimage.c_DEPENDENCIES = Makefile - - -pkglib_IMAGES += kernel.img -kernel_img_SOURCES = kern/i386/qemu/startup.S \ - kern/i386/misc.S \ - kern/i386/coreboot/init.c \ - kern/i386/qemu/mmap.c \ - kern/i386/halt.c \ - kern/main.c kern/device.c \ - kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \ - kern/misc.c kern/mm.c kern/term.c \ - kern/rescue_parser.c kern/rescue_reader.c \ - kern/time.c kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ - kern/$(target_cpu)/dl.c kern/parser.c kern/partition.c \ - kern/i386/tsc.c kern/i386/pit.c \ - kern/generic/rtc_get_time_ms.c \ - kern/generic/millisleep.c \ - kern/env.c \ - term/i386/pc/vga_text.c term/i386/vga_common.c \ - symlist.c -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ - partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ - machine/boot.h machine/console.h machine/init.h \ - machine/memory.h machine/loader.h list.h handler.h command.h i18n.h \ - env_private.h -kernel_img_CFLAGS = $(COMMON_CFLAGS) -DGRUB_BOOT_MACHINE_LINK_ADDR=$(GRUB_BOOT_MACHINE_LINK_ADDR) -kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -DGRUB_KERNEL_MACHINE_LINK_ADDR=$(GRUB_KERNEL_MACHINE_LINK_ADDR) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)$(GRUB_KERNEL_MACHINE_LINK_ADDR) -kernel_img_FORMAT = binary -endif - -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) +kernel_img_LDFLAGS += $(COMMON_LDFLAGS) -Wl,-N,-S,-Ttext,0x8200,-Bstatic sbin_SCRIPTS += grub-install grub_install_SOURCES = util/grub-install.in @@ -106,17 +33,7 @@ bin_SCRIPTS += grub-mkrescue grub_mkrescue_SOURCES = util/grub-mkrescue.in # Modules. -pkglib_MODULES = linux.mod \ - aout.mod play.mod serial.mod \ - memdisk.mod pci.mod lspci.mod reboot.mod \ - halt.mod datetime.mod date.mod datehook.mod \ - lsmmap.mod mmap.mod - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) +pkglib_MODULES = linux.mod aout.mod halt.mod datetime.mod mmap.mod # For mmap.mod. mmap_mod_SOURCES = mmap/mmap.c mmap/i386/uppermem.c mmap/i386/mmap.c @@ -129,21 +46,11 @@ linux_mod_SOURCES = loader/i386/linux.c linux_mod_CFLAGS = $(COMMON_CFLAGS) linux_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For reboot.mod. -reboot_mod_SOURCES = commands/reboot.c -reboot_mod_CFLAGS = $(COMMON_CFLAGS) -reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For halt.mod. halt_mod_SOURCES = commands/halt.c halt_mod_CFLAGS = $(COMMON_CFLAGS) halt_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For serial.mod. -serial_mod_SOURCES = term/serial.c -serial_mod_CFLAGS = $(COMMON_CFLAGS) -serial_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For aout.mod. aout_mod_SOURCES = loader/aout.c aout_mod_CFLAGS = $(COMMON_CFLAGS) @@ -156,45 +63,10 @@ bsd_mod_CFLAGS = $(COMMON_CFLAGS) bsd_mod_LDFLAGS = $(COMMON_LDFLAGS) bsd_mod_ASFLAGS = $(COMMON_ASFLAGS) -# For play.mod. -play_mod_SOURCES = commands/i386/pc/play.c -play_mod_CFLAGS = $(COMMON_CFLAGS) -play_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For memdisk.mod. -memdisk_mod_SOURCES = disk/memdisk.c -memdisk_mod_CFLAGS = $(COMMON_CFLAGS) -memdisk_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For pci.mod -pci_mod_SOURCES = bus/pci.c -pci_mod_CFLAGS = $(COMMON_CFLAGS) -pci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lspci.mod -lspci_mod_SOURCES = commands/lspci.c -lspci_mod_CFLAGS = $(COMMON_CFLAGS) -lspci_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For datetime.mod datetime_mod_SOURCES = lib/cmos_datetime.c datetime_mod_CFLAGS = $(COMMON_CFLAGS) datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For date.mod -date_mod_SOURCES = commands/date.c -date_mod_CFLAGS = $(COMMON_CFLAGS) -date_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datehook.mod -datehook_mod_SOURCES = hook/datehook.c -datehook_mod_CFLAGS = $(COMMON_CFLAGS) -datehook_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lsmmap.mod -lsmmap_mod_SOURCES = commands/lsmmap.c -lsmmap_mod_CFLAGS = $(COMMON_CFLAGS) -lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - include $(srcdir)/conf/i386.mk include $(srcdir)/conf/common.mk diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk index c03abb429..e826cb333 100644 --- a/conf/i386-efi.rmk +++ b/conf/i386-efi.rmk @@ -1,166 +1,5 @@ # -*- makefile -*- -COMMON_ASFLAGS = -nostdinc -fno-builtin -m32 -COMMON_CFLAGS = -fno-builtin -m32 -COMMON_LDFLAGS = -melf_i386 -nostdlib +COMMON_LDFLAGS = -melf_i386 -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h - -# Utilities. -bin_UTILITIES = grub-mkimage - -# For grub-mkimage. -grub_mkimage_SOURCES = gnulib/progname.c util/i386/efi/grub-mkimage.c \ - util/misc.c util/resolve.c -util/i386/efi/grub-mkimage.c_DEPENDENCIES = Makefile - -# For grub-setup. -#grub_setup_SOURCES = util/i386/pc/grub-setup.c util/hostdisk.c \ -# util/misc.c util/getroot.c kern/device.c kern/disk.c \ -# kern/err.c kern/misc.c fs/fat.c fs/ext2.c fs/xfs.c fs/affs.c \ -# fs/sfs.c kern/parser.c kern/partition.c partmap/msdos.c \ -# fs/ufs.c fs/ufs2.c fs/minix.c fs/hfs.c fs/jfs.c fs/hfsplus.c kern/file.c \ -# kern/fs.c kern/env.c fs/fshelp.c - -# Scripts. -sbin_SCRIPTS = grub-install - -# For grub-install. -grub_install_SOURCES = util/i386/efi/grub-install.in - -# Modules. -pkglib_PROGRAMS = kernel.img -pkglib_MODULES = chain.mod appleldr.mod \ - linux.mod halt.mod reboot.mod pci.mod lspci.mod \ - datetime.mod date.mod datehook.mod loadbios.mod \ - fixvideo.mod mmap.mod acpi.mod - -# For kernel.img. -kernel_img_RELOCATABLE = yes -kernel_img_SOURCES = kern/i386/efi/startup.S kern/main.c kern/device.c \ - kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \ - kern/misc.c kern/mm.c kern/term.c \ - kern/rescue_parser.c kern/rescue_reader.c \ - kern/$(target_cpu)/dl.c kern/i386/efi/init.c kern/parser.c kern/partition.c \ - kern/env.c symlist.c kern/efi/efi.c kern/efi/init.c kern/efi/mm.c \ - term/efi/console.c disk/efi/efidisk.c \ - kern/time.c kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ - kern/i386/tsc.c kern/i386/pit.c \ - kern/generic/rtc_get_time_ms.c \ - kern/generic/millisleep.c -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ - partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ - efi/efi.h efi/time.h efi/disk.h i386/pit.h list.h handler.h command.h \ - i18n.h env_private.h -kernel_img_CFLAGS = $(COMMON_CFLAGS) -kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) - -MOSTLYCLEANFILES += symlist.c -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For acpi.mod. -acpi_mod_SOURCES = commands/acpi.c commands/efi/acpi.c -acpi_mod_CFLAGS = $(COMMON_CFLAGS) -acpi_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For mmap.mod. -mmap_mod_SOURCES = mmap/mmap.c mmap/i386/uppermem.c mmap/i386/mmap.c \ - mmap/efi/mmap.c -mmap_mod_CFLAGS = $(COMMON_CFLAGS) -mmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For chain.mod. -chain_mod_SOURCES = loader/efi/chainloader.c -chain_mod_CFLAGS = $(COMMON_CFLAGS) -chain_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For appleldr.mod. -appleldr_mod_SOURCES = loader/efi/appleloader.c -appleldr_mod_CFLAGS = $(COMMON_CFLAGS) -appleldr_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For linux.mod. -linux_mod_SOURCES = loader/i386/efi/linux.c -linux_mod_CFLAGS = $(COMMON_CFLAGS) -linux_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For halt.mod. -halt_mod_SOURCES = commands/halt.c -halt_mod_CFLAGS = $(COMMON_CFLAGS) -halt_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For reboot.mod. -reboot_mod_SOURCES = commands/reboot.c -reboot_mod_CFLAGS = $(COMMON_CFLAGS) -reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For pci.mod -pci_mod_SOURCES = bus/pci.c -pci_mod_CFLAGS = $(COMMON_CFLAGS) -pci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lspci.mod -lspci_mod_SOURCES = commands/lspci.c -lspci_mod_CFLAGS = $(COMMON_CFLAGS) -lspci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datetime.mod -datetime_mod_SOURCES = lib/efi/datetime.c -datetime_mod_CFLAGS = $(COMMON_CFLAGS) -datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For date.mod -date_mod_SOURCES = commands/date.c -date_mod_CFLAGS = $(COMMON_CFLAGS) -date_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datehook.mod -datehook_mod_SOURCES = hook/datehook.c -datehook_mod_CFLAGS = $(COMMON_CFLAGS) -datehook_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For loadbios.mod -loadbios_mod_SOURCES = commands/efi/loadbios.c -loadbios_mod_CFLAGS = $(COMMON_CFLAGS) -loadbios_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For fixvideo.mod -fixvideo_mod_SOURCES = commands/efi/fixvideo.c -fixvideo_mod_CFLAGS = $(COMMON_CFLAGS) -fixvideo_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += efi_uga.mod -efi_uga_mod_SOURCES = video/efi_uga.c -efi_uga_mod_CFLAGS = $(COMMON_CFLAGS) -efi_uga_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += efi_gop.mod -efi_gop_mod_SOURCES = video/efi_gop.c -efi_gop_mod_CFLAGS = $(COMMON_CFLAGS) -efi_gop_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += xnu.mod -xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c \ - loader/macho32.c loader/macho64.c loader/macho.c loader/xnu.c -xnu_mod_CFLAGS = $(COMMON_CFLAGS) -xnu_mod_LDFLAGS = $(COMMON_LDFLAGS) -xnu_mod_ASFLAGS = $(COMMON_ASFLAGS) - -include $(srcdir)/conf/i386.mk -include $(srcdir)/conf/common.mk +include $(srcdir)/conf/x86-efi.mk diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk index e19f6e9a1..05ba38a65 100644 --- a/conf/i386-ieee1275.rmk +++ b/conf/i386-ieee1275.rmk @@ -1,11 +1,6 @@ # -*- makefile -*- -COMMON_ASFLAGS = -m32 -nostdinc -fno-builtin -COMMON_CFLAGS = -ffreestanding -mrtd -mregparm=3 -COMMON_LDFLAGS = -nostdlib - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h +COMMON_CFLAGS = -mrtd -mregparm=3 # Images. pkglib_PROGRAMS = kernel.img @@ -29,23 +24,10 @@ kernel_img_SOURCES = kern/i386/ieee1275/startup.S \ term/ieee1275/ofconsole.c \ disk/ieee1275/ofdisk.c \ symlist.c -kernel_img_HEADERS = cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ - partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ - ieee1275/ieee1275.h machine/kernel.h machine/loader.h machine/memory.h \ - list.h handler.h command.h i18n.h env_private.h +kernel_img_HEADERS += ieee1275/ieee1275.h kernel_img_CFLAGS = $(COMMON_CFLAGS) kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) -Wl,-N,-S,-Ttext,0x10000,-Bstatic - -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) +kernel_img_LDFLAGS += $(COMMON_LDFLAGS) -Wl,-N,-S,-Ttext,0x10000,-Bstatic # Scripts. sbin_SCRIPTS = grub-install @@ -54,16 +36,10 @@ sbin_SCRIPTS = grub-install grub_install_SOURCES = util/ieee1275/grub-install.in # Modules. -pkglib_MODULES = halt.mod reboot.mod suspend.mod \ - aout.mod serial.mod linux.mod \ - nand.mod memdisk.mod pci.mod lspci.mod datetime.mod \ - date.mod datehook.mod lsmmap.mod mmap.mod - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) +pkglib_MODULES = halt.mod suspend.mod \ + aout.mod linux.mod \ + nand.mod datetime.mod \ + mmap.mod # For mmap.mod. mmap_mod_SOURCES = mmap/mmap.c mmap/i386/uppermem.c mmap/i386/mmap.c @@ -81,21 +57,11 @@ suspend_mod_SOURCES = commands/ieee1275/suspend.c suspend_mod_CFLAGS = $(COMMON_CFLAGS) suspend_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For reboot.mod -reboot_mod_SOURCES = commands/reboot.c -reboot_mod_CFLAGS = $(COMMON_CFLAGS) -reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For halt.mod halt_mod_SOURCES = commands/halt.c halt_mod_CFLAGS = $(COMMON_CFLAGS) halt_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For serial.mod. -serial_mod_SOURCES = term/serial.c -serial_mod_CFLAGS = $(COMMON_CFLAGS) -serial_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For linux.mod. linux_mod_SOURCES = loader/i386/ieee1275/linux.c linux_mod_CFLAGS = $(COMMON_CFLAGS) @@ -106,40 +72,10 @@ nand_mod_SOURCES = disk/ieee1275/nand.c nand_mod_CFLAGS = $(COMMON_CFLAGS) nand_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For memdisk.mod. -memdisk_mod_SOURCES = disk/memdisk.c -memdisk_mod_CFLAGS = $(COMMON_CFLAGS) -memdisk_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For pci.mod -pci_mod_SOURCES = bus/pci.c -pci_mod_CFLAGS = $(COMMON_CFLAGS) -pci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lspci.mod -lspci_mod_SOURCES = commands/lspci.c -lspci_mod_CFLAGS = $(COMMON_CFLAGS) -lspci_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For datetime.mod datetime_mod_SOURCES = lib/cmos_datetime.c datetime_mod_CFLAGS = $(COMMON_CFLAGS) datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For date.mod -date_mod_SOURCES = commands/date.c -date_mod_CFLAGS = $(COMMON_CFLAGS) -date_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datehook.mod -datehook_mod_SOURCES = hook/datehook.c -datehook_mod_CFLAGS = $(COMMON_CFLAGS) -datehook_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lsmmap.mod -lsmmap_mod_SOURCES = commands/lsmmap.c -lsmmap_mod_CFLAGS = $(COMMON_CFLAGS) -lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - include $(srcdir)/conf/i386.mk include $(srcdir)/conf/common.mk diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 1c1ead332..801caba28 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -1,17 +1,9 @@ # -*- makefile -*- -GRUB_KERNEL_MACHINE_LINK_ADDR = 0x8200 - -COMMON_ASFLAGS = -nostdinc -fno-builtin -m32 -COMMON_CFLAGS = -fno-builtin -mrtd -mregparm=3 -m32 -COMMON_LDFLAGS = -m32 -nostdlib - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h +COMMON_CFLAGS = -mrtd -mregparm=3 # Images. -pkglib_IMAGES = boot.img cdboot.img diskboot.img kernel.img lnxboot.img \ - pxeboot.img +pkglib_IMAGES = boot.img cdboot.img diskboot.img lnxboot.img pxeboot.img # For boot.img. boot_img_SOURCES = boot/i386/pc/boot.S @@ -44,6 +36,7 @@ cdboot_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)0x7C00 cdboot_img_FORMAT = binary # For kernel.img. +pkglib_PROGRAMS = kernel.img kernel_img_SOURCES = kern/i386/pc/startup.S \ kern/i386/misc.S \ kern/main.c kern/device.c \ @@ -59,55 +52,35 @@ kernel_img_SOURCES = kern/i386/pc/startup.S \ kern/env.c \ term/i386/pc/console.c term/i386/vga_common.c \ symlist.c -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ - partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ - machine/biosdisk.h machine/boot.h machine/console.h machine/init.h \ - machine/memory.h machine/loader.h machine/vga.h machine/vbe.h \ - machine/kernel.h machine/pxe.h i386/pit.h list.h handler.h command.h \ - i18n.h env_private.h +kernel_img_HEADERS += machine/biosdisk.h machine/vga.h machine/vbe.h \ + machine/pxe.h i386/pit.h machine/kernel.h kernel_img_CFLAGS = $(COMMON_CFLAGS) $(TARGET_IMG_CFLAGS) kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)$(GRUB_KERNEL_MACHINE_LINK_ADDR) $(COMMON_CFLAGS) -kernel_img_FORMAT = binary - -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) +kernel_img_LDFLAGS += $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)0x8200 $(COMMON_CFLAGS) # Utilities. -bin_UTILITIES = grub-mkimage sbin_UTILITIES = grub-setup -# For grub-mkimage. -grub_mkimage_SOURCES = gnulib/progname.c util/grub-mkrawimage.c util/misc.c \ - util/resolve.c lib/LzmaEnc.c lib/LzFind.c -grub_mkimage_CFLAGS = -DGRUB_KERNEL_MACHINE_LINK_ADDR=$(GRUB_KERNEL_MACHINE_LINK_ADDR) -util/grub-mkrawimage.c_DEPENDENCIES = Makefile - # For grub-setup. util/i386/pc/grub-setup.c_DEPENDENCIES = grub_setup_init.h grub_setup_SOURCES = gnulib/progname.c \ util/i386/pc/grub-setup.c util/hostdisk.c \ util/misc.c util/getroot.c kern/device.c kern/disk.c \ kern/err.c kern/misc.c kern/parser.c kern/partition.c \ - kern/file.c kern/fs.c kern/env.c fs/fshelp.c \ + kern/file.c kern/fs.c kern/env.c kern/list.c \ + fs/fshelp.c \ \ - fs/affs.c fs/cpio.c fs/ext2.c fs/fat.c fs/hfs.c \ - fs/hfsplus.c fs/iso9660.c fs/udf.c fs/jfs.c fs/minix.c \ - fs/ntfs.c fs/ntfscomp.c fs/reiserfs.c fs/sfs.c \ - fs/ufs.c fs/ufs2.c fs/xfs.c fs/afs.c fs/afs_be.c \ - fs/befs.c fs/befs_be.c fs/tar.c \ + fs/affs.c fs/cpio.c fs/ext2.c fs/fat.c fs/hfs.c \ + fs/hfsplus.c fs/iso9660.c fs/udf.c fs/jfs.c fs/minix.c \ + fs/nilfs2.c fs/ntfs.c fs/ntfscomp.c fs/reiserfs.c \ + fs/sfs.c fs/ufs.c fs/ufs2.c fs/xfs.c fs/afs.c \ + fs/afs_be.c fs/befs.c fs/befs_be.c fs/tar.c \ \ - partmap/msdos.c partmap/gpt.c \ + partmap/msdos.c partmap/bsdlabel.c partmap/sunpc.c \ + partmap/gpt.c \ \ disk/raid.c disk/mdraid_linux.c disk/lvm.c \ - util/raid.c util/lvm.c \ + util/raid.c util/lvm.c util/mm.c \ grub_setup_init.c sbin_SCRIPTS += grub-install @@ -117,20 +90,14 @@ bin_SCRIPTS += grub-mkrescue grub_mkrescue_SOURCES = util/grub-mkrescue.in pkglib_MODULES = biosdisk.mod chain.mod \ - reboot.mod halt.mod \ - vbe.mod vbetest.mod vbeinfo.mod play.mod serial.mod \ - vga.mod memdisk.mod pci.mod lspci.mod \ - aout.mod bsd.mod pxe.mod pxecmd.mod datetime.mod date.mod \ - datehook.mod lsmmap.mod ata_pthru.mod hdparm.mod \ + halt.mod \ + vbe.mod vbetest.mod vbeinfo.mod \ + vga.mod \ + aout.mod bsd.mod pxe.mod pxecmd.mod datetime.mod \ + ata_pthru.mod hdparm.mod \ usb.mod uhci.mod ohci.mod usbtest.mod usbms.mod usb_keyboard.mod \ efiemu.mod mmap.mod acpi.mod drivemap.mod -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c lib/i386/pc/biosnum.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For drivemap.mod. drivemap_mod_SOURCES = commands/i386/pc/drivemap.c \ commands/i386/pc/drivemap_int13h.S @@ -187,21 +154,11 @@ xnu_mod_CFLAGS = $(COMMON_CFLAGS) xnu_mod_LDFLAGS = $(COMMON_LDFLAGS) xnu_mod_ASFLAGS = $(COMMON_ASFLAGS) -# For reboot.mod. -reboot_mod_SOURCES = commands/reboot.c -reboot_mod_CFLAGS = $(COMMON_CFLAGS) -reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For halt.mod. halt_mod_SOURCES = commands/i386/pc/halt.c halt_mod_CFLAGS = $(COMMON_CFLAGS) halt_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For serial.mod. -serial_mod_SOURCES = term/serial.c -serial_mod_CFLAGS = $(COMMON_CFLAGS) -serial_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For vbe.mod. vbe_mod_SOURCES = video/i386/pc/vbe.c vbe_mod_CFLAGS = $(COMMON_CFLAGS) @@ -217,31 +174,11 @@ vbetest_mod_SOURCES = commands/i386/pc/vbetest.c vbetest_mod_CFLAGS = $(COMMON_CFLAGS) vbetest_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For play.mod. -play_mod_SOURCES = commands/i386/pc/play.c -play_mod_CFLAGS = $(COMMON_CFLAGS) -play_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For vga.mod. vga_mod_SOURCES = term/i386/pc/vga.c vga_mod_CFLAGS = $(COMMON_CFLAGS) vga_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For memdisk.mod. -memdisk_mod_SOURCES = disk/memdisk.c -memdisk_mod_CFLAGS = $(COMMON_CFLAGS) -memdisk_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For pci.mod -pci_mod_SOURCES = bus/pci.c -pci_mod_CFLAGS = $(COMMON_CFLAGS) -pci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lspci.mod -lspci_mod_SOURCES = commands/lspci.c -lspci_mod_CFLAGS = $(COMMON_CFLAGS) -lspci_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For aout.mod aout_mod_SOURCES = loader/aout.c aout_mod_CFLAGS = $(COMMON_CFLAGS) @@ -298,21 +235,6 @@ datetime_mod_SOURCES = lib/cmos_datetime.c datetime_mod_CFLAGS = $(COMMON_CFLAGS) datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For date.mod -date_mod_SOURCES = commands/date.c -date_mod_CFLAGS = $(COMMON_CFLAGS) -date_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datehook.mod -datehook_mod_SOURCES = hook/datehook.c -datehook_mod_CFLAGS = $(COMMON_CFLAGS) -datehook_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lsmmap.mod -lsmmap_mod_SOURCES = commands/lsmmap.c -lsmmap_mod_CFLAGS = $(COMMON_CFLAGS) -lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For ata_pthru.mod. ata_pthru_mod_SOURCES = disk/ata_pthru.c ata_pthru_mod_CFLAGS = $(COMMON_CFLAGS) @@ -369,7 +291,7 @@ pkglib_DATA += efiemu32.o efiemu64.o endif -BOOTTARGET=bios-cd +BOOTTARGET=cd bootcheck-linux16-i386: linux-initramfs.i386 $(GRUB_PAYLOADS_DIR)/linux.i386 $(srcdir)/tests/boot/linux.cfg grub-shell timeout -s KILL $(BOOTCHECK_TIMEOUT) ./grub-shell --boot=$(BOOTTARGET) --qemu=qemu-system-i386 --files=/initrd=linux-initramfs.i386 --files=/linux=$(GRUB_PAYLOADS_DIR)/linux.i386 $(srcdir)/tests/boot/linux16.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null diff --git a/conf/i386-qemu.rmk b/conf/i386-qemu.rmk index 573a5d0f3..ff263245d 100644 --- a/conf/i386-qemu.rmk +++ b/conf/i386-qemu.rmk @@ -1,2 +1,82 @@ # -*- makefile -*- -include $(srcdir)/conf/i386-coreboot.mk + +COMMON_CFLAGS = -mrtd -mregparm=3 + +# Images. + +GRUB_KERNEL_MACHINE_LINK_ADDR = 0x8200 +GRUB_BOOT_MACHINE_LINK_ADDR = 0xffe00 + +pkglib_IMAGES += boot.img +boot_img_SOURCES = boot/i386/qemu/boot.S +boot_img_ASFLAGS = $(COMMON_ASFLAGS) -DGRUB_BOOT_MACHINE_LINK_ADDR=$(GRUB_BOOT_MACHINE_LINK_ADDR) +boot_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)$(GRUB_BOOT_MACHINE_LINK_ADDR) +boot_img_FORMAT = binary + +pkglib_PROGRAMS += kernel.img +kernel_img_SOURCES = kern/i386/qemu/startup.S \ + kern/i386/misc.S \ + kern/i386/coreboot/init.c \ + kern/i386/qemu/mmap.c \ + kern/i386/halt.c \ + kern/main.c kern/device.c \ + kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \ + kern/misc.c kern/mm.c kern/term.c \ + kern/rescue_parser.c kern/rescue_reader.c \ + kern/time.c kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ + kern/$(target_cpu)/dl.c kern/parser.c kern/partition.c \ + kern/i386/tsc.c kern/i386/pit.c \ + kern/generic/rtc_get_time_ms.c \ + kern/generic/millisleep.c \ + kern/env.c \ + term/i386/pc/vga_text.c term/i386/vga_common.c \ + symlist.c +kernel_img_CFLAGS = $(COMMON_CFLAGS) -DGRUB_BOOT_MACHINE_LINK_ADDR=$(GRUB_BOOT_MACHINE_LINK_ADDR) +kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -DGRUB_KERNEL_MACHINE_LINK_ADDR=$(GRUB_KERNEL_MACHINE_LINK_ADDR) +kernel_img_LDFLAGS += $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)$(GRUB_KERNEL_MACHINE_LINK_ADDR) +kernel_img_FORMAT = binary + +sbin_SCRIPTS += grub-install +grub_install_SOURCES = util/grub-install.in + +bin_SCRIPTS += grub-mkrescue +grub_mkrescue_SOURCES = util/grub-mkrescue.in + +# Modules. +pkglib_MODULES = linux.mod aout.mod halt.mod datetime.mod mmap.mod + +# For mmap.mod. +mmap_mod_SOURCES = mmap/mmap.c mmap/i386/uppermem.c mmap/i386/mmap.c +mmap_mod_CFLAGS = $(COMMON_CFLAGS) +mmap_mod_LDFLAGS = $(COMMON_LDFLAGS) +mmap_mod_ASFLAGS = $(COMMON_ASFLAGS) + +# For linux.mod. +linux_mod_SOURCES = loader/i386/linux.c +linux_mod_CFLAGS = $(COMMON_CFLAGS) +linux_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For halt.mod. +halt_mod_SOURCES = commands/halt.c +halt_mod_CFLAGS = $(COMMON_CFLAGS) +halt_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For aout.mod. +aout_mod_SOURCES = loader/aout.c +aout_mod_CFLAGS = $(COMMON_CFLAGS) +aout_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For bsd.mod +pkglib_MODULES += bsd.mod +bsd_mod_SOURCES = loader/i386/bsd.c loader/i386/bsd32.c loader/i386/bsd64.c loader/i386/bsd_helper.S loader/i386/bsd_trampoline.S +bsd_mod_CFLAGS = $(COMMON_CFLAGS) +bsd_mod_LDFLAGS = $(COMMON_LDFLAGS) +bsd_mod_ASFLAGS = $(COMMON_ASFLAGS) + +# For datetime.mod +datetime_mod_SOURCES = lib/cmos_datetime.c +datetime_mod_CFLAGS = $(COMMON_CFLAGS) +datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) + +include $(srcdir)/conf/i386.mk +include $(srcdir)/conf/common.mk diff --git a/conf/i386.rmk b/conf/i386.rmk index 829f82f1d..a3f79dd43 100644 --- a/conf/i386.rmk +++ b/conf/i386.rmk @@ -33,21 +33,41 @@ setpci_mod_CFLAGS = $(COMMON_CFLAGS) setpci_mod_LDFLAGS = $(COMMON_LDFLAGS) pkglib_MODULES += multiboot.mod -multiboot_mod_SOURCES = loader/i386/multiboot.c \ - loader/i386/multiboot_mbi.c \ - loader/multiboot_loader.c +multiboot_mod_SOURCES = loader/multiboot.c loader/i386/multiboot_mbi.c multiboot_mod_CFLAGS = $(COMMON_CFLAGS) multiboot_mod_LDFLAGS = $(COMMON_LDFLAGS) multiboot_mod_ASFLAGS = $(COMMON_ASFLAGS) pkglib_MODULES += multiboot2.mod -multiboot2_mod_SOURCES = loader/i386/multiboot.c \ - loader/i386/multiboot_mbi.c \ - loader/multiboot_loader.c +multiboot2_mod_SOURCES = loader/multiboot.c loader/multiboot_mbi2.c multiboot2_mod_CFLAGS = $(COMMON_CFLAGS) -DGRUB_USE_MULTIBOOT2 multiboot2_mod_LDFLAGS = $(COMMON_LDFLAGS) multiboot2_mod_ASFLAGS = $(COMMON_ASFLAGS) +# For serial.mod. +pkglib_MODULES += serial.mod +serial_mod_SOURCES = term/serial.c +serial_mod_CFLAGS = $(COMMON_CFLAGS) +serial_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For pci.mod +pkglib_MODULES += pci.mod +pci_mod_SOURCES = bus/pci.c +pci_mod_CFLAGS = $(COMMON_CFLAGS) +pci_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For lspci.mod +pkglib_MODULES += lspci.mod +lspci_mod_SOURCES = commands/lspci.c +lspci_mod_CFLAGS = $(COMMON_CFLAGS) +lspci_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For play.mod. +pkglib_MODULES += play.mod +play_mod_SOURCES = commands/i386/pc/play.c +play_mod_CFLAGS = $(COMMON_CFLAGS) +play_mod_LDFLAGS = $(COMMON_LDFLAGS) + linux.init.x86_64: $(srcdir)/tests/boot/linux.init-x86_64.S $(TARGET_CC) -o $@ $< -m64 -nostdlib -nostdinc -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" @@ -80,7 +100,7 @@ bootcheck-linux-i386: linux-initramfs.i386 $(GRUB_PAYLOADS_DIR)/linux.i386 $(src bootcheck-linux-x86_64: linux-initramfs.x86_64 $(GRUB_PAYLOADS_DIR)/linux.x86_64 $(srcdir)/tests/boot/linux.cfg grub-shell timeout -s KILL $(BOOTCHECK_TIMEOUT) ./grub-shell --boot=$(BOOTTARGET) --qemu=qemu-system-x86_64 --files=/initrd=linux-initramfs.x86_64 --files=/linux=$(GRUB_PAYLOADS_DIR)/linux.x86_64 $(srcdir)/tests/boot/linux.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null -BOOTCHECKS+=bootcheck-linux-i386 bootcheck-linux-x86_64 \ +BOOTCHECKS += bootcheck-linux-i386 bootcheck-linux-x86_64 \ bootcheck-kfreebsd-i386 bootcheck-kfreebsd-x86_64 .PHONY: bootcheck-linux-i386 bootcheck-linux-x86_64 \ diff --git a/conf/mips-qemu-mips.rmk b/conf/mips-qemu-mips.rmk index e06370122..2f80ab2e3 100644 --- a/conf/mips-qemu-mips.rmk +++ b/conf/mips-qemu-mips.rmk @@ -5,7 +5,7 @@ COMMON_CFLAGS += -march=mips3 COMMON_ASFLAGS += -march=mips3 include $(srcdir)/conf/mips.mk -pkglib_IMAGES = kernel.img +pkglib_PROGRAMS = kernel.img kernel_img_SOURCES = kern/$(target_cpu)/startup.S \ kern/main.c kern/device.c kern/$(target_cpu)/init.c \ kern/$(target_cpu)/$(target_machine)/init.c \ @@ -18,6 +18,5 @@ kernel_img_SOURCES = kern/$(target_cpu)/startup.S \ symlist.c kern/$(target_cpu)/cache.S kernel_img_CFLAGS = $(COMMON_CFLAGS) kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) -static-libgcc -lgcc \ - -Wl,-N,-S,-Ttext,$(LINK_BASE),-Bstatic +kernel_img_LDFLAGS = $(COMMON_LDFLAGS) -Wl,-N,-S,-Ttext,$(LINK_BASE),-Bstatic kernel_img_FORMAT = binary diff --git a/conf/mips-yeeloong.rmk b/conf/mips-yeeloong.rmk index 964f29384..5ce8ede9d 100644 --- a/conf/mips-yeeloong.rmk +++ b/conf/mips-yeeloong.rmk @@ -3,9 +3,12 @@ LINK_BASE = 0x80200000 target_machine=yeeloong COMMON_CFLAGS += -march=mips3 COMMON_ASFLAGS += -march=mips3 + +kernel_img_HEADERS += pci.h bitmap.h video.h gfxterm.h font.h bitmap_scale.h bufio.h + include $(srcdir)/conf/mips.mk -pkglib_IMAGES = kernel.img +pkglib_PROGRAMS = kernel.img kernel_img_SOURCES = kern/$(target_cpu)/startup.S \ kern/main.c kern/device.c kern/$(target_cpu)/init.c \ kern/$(target_cpu)/$(target_machine)/init.c \ @@ -21,13 +24,12 @@ kernel_img_SOURCES = kern/$(target_cpu)/startup.S \ font/font_cmd.c font/font.c io/bufio.c \ video/video.c video/fb/video_fb.c video/fb/fbblit.c \ video/fb/fbfill.c video/fb/fbutil.c video/bitmap.c \ - video/sm712.c bus/pci.c bus/bonito.c \ + video/bitmap_scale.c video/sm712.c bus/pci.c bus/bonito.c \ term/gfxterm.c commands/extcmd.c lib/arg.c \ symlist.c kernel_img_CFLAGS = $(COMMON_CFLAGS) -DUSE_ASCII_FAILBACK kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) -static-libgcc -lgcc \ - -Wl,-N,-S,-Ttext,$(LINK_BASE),-Bstatic +kernel_img_LDFLAGS += $(COMMON_LDFLAGS) -Wl,-N,-S,-Ttext,$(LINK_BASE),-Bstatic kernel_img_FORMAT = binary # For ata.mod. @@ -61,19 +63,11 @@ datetime_mod_SOURCES = lib/cmos_datetime.c datetime_mod_CFLAGS = $(COMMON_CFLAGS) datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For date.mod -pkglib_MODULES += date.mod -date_mod_SOURCES = commands/date.c -date_mod_CFLAGS = $(COMMON_CFLAGS) -date_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datehook.mod -pkglib_MODULES += datehook.mod -datehook_mod_SOURCES = hook/datehook.c -datehook_mod_CFLAGS = $(COMMON_CFLAGS) -datehook_mod_LDFLAGS = $(COMMON_LDFLAGS) - +pkglib_MODULES += linux.mod +linux_mod_SOURCES = loader/$(target_cpu)/linux.c +linux_mod_CFLAGS = $(COMMON_CFLAGS) +linux_mod_ASFLAGS = $(COMMON_ASFLAGS) +linux_mod_LDFLAGS = $(COMMON_LDFLAGS) sbin_SCRIPTS += grub-install grub_install_SOURCES = util/grub-install.in - diff --git a/conf/mips.rmk b/conf/mips.rmk index 1ef4fc395..7d30f55f1 100644 --- a/conf/mips.rmk +++ b/conf/mips.rmk @@ -1,65 +1,15 @@ # -*- makefile -*- -COMMON_ASFLAGS += -nostdinc -COMMON_CFLAGS += -ffreestanding -mexplicit-relocs -mflush-func=grub_cpu_flush_cache -COMMON_LDFLAGS += -nostdlib - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h +COMMON_CFLAGS += -mexplicit-relocs -mflush-func=grub_cpu_flush_cache # Images. - -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h misc.h mm.h net.h parser.h reader.h \ - symbol.h term.h time.h types.h loader.h partition.h \ - msdos_partition.h machine/kernel.h handler.h list.h \ - command.h machine/memory.h cpu/libgcc.h cpu/cache.h i18n.h env_private.h - -ifeq ($(platform), yeeloong) -kernel_img_HEADERS += pci.h -endif - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) +kernel_img_HEADERS += cpu/cache.h # Scripts. sbin_SCRIPTS = bin_SCRIPTS = -# For grub-mkimage. -bin_UTILITIES += grub-mkimage -grub_mkimage_SOURCES = gnulib/progname.c util/grub-mkrawimage.c util/misc.c \ - util/resolve.c lib/LzmaEnc.c lib/LzFind.c -grub_mkimage_CFLAGS = -DGRUB_KERNEL_MACHINE_LINK_ADDR=$(LINK_BASE) -util/grub-mkrawimage.c_DEPENDENCIES = Makefile - -# Modules. -pkglib_MODULES = memdisk.mod \ - lsmmap.mod - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c lib/i386/pc/biosnum.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For memdisk.mod. -memdisk_mod_SOURCES = disk/memdisk.c -memdisk_mod_CFLAGS = $(COMMON_CFLAGS) -memdisk_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lsmmap.mod -lsmmap_mod_SOURCES = commands/lsmmap.c -lsmmap_mod_CFLAGS = $(COMMON_CFLAGS) -lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For serial.mod. pkglib_MODULES += serial.mod serial_mod_SOURCES = term/serial.c @@ -73,10 +23,11 @@ relocator_mod_CFLAGS = $(COMMON_CFLAGS) relocator_mod_ASFLAGS = $(COMMON_ASFLAGS) relocator_mod_LDFLAGS = $(COMMON_LDFLAGS) -pkglib_MODULES += linux.mod -linux_mod_SOURCES = loader/$(target_cpu)/linux.c -linux_mod_CFLAGS = $(COMMON_CFLAGS) -linux_mod_ASFLAGS = $(COMMON_ASFLAGS) -linux_mod_LDFLAGS = $(COMMON_LDFLAGS) +pkglib_MODULES += multiboot2.mod +multiboot2_mod_SOURCES = loader/multiboot.c \ + loader/multiboot_mbi2.c +multiboot2_mod_CFLAGS = $(COMMON_CFLAGS) -DGRUB_USE_MULTIBOOT2 +multiboot2_mod_LDFLAGS = $(COMMON_LDFLAGS) +multiboot2_mod_ASFLAGS = $(COMMON_ASFLAGS) include $(srcdir)/conf/common.mk diff --git a/conf/powerpc-ieee1275.rmk b/conf/powerpc-ieee1275.rmk index 23bd2d620..d5968ac8e 100644 --- a/conf/powerpc-ieee1275.rmk +++ b/conf/powerpc-ieee1275.rmk @@ -1,29 +1,9 @@ # -*- makefile -*- -COMMON_ASFLAGS = -nostdinc -D__ASSEMBLY__ -COMMON_CFLAGS = -ffreestanding -COMMON_LDFLAGS += -nostdlib - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h - # Images. -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h misc.h mm.h net.h parser.h reader.h \ - symbol.h term.h time.h types.h powerpc/libgcc.h loader.h partition.h \ - msdos_partition.h ieee1275/ieee1275.h machine/kernel.h handler.h list.h \ - command.h i18n.h env_private.h - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) +kernel_img_HEADERS += ieee1275/ieee1275.h # Programs pkglib_PROGRAMS = kernel.img @@ -43,8 +23,7 @@ kernel_img_SOURCES = kern/powerpc/ieee1275/startup.S kern/ieee1275/cmain.c \ symlist.c kern/$(target_cpu)/cache.S kernel_img_CFLAGS = $(COMMON_CFLAGS) kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) -static-libgcc -lgcc \ - -Wl,-N,-S,-Ttext,0x200000,-Bstatic +kernel_img_LDFLAGS += $(COMMON_LDFLAGS) -Wl,-N,-S,-Ttext,0x200000,-Bstatic # Scripts. sbin_SCRIPTS = grub-install @@ -57,47 +36,33 @@ grub_install_SOURCES = util/ieee1275/grub-install.in grub_mkrescue_SOURCES = util/powerpc/ieee1275/grub-mkrescue.in # Modules. -pkglib_MODULES = halt.mod \ - linux.mod \ - reboot.mod \ - suspend.mod \ - memdisk.mod \ - lsmmap.mod - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c lib/i386/pc/biosnum.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) +pkglib_MODULES += ieee1275_fb.mod +ieee1275_fb_mod_SOURCES = video/ieee1275.c +ieee1275_fb_mod_CFLAGS = $(COMMON_CFLAGS) +ieee1275_fb_mod_LDFLAGS = $(COMMON_LDFLAGS) # For linux.mod. +pkglib_MODULES += linux.mod linux_mod_SOURCES = loader/powerpc/ieee1275/linux.c linux_mod_CFLAGS = $(COMMON_CFLAGS) linux_mod_LDFLAGS = $(COMMON_LDFLAGS) # For suspend.mod +pkglib_MODULES += suspend.mod suspend_mod_SOURCES = commands/ieee1275/suspend.c suspend_mod_CFLAGS = $(COMMON_CFLAGS) suspend_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For reboot.mod -reboot_mod_SOURCES = commands/reboot.c -reboot_mod_CFLAGS = $(COMMON_CFLAGS) -reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For halt.mod +pkglib_MODULES += halt.mod halt_mod_SOURCES = commands/halt.c halt_mod_CFLAGS = $(COMMON_CFLAGS) halt_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For memdisk.mod. -memdisk_mod_SOURCES = disk/memdisk.c -memdisk_mod_CFLAGS = $(COMMON_CFLAGS) -memdisk_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lsmmap.mod -lsmmap_mod_SOURCES = commands/lsmmap.c -lsmmap_mod_CFLAGS = $(COMMON_CFLAGS) -lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For datetime.mod +pkglib_MODULES += datetime.mod +datetime_mod_SOURCES = lib/ieee1275/datetime.c +datetime_mod_CFLAGS = $(COMMON_CFLAGS) +datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) include $(srcdir)/conf/common.mk diff --git a/conf/sparc64-ieee1275.rmk b/conf/sparc64-ieee1275.rmk index befc7dce5..5b71d4cb2 100644 --- a/conf/sparc64-ieee1275.rmk +++ b/conf/sparc64-ieee1275.rmk @@ -1,15 +1,12 @@ # -*- makefile -*- -COMMON_ASFLAGS = -nostdinc -m64 -COMMON_CFLAGS = -ffreestanding -m64 -mno-app-regs -COMMON_LDFLAGS = -melf64_sparc -nostdlib -mno-relax - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h +COMMON_CFLAGS = -mno-app-regs +COMMON_LDFLAGS = -melf64_sparc -mno-relax # Images. -pkglib_IMAGES = boot.img diskboot.img kernel.img +pkglib_IMAGES = boot.img diskboot.img +pkglib_PROGRAMS = kernel.img # For boot.img. boot_img_SOURCES = boot/sparc64/ieee1275/boot.S @@ -23,15 +20,7 @@ diskboot_img_ASFLAGS = $(COMMON_ASFLAGS) diskboot_img_LDFLAGS = $(COMMON_LDFLAGS) -Wl,-N,-Ttext,0x4200 diskboot_img_FORMAT = binary -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ - partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ - list.h handler.h command.h i18n.h \ - sparc64/libgcc.h ieee1275/ieee1275.h machine/kernel.h \ - sparc64/ieee1275/ieee1275.h env_private.h +kernel_img_HEADERS += ieee1275/ieee1275.h cpu/ieee1275/ieee1275.h kernel_img_SOURCES = kern/sparc64/ieee1275/crt0.S kern/ieee1275/cmain.c \ kern/ieee1275/ieee1275.c kern/main.c kern/device.c \ kern/disk.c kern/dl.c kern/err.c kern/file.c kern/fs.c \ @@ -48,45 +37,36 @@ kernel_img_SOURCES = kern/sparc64/ieee1275/crt0.S kern/ieee1275/cmain.c \ symlist.c kern/$(target_cpu)/cache.S kernel_img_CFLAGS = $(COMMON_CFLAGS) kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = -nostdlib -Wl,-N,-Ttext,0x200000,-Bstatic,-melf64_sparc -static-libgcc -lgcc +kernel_img_LDFLAGS += -nostdlib -Wl,-N,-Ttext,0x4400,-Bstatic,-melf64_sparc kernel_img_FORMAT = binary -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - # Utilities. -bin_UTILITIES = grub-mkimage sbin_UTILITIES = grub-setup grub-ofpathname -# For grub-mkimage. -grub_mkimage_SOURCES = util/sparc64/ieee1275/grub-mkimage.c util/misc.c \ - util/resolve.c gnulib/progname.c - # For grub-setup. util/sparc64/ieee1275/grub-setup.c_DEPENDENCIES = grub_setup_init.h grub_setup_SOURCES = util/sparc64/ieee1275/grub-setup.c util/hostdisk.c \ + util/ieee1275/ofpath.c \ util/misc.c util/getroot.c kern/device.c kern/disk.c \ kern/err.c kern/misc.c kern/parser.c kern/partition.c \ - kern/file.c kern/fs.c kern/env.c fs/fshelp.c \ + kern/file.c kern/fs.c kern/env.c kern/list.c \ + fs/fshelp.c \ \ fs/affs.c fs/cpio.c fs/ext2.c fs/fat.c fs/hfs.c \ fs/hfsplus.c fs/iso9660.c fs/udf.c fs/jfs.c fs/minix.c \ - fs/ntfs.c fs/ntfscomp.c fs/reiserfs.c fs/sfs.c \ - fs/ufs.c fs/ufs2.c fs/xfs.c fs/afs.c fs/afs_be.c \ - fs/befs.c fs/befs_be.c fs/tar.c \ + fs/nilfs2.c fs/ntfs.c fs/ntfscomp.c fs/reiserfs.c \ + fs/sfs.c fs/ufs.c fs/ufs2.c fs/xfs.c fs/afs.c \ + fs/afs_be.c fs/befs.c fs/befs_be.c fs/tar.c \ \ - partmap/amiga.c partmap/apple.c partmap/msdos.c \ - partmap/sun.c partmap/acorn.c \ + partmap/amiga.c partmap/apple.c partmap/msdos.c \ + partmap/bsdlabel.c partmap/sun.c partmap/acorn.c \ \ disk/raid.c disk/mdraid_linux.c disk/lvm.c \ - util/raid.c util/lvm.c gnulib/progname.c \ + util/raid.c util/lvm.c util/mm.c gnulib/progname.c \ grub_setup_init.c # For grub-ofpathname. -grub_ofpathname_SOURCES = util/sparc64/ieee1275/grub-ofpathname.c \ +grub_ofpathname_SOURCES = util/ieee1275/grub-ofpathname.c \ util/ieee1275/ofpath.c util/misc.c gnulib/progname.c # Scripts. @@ -96,41 +76,27 @@ sbin_SCRIPTS = grub-install grub_install_SOURCES = util/grub-install.in # Modules. -pkglib_MODULES = halt.mod \ - linux.mod \ - reboot.mod \ - memdisk.mod \ - lsmmap.mod - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c lib/i386/pc/biosnum.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) +pkglib_MODULES += ieee1275_fb.mod +ieee1275_fb_mod_SOURCES = video/ieee1275.c +ieee1275_fb_mod_CFLAGS = $(COMMON_CFLAGS) +ieee1275_fb_mod_LDFLAGS = $(COMMON_LDFLAGS) # For linux.mod. +pkglib_MODULES += linux.mod linux_mod_SOURCES = loader/sparc64/ieee1275/linux.c linux_mod_CFLAGS = $(COMMON_CFLAGS) linux_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For reboot.mod. -reboot_mod_SOURCES = commands/reboot.c -reboot_mod_CFLAGS = $(COMMON_CFLAGS) -reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) - # For halt.mod. +pkglib_MODULES += halt.mod halt_mod_SOURCES = commands/halt.c halt_mod_CFLAGS = $(COMMON_CFLAGS) halt_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For memdisk.mod. -memdisk_mod_SOURCES = disk/memdisk.c -memdisk_mod_CFLAGS = $(COMMON_CFLAGS) -memdisk_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lsmmap.mod -lsmmap_mod_SOURCES = commands/lsmmap.c -lsmmap_mod_CFLAGS = $(COMMON_CFLAGS) -lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For datetime.mod +pkglib_MODULES += datetime.mod +datetime_mod_SOURCES = lib/ieee1275/datetime.c +datetime_mod_CFLAGS = $(COMMON_CFLAGS) +datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) include $(srcdir)/conf/common.mk diff --git a/conf/tests.rmk b/conf/tests.rmk index 18b23ff5a..d48bc3dd9 100644 --- a/conf/tests.rmk +++ b/conf/tests.rmk @@ -37,12 +37,52 @@ example_scripted_test_SOURCES = tests/example_scripted_test.in check_SCRIPTS += example_grub_script_test example_grub_script_test_SOURCES = tests/example_grub_script_test.in +# +# Rules for real tests +# + +check_SCRIPTS += grub_script_echo1 +grub_script_echo1_SOURCES = tests/grub_script_echo1.in + +check_SCRIPTS += grub_script_echo_keywords +grub_script_echo_keywords_SOURCES = tests/grub_script_echo_keywords.in + +check_SCRIPTS += grub_script_vars1 +grub_script_vars1_SOURCES = tests/grub_script_vars1.in + +check_SCRIPTS += grub_script_for1 +grub_script_for1_SOURCES = tests/grub_script_for1.in + +check_SCRIPTS += grub_script_while1 +grub_script_while1_SOURCES = tests/grub_script_while1.in + +check_SCRIPTS += grub_script_if +grub_script_if_SOURCES = tests/grub_script_if.in + +check_SCRIPTS += grub_script_blanklines +grub_script_blanklines_SOURCES = tests/grub_script_blanklines.in + +check_SCRIPTS += grub_script_final_semicolon +grub_script_final_semicolon_SOURCES = tests/grub_script_final_semicolon.in + +check_SCRIPTS += grub_script_dollar +grub_script_dollar_SOURCES = tests/grub_script_dollar.in # List of tests to execute on "make check" -SCRIPTED_TESTS = example_scripted_test -SCRIPTED_TESTS += example_grub_script_test -UNIT_TESTS = example_unit_test -FUNCTIONAL_TESTS = example_functional_test.mod +# SCRIPTED_TESTS = example_scripted_test +# SCRIPTED_TESTS += example_grub_script_test +# UNIT_TESTS = example_unit_test +# FUNCTIONAL_TESTS = example_functional_test.mod + +SCRIPTED_TESTS = grub_script_echo1 +SCRIPTED_TESTS += grub_script_echo_keywords +SCRIPTED_TESTS += grub_script_vars1 +SCRIPTED_TESTS += grub_script_for1 +SCRIPTED_TESTS += grub_script_while1 +SCRIPTED_TESTS += grub_script_if +SCRIPTED_TESTS += grub_script_blanklines +SCRIPTED_TESTS += grub_script_final_semicolon +SCRIPTED_TESTS += grub_script_dollar # dependencies between tests and testing-tools $(SCRIPTED_TESTS): grub-shell grub-shell-tester diff --git a/conf/x86-efi.rmk b/conf/x86-efi.rmk new file mode 100644 index 000000000..99a9938ef --- /dev/null +++ b/conf/x86-efi.rmk @@ -0,0 +1,105 @@ +# -*- makefile -*- + +# Scripts. +sbin_SCRIPTS = grub-install + +# For grub-install. +grub_install_SOURCES = util/i386/efi/grub-install.in + +# Modules. +pkglib_PROGRAMS = kernel.img +pkglib_MODULES = chain.mod appleldr.mod \ + linux.mod halt.mod \ + datetime.mod loadbios.mod \ + fixvideo.mod mmap.mod acpi.mod + +# For kernel.img. +kernel_img_RELOCATABLE = yes +kernel_img_SOURCES = kern/$(target_cpu)/efi/startup.S kern/main.c kern/device.c \ + kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \ + kern/misc.c kern/mm.c kern/term.c \ + kern/rescue_parser.c kern/rescue_reader.c \ + kern/$(target_cpu)/dl.c kern/i386/efi/init.c kern/parser.c kern/partition.c \ + kern/env.c symlist.c kern/efi/efi.c kern/efi/init.c kern/efi/mm.c \ + term/efi/console.c disk/efi/efidisk.c \ + kern/time.c kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ + kern/i386/tsc.c kern/i386/pit.c \ + kern/generic/rtc_get_time_ms.c \ + kern/generic/millisleep.c +ifeq ($(target_cpu),x86_64) +kernel_img_SOURCES += kern/x86_64/efi/callwrap.S +endif +kernel_img_HEADERS += efi/efi.h efi/time.h efi/disk.h i386/pit.h +kernel_img_CFLAGS = $(COMMON_CFLAGS) +kernel_img_ASFLAGS = $(COMMON_ASFLAGS) +kernel_img_LDFLAGS += $(COMMON_LDFLAGS) + +# For acpi.mod. +acpi_mod_SOURCES = commands/acpi.c commands/efi/acpi.c +acpi_mod_CFLAGS = $(COMMON_CFLAGS) +acpi_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For mmap.mod. +mmap_mod_SOURCES = mmap/mmap.c mmap/i386/uppermem.c mmap/i386/mmap.c \ + mmap/efi/mmap.c +mmap_mod_CFLAGS = $(COMMON_CFLAGS) +mmap_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For chain.mod. +chain_mod_SOURCES = loader/efi/chainloader.c +chain_mod_CFLAGS = $(COMMON_CFLAGS) +chain_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For appleldr.mod. +appleldr_mod_SOURCES = loader/efi/appleloader.c +appleldr_mod_CFLAGS = $(COMMON_CFLAGS) +appleldr_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For linux.mod. +linux_mod_SOURCES = loader/i386/efi/linux.c +ifeq ($(target_cpu), x86_64) +linux_mod_SOURCES += loader/i386/linux_trampoline.S +endif +linux_mod_CFLAGS = $(COMMON_CFLAGS) +linux_mod_ASFLAGS = $(COMMON_ASFLAGS) +linux_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For halt.mod. +halt_mod_SOURCES = commands/halt.c +halt_mod_CFLAGS = $(COMMON_CFLAGS) +halt_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For datetime.mod +datetime_mod_SOURCES = lib/efi/datetime.c +datetime_mod_CFLAGS = $(COMMON_CFLAGS) +datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For loadbios.mod +loadbios_mod_SOURCES = commands/efi/loadbios.c +loadbios_mod_CFLAGS = $(COMMON_CFLAGS) +loadbios_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For fixvideo.mod +fixvideo_mod_SOURCES = commands/efi/fixvideo.c +fixvideo_mod_CFLAGS = $(COMMON_CFLAGS) +fixvideo_mod_LDFLAGS = $(COMMON_LDFLAGS) + +pkglib_MODULES += efi_uga.mod +efi_uga_mod_SOURCES = video/efi_uga.c +efi_uga_mod_CFLAGS = $(COMMON_CFLAGS) +efi_uga_mod_LDFLAGS = $(COMMON_LDFLAGS) + +pkglib_MODULES += efi_gop.mod +efi_gop_mod_SOURCES = video/efi_gop.c +efi_gop_mod_CFLAGS = $(COMMON_CFLAGS) +efi_gop_mod_LDFLAGS = $(COMMON_LDFLAGS) + +pkglib_MODULES += xnu.mod +xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c \ + loader/macho32.c loader/macho64.c loader/macho.c loader/xnu.c +xnu_mod_CFLAGS = $(COMMON_CFLAGS) +xnu_mod_LDFLAGS = $(COMMON_LDFLAGS) +xnu_mod_ASFLAGS = $(COMMON_ASFLAGS) + +include $(srcdir)/conf/i386.mk +include $(srcdir)/conf/common.mk diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk index d5c3c24cb..200621280 100644 --- a/conf/x86_64-efi.rmk +++ b/conf/x86_64-efi.rmk @@ -1,166 +1,5 @@ # -*- makefile -*- -COMMON_ASFLAGS = -nostdinc -fno-builtin -m64 -COMMON_CFLAGS = -fno-builtin -m64 -COMMON_LDFLAGS = -melf_x86_64 -nostdlib +COMMON_LDFLAGS = -melf_x86_64 -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h - -# Utilities. -bin_UTILITIES = grub-mkimage - -# For grub-mkimage. -grub_mkimage_SOURCES = gnulib/progname.c util/i386/efi/grub-mkimage.c \ - util/misc.c util/resolve.c - -# For grub-setup. -#grub_setup_SOURCES = util/i386/pc/grub-setup.c util/hostdisk.c \ -# util/misc.c util/getroot.c kern/device.c kern/disk.c \ -# kern/err.c kern/misc.c fs/fat.c fs/ext2.c fs/xfs.c fs/affs.c \ -# fs/sfs.c kern/parser.c kern/partition.c partmap/msdos.c \ -# fs/ufs.c fs/ufs2.c fs/minix.c fs/hfs.c fs/jfs.c fs/hfsplus.c kern/file.c \ -# kern/fs.c kern/env.c fs/fshelp.c - -# Scripts. -sbin_SCRIPTS = grub-install - -# For grub-install. -grub_install_SOURCES = util/i386/efi/grub-install.in - -# Modules. -pkglib_PROGRAMS = kernel.img -pkglib_MODULES = chain.mod appleldr.mod \ - halt.mod reboot.mod linux.mod pci.mod lspci.mod \ - datetime.mod date.mod datehook.mod loadbios.mod \ - fixvideo.mod mmap.mod acpi.mod - -# For kernel.img. -kernel_img_RELOCATABLE = yes -kernel_img_SOURCES = kern/x86_64/efi/startup.S kern/x86_64/efi/callwrap.S \ - kern/main.c kern/device.c \ - kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \ - kern/misc.c kern/mm.c kern/term.c \ - kern/rescue_parser.c kern/rescue_reader.c \ - kern/$(target_cpu)/dl.c kern/i386/efi/init.c kern/parser.c kern/partition.c \ - kern/env.c symlist.c kern/efi/efi.c kern/efi/init.c kern/efi/mm.c \ - kern/time.c kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ - kern/i386/tsc.c kern/i386/pit.c \ - kern/generic/millisleep.c kern/generic/rtc_get_time_ms.c \ - term/efi/console.c disk/efi/efidisk.c -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ - partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ - efi/efi.h efi/time.h efi/disk.h machine/loader.h i386/pit.h list.h \ - handler.h command.h i18n.h env_private.h -kernel_img_CFLAGS = $(COMMON_CFLAGS) -kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) - -MOSTLYCLEANFILES += symlist.c -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c lib/i386/pc/biosnum.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For acpi.mod. -acpi_mod_SOURCES = commands/acpi.c commands/efi/acpi.c -acpi_mod_CFLAGS = $(COMMON_CFLAGS) -acpi_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For mmap.mod. -mmap_mod_SOURCES = mmap/mmap.c mmap/i386/uppermem.c mmap/i386/mmap.c \ - mmap/efi/mmap.c -mmap_mod_CFLAGS = $(COMMON_CFLAGS) -mmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For chain.mod. -chain_mod_SOURCES = loader/efi/chainloader.c -chain_mod_CFLAGS = $(COMMON_CFLAGS) -chain_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For appleldr.mod. -appleldr_mod_SOURCES = loader/efi/appleloader.c -appleldr_mod_CFLAGS = $(COMMON_CFLAGS) -appleldr_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For linux.mod. -linux_mod_SOURCES = loader/i386/efi/linux.c loader/i386/linux_trampoline.S -linux_mod_CFLAGS = $(COMMON_CFLAGS) -linux_mod_ASFLAGS = $(COMMON_ASFLAGS) -linux_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For halt.mod. -halt_mod_SOURCES = commands/halt.c -halt_mod_CFLAGS = $(COMMON_CFLAGS) -halt_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For reboot.mod. -reboot_mod_SOURCES = commands/reboot.c -reboot_mod_CFLAGS = $(COMMON_CFLAGS) -reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For pci.mod -pci_mod_SOURCES = bus/pci.c -pci_mod_CFLAGS = $(COMMON_CFLAGS) -pci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lspci.mod -lspci_mod_SOURCES = commands/lspci.c -lspci_mod_CFLAGS = $(COMMON_CFLAGS) -lspci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datetime.mod -datetime_mod_SOURCES = lib/efi/datetime.c -datetime_mod_CFLAGS = $(COMMON_CFLAGS) -datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For date.mod -date_mod_SOURCES = commands/date.c -date_mod_CFLAGS = $(COMMON_CFLAGS) -date_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datehook.mod -datehook_mod_SOURCES = hook/datehook.c -datehook_mod_CFLAGS = $(COMMON_CFLAGS) -datehook_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For loadbios.mod -loadbios_mod_SOURCES = commands/efi/loadbios.c -loadbios_mod_CFLAGS = $(COMMON_CFLAGS) -loadbios_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For fixvideo.mod -fixvideo_mod_SOURCES = commands/efi/fixvideo.c -fixvideo_mod_CFLAGS = $(COMMON_CFLAGS) -fixvideo_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += efi_uga.mod -efi_uga_mod_SOURCES = video/efi_uga.c -efi_uga_mod_CFLAGS = $(COMMON_CFLAGS) -efi_uga_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += efi_gop.mod -efi_gop_mod_SOURCES = video/efi_gop.c -efi_gop_mod_CFLAGS = $(COMMON_CFLAGS) -efi_gop_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += xnu.mod -xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c \ - loader/macho32.c loader/macho64.c loader/macho.c loader/xnu.c -xnu_mod_CFLAGS = $(COMMON_CFLAGS) -xnu_mod_LDFLAGS = $(COMMON_LDFLAGS) -xnu_mod_ASFLAGS = $(COMMON_ASFLAGS) - -include $(srcdir)/conf/i386.mk -include $(srcdir)/conf/common.mk +include $(srcdir)/conf/x86-efi.mk diff --git a/configure.ac b/configure.ac index b1435de44..e3453b636 100644 --- a/configure.ac +++ b/configure.ac @@ -31,7 +31,7 @@ dnl (such as TARGET_CC, TARGET_CFLAGS, etc.) are used for the target dnl type. -AC_INIT([GRUB],[1.97],[bug-grub@gnu.org]) +AC_INIT([GRUB],[1.98],[bug-grub@gnu.org]) AM_INIT_AUTOMAKE() AC_PREREQ(2.60) AC_CONFIG_SRCDIR([include/grub/dl.h]) @@ -51,6 +51,7 @@ fi case "$target_cpu" in i[[3456]]86) target_cpu=i386 ;; + amd64) target_cpu=x86_64 ;; sparc) target_cpu=sparc64 ;; mipsel|mips64el) target_cpu=mips; @@ -90,6 +91,7 @@ fi if test -z "$target_alias"; then case "$target_cpu"-"$platform" in x86_64-efi) ;; + x86_64-emu) ;; x86_64-*) target_cpu=i386 ;; powerpc64-ieee1275) target_cpu=powerpc ;; esac @@ -128,6 +130,7 @@ case "$host_os" in gnu*) host_kernel=hurd ;; linux*) host_kernel=linux ;; freebsd* | kfreebsd*-gnu) host_kernel=kfreebsd ;; + netbsd*) host_kernel=netbsd ;; cygwin) host_kernel=windows ;; esac @@ -138,9 +141,15 @@ case "$platform" in qemu) machine_CFLAGS="-DGRUB_MACHINE_QEMU=1" ;; pc) machine_CFLAGS="-DGRUB_MACHINE_PCBIOS=1" ;; emu) machine_CFLAGS="-DGRUB_MACHINE_EMU=1" ;; - yeeloong) machine_CFLAGS="-DGRUB_MACHINE_MIPS_YEELOONG=1 -DGRUB_MACHINE_MIPS=1 -DGRUB_MACHINE_MIPS_BONITO=1" ;; - qemu-mips) machine_CFLAGS="-DGRUB_MACHINE_MIPS_QEMU_MIPS=1 -DGRUB_MACHINE_MIPS=1 -DGRUB_MACHINE_MIPS_BONITO=1" ;; + yeeloong) machine_CFLAGS="-DGRUB_MACHINE_MIPS_YEELOONG=1 -DGRUB_MACHINE_MIPS_BONITO=1" ;; + qemu-mips) machine_CFLAGS="-DGRUB_MACHINE_MIPS_QEMU_MIPS=1 -DGRUB_MACHINE_MIPS_BONITO=1" ;; esac +case "$target_cpu" in + mips) machine_CFLAGS="$machine_CFLAGS -DGRUB_MACHINE_MIPS=1" ;; + sparc64) machine_CFLAGS="$machine_CFLAGS -DGRUB_MACHINE_SPARC64=1" ;; +esac +machine_CFLAGS="$machine_CFLAGS -DMACHINE=`echo ${target_cpu}_$platform | sed y,abcdefghijklmnopqrstuvwxyz,ABCDEFGHIJKLMNOPQRSTUVWXYZ,`" + CFLAGS="$CFLAGS $machine_CFLAGS" TARGET_ASFLAGS="$TARGET_ASFLAGS $machine_CFLAGS" TARGET_CFLAGS="$TARGET_CFLAGS $machine_CFLAGS" @@ -177,9 +186,21 @@ done AC_PROG_INSTALL AC_PROG_AWK +AC_PROG_LEX AC_PROG_MAKE_SET AC_PROG_MKDIR_P +if test "x$LEX" = x; then + AC_MSG_ERROR([flex is not found]) +else + version=`$LEX --version | $AWK '{ split($NF,x,"."); print x[[1]]*10000+x[[2]]*100+x[[3]]; }'` + if test -n "$version" -a "$version" -ge 20535; then + : + else + AC_MSG_ERROR([flex is too old. GRUB requires 2.5.35 or above]) + fi +fi + # These are not a "must". AC_PATH_PROG(RUBY, ruby) AC_PATH_PROG(MAKEINFO, makeinfo) @@ -221,7 +242,21 @@ AC_HEADER_MAJOR AC_HEADER_DIRENT AC_CHECK_FUNCS(memmove sbrk strdup lstat getuid getgid) AC_CHECK_HEADERS(sys/mkdev.h sys/sysmacros.h malloc.h termios.h sys/types.h) -AC_CHECK_HEADERS(unistd.h string.h strings.h sys/stat.h sys/fcntl.h) +AC_CHECK_HEADERS(unistd.h string.h strings.h sys/stat.h sys/fcntl.h limits.h) + +# For opendisk() and getrawpartition() on NetBSD. +# Used in util/deviceiter.c and in util/hostdisk.c. +AC_CHECK_HEADER([util.h], [ + AC_CHECK_LIB([util], [opendisk], [ + LIBUTIL="-lutil" + AC_DEFINE(HAVE_OPENDISK, 1, [Define if opendisk() in -lutil can be used]) + ]) + AC_CHECK_LIB([util], [getrawpartition], [ + LIBUTIL="-lutil" + AC_DEFINE(HAVE_GETRAWPARTITION, 1, [Define if getrawpartition() in -lutil can be used]) + ]) +]) +AC_SUBST([LIBUTIL]) # # Check for target programs. @@ -326,7 +361,7 @@ else if test -f "${srcdir}/conf/${target_cpu}-${platform}-${host_os}-img-ld.sc"; then TARGET_IMG_LDSCRIPT='$(top_srcdir)'"/conf/${target_cpu}-${platform}-${host_os}-img-ld.sc" TARGET_IMG_LDFLAGS="-Wl,-T${TARGET_IMG_LDSCRIPT} -Wl,-Ttext," - TARGET_IMG_LDFLAGS_AC="-Wl,-T${srcdir}/conf/${target_cpu}-${platform}-${host_os}-img-ld.sc" + TARGET_IMG_LDFLAGS_AC="-Wl,-T${srcdir}/conf/${target_cpu}-${platform}-${host_os}-img-ld.sc -Wl,-Ttext," else TARGET_IMG_LDSCRIPT= TARGET_IMG_LDFLAGS='-Wl,-N -Wl,-Ttext,' @@ -342,7 +377,11 @@ AC_SUBST(TARGET_IMG_CFLAGS) # For platforms where ELF is not the default link format. AC_MSG_CHECKING([for command to convert module to ELF format]) case "${host_os}" in - cygwin) TARGET_OBJ2ELF='grub-pe2elf' ;; + cygwin) TARGET_OBJ2ELF='grub-pe2elf'; +# FIXME: put proper test here + AC_DEFINE([NEED_REGISTER_FRAME_INFO], 1, + [Define to 1 if GCC generates calls to __register_frame_info()]) + ;; *) ;; esac AC_SUBST(TARGET_OBJ2ELF) @@ -352,6 +391,7 @@ AC_MSG_RESULT([$TARGET_OBJ2ELF]) if test "x$target_m32" = x1; then # Force 32-bit mode. TARGET_CFLAGS="$TARGET_CFLAGS -m32" + TARGET_ASFLAGS="$TARGET_CFLAGS -m32" TARGET_LDFLAGS="$TARGET_LDFLAGS -m32" TARGET_MODULE_FORMAT="elf32" fi @@ -359,6 +399,7 @@ fi if test "x$target_m64" = x1; then # Force 64-bit mode. TARGET_CFLAGS="$TARGET_CFLAGS -m64" + TARGET_ASFLAGS="$TARGET_ASFLAGS -m64" TARGET_LDFLAGS="$TARGET_LDFLAGS -m64" TARGET_MODULE_FORMAT="elf64" fi @@ -373,9 +414,7 @@ if test "$target_cpu"-"$platform" = x86_64-efi; then [grub_cv_cc_mcmodel=no]) ]) if test "x$grub_cv_cc_mcmodel" = xno; then - CFLAGS="$SAVED_CFLAGS -m64 -DMCMODEL_SMALL=1" - TARGET_CFLAGS="$TARGET_CFLAGS -DMCMODEL_SMALL=1" - AC_MSG_WARN([-mcmodel=large not supported. You won't be able to use the memory over 4GiB. Upgrade your gcc]) + AC_MSG_ERROR([-mcmodel=large not supported. Upgrade your gcc.]) else TARGET_CFLAGS="$TARGET_CFLAGS -mcmodel=large" fi @@ -438,18 +477,33 @@ AC_SUBST(TARGET_ASFLAGS) AC_SUBST(TARGET_CPPFLAGS) AC_SUBST(TARGET_LDFLAGS) -# Check for libgcc symbols (must be performed before we add -nostdlib to LDFLAGS) -AC_CHECK_FUNCS(__bswapsi2 __bswapdi2 __ashldi3 __ashrdi3 __lshrdi3 __trampoline_setup __ucmpdi2) - # Set them to their new values for the tests below. CC="$TARGET_CC" +if test "x$TARGET_APPLE_CC" = x1 ; then +CFLAGS="$TARGET_CFLAGS -nostdlib -Wno-error" +else +CFLAGS="$TARGET_CFLAGS -nostdlib -Wl,--defsym,___main=0x8100 -Wno-error" +fi +CPPFLAGS="$TARGET_CPPFLAGS" +LDFLAGS="$TARGET_LDFLAGS" +LIBS=-lgcc + +grub_ASM_USCORE +if test x$grub_cv_asm_uscore = xyes; then +CFLAGS="$CFLAGS -Wl,--defsym,_abort=_main" +else +CFLAGS="$CFLAGS -Wl,--defsym,abort=main" +fi + +# Check for libgcc symbols +AC_CHECK_FUNCS(__bswapsi2 __bswapdi2 __ashldi3 __ashrdi3 __lshrdi3 __trampoline_setup __ucmpdi2 _restgpr_14_x) + if test "x$TARGET_APPLE_CC" = x1 ; then CFLAGS="$TARGET_CFLAGS -nostdlib" else CFLAGS="$TARGET_CFLAGS -nostdlib -Wl,--defsym,___main=0x8100" fi -CPPFLAGS="$TARGET_CPPFLAGS" -LDFLAGS="$TARGET_LDFLAGS" +LIBS="" # Defined in aclocal.m4. grub_PROG_TARGET_CC @@ -457,13 +511,12 @@ if test "x$TARGET_APPLE_CC" != x1 ; then grub_PROG_OBJCOPY_ABSOLUTE fi grub_PROG_LD_BUILD_ID_NONE -grub_ASM_USCORE if test "x$target_cpu" = xi386; then - if test ! -z "$TARGET_IMG_LDSCRIPT"; then - # Check symbols provided by linker script. - CFLAGS="$TARGET_CFLAGS -nostdlib $TARGET_IMG_LDFLAGS_AC -Wl,-Ttext,8000,--defsym,___main=0x8100" - fi - if test "x$TARGET_APPLE_CC" != x1 ; then + if test "$platform" != emu && test "x$TARGET_APPLE_CC" != x1 ; then + if test ! -z "$TARGET_IMG_LDSCRIPT"; then + # Check symbols provided by linker script. + CFLAGS="$TARGET_CFLAGS -nostdlib ${TARGET_IMG_LDFLAGS_AC}8000 -Wl,--defsym,___main=0x8100" + fi grub_CHECK_BSS_START_SYMBOL grub_CHECK_END_SYMBOL fi @@ -508,6 +561,21 @@ enable_efiemu=no fi AC_SUBST([enable_efiemu]) +if test "$platform" != emu; then +AC_CACHE_CHECK([whether -nostdinc -isystem works], [grub_cv_cc_isystem], [ + SAVED_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$TARGET_CPPFLAGS -nostdinc -isystem `$TARGET_CC -print-file-name=include`" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include +int va_arg_func (int fixed, va_list args);]], [[]])], + [grub_cv_cc_isystem=yes], + [grub_cv_cc_isystem=no]) + CPPFLAGS="$SAVED_CPPFLAGS" +]) + +if test x"$grub_cv_cc_isystem" = xyes ; then + TARGET_CPPFLAGS="$TARGET_CPPFLAGS -nostdinc -isystem `$TARGET_CC -print-file-name=include`" +fi +fi # Restore the flags. CC="$tmp_CC" @@ -531,10 +599,18 @@ AC_ARG_ENABLE([grub-emu-usb], [AS_HELP_STRING([--enable-grub-emu-usb], [build and install the `grub-emu' debugging utility with USB support (default=guessed)])]) +AC_ARG_ENABLE([grub-emu-sdl], + [AS_HELP_STRING([--enable-grub-emu-sdl], + [build and install the `grub-emu' debugging utility with SDL support (default=guessed)])]) + AC_ARG_ENABLE([grub-emu-pci], [AS_HELP_STRING([--enable-grub-emu-pci], [build and install the `grub-emu' debugging utility with PCI support (potentially dangerous) (default=no)])]) +AC_ARG_ENABLE([grub-emu-modules], + [AS_HELP_STRING([--enable-grub-emu-modules], + [Support module loading in `grub-emu' debugging utility (default=no)])]) + if test "$platform" = emu; then missing_ncurses= [# Check for curses libraries.] @@ -553,6 +629,19 @@ if test x"$missing_ncurses" = xtrue ; then AC_MSG_ERROR([grub-emu can't be compiled without ncurses]) fi +if test x"$enable_grub_emu_modules" = xyes ; then + TARGET_NO_MODULES=no +else + TARGET_NO_MODULES=yes +fi +AC_SUBST(TARGET_NO_MODULES) + +if test "$TARGET_NO_MODULES" = yes ; then + # Do not convert modules, otherwise linkage may fail (Cygwin only). + # FIXME: Should be checked above before TARGET_OBJ2ELF is set first. + TARGET_OBJ2ELF= +fi + if test x"$enable_grub_emu_usb" = xno ; then grub_emu_usb_excuse="explicitly disabled" fi @@ -581,6 +670,31 @@ else enable_grub_emu_usb=no fi +if test x"$enable_grub_emu_sdl" = xno ; then + grub_emu_sdl_excuse="explicitely disabled" +fi +[if [ x"$grub_emu_sdl_excuse" = x ]; then + # Check for libSDL libraries.] +AC_CHECK_LIB([SDL], [SDL_Init], [LIBSDL="-lSDL"], + [grub_emu_sdl_excuse=["libSDL libraries are required to build \`grub-emu' with SDL support"]]) + AC_SUBST([LIBSDL]) +[fi] + +[if [ x"$grub_emu_sdl_excuse" = x ]; then + # Check for headers.] + AC_CHECK_HEADERS([SDL/SDL.h], [], + [grub_emu_sdl_excuse=["libSDL header file is required to build \`grub-emu' with SDL support"]]) +[fi] + +if test x"enable_grub_emu_sdl" = xyes && test x"$grub_emu_sdl_excuse" != x ; then + AC_MSG_ERROR([SDL support for grub-emu was explicitely requested but can't be compiled]) +fi +if test x"$grub_emu_sdl_excuse" = x ; then +enable_grub_emu_sdl=yes +else +enable_grub_emu_sdl=no +fi + if test x"$enable_grub_emu_pci" != xyes ; then grub_emu_pci_excuse="not enabled" fi @@ -604,9 +718,11 @@ fi if test x"$grub_emu_pci_excuse" = x ; then enable_grub_emu_pci=yes else + enable_grub_emu_pci=no fi +AC_SUBST([enable_grub_emu_sdl]) AC_SUBST([enable_grub_emu_usb]) AC_SUBST([enable_grub_emu_pci]) fi @@ -640,6 +756,16 @@ if test x"$grub_mkfont_excuse" = x ; then freetype_cflags=`freetype-config --cflags` freetype_libs=`freetype-config --libs` fi + +if test x"$grub_mkfont_excuse" = x ; then + # Check for freetype libraries. + SAVED_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $freetype_cflags" + AC_CHECK_HEADERS([ft2build.h], [], + [grub_mkfont_excuse=["need freetype2 headers"]]) + CPPFLAGS="$SAVED_CPPFLAGS" +fi + if test x"$enable_grub_mkfont" = xyes && test x"$grub_mkfont_excuse" != x ; then AC_MSG_ERROR([grub-mkfont was explicitly requested but can't be compiled]) fi @@ -683,11 +809,21 @@ echo USB support for grub-emu: Yes else echo USB support for grub-emu: No "($grub_emu_usb_excuse)" fi +if [ x"$grub_emu_sdl_excuse" = x ]; then +echo SDL support for grub-emu: Yes +else +echo SDL support for grub-emu: No "($grub_emu_sdl_excuse)" +fi if [ x"$grub_emu_pci_excuse" = x ]; then echo PCI support for grub-emu: Yes else echo PCI support for grub-emu: No "($grub_emu_pci_excuse)" fi +if [ x"$TARGET_NO_MODULES" = xno ]; then +echo Module support for grub-emu: Yes +else +echo Module support for grub-emu: No +fi fi if [ x"$enable_mm_debug" = xyes ]; then echo With memory debugging: Yes diff --git a/disk/efi/efidisk.c b/disk/efi/efidisk.c index 58300a0d2..f9c6f3153 100644 --- a/disk/efi/efidisk.c +++ b/disk/efi/efidisk.c @@ -825,7 +825,7 @@ grub_efidisk_get_device_name (grub_efi_handle_t *handle) if (! disk) return 1; - if (disk->id == GRUB_DISK_DEVICE_EFIDISK_ID) + if (disk->dev->id == GRUB_DISK_DEVICE_EFIDISK_ID) { struct grub_efidisk_data *d; diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index 682474b1a..94d0e3708 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -307,8 +307,17 @@ grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, while (size) { grub_size_t len; + grub_size_t cdoff = 0; len = get_safe_sectors (sector, data->sectors); + + if (data->flags & GRUB_BIOSDISK_FLAG_CDROM) + { + cdoff = (sector & 3) << GRUB_DISK_SECTOR_BITS; + len = ALIGN_UP (sector + len, 4) - (sector & ~3); + sector &= ~3; + } + if (len > size) len = size; @@ -316,7 +325,7 @@ grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, GRUB_MEMORY_MACHINE_SCRATCH_SEG)) return grub_errno; - grub_memcpy (buf, (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, + grub_memcpy (buf, (void *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + cdoff), len << GRUB_DISK_SECTOR_BITS); buf += len << GRUB_DISK_SECTOR_BITS; sector += len; @@ -332,6 +341,9 @@ grub_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector, { struct grub_biosdisk_data *data = disk->data; + if (data->flags & GRUB_BIOSDISK_FLAG_CDROM) + return grub_error (GRUB_ERR_IO, "can't write to CDROM"); + while (size) { grub_size_t len; diff --git a/disk/ieee1275/ofdisk.c b/disk/ieee1275/ofdisk.c index c8c4d1a4e..e5a4a67fa 100644 --- a/disk/ieee1275/ofdisk.c +++ b/disk/ieee1275/ofdisk.c @@ -118,7 +118,7 @@ grub_ofdisk_iterate (int (*hook) (const char *name)) static char * compute_dev_path (const char *name) { - char *devpath = grub_malloc (grub_strlen (name) + 2); + char *devpath = grub_malloc (grub_strlen (name) + 3); char *p, c; if (!devpath) @@ -172,16 +172,6 @@ grub_ofdisk_open (const char *name, grub_disk_t disk) grub_dprintf ("disk", "Opening `%s'.\n", op->devpath); - grub_ieee1275_open (op->devpath, &dev_ihandle); - if (! dev_ihandle) - { - grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); - goto fail; - } - - grub_dprintf ("disk", "Opened `%s' as handle %p.\n", op->devpath, - (void *) (unsigned long) dev_ihandle); - if (grub_ieee1275_finddevice (op->devpath, &dev)) { grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't read device properties"); @@ -201,6 +191,16 @@ grub_ofdisk_open (const char *name, grub_disk_t disk) goto fail; } + grub_ieee1275_open (op->devpath, &dev_ihandle); + if (! dev_ihandle) + { + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); + goto fail; + } + + grub_dprintf ("disk", "Opened `%s' as handle %p.\n", op->devpath, + (void *) (unsigned long) dev_ihandle); + /* XXX: There is no property to read the number of blocks. There should be a property `#blocks', but it is not there. Perhaps it is possible to use seek for this. */ @@ -234,21 +234,17 @@ grub_ofdisk_read (grub_disk_t disk, grub_disk_addr_t sector, grub_ssize_t status, actual; unsigned long long pos; - grub_dprintf ("disk", - "Reading handle %p: sector 0x%llx, size 0x%lx, buf %p.\n", - (void *) disk->data, (long long) sector, (long) size, buf); - pos = sector * 512UL; grub_ieee1275_seek ((grub_ieee1275_ihandle_t) (unsigned long) disk->data, - (int) (pos >> 32), (int) pos & 0xFFFFFFFFUL, &status); + pos, &status); if (status < 0) return grub_error (GRUB_ERR_READ_ERROR, "seek error, can't seek block %llu", (long long) sector); grub_ieee1275_read ((grub_ieee1275_ihandle_t) (unsigned long) disk->data, buf, size * 512UL, &actual); - if (actual != actual) + if (actual != (grub_ssize_t) (size * 512UL)) return grub_error (GRUB_ERR_READ_ERROR, "read error on block: %llu", (long long) sector); diff --git a/disk/loopback.c b/disk/loopback.c index a8b7cf5d7..1b525e05f 100644 --- a/disk/loopback.c +++ b/disk/loopback.c @@ -242,7 +242,7 @@ static struct grub_disk_dev grub_loopback_dev = static grub_extcmd_t cmd; -GRUB_MOD_INIT(loop) +GRUB_MOD_INIT(loopback) { cmd = grub_register_extcmd ("loopback", grub_cmd_loopback, GRUB_COMMAND_FLAG_BOTH, @@ -251,7 +251,7 @@ GRUB_MOD_INIT(loop) grub_disk_dev_register (&grub_loopback_dev); } -GRUB_MOD_FINI(loop) +GRUB_MOD_FINI(loopback) { grub_unregister_extcmd (cmd); grub_disk_dev_unregister (&grub_loopback_dev); diff --git a/disk/lvm.c b/disk/lvm.c index 2c54ca3b3..c2ae82df2 100644 --- a/disk/lvm.c +++ b/disk/lvm.c @@ -24,6 +24,10 @@ #include #include +#ifdef GRUB_UTIL +#include +#endif + static struct grub_lvm_vg *vg_list; static int lv_count; @@ -68,6 +72,9 @@ grub_lvm_memberlist (grub_disk_t disk) if (lv->vg->pvs) for (pv = lv->vg->pvs; pv; pv = pv->next) { + if (!pv->disk) + grub_util_error ("Couldn't find PV %s. Check your device.map", + pv->name); tmp = grub_malloc (sizeof (*tmp)); tmp->disk = pv->disk; tmp->next = list; diff --git a/disk/memdisk.c b/disk/memdisk.c index 4a0470837..2e8885020 100644 --- a/disk/memdisk.c +++ b/disk/memdisk.c @@ -23,7 +23,6 @@ #include #include #include -#include static char *memdisk_addr; static grub_off_t memdisk_size = 0; diff --git a/disk/scsi.c b/disk/scsi.c index 6f3233b29..eba237287 100644 --- a/disk/scsi.c +++ b/disk/scsi.c @@ -208,12 +208,14 @@ grub_scsi_iterate (int (*hook) (const char *name)) for (i = 0; i < luns; i++) { char *sname; + int ret; sname = grub_xasprintf ("%s%c", name, 'a' + i); if (!sname) return 1; - if (hook (sname)) - return 1; + ret = hook (sname); grub_free (sname); + if (ret) + return 1; } return 0; } diff --git a/docs/grub.texi b/docs/grub.texi index 4fa44462e..f8a9bc414 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -1126,6 +1126,7 @@ you forget a command, you can run the command @command{help} * insmod:: Insert a module * keystatus:: Check key modifier status * ls:: List devices or files +* play:: Play a tune * reboot:: Reboot your computer * set:: Set an environment variable * unset:: Unset an environment variable @@ -1364,6 +1365,24 @@ name syntax}), then list the contents of that directory. @end deffn +@node play +@subsection play + +@deffn Command play file | tempo [pitch1 duration1] [pitch2 duration2] ... +Plays a tune + +If the argument is a file name (@pxref{File name syntax}), play the tune +recorded in it. The file format is first the tempo as an unsigned 32bit +little-endian number, then pairs of unsigned 16bit little-endian numbers for +pitch and duration pairs. + +If the arguments are a series of numbers, play the inline tune. + +The tempo is the base for all note durations. 60 gives a 1-second base, 120 +gives a half-second base, etc. Pitches are Hz. +@end deffn + + @node reboot @subsection reboot diff --git a/efiemu/main.c b/efiemu/main.c index 9480bfc4d..8a8a508fa 100644 --- a/efiemu/main.c +++ b/efiemu/main.c @@ -265,7 +265,7 @@ grub_efiemu_autocore (void) grub_free (filename); if (err) return err; -#ifndef GRUB_UTIL +#ifndef GRUB_MACHINE_EMU err = grub_machine_efiemu_init_tables (); if (err) return err; @@ -313,7 +313,7 @@ grub_cmd_efiemu_load (grub_command_t cmd __attribute__ ((unused)), err = grub_efiemu_load_file (args[0]); if (err) return err; -#ifndef GRUB_UTIL +#ifndef GRUB_MACHINE_EMU err = grub_machine_efiemu_init_tables (); if (err) return err; diff --git a/efiemu/mm.c b/efiemu/mm.c index 6099a14ee..4b293606f 100644 --- a/efiemu/mm.c +++ b/efiemu/mm.c @@ -281,7 +281,7 @@ grub_efiemu_mmap_init (void) // the place for memory used by efiemu itself mmap_reserved_size = GRUB_EFI_MAX_MEMORY_TYPE + 1; -#ifndef GRUB_UTIL +#ifndef GRUB_MACHINE_EMU grub_machine_mmap_iterate (bounds_hook); #endif @@ -394,7 +394,7 @@ grub_efiemu_mmap_fill (void) } } -#ifndef GRUB_UTIL +#ifndef GRUB_MACHINE_EMU grub_machine_mmap_iterate (fill_hook); #endif diff --git a/font/font.c b/font/font.c index 587e418cc..9a89cadc5 100644 --- a/font/font.c +++ b/font/font.c @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef USE_ASCII_FAILBACK #include "ascii.h" @@ -89,39 +90,25 @@ struct font_file_section int eof; }; -/* Font file format constants. */ -static const char pff2_magic[4] = { 'P', 'F', 'F', '2' }; -static const char section_names_file[4] = { 'F', 'I', 'L', 'E' }; -static const char section_names_font_name[4] = { 'N', 'A', 'M', 'E' }; -static const char section_names_point_size[4] = { 'P', 'T', 'S', 'Z' }; -static const char section_names_weight[4] = { 'W', 'E', 'I', 'G' }; -static const char section_names_max_char_width[4] = { 'M', 'A', 'X', 'W' }; -static const char section_names_max_char_height[4] = { 'M', 'A', 'X', 'H' }; -static const char section_names_ascent[4] = { 'A', 'S', 'C', 'E' }; -static const char section_names_descent[4] = { 'D', 'E', 'S', 'C' }; -static const char section_names_char_index[4] = { 'C', 'H', 'I', 'X' }; -static const char section_names_data[4] = { 'D', 'A', 'T', 'A' }; - /* Replace unknown glyphs with a rounded question mark. */ -static grub_uint8_t unknown_glyph_bitmap[] = -{ - /* 76543210 */ - 0x7C, /* ooooo */ - 0x82, /* o o */ - 0xBA, /* o ooo o */ - 0xAA, /* o o o o */ - 0xAA, /* o o o o */ - 0x8A, /* o o o */ - 0x9A, /* o oo o */ - 0x92, /* o o o */ - 0x92, /* o o o */ - 0x92, /* o o o */ - 0x92, /* o o o */ - 0x82, /* o o */ - 0x92, /* o o o */ - 0x82, /* o o */ - 0x7C, /* ooooo */ - 0x00 /* */ +static grub_uint8_t unknown_glyph_bitmap[] = { + /* 76543210 */ + 0x7C, /* ooooo */ + 0x82, /* o o */ + 0xBA, /* o ooo o */ + 0xAA, /* o o o o */ + 0xAA, /* o o o o */ + 0x8A, /* o o o */ + 0x9A, /* o oo o */ + 0x92, /* o o o */ + 0x92, /* o o o */ + 0x92, /* o o o */ + 0x92, /* o o o */ + 0x82, /* o o */ + 0x92, /* o o o */ + 0x82, /* o o */ + 0x7C, /* ooooo */ + 0x00 /* */ }; /* The "unknown glyph" glyph, used as a last resort. */ @@ -142,7 +129,7 @@ static struct grub_font_glyph *ascii_font_glyph[0x80]; static struct grub_font_glyph * ascii_glyph_lookup (grub_uint32_t code) { -#ifdef USE_ASCII_FAILBACK +#ifdef USE_ASCII_FAILBACK static int ascii_failback_initialized = 0; if (code >= 0x80) @@ -152,14 +139,14 @@ ascii_glyph_lookup (grub_uint32_t code) { int current; for (current = 0; current < 0x80; current++) - { - ascii_font_glyph[current] = grub_malloc(sizeof(struct grub_font_glyph) - + ASCII_BITMAP_SIZE); + { + ascii_font_glyph[current] = + grub_malloc (sizeof (struct grub_font_glyph) + ASCII_BITMAP_SIZE); - ascii_font_glyph[current]->width = 8; - ascii_font_glyph[current]->height = 16; - ascii_font_glyph[current]->offset_x = 0; - ascii_font_glyph[current]->offset_y = -2; + ascii_font_glyph[current]->width = 8; + ascii_font_glyph[current]->height = 16; + ascii_font_glyph[current]->offset_x = 0; + ascii_font_glyph[current]->offset_y = -2; ascii_font_glyph[current]->device_width = 8; grub_memcpy (ascii_font_glyph[current]->bitmap, @@ -185,9 +172,9 @@ grub_font_loader_init (void) return; /* Make glyph for unknown glyph. */ - unknown_glyph = grub_malloc(sizeof(struct grub_font_glyph) - + sizeof(unknown_glyph_bitmap)); - if (! unknown_glyph) + unknown_glyph = grub_malloc (sizeof (struct grub_font_glyph) + + sizeof (unknown_glyph_bitmap)); + if (!unknown_glyph) return; unknown_glyph->width = 8; @@ -195,13 +182,13 @@ grub_font_loader_init (void) unknown_glyph->offset_x = 0; unknown_glyph->offset_y = -3; unknown_glyph->device_width = 8; - grub_memcpy(unknown_glyph->bitmap, - unknown_glyph_bitmap, sizeof(unknown_glyph_bitmap)); + grub_memcpy (unknown_glyph->bitmap, + unknown_glyph_bitmap, sizeof (unknown_glyph_bitmap)); /* Initialize the null font. */ font_init (&null_font); null_font.name = ""; - null_font.ascent = unknown_glyph->height-3; + null_font.ascent = unknown_glyph->height - 3; null_font.descent = 3; null_font.max_char_width = unknown_glyph->width; null_font.max_char_height = unknown_glyph->height; @@ -259,7 +246,7 @@ open_section (grub_file_t file, struct font_file_section *section) else if (retval < 0) { grub_error (GRUB_ERR_BAD_FONT, - "font format error: can't read section name"); + "font format error: can't read section name"); return 1; } @@ -274,7 +261,7 @@ open_section (grub_file_t file, struct font_file_section *section) else if (retval < 0) { grub_error (GRUB_ERR_BAD_FONT, - "font format error: can't read section length"); + "font format error: can't read section length"); return 1; } @@ -295,22 +282,22 @@ open_section (grub_file_t file, struct font_file_section *section) grub_errno is set appropriately). */ static int load_font_index (grub_file_t file, grub_uint32_t sect_length, struct - grub_font *font) + grub_font *font) { unsigned i; grub_uint32_t last_code; #if FONT_DEBUG >= 2 - grub_printf("load_font_index(sect_length=%d)\n", sect_length); + grub_printf ("load_font_index(sect_length=%d)\n", sect_length); #endif /* Sanity check: ensure section length is divisible by the entry size. */ if ((sect_length % FONT_CHAR_INDEX_ENTRY_SIZE) != 0) { grub_error (GRUB_ERR_BAD_FONT, - "font file format error: character index length %d " - "is not a multiple of the entry size %d", - sect_length, FONT_CHAR_INDEX_ENTRY_SIZE); + "font file format error: character index length %d " + "is not a multiple of the entry size %d", + sect_length, FONT_CHAR_INDEX_ENTRY_SIZE); return 1; } @@ -319,11 +306,11 @@ load_font_index (grub_file_t file, grub_uint32_t sect_length, struct /* Allocate the character index array. */ font->char_index = grub_malloc (font->num_chars - * sizeof (struct char_index_entry)); - if (! font->char_index) + * sizeof (struct char_index_entry)); + if (!font->char_index) return 1; font->bmp_idx = grub_malloc (0x10000 * sizeof (grub_uint16_t)); - if (! font->bmp_idx) + if (!font->bmp_idx) { grub_free (font->char_index); return 1; @@ -332,7 +319,7 @@ load_font_index (grub_file_t file, grub_uint32_t sect_length, struct #if FONT_DEBUG >= 2 - grub_printf("num_chars=%d)\n", font->num_chars); + grub_printf ("num_chars=%d)\n", font->num_chars); #endif last_code = 0; @@ -344,17 +331,17 @@ load_font_index (grub_file_t file, grub_uint32_t sect_length, struct /* Read code point value; convert to native byte order. */ if (grub_file_read (file, &entry->code, 4) != 4) - return 1; + return 1; entry->code = grub_be_to_cpu32 (entry->code); /* Verify that characters are in ascending order. */ if (i != 0 && entry->code <= last_code) - { - grub_error (GRUB_ERR_BAD_FONT, - "font characters not in ascending order: %u <= %u", - entry->code, last_code); - return 1; - } + { + grub_error (GRUB_ERR_BAD_FONT, + "font characters not in ascending order: %u <= %u", + entry->code, last_code); + return 1; + } if (entry->code < 0x10000) font->bmp_idx[entry->code] = i; @@ -363,11 +350,11 @@ load_font_index (grub_file_t file, grub_uint32_t sect_length, struct /* Read storage flags byte. */ if (grub_file_read (file, &entry->storage_flags, 1) != 1) - return 1; + return 1; /* Read glyph data offset; convert to native byte order. */ if (grub_file_read (file, &entry->offset, 4) != 4) - return 1; + return 1; entry->offset = grub_be_to_cpu32 (entry->offset); /* No glyph loaded. Will be loaded on demand and cached thereafter. */ @@ -376,7 +363,7 @@ load_font_index (grub_file_t file, grub_uint32_t sect_length, struct #if FONT_DEBUG >= 5 /* Print the 1st 10 characters. */ if (i < 10) - grub_printf("c=%d o=%d\n", entry->code, entry->offset); + grub_printf ("c=%d o=%d\n", entry->code, entry->offset); #endif } @@ -392,7 +379,7 @@ read_section_as_string (struct font_file_section *section) grub_ssize_t ret; str = grub_malloc (section->length + 1); - if (! str) + if (!str) return 0; ret = grub_file_read (section->file, str, section->length); @@ -410,18 +397,18 @@ read_section_as_string (struct font_file_section *section) which is stored into *VALUE. Returns 0 upon success, nonzero upon failure. */ static int -read_section_as_short (struct font_file_section *section, grub_int16_t *value) +read_section_as_short (struct font_file_section *section, + grub_int16_t * value) { grub_uint16_t raw_value; if (section->length != 2) { grub_error (GRUB_ERR_BAD_FONT, - "font file format error: section %c%c%c%c length " - "is %d but should be 2", - section->name[0], section->name[1], - section->name[2], section->name[3], - section->length); + "font file format error: section %c%c%c%c length " + "is %d but should be 2", + section->name[0], section->name[1], + section->name[2], section->name[3], section->length); return 1; } if (grub_file_read (section->file, &raw_value, 2) != 2) @@ -442,7 +429,7 @@ grub_font_load (const char *filename) grub_font_t font = 0; #if FONT_DEBUG >= 1 - grub_printf("add_font(%s)\n", filename); + grub_printf ("add_font(%s)\n", filename); #endif file = grub_buffile_open (filename, 1024); @@ -450,7 +437,7 @@ grub_font_load (const char *filename) goto fail; #if FONT_DEBUG >= 3 - grub_printf("file opened\n"); + grub_printf ("file opened\n"); #endif /* Read the FILE section. It indicates the file format. */ @@ -458,144 +445,166 @@ grub_font_load (const char *filename) goto fail; #if FONT_DEBUG >= 3 - grub_printf("opened FILE section\n"); + grub_printf ("opened FILE section\n"); #endif - if (grub_memcmp (section.name, section_names_file, 4) != 0) + if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FILE, + sizeof (FONT_FORMAT_SECTION_NAMES_FILE) - 1) != 0) { grub_error (GRUB_ERR_BAD_FONT, - "font file format error: 1st section must be FILE"); + "font file format error: 1st section must be FILE"); goto fail; } #if FONT_DEBUG >= 3 - grub_printf("section name ok\n"); + grub_printf ("section name ok\n"); #endif if (section.length != 4) { grub_error (GRUB_ERR_BAD_FONT, - "font file format error (file type ID length is %d " - "but should be 4)", section.length); + "font file format error (file type ID length is %d " + "but should be 4)", section.length); goto fail; } #if FONT_DEBUG >= 3 - grub_printf("section length ok\n"); + grub_printf ("section length ok\n"); #endif /* Check the file format type code. */ if (grub_file_read (file, magic, 4) != 4) goto fail; #if FONT_DEBUG >= 3 - grub_printf("read magic ok\n"); + grub_printf ("read magic ok\n"); #endif - if (grub_memcmp (magic, pff2_magic, 4) != 0) + if (grub_memcmp (magic, FONT_FORMAT_PFF2_MAGIC, 4) != 0) { grub_error (GRUB_ERR_BAD_FONT, "invalid font magic %x %x %x %x", - magic[0], magic[1], magic[2], magic[3]); + magic[0], magic[1], magic[2], magic[3]); goto fail; } #if FONT_DEBUG >= 3 - grub_printf("compare magic ok\n"); + grub_printf ("compare magic ok\n"); #endif /* Allocate the font object. */ font = (grub_font_t) grub_malloc (sizeof (struct grub_font)); - if (! font) + if (!font) goto fail; font_init (font); font->file = file; #if FONT_DEBUG >= 3 - grub_printf("allocate font ok; loading font info\n"); + grub_printf ("allocate font ok; loading font info\n"); #endif /* Load the font information. */ while (1) { if (open_section (file, §ion) != 0) - { - if (section.eof) - break; /* Done reading the font file. */ - else - goto fail; - } + { + if (section.eof) + break; /* Done reading the font file. */ + else + goto fail; + } #if FONT_DEBUG >= 2 - grub_printf("opened section %c%c%c%c ok\n", - section.name[0], section.name[1], - section.name[2], section.name[3]); + grub_printf ("opened section %c%c%c%c ok\n", + section.name[0], section.name[1], + section.name[2], section.name[3]); #endif - if (grub_memcmp (section.name, section_names_font_name, 4) == 0) - { - font->name = read_section_as_string (§ion); - if (!font->name) - goto fail; - } - else if (grub_memcmp (section.name, section_names_point_size, 4) == 0) - { - if (read_section_as_short (§ion, &font->point_size) != 0) - goto fail; - } - else if (grub_memcmp (section.name, section_names_weight, 4) == 0) - { - char *wt; - wt = read_section_as_string (§ion); - if (!wt) - continue; - /* Convert the weight string 'normal' or 'bold' into a number. */ - if (grub_strcmp (wt, "normal") == 0) - font->weight = FONT_WEIGHT_NORMAL; - else if (grub_strcmp (wt, "bold") == 0) - font->weight = FONT_WEIGHT_BOLD; - grub_free (wt); - } - else if (grub_memcmp (section.name, section_names_max_char_width, 4) == 0) - { - if (read_section_as_short (§ion, &font->max_char_width) != 0) - goto fail; - } - else if (grub_memcmp (section.name, section_names_max_char_height, 4) == 0) - { - if (read_section_as_short (§ion, &font->max_char_height) != 0) - goto fail; - } - else if (grub_memcmp (section.name, section_names_ascent, 4) == 0) - { - if (read_section_as_short (§ion, &font->ascent) != 0) - goto fail; - } - else if (grub_memcmp (section.name, section_names_descent, 4) == 0) - { - if (read_section_as_short (§ion, &font->descent) != 0) - goto fail; - } - else if (grub_memcmp (section.name, section_names_char_index, 4) == 0) - { - if (load_font_index (file, section.length, font) != 0) - goto fail; - } - else if (grub_memcmp (section.name, section_names_data, 4) == 0) - { - /* When the DATA section marker is reached, we stop reading. */ - break; - } + if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FONT_NAME, + sizeof (FONT_FORMAT_SECTION_NAMES_FONT_NAME) - 1) == 0) + { + font->name = read_section_as_string (§ion); + if (!font->name) + goto fail; + } + else if (grub_memcmp (section.name, + FONT_FORMAT_SECTION_NAMES_POINT_SIZE, + sizeof (FONT_FORMAT_SECTION_NAMES_POINT_SIZE) - + 1) == 0) + { + if (read_section_as_short (§ion, &font->point_size) != 0) + goto fail; + } + else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_WEIGHT, + sizeof (FONT_FORMAT_SECTION_NAMES_WEIGHT) - 1) + == 0) + { + char *wt; + wt = read_section_as_string (§ion); + if (!wt) + continue; + /* Convert the weight string 'normal' or 'bold' into a number. */ + if (grub_strcmp (wt, "normal") == 0) + font->weight = FONT_WEIGHT_NORMAL; + else if (grub_strcmp (wt, "bold") == 0) + font->weight = FONT_WEIGHT_BOLD; + grub_free (wt); + } + else if (grub_memcmp (section.name, + FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH, + sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH) + - 1) == 0) + { + if (read_section_as_short (§ion, &font->max_char_width) != 0) + goto fail; + } + else if (grub_memcmp (section.name, + FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT, + sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT) + - 1) == 0) + { + if (read_section_as_short (§ion, &font->max_char_height) != 0) + goto fail; + } + else if (grub_memcmp (section.name, + FONT_FORMAT_SECTION_NAMES_ASCENT, + sizeof (FONT_FORMAT_SECTION_NAMES_ASCENT) - 1) + == 0) + { + if (read_section_as_short (§ion, &font->ascent) != 0) + goto fail; + } + else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_DESCENT, + sizeof (FONT_FORMAT_SECTION_NAMES_DESCENT) - 1) + == 0) + { + if (read_section_as_short (§ion, &font->descent) != 0) + goto fail; + } + else if (grub_memcmp (section.name, + FONT_FORMAT_SECTION_NAMES_CHAR_INDEX, + sizeof (FONT_FORMAT_SECTION_NAMES_CHAR_INDEX) - + 1) == 0) + { + if (load_font_index (file, section.length, font) != 0) + goto fail; + } + else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_DATA, + sizeof (FONT_FORMAT_SECTION_NAMES_DATA) - 1) == 0) + { + /* When the DATA section marker is reached, we stop reading. */ + break; + } else - { - /* Unhandled section type, simply skip past it. */ + { + /* Unhandled section type, simply skip past it. */ #if FONT_DEBUG >= 3 - grub_printf("Unhandled section type, skipping.\n"); + grub_printf ("Unhandled section type, skipping.\n"); #endif - grub_off_t section_end = grub_file_tell (file) + section.length; - if ((int) grub_file_seek (file, section_end) == -1) - goto fail; - } + grub_off_t section_end = grub_file_tell (file) + section.length; + if ((int) grub_file_seek (file, section_end) == -1) + goto fail; + } } - if (! font->name) + if (!font->name) { grub_printf ("Note: Font has no name.\n"); font->name = grub_strdup ("Unknown"); @@ -603,22 +612,19 @@ grub_font_load (const char *filename) #if FONT_DEBUG >= 1 grub_printf ("Loaded font `%s'.\n" - "Ascent=%d Descent=%d MaxW=%d MaxH=%d Number of characters=%d.\n", - font->name, - font->ascent, font->descent, - font->max_char_width, font->max_char_height, - font->num_chars); + "Ascent=%d Descent=%d MaxW=%d MaxH=%d Number of characters=%d.\n", + font->name, + font->ascent, font->descent, + font->max_char_width, font->max_char_height, font->num_chars); #endif if (font->max_char_width == 0 || font->max_char_height == 0 || font->num_chars == 0 - || font->char_index == 0 - || font->ascent == 0 - || font->descent == 0) + || font->char_index == 0 || font->ascent == 0 || font->descent == 0) { grub_error (GRUB_ERR_BAD_FONT, - "invalid font file: missing some required data"); + "invalid font file: missing some required data"); goto fail; } @@ -665,7 +671,7 @@ find_glyph (const grub_font_t font, grub_uint32_t code) table = font->char_index; /* Use BMP index if possible. */ - if (code < 0x10000) + if (code < 0x10000 && font->bmp_idx) { if (font->bmp_idx[code] == 0xffff) return 0; @@ -676,18 +682,18 @@ find_glyph (const grub_font_t font, grub_uint32_t code) lo = 0; hi = font->num_chars - 1; - if (! table) + if (!table) return 0; while (lo <= hi) { mid = lo + (hi - lo) / 2; if (code < table[mid].code) - hi = mid - 1; + hi = mid - 1; else if (code > table[mid].code) - lo = mid + 1; + lo = mid + 1; else - return &table[mid]; + return &table[mid]; } return 0; @@ -713,12 +719,12 @@ grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code) int len; if (index_entry->glyph) - /* Return cached glyph. */ - return index_entry->glyph; + /* Return cached glyph. */ + return index_entry->glyph; - if (! font->file) - /* No open file, can't load any glyphs. */ - return 0; + if (!font->file) + /* No open file, can't load any glyphs. */ + return 0; /* Make sure we can find glyphs for error messages. Push active error message to error stack and reset error message. */ @@ -727,23 +733,23 @@ grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code) grub_file_seek (font->file, index_entry->offset); /* Read the glyph width, height, and baseline. */ - if (read_be_uint16(font->file, &width) != 0 - || read_be_uint16(font->file, &height) != 0 - || read_be_int16(font->file, &xoff) != 0 - || read_be_int16(font->file, &yoff) != 0 - || read_be_int16(font->file, &dwidth) != 0) - { - remove_font (font); - return 0; - } + if (read_be_uint16 (font->file, &width) != 0 + || read_be_uint16 (font->file, &height) != 0 + || read_be_int16 (font->file, &xoff) != 0 + || read_be_int16 (font->file, &yoff) != 0 + || read_be_int16 (font->file, &dwidth) != 0) + { + remove_font (font); + return 0; + } len = (width * height + 7) / 8; glyph = grub_malloc (sizeof (struct grub_font_glyph) + len); - if (! glyph) - { - remove_font (font); - return 0; - } + if (!glyph) + { + remove_font (font); + return 0; + } glyph->font = font; glyph->width = width; @@ -754,13 +760,13 @@ grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code) /* Don't try to read empty bitmaps (e.g., space characters). */ if (len != 0) - { - if (grub_file_read (font->file, glyph->bitmap, len) != len) - { - remove_font (font); - return 0; - } - } + { + if (grub_file_read (font->file, glyph->bitmap, len) != len) + { + remove_font (font); + return 0; + } + } /* Restore old error message. */ grub_error_pop (); @@ -784,7 +790,7 @@ free_font (grub_font_t font) if (font) { if (font->file) - grub_file_close (font->file); + grub_file_close (font->file); grub_free (font->name); grub_free (font->family); grub_free (font->char_index); @@ -801,7 +807,7 @@ register_font (grub_font_t font) struct grub_font_node *node = 0; node = grub_malloc (sizeof (struct grub_font_node)); - if (! node) + if (!node) return 1; node->value = font; @@ -819,18 +825,17 @@ remove_font (grub_font_t font) struct grub_font_node **nextp, *cur; for (nextp = &grub_font_list, cur = *nextp; - cur; - nextp = &cur->next, cur = cur->next) + cur; nextp = &cur->next, cur = cur->next) { if (cur->value == font) - { - *nextp = cur->next; + { + *nextp = cur->next; - /* Free the node, but not the font itself. */ - grub_free (cur); + /* Free the node, but not the font itself. */ + grub_free (cur); - return; - } + return; + } } } @@ -847,7 +852,7 @@ grub_font_get (const char *font_name) { grub_font_t font = node->value; if (grub_strcmp (font->name, font_name) == 0) - return font; + return font; } /* If no font by that name is found, return the first font in the list @@ -859,7 +864,7 @@ grub_font_get (const char *font_name) return &null_font; } -/* Get the full name of the font. For instance, "Helvetica Bold 12". */ +/* Get the full name of the font. */ const char * grub_font_get_name (grub_font_t font) { @@ -924,7 +929,7 @@ grub_font_get_string_width (grub_font_t font, const char *str) const grub_uint8_t *ptr; for (ptr = (const grub_uint8_t *) str, width = 0; - grub_utf8_to_ucs4 (&code, 1, ptr, -1, &ptr) > 0; ) + grub_utf8_to_ucs4 (&code, 1, ptr, -1, &ptr) > 0;) { glyph = grub_font_get_glyph_with_fallback (font, code); width += glyph->device_width; @@ -939,8 +944,9 @@ grub_font_get_string_width (grub_font_t font, const char *str) struct grub_font_glyph * grub_font_get_glyph (grub_font_t font, grub_uint32_t code) { - struct grub_font_glyph *glyph; - glyph = grub_font_get_glyph_internal (font, code); + struct grub_font_glyph *glyph = 0; + if (font) + glyph = grub_font_get_glyph_internal (font, code); if (glyph == 0) { glyph = ascii_glyph_lookup (code); @@ -957,7 +963,7 @@ grub_font_get_glyph (grub_font_t font, grub_uint32_t code) sizes are used so that tiny 8 point glyphs are not mixed into a string of 24 point text unless there is no other choice. */ static int -get_font_diversity(grub_font_t a, grub_font_t b) +get_font_diversity (grub_font_t a, grub_font_t b) { int d; @@ -1007,7 +1013,7 @@ grub_font_get_glyph_with_fallback (grub_font_t font, grub_uint32_t code) /* First try to get the glyph from the specified font. */ glyph = grub_font_get_glyph_internal (font, code); if (glyph) - return glyph; + return glyph; } /* Otherwise, search all loaded fonts for the glyph and use the one from @@ -1024,16 +1030,16 @@ grub_font_get_glyph_with_fallback (grub_font_t font, grub_uint32_t code) glyph = grub_font_get_glyph_internal (curfont, code); if (glyph) - { - int d; + { + int d; - d = get_font_diversity (curfont, font); - if (d < best_diversity) - { - best_diversity = d; - best_glyph = glyph; - } - } + d = get_font_diversity (curfont, font); + if (d < best_diversity) + { + best_diversity = d; + best_glyph = glyph; + } + } } if (best_glyph) @@ -1048,9 +1054,8 @@ grub_font_get_glyph_with_fallback (grub_font_t font, grub_uint32_t code) baseline of the character, while the x coordinate designates the left side location of the character. */ grub_err_t -grub_font_draw_glyph (struct grub_font_glyph *glyph, - grub_video_color_t color, - int left_x, int baseline_y) +grub_font_draw_glyph (struct grub_font_glyph * glyph, + grub_video_color_t color, int left_x, int baseline_y) { struct grub_video_bitmap glyph_bitmap; @@ -1061,8 +1066,7 @@ grub_font_draw_glyph (struct grub_font_glyph *glyph, glyph_bitmap.mode_info.width = glyph->width; glyph_bitmap.mode_info.height = glyph->height; glyph_bitmap.mode_info.mode_type = - (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) - | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP; + (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP; glyph_bitmap.mode_info.blit_format = GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED; glyph_bitmap.mode_info.bpp = 1; @@ -1077,11 +1081,11 @@ grub_font_draw_glyph (struct grub_font_glyph *glyph, glyph_bitmap.mode_info.bg_green = 0; glyph_bitmap.mode_info.bg_blue = 0; glyph_bitmap.mode_info.bg_alpha = 0; - grub_video_unmap_color(color, - &glyph_bitmap.mode_info.fg_red, - &glyph_bitmap.mode_info.fg_green, - &glyph_bitmap.mode_info.fg_blue, - &glyph_bitmap.mode_info.fg_alpha); + grub_video_unmap_color (color, + &glyph_bitmap.mode_info.fg_red, + &glyph_bitmap.mode_info.fg_green, + &glyph_bitmap.mode_info.fg_blue, + &glyph_bitmap.mode_info.fg_alpha); glyph_bitmap.data = glyph->bitmap; int bitmap_left = left_x + glyph->offset_x; @@ -1089,9 +1093,8 @@ grub_font_draw_glyph (struct grub_font_glyph *glyph, int bitmap_top = bitmap_bottom - glyph->height; return grub_video_blit_bitmap (&glyph_bitmap, GRUB_VIDEO_BLIT_BLEND, - bitmap_left, bitmap_top, - 0, 0, - glyph->width, glyph->height); + bitmap_left, bitmap_top, + 0, 0, glyph->width, glyph->height); } /* Draw a UTF-8 string of text on the current video render target. @@ -1101,8 +1104,7 @@ grub_font_draw_glyph (struct grub_font_glyph *glyph, a glyph from another loaded font may be used instead. */ grub_err_t grub_font_draw_string (const char *str, grub_font_t font, - grub_video_color_t color, - int left_x, int baseline_y) + grub_video_color_t color, int left_x, int baseline_y) { int x; struct grub_font_glyph *glyph; @@ -1110,15 +1112,13 @@ grub_font_draw_string (const char *str, grub_font_t font, const grub_uint8_t *ptr; for (ptr = (const grub_uint8_t *) str, x = left_x; - grub_utf8_to_ucs4 (&code, 1, ptr, -1, &ptr) > 0; ) + grub_utf8_to_ucs4 (&code, 1, ptr, -1, &ptr) > 0;) { glyph = grub_font_get_glyph_with_fallback (font, code); - if (grub_font_draw_glyph (glyph, color, x, baseline_y) - != GRUB_ERR_NONE) - return grub_errno; + if (grub_font_draw_glyph (glyph, color, x, baseline_y) != GRUB_ERR_NONE) + return grub_errno; x += glyph->device_width; } return GRUB_ERR_NONE; } - diff --git a/font/font_cmd.c b/font/font_cmd.c index 98216ae44..5515b2cdc 100644 --- a/font/font_cmd.c +++ b/font/font_cmd.c @@ -56,7 +56,7 @@ lsfonts_command (grub_command_t cmd __attribute__ ((unused)), static grub_command_t cmd_loadfont, cmd_lsfonts; -GRUB_MOD_INIT(font_manager) +GRUB_MOD_INIT(font) { grub_font_loader_init (); @@ -69,7 +69,7 @@ GRUB_MOD_INIT(font_manager) 0, "List the loaded fonts."); } -GRUB_MOD_FINI(font_manager) +GRUB_MOD_FINI(font) { /* TODO: Determine way to free allocated resources. Warning: possible pointer references could be in use. */ diff --git a/fs/ext2.c b/fs/ext2.c index ac36b329b..f2fec828a 100644 --- a/fs/ext2.c +++ b/fs/ext2.c @@ -436,7 +436,8 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) grub_uint32_t indir[blksz / 4]; if (grub_disk_read (data->disk, - grub_le_to_cpu32 (inode->blocks.indir_block) + ((grub_disk_addr_t) + grub_le_to_cpu32 (inode->blocks.indir_block)) << log2_blksz, 0, blksz, indir)) return grub_errno; @@ -452,13 +453,15 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) grub_uint32_t indir[blksz / 4]; if (grub_disk_read (data->disk, - grub_le_to_cpu32 (inode->blocks.double_indir_block) + ((grub_disk_addr_t) + grub_le_to_cpu32 (inode->blocks.double_indir_block)) << log2_blksz, 0, blksz, indir)) return grub_errno; if (grub_disk_read (data->disk, - grub_le_to_cpu32 (indir[rblock / perblock]) + ((grub_disk_addr_t) + grub_le_to_cpu32 (indir[rblock / perblock])) << log2_blksz, 0, blksz, indir)) return grub_errno; diff --git a/fs/fat.c b/fs/fat.c index d008dc10d..89050943c 100644 --- a/fs/fat.c +++ b/fs/fat.c @@ -592,6 +592,7 @@ grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data, } grub_free (filename); + grub_free (unibuf); return grub_errno; } diff --git a/fs/i386/pc/pxe.c b/fs/i386/pc/pxe.c index 8bfe17594..82d8ee583 100644 --- a/fs/i386/pc/pxe.c +++ b/fs/i386/pc/pxe.c @@ -173,12 +173,15 @@ static struct grub_disk_dev grub_pxe_dev = }; static grub_err_t -grub_pxefs_dir (grub_device_t device __attribute__ ((unused)), +grub_pxefs_dir (grub_device_t device, const char *path __attribute__ ((unused)), int (*hook) (const char *filename, const struct grub_dirhook_info *info) __attribute__ ((unused))) { + if (device->disk->dev->id != GRUB_DISK_DEVICE_PXE_ID) + return grub_error (GRUB_ERR_IO, "not a pxe disk"); + return GRUB_ERR_NONE; } @@ -194,6 +197,9 @@ grub_pxefs_open (struct grub_file *file, const char *name) struct grub_pxe_disk_data *disk_data = file->device->disk->data; grub_file_t file_int, bufio; + if (file->device->disk->dev->id != GRUB_DISK_DEVICE_PXE_ID) + return grub_error (GRUB_ERR_IO, "not a pxe disk"); + if (curr_file != 0) { grub_pxe_call (GRUB_PXENV_TFTP_CLOSE, &c.c2); @@ -562,21 +568,21 @@ GRUB_MOD_INIT(pxe) buf = grub_xasprintf ("%d", grub_pxe_blksize); if (buf) - grub_env_set ("net_pxe_blksize", buf); + grub_env_set ("pxe_blksize", buf); grub_free (buf); set_ip_env ("pxe_default_server", grub_pxe_default_server_ip); set_ip_env ("pxe_default_gateway", grub_pxe_default_gateway_ip); set_ip_env ("net_pxe_ip", grub_pxe_your_ip); - grub_register_variable_hook ("net_pxe_default_server", 0, + grub_register_variable_hook ("pxe_default_server", 0, grub_env_write_pxe_default_server); - grub_register_variable_hook ("net_pxe_default_gateway", 0, + grub_register_variable_hook ("pxe_default_gateway", 0, grub_env_write_pxe_default_gateway); /* XXX: Is it possible to change IP in PXE? */ grub_register_variable_hook ("net_pxe_ip", 0, grub_env_write_readonly); - grub_register_variable_hook ("net_pxe_blksize", 0, + grub_register_variable_hook ("pxe_blksize", 0, grub_env_write_pxe_blocksize); grub_disk_dev_register (&grub_pxe_dev); grub_fs_register (&grub_pxefs_fs); diff --git a/fs/iso9660.c b/fs/iso9660.c index a8a310f50..6dc465f25 100644 --- a/fs/iso9660.c +++ b/fs/iso9660.c @@ -136,7 +136,6 @@ struct grub_iso9660_data struct grub_iso9660_primary_voldesc voldesc; grub_disk_t disk; unsigned int first_sector; - unsigned int length; int rockridge; int susp_skip; int joliet; @@ -630,12 +629,16 @@ grub_iso9660_iterate_dir (grub_fshelp_node_t dir, if (dir->data->joliet) { - char *oldname; + char *oldname, *semicolon; oldname = filename; filename = grub_iso9660_convert_string ((grub_uint16_t *) oldname, dirent.namelen >> 1); + semicolon = grub_strrchr (filename, ';'); + if (semicolon) + *semicolon = '\0'; + if (filename_alloc) grub_free (oldname); @@ -744,7 +747,6 @@ grub_iso9660_open (struct grub_file *file, const char *name) goto fail; data->first_sector = foundnode->blk; - data->length = foundnode->size; file->data = data; file->size = foundnode->size; diff --git a/fs/nilfs2.c b/fs/nilfs2.c new file mode 100644 index 000000000..7e0415d12 --- /dev/null +++ b/fs/nilfs2.c @@ -0,0 +1,1133 @@ +/* + * nilfs2.c - New Implementation of Log filesystem + * + * Written by Jiro SEKIBA + * + * Copyright (C) 2003,2004,2005,2007,2008,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + + +/* Filetype information as used in inodes. */ +#define FILETYPE_INO_MASK 0170000 +#define FILETYPE_INO_REG 0100000 +#define FILETYPE_INO_DIRECTORY 0040000 +#define FILETYPE_INO_SYMLINK 0120000 + +#include +#include +#include +#include +#include +#include +#include +#include + +#define NILFS_INODE_BMAP_SIZE 7 + +#define NILFS_SUPORT_REV 2 + +/* Magic value used to identify an nilfs2 filesystem. */ +#define NILFS2_SUPER_MAGIC 0x3434 +/* nilfs btree node flag. */ +#define NILFS_BTREE_NODE_ROOT 0x01 + +/* nilfs btree node level. */ +#define NILFS_BTREE_LEVEL_DATA 0 +#define NILFS_BTREE_LEVEL_NODE_MIN (NILFS_BTREE_LEVEL_DATA + 1) +#define NILFS_BTREE_LEVEL_MAX 14 + +struct grub_nilfs2_inode +{ + grub_uint64_t i_blocks; + grub_uint64_t i_size; + grub_uint64_t i_ctime; + grub_uint64_t i_mtime; + grub_uint32_t i_ctime_nsec; + grub_uint32_t i_mtime_nsec; + grub_uint32_t i_uid; + grub_uint32_t i_gid; + grub_uint16_t i_mode; + grub_uint16_t i_links_count; + grub_uint32_t i_flags; + grub_uint64_t i_bmap[NILFS_INODE_BMAP_SIZE]; +#define i_device_code i_bmap[0] + grub_uint64_t i_xattr; + grub_uint32_t i_generation; + grub_uint32_t i_pad; +}; + +struct grub_nilfs2_super_root +{ + grub_uint32_t sr_sum; + grub_uint16_t sr_bytes; + grub_uint16_t sr_flags; + grub_uint64_t sr_nongc_ctime; + struct grub_nilfs2_inode sr_dat; + struct grub_nilfs2_inode sr_cpfile; + struct grub_nilfs2_inode sr_sufile; +}; + +struct grub_nilfs2_super_block +{ + grub_uint32_t s_rev_level; + grub_uint16_t s_minor_rev_level; + grub_uint16_t s_magic; + grub_uint16_t s_bytes; + grub_uint16_t s_flags; + grub_uint32_t s_crc_seed; + grub_uint32_t s_sum; + grub_uint32_t s_log_block_size; + grub_uint64_t s_nsegments; + grub_uint64_t s_dev_size; + grub_uint64_t s_first_data_block; + grub_uint32_t s_blocks_per_segment; + grub_uint32_t s_r_segments_percentage; + grub_uint64_t s_last_cno; + grub_uint64_t s_last_pseg; + grub_uint64_t s_last_seq; + grub_uint64_t s_free_blocks_count; + grub_uint64_t s_ctime; + grub_uint64_t s_mtime; + grub_uint64_t s_wtime; + grub_uint16_t s_mnt_count; + grub_uint16_t s_max_mnt_count; + grub_uint16_t s_state; + grub_uint16_t s_errors; + grub_uint64_t s_lastcheck; + grub_uint32_t s_checkinterval; + grub_uint32_t s_creator_os; + grub_uint16_t s_def_resuid; + grub_uint16_t s_def_resgid; + grub_uint32_t s_first_ino; + grub_uint16_t s_inode_size; + grub_uint16_t s_dat_entry_size; + grub_uint16_t s_checkpoint_size; + grub_uint16_t s_segment_usage_size; + grub_uint8_t s_uuid[16]; + char s_volume_name[16]; + char s_last_mounted[64]; + grub_uint32_t s_c_interval; + grub_uint32_t s_c_block_max; + grub_uint32_t s_reserved[192]; +}; + +struct grub_nilfs2_dir_entry +{ + grub_uint64_t inode; + grub_uint16_t rec_len; + grub_uint8_t name_len; + grub_uint8_t file_type; +#if 0 /* followed by file name. */ + char name[NILFS_NAME_LEN]; + char pad; +#endif +} __attribute__ ((packed)); + +enum +{ + NILFS_FT_UNKNOWN, + NILFS_FT_REG_FILE, + NILFS_FT_DIR, + NILFS_FT_CHRDEV, + NILFS_FT_BLKDEV, + NILFS_FT_FIFO, + NILFS_FT_SOCK, + NILFS_FT_SYMLINK, + NILFS_FT_MAX +}; + +struct grub_nilfs2_finfo +{ + grub_uint64_t fi_ino; + grub_uint64_t fi_cno; + grub_uint32_t fi_nblocks; + grub_uint32_t fi_ndatablk; +}; + +struct grub_nilfs2_binfo_v +{ + grub_uint64_t bi_vblocknr; + grub_uint64_t bi_blkoff; +}; + +struct grub_nilfs2_binfo_dat +{ + grub_uint64_t bi_blkoff; + grub_uint8_t bi_level; + grub_uint8_t bi_pad[7]; +}; + +union grub_nilfs2_binfo +{ + struct grub_nilfs2_binfo_v bi_v; + struct grub_nilfs2_binfo_dat bi_dat; +}; + +struct grub_nilfs2_segment_summary +{ + grub_uint32_t ss_datasum; + grub_uint32_t ss_sumsum; + grub_uint32_t ss_magic; + grub_uint16_t ss_bytes; + grub_uint16_t ss_flags; + grub_uint64_t ss_seq; + grub_uint64_t ss_create; + grub_uint64_t ss_next; + grub_uint32_t ss_nblocks; + grub_uint32_t ss_nfinfo; + grub_uint32_t ss_sumbytes; + grub_uint32_t ss_pad; +}; + +struct grub_nilfs2_btree_node +{ + grub_uint8_t bn_flags; + grub_uint8_t bn_level; + grub_uint16_t bn_nchildren; + grub_uint32_t bn_pad; +}; + +struct grub_nilfs2_palloc_group_desc +{ + grub_uint32_t pg_nfrees; +}; + +struct grub_nilfs2_dat_entry +{ + grub_uint64_t de_blocknr; + grub_uint64_t de_start; + grub_uint64_t de_end; + grub_uint64_t de_rsv; +}; + +struct grub_nilfs2_snapshot_list +{ + grub_uint64_t ssl_next; + grub_uint64_t ssl_prev; +}; + +struct grub_nilfs2_cpfile_header +{ + grub_uint64_t ch_ncheckpoints; + grub_uint64_t ch_nsnapshots; + struct grub_nilfs2_snapshot_list ch_snapshot_list; +}; + +struct grub_nilfs2_checkpoint +{ + grub_uint32_t cp_flags; + grub_uint32_t cp_checkpoints_count; + struct grub_nilfs2_snapshot_list cp_snapshot_list; + grub_uint64_t cp_cno; + grub_uint64_t cp_create; + grub_uint64_t cp_nblk_inc; + grub_uint64_t cp_inodes_count; + grub_uint64_t cp_blocks_count; + struct grub_nilfs2_inode cp_ifile_inode; +}; + + +#define NILFS_BMAP_LARGE 0x1 +#define NILFS_BMAP_SIZE (NILFS_INODE_BMAP_SIZE * sizeof(grub_uint64_t)) + +/* nilfs extra padding for nonroot btree node. */ +#define NILFS_BTREE_NODE_EXTRA_PAD_SIZE (sizeof(grub_uint64_t)) +#define NILFS_BTREE_ROOT_SIZE NILFS_BMAP_SIZE +#define NILFS_BTREE_ROOT_NCHILDREN_MAX \ + ((NILFS_BTREE_ROOT_SIZE - sizeof(struct nilfs_btree_node)) / \ + (sizeof(grub_uint64_t) + sizeof(grub_uint64_t)) ) + + +struct grub_fshelp_node +{ + struct grub_nilfs2_data *data; + struct grub_nilfs2_inode inode; + grub_uint64_t ino; + int inode_read; +}; + +struct grub_nilfs2_data +{ + struct grub_nilfs2_super_block sblock; + struct grub_nilfs2_super_root sroot; + struct grub_nilfs2_inode ifile; + grub_disk_t disk; + struct grub_nilfs2_inode *inode; + struct grub_fshelp_node diropen; +}; + +/* Log2 size of nilfs2 block in 512 blocks. */ +#define LOG2_NILFS2_BLOCK_SIZE(data) \ + (grub_le_to_cpu32 (data->sblock.s_log_block_size) + 1) + +/* Log2 size of nilfs2 block in bytes. */ +#define LOG2_BLOCK_SIZE(data) \ + (grub_le_to_cpu32 (data->sblock.s_log_block_size) + 10) + +/* The size of an nilfs2 block in bytes. */ +#define NILFS2_BLOCK_SIZE(data) (1 << LOG2_BLOCK_SIZE (data)) + +static grub_uint64_t +grub_nilfs2_dat_translate (struct grub_nilfs2_data *data, grub_uint64_t key); +static grub_dl_t my_mod; + + + +static inline unsigned long +grub_nilfs2_palloc_entries_per_group (struct grub_nilfs2_data *data) +{ + return 1UL << (LOG2_BLOCK_SIZE (data) + 3); +} + +static inline grub_uint64_t +grub_nilfs2_palloc_group (struct grub_nilfs2_data *data, + grub_uint64_t nr, grub_uint32_t * offset) +{ + return grub_divmod64 (nr, grub_nilfs2_palloc_entries_per_group (data), + offset); +} + +static inline grub_uint32_t +grub_nilfs2_palloc_groups_per_desc_block (struct grub_nilfs2_data *data) +{ + return NILFS2_BLOCK_SIZE (data) / + sizeof (struct grub_nilfs2_palloc_group_desc); +} + +static inline grub_uint32_t +grub_nilfs2_entries_per_block (struct grub_nilfs2_data *data, + unsigned long entry_size) +{ + return NILFS2_BLOCK_SIZE (data) / entry_size; +} + + +static inline grub_uint32_t +grub_nilfs2_blocks_per_group (struct grub_nilfs2_data *data, + unsigned long entry_size) +{ + return grub_div_roundup (grub_nilfs2_palloc_entries_per_group (data), + grub_nilfs2_entries_per_block (data, + entry_size)) + 1; +} + +static inline grub_uint32_t +grub_nilfs2_blocks_per_desc_block (struct grub_nilfs2_data *data, + unsigned long entry_size) +{ + return grub_nilfs2_palloc_groups_per_desc_block (data) * + grub_nilfs2_blocks_per_group (data, entry_size) + 1; +} + +static inline grub_uint32_t +grub_nilfs2_palloc_desc_block_offset (struct grub_nilfs2_data *data, + unsigned long group, + unsigned long entry_size) +{ + grub_uint32_t desc_block = + group / grub_nilfs2_palloc_groups_per_desc_block (data); + return desc_block * grub_nilfs2_blocks_per_desc_block (data, entry_size); +} + +static inline grub_uint32_t +grub_nilfs2_palloc_bitmap_block_offset (struct grub_nilfs2_data *data, + unsigned long group, + unsigned long entry_size) +{ + unsigned long desc_offset = group % + grub_nilfs2_palloc_groups_per_desc_block (data); + + return grub_nilfs2_palloc_desc_block_offset (data, group, entry_size) + 1 + + desc_offset * grub_nilfs2_blocks_per_group (data, entry_size); +} + +static inline grub_uint32_t +grub_nilfs2_palloc_entry_offset (struct grub_nilfs2_data *data, + grub_uint64_t nr, unsigned long entry_size) +{ + unsigned long group; + grub_uint32_t group_offset; + + group = grub_nilfs2_palloc_group (data, nr, &group_offset); + + return grub_nilfs2_palloc_bitmap_block_offset (data, group, + entry_size) + 1 + + group_offset / grub_nilfs2_entries_per_block (data, entry_size); + +} + +static inline struct grub_nilfs2_btree_node * +grub_nilfs2_btree_get_root (struct grub_nilfs2_inode *inode) +{ + return (struct grub_nilfs2_btree_node *) &inode->i_bmap[0]; +} + +static inline int +grub_nilfs2_btree_get_level (struct grub_nilfs2_btree_node *node) +{ + return node->bn_level; +} + +static inline grub_uint64_t * +grub_nilfs2_btree_node_dkeys (struct grub_nilfs2_btree_node *node) +{ + return (grub_uint64_t *) ((char *) (node + 1) + + ((node->bn_flags & NILFS_BTREE_NODE_ROOT) ? + 0 : NILFS_BTREE_NODE_EXTRA_PAD_SIZE)); +} + +static inline grub_uint64_t +grub_nilfs2_btree_node_get_key (struct grub_nilfs2_btree_node *node, + int index) +{ + return grub_le_to_cpu64 (*(grub_nilfs2_btree_node_dkeys (node) + index)); +} + +static inline int +grub_nilfs2_btree_node_lookup (struct grub_nilfs2_btree_node *node, + grub_uint64_t key, int *indexp) +{ + grub_uint64_t nkey; + int index, low, high, s; + + low = 0; + high = grub_le_to_cpu16 (node->bn_nchildren) - 1; + index = 0; + s = 0; + while (low <= high) + { + index = (low + high) / 2; + nkey = grub_nilfs2_btree_node_get_key (node, index); + if (nkey == key) + { + *indexp = index; + return 1; + } + else if (nkey < key) + { + low = index + 1; + s = -1; + } + else + { + high = index - 1; + s = 1; + } + } + + if (node->bn_level > NILFS_BTREE_LEVEL_NODE_MIN) + { + if (s > 0 && index > 0) + index--; + } + else if (s < 0) + index++; + + *indexp = index; + return s == 0; +} + +static inline int +grub_nilfs2_btree_node_nchildren_max (struct grub_nilfs2_data *data, + struct grub_nilfs2_btree_node *node) +{ + int node_children_max = ((NILFS2_BLOCK_SIZE (data) - + sizeof (struct grub_nilfs2_btree_node) - + NILFS_BTREE_NODE_EXTRA_PAD_SIZE) / + (sizeof (grub_uint64_t) + sizeof (grub_uint64_t))); + + return (node->bn_flags & NILFS_BTREE_NODE_ROOT) ? 3 : node_children_max; +} + +static inline grub_uint64_t * +grub_nilfs2_btree_node_dptrs (struct grub_nilfs2_data *data, + struct grub_nilfs2_btree_node *node) +{ + return (grub_uint64_t *) (grub_nilfs2_btree_node_dkeys (node) + + grub_nilfs2_btree_node_nchildren_max (data, + node)); +} + +static inline grub_uint64_t +grub_nilfs2_btree_node_get_ptr (struct grub_nilfs2_data *data, + struct grub_nilfs2_btree_node *node, + int index) +{ + return + grub_le_to_cpu64 (*(grub_nilfs2_btree_node_dptrs (data, node) + index)); +} + +static inline int +grub_nilfs2_btree_get_nonroot_node (struct grub_nilfs2_data *data, + grub_uint64_t ptr, void *block) +{ + grub_disk_t disk = data->disk; + unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); + + return grub_disk_read (disk, ptr * nilfs2_block_count, 0, + NILFS2_BLOCK_SIZE (data), block); +} + +static grub_uint64_t +grub_nilfs2_btree_lookup (struct grub_nilfs2_data *data, + struct grub_nilfs2_inode *inode, + grub_uint64_t key, int need_translate) +{ + struct grub_nilfs2_btree_node *node; + unsigned char block[NILFS2_BLOCK_SIZE (data)]; + grub_uint64_t ptr; + int level, found, index; + + node = grub_nilfs2_btree_get_root (inode); + level = grub_nilfs2_btree_get_level (node); + + found = grub_nilfs2_btree_node_lookup (node, key, &index); + ptr = grub_nilfs2_btree_node_get_ptr (data, node, index); + if (need_translate) + ptr = grub_nilfs2_dat_translate (data, ptr); + + for (level--; level >= NILFS_BTREE_LEVEL_NODE_MIN; level--) + { + grub_nilfs2_btree_get_nonroot_node (data, ptr, block); + if (grub_errno) + { + return -1; + } + node = (struct grub_nilfs2_btree_node *) block; + + if (node->bn_level != level) + { + grub_error (GRUB_ERR_BAD_FS, "btree level mismatch\n"); + return -1; + } + + if (!found) + found = grub_nilfs2_btree_node_lookup (node, key, &index); + else + index = 0; + + if (index < grub_nilfs2_btree_node_nchildren_max (data, node)) + { + ptr = grub_nilfs2_btree_node_get_ptr (data, node, index); + if (need_translate) + ptr = grub_nilfs2_dat_translate (data, ptr); + } + else + { + grub_error (GRUB_ERR_BAD_FS, "btree corruption\n"); + return -1; + } + } + + if (!found) + return -1; + + return ptr; +} + +static inline grub_uint64_t +grub_nilfs2_direct_lookup (struct grub_nilfs2_inode *inode, grub_uint64_t key) +{ + return grub_le_to_cpu64 (inode->i_bmap[1 + key]); +} + +static inline grub_uint64_t +grub_nilfs2_bmap_lookup (struct grub_nilfs2_data *data, + struct grub_nilfs2_inode *inode, + grub_uint64_t key, int need_translate) +{ + struct grub_nilfs2_btree_node *root = grub_nilfs2_btree_get_root (inode); + if (root->bn_flags & NILFS_BMAP_LARGE) + return grub_nilfs2_btree_lookup (data, inode, key, need_translate); + else + { + grub_uint64_t ptr; + ptr = grub_nilfs2_direct_lookup (inode, key); + if (need_translate) + ptr = grub_nilfs2_dat_translate (data, ptr); + return ptr; + } +} + +static grub_uint64_t +grub_nilfs2_dat_translate (struct grub_nilfs2_data *data, grub_uint64_t key) +{ + struct grub_nilfs2_dat_entry entry; + grub_disk_t disk = data->disk; + grub_uint64_t pptr; + grub_uint32_t blockno, offset; + unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); + + blockno = grub_nilfs2_palloc_entry_offset (data, key, + sizeof (struct + grub_nilfs2_dat_entry)); + + grub_divmod64 (key * sizeof (struct grub_nilfs2_dat_entry), + NILFS2_BLOCK_SIZE (data), &offset); + + pptr = grub_nilfs2_bmap_lookup (data, &data->sroot.sr_dat, blockno, 0); + if (pptr == (grub_uint64_t) - 1) + { + grub_error (GRUB_ERR_BAD_FS, "btree lookup failure"); + return -1; + } + + grub_disk_read (disk, pptr * nilfs2_block_count, offset, + sizeof (struct grub_nilfs2_dat_entry), &entry); + + return grub_le_to_cpu64 (entry.de_blocknr); +} + + +static grub_disk_addr_t +grub_nilfs2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) +{ + struct grub_nilfs2_data *data = node->data; + struct grub_nilfs2_inode *inode = &node->inode; + grub_uint64_t pptr = -1; + + pptr = grub_nilfs2_bmap_lookup (data, inode, fileblock, 1); + if (pptr == (grub_uint64_t) - 1) + { + grub_error (GRUB_ERR_BAD_FS, "btree lookup failure"); + return -1; + } + + return pptr; +} + +/* Read LEN bytes from the file described by DATA starting with byte + POS. Return the amount of read bytes in READ. */ +static grub_ssize_t +grub_nilfs2_read_file (grub_fshelp_node_t node, + void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t + sector, + unsigned offset, + unsigned length), + int pos, grub_size_t len, char *buf) +{ + return grub_fshelp_read_file (node->data->disk, node, read_hook, + pos, len, buf, grub_nilfs2_read_block, + grub_le_to_cpu64 (node->inode.i_size), + LOG2_NILFS2_BLOCK_SIZE (node->data)); + +} + +static grub_err_t +grub_nilfs2_read_checkpoint (struct grub_nilfs2_data *data, + grub_uint64_t cpno, + struct grub_nilfs2_checkpoint *cpp) +{ + grub_uint64_t blockno; + grub_uint32_t offset; + grub_uint64_t pptr; + grub_disk_t disk = data->disk; + unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); + + /* Assume sizeof(struct grub_nilfs2_cpfile_header) < + sizeof(struct grub_nilfs2_checkpoint). + */ + blockno = grub_divmod64 (cpno, NILFS2_BLOCK_SIZE (data) / + sizeof (struct grub_nilfs2_checkpoint), &offset); + + pptr = grub_nilfs2_bmap_lookup (data, &data->sroot.sr_cpfile, blockno, 1); + if (pptr == (grub_uint64_t) - 1) + { + return grub_error (GRUB_ERR_BAD_FS, "btree lookup failure"); + } + + return grub_disk_read (disk, pptr * nilfs2_block_count, + offset * sizeof (struct grub_nilfs2_checkpoint), + sizeof (struct grub_nilfs2_checkpoint), cpp); +} + +static inline grub_err_t +grub_nilfs2_read_last_checkpoint (struct grub_nilfs2_data *data, + struct grub_nilfs2_checkpoint *cpp) +{ + return grub_nilfs2_read_checkpoint (data, + grub_le_to_cpu64 (data-> + sblock.s_last_cno), + cpp); +} + +/* Read the inode INO for the file described by DATA into INODE. */ +static grub_err_t +grub_nilfs2_read_inode (struct grub_nilfs2_data *data, + grub_uint64_t ino, struct grub_nilfs2_inode *inodep) +{ + grub_uint64_t blockno; + unsigned int offset; + grub_uint64_t pptr; + grub_disk_t disk = data->disk; + unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); + + blockno = grub_nilfs2_palloc_entry_offset (data, ino, + sizeof (struct + grub_nilfs2_inode)); + + grub_divmod64 (sizeof (struct grub_nilfs2_inode) * ino, + NILFS2_BLOCK_SIZE (data), &offset); + pptr = grub_nilfs2_bmap_lookup (data, &data->ifile, blockno, 1); + if (pptr == (grub_uint64_t) - 1) + { + return grub_error (GRUB_ERR_BAD_FS, "btree lookup failure"); + } + + return grub_disk_read (disk, pptr * nilfs2_block_count, offset, + sizeof (struct grub_nilfs2_inode), inodep); +} + +static int +grub_nilfs2_valid_sb (struct grub_nilfs2_super_block *sbp) +{ + if (grub_le_to_cpu16 (sbp->s_magic) != NILFS2_SUPER_MAGIC) + return 0; + + if (grub_le_to_cpu32 (sbp->s_rev_level) != NILFS_SUPORT_REV) + return 0; + + return 1; +} + +static struct grub_nilfs2_data * +grub_nilfs2_mount (grub_disk_t disk) +{ + struct grub_nilfs2_data *data; + struct grub_nilfs2_segment_summary ss; + struct grub_nilfs2_checkpoint last_checkpoint; + grub_uint64_t last_pseg; + grub_uint32_t nblocks; + unsigned int nilfs2_block_count; + + data = grub_malloc (sizeof (struct grub_nilfs2_data)); + if (!data) + return 0; + + /* Read the superblock. */ + grub_disk_read (disk, 1 * 2, 0, sizeof (struct grub_nilfs2_super_block), + &data->sblock); + if (grub_errno) + goto fail; + + /* Make sure this is an nilfs2 filesystem. */ + if (!grub_nilfs2_valid_sb (&data->sblock)) + { + grub_error (GRUB_ERR_BAD_FS, "not a nilfs2 filesystem"); + goto fail; + } + + nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); + + /* Read the last segment summary. */ + last_pseg = grub_le_to_cpu64 (data->sblock.s_last_pseg); + grub_disk_read (disk, last_pseg * nilfs2_block_count, 0, + sizeof (struct grub_nilfs2_segment_summary), &ss); + + if (grub_errno) + goto fail; + + /* Read the super root block. */ + nblocks = grub_le_to_cpu32 (ss.ss_nblocks); + grub_disk_read (disk, (last_pseg + (nblocks - 1)) * nilfs2_block_count, 0, + sizeof (struct grub_nilfs2_super_root), &data->sroot); + + if (grub_errno) + goto fail; + + data->disk = disk; + + grub_nilfs2_read_last_checkpoint (data, &last_checkpoint); + + if (grub_errno) + goto fail; + + grub_memcpy (&data->ifile, &last_checkpoint.cp_ifile_inode, + sizeof (struct grub_nilfs2_inode)); + + data->diropen.data = data; + data->diropen.ino = 2; + data->diropen.inode_read = 1; + data->inode = &data->diropen.inode; + + grub_nilfs2_read_inode (data, 2, data->inode); + + return data; + +fail: + if (grub_errno == GRUB_ERR_OUT_OF_RANGE) + grub_error (GRUB_ERR_BAD_FS, "not a nilfs2 filesystem"); + + grub_free (data); + return 0; +} + +static char * +grub_nilfs2_read_symlink (grub_fshelp_node_t node) +{ + char *symlink; + struct grub_fshelp_node *diro = node; + + if (!diro->inode_read) + { + grub_nilfs2_read_inode (diro->data, diro->ino, &diro->inode); + if (grub_errno) + return 0; + } + + symlink = grub_malloc (grub_le_to_cpu64 (diro->inode.i_size) + 1); + if (!symlink) + return 0; + + grub_nilfs2_read_file (diro, 0, 0, + grub_le_to_cpu64 (diro->inode.i_size), symlink); + if (grub_errno) + { + grub_free (symlink); + return 0; + } + + symlink[grub_le_to_cpu64 (diro->inode.i_size)] = '\0'; + return symlink; +} + +static int +grub_nilfs2_iterate_dir (grub_fshelp_node_t dir, + int NESTED_FUNC_ATTR + (*hook) (const char *filename, + enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node)) +{ + unsigned int fpos = 0; + struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir; + + if (!diro->inode_read) + { + grub_nilfs2_read_inode (diro->data, diro->ino, &diro->inode); + if (grub_errno) + return 0; + } + + /* Iterate files. */ + while (fpos < grub_le_to_cpu64 (diro->inode.i_size)) + { + struct grub_nilfs2_dir_entry dirent; + + grub_nilfs2_read_file (diro, 0, fpos, + sizeof (struct grub_nilfs2_dir_entry), + (char *) &dirent); + if (grub_errno) + return 0; + + if (dirent.rec_len == 0) + return 0; + + if (dirent.name_len != 0) + { + char filename[dirent.name_len + 1]; + struct grub_fshelp_node *fdiro; + enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN; + + grub_nilfs2_read_file (diro, 0, + fpos + sizeof (struct grub_nilfs2_dir_entry), + dirent.name_len, filename); + if (grub_errno) + return 0; + + fdiro = grub_malloc (sizeof (struct grub_fshelp_node)); + if (!fdiro) + return 0; + + fdiro->data = diro->data; + fdiro->ino = grub_le_to_cpu64 (dirent.inode); + + filename[dirent.name_len] = '\0'; + + if (dirent.file_type != NILFS_FT_UNKNOWN) + { + fdiro->inode_read = 0; + + if (dirent.file_type == NILFS_FT_DIR) + type = GRUB_FSHELP_DIR; + else if (dirent.file_type == NILFS_FT_SYMLINK) + type = GRUB_FSHELP_SYMLINK; + else if (dirent.file_type == NILFS_FT_REG_FILE) + type = GRUB_FSHELP_REG; + } + else + { + /* The filetype can not be read from the dirent, read + the inode to get more information. */ + grub_nilfs2_read_inode (diro->data, + grub_le_to_cpu64 (dirent.inode), + &fdiro->inode); + if (grub_errno) + { + grub_free (fdiro); + return 0; + } + + fdiro->inode_read = 1; + + if ((grub_le_to_cpu16 (fdiro->inode.i_mode) + & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY) + type = GRUB_FSHELP_DIR; + else if ((grub_le_to_cpu16 (fdiro->inode.i_mode) + & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK) + type = GRUB_FSHELP_SYMLINK; + else if ((grub_le_to_cpu16 (fdiro->inode.i_mode) + & FILETYPE_INO_MASK) == FILETYPE_INO_REG) + type = GRUB_FSHELP_REG; + } + + if (hook (filename, type, fdiro)) + return 1; + } + + fpos += grub_le_to_cpu16 (dirent.rec_len); + } + + return 0; +} + +/* Open a file named NAME and initialize FILE. */ +static grub_err_t +grub_nilfs2_open (struct grub_file *file, const char *name) +{ + struct grub_nilfs2_data *data = NULL; + struct grub_fshelp_node *fdiro = 0; + + grub_dl_ref (my_mod); + + data = grub_nilfs2_mount (file->device->disk); + if (!data) + goto fail; + + grub_fshelp_find_file (name, &data->diropen, &fdiro, + grub_nilfs2_iterate_dir, grub_nilfs2_read_symlink, + GRUB_FSHELP_REG); + if (grub_errno) + goto fail; + + if (!fdiro->inode_read) + { + grub_nilfs2_read_inode (data, fdiro->ino, &fdiro->inode); + if (grub_errno) + goto fail; + } + + grub_memcpy (data->inode, &fdiro->inode, sizeof (struct grub_nilfs2_inode)); + grub_free (fdiro); + + file->size = grub_le_to_cpu64 (data->inode->i_size); + file->data = data; + file->offset = 0; + + return 0; + +fail: + if (fdiro != &data->diropen) + grub_free (fdiro); + grub_free (data); + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static grub_err_t +grub_nilfs2_close (grub_file_t file) +{ + grub_free (file->data); + + grub_dl_unref (my_mod); + + return GRUB_ERR_NONE; +} + +/* Read LEN bytes data from FILE into BUF. */ +static grub_ssize_t +grub_nilfs2_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_nilfs2_data *data = (struct grub_nilfs2_data *) file->data; + + return grub_nilfs2_read_file (&data->diropen, file->read_hook, + file->offset, len, buf); +} + +static grub_err_t +grub_nilfs2_dir (grub_device_t device, const char *path, + int (*hook) (const char *filename, + const struct grub_dirhook_info * info)) +{ + struct grub_nilfs2_data *data = 0; + struct grub_fshelp_node *fdiro = 0; + + auto int NESTED_FUNC_ATTR iterate (const char *filename, + enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node); + + int NESTED_FUNC_ATTR iterate (const char *filename, + enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node) + { + struct grub_dirhook_info info; + grub_memset (&info, 0, sizeof (info)); + if (!node->inode_read) + { + grub_nilfs2_read_inode (data, node->ino, &node->inode); + if (!grub_errno) + node->inode_read = 1; + grub_errno = GRUB_ERR_NONE; + } + if (node->inode_read) + { + info.mtimeset = 1; + info.mtime = grub_le_to_cpu64 (node->inode.i_mtime); + } + + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + grub_free (node); + return hook (filename, &info); + } + + grub_dl_ref (my_mod); + + data = grub_nilfs2_mount (device->disk); + if (!data) + goto fail; + + grub_fshelp_find_file (path, &data->diropen, &fdiro, + grub_nilfs2_iterate_dir, grub_nilfs2_read_symlink, + GRUB_FSHELP_DIR); + if (grub_errno) + goto fail; + + grub_nilfs2_iterate_dir (fdiro, iterate); + +fail: + if (fdiro != &data->diropen) + grub_free (fdiro); + grub_free (data); + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static grub_err_t +grub_nilfs2_label (grub_device_t device, char **label) +{ + struct grub_nilfs2_data *data; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_nilfs2_mount (disk); + if (data) + *label = grub_strndup (data->sblock.s_volume_name, 14); + else + *label = NULL; + + grub_dl_unref (my_mod); + + grub_free (data); + + return grub_errno; +} + +static grub_err_t +grub_nilfs2_uuid (grub_device_t device, char **uuid) +{ + struct grub_nilfs2_data *data; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_nilfs2_mount (disk); + if (data) + { + *uuid = + grub_xasprintf + ("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%0x-%02x%02x%02x%02x%02x%02x", + data->sblock.s_uuid[0], data->sblock.s_uuid[1], + data->sblock.s_uuid[2], data->sblock.s_uuid[3], + data->sblock.s_uuid[4], data->sblock.s_uuid[5], + data->sblock.s_uuid[6], data->sblock.s_uuid[7], + data->sblock.s_uuid[8], data->sblock.s_uuid[9], + data->sblock.s_uuid[10], data->sblock.s_uuid[11], + data->sblock.s_uuid[12], data->sblock.s_uuid[13], + data->sblock.s_uuid[14], data->sblock.s_uuid[15]); + } + else + *uuid = NULL; + + grub_dl_unref (my_mod); + + grub_free (data); + + return grub_errno; +} + +/* Get mtime. */ +static grub_err_t +grub_nilfs2_mtime (grub_device_t device, grub_int32_t * tm) +{ + struct grub_nilfs2_data *data; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_nilfs2_mount (disk); + if (!data) + *tm = 0; + else + *tm = (grub_int32_t) grub_le_to_cpu64 (data->sblock.s_mtime); + + grub_dl_unref (my_mod); + + grub_free (data); + + return grub_errno; +} + + + +static struct grub_fs grub_nilfs2_fs = { + .name = "nilfs2", + .dir = grub_nilfs2_dir, + .open = grub_nilfs2_open, + .read = grub_nilfs2_read, + .close = grub_nilfs2_close, + .label = grub_nilfs2_label, + .uuid = grub_nilfs2_uuid, + .mtime = grub_nilfs2_mtime, +#ifdef GRUB_UTIL + .reserved_first_sector = 1, +#endif + .next = 0 +}; + +GRUB_MOD_INIT (nilfs2) +{ + grub_fs_register (&grub_nilfs2_fs); + my_mod = mod; +} + +GRUB_MOD_FINI (nilfs2) +{ + grub_fs_unregister (&grub_nilfs2_fs); +} diff --git a/fs/reiserfs.c b/fs/reiserfs.c index 444bf3120..5acd339c5 100644 --- a/fs/reiserfs.c +++ b/fs/reiserfs.c @@ -62,7 +62,7 @@ static grub_dl_t my_mod; -#define assert(boolean) real_assert (boolean, __FILE__, __LINE__) +#define assert(boolean) real_assert (boolean, GRUB_FILE, __LINE__) static inline void real_assert (int boolean, const char *file, const int line) { @@ -1189,7 +1189,8 @@ grub_reiserfs_read (grub_file_t file, char *buf, grub_size_t len) (unsigned long long) (current_position - initial_position), (unsigned long) len); return current_position - initial_position; -/* + +#if 0 switch (found.type) { case GRUB_REISERFS_DIRECT: @@ -1232,7 +1233,8 @@ grub_reiserfs_read (grub_file_t file, char *buf, grub_size_t len) goto fail; } - return read_length;*/ + return read_length; +#endif fail: grub_free (indirect_block_ptr); diff --git a/fs/udf.c b/fs/udf.c index cecb6eb78..ad109bed9 100644 --- a/fs/udf.c +++ b/fs/udf.c @@ -25,6 +25,7 @@ #include #include #include +#include #define GRUB_UDF_MAX_PDS 2 #define GRUB_UDF_MAX_PMS 6 @@ -745,19 +746,41 @@ grub_udf_iterate_dir (grub_fshelp_node_t dir, else { enum grub_fshelp_filetype type; - char filename[dirent.file_ident_length + 1]; + grub_uint8_t raw[dirent.file_ident_length]; + grub_uint16_t utf16[dirent.file_ident_length - 1]; + grub_uint8_t filename[dirent.file_ident_length * 2]; + grub_size_t utf16len = 0; type = ((dirent.characteristics & GRUB_UDF_FID_CHAR_DIRECTORY) ? (GRUB_FSHELP_DIR) : (GRUB_FSHELP_REG)); if ((grub_udf_read_file (dir, 0, offset, - dirent.file_ident_length, filename)) + dirent.file_ident_length, + (char *) raw)) != dirent.file_ident_length) return 0; - filename[dirent.file_ident_length] = 0; - if (hook (&filename[1], type, child)) - return 1; + if (raw[0] == 8) + { + unsigned i; + utf16len = dirent.file_ident_length - 1; + for (i = 0; i < utf16len; i++) + utf16[i] = raw[i + 1]; + } + if (raw[0] == 16) + { + unsigned i; + utf16len = (dirent.file_ident_length - 1) / 2; + for (i = 0; i < utf16len; i++) + utf16[i] = (raw[2 * i + 1] << 8) | raw[2*i + 2]; + } + if (raw[0] == 8 || raw[0] == 16) + { + *grub_utf16_to_utf8 (filename, utf16, utf16len) = '\0'; + + if (hook ((char *) filename, type, child)) + return 1; + } } /* Align to dword boundary. */ diff --git a/genemuinit.sh b/genemuinit.sh new file mode 100644 index 000000000..45c15ecb9 --- /dev/null +++ b/genemuinit.sh @@ -0,0 +1,72 @@ +#! /bin/sh +# +# Copyright (C) 2002,2005,2007 Free Software Foundation, Inc. +# +# This gensymlist.sh is free software; the author +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +nm="$1" +shift + +cat <. + */ + +#include "grub_emu_init.h" + +EOF + +cat < /dev/null; then + echo "grub_${line}_init ();" | sed 's,\.mod,,g;' + fi +done + +cat < /dev/null; then + echo "grub_${line}_fini ();" | sed 's,\.mod,,g;' + fi +done + +cat <. + */ + +EOF + +cat < /dev/null; then + echo "void grub_${line}_init (void);" | sed 's,\.mod,,g;' + fi + if ${nm} --defined-only -P -p ${line} | grep grub_mod_fini > /dev/null; then + echo "void grub_${line}_fini (void);" | sed 's,\.mod,,g;' + fi +done diff --git a/genkernsyms.sh.in b/genkernsyms.sh.in index b2f3f7af9..c5c63b2d5 100644 --- a/genkernsyms.sh.in +++ b/genkernsyms.sh.in @@ -14,7 +14,7 @@ ### The configure script will replace these variables. : ${srcdir=@srcdir@} -: ${CC=@CC@} +: ${CC=@TARGET_CC@} u= grep "^#define HAVE_ASM_USCORE" config.h >/dev/null 2>&1 && u="_" diff --git a/genmk.rb b/genmk.rb index df03e1dfe..e62dbd4f6 100644 --- a/genmk.rb +++ b/genmk.rb @@ -91,7 +91,7 @@ endif dir = File.dirname(src) "#{obj}: #{src} $(#{src}_DEPENDENCIES) - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -MD -c -o $@ $< + $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -DGRUB_FILE=\\\"#{src}\\\" -MD -c -o $@ $< -include #{dep} " @@ -143,27 +143,35 @@ mostlyclean-module-#{@name}.#{@rule_count}: MOSTLYCLEAN_MODULE_TARGETS += mostlyclean-module-#{@name}.#{@rule_count} UNDSYMFILES += #{undsym} +ifeq ($(TARGET_NO_MODULES), yes) +#{@name}: #{pre_obj} $(TARGET_OBJ2ELF) + -rm -f $@ + $(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ #{pre_obj} + if test ! -z \"$(TARGET_OBJ2ELF)\"; then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi + if test x$(TARGET_NO_STRIP) != xyes ; then $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -K _grub_mod_init -K _grub_mod_fini -R .note -R .comment $@; fi +else ifneq ($(TARGET_APPLE_CC),1) #{@name}: #{pre_obj} #{mod_obj} $(TARGET_OBJ2ELF) -rm -f $@ $(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ #{pre_obj} #{mod_obj} if test ! -z \"$(TARGET_OBJ2ELF)\"; then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi - $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -K _grub_mod_init -K _grub_mod_fini -R .note -R .comment $@ + if test x$(TARGET_NO_STRIP) != xyes ; then $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -K _grub_mod_init -K _grub_mod_fini -R .note -R .comment $@; fi else #{@name}: #{pre_obj} #{mod_obj} $(TARGET_OBJ2ELF) -rm -f $@ -rm -f $@.bin $(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@.bin #{pre_obj} #{mod_obj} - $(OBJCONV) -f$(TARGET_MODULE_FORMAT) -nr:_grub_mod_init:grub_mod_init -nr:_grub_mod_fini:grub_mod_fini -wd1106 -nu -nd $@.bin $@ + $(OBJCONV) -f$(TARGET_MODULE_FORMAT) -nr:_grub_mod_init:grub_mod_init -nr:_grub_mod_fini:grub_mod_fini -wd1106 -ew2030 -ew2050 -nu -nd $@.bin $@ -rm -f $@.bin endif +endif #{pre_obj}: $(#{prefix}_DEPENDENCIES) #{objs_str} -rm -f $@ $(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ #{objs_str} #{mod_obj}: #{mod_src} - $(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(#{prefix}_CFLAGS) -c -o $@ $< + $(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(#{prefix}_CFLAGS) -DGRUB_FILE=\\\"#{mod_src}\\\" -c -o $@ $< #{mod_src}: $(builddir)/moddep.lst $(srcdir)/genmodsrc.sh sh $(srcdir)/genmodsrc.sh '#{mod_name}' $< > $@ || (rm -f $@; exit 1) @@ -197,11 +205,11 @@ endif dir = File.dirname(src) "#{obj}: #{src} $(#{src}_DEPENDENCIES) - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -MD -c -o $@ $< + $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -DGRUB_FILE=\\\"#{src}\\\" -MD -c -o $@ $< -include #{dep} clean-module-#{extra_target}.#{@rule_count}: - rm -f #{command} #{fs} #{partmap} #{handler} #{parttool} #{video} + rm -f #{command} #{fs} #{partmap} #{handler} #{parttool} #{video} #{terminal} CLEAN_MODULE_TARGETS += clean-module-#{extra_target}.#{@rule_count} @@ -299,7 +307,7 @@ MOSTLYCLEAN_UTILITY_TARGETS += mostlyclean-utility-#{@name}.#{@rule_count} dir = File.dirname(src) "#{obj}: #{src} $(#{src}_DEPENDENCIES) - $(CC) -I#{dir} -I$(srcdir)/#{dir} $(CPPFLAGS) $(CFLAGS) -DGRUB_UTIL=1 $(#{prefix}_CFLAGS) -MD -c -o $@ $< + $(CC) -I#{dir} -I$(srcdir)/#{dir} $(CPPFLAGS) $(CFLAGS) -DGRUB_UTIL=1 $(#{prefix}_CFLAGS) -DGRUB_FILE=\\\"#{src}\\\" -MD -c -o $@ $< -include #{dep} " @@ -314,28 +322,32 @@ class Program end attr_reader :dir, :name + def print_tail() + prefix = @name.to_var + print "CLEANFILES += #{@name} $(#{prefix}_OBJECTS) +ifeq ($(#{prefix}_RELOCATABLE),yes) +#{@name}: $(#{prefix}_DEPENDENCIES) $(#{prefix}_OBJECTS) + $(TARGET_CC) -Wl,-r,-d -o $@ $(#{prefix}_OBJECTS) $(TARGET_LDFLAGS) $(#{prefix}_LDFLAGS) + if test x$(TARGET_NO_STRIP) != xyes ; then $(STRIP) --strip-unneeded -K start -R .note -R .comment $@; fi +else +#{@name}: $(#{prefix}_DEPENDENCIES) $(#{prefix}_OBJECTS) + $(TARGET_CC) -o $@ $(#{prefix}_OBJECTS) $(TARGET_LDFLAGS) $(#{prefix}_LDFLAGS) + if test x$(TARGET_NO_STRIP) != xyes ; then $(STRIP) -R .rel.dyn -R .reginfo -R .note -R .comment $@; fi +endif + +" + end + def rule(sources) prefix = @name.to_var objs = sources.collect do |src| raise "unknown source file `#{src}'" if /\.[cS]$/ !~ src prefix + '-' + src.to_obj end - objs_str = objs.join(' '); deps = objs.collect {|obj| obj.suffix('d')} deps_str = deps.join(' '); - "CLEANFILES += #{@name} #{objs_str} -MOSTLYCLEANFILES += #{deps_str} - -ifeq ($(#{prefix}_RELOCATABLE),yes) -#{@name}: $(#{prefix}_DEPENDENCIES) #{objs_str} - $(TARGET_CC) -Wl,-r,-d -o $@ #{objs_str} $(TARGET_LDFLAGS) $(#{prefix}_LDFLAGS) - $(STRIP) --strip-unneeded -K start -R .note -R .comment $@ -else -#{@name}: $(#{prefix}_DEPENDENCIES) #{objs_str} - $(TARGET_CC) -o $@ #{objs_str} $(TARGET_LDFLAGS) $(#{prefix}_LDFLAGS) - $(STRIP) -R .rel.dyn -R .reginfo -R .note -R .comment $@ -endif + "MOSTLYCLEANFILES += #{deps_str} " + objs.collect_with_index do |obj, i| src = sources[i] @@ -346,10 +358,11 @@ endif dir = File.dirname(src) "#{obj}: #{src} $(#{src}_DEPENDENCIES) - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -MD -c -o $@ $< + $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -DGRUB_FILE=\\\"#{src}\\\" -MD -c -o $@ $< -include #{dep} +#{prefix}_OBJECTS += #{obj} " end.join('') end @@ -458,4 +471,5 @@ while l = gets end utils.each {|util| util.print_tail()} +programs.each {|program| program.print_tail()} diff --git a/genmoddep.awk b/genmoddep.awk index 19ac80c71..48419a091 100644 --- a/genmoddep.awk +++ b/genmoddep.awk @@ -32,13 +32,12 @@ FNR == 1 { else if ($1 != "__gnu_local_gp") { printf "%s in %s is not defined\n", $1, module >"/dev/stderr"; error++; - exit; } } # Output the result. END { - if (error == 1) + if (error >= 1) exit 1; for (mod in modtab) { diff --git a/gensymlist.sh.in b/gensymlist.sh.in index 27fc5e61a..3c3ddfa6c 100644 --- a/gensymlist.sh.in +++ b/gensymlist.sh.in @@ -14,7 +14,7 @@ ### The configure script will replace these variables. : ${srcdir=@srcdir@} -: ${CC=@CC@} +: ${CC=@TARGET_CC@} cat <. */ +#include EOF for i in $*; do diff --git a/gentrigtables.c b/gentrigtables.c new file mode 100644 index 000000000..772cd6224 --- /dev/null +++ b/gentrigtables.c @@ -0,0 +1,48 @@ +/* Generate trigonometric function tables. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008, 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#define _GNU_SOURCE 1 + +#include +#include +#include + +int +main () +{ + int i; + + printf ("#include \n"); + +#define TAB(op) \ + printf ("grub_int16_t grub_trig_" #op "tab[] =\n{"); \ + for (i = 0; i < GRUB_TRIG_ANGLE_MAX; i++) \ + { \ + double x = i * 2 * M_PI / GRUB_TRIG_ANGLE_MAX; \ + if (i % 10 == 0) \ + printf ("\n "); \ + printf ("%d,", (int) (round (op (x) * GRUB_TRIG_FRACTION_SCALE))); \ + } \ + printf ("\n};\n") + + TAB(sin); + TAB(cos); + + return 0; +} diff --git a/gettext/gettext.c b/gettext/gettext.c index 9a1756be7..0aa8decbd 100644 --- a/gettext/gettext.c +++ b/gettext/gettext.c @@ -279,13 +279,6 @@ grub_gettext_init_ext (const char *lang) /* mo_file e.g.: /boot/grub/locale/ca.mo */ - mo_file = - grub_malloc (grub_strlen (locale_dir) + grub_strlen ("/") + - grub_strlen (lang) + grub_strlen (".mo") + 1); - - /* Warning: if changing some paths in the below line, change the grub_malloc - contents below. */ - mo_file = grub_xasprintf ("%s/%s.mo", locale_dir, lang); if (!mo_file) return; diff --git a/gfxmenu/gfxmenu.c b/gfxmenu/gfxmenu.c new file mode 100644 index 000000000..a2e765156 --- /dev/null +++ b/gfxmenu/gfxmenu.c @@ -0,0 +1,144 @@ +/* gfxmenu.c - Graphical menu interface controller. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +grub_gfxmenu_view_t cached_view; + +static void +grub_gfxmenu_viewer_fini (void *data __attribute__ ((unused))) +{ +} + +/* FIXME: Previously 't' changed to text menu is it necessary? */ +static grub_err_t +grub_gfxmenu_try (int entry, grub_menu_t menu, int nested) +{ + grub_gfxmenu_view_t view = NULL; + const char *theme_path; + struct grub_menu_viewer *instance; + grub_err_t err; + struct grub_video_mode_info mode_info; + + theme_path = grub_env_get ("theme"); + if (! theme_path) + { + grub_error_push (); + grub_gfxterm_fullscreen (); + grub_error_pop (); + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "no theme specified"); + } + + instance = grub_zalloc (sizeof (*instance)); + if (!instance) + { + grub_error_push (); + grub_gfxterm_fullscreen (); + grub_error_pop (); + return grub_errno; + } + + err = grub_video_get_info (&mode_info); + if (err) + { + grub_error_push (); + grub_gfxterm_fullscreen (); + grub_error_pop (); + return err; + } + + if (!cached_view || grub_strcmp (cached_view->theme_path, theme_path) != 0 + || cached_view->screen.width != mode_info.width + || cached_view->screen.height != mode_info.height) + { + grub_free (cached_view); + /* Create the view. */ + cached_view = grub_gfxmenu_view_new (theme_path, mode_info.width, + mode_info.height); + } + + if (! cached_view) + { + grub_free (instance); + grub_error_push (); + grub_gfxterm_fullscreen (); + grub_error_pop (); + return grub_errno; + } + + view = cached_view; + + view->double_repaint = (mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED) + && !(mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + view->selected = entry; + view->menu = menu; + view->nested = nested; + view->first_timeout = -1; + + grub_gfxmenu_view_draw (view); + + instance->data = view; + instance->set_chosen_entry = grub_gfxmenu_set_chosen_entry; + instance->fini = grub_gfxmenu_viewer_fini; + instance->print_timeout = grub_gfxmenu_print_timeout; + instance->clear_timeout = grub_gfxmenu_clear_timeout; + + grub_menu_register_viewer (instance); + + return GRUB_ERR_NONE; +} + +GRUB_MOD_INIT (gfxmenu) +{ + struct grub_term_output *term; + + FOR_ACTIVE_TERM_OUTPUTS(term) + if (grub_gfxmenu_try_hook && grub_strcmp (term->name, "gfxterm") == 0) + { + grub_gfxterm_fullscreen (); + break; + } + + grub_gfxmenu_try_hook = grub_gfxmenu_try; +} + +GRUB_MOD_FINI (gfxmenu) +{ + grub_gfxmenu_view_destroy (cached_view); + grub_gfxmenu_try_hook = NULL; +} diff --git a/gfxmenu/gui_box.c b/gfxmenu/gui_box.c new file mode 100644 index 000000000..38b15f96d --- /dev/null +++ b/gfxmenu/gui_box.c @@ -0,0 +1,412 @@ +/* gui_box.c - GUI container that stack components. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +struct component_node +{ + grub_gui_component_t component; + struct component_node *next; + struct component_node *prev; +}; + +typedef struct grub_gui_box *grub_gui_box_t; + +typedef void (*layout_func_t) (grub_gui_box_t self, int modify_layout, + unsigned *minimal_width, + unsigned *minimal_height); + +struct grub_gui_box +{ + struct grub_gui_container container; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + + /* Doubly linked list of components with dummy head & tail nodes. */ + struct component_node chead; + struct component_node ctail; + + /* The layout function: differs for vertical and horizontal boxes. */ + layout_func_t layout_func; +}; + +static void +box_destroy (void *vself) +{ + grub_gui_box_t self = vself; + struct component_node *cur; + struct component_node *next; + for (cur = self->chead.next; cur != &self->ctail; cur = next) + { + /* Copy the 'next' pointer, since we need it for the next iteration, + and we're going to free the memory it is stored in. */ + next = cur->next; + /* Destroy the child component. */ + cur->component->ops->destroy (cur->component); + /* Free the linked list node. */ + grub_free (cur); + } + grub_free (self); +} + +static const char * +box_get_id (void *vself) +{ + grub_gui_box_t self = vself; + return self->id; +} + +static int +box_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return (grub_strcmp (type, "component") == 0 + || grub_strcmp (type, "container") == 0); +} + +static void +layout_horizontally (grub_gui_box_t self, int modify_layout, + unsigned *min_width, unsigned *min_height) +{ + /* Start at the left (chead) and set the x coordinates as we go right. */ + /* All components have their width set to the box's width. */ + + struct component_node *cur; + unsigned w = 0, mwfrac = 0, h = 0, x = 0; + grub_fixed_signed_t wfrac = 0; + int bogus_frac = 0; + + for (cur = self->chead.next; cur != &self->ctail; cur = cur->next) + { + grub_gui_component_t c = cur->component; + unsigned mw = 0, mh = 0; + + if (c->ops->get_minimal_size) + c->ops->get_minimal_size (c, &mw, &mh); + + if (c->h > (signed) h) + h = c->h; + if (mh > h) + h = mh; + wfrac += c->wfrac; + w += c->w; + if (mw - c->w > 0) + mwfrac += mw - c->w; + } + if (wfrac > GRUB_FIXED_1 || (w > 0 && wfrac == GRUB_FIXED_1)) + bogus_frac = 1; + + if (min_width) + { + if (wfrac < GRUB_FIXED_1) + *min_width = grub_fixed_sfs_divide (w, GRUB_FIXED_1 - wfrac); + else + *min_width = w; + if (*min_width < w + mwfrac) + *min_width = w + mwfrac; + } + if (min_height) + *min_height = h; + + if (!modify_layout) + return; + + for (cur = self->chead.next; cur != &self->ctail; cur = cur->next) + { + grub_video_rect_t r; + grub_gui_component_t c = cur->component; + unsigned mw = 0, mh = 0; + + r.x = x; + r.y = 0; + r.height = h; + + if (c->ops->get_minimal_size) + c->ops->get_minimal_size (c, &mw, &mh); + + r.width = c->w; + if (!bogus_frac) + r.width += grub_fixed_sfs_multiply (self->bounds.width, c->wfrac); + + if (r.width < mw) + r.width = mw; + + c->ops->set_bounds (c, &r); + + x += r.width; + } +} + +static void +layout_vertically (grub_gui_box_t self, int modify_layout, + unsigned *min_width, unsigned *min_height) +{ + /* Start at the top (chead) and set the y coordinates as we go rdown. */ + /* All components have their height set to the box's height. */ + + struct component_node *cur; + unsigned h = 0, mhfrac = 0, w = 0, y = 0; + grub_fixed_signed_t hfrac = 0; + int bogus_frac = 0; + + for (cur = self->chead.next; cur != &self->ctail; cur = cur->next) + { + grub_gui_component_t c = cur->component; + unsigned mw = 0, mh = 0; + + if (c->ops->get_minimal_size) + c->ops->get_minimal_size (c, &mw, &mh); + + if (c->w > (signed) w) + w = c->w; + if (mw > w) + w = mw; + hfrac += c->hfrac; + h += c->h; + if (mh - c->h > 0) + mhfrac += mh - c->h; + } + if (hfrac > GRUB_FIXED_1 || (h > 0 && hfrac == GRUB_FIXED_1)) + bogus_frac = 1; + + if (min_height) + { + if (hfrac < GRUB_FIXED_1) + *min_height = grub_fixed_sfs_divide (h, GRUB_FIXED_1 - hfrac); + else + *min_height = h; + if (*min_height < h + mhfrac) + *min_height = h + mhfrac; + } + if (min_width) + *min_width = w; + + if (!modify_layout) + return; + + for (cur = self->chead.next; cur != &self->ctail; cur = cur->next) + { + grub_video_rect_t r; + grub_gui_component_t c = cur->component; + unsigned mw = 0, mh = 0; + + r.x = 0; + r.y = y; + r.width = w; + + if (c->ops->get_minimal_size) + c->ops->get_minimal_size (c, &mw, &mh); + + r.height = c->h; + if (!bogus_frac) + r.height += grub_fixed_sfs_multiply (self->bounds.height, c->hfrac); + + if (r.height < mh) + r.height = mh; + + c->ops->set_bounds (c, &r); + + y += r.height; + } +} + +static void +box_paint (void *vself, const grub_video_rect_t *region) +{ + grub_gui_box_t self = vself; + struct component_node *cur; + grub_video_rect_t vpsave; + + grub_gui_set_viewport (&self->bounds, &vpsave); + for (cur = self->chead.next; cur != &self->ctail; cur = cur->next) + { + grub_gui_component_t comp = cur->component; + comp->ops->paint (comp, region); + } + grub_gui_restore_viewport (&vpsave); +} + +static void +box_set_parent (void *vself, grub_gui_container_t parent) +{ + grub_gui_box_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +box_get_parent (void *vself) +{ + grub_gui_box_t self = vself; + return self->parent; +} + +static void +box_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + grub_gui_box_t self = vself; + self->bounds = *bounds; + self->layout_func (self, 1, 0, 0); /* Relayout the children. */ +} + +static void +box_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + grub_gui_box_t self = vself; + *bounds = self->bounds; +} + +/* The box's preferred size is based on the preferred sizes + of its children. */ +static void +box_get_minimal_size (void *vself, unsigned *width, unsigned *height) +{ + grub_gui_box_t self = vself; + self->layout_func (self, 0, width, height); /* Just calculate the size. */ +} + +static grub_err_t +box_set_property (void *vself, const char *name, const char *value) +{ + grub_gui_box_t self = vself; + if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + { + self->id = grub_strdup (value); + if (! self->id) + return grub_errno; + } + else + self->id = 0; + } + + return grub_errno; +} + +static void +box_add (void *vself, grub_gui_component_t comp) +{ + grub_gui_box_t self = vself; + struct component_node *node; + node = grub_malloc (sizeof (*node)); + if (! node) + return; /* Note: probably should handle the error. */ + node->component = comp; + /* Insert the node before the tail. */ + node->prev = self->ctail.prev; + node->prev->next = node; + node->next = &self->ctail; + node->next->prev = node; + + comp->ops->set_parent (comp, (grub_gui_container_t) self); + self->layout_func (self, 1, 0, 0); /* Relayout the children. */ +} + +static void +box_remove (void *vself, grub_gui_component_t comp) +{ + grub_gui_box_t self = vself; + struct component_node *cur; + for (cur = self->chead.next; cur != &self->ctail; cur = cur->next) + { + if (cur->component == comp) + { + /* Unlink 'cur' from the list. */ + cur->prev->next = cur->next; + cur->next->prev = cur->prev; + /* Free the node's memory (but don't destroy the component). */ + grub_free (cur); + /* Must not loop again, since 'cur' would be dereferenced! */ + return; + } + } +} + +static void +box_iterate_children (void *vself, + grub_gui_component_callback cb, void *userdata) +{ + grub_gui_box_t self = vself; + struct component_node *cur; + for (cur = self->chead.next; cur != &self->ctail; cur = cur->next) + cb (cur->component, userdata); +} + +static struct grub_gui_component_ops box_comp_ops = + { + .destroy = box_destroy, + .get_id = box_get_id, + .is_instance = box_is_instance, + .paint = box_paint, + .set_parent = box_set_parent, + .get_parent = box_get_parent, + .set_bounds = box_set_bounds, + .get_bounds = box_get_bounds, + .get_minimal_size = box_get_minimal_size, + .set_property = box_set_property + }; + +static struct grub_gui_container_ops box_ops = +{ + .add = box_add, + .remove = box_remove, + .iterate_children = box_iterate_children +}; + +/* Box constructor. Specify the appropriate layout function to create + a horizontal or vertical stacking box. */ +static grub_gui_box_t +box_new (layout_func_t layout_func) +{ + grub_gui_box_t box; + box = grub_zalloc (sizeof (*box)); + if (! box) + return 0; + box->container.ops = &box_ops; + box->container.component.ops = &box_comp_ops; + box->chead.next = &box->ctail; + box->ctail.prev = &box->chead; + box->layout_func = layout_func; + return box; +} + +/* Create a new container that stacks its child components horizontally, + from left to right. Each child get a width corresponding to its + preferred width. The height of each child is set the maximum of the + preferred heights of all children. */ +grub_gui_container_t +grub_gui_hbox_new (void) +{ + return (grub_gui_container_t) box_new (layout_horizontally); +} + +/* Create a new container that stacks its child components verticallyj, + from top to bottom. Each child get a height corresponding to its + preferred height. The width of each child is set the maximum of the + preferred widths of all children. */ +grub_gui_container_t +grub_gui_vbox_new (void) +{ + return (grub_gui_container_t) box_new (layout_vertically); +} diff --git a/gfxmenu/gui_canvas.c b/gfxmenu/gui_canvas.c new file mode 100644 index 000000000..b3919c2d3 --- /dev/null +++ b/gfxmenu/gui_canvas.c @@ -0,0 +1,267 @@ +/* gui_canvas.c - GUI container allowing manually placed components. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +/* TODO Add layering so that components can be properly overlaid. */ + +struct component_node +{ + grub_gui_component_t component; + struct component_node *next; +}; + +struct grub_gui_canvas +{ + struct grub_gui_container container; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + /* Component list (dummy head node). */ + struct component_node components; +}; + +typedef struct grub_gui_canvas *grub_gui_canvas_t; + +static void +canvas_destroy (void *vself) +{ + grub_gui_canvas_t self = vself; + struct component_node *cur; + struct component_node *next; + for (cur = self->components.next; cur; cur = next) + { + /* Copy the 'next' pointer, since we need it for the next iteration, + and we're going to free the memory it is stored in. */ + next = cur->next; + /* Destroy the child component. */ + cur->component->ops->destroy (cur->component); + /* Free the linked list node. */ + grub_free (cur); + } + grub_free (self); +} + +static const char * +canvas_get_id (void *vself) +{ + grub_gui_canvas_t self = vself; + return self->id; +} + +static int +canvas_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return (grub_strcmp (type, "component") == 0 + || grub_strcmp (type, "container") == 0); +} + +static void +canvas_paint (void *vself, const grub_video_rect_t *region) +{ + grub_gui_canvas_t self = vself; + struct component_node *cur; + grub_video_rect_t vpsave; + + grub_gui_set_viewport (&self->bounds, &vpsave); + for (cur = self->components.next; cur; cur = cur->next) + { + grub_video_rect_t r; + grub_gui_component_t comp; + signed x, y, w, h; + + comp = cur->component; + + w = grub_fixed_sfs_multiply (self->bounds.width, comp->wfrac) + comp->w; + h = grub_fixed_sfs_multiply (self->bounds.height, comp->hfrac) + comp->h; + x = grub_fixed_sfs_multiply (self->bounds.width, comp->xfrac) + comp->x; + y = grub_fixed_sfs_multiply (self->bounds.height, comp->yfrac) + comp->y; + + if (comp->ops->get_minimal_size) + { + unsigned mw; + unsigned mh; + comp->ops->get_minimal_size (comp, &mw, &mh); + if (w < (signed) mw) + w = mw; + if (h < (signed) mh) + h = mh; + } + + /* Sanity checks. */ + if (w <= 0) + w = 32; + if (h <= 0) + h = 32; + + if (x >= (signed) self->bounds.width) + x = self->bounds.width - 32; + if (y >= (signed) self->bounds.height) + y = self->bounds.height - 32; + + if (x < 0) + x = 0; + if (y < 0) + y = 0; + + if (x + w >= (signed) self->bounds.width) + w = self->bounds.width - x; + if (y + h >= (signed) self->bounds.height) + h = self->bounds.height - y; + + r.x = x; + r.y = y; + r.width = w; + r.height = h; + comp->ops->set_bounds (comp, &r); + + /* Paint the child. */ + if (grub_video_have_common_points (region, &r)) + comp->ops->paint (comp, region); + } + grub_gui_restore_viewport (&vpsave); +} + +static void +canvas_set_parent (void *vself, grub_gui_container_t parent) +{ + grub_gui_canvas_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +canvas_get_parent (void *vself) +{ + grub_gui_canvas_t self = vself; + return self->parent; +} + +static void +canvas_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + grub_gui_canvas_t self = vself; + self->bounds = *bounds; +} + +static void +canvas_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + grub_gui_canvas_t self = vself; + *bounds = self->bounds; +} + +static grub_err_t +canvas_set_property (void *vself, const char *name, const char *value) +{ + grub_gui_canvas_t self = vself; + if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + { + self->id = grub_strdup (value); + if (! self->id) + return grub_errno; + } + else + self->id = 0; + } + return grub_errno; +} + +static void +canvas_add (void *vself, grub_gui_component_t comp) +{ + grub_gui_canvas_t self = vself; + struct component_node *node; + node = grub_malloc (sizeof (*node)); + if (! node) + return; /* Note: probably should handle the error. */ + node->component = comp; + node->next = self->components.next; + self->components.next = node; + comp->ops->set_parent (comp, (grub_gui_container_t) self); +} + +static void +canvas_remove (void *vself, grub_gui_component_t comp) +{ + grub_gui_canvas_t self = vself; + struct component_node *cur; + struct component_node *prev; + prev = &self->components; + for (cur = self->components.next; cur; prev = cur, cur = cur->next) + { + if (cur->component == comp) + { + /* Unlink 'cur' from the list. */ + prev->next = cur->next; + /* Free the node's memory (but don't destroy the component). */ + grub_free (cur); + /* Must not loop again, since 'cur' would be dereferenced! */ + return; + } + } +} + +static void +canvas_iterate_children (void *vself, + grub_gui_component_callback cb, void *userdata) +{ + grub_gui_canvas_t self = vself; + struct component_node *cur; + for (cur = self->components.next; cur; cur = cur->next) + cb (cur->component, userdata); +} + +static struct grub_gui_component_ops canvas_comp_ops = +{ + .destroy = canvas_destroy, + .get_id = canvas_get_id, + .is_instance = canvas_is_instance, + .paint = canvas_paint, + .set_parent = canvas_set_parent, + .get_parent = canvas_get_parent, + .set_bounds = canvas_set_bounds, + .get_bounds = canvas_get_bounds, + .set_property = canvas_set_property +}; + +static struct grub_gui_container_ops canvas_ops = +{ + .add = canvas_add, + .remove = canvas_remove, + .iterate_children = canvas_iterate_children +}; + +grub_gui_container_t +grub_gui_canvas_new (void) +{ + grub_gui_canvas_t canvas; + canvas = grub_zalloc (sizeof (*canvas)); + if (! canvas) + return 0; + canvas->container.ops = &canvas_ops; + canvas->container.component.ops = &canvas_comp_ops; + return (grub_gui_container_t) canvas; +} diff --git a/gfxmenu/gui_circular_progress.c b/gfxmenu/gui_circular_progress.c new file mode 100644 index 000000000..9a859ee2e --- /dev/null +++ b/gfxmenu/gui_circular_progress.c @@ -0,0 +1,302 @@ +/* gui_circular_process.c - GUI circular progress indicator component. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct grub_gui_circular_progress +{ + struct grub_gui_progress progress; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + int visible; + int start; + int end; + int value; + int num_ticks; + int start_angle; + int ticks_disappear; + char *theme_dir; + int need_to_load_pixmaps; + char *center_file; + char *tick_file; + struct grub_video_bitmap *center_bitmap; + struct grub_video_bitmap *tick_bitmap; +}; + +typedef struct grub_gui_circular_progress *circular_progress_t; + +static void +circprog_destroy (void *vself) +{ + circular_progress_t self = vself; + grub_free (self); +} + +static const char * +circprog_get_id (void *vself) +{ + circular_progress_t self = vself; + return self->id; +} + +static int +circprog_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return grub_strcmp (type, "component") == 0; +} + +static struct grub_video_bitmap * +load_bitmap (const char *dir, const char *file) +{ + struct grub_video_bitmap *bitmap; + char *abspath; + + /* Check arguments. */ + if (! dir || ! file) + return 0; + + /* Resolve to an absolute path. */ + abspath = grub_resolve_relative_path (dir, file); + if (! abspath) + return 0; + + /* Load the image. */ + grub_errno = GRUB_ERR_NONE; + grub_video_bitmap_load (&bitmap, abspath); + grub_errno = GRUB_ERR_NONE; + + grub_free (abspath); + return bitmap; +} + +static int +check_pixmaps (circular_progress_t self) +{ + if (self->need_to_load_pixmaps) + { + if (self->center_bitmap) + grub_video_bitmap_destroy (self->center_bitmap); + self->center_bitmap = load_bitmap (self->theme_dir, self->center_file); + self->tick_bitmap = load_bitmap (self->theme_dir, self->tick_file); + self->need_to_load_pixmaps = 0; + } + + return (self->center_bitmap != 0 && self->tick_bitmap != 0); +} + +static void +circprog_paint (void *vself, const grub_video_rect_t *region) +{ + circular_progress_t self = vself; + + if (! self->visible) + return; + + if (!grub_video_have_common_points (region, &self->bounds)) + return; + + if (! check_pixmaps (self)) + return; + + grub_video_rect_t vpsave; + grub_gui_set_viewport (&self->bounds, &vpsave); + + int width = self->bounds.width; + int height = self->bounds.height; + int center_width = grub_video_bitmap_get_width (self->center_bitmap); + int center_height = grub_video_bitmap_get_height (self->center_bitmap); + int tick_width = grub_video_bitmap_get_width (self->tick_bitmap); + int tick_height = grub_video_bitmap_get_height (self->tick_bitmap); + grub_video_blit_bitmap (self->center_bitmap, GRUB_VIDEO_BLIT_BLEND, + (width - center_width) / 2, + (height - center_height) / 2, 0, 0, + center_width, center_height); + + int radius = width / 2 - tick_width / 2 - 1; + int nticks; + int tick_begin; + int tick_end; + if (self->end == self->start) + nticks = 0; + else + nticks = (self->num_ticks + * (self->value - self->start) + / (self->end - self->start)); + /* Do ticks appear or disappear as the value approached the end? */ + if (self->ticks_disappear) + { + tick_begin = nticks; + tick_end = self->num_ticks - 1; + } + else + { + tick_begin = 0; + tick_end = nticks - 1; + } + + int i; + for (i = tick_begin; i < tick_end; i++) + { + int x; + int y; + int angle; + + /* Calculate the location of the tick. */ + angle = self->start_angle + i * GRUB_TRIG_ANGLE_MAX / self->num_ticks; + x = width / 2 + (grub_cos (angle) * radius / GRUB_TRIG_FRACTION_SCALE); + y = height / 2 + (grub_sin (angle) * radius / GRUB_TRIG_FRACTION_SCALE); + + /* Adjust (x,y) so the tick is centered. */ + x -= tick_width / 2; + y -= tick_height / 2; + + /* Draw the tick. */ + grub_video_blit_bitmap (self->tick_bitmap, GRUB_VIDEO_BLIT_BLEND, + x, y, 0, 0, tick_width, tick_height); + } + + grub_gui_restore_viewport (&vpsave); +} + +static void +circprog_set_parent (void *vself, grub_gui_container_t parent) +{ + circular_progress_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +circprog_get_parent (void *vself) +{ + circular_progress_t self = vself; + return self->parent; +} + +static void +circprog_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + circular_progress_t self = vself; + self->bounds = *bounds; +} + +static void +circprog_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + circular_progress_t self = vself; + *bounds = self->bounds; +} + +static grub_err_t +circprog_set_property (void *vself, const char *name, const char *value) +{ + circular_progress_t self = vself; + if (grub_strcmp (name, "num_ticks") == 0) + { + self->num_ticks = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "start_angle") == 0) + { + self->start_angle = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "ticks_disappear") == 0) + { + self->ticks_disappear = grub_strcmp (value, "false") != 0; + } + else if (grub_strcmp (name, "center_bitmap") == 0) + { + self->need_to_load_pixmaps = 1; + grub_free (self->center_file); + self->center_file = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "tick_bitmap") == 0) + { + self->need_to_load_pixmaps = 1; + grub_free (self->tick_file); + self->tick_file = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "theme_dir") == 0) + { + self->need_to_load_pixmaps = 1; + grub_free (self->theme_dir); + self->theme_dir = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + self->id = grub_strdup (value); + else + self->id = 0; + } + return grub_errno; +} + +static void +circprog_set_state (void *vself, int visible, int start, + int current, int end) +{ + circular_progress_t self = vself; + self->visible = visible; + self->start = start; + self->value = current; + self->end = end; +} + +static struct grub_gui_component_ops circprog_ops = +{ + .destroy = circprog_destroy, + .get_id = circprog_get_id, + .is_instance = circprog_is_instance, + .paint = circprog_paint, + .set_parent = circprog_set_parent, + .get_parent = circprog_get_parent, + .set_bounds = circprog_set_bounds, + .get_bounds = circprog_get_bounds, + .set_property = circprog_set_property +}; + +static struct grub_gui_progress_ops circprog_prog_ops = + { + .set_state = circprog_set_state + }; + +grub_gui_component_t +grub_gui_circular_progress_new (void) +{ + circular_progress_t self; + self = grub_zalloc (sizeof (*self)); + if (! self) + return 0; + self->progress.ops = &circprog_prog_ops; + self->progress.component.ops = &circprog_ops; + self->visible = 1; + self->num_ticks = 64; + self->start_angle = -64; + + return (grub_gui_component_t) self; +} diff --git a/gfxmenu/gui_image.c b/gfxmenu/gui_image.c new file mode 100644 index 000000000..3988f4ba8 --- /dev/null +++ b/gfxmenu/gui_image.c @@ -0,0 +1,269 @@ +/* gui_image.c - GUI component to display an image. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +struct grub_gui_image +{ + struct grub_gui_component component; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + char *theme_dir; + struct grub_video_bitmap *raw_bitmap; + struct grub_video_bitmap *bitmap; +}; + +typedef struct grub_gui_image *grub_gui_image_t; + +static void +image_destroy (void *vself) +{ + grub_gui_image_t self = vself; + + /* Free the scaled bitmap, unless it's a reference to the raw bitmap. */ + if (self->bitmap && (self->bitmap != self->raw_bitmap)) + grub_video_bitmap_destroy (self->bitmap); + if (self->raw_bitmap) + grub_video_bitmap_destroy (self->raw_bitmap); + + grub_free (self); +} + +static const char * +image_get_id (void *vself) +{ + grub_gui_image_t self = vself; + return self->id; +} + +static int +image_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return grub_strcmp (type, "component") == 0; +} + +static void +image_paint (void *vself, const grub_video_rect_t *region) +{ + grub_gui_image_t self = vself; + grub_video_rect_t vpsave; + + if (! self->bitmap) + return; + if (!grub_video_have_common_points (region, &self->bounds)) + return; + + grub_gui_set_viewport (&self->bounds, &vpsave); + grub_video_blit_bitmap (self->bitmap, GRUB_VIDEO_BLIT_BLEND, + 0, 0, 0, 0, + grub_video_bitmap_get_width (self->bitmap), + grub_video_bitmap_get_height (self->bitmap)); + grub_gui_restore_viewport (&vpsave); +} + +static void +image_set_parent (void *vself, grub_gui_container_t parent) +{ + grub_gui_image_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +image_get_parent (void *vself) +{ + grub_gui_image_t self = vself; + return self->parent; +} + +static grub_err_t +rescale_image (grub_gui_image_t self) +{ + if (! self->raw_bitmap) + { + if (self->bitmap) + { + grub_video_bitmap_destroy (self->bitmap); + self->bitmap = 0; + } + return grub_errno; + } + + unsigned width = self->bounds.width; + unsigned height = self->bounds.height; + + if (self->bitmap + && (grub_video_bitmap_get_width (self->bitmap) == width) + && (grub_video_bitmap_get_height (self->bitmap) == height)) + { + /* Nothing to do; already the right size. */ + return grub_errno; + } + + /* Free any old scaled bitmap, + *unless* it's a reference to the raw bitmap. */ + if (self->bitmap && (self->bitmap != self->raw_bitmap)) + grub_video_bitmap_destroy (self->bitmap); + + self->bitmap = 0; + + /* Create a scaled bitmap, unless the requested size is the same + as the raw size -- in that case a reference is made. */ + if (grub_video_bitmap_get_width (self->raw_bitmap) == width + && grub_video_bitmap_get_height (self->raw_bitmap) == height) + { + self->bitmap = self->raw_bitmap; + return grub_errno; + } + + /* Don't scale to an invalid size. */ + if (width == 0 || height == 0) + return grub_errno; + + /* Create the scaled bitmap. */ + grub_video_bitmap_create_scaled (&self->bitmap, + width, + height, + self->raw_bitmap, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + if (grub_errno != GRUB_ERR_NONE) + { + grub_error_push (); + grub_error (grub_errno, "failed to scale bitmap for image component"); + } + return grub_errno; +} + +static void +image_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + grub_gui_image_t self = vself; + self->bounds = *bounds; + rescale_image (self); +} + +static void +image_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + grub_gui_image_t self = vself; + *bounds = self->bounds; +} + +/* FIXME: inform rendering system it's not forced minimum. */ +static void +image_get_minimal_size (void *vself, unsigned *width, unsigned *height) +{ + grub_gui_image_t self = vself; + + if (self->raw_bitmap) + { + *width = grub_video_bitmap_get_width (self->raw_bitmap); + *height = grub_video_bitmap_get_height (self->raw_bitmap); + } + else + { + *width = 0; + *height = 0; + } +} + +static grub_err_t +load_image (grub_gui_image_t self, const char *path) +{ + struct grub_video_bitmap *bitmap; + if (grub_video_bitmap_load (&bitmap, path) != GRUB_ERR_NONE) + return grub_errno; + + if (self->bitmap && (self->bitmap != self->raw_bitmap)) + grub_video_bitmap_destroy (self->bitmap); + if (self->raw_bitmap) + grub_video_bitmap_destroy (self->raw_bitmap); + + self->raw_bitmap = bitmap; + return rescale_image (self); +} + +static grub_err_t +image_set_property (void *vself, const char *name, const char *value) +{ + grub_gui_image_t self = vself; + if (grub_strcmp (name, "theme_dir") == 0) + { + grub_free (self->theme_dir); + self->theme_dir = grub_strdup (value); + } + else if (grub_strcmp (name, "file") == 0) + { + char *absvalue; + grub_err_t err; + + /* Resolve to an absolute path. */ + if (! self->theme_dir) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unspecified theme_dir"); + absvalue = grub_resolve_relative_path (self->theme_dir, value); + if (! absvalue) + return grub_errno; + + err = load_image (self, absvalue); + grub_free (absvalue); + + return err; + } + else if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + self->id = grub_strdup (value); + else + self->id = 0; + } + return grub_errno; +} + +static struct grub_gui_component_ops image_ops = +{ + .destroy = image_destroy, + .get_id = image_get_id, + .is_instance = image_is_instance, + .paint = image_paint, + .set_parent = image_set_parent, + .get_parent = image_get_parent, + .set_bounds = image_set_bounds, + .get_bounds = image_get_bounds, + .get_minimal_size = image_get_minimal_size, + .set_property = image_set_property +}; + +grub_gui_component_t +grub_gui_image_new (void) +{ + grub_gui_image_t image; + image = grub_zalloc (sizeof (*image)); + if (! image) + return 0; + image->component.ops = &image_ops; + return (grub_gui_component_t) image; +} + diff --git a/gfxmenu/gui_label.c b/gfxmenu/gui_label.c new file mode 100644 index 000000000..a9dd575ac --- /dev/null +++ b/gfxmenu/gui_label.c @@ -0,0 +1,226 @@ +/* gui_label.c - GUI component to display a line of text. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +static const char *align_options[] = +{ + "left", + "center", + "right", + 0 +}; + +enum align_mode { + align_left, + align_center, + align_right +}; + +struct grub_gui_label +{ + struct grub_gui_component comp; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + int visible; + char *text; + grub_font_t font; + grub_gui_color_t color; + enum align_mode align; +}; + +typedef struct grub_gui_label *grub_gui_label_t; + +static void +label_destroy (void *vself) +{ + grub_gui_label_t self = vself; + grub_free (self->text); + grub_free (self); +} + +static const char * +label_get_id (void *vself) +{ + grub_gui_label_t self = vself; + return self->id; +} + +static int +label_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return grub_strcmp (type, "component") == 0; +} + +static void +label_paint (void *vself, const grub_video_rect_t *region) +{ + grub_gui_label_t self = vself; + + if (! self->visible) + return; + + if (!grub_video_have_common_points (region, &self->bounds)) + return; + + /* Calculate the starting x coordinate. */ + int left_x; + if (self->align == align_left) + left_x = 0; + else if (self->align == align_center) + left_x = ((self->bounds.width + - grub_font_get_string_width (self->font, self->text)) + ) / 2; + else if (self->align == align_right) + left_x = (self->bounds.width + - grub_font_get_string_width (self->font, self->text)); + else + return; /* Invalid alignment. */ + + grub_video_rect_t vpsave; + grub_gui_set_viewport (&self->bounds, &vpsave); + grub_font_draw_string (self->text, + self->font, + grub_gui_map_color (self->color), + left_x, + grub_font_get_ascent (self->font)); + grub_gui_restore_viewport (&vpsave); +} + +static void +label_set_parent (void *vself, grub_gui_container_t parent) +{ + grub_gui_label_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +label_get_parent (void *vself) +{ + grub_gui_label_t self = vself; + return self->parent; +} + +static void +label_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + grub_gui_label_t self = vself; + self->bounds = *bounds; +} + +static void +label_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + grub_gui_label_t self = vself; + *bounds = self->bounds; +} + +static void +label_get_minimal_size (void *vself, unsigned *width, unsigned *height) +{ + grub_gui_label_t self = vself; + *width = grub_font_get_string_width (self->font, self->text); + *height = (grub_font_get_ascent (self->font) + + grub_font_get_descent (self->font)); +} + +static grub_err_t +label_set_property (void *vself, const char *name, const char *value) +{ + grub_gui_label_t self = vself; + if (grub_strcmp (name, "text") == 0) + { + grub_free (self->text); + if (! value) + value = ""; + self->text = grub_strdup (value); + } + else if (grub_strcmp (name, "font") == 0) + { + self->font = grub_font_get (value); + } + else if (grub_strcmp (name, "color") == 0) + { + grub_gui_parse_color (value, &self->color); + } + else if (grub_strcmp (name, "align") == 0) + { + int i; + for (i = 0; align_options[i]; i++) + { + if (grub_strcmp (align_options[i], value) == 0) + { + self->align = i; /* Set the alignment mode. */ + break; + } + } + } + else if (grub_strcmp (name, "visible") == 0) + { + self->visible = grub_strcmp (value, "false") != 0; + } + else if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + self->id = grub_strdup (value); + else + self->id = 0; + } + return GRUB_ERR_NONE; +} + +static struct grub_gui_component_ops label_ops = +{ + .destroy = label_destroy, + .get_id = label_get_id, + .is_instance = label_is_instance, + .paint = label_paint, + .set_parent = label_set_parent, + .get_parent = label_get_parent, + .set_bounds = label_set_bounds, + .get_bounds = label_get_bounds, + .get_minimal_size = label_get_minimal_size, + .set_property = label_set_property +}; + +grub_gui_component_t +grub_gui_label_new (void) +{ + grub_gui_label_t label; + label = grub_zalloc (sizeof (*label)); + if (! label) + return 0; + label->comp.ops = &label_ops; + label->visible = 1; + label->text = grub_strdup (""); + label->font = grub_font_get ("Unknown Regular 16"); + label->color.red = 0; + label->color.green = 0; + label->color.blue = 0; + label->color.alpha = 255; + label->align = align_left; + return (grub_gui_component_t) label; +} diff --git a/gfxmenu/gui_list.c b/gfxmenu/gui_list.c new file mode 100644 index 000000000..0d771413f --- /dev/null +++ b/gfxmenu/gui_list.c @@ -0,0 +1,612 @@ +/* gui_list.c - GUI component to display a selectable list of items. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +struct grub_gui_list_impl +{ + struct grub_gui_list list; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + int visible; + + int icon_width; + int icon_height; + int item_height; + int item_padding; + int item_icon_space; + int item_spacing; + grub_font_t item_font; + grub_font_t selected_item_font; + grub_gui_color_t item_color; + int selected_item_color_set; + grub_gui_color_t selected_item_color; + + int draw_scrollbar; + int need_to_recreate_scrollbar; + char *scrollbar_frame_pattern; + char *scrollbar_thumb_pattern; + grub_gfxmenu_box_t scrollbar_frame; + grub_gfxmenu_box_t scrollbar_thumb; + int scrollbar_width; + + int first_shown_index; + + int need_to_recreate_boxes; + char *theme_dir; + char *menu_box_pattern; + char *selected_item_box_pattern; + grub_gfxmenu_box_t menu_box; + grub_gfxmenu_box_t selected_item_box; + + grub_gfxmenu_icon_manager_t icon_manager; + + grub_gfxmenu_view_t view; +}; + +typedef struct grub_gui_list_impl *list_impl_t; + +static void +list_destroy (void *vself) +{ + list_impl_t self = vself; + + grub_free (self->theme_dir); + grub_free (self->menu_box_pattern); + grub_free (self->selected_item_box_pattern); + if (self->menu_box) + self->menu_box->destroy (self->menu_box); + if (self->selected_item_box) + self->selected_item_box->destroy (self->selected_item_box); + if (self->icon_manager) + grub_gfxmenu_icon_manager_destroy (self->icon_manager); + + grub_free (self); +} + +static int +get_num_shown_items (list_impl_t self) +{ + int boxpad = self->item_padding; + int item_vspace = self->item_spacing; + int item_height = self->item_height; + + grub_gfxmenu_box_t box = self->menu_box; + int box_top_pad = box->get_top_pad (box); + int box_bottom_pad = box->get_bottom_pad (box); + + return (self->bounds.height + item_vspace - 2 * boxpad + - box_top_pad - box_bottom_pad) / (item_height + item_vspace); +} + +static int +check_boxes (list_impl_t self) +{ + if (self->need_to_recreate_boxes) + { + grub_gui_recreate_box (&self->menu_box, + self->menu_box_pattern, + self->theme_dir); + + grub_gui_recreate_box (&self->selected_item_box, + self->selected_item_box_pattern, + self->theme_dir); + + self->need_to_recreate_boxes = 0; + } + + return (self->menu_box != 0 && self->selected_item_box != 0); +} + +static int +check_scrollbar (list_impl_t self) +{ + if (self->need_to_recreate_scrollbar) + { + grub_gui_recreate_box (&self->scrollbar_frame, + self->scrollbar_frame_pattern, + self->theme_dir); + + grub_gui_recreate_box (&self->scrollbar_thumb, + self->scrollbar_thumb_pattern, + self->theme_dir); + + self->need_to_recreate_scrollbar = 0; + } + + return (self->scrollbar_frame != 0 && self->scrollbar_thumb != 0); +} + +static const char * +list_get_id (void *vself) +{ + list_impl_t self = vself; + return self->id; +} + +static int +list_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return (grub_strcmp (type, "component") == 0 + || grub_strcmp (type, "list") == 0); +} + +static struct grub_video_bitmap * +get_item_icon (list_impl_t self, int item_index) +{ + grub_menu_entry_t entry; + entry = grub_menu_get_entry (self->view->menu, item_index); + if (! entry) + return 0; + + return grub_gfxmenu_icon_manager_get_icon (self->icon_manager, entry); +} + +static void +make_selected_item_visible (list_impl_t self) +{ + int selected_index = self->view->selected; + if (selected_index < 0) + return; /* No item is selected. */ + int num_shown_items = get_num_shown_items (self); + int last_shown_index = self->first_shown_index + (num_shown_items - 1); + if (selected_index < self->first_shown_index) + self->first_shown_index = selected_index; + else if (selected_index > last_shown_index) + self->first_shown_index = selected_index - (num_shown_items - 1); +} + +/* Draw a scrollbar on the menu. */ +static void +draw_scrollbar (list_impl_t self, + int value, int extent, int min, int max, + int rightx, int topy, int height) +{ + grub_gfxmenu_box_t frame = self->scrollbar_frame; + grub_gfxmenu_box_t thumb = self->scrollbar_thumb; + int frame_vertical_pad = (frame->get_top_pad (frame) + + frame->get_bottom_pad (frame)); + int frame_horizontal_pad = (frame->get_left_pad (frame) + + frame->get_right_pad (frame)); + int tracktop = topy + frame->get_top_pad (frame); + int tracklen = height - frame_vertical_pad; + frame->set_content_size (frame, self->scrollbar_width, tracklen); + int thumby = tracktop + tracklen * (value - min) / (max - min); + int thumbheight = tracklen * extent / (max - min) + 1; + thumb->set_content_size (thumb, + self->scrollbar_width - frame_horizontal_pad, + thumbheight - (thumb->get_top_pad (thumb) + + thumb->get_bottom_pad (thumb))); + frame->draw (frame, + rightx - (self->scrollbar_width + frame_horizontal_pad), + topy); + thumb->draw (thumb, + rightx - (self->scrollbar_width - frame->get_right_pad (frame)), + thumby); +} + +/* Draw the list of items. */ +static void +draw_menu (list_impl_t self, int width, int drawing_scrollbar, + int num_shown_items) +{ + if (! self->menu_box || ! self->selected_item_box) + return; + + int boxpad = self->item_padding; + int icon_text_space = self->item_icon_space; + int item_vspace = self->item_spacing; + + int ascent = grub_font_get_ascent (self->item_font); + int descent = grub_font_get_descent (self->item_font); + int item_height = self->item_height; + + make_selected_item_visible (self); + + int scrollbar_h_space = drawing_scrollbar ? self->scrollbar_width : 0; + + grub_gfxmenu_box_t selbox = self->selected_item_box; + int sel_leftpad = selbox->get_left_pad (selbox); + int item_top = boxpad; + int item_left = boxpad + sel_leftpad; + int menu_index; + int visible_index; + + for (visible_index = 0, menu_index = self->first_shown_index; + visible_index < num_shown_items && menu_index < self->view->menu->size; + visible_index++, menu_index++) + { + int is_selected = (menu_index == self->view->selected); + + if (is_selected) + { + int sel_toppad = selbox->get_top_pad (selbox); + selbox->set_content_size (selbox, + (width - 2 * boxpad + - scrollbar_h_space), + item_height); + selbox->draw (selbox, + item_left - sel_leftpad, + item_top - sel_toppad); + } + + struct grub_video_bitmap *icon; + if ((icon = get_item_icon (self, menu_index)) != 0) + grub_video_blit_bitmap (icon, GRUB_VIDEO_BLIT_BLEND, + item_left, + item_top + (item_height - self->icon_height) / 2, + 0, 0, self->icon_width, self->icon_height); + + const char *item_title = + grub_menu_get_entry (self->view->menu, menu_index)->title; + grub_font_t font = + (is_selected && self->selected_item_font + ? self->selected_item_font + : self->item_font); + grub_gui_color_t text_color = + ((is_selected && self->selected_item_color_set) + ? self->selected_item_color + : self->item_color); + grub_font_draw_string (item_title, + font, + grub_gui_map_color (text_color), + item_left + self->icon_width + icon_text_space, + (item_top + (item_height - (ascent + descent)) + / 2 + ascent)); + + item_top += item_height + item_vspace; + } +} + +static void +list_paint (void *vself, const grub_video_rect_t *region) +{ + list_impl_t self = vself; + grub_video_rect_t vpsave; + + if (! self->visible) + return; + if (!grub_video_have_common_points (region, &self->bounds)) + return; + + check_boxes (self); + + if (! self->menu_box || ! self->selected_item_box) + return; + + grub_gui_set_viewport (&self->bounds, &vpsave); + { + grub_gfxmenu_box_t box = self->menu_box; + int box_left_pad = box->get_left_pad (box); + int box_top_pad = box->get_top_pad (box); + int box_right_pad = box->get_right_pad (box); + int box_bottom_pad = box->get_bottom_pad (box); + grub_video_rect_t vpsave2, content_rect; + int num_shown_items = get_num_shown_items (self); + int drawing_scrollbar = (self->draw_scrollbar + && (num_shown_items < self->view->menu->size) + && check_scrollbar (self)); + + content_rect.x = box_left_pad; + content_rect.y = box_top_pad; + content_rect.width = self->bounds.width - box_left_pad - box_right_pad; + content_rect.height = self->bounds.height - box_top_pad - box_bottom_pad; + + box->set_content_size (box, content_rect.width, content_rect.height); + + box->draw (box, 0, 0); + + grub_gui_set_viewport (&content_rect, &vpsave2); + draw_menu (self, content_rect.width, drawing_scrollbar, num_shown_items); + grub_gui_restore_viewport (&vpsave2); + + if (drawing_scrollbar) + draw_scrollbar (self, + self->first_shown_index, num_shown_items, + 0, self->view->menu->size, + self->bounds.width - box_right_pad + + self->scrollbar_width, + box_top_pad + self->item_padding, + self->bounds.height - box_top_pad - box_bottom_pad); + } + + grub_gui_restore_viewport (&vpsave); +} + +static void +list_set_parent (void *vself, grub_gui_container_t parent) +{ + list_impl_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +list_get_parent (void *vself) +{ + list_impl_t self = vself; + return self->parent; +} + +static void +list_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + list_impl_t self = vself; + self->bounds = *bounds; +} + +static void +list_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + list_impl_t self = vself; + *bounds = self->bounds; +} + +static void +list_get_minimal_size (void *vself, unsigned *width, unsigned *height) +{ + list_impl_t self = vself; + + if (check_boxes (self)) + { + int boxpad = self->item_padding; + int item_vspace = self->item_spacing; + int item_height = self->item_height; + int num_items = 3; + + grub_gfxmenu_box_t box = self->menu_box; + int box_left_pad = box->get_left_pad (box); + int box_top_pad = box->get_top_pad (box); + int box_right_pad = box->get_right_pad (box); + int box_bottom_pad = box->get_bottom_pad (box); + unsigned width_s; + + *width = grub_font_get_string_width (self->item_font, "Typical OS"); + width_s = grub_font_get_string_width (self->selected_item_font, + "Typical OS"); + if (*width < width_s) + *width = width_s; + + *width += 2 * boxpad + box_left_pad + box_right_pad; + + /* Set the menu box height to fit the items. */ + *height = (item_height * num_items + + item_vspace * (num_items - 1) + + 2 * boxpad + + box_top_pad + box_bottom_pad); + } + else + { + *width = 0; + *height = 0; + } +} + +static grub_err_t +list_set_property (void *vself, const char *name, const char *value) +{ + list_impl_t self = vself; + if (grub_strcmp (name, "item_font") == 0) + { + self->item_font = grub_font_get (value); + } + else if (grub_strcmp (name, "selected_item_font") == 0) + { + if (! value || grub_strcmp (value, "inherit") == 0) + self->selected_item_font = 0; + else + self->selected_item_font = grub_font_get (value); + } + else if (grub_strcmp (name, "item_color") == 0) + { + grub_gui_parse_color (value, &self->item_color); + } + else if (grub_strcmp (name, "selected_item_color") == 0) + { + if (! value || grub_strcmp (value, "inherit") == 0) + { + self->selected_item_color_set = 0; + } + else + { + if (grub_gui_parse_color (value, &self->selected_item_color) + == GRUB_ERR_NONE) + self->selected_item_color_set = 1; + } + } + else if (grub_strcmp (name, "icon_width") == 0) + { + self->icon_width = grub_strtol (value, 0, 10); + grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager, + self->icon_width, + self->icon_height); + } + else if (grub_strcmp (name, "icon_height") == 0) + { + self->icon_height = grub_strtol (value, 0, 10); + grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager, + self->icon_width, + self->icon_height); + } + else if (grub_strcmp (name, "item_height") == 0) + { + self->item_height = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "item_padding") == 0) + { + self->item_padding = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "item_icon_space") == 0) + { + self->item_icon_space = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "item_spacing") == 0) + { + self->item_spacing = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "visible") == 0) + { + self->visible = grub_strcmp (value, "false") != 0; + } + else if (grub_strcmp (name, "menu_pixmap_style") == 0) + { + self->need_to_recreate_boxes = 1; + grub_free (self->menu_box_pattern); + self->menu_box_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "selected_item_pixmap_style") == 0) + { + self->need_to_recreate_boxes = 1; + grub_free (self->selected_item_box_pattern); + self->selected_item_box_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "scrollbar_frame") == 0) + { + self->need_to_recreate_scrollbar = 1; + grub_free (self->scrollbar_frame_pattern); + self->scrollbar_frame_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "scrollbar_thumb") == 0) + { + self->need_to_recreate_scrollbar = 1; + grub_free (self->scrollbar_thumb_pattern); + self->scrollbar_thumb_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "scrollbar_width") == 0) + { + self->scrollbar_width = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "scrollbar") == 0) + { + self->draw_scrollbar = grub_strcmp (value, "false") != 0; + } + else if (grub_strcmp (name, "theme_dir") == 0) + { + self->need_to_recreate_boxes = 1; + grub_free (self->theme_dir); + self->theme_dir = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + self->id = grub_strdup (value); + else + self->id = 0; + } + return grub_errno; +} + +/* Set necessary information that the gfxmenu view provides. */ +static void +list_set_view_info (void *vself, + grub_gfxmenu_view_t view) +{ + list_impl_t self = vself; + grub_gfxmenu_icon_manager_set_theme_path (self->icon_manager, + view->theme_path); + self->view = view; +} + +static struct grub_gui_component_ops list_comp_ops = + { + .destroy = list_destroy, + .get_id = list_get_id, + .is_instance = list_is_instance, + .paint = list_paint, + .set_parent = list_set_parent, + .get_parent = list_get_parent, + .set_bounds = list_set_bounds, + .get_bounds = list_get_bounds, + .get_minimal_size = list_get_minimal_size, + .set_property = list_set_property + }; + +static struct grub_gui_list_ops list_ops = +{ + .set_view_info = list_set_view_info +}; + +grub_gui_component_t +grub_gui_list_new (void) +{ + list_impl_t self; + grub_font_t default_font; + grub_gui_color_t default_fg_color; + grub_gui_color_t default_bg_color; + + self = grub_zalloc (sizeof (*self)); + if (! self) + return 0; + + self->list.ops = &list_ops; + self->list.component.ops = &list_comp_ops; + + self->visible = 1; + + default_font = grub_font_get ("Unknown Regular 16"); + default_fg_color = grub_gui_color_rgb (0, 0, 0); + default_bg_color = grub_gui_color_rgb (255, 255, 255); + + self->icon_width = 32; + self->icon_height = 32; + self->item_height = 42; + self->item_padding = 14; + self->item_icon_space = 4; + self->item_spacing = 16; + self->item_font = default_font; + self->selected_item_font = 0; /* Default to using the item_font. */ + self->item_color = default_fg_color; + self->selected_item_color_set = 0; /* Default to using the item_color. */ + self->selected_item_color = default_fg_color; + + self->draw_scrollbar = 1; + self->need_to_recreate_scrollbar = 1; + self->scrollbar_frame = 0; + self->scrollbar_thumb = 0; + self->scrollbar_frame_pattern = 0; + self->scrollbar_thumb_pattern = 0; + self->scrollbar_width = 16; + + self->first_shown_index = 0; + + self->need_to_recreate_boxes = 0; + self->theme_dir = 0; + self->menu_box_pattern = 0; + self->selected_item_box_pattern = 0; + self->menu_box = grub_gfxmenu_create_box (0, 0); + self->selected_item_box = grub_gfxmenu_create_box (0, 0); + + self->icon_manager = grub_gfxmenu_icon_manager_new (); + if (! self->icon_manager) + { + self->list.component.ops->destroy (self); + return 0; + } + grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager, + self->icon_width, + self->icon_height); + return (grub_gui_component_t) self; +} diff --git a/gfxmenu/gui_progress_bar.c b/gfxmenu/gui_progress_bar.c new file mode 100644 index 000000000..d786aae31 --- /dev/null +++ b/gfxmenu/gui_progress_bar.c @@ -0,0 +1,384 @@ +/* gui_progress_bar.c - GUI progress bar component. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct grub_gui_progress_bar +{ + struct grub_gui_progress progress; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + int visible; + int start; + int end; + int value; + int show_text; + char *template; + grub_font_t font; + grub_gui_color_t text_color; + grub_gui_color_t border_color; + grub_gui_color_t bg_color; + grub_gui_color_t fg_color; + + char *theme_dir; + int need_to_recreate_pixmaps; + int pixmapbar_available; + char *bar_pattern; + char *highlight_pattern; + grub_gfxmenu_box_t bar_box; + grub_gfxmenu_box_t highlight_box; +}; + +typedef struct grub_gui_progress_bar *grub_gui_progress_bar_t; + +static void +progress_bar_destroy (void *vself) +{ + grub_gui_progress_bar_t self = vself; + grub_free (self); +} + +static const char * +progress_bar_get_id (void *vself) +{ + grub_gui_progress_bar_t self = vself; + return self->id; +} + +static int +progress_bar_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return grub_strcmp (type, "component") == 0; +} + +static int +check_pixmaps (grub_gui_progress_bar_t self) +{ + if (!self->pixmapbar_available) + return 0; + if (self->need_to_recreate_pixmaps) + { + grub_gui_recreate_box (&self->bar_box, + self->bar_pattern, + self->theme_dir); + + grub_gui_recreate_box (&self->highlight_box, + self->highlight_pattern, + self->theme_dir); + + self->need_to_recreate_pixmaps = 0; + } + + return (self->bar_box != 0 && self->highlight_box != 0); +} + +static void +draw_filled_rect_bar (grub_gui_progress_bar_t self) +{ + /* Set the progress bar's frame. */ + grub_video_rect_t f; + f.x = 1; + f.y = 1; + f.width = self->bounds.width - 2; + f.height = self->bounds.height - 2; + + /* Border. */ + grub_video_fill_rect (grub_gui_map_color (self->border_color), + f.x - 1, f.y - 1, + f.width + 2, f.height + 2); + + /* Bar background. */ + int barwidth = (f.width + * (self->value - self->start) + / (self->end - self->start)); + grub_video_fill_rect (grub_gui_map_color (self->bg_color), + f.x + barwidth, f.y, + f.width - barwidth, f.height); + + /* Bar foreground. */ + grub_video_fill_rect (grub_gui_map_color (self->fg_color), + f.x, f.y, + barwidth, f.height); +} + +static void +draw_pixmap_bar (grub_gui_progress_bar_t self) +{ + grub_gfxmenu_box_t bar = self->bar_box; + grub_gfxmenu_box_t hl = self->highlight_box; + int w = self->bounds.width; + int h = self->bounds.height; + int bar_l_pad = bar->get_left_pad (bar); + int bar_r_pad = bar->get_right_pad (bar); + int bar_t_pad = bar->get_top_pad (bar); + int bar_b_pad = bar->get_bottom_pad (bar); + int bar_h_pad = bar_l_pad + bar_r_pad; + int bar_v_pad = bar_t_pad + bar_b_pad; + int tracklen = w - bar_h_pad; + int trackheight = h - bar_v_pad; + int barwidth; + + bar->set_content_size (bar, tracklen, trackheight); + + barwidth = (tracklen * (self->value - self->start) + / (self->end - self->start)); + + hl->set_content_size (hl, barwidth, h - bar_v_pad); + + bar->draw (bar, 0, 0); + hl->draw (hl, bar_l_pad, bar_t_pad); +} + +static void +draw_text (grub_gui_progress_bar_t self) +{ + if (self->template) + { + grub_font_t font = self->font; + grub_video_color_t text_color = grub_gui_map_color (self->text_color); + int width = self->bounds.width; + int height = self->bounds.height; + char *text; + text = grub_xasprintf (self->template, + self->value > 0 ? self->value : -self->value); + if (!text) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + return; + } + /* Center the text. */ + int text_width = grub_font_get_string_width (font, text); + int x = (width - text_width) / 2; + int y = ((height - grub_font_get_descent (font)) / 2 + + grub_font_get_ascent (font) / 2); + grub_font_draw_string (text, font, text_color, x, y); + } +} + +static void +progress_bar_paint (void *vself, const grub_video_rect_t *region) +{ + grub_gui_progress_bar_t self = vself; + grub_video_rect_t vpsave; + + if (! self->visible) + return; + if (!grub_video_have_common_points (region, &self->bounds)) + return; + + if (self->end == self->start) + return; + + grub_gui_set_viewport (&self->bounds, &vpsave); + + if (check_pixmaps (self)) + draw_pixmap_bar (self); + else + draw_filled_rect_bar (self); + + draw_text (self); + + grub_gui_restore_viewport (&vpsave); +} + +static void +progress_bar_set_parent (void *vself, grub_gui_container_t parent) +{ + grub_gui_progress_bar_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +progress_bar_get_parent (void *vself) +{ + grub_gui_progress_bar_t self = vself; + return self->parent; +} + +static void +progress_bar_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + grub_gui_progress_bar_t self = vself; + self->bounds = *bounds; +} + +static void +progress_bar_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + grub_gui_progress_bar_t self = vself; + *bounds = self->bounds; +} + +static void +progress_bar_get_minimal_size (void *vself, + unsigned *width, unsigned *height) +{ + unsigned text_width = 0, text_height = 0; + grub_gui_progress_bar_t self = vself; + + if (self->template) + { + text_width = grub_font_get_string_width (self->font, self->template); + text_width += grub_font_get_string_width (self->font, "XXXXXXXXXX"); + text_height = grub_font_get_descent (self->font) + + grub_font_get_ascent (self->font); + } + *width = 200; + if (*width < text_width) + *width = text_width; + *height = 28; + if (*height < text_height) + *height = text_height; +} + +static void +progress_bar_set_state (void *vself, int visible, int start, + int current, int end) +{ + grub_gui_progress_bar_t self = vself; + self->visible = visible; + self->start = start; + self->value = current; + self->end = end; +} + +static grub_err_t +progress_bar_set_property (void *vself, const char *name, const char *value) +{ + grub_gui_progress_bar_t self = vself; + if (grub_strcmp (name, "text") == 0) + { + grub_free (self->template); + if (grub_strcmp (value, "@TIMEOUT_NOTIFICATION_LONG@") == 0) + value + = _("The highlighted entry will be executed automatically in %ds."); + else if (grub_strcmp (value, "@TIMEOUT_NOTIFICATION_MIDDLE@") == 0) + /* TRANSLATORS: 's' stands for seconds. + It's a standalone timeout notification. + Please use the short form in your language. */ + value = _("%ds remaining."); + else if (grub_strcmp (value, "@TIMEOUT_NOTIFICATION_SHORT@") == 0) + /* TRANSLATORS: 's' stands for seconds. + It's a standalone timeout notification. + Please use the shortest form available in you language. */ + value = _("%ds"); + + self->template = grub_strdup (value); + } + else if (grub_strcmp (name, "font") == 0) + { + self->font = grub_font_get (value); + } + else if (grub_strcmp (name, "text_color") == 0) + { + grub_gui_parse_color (value, &self->text_color); + } + else if (grub_strcmp (name, "border_color") == 0) + { + grub_gui_parse_color (value, &self->border_color); + } + else if (grub_strcmp (name, "bg_color") == 0) + { + grub_gui_parse_color (value, &self->bg_color); + } + else if (grub_strcmp (name, "fg_color") == 0) + { + grub_gui_parse_color (value, &self->fg_color); + } + else if (grub_strcmp (name, "bar_style") == 0) + { + self->need_to_recreate_pixmaps = 1; + self->pixmapbar_available = 1; + grub_free (self->bar_pattern); + self->bar_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "highlight_style") == 0) + { + self->need_to_recreate_pixmaps = 1; + self->pixmapbar_available = 1; + grub_free (self->highlight_pattern); + self->highlight_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "theme_dir") == 0) + { + self->need_to_recreate_pixmaps = 1; + grub_free (self->theme_dir); + self->theme_dir = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + self->id = grub_strdup (value); + else + self->id = 0; + } + return grub_errno; +} + +static struct grub_gui_component_ops progress_bar_ops = +{ + .destroy = progress_bar_destroy, + .get_id = progress_bar_get_id, + .is_instance = progress_bar_is_instance, + .paint = progress_bar_paint, + .set_parent = progress_bar_set_parent, + .get_parent = progress_bar_get_parent, + .set_bounds = progress_bar_set_bounds, + .get_bounds = progress_bar_get_bounds, + .get_minimal_size = progress_bar_get_minimal_size, + .set_property = progress_bar_set_property +}; + +static struct grub_gui_progress_ops progress_bar_pb_ops = + { + .set_state = progress_bar_set_state + }; + +grub_gui_component_t +grub_gui_progress_bar_new (void) +{ + grub_gui_progress_bar_t self; + self = grub_zalloc (sizeof (*self)); + if (! self) + return 0; + self->progress.ops = &progress_bar_pb_ops; + self->progress.component.ops = &progress_bar_ops; + self->visible = 1; + self->font = grub_font_get ("Unknown Regular 16"); + grub_gui_color_t black = { .red = 0, .green = 0, .blue = 0, .alpha = 255 }; + grub_gui_color_t gray = { .red = 128, .green = 128, .blue = 128, .alpha = 255 }; + grub_gui_color_t lightgray = { .red = 200, .green = 200, .blue = 200, .alpha = 255 }; + self->text_color = black; + self->border_color = black; + self->bg_color = gray; + self->fg_color = lightgray; + + return (grub_gui_component_t) self; +} diff --git a/gfxmenu/gui_string_util.c b/gfxmenu/gui_string_util.c new file mode 100644 index 000000000..8c51e396a --- /dev/null +++ b/gfxmenu/gui_string_util.c @@ -0,0 +1,327 @@ +/* gui_string_util.c - String utilities used by the GUI system. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +/* Create a new NUL-terminated string on the heap as a substring of BUF. + The range of buf included is the half-open interval [START,END). + The index START is inclusive, END is exclusive. */ +char * +grub_new_substring (const char *buf, + grub_size_t start, grub_size_t end) +{ + if (end < start) + return 0; + grub_size_t len = end - start; + char *s = grub_malloc (len + 1); + if (! s) + return 0; + grub_memcpy (s, buf + start, len); + s[len] = '\0'; + return s; +} + +/* Eliminate "." and ".." path elements from PATH. A new heap-allocated + string is returned. */ +static char * +canonicalize_path (const char *path) +{ + int i; + const char *p; + char *newpath = 0; + + /* Count the path components in path. */ + int components = 1; + for (p = path; *p; p++) + if (*p == '/') + components++; + + char **path_array = grub_malloc (components * sizeof (*path_array)); + if (! path_array) + return 0; + + /* Initialize array elements to NULL pointers; in case once of the + allocations fails, the cleanup code can just call grub_free() for all + pointers in the array. */ + for (i = 0; i < components; i++) + path_array[i] = 0; + + /* Parse the path into path_array. */ + p = path; + for (i = 0; i < components && p; i++) + { + /* Find the end of the path element. */ + const char *end = grub_strchr (p, '/'); + if (!end) + end = p + grub_strlen (p); + + /* Copy the element. */ + path_array[i] = grub_new_substring (p, 0, end - p); + if (! path_array[i]) + goto cleanup; + + /* Advance p to point to the start of the next element, or NULL. */ + if (*end) + p = end + 1; + else + p = 0; + } + + /* Eliminate '.' and '..' elements from the path array. */ + int newpath_length = 0; + for (i = components - 1; i >= 0; --i) + { + if (! grub_strcmp (path_array[i], ".")) + { + grub_free (path_array[i]); + path_array[i] = 0; + } + else if (! grub_strcmp (path_array[i], "..") + && i > 0) + { + /* Delete the '..' and the prior path element. */ + grub_free (path_array[i]); + path_array[i] = 0; + --i; + grub_free (path_array[i]); + path_array[i] = 0; + } + else + { + newpath_length += grub_strlen (path_array[i]) + 1; + } + } + + /* Construct a new path string. */ + newpath = grub_malloc (newpath_length + 1); + if (! newpath) + goto cleanup; + + newpath[0] = '\0'; + char *newpath_end = newpath; + int first = 1; + for (i = 0; i < components; i++) + { + char *element = path_array[i]; + if (element) + { + /* For all components but the first, prefix with a slash. */ + if (! first) + newpath_end = grub_stpcpy (newpath_end, "/"); + newpath_end = grub_stpcpy (newpath_end, element); + first = 0; + } + } + +cleanup: + for (i = 0; i < components; i++) + grub_free (path_array[i]); + grub_free (path_array); + + return newpath; +} + +/* Return a new heap-allocated string representing to absolute path + to the file referred to by PATH. If PATH is an absolute path, then + the returned path is a copy of PATH. If PATH is a relative path, then + BASE is with PATH used to construct the absolute path. */ +char * +grub_resolve_relative_path (const char *base, const char *path) +{ + char *abspath; + char *canonpath; + char *p; + grub_size_t l; + + /* If PATH is an absolute path, then just use it as is. */ + if (path[0] == '/' || path[0] == '(') + return canonicalize_path (path); + + abspath = grub_malloc (grub_strlen (base) + grub_strlen (path) + 3); + if (! abspath) + return 0; + + /* Concatenate BASE and PATH. */ + p = grub_stpcpy (abspath, base); + l = grub_strlen (abspath); + if (l == 0 || abspath[l-1] != '/') + { + *p = '/'; + p++; + *p = 0; + } + grub_stpcpy (p, path); + + canonpath = canonicalize_path (abspath); + if (! canonpath) + return abspath; + + grub_free (abspath); + return canonpath; +} + +/* Get the path of the directory where the file at FILE_PATH is located. + FILE_PATH should refer to a file, not a directory. The returned path + includes a trailing slash. + This does not handle GRUB "(hd0,0)" paths properly yet since it only + looks at slashes. */ +char * +grub_get_dirname (const char *file_path) +{ + int i; + int last_slash; + + last_slash = -1; + for (i = grub_strlen (file_path) - 1; i >= 0; --i) + { + if (file_path[i] == '/') + { + last_slash = i; + break; + } + } + if (last_slash == -1) + return grub_strdup ("/"); + + return grub_new_substring (file_path, 0, last_slash + 1); +} + +static __inline int +my_isxdigit (char c) +{ + return ((c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F')); +} + +static int +parse_hex_color_component (const char *s, unsigned start, unsigned end) +{ + unsigned len; + char buf[3]; + + len = end - start; + /* Check the limits so we don't overrun the buffer. */ + if (len < 1 || len > 2) + return 0; + + if (len == 1) + { + buf[0] = s[start]; /* Get the first and only hex digit. */ + buf[1] = buf[0]; /* Duplicate the hex digit. */ + } + else if (len == 2) + { + buf[0] = s[start]; + buf[1] = s[start + 1]; + } + + buf[2] = '\0'; + + return grub_strtoul (buf, 0, 16); +} + +/* Parse a color string of the form "r, g, b", "#RGB", "#RGBA", + "#RRGGBB", or "#RRGGBBAA". */ +grub_err_t +grub_gui_parse_color (const char *s, grub_gui_color_t *color) +{ + grub_gui_color_t c; + + /* Skip whitespace. */ + while (*s && grub_isspace (*s)) + s++; + + if (*s == '#') + { + /* HTML-style. Number if hex digits: + [6] #RRGGBB [3] #RGB + [8] #RRGGBBAA [4] #RGBA */ + + s++; /* Skip the '#'. */ + /* Count the hexits to determine the format. */ + int hexits = 0; + const char *end = s; + while (my_isxdigit (*end)) + { + end++; + hexits++; + } + + /* Parse the color components based on the format. */ + if (hexits == 3 || hexits == 4) + { + c.red = parse_hex_color_component (s, 0, 1); + c.green = parse_hex_color_component (s, 1, 2); + c.blue = parse_hex_color_component (s, 2, 3); + if (hexits == 4) + c.alpha = parse_hex_color_component (s, 3, 4); + else + c.alpha = 255; + } + else if (hexits == 6 || hexits == 8) + { + c.red = parse_hex_color_component (s, 0, 2); + c.green = parse_hex_color_component (s, 2, 4); + c.blue = parse_hex_color_component (s, 4, 6); + if (hexits == 8) + c.alpha = parse_hex_color_component (s, 6, 8); + else + c.alpha = 255; + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "invalid HTML-type color string `%s'", s); + } + else if (grub_isdigit (*s)) + { + /* Comma separated decimal values. */ + c.red = grub_strtoul (s, 0, 0); + if ((s = grub_strchr (s, ',')) == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "missing 1st comma separator in color `%s'", s); + s++; + c.green = grub_strtoul (s, 0, 0); + if ((s = grub_strchr (s, ',')) == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "missing 2nd comma separator in color `%s'", s); + s++; + c.blue = grub_strtoul (s, 0, 0); + if ((s = grub_strchr (s, ',')) == 0) + c.alpha = 255; + else + { + s++; + c.alpha = grub_strtoul (s, 0, 0); + } + } + else + { + if (! grub_gui_get_named_color (s, &c)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "invalid named color `%s'", s); + } + + if (grub_errno == GRUB_ERR_NONE) + *color = c; + return grub_errno; +} diff --git a/gfxmenu/gui_util.c b/gfxmenu/gui_util.c new file mode 100644 index 000000000..eba7bb39e --- /dev/null +++ b/gfxmenu/gui_util.c @@ -0,0 +1,101 @@ +/* gui_util.c - GUI utility functions. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + + +struct find_by_id_state +{ + const char *match_id; + grub_gui_component_callback match_callback; + void *match_userdata; +}; + +static void +find_by_id_recursively (grub_gui_component_t component, void *userdata) +{ + struct find_by_id_state *state; + const char *id; + + state = (struct find_by_id_state *) userdata; + id = component->ops->get_id (component); + if (id && grub_strcmp (id, state->match_id) == 0) + state->match_callback (component, state->match_userdata); + + if (component->ops->is_instance (component, "container")) + { + grub_gui_container_t container; + container = (grub_gui_container_t) component; + container->ops->iterate_children (container, + find_by_id_recursively, + state); + } +} + +void +grub_gui_find_by_id (grub_gui_component_t root, + const char *id, + grub_gui_component_callback cb, + void *userdata) +{ + struct find_by_id_state state; + state.match_id = id; + state.match_callback = cb; + state.match_userdata = userdata; + find_by_id_recursively (root, &state); +} + + +struct iterate_recursively_state +{ + grub_gui_component_callback callback; + void *userdata; +}; + +static +void iterate_recursively_cb (grub_gui_component_t component, void *userdata) +{ + struct iterate_recursively_state *state; + + state = (struct iterate_recursively_state *) userdata; + state->callback (component, state->userdata); + + if (component->ops->is_instance (component, "container")) + { + grub_gui_container_t container; + container = (grub_gui_container_t) component; + container->ops->iterate_children (container, + iterate_recursively_cb, + state); + } +} + +void +grub_gui_iterate_recursively (grub_gui_component_t root, + grub_gui_component_callback cb, + void *userdata) +{ + struct iterate_recursively_state state; + state.callback = cb; + state.userdata = userdata; + iterate_recursively_cb (root, &state); +} diff --git a/gfxmenu/icon_manager.c b/gfxmenu/icon_manager.c new file mode 100644 index 000000000..0c304ede0 --- /dev/null +++ b/gfxmenu/icon_manager.c @@ -0,0 +1,263 @@ +/* icon_manager.c - gfxmenu icon manager. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Currently hard coded to '.png' extension. */ +static const char icon_extension[] = ".png"; + +typedef struct icon_entry +{ + char *class_name; + struct grub_video_bitmap *bitmap; + struct icon_entry *next; +} *icon_entry_t; + +struct grub_gfxmenu_icon_manager +{ + char *theme_path; + int icon_width; + int icon_height; + + /* Icon cache: linked list w/ dummy head node. */ + struct icon_entry cache; +}; + + +/* Create a new icon manager and return a point to it. */ +grub_gfxmenu_icon_manager_t +grub_gfxmenu_icon_manager_new (void) +{ + grub_gfxmenu_icon_manager_t mgr; + mgr = grub_malloc (sizeof (*mgr)); + if (! mgr) + return 0; + + mgr->theme_path = 0; + mgr->icon_width = 0; + mgr->icon_height = 0; + + /* Initialize the dummy head node. */ + mgr->cache.class_name = 0; + mgr->cache.bitmap = 0; + mgr->cache.next = 0; + + return mgr; +} + +/* Destroy the icon manager MGR, freeing all resources used by it. + +Note: Any bitmaps returned by grub_gfxmenu_icon_manager_get_icon() +are destroyed and must not be used by the caller after this function +is called. */ +void +grub_gfxmenu_icon_manager_destroy (grub_gfxmenu_icon_manager_t mgr) +{ + grub_gfxmenu_icon_manager_clear_cache (mgr); + grub_free (mgr->theme_path); + grub_free (mgr); +} + +/* Clear the icon cache. */ +void +grub_gfxmenu_icon_manager_clear_cache (grub_gfxmenu_icon_manager_t mgr) +{ + icon_entry_t cur; + icon_entry_t next; + for (cur = mgr->cache.next; cur; cur = next) + { + next = cur->next; + grub_free (cur->class_name); + grub_video_bitmap_destroy (cur->bitmap); + grub_free (cur); + } + mgr->cache.next = 0; +} + +/* Set the theme path. If the theme path is changed, the icon cache + is cleared. */ +void +grub_gfxmenu_icon_manager_set_theme_path (grub_gfxmenu_icon_manager_t mgr, + const char *path) +{ + /* Clear the cache if the theme path has changed. */ + if (((mgr->theme_path == 0) != (path == 0)) + || (grub_strcmp (mgr->theme_path, path) != 0)) + grub_gfxmenu_icon_manager_clear_cache (mgr); + + grub_free (mgr->theme_path); + mgr->theme_path = path ? grub_strdup (path) : 0; +} + +/* Set the icon size. When icons are requested from the icon manager, + they are scaled to this size before being returned. If the size is + changed, the icon cache is cleared. */ +void +grub_gfxmenu_icon_manager_set_icon_size (grub_gfxmenu_icon_manager_t mgr, + int width, int height) +{ + /* If the width or height is changed, we must clear the cache, since the + scaled bitmaps are stored in the cache. */ + if (width != mgr->icon_width || height != mgr->icon_height) + grub_gfxmenu_icon_manager_clear_cache (mgr); + + mgr->icon_width = width; + mgr->icon_height = height; +} + +/* Try to load an icon for the specified CLASS_NAME in the directory DIR. + Returns 0 if the icon could not be loaded, or returns a pointer to a new + bitmap if it was successful. */ +static struct grub_video_bitmap * +try_loading_icon (grub_gfxmenu_icon_manager_t mgr, + const char *dir, const char *class_name) +{ + char *path; + int l; + + path = grub_malloc (grub_strlen (dir) + grub_strlen (class_name) + + grub_strlen (icon_extension) + 3); + if (! path) + return 0; + + grub_strcpy (path, dir); + l = grub_strlen (path); + if (path[l-1] != '/') + { + path[l] = '/'; + path[l+1] = 0; + } + grub_strcat (path, class_name); + grub_strcat (path, icon_extension); + + struct grub_video_bitmap *raw_bitmap; + grub_video_bitmap_load (&raw_bitmap, path); + grub_free (path); + grub_errno = GRUB_ERR_NONE; /* Critical to clear the error!! */ + if (! raw_bitmap) + return 0; + + struct grub_video_bitmap *scaled_bitmap; + grub_video_bitmap_create_scaled (&scaled_bitmap, + mgr->icon_width, mgr->icon_height, + raw_bitmap, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + grub_video_bitmap_destroy (raw_bitmap); + if (! scaled_bitmap) + { + grub_error_push (); + grub_error (grub_errno, "failed to scale icon"); + return 0; + } + + return scaled_bitmap; +} + +/* Get the icon for the specified class CLASS_NAME. If an icon for + CLASS_NAME already exists in the cache, then a reference to the cached + bitmap is returned. If it is not cached, then it is loaded and cached. + If no icon could be could for CLASS_NAME, then 0 is returned. */ +static struct grub_video_bitmap * +get_icon_by_class (grub_gfxmenu_icon_manager_t mgr, const char *class_name) +{ + /* First check the icon cache. */ + icon_entry_t entry; + for (entry = mgr->cache.next; entry; entry = entry->next) + { + if (grub_strcmp (entry->class_name, class_name) == 0) + return entry->bitmap; + } + + if (! mgr->theme_path) + return 0; + + /* Otherwise, we search for an icon to load. */ + char *theme_dir = grub_get_dirname (mgr->theme_path); + char *icons_dir; + struct grub_video_bitmap *icon; + icon = 0; + /* First try the theme's own icons, from "grub/themes/NAME/icons/" */ + icons_dir = grub_resolve_relative_path (theme_dir, "icons/"); + if (icons_dir) + { + icon = try_loading_icon (mgr, icons_dir, class_name); + grub_free (icons_dir); + } + + grub_free (theme_dir); + if (! icon) + { + const char *icondir; + + icondir = grub_env_get ("icondir"); + if (icondir) + icon = try_loading_icon (mgr, icondir, class_name); + } + + /* No icon was found. */ + /* This should probably be noted in the cache, so that a search is not + performed each time an icon for CLASS_NAME is requested. */ + if (! icon) + return 0; + + /* Insert a new cache entry for this icon. */ + entry = grub_malloc (sizeof (*entry)); + if (! entry) + { + grub_video_bitmap_destroy (icon); + return 0; + } + entry->class_name = grub_strdup (class_name); + entry->bitmap = icon; + entry->next = mgr->cache.next; + mgr->cache.next = entry; /* Link it into the cache. */ + return entry->bitmap; +} + +/* Get the best available icon for ENTRY. Beginning with the first class + listed in the menu entry and proceeding forward, an icon for each class + is searched for. The first icon found is returned. The returned icon + is scaled to the size specified by + grub_gfxmenu_icon_manager_set_icon_size(). + + Note: Bitmaps returned by this function are destroyed when the + icon manager is destroyed. + */ +struct grub_video_bitmap * +grub_gfxmenu_icon_manager_get_icon (grub_gfxmenu_icon_manager_t mgr, + grub_menu_entry_t entry) +{ + struct grub_menu_entry_class *c; + struct grub_video_bitmap *icon; + + /* Try each class in succession. */ + icon = 0; + for (c = entry->classes->next; c && ! icon; c = c->next) + icon = get_icon_by_class (mgr, c->name); + return icon; +} diff --git a/include/grub/mips/qemu-mips/boot.h b/gfxmenu/model.c similarity index 100% rename from include/grub/mips/qemu-mips/boot.h rename to gfxmenu/model.c diff --git a/gfxmenu/named_colors.c b/gfxmenu/named_colors.c new file mode 100644 index 000000000..eedbc47fb --- /dev/null +++ b/gfxmenu/named_colors.c @@ -0,0 +1,209 @@ +/* named_colors.c - Named color values. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +struct named_color +{ + const char *name; + grub_gui_color_t color; +}; + +/* + Named color list generated from the list of SVG color keywords from + , + processed through the following Perl command: + perl -ne 'chomp;split;print "{ \"$_[0]\", RGB_COLOR($_[2]) },\n"' + */ + +#define RGB_COLOR(r,g,b) {.red = r, .green = g, .blue = b, .alpha = 255} + +static struct named_color named_colors[] = +{ + { "aliceblue", RGB_COLOR(240,248,255) }, + { "antiquewhite", RGB_COLOR(250,235,215) }, + { "aqua", RGB_COLOR(0,255,255) }, + { "aquamarine", RGB_COLOR(127,255,212) }, + { "azure", RGB_COLOR(240,255,255) }, + { "beige", RGB_COLOR(245,245,220) }, + { "bisque", RGB_COLOR(255,228,196) }, + { "black", RGB_COLOR(0,0,0) }, + { "blanchedalmond", RGB_COLOR(255,235,205) }, + { "blue", RGB_COLOR(0,0,255) }, + { "blueviolet", RGB_COLOR(138,43,226) }, + { "brown", RGB_COLOR(165,42,42) }, + { "burlywood", RGB_COLOR(222,184,135) }, + { "cadetblue", RGB_COLOR(95,158,160) }, + { "chartreuse", RGB_COLOR(127,255,0) }, + { "chocolate", RGB_COLOR(210,105,30) }, + { "coral", RGB_COLOR(255,127,80) }, + { "cornflowerblue", RGB_COLOR(100,149,237) }, + { "cornsilk", RGB_COLOR(255,248,220) }, + { "crimson", RGB_COLOR(220,20,60) }, + { "cyan", RGB_COLOR(0,255,255) }, + { "darkblue", RGB_COLOR(0,0,139) }, + { "darkcyan", RGB_COLOR(0,139,139) }, + { "darkgoldenrod", RGB_COLOR(184,134,11) }, + { "darkgray", RGB_COLOR(169,169,169) }, + { "darkgreen", RGB_COLOR(0,100,0) }, + { "darkgrey", RGB_COLOR(169,169,169) }, + { "darkkhaki", RGB_COLOR(189,183,107) }, + { "darkmagenta", RGB_COLOR(139,0,139) }, + { "darkolivegreen", RGB_COLOR(85,107,47) }, + { "darkorange", RGB_COLOR(255,140,0) }, + { "darkorchid", RGB_COLOR(153,50,204) }, + { "darkred", RGB_COLOR(139,0,0) }, + { "darksalmon", RGB_COLOR(233,150,122) }, + { "darkseagreen", RGB_COLOR(143,188,143) }, + { "darkslateblue", RGB_COLOR(72,61,139) }, + { "darkslategray", RGB_COLOR(47,79,79) }, + { "darkslategrey", RGB_COLOR(47,79,79) }, + { "darkturquoise", RGB_COLOR(0,206,209) }, + { "darkviolet", RGB_COLOR(148,0,211) }, + { "deeppink", RGB_COLOR(255,20,147) }, + { "deepskyblue", RGB_COLOR(0,191,255) }, + { "dimgray", RGB_COLOR(105,105,105) }, + { "dimgrey", RGB_COLOR(105,105,105) }, + { "dodgerblue", RGB_COLOR(30,144,255) }, + { "firebrick", RGB_COLOR(178,34,34) }, + { "floralwhite", RGB_COLOR(255,250,240) }, + { "forestgreen", RGB_COLOR(34,139,34) }, + { "fuchsia", RGB_COLOR(255,0,255) }, + { "gainsboro", RGB_COLOR(220,220,220) }, + { "ghostwhite", RGB_COLOR(248,248,255) }, + { "gold", RGB_COLOR(255,215,0) }, + { "goldenrod", RGB_COLOR(218,165,32) }, + { "gray", RGB_COLOR(128,128,128) }, + { "green", RGB_COLOR(0,128,0) }, + { "greenyellow", RGB_COLOR(173,255,47) }, + { "grey", RGB_COLOR(128,128,128) }, + { "honeydew", RGB_COLOR(240,255,240) }, + { "hotpink", RGB_COLOR(255,105,180) }, + { "indianred", RGB_COLOR(205,92,92) }, + { "indigo", RGB_COLOR(75,0,130) }, + { "ivory", RGB_COLOR(255,255,240) }, + { "khaki", RGB_COLOR(240,230,140) }, + { "lavender", RGB_COLOR(230,230,250) }, + { "lavenderblush", RGB_COLOR(255,240,245) }, + { "lawngreen", RGB_COLOR(124,252,0) }, + { "lemonchiffon", RGB_COLOR(255,250,205) }, + { "lightblue", RGB_COLOR(173,216,230) }, + { "lightcoral", RGB_COLOR(240,128,128) }, + { "lightcyan", RGB_COLOR(224,255,255) }, + { "lightgoldenrodyellow", RGB_COLOR(250,250,210) }, + { "lightgray", RGB_COLOR(211,211,211) }, + { "lightgreen", RGB_COLOR(144,238,144) }, + { "lightgrey", RGB_COLOR(211,211,211) }, + { "lightpink", RGB_COLOR(255,182,193) }, + { "lightsalmon", RGB_COLOR(255,160,122) }, + { "lightseagreen", RGB_COLOR(32,178,170) }, + { "lightskyblue", RGB_COLOR(135,206,250) }, + { "lightslategray", RGB_COLOR(119,136,153) }, + { "lightslategrey", RGB_COLOR(119,136,153) }, + { "lightsteelblue", RGB_COLOR(176,196,222) }, + { "lightyellow", RGB_COLOR(255,255,224) }, + { "lime", RGB_COLOR(0,255,0) }, + { "limegreen", RGB_COLOR(50,205,50) }, + { "linen", RGB_COLOR(250,240,230) }, + { "magenta", RGB_COLOR(255,0,255) }, + { "maroon", RGB_COLOR(128,0,0) }, + { "mediumaquamarine", RGB_COLOR(102,205,170) }, + { "mediumblue", RGB_COLOR(0,0,205) }, + { "mediumorchid", RGB_COLOR(186,85,211) }, + { "mediumpurple", RGB_COLOR(147,112,219) }, + { "mediumseagreen", RGB_COLOR(60,179,113) }, + { "mediumslateblue", RGB_COLOR(123,104,238) }, + { "mediumspringgreen", RGB_COLOR(0,250,154) }, + { "mediumturquoise", RGB_COLOR(72,209,204) }, + { "mediumvioletred", RGB_COLOR(199,21,133) }, + { "midnightblue", RGB_COLOR(25,25,112) }, + { "mintcream", RGB_COLOR(245,255,250) }, + { "mistyrose", RGB_COLOR(255,228,225) }, + { "moccasin", RGB_COLOR(255,228,181) }, + { "navajowhite", RGB_COLOR(255,222,173) }, + { "navy", RGB_COLOR(0,0,128) }, + { "oldlace", RGB_COLOR(253,245,230) }, + { "olive", RGB_COLOR(128,128,0) }, + { "olivedrab", RGB_COLOR(107,142,35) }, + { "orange", RGB_COLOR(255,165,0) }, + { "orangered", RGB_COLOR(255,69,0) }, + { "orchid", RGB_COLOR(218,112,214) }, + { "palegoldenrod", RGB_COLOR(238,232,170) }, + { "palegreen", RGB_COLOR(152,251,152) }, + { "paleturquoise", RGB_COLOR(175,238,238) }, + { "palevioletred", RGB_COLOR(219,112,147) }, + { "papayawhip", RGB_COLOR(255,239,213) }, + { "peachpuff", RGB_COLOR(255,218,185) }, + { "peru", RGB_COLOR(205,133,63) }, + { "pink", RGB_COLOR(255,192,203) }, + { "plum", RGB_COLOR(221,160,221) }, + { "powderblue", RGB_COLOR(176,224,230) }, + { "purple", RGB_COLOR(128,0,128) }, + { "red", RGB_COLOR(255,0,0) }, + { "rosybrown", RGB_COLOR(188,143,143) }, + { "royalblue", RGB_COLOR(65,105,225) }, + { "saddlebrown", RGB_COLOR(139,69,19) }, + { "salmon", RGB_COLOR(250,128,114) }, + { "sandybrown", RGB_COLOR(244,164,96) }, + { "seagreen", RGB_COLOR(46,139,87) }, + { "seashell", RGB_COLOR(255,245,238) }, + { "sienna", RGB_COLOR(160,82,45) }, + { "silver", RGB_COLOR(192,192,192) }, + { "skyblue", RGB_COLOR(135,206,235) }, + { "slateblue", RGB_COLOR(106,90,205) }, + { "slategray", RGB_COLOR(112,128,144) }, + { "slategrey", RGB_COLOR(112,128,144) }, + { "snow", RGB_COLOR(255,250,250) }, + { "springgreen", RGB_COLOR(0,255,127) }, + { "steelblue", RGB_COLOR(70,130,180) }, + { "tan", RGB_COLOR(210,180,140) }, + { "teal", RGB_COLOR(0,128,128) }, + { "thistle", RGB_COLOR(216,191,216) }, + { "tomato", RGB_COLOR(255,99,71) }, + { "turquoise", RGB_COLOR(64,224,208) }, + { "violet", RGB_COLOR(238,130,238) }, + { "wheat", RGB_COLOR(245,222,179) }, + { "white", RGB_COLOR(255,255,255) }, + { "whitesmoke", RGB_COLOR(245,245,245) }, + { "yellow", RGB_COLOR(255,255,0) }, + { "yellowgreen", RGB_COLOR(154,205,50) }, + { 0, { 0, 0, 0, 0 } } /* Terminator. */ +}; + +/* Get the color named NAME. If the color was found, returns 1 and + stores the color into *COLOR. If the color was not found, returns 0 and + does not modify *COLOR. */ +int +grub_gui_get_named_color (const char *name, + grub_gui_color_t *color) +{ + int i; + for (i = 0; named_colors[i].name; i++) + { + if (grub_strcmp (named_colors[i].name, name) == 0) + { + *color = named_colors[i].color; + return 1; + } + } + return 0; +} diff --git a/gfxmenu/theme_loader.c b/gfxmenu/theme_loader.c new file mode 100644 index 000000000..3854c6c53 --- /dev/null +++ b/gfxmenu/theme_loader.c @@ -0,0 +1,723 @@ +/* theme_loader.c - Theme file loader for gfxmenu. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Construct a new box widget using ABSPATTERN to find the pixmap files for + it, storing the new box instance at *BOXPTR. + PATTERN should be of the form: "(hd0,0)/somewhere/style*.png". + The '*' then gets substituted with the various pixmap names that the + box uses. */ +static grub_err_t +recreate_box_absolute (grub_gfxmenu_box_t *boxptr, const char *abspattern) +{ + char *prefix; + char *suffix; + char *star; + grub_gfxmenu_box_t box; + + star = grub_strchr (abspattern, '*'); + if (! star) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "missing `*' in box pixmap pattern `%s'", abspattern); + + /* Prefix: Get the part before the '*'. */ + prefix = grub_malloc (star - abspattern + 1); + if (! prefix) + return grub_errno; + + grub_memcpy (prefix, abspattern, star - abspattern); + prefix[star - abspattern] = '\0'; + + /* Suffix: Everything after the '*' is the suffix. */ + suffix = star + 1; + + box = grub_gfxmenu_create_box (prefix, suffix); + grub_free (prefix); + if (! box) + return grub_errno; + + if (*boxptr) + (*boxptr)->destroy (*boxptr); + *boxptr = box; + return grub_errno; +} + + +/* Construct a new box widget using PATTERN to find the pixmap files for it, + storing the new widget at *BOXPTR. PATTERN should be of the form: + "somewhere/style*.png". The '*' then gets substituted with the various + pixmap names that the widget uses. + + Important! The value of *BOXPTR must be initialized! It must either + (1) Be 0 (a NULL pointer), or + (2) Be a pointer to a valid 'grub_gfxmenu_box_t' instance. + In this case, the previous instance is destroyed. */ +grub_err_t +grub_gui_recreate_box (grub_gfxmenu_box_t *boxptr, + const char *pattern, const char *theme_dir) +{ + char *abspattern; + + /* Check arguments. */ + if (! pattern) + { + /* If no pixmap pattern is given, then just create an empty box. */ + if (*boxptr) + (*boxptr)->destroy (*boxptr); + *boxptr = grub_gfxmenu_create_box (0, 0); + return grub_errno; + } + + if (! theme_dir) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "styled box missing theme directory"); + + /* Resolve to an absolute path. */ + abspattern = grub_resolve_relative_path (theme_dir, pattern); + if (! abspattern) + return grub_errno; + + /* Create the box. */ + recreate_box_absolute (boxptr, abspattern); + grub_free (abspattern); + return grub_errno; +} + +/* Set the specified property NAME on the view to the given string VALUE. + The caller is responsible for the lifetimes of NAME and VALUE. */ +static grub_err_t +theme_set_string (grub_gfxmenu_view_t view, + const char *name, + const char *value, + const char *theme_dir, + const char *filename, + int line_num, + int col_num) +{ + if (! grub_strcmp ("title-font", name)) + view->title_font = grub_font_get (value); + else if (! grub_strcmp ("message-font", name)) + view->message_font = grub_font_get (value); + else if (! grub_strcmp ("terminal-font", name)) + { + grub_free (view->terminal_font_name); + view->terminal_font_name = grub_strdup (value); + if (! view->terminal_font_name) + return grub_errno; + } + else if (! grub_strcmp ("title-color", name)) + grub_gui_parse_color (value, &view->title_color); + else if (! grub_strcmp ("message-color", name)) + grub_gui_parse_color (value, &view->message_color); + else if (! grub_strcmp ("message-bg-color", name)) + grub_gui_parse_color (value, &view->message_bg_color); + else if (! grub_strcmp ("desktop-image", name)) + { + struct grub_video_bitmap *raw_bitmap; + struct grub_video_bitmap *scaled_bitmap; + char *path; + path = grub_resolve_relative_path (theme_dir, value); + if (! path) + return grub_errno; + if (grub_video_bitmap_load (&raw_bitmap, path) != GRUB_ERR_NONE) + { + grub_free (path); + return grub_errno; + } + grub_free(path); + grub_video_bitmap_create_scaled (&scaled_bitmap, + view->screen.width, + view->screen.height, + raw_bitmap, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + grub_video_bitmap_destroy (raw_bitmap); + if (! scaled_bitmap) + { + grub_error_push (); + return grub_error (grub_errno, "error scaling desktop image"); + } + + grub_video_bitmap_destroy (view->desktop_image); + view->desktop_image = scaled_bitmap; + } + else if (! grub_strcmp ("desktop-color", name)) + grub_gui_parse_color (value, &view->desktop_color); + else if (! grub_strcmp ("terminal-box", name)) + { + grub_err_t err; + err = grub_gui_recreate_box (&view->terminal_box, value, theme_dir); + if (err != GRUB_ERR_NONE) + return err; + } + else if (! grub_strcmp ("title-text", name)) + { + grub_free (view->title_text); + view->title_text = grub_strdup (value); + if (! view->title_text) + return grub_errno; + } + else + { + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "%s:%d:%d unknown property `%s'", + filename, line_num, col_num, name); + } + return grub_errno; +} + +struct parsebuf +{ + char *buf; + int pos; + int len; + int line_num; + int col_num; + const char *filename; + char *theme_dir; + grub_gfxmenu_view_t view; +}; + +static int +has_more (struct parsebuf *p) +{ + return p->pos < p->len; +} + +static int +read_char (struct parsebuf *p) +{ + if (has_more (p)) + { + char c; + c = p->buf[p->pos++]; + if (c == '\n') + { + p->line_num++; + p->col_num = 1; + } + else + { + p->col_num++; + } + return c; + } + else + return -1; +} + +static int +peek_char (struct parsebuf *p) +{ + if (has_more (p)) + return p->buf[p->pos]; + else + return -1; +} + +static int +is_whitespace (char c) +{ + return (c == ' ' + || c == '\t' + || c == '\r' + || c == '\n' + || c == '\f'); +} + +static void +skip_whitespace (struct parsebuf *p) +{ + while (has_more (p) && is_whitespace(peek_char (p))) + read_char (p); +} + +static void +advance_to_next_line (struct parsebuf *p) +{ + int c; + + /* Eat characters up to the newline. */ + do + { + c = read_char (p); + } + while (c != -1 && c != '\n'); +} + +static int +is_identifier_char (int c) +{ + return (c != -1 + && (grub_isalpha(c) + || grub_isdigit(c) + || c == '_' + || c == '-')); +} + +static char * +read_identifier (struct parsebuf *p) +{ + /* Index of the first character of the identifier in p->buf. */ + int start; + /* Next index after the last character of the identifer in p->buf. */ + int end; + + skip_whitespace (p); + + /* Capture the start of the identifier. */ + start = p->pos; + + /* Scan for the end. */ + while (is_identifier_char (peek_char (p))) + read_char (p); + end = p->pos; + + if (end - start < 1) + return 0; + + return grub_new_substring (p->buf, start, end); +} + +static char * +read_expression (struct parsebuf *p) +{ + int start; + int end; + + skip_whitespace (p); + if (peek_char (p) == '"') + { + /* Read as a quoted string. + The quotation marks are not included in the expression value. */ + /* Skip opening quotation mark. */ + read_char (p); + start = p->pos; + while (has_more (p) && peek_char (p) != '"') + read_char (p); + end = p->pos; + /* Skip the terminating quotation mark. */ + read_char (p); + } + else if (peek_char (p) == '(') + { + /* Read as a parenthesized string -- for tuples/coordinates. */ + /* The parentheses are included in the expression value. */ + int c; + + start = p->pos; + do + { + c = read_char (p); + } + while (c != -1 && c != ')'); + end = p->pos; + } + else if (has_more (p)) + { + /* Read as a single word -- for numeric values or words without + whitespace. */ + start = p->pos; + while (has_more (p) && ! is_whitespace (peek_char (p))) + read_char (p); + end = p->pos; + } + else + { + /* The end of the theme file has been reached. */ + grub_error (GRUB_ERR_IO, "%s:%d:%d expression expected in theme file", + p->filename, p->line_num, p->col_num); + return 0; + } + + return grub_new_substring (p->buf, start, end); +} + +static grub_err_t +parse_proportional_spec (char *value, signed *abs, grub_fixed_signed_t *prop) +{ + signed num; + char *ptr; + int sig = 0; + *abs = 0; + *prop = 0; + ptr = value; + while (*ptr) + { + sig = 0; + + while (*ptr == '-' || *ptr == '+') + { + if (*ptr == '-') + sig = !sig; + ptr++; + } + + num = grub_strtoul (ptr, &ptr, 0); + if (grub_errno) + return grub_errno; + if (sig) + num = -num; + if (*ptr == '%') + { + *prop += grub_fixed_fsf_divide (grub_signed_to_fixed (num), 100); + ptr++; + } + else + *abs += num; + } + return GRUB_ERR_NONE; +} + + +/* Read a GUI object specification from the theme file. + Any components created will be added to the GUI container PARENT. */ +static grub_err_t +read_object (struct parsebuf *p, grub_gui_container_t parent) +{ + grub_video_rect_t bounds; + + char *name; + name = read_identifier (p); + if (! name) + goto cleanup; + + grub_gui_component_t component = 0; + if (grub_strcmp (name, "label") == 0) + { + component = grub_gui_label_new (); + } + else if (grub_strcmp (name, "image") == 0) + { + component = grub_gui_image_new (); + } + else if (grub_strcmp (name, "vbox") == 0) + { + component = (grub_gui_component_t) grub_gui_vbox_new (); + } + else if (grub_strcmp (name, "hbox") == 0) + { + component = (grub_gui_component_t) grub_gui_hbox_new (); + } + else if (grub_strcmp (name, "canvas") == 0) + { + component = (grub_gui_component_t) grub_gui_canvas_new (); + } + else if (grub_strcmp (name, "progress_bar") == 0) + { + component = grub_gui_progress_bar_new (); + } + else if (grub_strcmp (name, "circular_progress") == 0) + { + component = grub_gui_circular_progress_new (); + } + else if (grub_strcmp (name, "boot_menu") == 0) + { + component = grub_gui_list_new (); + } + else + { + /* Unknown type. */ + grub_error (GRUB_ERR_IO, "%s:%d:%d unknown object type `%s'", + p->filename, p->line_num, p->col_num, name); + goto cleanup; + } + + if (! component) + goto cleanup; + + /* Inform the component about the theme so it can find its resources. */ + component->ops->set_property (component, "theme_dir", p->theme_dir); + component->ops->set_property (component, "theme_path", p->filename); + + /* Add the component as a child of PARENT. */ + bounds.x = 0; + bounds.y = 0; + bounds.width = -1; + bounds.height = -1; + component->ops->set_bounds (component, &bounds); + parent->ops->add (parent, component); + + skip_whitespace (p); + if (read_char (p) != '{') + { + grub_error (GRUB_ERR_IO, + "%s:%d:%d expected `{' after object type name `%s'", + p->filename, p->line_num, p->col_num, name); + goto cleanup; + } + + while (has_more (p)) + { + skip_whitespace (p); + + /* Check whether the end has been encountered. */ + if (peek_char (p) == '}') + { + /* Skip the closing brace. */ + read_char (p); + break; + } + + if (peek_char (p) == '#') + { + /* Skip comments. */ + advance_to_next_line (p); + continue; + } + + if (peek_char (p) == '+') + { + /* Skip the '+'. */ + read_char (p); + + /* Check whether this component is a container. */ + if (component->ops->is_instance (component, "container")) + { + /* Read the sub-object recursively and add it as a child. */ + if (read_object (p, (grub_gui_container_t) component) != 0) + goto cleanup; + /* After reading the sub-object, resume parsing, expecting + another property assignment or sub-object definition. */ + continue; + } + else + { + grub_error (GRUB_ERR_IO, + "%s:%d:%d attempted to add object to non-container", + p->filename, p->line_num, p->col_num); + goto cleanup; + } + } + + char *property; + property = read_identifier (p); + if (! property) + { + grub_error (GRUB_ERR_IO, "%s:%d:%d identifier expected in theme file", + p->filename, p->line_num, p->col_num); + goto cleanup; + } + + skip_whitespace (p); + if (read_char (p) != '=') + { + grub_error (GRUB_ERR_IO, + "%s:%d:%d expected `=' after property name `%s'", + p->filename, p->line_num, p->col_num, property); + grub_free (property); + goto cleanup; + } + skip_whitespace (p); + + char *value; + value = read_expression (p); + if (! value) + { + grub_free (property); + goto cleanup; + } + + /* Handle the property value. */ + if (grub_strcmp (property, "left") == 0) + parse_proportional_spec (value, &component->x, &component->xfrac); + else if (grub_strcmp (property, "top") == 0) + parse_proportional_spec (value, &component->y, &component->yfrac); + else if (grub_strcmp (property, "width") == 0) + parse_proportional_spec (value, &component->w, &component->wfrac); + else if (grub_strcmp (property, "height") == 0) + parse_proportional_spec (value, &component->h, &component->hfrac); + else + /* General property handling. */ + component->ops->set_property (component, property, value); + + grub_free (value); + grub_free (property); + if (grub_errno != GRUB_ERR_NONE) + goto cleanup; + } + +cleanup: + grub_free (name); + return grub_errno; +} + +static grub_err_t +read_property (struct parsebuf *p) +{ + char *name; + + /* Read the property name. */ + name = read_identifier (p); + if (! name) + { + advance_to_next_line (p); + return grub_errno; + } + + /* Skip whitespace before separator. */ + skip_whitespace (p); + + /* Read separator. */ + if (read_char (p) != ':') + { + grub_error (GRUB_ERR_IO, + "%s:%d:%d missing separator after property name `%s'", + p->filename, p->line_num, p->col_num, name); + goto done; + } + + /* Skip whitespace after separator. */ + skip_whitespace (p); + + /* Get the value based on its type. */ + if (peek_char (p) == '"') + { + /* String value (e.g., '"My string"'). */ + char *value = read_expression (p); + if (! value) + { + grub_error (GRUB_ERR_IO, "%s:%d:%d missing property value", + p->filename, p->line_num, p->col_num); + goto done; + } + /* If theme_set_string results in an error, grub_errno will be returned + below. */ + theme_set_string (p->view, name, value, p->theme_dir, + p->filename, p->line_num, p->col_num); + grub_free (value); + } + else + { + grub_error (GRUB_ERR_IO, + "%s:%d:%d property value invalid; " + "enclose literal values in quotes (\")", + p->filename, p->line_num, p->col_num); + goto done; + } + +done: + grub_free (name); + return grub_errno; +} + +/* Set properties on the view based on settings from the specified + theme file. */ +grub_err_t +grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, const char *theme_path) +{ + grub_file_t file; + struct parsebuf p; + + p.view = view; + p.theme_dir = grub_get_dirname (theme_path); + + file = grub_file_open (theme_path); + if (! file) + { + grub_free (p.theme_dir); + return grub_errno; + } + + p.len = grub_file_size (file); + p.buf = grub_malloc (p.len); + p.pos = 0; + p.line_num = 1; + p.col_num = 1; + p.filename = theme_path; + if (! p.buf) + { + grub_file_close (file); + grub_free (p.theme_dir); + return grub_errno; + } + if (grub_file_read (file, p.buf, p.len) != p.len) + { + grub_free (p.buf); + grub_file_close (file); + grub_free (p.theme_dir); + return grub_errno; + } + + if (view->canvas) + view->canvas->component.ops->destroy (view->canvas); + + view->canvas = grub_gui_canvas_new (); + ((grub_gui_component_t) view->canvas) + ->ops->set_bounds ((grub_gui_component_t) view->canvas, + &view->screen); + + while (has_more (&p)) + { + /* Skip comments (lines beginning with #). */ + if (peek_char (&p) == '#') + { + advance_to_next_line (&p); + continue; + } + + /* Find the first non-whitespace character. */ + skip_whitespace (&p); + + /* Handle the content. */ + if (peek_char (&p) == '+') + { + /* Skip the '+'. */ + read_char (&p); + read_object (&p, view->canvas); + } + else + { + read_property (&p); + } + + if (grub_errno != GRUB_ERR_NONE) + goto fail; + } + + /* Set the new theme path. */ + grub_free (view->theme_path); + view->theme_path = grub_strdup (theme_path); + goto cleanup; + +fail: + if (view->canvas) + { + view->canvas->component.ops->destroy (view->canvas); + view->canvas = 0; + } + +cleanup: + grub_free (p.buf); + grub_file_close (file); + grub_free (p.theme_dir); + return grub_errno; +} diff --git a/gfxmenu/view.c b/gfxmenu/view.c new file mode 100644 index 000000000..bf637a96d --- /dev/null +++ b/gfxmenu/view.c @@ -0,0 +1,486 @@ +/* view.c - Graphical menu interface MVC view. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The component ID identifying GUI components to be updated as the timeout + status changes. */ +#define TIMEOUT_COMPONENT_ID "__timeout__" + +static void +init_terminal (grub_gfxmenu_view_t view); +static grub_video_rect_t term_rect; +static grub_gfxmenu_view_t term_view; + +/* Create a new view object, loading the theme specified by THEME_PATH and + associating MODEL with the view. */ +grub_gfxmenu_view_t +grub_gfxmenu_view_new (const char *theme_path, + int width, int height) +{ + grub_gfxmenu_view_t view; + grub_font_t default_font; + grub_gui_color_t default_fg_color; + grub_gui_color_t default_bg_color; + + view = grub_malloc (sizeof (*view)); + if (! view) + return 0; + + view->screen.x = 0; + view->screen.y = 0; + view->screen.width = width; + view->screen.height = height; + + default_font = grub_font_get ("Unknown Regular 16"); + default_fg_color = grub_gui_color_rgb (0, 0, 0); + default_bg_color = grub_gui_color_rgb (255, 255, 255); + + view->canvas = 0; + + view->title_font = default_font; + view->message_font = default_font; + view->terminal_font_name = grub_strdup ("Fixed 10"); + view->title_color = default_fg_color; + view->message_color = default_bg_color; + view->message_bg_color = default_fg_color; + view->desktop_image = 0; + view->desktop_color = default_bg_color; + view->terminal_box = grub_gfxmenu_create_box (0, 0); + view->title_text = grub_strdup ("GRUB Boot Menu"); + view->progress_message_text = 0; + view->theme_path = 0; + + /* Set the timeout bar's frame. */ + view->progress_message_frame.width = view->screen.width * 4 / 5; + view->progress_message_frame.height = 50; + view->progress_message_frame.x = view->screen.x + + (view->screen.width - view->progress_message_frame.width) / 2; + view->progress_message_frame.y = view->screen.y + + view->screen.height - 90 - 20 - view->progress_message_frame.height; + + if (grub_gfxmenu_view_load_theme (view, theme_path) != 0) + { + grub_gfxmenu_view_destroy (view); + return 0; + } + + return view; +} + +/* Destroy the view object. All used memory is freed. */ +void +grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view) +{ + if (!view) + return; + grub_video_bitmap_destroy (view->desktop_image); + if (view->terminal_box) + view->terminal_box->destroy (view->terminal_box); + grub_free (view->terminal_font_name); + grub_free (view->title_text); + grub_free (view->progress_message_text); + grub_free (view->theme_path); + if (view->canvas) + view->canvas->component.ops->destroy (view->canvas); + grub_free (view); +} + +static void +redraw_background (grub_gfxmenu_view_t view, + const grub_video_rect_t *bounds) +{ + if (view->desktop_image) + { + struct grub_video_bitmap *img = view->desktop_image; + grub_video_blit_bitmap (img, GRUB_VIDEO_BLIT_REPLACE, + bounds->x, bounds->y, + bounds->x - view->screen.x, + bounds->y - view->screen.y, + bounds->width, bounds->height); + } + else + { + grub_video_fill_rect (grub_gui_map_color (view->desktop_color), + bounds->x, bounds->y, + bounds->width, bounds->height); + } +} + +static void +draw_title (grub_gfxmenu_view_t view) +{ + if (! view->title_text) + return; + + /* Center the title. */ + int title_width = grub_font_get_string_width (view->title_font, + view->title_text); + int x = (view->screen.width - title_width) / 2; + int y = 40 + grub_font_get_ascent (view->title_font); + grub_font_draw_string (view->title_text, + view->title_font, + grub_gui_map_color (view->title_color), + x, y); +} + +struct progress_value_data +{ + int visible; + int start; + int end; + int value; +}; + +static void +update_timeout_visit (grub_gui_component_t component, + void *userdata) +{ + struct progress_value_data *pv; + pv = (struct progress_value_data *) userdata; + + ((struct grub_gui_progress *) component)->ops + ->set_state ((struct grub_gui_progress *) component, + pv->visible, pv->start, pv->value, pv->end); +} + +void +grub_gfxmenu_print_timeout (int timeout, void *data) +{ + struct grub_gfxmenu_view *view = data; + + struct progress_value_data pv; + + auto void redraw_timeout_visit (grub_gui_component_t component, + void *userdata __attribute__ ((unused))); + + auto void redraw_timeout_visit (grub_gui_component_t component, + void *userdata __attribute__ ((unused))) + { + grub_video_rect_t bounds; + component->ops->get_bounds (component, &bounds); + grub_gfxmenu_view_redraw (view, &bounds); + } + + if (view->first_timeout == -1) + view->first_timeout = timeout; + + pv.visible = 1; + pv.start = -(view->first_timeout + 1); + pv.end = 0; + pv.value = -timeout; + + grub_gui_find_by_id ((grub_gui_component_t) view->canvas, + TIMEOUT_COMPONENT_ID, update_timeout_visit, &pv); + grub_gui_find_by_id ((grub_gui_component_t) view->canvas, + TIMEOUT_COMPONENT_ID, redraw_timeout_visit, &pv); + grub_video_swap_buffers (); + if (view->double_repaint) + grub_gui_find_by_id ((grub_gui_component_t) view->canvas, + TIMEOUT_COMPONENT_ID, redraw_timeout_visit, &pv); +} + +void +grub_gfxmenu_clear_timeout (void *data) +{ + struct progress_value_data pv; + struct grub_gfxmenu_view *view = data; + + auto void redraw_timeout_visit (grub_gui_component_t component, + void *userdata __attribute__ ((unused))); + + auto void redraw_timeout_visit (grub_gui_component_t component, + void *userdata __attribute__ ((unused))) + { + grub_video_rect_t bounds; + component->ops->get_bounds (component, &bounds); + grub_gfxmenu_view_redraw (view, &bounds); + } + + pv.visible = 0; + pv.start = 1; + pv.end = 0; + pv.value = 0; + + grub_gui_find_by_id ((grub_gui_component_t) view->canvas, + TIMEOUT_COMPONENT_ID, update_timeout_visit, &pv); + grub_gui_find_by_id ((grub_gui_component_t) view->canvas, + TIMEOUT_COMPONENT_ID, redraw_timeout_visit, &pv); + grub_video_swap_buffers (); + if (view->double_repaint) + grub_gui_find_by_id ((grub_gui_component_t) view->canvas, + TIMEOUT_COMPONENT_ID, redraw_timeout_visit, &pv); +} + +static void +update_menu_visit (grub_gui_component_t component, + void *userdata) +{ + grub_gfxmenu_view_t view; + view = userdata; + if (component->ops->is_instance (component, "list")) + { + grub_gui_list_t list = (grub_gui_list_t) component; + list->ops->set_view_info (list, view); + } +} + +/* Update any boot menu components with the current menu model and + theme path. */ +static void +update_menu_components (grub_gfxmenu_view_t view) +{ + grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas, + update_menu_visit, view); +} + +static void +draw_message (grub_gfxmenu_view_t view) +{ + char *text = view->progress_message_text; + grub_video_rect_t f = view->progress_message_frame; + if (! text) + return; + + grub_font_t font = view->message_font; + grub_video_color_t color = grub_gui_map_color (view->message_color); + + /* Border. */ + grub_video_fill_rect (color, + f.x-1, f.y-1, f.width+2, f.height+2); + /* Fill. */ + grub_video_fill_rect (grub_gui_map_color (view->message_bg_color), + f.x, f.y, f.width, f.height); + + /* Center the text. */ + int text_width = grub_font_get_string_width (font, text); + int x = f.x + (f.width - text_width) / 2; + int y = (f.y + (f.height - grub_font_get_descent (font)) / 2 + + grub_font_get_ascent (font) / 2); + grub_font_draw_string (text, font, color, x, y); +} + +void +grub_gfxmenu_view_redraw (grub_gfxmenu_view_t view, + const grub_video_rect_t *region) +{ + if (grub_video_have_common_points (&term_rect, region)) + grub_gfxterm_schedule_repaint (); + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + redraw_background (view, region); + if (view->canvas) + view->canvas->component.ops->paint (view->canvas, region); + draw_title (view); + if (grub_video_have_common_points (&view->progress_message_frame, region)) + draw_message (view); +} + +void +grub_gfxmenu_view_draw (grub_gfxmenu_view_t view) +{ + init_terminal (view); + + /* Clear the screen; there may be garbage left over in video memory. */ + grub_video_fill_rect (grub_video_map_rgb (0, 0, 0), + view->screen.x, view->screen.y, + view->screen.width, view->screen.height); + grub_video_swap_buffers (); + if (view->double_repaint) + grub_video_fill_rect (grub_video_map_rgb (0, 0, 0), + view->screen.x, view->screen.y, + view->screen.width, view->screen.height); + + update_menu_components (view); + + grub_gfxmenu_view_redraw (view, &view->screen); + grub_video_swap_buffers (); + if (view->double_repaint) + grub_gfxmenu_view_redraw (view, &view->screen); +} + +static void +redraw_menu_visit (grub_gui_component_t component, + void *userdata) +{ + grub_gfxmenu_view_t view; + view = userdata; + if (component->ops->is_instance (component, "list")) + { + grub_gui_list_t list; + grub_video_rect_t bounds; + + list = (grub_gui_list_t) component; + component->ops->get_bounds (component, &bounds); + grub_gfxmenu_view_redraw (view, &bounds); + } +} + +void +grub_gfxmenu_redraw_menu (grub_gfxmenu_view_t view) +{ + update_menu_components (view); + + grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas, + redraw_menu_visit, view); + grub_video_swap_buffers (); + if (view->double_repaint) + { + grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas, + redraw_menu_visit, view); + } +} + +void +grub_gfxmenu_set_chosen_entry (int entry, void *data) +{ + grub_gfxmenu_view_t view = data; + + view->selected = entry; + grub_gfxmenu_redraw_menu (view); +} + +static void +grub_gfxmenu_draw_terminal_box (void) +{ + grub_gfxmenu_box_t term_box; + + term_box = term_view->terminal_box; + if (!term_box) + return; + + term_box->set_content_size (term_box, term_rect.width, + term_rect.height); + + term_box->draw (term_box, + term_rect.x - term_box->get_left_pad (term_box), + term_rect.y - term_box->get_top_pad (term_box)); + grub_video_swap_buffers (); + if (term_view->double_repaint) + term_box->draw (term_box, + term_rect.x - term_box->get_left_pad (term_box), + term_rect.y - term_box->get_top_pad (term_box)); +} + +static void +init_terminal (grub_gfxmenu_view_t view) +{ + term_rect.width = view->screen.width * 7 / 10; + term_rect.height = view->screen.height * 7 / 10; + + term_rect.x = view->screen.x + view->screen.width * (10 - 7) / 10 / 2; + term_rect.y = view->screen.y + view->screen.height * (10 - 7) / 10 / 2; + + term_view = view; + + /* Note: currently there is no API for changing the gfxterm font + on the fly, so whatever font the initially loaded theme specifies + will be permanent. */ + grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY, term_rect.x, + term_rect.y, + term_rect.width, term_rect.height, + view->double_repaint, view->terminal_font_name, 3); + grub_gfxterm_decorator_hook = grub_gfxmenu_draw_terminal_box; +} + +/* FIXME: previously notifications were displayed in special case. + Is it necessary? + */ +#if 0 +/* Sets MESSAGE as the progress message for the view. + MESSAGE can be 0, in which case no message is displayed. */ +static void +set_progress_message (grub_gfxmenu_view_t view, const char *message) +{ + grub_free (view->progress_message_text); + if (message) + view->progress_message_text = grub_strdup (message); + else + view->progress_message_text = 0; +} + +static void +notify_booting (grub_menu_entry_t entry, void *userdata) +{ + grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata; + + char *s = grub_malloc (100 + grub_strlen (entry->title)); + if (!s) + return; + + grub_sprintf (s, "Booting '%s'", entry->title); + set_progress_message (view, s); + grub_free (s); + grub_gfxmenu_view_redraw (view, &view->progress_message_frame); + grub_video_swap_buffers (); + if (view->double_repaint) + grub_gfxmenu_view_redraw (view, &view->progress_message_frame); +} + +static void +notify_fallback (grub_menu_entry_t entry, void *userdata) +{ + grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata; + + char *s = grub_malloc (100 + grub_strlen (entry->title)); + if (!s) + return; + + grub_sprintf (s, "Falling back to '%s'", entry->title); + set_progress_message (view, s); + grub_free (s); + grub_gfxmenu_view_redraw (view, &view->progress_message_frame); + grub_video_swap_buffers (); + if (view->double_repaint) + grub_gfxmenu_view_redraw (view, &view->progress_message_frame); +} + +static void +notify_execution_failure (void *userdata __attribute__ ((unused))) +{ +} + + +static struct grub_menu_execute_callback execute_callback = +{ + .notify_booting = notify_booting, + .notify_fallback = notify_fallback, + .notify_failure = notify_execution_failure +}; + +#endif diff --git a/gfxmenu/widget-box.c b/gfxmenu/widget-box.c new file mode 100644 index 000000000..079fd66d4 --- /dev/null +++ b/gfxmenu/widget-box.c @@ -0,0 +1,313 @@ +/* widget_box.c - Pixmap-stylized box widget. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum box_pixmaps +{ + BOX_PIXMAP_NW, BOX_PIXMAP_NE, BOX_PIXMAP_SE, BOX_PIXMAP_SW, + BOX_PIXMAP_N, BOX_PIXMAP_E, BOX_PIXMAP_S, BOX_PIXMAP_W, + BOX_PIXMAP_CENTER +}; + +static const char *box_pixmap_names[] = { + /* Corners: */ + "nw", "ne", "se", "sw", + /* Sides: */ + "n", "e", "s", "w", + /* Center: */ + "c" +}; + +#define BOX_NUM_PIXMAPS (sizeof(box_pixmap_names)/sizeof(*box_pixmap_names)) + +static int +get_height (struct grub_video_bitmap *bitmap) +{ + if (bitmap) + return grub_video_bitmap_get_height (bitmap); + else + return 0; +} + +static int +get_width (struct grub_video_bitmap *bitmap) +{ + if (bitmap) + return grub_video_bitmap_get_width (bitmap); + else + return 0; +} + +static void +blit (grub_gfxmenu_box_t self, int pixmap_index, int x, int y) +{ + struct grub_video_bitmap *bitmap; + bitmap = self->scaled_pixmaps[pixmap_index]; + if (! bitmap) + return; + grub_video_blit_bitmap (bitmap, GRUB_VIDEO_BLIT_BLEND, + x, y, 0, 0, + grub_video_bitmap_get_width (bitmap), + grub_video_bitmap_get_height (bitmap)); +} + +static void +draw (grub_gfxmenu_box_t self, int x, int y) +{ + int height_n; + int height_s; + int height_e; + int height_w; + int width_n; + int width_s; + int width_e; + int width_w; + + height_n = get_height (self->scaled_pixmaps[BOX_PIXMAP_N]); + height_s = get_height (self->scaled_pixmaps[BOX_PIXMAP_S]); + height_e = get_height (self->scaled_pixmaps[BOX_PIXMAP_E]); + height_w = get_height (self->scaled_pixmaps[BOX_PIXMAP_W]); + width_n = get_width (self->scaled_pixmaps[BOX_PIXMAP_N]); + width_s = get_width (self->scaled_pixmaps[BOX_PIXMAP_S]); + width_e = get_width (self->scaled_pixmaps[BOX_PIXMAP_E]); + width_w = get_width (self->scaled_pixmaps[BOX_PIXMAP_W]); + + /* Draw sides. */ + blit (self, BOX_PIXMAP_N, x + width_w, y); + blit (self, BOX_PIXMAP_S, x + width_w, y + height_n + self->content_height); + blit (self, BOX_PIXMAP_E, x + width_w + self->content_width, y + height_n); + blit (self, BOX_PIXMAP_W, x, y + height_n); + + /* Draw corners. */ + blit (self, BOX_PIXMAP_NW, x, y); + blit (self, BOX_PIXMAP_NE, x + width_w + self->content_width, y); + blit (self, BOX_PIXMAP_SE, + x + width_w + self->content_width, + y + height_n + self->content_height); + blit (self, BOX_PIXMAP_SW, x, y + height_n + self->content_height); + + /* Draw center. */ + blit (self, BOX_PIXMAP_CENTER, x + width_w, y + height_n); +} + +static grub_err_t +scale_pixmap (grub_gfxmenu_box_t self, int i, int w, int h) +{ + struct grub_video_bitmap **scaled = &self->scaled_pixmaps[i]; + struct grub_video_bitmap *raw = self->raw_pixmaps[i]; + + if (raw == 0) + return grub_errno; + + if (w == -1) + w = grub_video_bitmap_get_width (raw); + if (h == -1) + h = grub_video_bitmap_get_height (raw); + + if (*scaled == 0 + || ((int) grub_video_bitmap_get_width (*scaled) != w) + || ((int) grub_video_bitmap_get_height (*scaled) != h)) + { + if (*scaled) + { + grub_video_bitmap_destroy (*scaled); + *scaled = 0; + } + + /* Don't try to create a bitmap with a zero dimension. */ + if (w != 0 && h != 0) + grub_video_bitmap_create_scaled (scaled, w, h, raw, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + if (grub_errno != GRUB_ERR_NONE) + { + grub_error_push (); + grub_error (grub_errno, + "failed to scale bitmap for styled box pixmap #%d", i); + } + } + + return grub_errno; +} + +static void +set_content_size (grub_gfxmenu_box_t self, + int width, int height) +{ + self->content_width = width; + self->content_height = height; + + /* Resize sides to match the width and height. */ + /* It is assumed that the corners width/height match the adjacent sides. */ + + /* Resize N and S sides to match width. */ + if (scale_pixmap(self, BOX_PIXMAP_N, width, -1) != GRUB_ERR_NONE) + return; + if (scale_pixmap(self, BOX_PIXMAP_S, width, -1) != GRUB_ERR_NONE) + return; + + /* Resize E and W sides to match height. */ + if (scale_pixmap(self, BOX_PIXMAP_E, -1, height) != GRUB_ERR_NONE) + return; + if (scale_pixmap(self, BOX_PIXMAP_W, -1, height) != GRUB_ERR_NONE) + return; + + /* Don't scale the corners--they are assumed to match the sides. */ + if (scale_pixmap(self, BOX_PIXMAP_NW, -1, -1) != GRUB_ERR_NONE) + return; + if (scale_pixmap(self, BOX_PIXMAP_SW, -1, -1) != GRUB_ERR_NONE) + return; + if (scale_pixmap(self, BOX_PIXMAP_NE, -1, -1) != GRUB_ERR_NONE) + return; + if (scale_pixmap(self, BOX_PIXMAP_SE, -1, -1) != GRUB_ERR_NONE) + return; + + /* Scale the center area. */ + if (scale_pixmap(self, BOX_PIXMAP_CENTER, width, height) != GRUB_ERR_NONE) + return; +} + +static int +get_left_pad (grub_gfxmenu_box_t self) +{ + return get_width (self->raw_pixmaps[BOX_PIXMAP_W]); +} + +static int +get_top_pad (grub_gfxmenu_box_t self) +{ + return get_height (self->raw_pixmaps[BOX_PIXMAP_N]); +} + +static int +get_right_pad (grub_gfxmenu_box_t self) +{ + return get_width (self->raw_pixmaps[BOX_PIXMAP_E]); +} + +static int +get_bottom_pad (grub_gfxmenu_box_t self) +{ + return get_height (self->raw_pixmaps[BOX_PIXMAP_S]); +} + +static void +destroy (grub_gfxmenu_box_t self) +{ + unsigned i; + for (i = 0; i < BOX_NUM_PIXMAPS; i++) + { + if (self->raw_pixmaps[i]) + grub_video_bitmap_destroy(self->raw_pixmaps[i]); + self->raw_pixmaps[i] = 0; + + if (self->scaled_pixmaps[i]) + grub_video_bitmap_destroy(self->scaled_pixmaps[i]); + self->scaled_pixmaps[i] = 0; + } + grub_free (self->raw_pixmaps); + self->raw_pixmaps = 0; + grub_free (self->scaled_pixmaps); + self->scaled_pixmaps = 0; + + /* Free self: must be the last step! */ + grub_free (self); +} + + +/* Create a new box. If PIXMAPS_PREFIX and PIXMAPS_SUFFIX are both non-null, + then an attempt is made to load the north, south, east, west, northwest, + northeast, southeast, southwest, and center pixmaps. + If either PIXMAPS_PREFIX or PIXMAPS_SUFFIX is 0, then no pixmaps are + loaded, and the box has zero-width borders and is drawn transparent. */ +grub_gfxmenu_box_t +grub_gfxmenu_create_box (const char *pixmaps_prefix, + const char *pixmaps_suffix) +{ + unsigned i; + grub_gfxmenu_box_t box; + + box = (grub_gfxmenu_box_t) grub_malloc (sizeof (*box)); + if (! box) + return 0; + + box->content_width = 0; + box->content_height = 0; + box->raw_pixmaps = + (struct grub_video_bitmap **) + grub_malloc (BOX_NUM_PIXMAPS * sizeof (struct grub_video_bitmap *)); + box->scaled_pixmaps = + (struct grub_video_bitmap **) + grub_malloc (BOX_NUM_PIXMAPS * sizeof (struct grub_video_bitmap *)); + + /* Initialize all pixmap pointers to NULL so that proper destruction can + be performed if an error is encountered partway through construction. */ + for (i = 0; i < BOX_NUM_PIXMAPS; i++) + box->raw_pixmaps[i] = 0; + for (i = 0; i < BOX_NUM_PIXMAPS; i++) + box->scaled_pixmaps[i] = 0; + + /* Load the pixmaps. */ + for (i = 0; i < BOX_NUM_PIXMAPS; i++) + { + if (pixmaps_prefix && pixmaps_suffix) + { + char *path; + char *path_end; + + path = grub_malloc (grub_strlen (pixmaps_prefix) + + grub_strlen (box_pixmap_names[i]) + + grub_strlen (pixmaps_suffix) + + 1); + if (! path) + goto fail_and_destroy; + + /* Construct the specific path for this pixmap. */ + path_end = grub_stpcpy (path, pixmaps_prefix); + path_end = grub_stpcpy (path_end, box_pixmap_names[i]); + path_end = grub_stpcpy (path_end, pixmaps_suffix); + + grub_video_bitmap_load (&box->raw_pixmaps[i], path); + grub_free (path); + + /* Ignore missing pixmaps. */ + grub_errno = GRUB_ERR_NONE; + } + } + + box->draw = draw; + box->set_content_size = set_content_size; + box->get_left_pad = get_left_pad; + box->get_top_pad = get_top_pad; + box->get_right_pad = get_right_pad; + box->get_bottom_pad = get_bottom_pad; + box->destroy = destroy; + return box; + +fail_and_destroy: + destroy (box); + return 0; +} diff --git a/gnulib/alloca.h b/gnulib/alloca.h index 5d16e08b7..107534e98 100644 --- a/gnulib/alloca.h +++ b/gnulib/alloca.h @@ -1,7 +1,7 @@ /* Memory allocation on the stack. - Copyright (C) 1995, 1999, 2001-2004, 2006-2008 Free Software - Foundation, Inc. + Copyright (C) 1995, 1999, 2001-2004, 2006-2010 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 diff --git a/gnulib/argp-ba.c b/gnulib/argp-ba.c new file mode 100644 index 000000000..95feabb86 --- /dev/null +++ b/gnulib/argp-ba.c @@ -0,0 +1,34 @@ +/* Default definition for ARGP_PROGRAM_BUG_ADDRESS. + Copyright (C) 1996, 1997, 1999, 2009, 2010 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* If set by the user program, it should point to string that is the + bug-reporting address for the program. It will be printed by argp_help if + the ARGP_HELP_BUG_ADDR flag is set (as it is by various standard help + messages), embedded in a sentence that says something like `Report bugs to + ADDR.'. */ +const char *argp_program_bug_address +/* This variable should be zero-initialized. On most systems, putting it into + BSS is sufficient. Not so on MacOS X 10.3 and 10.4, see + + . */ +#if defined __ELF__ + /* On ELF systems, variables in BSS behave well. */ +#else + = (const char *) 0 +#endif + ; diff --git a/gnulib/argp-eexst.c b/gnulib/argp-eexst.c new file mode 100644 index 000000000..115a8cd5d --- /dev/null +++ b/gnulib/argp-eexst.c @@ -0,0 +1,30 @@ +/* Default definition for ARGP_ERR_EXIT_STATUS + Copyright (C) 1997, 2009, 2010 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "argp.h" + +/* The exit status that argp will use when exiting due to a parsing error. + If not defined or set by the user program, this defaults to EX_USAGE from + . */ +error_t argp_err_exit_status = EX_USAGE; diff --git a/gnulib/argp-fmtstream.c b/gnulib/argp-fmtstream.c new file mode 100644 index 000000000..70bbebc21 --- /dev/null +++ b/gnulib/argp-fmtstream.c @@ -0,0 +1,435 @@ +/* Word-wrapping and line-truncating streams + Copyright (C) 1997-1999, 2001-2003, 2005, 2009-2010 Free Software + Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* This package emulates glibc `line_wrap_stream' semantics for systems that + don't have that. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include "argp-fmtstream.h" +#include "argp-namefrob.h" + +#ifndef ARGP_FMTSTREAM_USE_LINEWRAP + +#ifndef isblank +#define isblank(ch) ((ch)==' ' || (ch)=='\t') +#endif + +#if defined _LIBC && defined USE_IN_LIBIO +# include +# include +# define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a) +#endif + +#define INIT_BUF_SIZE 200 +#define PRINTF_SIZE_GUESS 150 + +/* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines + written on it with LMARGIN spaces and limits them to RMARGIN columns + total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by + replacing the whitespace before them with a newline and WMARGIN spaces. + Otherwise, chars beyond RMARGIN are simply dropped until a newline. + Returns NULL if there was an error. */ +argp_fmtstream_t +__argp_make_fmtstream (FILE *stream, + size_t lmargin, size_t rmargin, ssize_t wmargin) +{ + argp_fmtstream_t fs; + + fs = (struct argp_fmtstream *) malloc (sizeof (struct argp_fmtstream)); + if (fs != NULL) + { + fs->stream = stream; + + fs->lmargin = lmargin; + fs->rmargin = rmargin; + fs->wmargin = wmargin; + fs->point_col = 0; + fs->point_offs = 0; + + fs->buf = (char *) malloc (INIT_BUF_SIZE); + if (! fs->buf) + { + free (fs); + fs = 0; + } + else + { + fs->p = fs->buf; + fs->end = fs->buf + INIT_BUF_SIZE; + } + } + + return fs; +} +#if 0 +/* Not exported. */ +#ifdef weak_alias +weak_alias (__argp_make_fmtstream, argp_make_fmtstream) +#endif +#endif + +/* Flush FS to its stream, and free it (but don't close the stream). */ +void +__argp_fmtstream_free (argp_fmtstream_t fs) +{ + __argp_fmtstream_update (fs); + if (fs->p > fs->buf) + { +#ifdef USE_IN_LIBIO + __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf); +#else + fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream); +#endif + } + free (fs->buf); + free (fs); +} +#if 0 +/* Not exported. */ +#ifdef weak_alias +weak_alias (__argp_fmtstream_free, argp_fmtstream_free) +#endif +#endif + +/* Process FS's buffer so that line wrapping is done from POINT_OFFS to the + end of its buffer. This code is mostly from glibc stdio/linewrap.c. */ +void +__argp_fmtstream_update (argp_fmtstream_t fs) +{ + char *buf, *nl; + size_t len; + + /* Scan the buffer for newlines. */ + buf = fs->buf + fs->point_offs; + while (buf < fs->p) + { + size_t r; + + if (fs->point_col == 0 && fs->lmargin != 0) + { + /* We are starting a new line. Print spaces to the left margin. */ + const size_t pad = fs->lmargin; + if (fs->p + pad < fs->end) + { + /* We can fit in them in the buffer by moving the + buffer text up and filling in the beginning. */ + memmove (buf + pad, buf, fs->p - buf); + fs->p += pad; /* Compensate for bigger buffer. */ + memset (buf, ' ', pad); /* Fill in the spaces. */ + buf += pad; /* Don't bother searching them. */ + } + else + { + /* No buffer space for spaces. Must flush. */ + size_t i; + for (i = 0; i < pad; i++) + { +#ifdef USE_IN_LIBIO + if (_IO_fwide (fs->stream, 0) > 0) + putwc_unlocked (L' ', fs->stream); + else +#endif + putc_unlocked (' ', fs->stream); + } + } + fs->point_col = pad; + } + + len = fs->p - buf; + nl = memchr (buf, '\n', len); + + if (fs->point_col < 0) + fs->point_col = 0; + + if (!nl) + { + /* The buffer ends in a partial line. */ + + if (fs->point_col + len < fs->rmargin) + { + /* The remaining buffer text is a partial line and fits + within the maximum line width. Advance point for the + characters to be written and stop scanning. */ + fs->point_col += len; + break; + } + else + /* Set the end-of-line pointer for the code below to + the end of the buffer. */ + nl = fs->p; + } + else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin) + { + /* The buffer contains a full line that fits within the maximum + line width. Reset point and scan the next line. */ + fs->point_col = 0; + buf = nl + 1; + continue; + } + + /* This line is too long. */ + r = fs->rmargin - 1; + + if (fs->wmargin < 0) + { + /* Truncate the line by overwriting the excess with the + newline and anything after it in the buffer. */ + if (nl < fs->p) + { + memmove (buf + (r - fs->point_col), nl, fs->p - nl); + fs->p -= buf + (r - fs->point_col) - nl; + /* Reset point for the next line and start scanning it. */ + fs->point_col = 0; + buf += r + 1; /* Skip full line plus \n. */ + } + else + { + /* The buffer ends with a partial line that is beyond the + maximum line width. Advance point for the characters + written, and discard those past the max from the buffer. */ + fs->point_col += len; + fs->p -= fs->point_col - r; + break; + } + } + else + { + /* Do word wrap. Go to the column just past the maximum line + width and scan back for the beginning of the word there. + Then insert a line break. */ + + char *p, *nextline; + int i; + + p = buf + (r + 1 - fs->point_col); + while (p >= buf && !isblank ((unsigned char) *p)) + --p; + nextline = p + 1; /* This will begin the next line. */ + + if (nextline > buf) + { + /* Swallow separating blanks. */ + if (p >= buf) + do + --p; + while (p >= buf && isblank ((unsigned char) *p)); + nl = p + 1; /* The newline will replace the first blank. */ + } + else + { + /* A single word that is greater than the maximum line width. + Oh well. Put it on an overlong line by itself. */ + p = buf + (r + 1 - fs->point_col); + /* Find the end of the long word. */ + if (p < nl) + do + ++p; + while (p < nl && !isblank ((unsigned char) *p)); + if (p == nl) + { + /* It already ends a line. No fussing required. */ + fs->point_col = 0; + buf = nl + 1; + continue; + } + /* We will move the newline to replace the first blank. */ + nl = p; + /* Swallow separating blanks. */ + do + ++p; + while (isblank ((unsigned char) *p)); + /* The next line will start here. */ + nextline = p; + } + + /* Note: There are a bunch of tests below for + NEXTLINE == BUF + LEN + 1; this case is where NL happens to fall + at the end of the buffer, and NEXTLINE is in fact empty (and so + we need not be careful to maintain its contents). */ + + if ((nextline == buf + len + 1 + ? fs->end - nl < fs->wmargin + 1 + : nextline - (nl + 1) < fs->wmargin) + && fs->p > nextline) + { + /* The margin needs more blanks than we removed. */ + if (fs->end - fs->p > fs->wmargin + 1) + /* Make some space for them. */ + { + size_t mv = fs->p - nextline; + memmove (nl + 1 + fs->wmargin, nextline, mv); + nextline = nl + 1 + fs->wmargin; + len = nextline + mv - buf; + *nl++ = '\n'; + } + else + /* Output the first line so we can use the space. */ + { +#ifdef _LIBC + __fxprintf (fs->stream, "%.*s\n", + (int) (nl - fs->buf), fs->buf); +#else + if (nl > fs->buf) + fwrite_unlocked (fs->buf, 1, nl - fs->buf, fs->stream); + putc_unlocked ('\n', fs->stream); +#endif + + len += buf - fs->buf; + nl = buf = fs->buf; + } + } + else + /* We can fit the newline and blanks in before + the next word. */ + *nl++ = '\n'; + + if (nextline - nl >= fs->wmargin + || (nextline == buf + len + 1 && fs->end - nextline >= fs->wmargin)) + /* Add blanks up to the wrap margin column. */ + for (i = 0; i < fs->wmargin; ++i) + *nl++ = ' '; + else + for (i = 0; i < fs->wmargin; ++i) +#ifdef USE_IN_LIBIO + if (_IO_fwide (fs->stream, 0) > 0) + putwc_unlocked (L' ', fs->stream); + else +#endif + putc_unlocked (' ', fs->stream); + + /* Copy the tail of the original buffer into the current buffer + position. */ + if (nl < nextline) + memmove (nl, nextline, buf + len - nextline); + len -= nextline - buf; + + /* Continue the scan on the remaining lines in the buffer. */ + buf = nl; + + /* Restore bufp to include all the remaining text. */ + fs->p = nl + len; + + /* Reset the counter of what has been output this line. If wmargin + is 0, we want to avoid the lmargin getting added, so we set + point_col to a magic value of -1 in that case. */ + fs->point_col = fs->wmargin ? fs->wmargin : -1; + } + } + + /* Remember that we've scanned as far as the end of the buffer. */ + fs->point_offs = fs->p - fs->buf; +} + +/* Ensure that FS has space for AMOUNT more bytes in its buffer, either by + growing the buffer, or by flushing it. True is returned iff we succeed. */ +int +__argp_fmtstream_ensure (struct argp_fmtstream *fs, size_t amount) +{ + if ((size_t) (fs->end - fs->p) < amount) + { + ssize_t wrote; + + /* Flush FS's buffer. */ + __argp_fmtstream_update (fs); + +#ifdef _LIBC + __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf); + wrote = fs->p - fs->buf; +#else + wrote = fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream); +#endif + if (wrote == fs->p - fs->buf) + { + fs->p = fs->buf; + fs->point_offs = 0; + } + else + { + fs->p -= wrote; + fs->point_offs -= wrote; + memmove (fs->buf, fs->buf + wrote, fs->p - fs->buf); + return 0; + } + + if ((size_t) (fs->end - fs->buf) < amount) + /* Gotta grow the buffer. */ + { + size_t old_size = fs->end - fs->buf; + size_t new_size = old_size + amount; + char *new_buf; + + if (new_size < old_size || ! (new_buf = realloc (fs->buf, new_size))) + { + __set_errno (ENOMEM); + return 0; + } + + fs->buf = new_buf; + fs->end = new_buf + new_size; + fs->p = fs->buf; + } + } + + return 1; +} + +ssize_t +__argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...) +{ + int out; + size_t avail; + size_t size_guess = PRINTF_SIZE_GUESS; /* How much space to reserve. */ + + do + { + va_list args; + + if (! __argp_fmtstream_ensure (fs, size_guess)) + return -1; + + va_start (args, fmt); + avail = fs->end - fs->p; + out = __vsnprintf (fs->p, avail, fmt, args); + va_end (args); + if ((size_t) out >= avail) + size_guess = out + 1; + } + while ((size_t) out >= avail); + + fs->p += out; + + return out; +} +#if 0 +/* Not exported. */ +#ifdef weak_alias +weak_alias (__argp_fmtstream_printf, argp_fmtstream_printf) +#endif +#endif + +#endif /* !ARGP_FMTSTREAM_USE_LINEWRAP */ diff --git a/gnulib/argp-fmtstream.h b/gnulib/argp-fmtstream.h new file mode 100644 index 000000000..b913d1b25 --- /dev/null +++ b/gnulib/argp-fmtstream.h @@ -0,0 +1,354 @@ +/* Word-wrapping and line-truncating streams. + Copyright (C) 1997, 2006-2010 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* This package emulates glibc `line_wrap_stream' semantics for systems that + don't have that. If the system does have it, it is just a wrapper for + that. This header file is only used internally while compiling argp, and + shouldn't be installed. */ + +#ifndef _ARGP_FMTSTREAM_H +#define _ARGP_FMTSTREAM_H + +#include +#include +#include + +#ifndef __attribute__ +/* The __attribute__ feature is available in gcc versions 2.5 and later. + The __-protected variants of the attributes 'format' and 'printf' are + accepted by gcc versions 2.6.4 (effectively 2.7) and later. + We enable __attribute__ only if these are supported too, because + gnulib and libintl do '#define printf __printf__' when they override + the 'printf' function. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __attribute__(Spec) /* empty */ +# endif +#endif + +#if (_LIBC - 0 && !defined (USE_IN_LIBIO)) \ + || (defined (__GNU_LIBRARY__) && defined (HAVE_LINEWRAP_H)) +/* line_wrap_stream is available, so use that. */ +#define ARGP_FMTSTREAM_USE_LINEWRAP +#endif + +#ifdef ARGP_FMTSTREAM_USE_LINEWRAP +/* Just be a simple wrapper for line_wrap_stream; the semantics are + *slightly* different, as line_wrap_stream doesn't actually make a new + object, it just modifies the given stream (reversibly) to do + line-wrapping. Since we control who uses this code, it doesn't matter. */ + +#include + +typedef FILE *argp_fmtstream_t; + +#define argp_make_fmtstream line_wrap_stream +#define __argp_make_fmtstream line_wrap_stream +#define argp_fmtstream_free line_unwrap_stream +#define __argp_fmtstream_free line_unwrap_stream + +#define __argp_fmtstream_putc(fs,ch) putc(ch,fs) +#define argp_fmtstream_putc(fs,ch) putc(ch,fs) +#define __argp_fmtstream_puts(fs,str) fputs(str,fs) +#define argp_fmtstream_puts(fs,str) fputs(str,fs) +#define __argp_fmtstream_write(fs,str,len) fwrite(str,1,len,fs) +#define argp_fmtstream_write(fs,str,len) fwrite(str,1,len,fs) +#define __argp_fmtstream_printf fprintf +#define argp_fmtstream_printf fprintf + +#define __argp_fmtstream_lmargin line_wrap_lmargin +#define argp_fmtstream_lmargin line_wrap_lmargin +#define __argp_fmtstream_set_lmargin line_wrap_set_lmargin +#define argp_fmtstream_set_lmargin line_wrap_set_lmargin +#define __argp_fmtstream_rmargin line_wrap_rmargin +#define argp_fmtstream_rmargin line_wrap_rmargin +#define __argp_fmtstream_set_rmargin line_wrap_set_rmargin +#define argp_fmtstream_set_rmargin line_wrap_set_rmargin +#define __argp_fmtstream_wmargin line_wrap_wmargin +#define argp_fmtstream_wmargin line_wrap_wmargin +#define __argp_fmtstream_set_wmargin line_wrap_set_wmargin +#define argp_fmtstream_set_wmargin line_wrap_set_wmargin +#define __argp_fmtstream_point line_wrap_point +#define argp_fmtstream_point line_wrap_point + +#else /* !ARGP_FMTSTREAM_USE_LINEWRAP */ +/* Guess we have to define our own version. */ + +struct argp_fmtstream +{ + FILE *stream; /* The stream we're outputting to. */ + + size_t lmargin, rmargin; /* Left and right margins. */ + ssize_t wmargin; /* Margin to wrap to, or -1 to truncate. */ + + /* Point in buffer to which we've processed for wrapping, but not output. */ + size_t point_offs; + /* Output column at POINT_OFFS, or -1 meaning 0 but don't add lmargin. */ + ssize_t point_col; + + char *buf; /* Output buffer. */ + char *p; /* Current end of text in BUF. */ + char *end; /* Absolute end of BUF. */ +}; + +typedef struct argp_fmtstream *argp_fmtstream_t; + +/* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines + written on it with LMARGIN spaces and limits them to RMARGIN columns + total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by + replacing the whitespace before them with a newline and WMARGIN spaces. + Otherwise, chars beyond RMARGIN are simply dropped until a newline. + Returns NULL if there was an error. */ +extern argp_fmtstream_t __argp_make_fmtstream (FILE *__stream, + size_t __lmargin, + size_t __rmargin, + ssize_t __wmargin); +extern argp_fmtstream_t argp_make_fmtstream (FILE *__stream, + size_t __lmargin, + size_t __rmargin, + ssize_t __wmargin); + +/* Flush __FS to its stream, and free it (but don't close the stream). */ +extern void __argp_fmtstream_free (argp_fmtstream_t __fs); +extern void argp_fmtstream_free (argp_fmtstream_t __fs); + +extern ssize_t __argp_fmtstream_printf (argp_fmtstream_t __fs, + const char *__fmt, ...) + __attribute__ ((__format__ (printf, 2, 3))); +extern ssize_t argp_fmtstream_printf (argp_fmtstream_t __fs, + const char *__fmt, ...) + __attribute__ ((__format__ (printf, 2, 3))); + +#if _LIBC || !defined __OPTIMIZE__ +extern int __argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch); +extern int argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch); + +extern int __argp_fmtstream_puts (argp_fmtstream_t __fs, const char *__str); +extern int argp_fmtstream_puts (argp_fmtstream_t __fs, const char *__str); + +extern size_t __argp_fmtstream_write (argp_fmtstream_t __fs, + const char *__str, size_t __len); +extern size_t argp_fmtstream_write (argp_fmtstream_t __fs, + const char *__str, size_t __len); +#endif + +/* Access macros for various bits of state. */ +#define argp_fmtstream_lmargin(__fs) ((__fs)->lmargin) +#define argp_fmtstream_rmargin(__fs) ((__fs)->rmargin) +#define argp_fmtstream_wmargin(__fs) ((__fs)->wmargin) +#define __argp_fmtstream_lmargin argp_fmtstream_lmargin +#define __argp_fmtstream_rmargin argp_fmtstream_rmargin +#define __argp_fmtstream_wmargin argp_fmtstream_wmargin + +#if _LIBC || !defined __OPTIMIZE__ +/* Set __FS's left margin to LMARGIN and return the old value. */ +extern size_t argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, + size_t __lmargin); +extern size_t __argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, + size_t __lmargin); + +/* Set __FS's right margin to __RMARGIN and return the old value. */ +extern size_t argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, + size_t __rmargin); +extern size_t __argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, + size_t __rmargin); + +/* Set __FS's wrap margin to __WMARGIN and return the old value. */ +extern size_t argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, + size_t __wmargin); +extern size_t __argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, + size_t __wmargin); + +/* Return the column number of the current output point in __FS. */ +extern size_t argp_fmtstream_point (argp_fmtstream_t __fs); +extern size_t __argp_fmtstream_point (argp_fmtstream_t __fs); +#endif + +/* Internal routines. */ +extern void _argp_fmtstream_update (argp_fmtstream_t __fs); +extern void __argp_fmtstream_update (argp_fmtstream_t __fs); +extern int _argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount); +extern int __argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount); + +#ifdef __OPTIMIZE__ +/* Inline versions of above routines. */ + +#if !_LIBC +#define __argp_fmtstream_putc argp_fmtstream_putc +#define __argp_fmtstream_puts argp_fmtstream_puts +#define __argp_fmtstream_write argp_fmtstream_write +#define __argp_fmtstream_set_lmargin argp_fmtstream_set_lmargin +#define __argp_fmtstream_set_rmargin argp_fmtstream_set_rmargin +#define __argp_fmtstream_set_wmargin argp_fmtstream_set_wmargin +#define __argp_fmtstream_point argp_fmtstream_point +#define __argp_fmtstream_update _argp_fmtstream_update +#define __argp_fmtstream_ensure _argp_fmtstream_ensure +#endif + +#ifndef ARGP_FS_EI +# ifdef __GNUC__ + /* GCC 4.3 and above with -std=c99 or -std=gnu99 implements ISO C99 + inline semantics, unless -fgnu89-inline is used. It defines a macro + __GNUC_STDC_INLINE__ to indicate this situation or a macro + __GNUC_GNU_INLINE__ to indicate the opposite situation. + + GCC 4.2 with -std=c99 or -std=gnu99 implements the GNU C inline + semantics but warns, unless -fgnu89-inline is used: + warning: C99 inline functions are not supported; using GNU89 + warning: to disable this warning use -fgnu89-inline or the gnu_inline function attribute + It defines a macro __GNUC_GNU_INLINE__ to indicate this situation. + + Whereas Apple GCC 4.0.1 build 5479 without -std=c99 or -std=gnu99 + implements the GNU C inline semantics and defines the macro + __GNUC_GNU_INLINE__, but it does not warn and does not support + __attribute__ ((__gnu_inline__)). + + All in all, these are the possible combinations. For every compiler, + we need to choose ARGP_FS_EI so that the corresponding table cell + contains an "ok". + + \ ARGP_FS_EI inline extern extern + \ inline inline + CC \ __attribute__ + ((gnu_inline)) + + gcc 4.3.0 error ok ok + gcc 4.3.0 -std=gnu99 -fgnu89-inline error ok ok + gcc 4.3.0 -std=gnu99 ok error ok + + gcc 4.2.2 error ok ok + gcc 4.2.2 -std=gnu99 -fgnu89-inline error ok ok + gcc 4.2.2 -std=gnu99 error warning ok + + gcc 4.1.2 error ok warning + gcc 4.1.2 -std=gnu99 error ok warning + + Apple gcc 4.0.1 error ok warning + Apple gcc 4.0.1 -std=gnu99 ok error warning + */ +# if defined __GNUC_STDC_INLINE__ +# define ARGP_FS_EI inline +# elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2) +# define ARGP_FS_EI extern inline __attribute__ ((__gnu_inline__)) +# else +# define ARGP_FS_EI extern inline +# endif +# else + /* With other compilers, assume the ISO C99 meaning of 'inline', if + the compiler supports 'inline' at all. */ +# define ARGP_FS_EI inline +# endif +#endif + +ARGP_FS_EI size_t +__argp_fmtstream_write (argp_fmtstream_t __fs, + const char *__str, size_t __len) +{ + if (__fs->p + __len <= __fs->end || __argp_fmtstream_ensure (__fs, __len)) + { + memcpy (__fs->p, __str, __len); + __fs->p += __len; + return __len; + } + else + return 0; +} + +ARGP_FS_EI int +__argp_fmtstream_puts (argp_fmtstream_t __fs, const char *__str) +{ + size_t __len = strlen (__str); + if (__len) + { + size_t __wrote = __argp_fmtstream_write (__fs, __str, __len); + return __wrote == __len ? 0 : -1; + } + else + return 0; +} + +ARGP_FS_EI int +__argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch) +{ + if (__fs->p < __fs->end || __argp_fmtstream_ensure (__fs, 1)) + return *__fs->p++ = __ch; + else + return EOF; +} + +/* Set __FS's left margin to __LMARGIN and return the old value. */ +ARGP_FS_EI size_t +__argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, size_t __lmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->lmargin; + __fs->lmargin = __lmargin; + return __old; +} + +/* Set __FS's right margin to __RMARGIN and return the old value. */ +ARGP_FS_EI size_t +__argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, size_t __rmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->rmargin; + __fs->rmargin = __rmargin; + return __old; +} + +/* Set FS's wrap margin to __WMARGIN and return the old value. */ +ARGP_FS_EI size_t +__argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, size_t __wmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->wmargin; + __fs->wmargin = __wmargin; + return __old; +} + +/* Return the column number of the current output point in __FS. */ +ARGP_FS_EI size_t +__argp_fmtstream_point (argp_fmtstream_t __fs) +{ + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + return __fs->point_col >= 0 ? __fs->point_col : 0; +} + +#if !_LIBC +#undef __argp_fmtstream_putc +#undef __argp_fmtstream_puts +#undef __argp_fmtstream_write +#undef __argp_fmtstream_set_lmargin +#undef __argp_fmtstream_set_rmargin +#undef __argp_fmtstream_set_wmargin +#undef __argp_fmtstream_point +#undef __argp_fmtstream_update +#undef __argp_fmtstream_ensure +#endif + +#endif /* __OPTIMIZE__ */ + +#endif /* ARGP_FMTSTREAM_USE_LINEWRAP */ + +#endif /* argp-fmtstream.h */ diff --git a/gnulib/argp-fs-xinl.c b/gnulib/argp-fs-xinl.c new file mode 100644 index 000000000..2c683f914 --- /dev/null +++ b/gnulib/argp-fs-xinl.c @@ -0,0 +1,42 @@ +/* Real definitions for extern inline functions in argp-fmtstream.h + Copyright (C) 1997, 2003, 2004, 2009, 2010 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#define ARGP_FS_EI +#undef __OPTIMIZE__ +#define __OPTIMIZE__ 1 +#include "argp-fmtstream.h" + +#if 0 +/* Not exported. */ +/* Add weak aliases. */ +#if _LIBC - 0 && !defined (ARGP_FMTSTREAM_USE_LINEWRAP) && defined (weak_alias) + +weak_alias (__argp_fmtstream_putc, argp_fmtstream_putc) +weak_alias (__argp_fmtstream_puts, argp_fmtstream_puts) +weak_alias (__argp_fmtstream_write, argp_fmtstream_write) +weak_alias (__argp_fmtstream_set_lmargin, argp_fmtstream_set_lmargin) +weak_alias (__argp_fmtstream_set_rmargin, argp_fmtstream_set_rmargin) +weak_alias (__argp_fmtstream_set_wmargin, argp_fmtstream_set_wmargin) +weak_alias (__argp_fmtstream_point, argp_fmtstream_point) + +#endif +#endif diff --git a/gnulib/argp-help.c b/gnulib/argp-help.c new file mode 100644 index 000000000..5b6d950be --- /dev/null +++ b/gnulib/argp-help.c @@ -0,0 +1,1951 @@ +/* Hierarchial argument parsing help output + Copyright (C) 1995-2005, 2007, 2009-2010 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_IN_LIBIO +# include +#endif + +#ifdef _LIBC +# include +# undef dgettext +# define dgettext(domain, msgid) \ + INTUSE(__dcgettext) (domain, msgid, LC_MESSAGES) +#else +# include "gettext.h" +#endif + +#include "argp.h" +#include "argp-fmtstream.h" +#include "argp-namefrob.h" + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif + +/* User-selectable (using an environment variable) formatting parameters. + + These may be specified in an environment variable called `ARGP_HELP_FMT', + with a contents like: VAR1=VAL1,VAR2=VAL2,BOOLVAR2,no-BOOLVAR2 + Where VALn must be a positive integer. The list of variables is in the + UPARAM_NAMES vector, below. */ + +/* Default parameters. */ +#define DUP_ARGS 0 /* True if option argument can be duplicated. */ +#define DUP_ARGS_NOTE 1 /* True to print a note about duplicate args. */ +#define SHORT_OPT_COL 2 /* column in which short options start */ +#define LONG_OPT_COL 6 /* column in which long options start */ +#define DOC_OPT_COL 2 /* column in which doc options start */ +#define OPT_DOC_COL 29 /* column in which option text starts */ +#define HEADER_COL 1 /* column in which group headers are printed */ +#define USAGE_INDENT 12 /* indentation of wrapped usage lines */ +#define RMARGIN 79 /* right margin used for wrapping */ + +/* User-selectable (using an environment variable) formatting parameters. + They must all be of type `int' for the parsing code to work. */ +struct uparams +{ + /* If true, arguments for an option are shown with both short and long + options, even when a given option has both, e.g. `-x ARG, --longx=ARG'. + If false, then if an option has both, the argument is only shown with + the long one, e.g., `-x, --longx=ARG', and a message indicating that + this really means both is printed below the options. */ + int dup_args; + + /* This is true if when DUP_ARGS is false, and some duplicate arguments have + been suppressed, an explanatory message should be printed. */ + int dup_args_note; + + /* Various output columns. */ + int short_opt_col; /* column in which short options start */ + int long_opt_col; /* column in which long options start */ + int doc_opt_col; /* column in which doc options start */ + int opt_doc_col; /* column in which option text starts */ + int header_col; /* column in which group headers are printed */ + int usage_indent; /* indentation of wrapped usage lines */ + int rmargin; /* right margin used for wrapping */ + + int valid; /* True when the values in here are valid. */ +}; + +/* This is a global variable, as user options are only ever read once. */ +static struct uparams uparams = { + DUP_ARGS, DUP_ARGS_NOTE, + SHORT_OPT_COL, LONG_OPT_COL, DOC_OPT_COL, OPT_DOC_COL, HEADER_COL, + USAGE_INDENT, RMARGIN, + 0 +}; + +/* A particular uparam, and what the user name is. */ +struct uparam_name +{ + const char *name; /* User name. */ + int is_bool; /* Whether it's `boolean'. */ + size_t uparams_offs; /* Location of the (int) field in UPARAMS. */ +}; + +/* The name-field mappings we know about. */ +static const struct uparam_name uparam_names[] = +{ + { "dup-args", 1, offsetof (struct uparams, dup_args) }, + { "dup-args-note", 1, offsetof (struct uparams, dup_args_note) }, + { "short-opt-col", 0, offsetof (struct uparams, short_opt_col) }, + { "long-opt-col", 0, offsetof (struct uparams, long_opt_col) }, + { "doc-opt-col", 0, offsetof (struct uparams, doc_opt_col) }, + { "opt-doc-col", 0, offsetof (struct uparams, opt_doc_col) }, + { "header-col", 0, offsetof (struct uparams, header_col) }, + { "usage-indent", 0, offsetof (struct uparams, usage_indent) }, + { "rmargin", 0, offsetof (struct uparams, rmargin) }, + { 0 } +}; + +static void +validate_uparams (const struct argp_state *state, struct uparams *upptr) +{ + const struct uparam_name *up; + + for (up = uparam_names; up->name; up++) + { + if (up->is_bool + || up->uparams_offs == offsetof (struct uparams, rmargin)) + continue; + if (*(int *)((char *)upptr + up->uparams_offs) >= upptr->rmargin) + { + __argp_failure (state, 0, 0, + dgettext (state->root_argp->argp_domain, + "\ +ARGP_HELP_FMT: %s value is less than or equal to %s"), + "rmargin", up->name); + return; + } + } + uparams = *upptr; + uparams.valid = 1; +} + +/* Read user options from the environment, and fill in UPARAMS appropiately. */ +static void +fill_in_uparams (const struct argp_state *state) +{ + const char *var = getenv ("ARGP_HELP_FMT"); + struct uparams new_params = uparams; + +#define SKIPWS(p) do { while (isspace ((unsigned char) *p)) p++; } while (0); + + if (var) + { + /* Parse var. */ + while (*var) + { + SKIPWS (var); + + if (isalpha ((unsigned char) *var)) + { + size_t var_len; + const struct uparam_name *un; + int unspec = 0, val = 0; + const char *arg = var; + + while (isalnum ((unsigned char) *arg) || *arg == '-' || *arg == '_') + arg++; + var_len = arg - var; + + SKIPWS (arg); + + if (*arg == '\0' || *arg == ',') + unspec = 1; + else if (*arg == '=') + { + arg++; + SKIPWS (arg); + } + + if (unspec) + { + if (var[0] == 'n' && var[1] == 'o' && var[2] == '-') + { + val = 0; + var += 3; + var_len -= 3; + } + else + val = 1; + } + else if (isdigit ((unsigned char) *arg)) + { + val = atoi (arg); + while (isdigit ((unsigned char) *arg)) + arg++; + SKIPWS (arg); + } + + for (un = uparam_names; un->name; un++) + if (strlen (un->name) == var_len + && strncmp (var, un->name, var_len) == 0) + { + if (unspec && !un->is_bool) + __argp_failure (state, 0, 0, + dgettext (state->root_argp->argp_domain, + "\ +%.*s: ARGP_HELP_FMT parameter requires a value"), + (int) var_len, var); + else if (val < 0) + __argp_failure (state, 0, 0, + dgettext (state->root_argp->argp_domain, + "\ +%.*s: ARGP_HELP_FMT parameter must be positive"), + (int) var_len, var); + else + *(int *)((char *)&new_params + un->uparams_offs) = val; + break; + } + if (! un->name) + __argp_failure (state, 0, 0, + dgettext (state->root_argp->argp_domain, "\ +%.*s: Unknown ARGP_HELP_FMT parameter"), + (int) var_len, var); + + var = arg; + if (*var == ',') + var++; + } + else if (*var) + { + __argp_failure (state, 0, 0, + dgettext (state->root_argp->argp_domain, + "Garbage in ARGP_HELP_FMT: %s"), var); + break; + } + } + validate_uparams (state, &new_params); + } +} + +/* Returns true if OPT hasn't been marked invisible. Visibility only affects + whether OPT is displayed or used in sorting, not option shadowing. */ +#define ovisible(opt) (! ((opt)->flags & OPTION_HIDDEN)) + +/* Returns true if OPT is an alias for an earlier option. */ +#define oalias(opt) ((opt)->flags & OPTION_ALIAS) + +/* Returns true if OPT is an documentation-only entry. */ +#define odoc(opt) ((opt)->flags & OPTION_DOC) + +/* Returns true if OPT should not be translated */ +#define onotrans(opt) ((opt)->flags & OPTION_NO_TRANS) + +/* Returns true if OPT is the end-of-list marker for a list of options. */ +#define oend(opt) __option_is_end (opt) + +/* Returns true if OPT has a short option. */ +#define oshort(opt) __option_is_short (opt) + +/* + The help format for a particular option is like: + + -xARG, -yARG, --long1=ARG, --long2=ARG Documentation... + + Where ARG will be omitted if there's no argument, for this option, or + will be surrounded by "[" and "]" appropiately if the argument is + optional. The documentation string is word-wrapped appropiately, and if + the list of options is long enough, it will be started on a separate line. + If there are no short options for a given option, the first long option is + indented slighly in a way that's supposed to make most long options appear + to be in a separate column. + + For example, the following output (from ps): + + -p PID, --pid=PID List the process PID + --pgrp=PGRP List processes in the process group PGRP + -P, -x, --no-parent Include processes without parents + -Q, --all-fields Don't elide unusable fields (normally if there's + some reason ps can't print a field for any + process, it's removed from the output entirely) + -r, --reverse, --gratuitously-long-reverse-option + Reverse the order of any sort + --session[=SID] Add the processes from the session SID (which + defaults to the sid of the current process) + + Here are some more options: + -f ZOT, --foonly=ZOT Glork a foonly + -z, --zaza Snit a zar + + -?, --help Give this help list + --usage Give a short usage message + -V, --version Print program version + + The struct argp_option array for the above could look like: + + { + {"pid", 'p', "PID", 0, "List the process PID"}, + {"pgrp", OPT_PGRP, "PGRP", 0, "List processes in the process group PGRP"}, + {"no-parent", 'P', 0, 0, "Include processes without parents"}, + {0, 'x', 0, OPTION_ALIAS}, + {"all-fields",'Q', 0, 0, "Don't elide unusable fields (normally" + " if there's some reason ps can't" + " print a field for any process, it's" + " removed from the output entirely)" }, + {"reverse", 'r', 0, 0, "Reverse the order of any sort"}, + {"gratuitously-long-reverse-option", 0, 0, OPTION_ALIAS}, + {"session", OPT_SESS, "SID", OPTION_ARG_OPTIONAL, + "Add the processes from the session" + " SID (which defaults to the sid of" + " the current process)" }, + + {0,0,0,0, "Here are some more options:"}, + {"foonly", 'f', "ZOT", 0, "Glork a foonly"}, + {"zaza", 'z', 0, 0, "Snit a zar"}, + + {0} + } + + Note that the last three options are automatically supplied by argp_parse, + unless you tell it not to with ARGP_NO_HELP. + +*/ + +/* Returns true if CH occurs between BEG and END. */ +static int +find_char (char ch, char *beg, char *end) +{ + while (beg < end) + if (*beg == ch) + return 1; + else + beg++; + return 0; +} + +struct hol_cluster; /* fwd decl */ + +struct hol_entry +{ + /* First option. */ + const struct argp_option *opt; + /* Number of options (including aliases). */ + unsigned num; + + /* A pointers into the HOL's short_options field, to the first short option + letter for this entry. The order of the characters following this point + corresponds to the order of options pointed to by OPT, and there are at + most NUM. A short option recorded in a option following OPT is only + valid if it occurs in the right place in SHORT_OPTIONS (otherwise it's + probably been shadowed by some other entry). */ + char *short_options; + + /* Entries are sorted by their group first, in the order: + 1, 2, ..., n, 0, -m, ..., -2, -1 + and then alphabetically within each group. The default is 0. */ + int group; + + /* The cluster of options this entry belongs to, or 0 if none. */ + struct hol_cluster *cluster; + + /* The argp from which this option came. */ + const struct argp *argp; + + /* Position in the array */ + unsigned ord; +}; + +/* A cluster of entries to reflect the argp tree structure. */ +struct hol_cluster +{ + /* A descriptive header printed before options in this cluster. */ + const char *header; + + /* Used to order clusters within the same group with the same parent, + according to the order in which they occurred in the parent argp's child + list. */ + int index; + + /* How to sort this cluster with respect to options and other clusters at the + same depth (clusters always follow options in the same group). */ + int group; + + /* The cluster to which this cluster belongs, or 0 if it's at the base + level. */ + struct hol_cluster *parent; + + /* The argp from which this cluster is (eventually) derived. */ + const struct argp *argp; + + /* The distance this cluster is from the root. */ + int depth; + + /* Clusters in a given hol are kept in a linked list, to make freeing them + possible. */ + struct hol_cluster *next; +}; + +/* A list of options for help. */ +struct hol +{ + /* An array of hol_entry's. */ + struct hol_entry *entries; + /* The number of entries in this hol. If this field is zero, the others + are undefined. */ + unsigned num_entries; + + /* A string containing all short options in this HOL. Each entry contains + pointers into this string, so the order can't be messed with blindly. */ + char *short_options; + + /* Clusters of entries in this hol. */ + struct hol_cluster *clusters; +}; + +/* Create a struct hol from the options in ARGP. CLUSTER is the + hol_cluster in which these entries occur, or 0, if at the root. */ +static struct hol * +make_hol (const struct argp *argp, struct hol_cluster *cluster) +{ + char *so; + const struct argp_option *o; + const struct argp_option *opts = argp->options; + struct hol_entry *entry; + unsigned num_short_options = 0; + struct hol *hol = malloc (sizeof (struct hol)); + + assert (hol); + + hol->num_entries = 0; + hol->clusters = 0; + + if (opts) + { + int cur_group = 0; + + /* The first option must not be an alias. */ + assert (! oalias (opts)); + + /* Calculate the space needed. */ + for (o = opts; ! oend (o); o++) + { + if (! oalias (o)) + hol->num_entries++; + if (oshort (o)) + num_short_options++; /* This is an upper bound. */ + } + + hol->entries = malloc (sizeof (struct hol_entry) * hol->num_entries); + hol->short_options = malloc (num_short_options + 1); + + assert (hol->entries && hol->short_options); + if (SIZE_MAX <= UINT_MAX) + assert (hol->num_entries <= SIZE_MAX / sizeof (struct hol_entry)); + + /* Fill in the entries. */ + so = hol->short_options; + for (o = opts, entry = hol->entries; ! oend (o); entry++) + { + entry->opt = o; + entry->num = 0; + entry->short_options = so; + entry->group = cur_group = + o->group + ? o->group + : ((!o->name && !o->key) + ? cur_group + 1 + : cur_group); + entry->cluster = cluster; + entry->argp = argp; + + do + { + entry->num++; + if (oshort (o) && ! find_char (o->key, hol->short_options, so)) + /* O has a valid short option which hasn't already been used.*/ + *so++ = o->key; + o++; + } + while (! oend (o) && oalias (o)); + } + *so = '\0'; /* null terminated so we can find the length */ + } + + return hol; +} + +/* Add a new cluster to HOL, with the given GROUP and HEADER (taken from the + associated argp child list entry), INDEX, and PARENT, and return a pointer + to it. ARGP is the argp that this cluster results from. */ +static struct hol_cluster * +hol_add_cluster (struct hol *hol, int group, const char *header, int index, + struct hol_cluster *parent, const struct argp *argp) +{ + struct hol_cluster *cl = malloc (sizeof (struct hol_cluster)); + if (cl) + { + cl->group = group; + cl->header = header; + + cl->index = index; + cl->parent = parent; + cl->argp = argp; + cl->depth = parent ? parent->depth + 1 : 0; + + cl->next = hol->clusters; + hol->clusters = cl; + } + return cl; +} + +/* Free HOL and any resources it uses. */ +static void +hol_free (struct hol *hol) +{ + struct hol_cluster *cl = hol->clusters; + + while (cl) + { + struct hol_cluster *next = cl->next; + free (cl); + cl = next; + } + + if (hol->num_entries > 0) + { + free (hol->entries); + free (hol->short_options); + } + + free (hol); +} + +static int +hol_entry_short_iterate (const struct hol_entry *entry, + int (*func)(const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie), + const char *domain, void *cookie) +{ + unsigned nopts; + int val = 0; + const struct argp_option *opt, *real = entry->opt; + char *so = entry->short_options; + + for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--) + if (oshort (opt) && *so == opt->key) + { + if (!oalias (opt)) + real = opt; + if (ovisible (opt)) + val = (*func)(opt, real, domain, cookie); + so++; + } + + return val; +} + +static inline int +__attribute__ ((always_inline)) +hol_entry_long_iterate (const struct hol_entry *entry, + int (*func)(const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie), + const char *domain, void *cookie) +{ + unsigned nopts; + int val = 0; + const struct argp_option *opt, *real = entry->opt; + + for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--) + if (opt->name) + { + if (!oalias (opt)) + real = opt; + if (ovisible (opt)) + val = (*func)(opt, real, domain, cookie); + } + + return val; +} + +/* Iterator that returns true for the first short option. */ +static int +until_short (const struct argp_option *opt, const struct argp_option *real, + const char *domain, void *cookie) +{ + return oshort (opt) ? opt->key : 0; +} + +/* Returns the first valid short option in ENTRY, or 0 if there is none. */ +static char +hol_entry_first_short (const struct hol_entry *entry) +{ + return hol_entry_short_iterate (entry, until_short, + entry->argp->argp_domain, 0); +} + +/* Returns the first valid long option in ENTRY, or 0 if there is none. */ +static const char * +hol_entry_first_long (const struct hol_entry *entry) +{ + const struct argp_option *opt; + unsigned num; + for (opt = entry->opt, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + return opt->name; + return 0; +} + +/* Returns the entry in HOL with the long option name NAME, or 0 if there is + none. */ +static struct hol_entry * +hol_find_entry (struct hol *hol, const char *name) +{ + struct hol_entry *entry = hol->entries; + unsigned num_entries = hol->num_entries; + + while (num_entries-- > 0) + { + const struct argp_option *opt = entry->opt; + unsigned num_opts = entry->num; + + while (num_opts-- > 0) + if (opt->name && ovisible (opt) && strcmp (opt->name, name) == 0) + return entry; + else + opt++; + + entry++; + } + + return 0; +} + +/* If an entry with the long option NAME occurs in HOL, set it's special + sort position to GROUP. */ +static void +hol_set_group (struct hol *hol, const char *name, int group) +{ + struct hol_entry *entry = hol_find_entry (hol, name); + if (entry) + entry->group = group; +} + +/* Order by group: 0, 1, 2, ..., n, -m, ..., -2, -1. + EQ is what to return if GROUP1 and GROUP2 are the same. */ +static int +group_cmp (int group1, int group2, int eq) +{ + if (group1 == group2) + return eq; + else if ((group1 < 0 && group2 < 0) || (group1 >= 0 && group2 >= 0)) + return group1 - group2; + else + return group2 - group1; +} + +/* Compare clusters CL1 & CL2 by the order that they should appear in + output. */ +static int +hol_cluster_cmp (const struct hol_cluster *cl1, const struct hol_cluster *cl2) +{ + /* If one cluster is deeper than the other, use its ancestor at the same + level, so that finding the common ancestor is straightforward. + + clN->depth > 0 means that clN->parent != NULL (see hol_add_cluster) */ + while (cl1->depth > cl2->depth) + cl1 = cl1->parent; + while (cl2->depth > cl1->depth) + cl2 = cl2->parent; + + /* Now reduce both clusters to their ancestors at the point where both have + a common parent; these can be directly compared. */ + while (cl1->parent != cl2->parent) + cl1 = cl1->parent, cl2 = cl2->parent; + + return group_cmp (cl1->group, cl2->group, cl2->index - cl1->index); +} + +/* Return the ancestor of CL that's just below the root (i.e., has a parent + of 0). */ +static struct hol_cluster * +hol_cluster_base (struct hol_cluster *cl) +{ + while (cl->parent) + cl = cl->parent; + return cl; +} + +/* Return true if CL1 is a child of CL2. */ +static int +hol_cluster_is_child (const struct hol_cluster *cl1, + const struct hol_cluster *cl2) +{ + while (cl1 && cl1 != cl2) + cl1 = cl1->parent; + return cl1 == cl2; +} + +/* Given the name of a OPTION_DOC option, modifies NAME to start at the tail + that should be used for comparisons, and returns true iff it should be + treated as a non-option. */ +static int +canon_doc_option (const char **name) +{ + int non_opt; + + if (!*name) + non_opt = 1; + else + { + /* Skip initial whitespace. */ + while (isspace ((unsigned char) **name)) + (*name)++; + /* Decide whether this looks like an option (leading `-') or not. */ + non_opt = (**name != '-'); + /* Skip until part of name used for sorting. */ + while (**name && !isalnum ((unsigned char) **name)) + (*name)++; + } + return non_opt; +} + +#define HOL_ENTRY_PTRCMP(a,b) ((a)->ord < (b)->ord ? -1 : 1) + +/* Order ENTRY1 & ENTRY2 by the order which they should appear in a help + listing. */ +static int +hol_entry_cmp (const struct hol_entry *entry1, + const struct hol_entry *entry2) +{ + /* The group numbers by which the entries should be ordered; if either is + in a cluster, then this is just the group within the cluster. */ + int group1 = entry1->group, group2 = entry2->group; + int rc; + + if (entry1->cluster != entry2->cluster) + { + /* The entries are not within the same cluster, so we can't compare them + directly, we have to use the appropiate clustering level too. */ + if (! entry1->cluster) + /* ENTRY1 is at the `base level', not in a cluster, so we have to + compare it's group number with that of the base cluster in which + ENTRY2 resides. Note that if they're in the same group, the + clustered option always comes laster. */ + return group_cmp (group1, hol_cluster_base (entry2->cluster)->group, -1); + else if (! entry2->cluster) + /* Likewise, but ENTRY2's not in a cluster. */ + return group_cmp (hol_cluster_base (entry1->cluster)->group, group2, 1); + else + /* Both entries are in clusters, we can just compare the clusters. */ + return (rc = hol_cluster_cmp (entry1->cluster, entry2->cluster)) ? + rc : HOL_ENTRY_PTRCMP (entry1, entry2); + } + else if (group1 == group2) + /* The entries are both in the same cluster and group, so compare them + alphabetically. */ + { + int short1 = hol_entry_first_short (entry1); + int short2 = hol_entry_first_short (entry2); + int doc1 = odoc (entry1->opt); + int doc2 = odoc (entry2->opt); + const char *long1 = hol_entry_first_long (entry1); + const char *long2 = hol_entry_first_long (entry2); + + if (doc1) + doc1 = canon_doc_option (&long1); + if (doc2) + doc2 = canon_doc_option (&long2); + + if (doc1 != doc2) + /* `documentation' options always follow normal options (or + documentation options that *look* like normal options). */ + return doc1 - doc2; + else if (!short1 && !short2 && long1 && long2) + /* Only long options. */ + return (rc = __strcasecmp (long1, long2)) ? + rc : HOL_ENTRY_PTRCMP (entry1, entry2); + else + /* Compare short/short, long/short, short/long, using the first + character of long options. Entries without *any* valid + options (such as options with OPTION_HIDDEN set) will be put + first, but as they're not displayed, it doesn't matter where + they are. */ + { + unsigned char first1 = short1 ? short1 : long1 ? *long1 : 0; + unsigned char first2 = short2 ? short2 : long2 ? *long2 : 0; + /* Use tolower, not _tolower, since only the former is + guaranteed to work on something already lower case. */ + int lower_cmp = tolower (first1) - tolower (first2); + /* Compare ignoring case, except when the options are both the + same letter, in which case lower-case always comes first. */ + return lower_cmp ? lower_cmp : + (rc = first2 - first1) ? + rc : HOL_ENTRY_PTRCMP (entry1, entry2); + } + } + else + /* Within the same cluster, but not the same group, so just compare + groups. */ + return group_cmp (group1, group2, HOL_ENTRY_PTRCMP (entry1, entry2)); +} + +/* Version of hol_entry_cmp with correct signature for qsort. */ +static int +hol_entry_qcmp (const void *entry1_v, const void *entry2_v) +{ + return hol_entry_cmp (entry1_v, entry2_v); +} + +/* Sort HOL by group and alphabetically by option name (with short options + taking precedence over long). Since the sorting is for display purposes + only, the shadowing of options isn't effected. */ +static void +hol_sort (struct hol *hol) +{ + if (hol->num_entries > 0) + { + unsigned i; + struct hol_entry *e; + for (i = 0, e = hol->entries; i < hol->num_entries; i++, e++) + e->ord = i; + qsort (hol->entries, hol->num_entries, sizeof (struct hol_entry), + hol_entry_qcmp); + } +} + +/* Append MORE to HOL, destroying MORE in the process. Options in HOL shadow + any in MORE with the same name. */ +static void +hol_append (struct hol *hol, struct hol *more) +{ + struct hol_cluster **cl_end = &hol->clusters; + + /* Steal MORE's cluster list, and add it to the end of HOL's. */ + while (*cl_end) + cl_end = &(*cl_end)->next; + *cl_end = more->clusters; + more->clusters = 0; + + /* Merge entries. */ + if (more->num_entries > 0) + { + if (hol->num_entries == 0) + { + hol->num_entries = more->num_entries; + hol->entries = more->entries; + hol->short_options = more->short_options; + more->num_entries = 0; /* Mark MORE's fields as invalid. */ + } + else + /* Append the entries in MORE to those in HOL, taking care to only add + non-shadowed SHORT_OPTIONS values. */ + { + unsigned left; + char *so, *more_so; + struct hol_entry *e; + unsigned num_entries = hol->num_entries + more->num_entries; + struct hol_entry *entries = + malloc (num_entries * sizeof (struct hol_entry)); + unsigned hol_so_len = strlen (hol->short_options); + char *short_options = + malloc (hol_so_len + strlen (more->short_options) + 1); + + assert (entries && short_options); + if (SIZE_MAX <= UINT_MAX) + assert (num_entries <= SIZE_MAX / sizeof (struct hol_entry)); + + __mempcpy (__mempcpy (entries, hol->entries, + hol->num_entries * sizeof (struct hol_entry)), + more->entries, + more->num_entries * sizeof (struct hol_entry)); + + __mempcpy (short_options, hol->short_options, hol_so_len); + + /* Fix up the short options pointers from HOL. */ + for (e = entries, left = hol->num_entries; left > 0; e++, left--) + e->short_options += (short_options - hol->short_options); + + /* Now add the short options from MORE, fixing up its entries + too. */ + so = short_options + hol_so_len; + more_so = more->short_options; + for (left = more->num_entries; left > 0; e++, left--) + { + int opts_left; + const struct argp_option *opt; + + e->short_options = so; + + for (opts_left = e->num, opt = e->opt; opts_left; opt++, opts_left--) + { + int ch = *more_so; + if (oshort (opt) && ch == opt->key) + /* The next short option in MORE_SO, CH, is from OPT. */ + { + if (! find_char (ch, short_options, + short_options + hol_so_len)) + /* The short option CH isn't shadowed by HOL's options, + so add it to the sum. */ + *so++ = ch; + more_so++; + } + } + } + + *so = '\0'; + + free (hol->entries); + free (hol->short_options); + + hol->entries = entries; + hol->num_entries = num_entries; + hol->short_options = short_options; + } + } + + hol_free (more); +} + +/* Inserts enough spaces to make sure STREAM is at column COL. */ +static void +indent_to (argp_fmtstream_t stream, unsigned col) +{ + int needed = col - __argp_fmtstream_point (stream); + while (needed-- > 0) + __argp_fmtstream_putc (stream, ' '); +} + +/* Output to STREAM either a space, or a newline if there isn't room for at + least ENSURE characters before the right margin. */ +static void +space (argp_fmtstream_t stream, size_t ensure) +{ + if (__argp_fmtstream_point (stream) + ensure + >= __argp_fmtstream_rmargin (stream)) + __argp_fmtstream_putc (stream, '\n'); + else + __argp_fmtstream_putc (stream, ' '); +} + +/* If the option REAL has an argument, we print it in using the printf + format REQ_FMT or OPT_FMT depending on whether it's a required or + optional argument. */ +static void +arg (const struct argp_option *real, const char *req_fmt, const char *opt_fmt, + const char *domain, argp_fmtstream_t stream) +{ + if (real->arg) + { + if (real->flags & OPTION_ARG_OPTIONAL) + __argp_fmtstream_printf (stream, opt_fmt, + dgettext (domain, real->arg)); + else + __argp_fmtstream_printf (stream, req_fmt, + dgettext (domain, real->arg)); + } +} + +/* Helper functions for hol_entry_help. */ + +/* State used during the execution of hol_help. */ +struct hol_help_state +{ + /* PREV_ENTRY should contain the previous entry printed, or 0. */ + struct hol_entry *prev_entry; + + /* If an entry is in a different group from the previous one, and SEP_GROUPS + is true, then a blank line will be printed before any output. */ + int sep_groups; + + /* True if a duplicate option argument was suppressed (only ever set if + UPARAMS.dup_args is false). */ + int suppressed_dup_arg; +}; + +/* Some state used while printing a help entry (used to communicate with + helper functions). See the doc for hol_entry_help for more info, as most + of the fields are copied from its arguments. */ +struct pentry_state +{ + const struct hol_entry *entry; + argp_fmtstream_t stream; + struct hol_help_state *hhstate; + + /* True if nothing's been printed so far. */ + int first; + + /* If non-zero, the state that was used to print this help. */ + const struct argp_state *state; +}; + +/* If a user doc filter should be applied to DOC, do so. */ +static const char * +filter_doc (const char *doc, int key, const struct argp *argp, + const struct argp_state *state) +{ + if (argp->help_filter) + /* We must apply a user filter to this output. */ + { + void *input = __argp_input (argp, state); + return (*argp->help_filter) (key, doc, input); + } + else + /* No filter. */ + return doc; +} + +/* Prints STR as a header line, with the margin lines set appropiately, and + notes the fact that groups should be separated with a blank line. ARGP is + the argp that should dictate any user doc filtering to take place. Note + that the previous wrap margin isn't restored, but the left margin is reset + to 0. */ +static void +print_header (const char *str, const struct argp *argp, + struct pentry_state *pest) +{ + const char *tstr = dgettext (argp->argp_domain, str); + const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_HEADER, argp, pest->state); + + if (fstr) + { + if (*fstr) + { + if (pest->hhstate->prev_entry) + /* Precede with a blank line. */ + __argp_fmtstream_putc (pest->stream, '\n'); + indent_to (pest->stream, uparams.header_col); + __argp_fmtstream_set_lmargin (pest->stream, uparams.header_col); + __argp_fmtstream_set_wmargin (pest->stream, uparams.header_col); + __argp_fmtstream_puts (pest->stream, fstr); + __argp_fmtstream_set_lmargin (pest->stream, 0); + __argp_fmtstream_putc (pest->stream, '\n'); + } + + pest->hhstate->sep_groups = 1; /* Separate subsequent groups. */ + } + + if (fstr != tstr) + free ((char *) fstr); +} + +/* Inserts a comma if this isn't the first item on the line, and then makes + sure we're at least to column COL. If this *is* the first item on a line, + prints any pending whitespace/headers that should precede this line. Also + clears FIRST. */ +static void +comma (unsigned col, struct pentry_state *pest) +{ + if (pest->first) + { + const struct hol_entry *pe = pest->hhstate->prev_entry; + const struct hol_cluster *cl = pest->entry->cluster; + + if (pest->hhstate->sep_groups && pe && pest->entry->group != pe->group) + __argp_fmtstream_putc (pest->stream, '\n'); + + if (cl && cl->header && *cl->header + && (!pe + || (pe->cluster != cl + && !hol_cluster_is_child (pe->cluster, cl)))) + /* If we're changing clusters, then this must be the start of the + ENTRY's cluster unless that is an ancestor of the previous one + (in which case we had just popped into a sub-cluster for a bit). + If so, then print the cluster's header line. */ + { + int old_wm = __argp_fmtstream_wmargin (pest->stream); + print_header (cl->header, cl->argp, pest); + __argp_fmtstream_set_wmargin (pest->stream, old_wm); + } + + pest->first = 0; + } + else + __argp_fmtstream_puts (pest->stream, ", "); + + indent_to (pest->stream, col); +} + +/* Print help for ENTRY to STREAM. */ +static void +hol_entry_help (struct hol_entry *entry, const struct argp_state *state, + argp_fmtstream_t stream, struct hol_help_state *hhstate) +{ + unsigned num; + const struct argp_option *real = entry->opt, *opt; + char *so = entry->short_options; + int have_long_opt = 0; /* We have any long options. */ + /* Saved margins. */ + int old_lm = __argp_fmtstream_set_lmargin (stream, 0); + int old_wm = __argp_fmtstream_wmargin (stream); + /* PEST is a state block holding some of our variables that we'd like to + share with helper functions. */ + struct pentry_state pest; + + pest.entry = entry; + pest.stream = stream; + pest.hhstate = hhstate; + pest.first = 1; + pest.state = state; + + if (! odoc (real)) + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + { + have_long_opt = 1; + break; + } + + /* First emit short options. */ + __argp_fmtstream_set_wmargin (stream, uparams.short_opt_col); /* For truly bizarre cases. */ + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (oshort (opt) && opt->key == *so) + /* OPT has a valid (non shadowed) short option. */ + { + if (ovisible (opt)) + { + comma (uparams.short_opt_col, &pest); + __argp_fmtstream_putc (stream, '-'); + __argp_fmtstream_putc (stream, *so); + if (!have_long_opt || uparams.dup_args) + arg (real, " %s", "[%s]", state->root_argp->argp_domain, stream); + else if (real->arg) + hhstate->suppressed_dup_arg = 1; + } + so++; + } + + /* Now, long options. */ + if (odoc (real)) + /* A `documentation' option. */ + { + __argp_fmtstream_set_wmargin (stream, uparams.doc_opt_col); + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (opt->name && *opt->name && ovisible (opt)) + { + comma (uparams.doc_opt_col, &pest); + /* Calling dgettext here isn't quite right, since sorting will + have been done on the original; but documentation options + should be pretty rare anyway... */ + __argp_fmtstream_puts (stream, + onotrans (opt) ? + opt->name : + dgettext (state->root_argp->argp_domain, + opt->name)); + } + } + else + /* A real long option. */ + { + int first_long_opt = 1; + + __argp_fmtstream_set_wmargin (stream, uparams.long_opt_col); + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + { + comma (uparams.long_opt_col, &pest); + __argp_fmtstream_printf (stream, "--%s", opt->name); + if (first_long_opt || uparams.dup_args) + arg (real, "=%s", "[=%s]", state->root_argp->argp_domain, + stream); + else if (real->arg) + hhstate->suppressed_dup_arg = 1; + } + } + + /* Next, documentation strings. */ + __argp_fmtstream_set_lmargin (stream, 0); + + if (pest.first) + { + /* Didn't print any switches, what's up? */ + if (!oshort (real) && !real->name) + /* This is a group header, print it nicely. */ + print_header (real->doc, entry->argp, &pest); + else + /* Just a totally shadowed option or null header; print nothing. */ + goto cleanup; /* Just return, after cleaning up. */ + } + else + { + const char *tstr = real->doc ? dgettext (state->root_argp->argp_domain, + real->doc) : 0; + const char *fstr = filter_doc (tstr, real->key, entry->argp, state); + if (fstr && *fstr) + { + unsigned int col = __argp_fmtstream_point (stream); + + __argp_fmtstream_set_lmargin (stream, uparams.opt_doc_col); + __argp_fmtstream_set_wmargin (stream, uparams.opt_doc_col); + + if (col > (unsigned int) (uparams.opt_doc_col + 3)) + __argp_fmtstream_putc (stream, '\n'); + else if (col >= (unsigned int) uparams.opt_doc_col) + __argp_fmtstream_puts (stream, " "); + else + indent_to (stream, uparams.opt_doc_col); + + __argp_fmtstream_puts (stream, fstr); + } + if (fstr && fstr != tstr) + free ((char *) fstr); + + /* Reset the left margin. */ + __argp_fmtstream_set_lmargin (stream, 0); + __argp_fmtstream_putc (stream, '\n'); + } + + hhstate->prev_entry = entry; + +cleanup: + __argp_fmtstream_set_lmargin (stream, old_lm); + __argp_fmtstream_set_wmargin (stream, old_wm); +} + +/* Output a long help message about the options in HOL to STREAM. */ +static void +hol_help (struct hol *hol, const struct argp_state *state, + argp_fmtstream_t stream) +{ + unsigned num; + struct hol_entry *entry; + struct hol_help_state hhstate = { 0, 0, 0 }; + + for (entry = hol->entries, num = hol->num_entries; num > 0; entry++, num--) + hol_entry_help (entry, state, stream, &hhstate); + + if (hhstate.suppressed_dup_arg && uparams.dup_args_note) + { + const char *tstr = dgettext (state->root_argp->argp_domain, "\ +Mandatory or optional arguments to long options are also mandatory or \ +optional for any corresponding short options."); + const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_DUP_ARGS_NOTE, + state ? state->root_argp : 0, state); + if (fstr && *fstr) + { + __argp_fmtstream_putc (stream, '\n'); + __argp_fmtstream_puts (stream, fstr); + __argp_fmtstream_putc (stream, '\n'); + } + if (fstr && fstr != tstr) + free ((char *) fstr); + } +} + +/* Helper functions for hol_usage. */ + +/* If OPT is a short option without an arg, append its key to the string + pointer pointer to by COOKIE, and advance the pointer. */ +static int +add_argless_short_opt (const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie) +{ + char **snao_end = cookie; + if (!(opt->arg || real->arg) + && !((opt->flags | real->flags) & OPTION_NO_USAGE)) + *(*snao_end)++ = opt->key; + return 0; +} + +/* If OPT is a short option with an arg, output a usage entry for it to the + stream pointed at by COOKIE. */ +static int +usage_argful_short_opt (const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie) +{ + argp_fmtstream_t stream = cookie; + const char *arg = opt->arg; + int flags = opt->flags | real->flags; + + if (! arg) + arg = real->arg; + + if (arg && !(flags & OPTION_NO_USAGE)) + { + arg = dgettext (domain, arg); + + if (flags & OPTION_ARG_OPTIONAL) + __argp_fmtstream_printf (stream, " [-%c[%s]]", opt->key, arg); + else + { + /* Manually do line wrapping so that it (probably) won't + get wrapped at the embedded space. */ + space (stream, 6 + strlen (arg)); + __argp_fmtstream_printf (stream, "[-%c %s]", opt->key, arg); + } + } + + return 0; +} + +/* Output a usage entry for the long option opt to the stream pointed at by + COOKIE. */ +static int +usage_long_opt (const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie) +{ + argp_fmtstream_t stream = cookie; + const char *arg = opt->arg; + int flags = opt->flags | real->flags; + + if (! arg) + arg = real->arg; + + if (! (flags & OPTION_NO_USAGE) && !odoc (opt)) + { + if (arg) + { + arg = dgettext (domain, arg); + if (flags & OPTION_ARG_OPTIONAL) + __argp_fmtstream_printf (stream, " [--%s[=%s]]", opt->name, arg); + else + __argp_fmtstream_printf (stream, " [--%s=%s]", opt->name, arg); + } + else + __argp_fmtstream_printf (stream, " [--%s]", opt->name); + } + + return 0; +} + +/* Print a short usage description for the arguments in HOL to STREAM. */ +static void +hol_usage (struct hol *hol, argp_fmtstream_t stream) +{ + if (hol->num_entries > 0) + { + unsigned nentries; + struct hol_entry *entry; + char *short_no_arg_opts = alloca (strlen (hol->short_options) + 1); + char *snao_end = short_no_arg_opts; + + /* First we put a list of short options without arguments. */ + for (entry = hol->entries, nentries = hol->num_entries + ; nentries > 0 + ; entry++, nentries--) + hol_entry_short_iterate (entry, add_argless_short_opt, + entry->argp->argp_domain, &snao_end); + if (snao_end > short_no_arg_opts) + { + *snao_end++ = 0; + __argp_fmtstream_printf (stream, " [-%s]", short_no_arg_opts); + } + + /* Now a list of short options *with* arguments. */ + for (entry = hol->entries, nentries = hol->num_entries + ; nentries > 0 + ; entry++, nentries--) + hol_entry_short_iterate (entry, usage_argful_short_opt, + entry->argp->argp_domain, stream); + + /* Finally, a list of long options (whew!). */ + for (entry = hol->entries, nentries = hol->num_entries + ; nentries > 0 + ; entry++, nentries--) + hol_entry_long_iterate (entry, usage_long_opt, + entry->argp->argp_domain, stream); + } +} + +/* Make a HOL containing all levels of options in ARGP. CLUSTER is the + cluster in which ARGP's entries should be clustered, or 0. */ +static struct hol * +argp_hol (const struct argp *argp, struct hol_cluster *cluster) +{ + const struct argp_child *child = argp->children; + struct hol *hol = make_hol (argp, cluster); + if (child) + while (child->argp) + { + struct hol_cluster *child_cluster = + ((child->group || child->header) + /* Put CHILD->argp within its own cluster. */ + ? hol_add_cluster (hol, child->group, child->header, + child - argp->children, cluster, argp) + /* Just merge it into the parent's cluster. */ + : cluster); + hol_append (hol, argp_hol (child->argp, child_cluster)) ; + child++; + } + return hol; +} + +/* Calculate how many different levels with alternative args strings exist in + ARGP. */ +static size_t +argp_args_levels (const struct argp *argp) +{ + size_t levels = 0; + const struct argp_child *child = argp->children; + + if (argp->args_doc && strchr (argp->args_doc, '\n')) + levels++; + + if (child) + while (child->argp) + levels += argp_args_levels ((child++)->argp); + + return levels; +} + +/* Print all the non-option args documented in ARGP to STREAM. Any output is + preceded by a space. LEVELS is a pointer to a byte vector the length + returned by argp_args_levels; it should be initialized to zero, and + updated by this routine for the next call if ADVANCE is true. True is + returned as long as there are more patterns to output. */ +static int +argp_args_usage (const struct argp *argp, const struct argp_state *state, + char **levels, int advance, argp_fmtstream_t stream) +{ + char *our_level = *levels; + int multiple = 0; + const struct argp_child *child = argp->children; + const char *tdoc = dgettext (argp->argp_domain, argp->args_doc), *nl = 0; + const char *fdoc = filter_doc (tdoc, ARGP_KEY_HELP_ARGS_DOC, argp, state); + + if (fdoc) + { + const char *cp = fdoc; + nl = __strchrnul (cp, '\n'); + if (*nl != '\0') + /* This is a `multi-level' args doc; advance to the correct position + as determined by our state in LEVELS, and update LEVELS. */ + { + int i; + multiple = 1; + for (i = 0; i < *our_level; i++) + cp = nl + 1, nl = __strchrnul (cp, '\n'); + (*levels)++; + } + + /* Manually do line wrapping so that it (probably) won't get wrapped at + any embedded spaces. */ + space (stream, 1 + nl - cp); + + __argp_fmtstream_write (stream, cp, nl - cp); + } + if (fdoc && fdoc != tdoc) + free ((char *)fdoc); /* Free user's modified doc string. */ + + if (child) + while (child->argp) + advance = !argp_args_usage ((child++)->argp, state, levels, advance, stream); + + if (advance && multiple) + { + /* Need to increment our level. */ + if (*nl) + /* There's more we can do here. */ + { + (*our_level)++; + advance = 0; /* Our parent shouldn't advance also. */ + } + else if (*our_level > 0) + /* We had multiple levels, but used them up; reset to zero. */ + *our_level = 0; + } + + return !advance; +} + +/* Print the documentation for ARGP to STREAM; if POST is false, then + everything preceeding a `\v' character in the documentation strings (or + the whole string, for those with none) is printed, otherwise, everything + following the `\v' character (nothing for strings without). Each separate + bit of documentation is separated a blank line, and if PRE_BLANK is true, + then the first is as well. If FIRST_ONLY is true, only the first + occurrence is output. Returns true if anything was output. */ +static int +argp_doc (const struct argp *argp, const struct argp_state *state, + int post, int pre_blank, int first_only, + argp_fmtstream_t stream) +{ + const char *text; + const char *inp_text; + size_t inp_text_len = 0; + const char *trans_text; + void *input = 0; + int anything = 0; + const struct argp_child *child = argp->children; + + if (argp->doc) + { + char *vt = strchr (argp->doc, '\v'); + if (vt) + { + if (post) + inp_text = vt + 1; + else + { + inp_text_len = vt - argp->doc; + inp_text = __strndup (argp->doc, inp_text_len); + } + } + else + inp_text = post ? 0 : argp->doc; + trans_text = inp_text ? dgettext (argp->argp_domain, inp_text) : NULL; + } + else + trans_text = inp_text = 0; + + if (argp->help_filter) + /* We have to filter the doc strings. */ + { + input = __argp_input (argp, state); + text = + (*argp->help_filter) (post + ? ARGP_KEY_HELP_POST_DOC + : ARGP_KEY_HELP_PRE_DOC, + trans_text, input); + } + else + text = (const char *) trans_text; + + if (text) + { + if (pre_blank) + __argp_fmtstream_putc (stream, '\n'); + + __argp_fmtstream_puts (stream, text); + + if (__argp_fmtstream_point (stream) > __argp_fmtstream_lmargin (stream)) + __argp_fmtstream_putc (stream, '\n'); + + anything = 1; + } + + if (text && text != trans_text) + free ((char *) text); /* Free TEXT returned from the help filter. */ + + if (inp_text && inp_text_len) + free ((char *) inp_text); /* We copied INP_TEXT, so free it now. */ + + if (post && argp->help_filter) + /* Now see if we have to output a ARGP_KEY_HELP_EXTRA text. */ + { + text = (*argp->help_filter) (ARGP_KEY_HELP_EXTRA, 0, input); + if (text) + { + if (anything || pre_blank) + __argp_fmtstream_putc (stream, '\n'); + __argp_fmtstream_puts (stream, text); + free ((char *) text); + if (__argp_fmtstream_point (stream) + > __argp_fmtstream_lmargin (stream)) + __argp_fmtstream_putc (stream, '\n'); + anything = 1; + } + } + + if (child) + while (child->argp && !(first_only && anything)) + anything |= + argp_doc ((child++)->argp, state, + post, anything || pre_blank, first_only, + stream); + + return anything; +} + +/* Output a usage message for ARGP to STREAM. If called from + argp_state_help, STATE is the relevent parsing state. FLAGS are from the + set ARGP_HELP_*. NAME is what to use wherever a `program name' is + needed. */ +static void +_help (const struct argp *argp, const struct argp_state *state, FILE *stream, + unsigned flags, char *name) +{ + int anything = 0; /* Whether we've output anything. */ + struct hol *hol = 0; + argp_fmtstream_t fs; + + if (! stream) + return; + +#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) + __flockfile (stream); +#endif + + if (! uparams.valid) + fill_in_uparams (state); + + fs = __argp_make_fmtstream (stream, 0, uparams.rmargin, 0); + if (! fs) + { +#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) + __funlockfile (stream); +#endif + return; + } + + if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG)) + { + hol = argp_hol (argp, 0); + + /* If present, these options always come last. */ + hol_set_group (hol, "help", -1); + hol_set_group (hol, "version", -1); + + hol_sort (hol); + } + + if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE)) + /* Print a short `Usage:' message. */ + { + int first_pattern = 1, more_patterns; + size_t num_pattern_levels = argp_args_levels (argp); + char *pattern_levels = alloca (num_pattern_levels); + + memset (pattern_levels, 0, num_pattern_levels); + + do + { + int old_lm; + int old_wm = __argp_fmtstream_set_wmargin (fs, uparams.usage_indent); + char *levels = pattern_levels; + + if (first_pattern) + __argp_fmtstream_printf (fs, "%s %s", + dgettext (argp->argp_domain, "Usage:"), + name); + else + __argp_fmtstream_printf (fs, "%s %s", + dgettext (argp->argp_domain, " or: "), + name); + + /* We set the lmargin as well as the wmargin, because hol_usage + manually wraps options with newline to avoid annoying breaks. */ + old_lm = __argp_fmtstream_set_lmargin (fs, uparams.usage_indent); + + if (flags & ARGP_HELP_SHORT_USAGE) + /* Just show where the options go. */ + { + if (hol->num_entries > 0) + __argp_fmtstream_puts (fs, dgettext (argp->argp_domain, + " [OPTION...]")); + } + else + /* Actually print the options. */ + { + hol_usage (hol, fs); + flags |= ARGP_HELP_SHORT_USAGE; /* But only do so once. */ + } + + more_patterns = argp_args_usage (argp, state, &levels, 1, fs); + + __argp_fmtstream_set_wmargin (fs, old_wm); + __argp_fmtstream_set_lmargin (fs, old_lm); + + __argp_fmtstream_putc (fs, '\n'); + anything = 1; + + first_pattern = 0; + } + while (more_patterns); + } + + if (flags & ARGP_HELP_PRE_DOC) + anything |= argp_doc (argp, state, 0, 0, 1, fs); + + if (flags & ARGP_HELP_SEE) + { + __argp_fmtstream_printf (fs, dgettext (argp->argp_domain, "\ +Try `%s --help' or `%s --usage' for more information.\n"), + name, name); + anything = 1; + } + + if (flags & ARGP_HELP_LONG) + /* Print a long, detailed help message. */ + { + /* Print info about all the options. */ + if (hol->num_entries > 0) + { + if (anything) + __argp_fmtstream_putc (fs, '\n'); + hol_help (hol, state, fs); + anything = 1; + } + } + + if (flags & ARGP_HELP_POST_DOC) + /* Print any documentation strings at the end. */ + anything |= argp_doc (argp, state, 1, anything, 0, fs); + + if ((flags & ARGP_HELP_BUG_ADDR) && argp_program_bug_address) + { + if (anything) + __argp_fmtstream_putc (fs, '\n'); + __argp_fmtstream_printf (fs, dgettext (argp->argp_domain, + "Report bugs to %s.\n"), + argp_program_bug_address); + anything = 1; + } + +#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) + __funlockfile (stream); +#endif + + if (hol) + hol_free (hol); + + __argp_fmtstream_free (fs); +} + +/* Output a usage message for ARGP to STREAM. FLAGS are from the set + ARGP_HELP_*. NAME is what to use wherever a `program name' is needed. */ +void __argp_help (const struct argp *argp, FILE *stream, + unsigned flags, char *name) +{ + struct argp_state state; + memset (&state, 0, sizeof state); + state.root_argp = argp; + _help (argp, &state, stream, flags, name); +} +#ifdef weak_alias +weak_alias (__argp_help, argp_help) +#endif + +#if ! (defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME) +char * +__argp_short_program_name (void) +{ +# if HAVE_DECL_PROGRAM_INVOCATION_NAME + return __argp_base_name (program_invocation_name); +# else + /* FIXME: What now? Miles suggests that it is better to use NULL, + but currently the value is passed on directly to fputs_unlocked, + so that requires more changes. */ +# if __GNUC__ +# warning No reasonable value to return +# endif /* __GNUC__ */ + return ""; +# endif +} +#endif + +/* Output, if appropriate, a usage message for STATE to STREAM. FLAGS are + from the set ARGP_HELP_*. */ +void +__argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags) +{ + if ((!state || ! (state->flags & ARGP_NO_ERRS)) && stream) + { + if (state && (state->flags & ARGP_LONG_ONLY)) + flags |= ARGP_HELP_LONG_ONLY; + + _help (state ? state->root_argp : 0, state, stream, flags, + state ? state->name : __argp_short_program_name ()); + + if (!state || ! (state->flags & ARGP_NO_EXIT)) + { + if (flags & ARGP_HELP_EXIT_ERR) + exit (argp_err_exit_status); + if (flags & ARGP_HELP_EXIT_OK) + exit (0); + } + } +} +#ifdef weak_alias +weak_alias (__argp_state_help, argp_state_help) +#endif + +/* If appropriate, print the printf string FMT and following args, preceded + by the program name and `:', to stderr, and followed by a `Try ... --help' + message, then exit (1). */ +void +__argp_error (const struct argp_state *state, const char *fmt, ...) +{ + if (!state || !(state->flags & ARGP_NO_ERRS)) + { + FILE *stream = state ? state->err_stream : stderr; + + if (stream) + { + va_list ap; + +#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) + __flockfile (stream); +#endif + + va_start (ap, fmt); + +#ifdef USE_IN_LIBIO + if (_IO_fwide (stream, 0) > 0) + { + char *buf; + + if (__asprintf (&buf, fmt, ap) < 0) + buf = NULL; + + __fwprintf (stream, L"%s: %s\n", + state ? state->name : __argp_short_program_name (), + buf); + + free (buf); + } + else +#endif + { + fputs_unlocked (state + ? state->name : __argp_short_program_name (), + stream); + putc_unlocked (':', stream); + putc_unlocked (' ', stream); + + vfprintf (stream, fmt, ap); + + putc_unlocked ('\n', stream); + } + + __argp_state_help (state, stream, ARGP_HELP_STD_ERR); + + va_end (ap); + +#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) + __funlockfile (stream); +#endif + } + } +} +#ifdef weak_alias +weak_alias (__argp_error, argp_error) +#endif + +/* Similar to the standard gnu error-reporting function error(), but will + respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print + to STATE->err_stream. This is useful for argument parsing code that is + shared between program startup (when exiting is desired) and runtime + option parsing (when typically an error code is returned instead). The + difference between this function and argp_error is that the latter is for + *parsing errors*, and the former is for other problems that occur during + parsing but don't reflect a (syntactic) problem with the input. */ +void +__argp_failure (const struct argp_state *state, int status, int errnum, + const char *fmt, ...) +{ + if (!state || !(state->flags & ARGP_NO_ERRS)) + { + FILE *stream = state ? state->err_stream : stderr; + + if (stream) + { +#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) + __flockfile (stream); +#endif + +#ifdef USE_IN_LIBIO + if (_IO_fwide (stream, 0) > 0) + __fwprintf (stream, L"%s", + state ? state->name : __argp_short_program_name ()); + else +#endif + fputs_unlocked (state + ? state->name : __argp_short_program_name (), + stream); + + if (fmt) + { + va_list ap; + + va_start (ap, fmt); +#ifdef USE_IN_LIBIO + if (_IO_fwide (stream, 0) > 0) + { + char *buf; + + if (__asprintf (&buf, fmt, ap) < 0) + buf = NULL; + + __fwprintf (stream, L": %s", buf); + + free (buf); + } + else +#endif + { + putc_unlocked (':', stream); + putc_unlocked (' ', stream); + + vfprintf (stream, fmt, ap); + } + + va_end (ap); + } + + if (errnum) + { + char buf[200]; + +#ifdef USE_IN_LIBIO + if (_IO_fwide (stream, 0) > 0) + __fwprintf (stream, L": %s", + __strerror_r (errnum, buf, sizeof (buf))); + else +#endif + { + char const *s = NULL; + putc_unlocked (':', stream); + putc_unlocked (' ', stream); +#if _LIBC || (HAVE_DECL_STRERROR_R && STRERROR_R_CHAR_P) + s = __strerror_r (errnum, buf, sizeof buf); +#elif HAVE_DECL_STRERROR_R + if (__strerror_r (errnum, buf, sizeof buf) == 0) + s = buf; +#endif +#if !_LIBC + if (! s && ! (s = strerror (errnum))) + s = dgettext (state->root_argp->argp_domain, + "Unknown system error"); +#endif + fputs (s, stream); + } + } + +#ifdef USE_IN_LIBIO + if (_IO_fwide (stream, 0) > 0) + putwc_unlocked (L'\n', stream); + else +#endif + putc_unlocked ('\n', stream); + +#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) + __funlockfile (stream); +#endif + + if (status && (!state || !(state->flags & ARGP_NO_EXIT))) + exit (status); + } + } +} +#ifdef weak_alias +weak_alias (__argp_failure, argp_failure) +#endif diff --git a/gnulib/argp-namefrob.h b/gnulib/argp-namefrob.h new file mode 100644 index 000000000..24581a626 --- /dev/null +++ b/gnulib/argp-namefrob.h @@ -0,0 +1,157 @@ +/* Name frobnication for compiling argp outside of glibc + Copyright (C) 1997, 2003, 2007, 2009, 2010 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#if !_LIBC +/* This code is written for inclusion in gnu-libc, and uses names in the + namespace reserved for libc. If we're not compiling in libc, define those + names to be the normal ones instead. */ + +/* argp-parse functions */ +#undef __argp_parse +#define __argp_parse argp_parse +#undef __option_is_end +#define __option_is_end _option_is_end +#undef __option_is_short +#define __option_is_short _option_is_short +#undef __argp_input +#define __argp_input _argp_input + +/* argp-help functions */ +#undef __argp_help +#define __argp_help argp_help +#undef __argp_error +#define __argp_error argp_error +#undef __argp_failure +#define __argp_failure argp_failure +#undef __argp_state_help +#define __argp_state_help argp_state_help +#undef __argp_usage +#define __argp_usage argp_usage + +/* argp-fmtstream functions */ +#undef __argp_make_fmtstream +#define __argp_make_fmtstream argp_make_fmtstream +#undef __argp_fmtstream_free +#define __argp_fmtstream_free argp_fmtstream_free +#undef __argp_fmtstream_putc +#define __argp_fmtstream_putc argp_fmtstream_putc +#undef __argp_fmtstream_puts +#define __argp_fmtstream_puts argp_fmtstream_puts +#undef __argp_fmtstream_write +#define __argp_fmtstream_write argp_fmtstream_write +#undef __argp_fmtstream_printf +#define __argp_fmtstream_printf argp_fmtstream_printf +#undef __argp_fmtstream_set_lmargin +#define __argp_fmtstream_set_lmargin argp_fmtstream_set_lmargin +#undef __argp_fmtstream_set_rmargin +#define __argp_fmtstream_set_rmargin argp_fmtstream_set_rmargin +#undef __argp_fmtstream_set_wmargin +#define __argp_fmtstream_set_wmargin argp_fmtstream_set_wmargin +#undef __argp_fmtstream_point +#define __argp_fmtstream_point argp_fmtstream_point +#undef __argp_fmtstream_update +#define __argp_fmtstream_update _argp_fmtstream_update +#undef __argp_fmtstream_ensure +#define __argp_fmtstream_ensure _argp_fmtstream_ensure +#undef __argp_fmtstream_lmargin +#define __argp_fmtstream_lmargin argp_fmtstream_lmargin +#undef __argp_fmtstream_rmargin +#define __argp_fmtstream_rmargin argp_fmtstream_rmargin +#undef __argp_fmtstream_wmargin +#define __argp_fmtstream_wmargin argp_fmtstream_wmargin + +/* normal libc functions we call */ +#undef __flockfile +#define __flockfile flockfile +#undef __funlockfile +#define __funlockfile funlockfile +#undef __mempcpy +#define __mempcpy mempcpy +#undef __sleep +#define __sleep sleep +#undef __strcasecmp +#define __strcasecmp strcasecmp +#undef __strchrnul +#define __strchrnul strchrnul +#undef __strerror_r +#define __strerror_r strerror_r +#undef __strndup +#define __strndup strndup +#undef __vsnprintf +#define __vsnprintf vsnprintf + +#if defined(HAVE_DECL_CLEARERR_UNLOCKED) && !HAVE_DECL_CLEARERR_UNLOCKED +# define clearerr_unlocked(x) clearerr (x) +#endif +#if defined(HAVE_DECL_FEOF_UNLOCKED) && !HAVE_DECL_FEOF_UNLOCKED +# define feof_unlocked(x) feof (x) +# endif +#if defined(HAVE_DECL_FERROR_UNLOCKED) && !HAVE_DECL_FERROR_UNLOCKED +# define ferror_unlocked(x) ferror (x) +# endif +#if defined(HAVE_DECL_FFLUSH_UNLOCKED) && !HAVE_DECL_FFLUSH_UNLOCKED +# define fflush_unlocked(x) fflush (x) +# endif +#if defined(HAVE_DECL_FGETS_UNLOCKED) && !HAVE_DECL_FGETS_UNLOCKED +# define fgets_unlocked(x,y,z) fgets (x,y,z) +# endif +#if defined(HAVE_DECL_FPUTC_UNLOCKED) && !HAVE_DECL_FPUTC_UNLOCKED +# define fputc_unlocked(x,y) fputc (x,y) +# endif +#if defined(HAVE_DECL_FPUTS_UNLOCKED) && !HAVE_DECL_FPUTS_UNLOCKED +# define fputs_unlocked(x,y) fputs (x,y) +# endif +#if defined(HAVE_DECL_FREAD_UNLOCKED) && !HAVE_DECL_FREAD_UNLOCKED +# define fread_unlocked(w,x,y,z) fread (w,x,y,z) +# endif +#if defined(HAVE_DECL_FWRITE_UNLOCKED) && !HAVE_DECL_FWRITE_UNLOCKED +# define fwrite_unlocked(w,x,y,z) fwrite (w,x,y,z) +# endif +#if defined(HAVE_DECL_GETC_UNLOCKED) && !HAVE_DECL_GETC_UNLOCKED +# define getc_unlocked(x) getc (x) +# endif +#if defined(HAVE_DECL_GETCHAR_UNLOCKED) && !HAVE_DECL_GETCHAR_UNLOCKED +# define getchar_unlocked() getchar () +# endif +#if defined(HAVE_DECL_PUTC_UNLOCKED) && !HAVE_DECL_PUTC_UNLOCKED +# define putc_unlocked(x,y) putc (x,y) +# endif +#if defined(HAVE_DECL_PUTCHAR_UNLOCKED) && !HAVE_DECL_PUTCHAR_UNLOCKED +# define putchar_unlocked(x) putchar (x) +# endif + +#endif /* !_LIBC */ + +#ifndef __set_errno +#define __set_errno(e) (errno = (e)) +#endif + +#if defined GNULIB_ARGP_DISABLE_DIRNAME +# define __argp_base_name(arg) arg +#elif defined GNULIB_ARGP_EXTERN_BASENAME +extern char *__argp_base_name (const char *arg); +#else +# include "dirname.h" +# define __argp_base_name last_component +#endif + +#if defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME +# define __argp_short_program_name() (program_invocation_short_name) +#else +extern char *__argp_short_program_name (void); +#endif diff --git a/gnulib/argp-parse.c b/gnulib/argp-parse.c new file mode 100644 index 000000000..a1cbf884e --- /dev/null +++ b/gnulib/argp-parse.c @@ -0,0 +1,952 @@ +/* Hierarchial argument parsing, layered over getopt + Copyright (C) 1995-2000, 2002-2004, 2009-2010 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _LIBC +# include +# undef dgettext +# define dgettext(domain, msgid) \ + INTUSE(__dcgettext) (domain, msgid, LC_MESSAGES) +#else +# include "gettext.h" +#endif +#define N_(msgid) msgid + +#include "argp.h" +#include "argp-namefrob.h" + +#define alignof(type) offsetof (struct { char c; type x; }, x) +#define alignto(n, d) ((((n) + (d) - 1) / (d)) * (d)) + +/* Getopt return values. */ +#define KEY_END (-1) /* The end of the options. */ +#define KEY_ARG 1 /* A non-option argument. */ +#define KEY_ERR '?' /* An error parsing the options. */ + +/* The meta-argument used to prevent any further arguments being interpreted + as options. */ +#define QUOTE "--" + +/* The number of bits we steal in a long-option value for our own use. */ +#define GROUP_BITS CHAR_BIT + +/* The number of bits available for the user value. */ +#define USER_BITS ((sizeof ((struct option *)0)->val * CHAR_BIT) - GROUP_BITS) +#define USER_MASK ((1 << USER_BITS) - 1) + +/* EZ alias for ARGP_ERR_UNKNOWN. */ +#define EBADKEY ARGP_ERR_UNKNOWN + +/* Default options. */ + +/* When argp is given the --HANG switch, _ARGP_HANG is set and argp will sleep + for one second intervals, decrementing _ARGP_HANG until it's zero. Thus + you can force the program to continue by attaching a debugger and setting + it to 0 yourself. */ +static volatile int _argp_hang; + +#define OPT_PROGNAME -2 +#define OPT_USAGE -3 +#define OPT_HANG -4 + +static const struct argp_option argp_default_options[] = +{ + {"help", '?', 0, 0, N_("give this help list"), -1}, + {"usage", OPT_USAGE, 0, 0, N_("give a short usage message"), 0}, + {"program-name",OPT_PROGNAME,N_("NAME"), OPTION_HIDDEN, N_("set the program name"), 0}, + {"HANG", OPT_HANG, N_("SECS"), OPTION_ARG_OPTIONAL | OPTION_HIDDEN, + N_("hang for SECS seconds (default 3600)"), 0}, + {NULL, 0, 0, 0, NULL, 0} +}; + +static error_t +argp_default_parser (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case '?': + __argp_state_help (state, state->out_stream, ARGP_HELP_STD_HELP); + break; + case OPT_USAGE: + __argp_state_help (state, state->out_stream, + ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK); + break; + + case OPT_PROGNAME: /* Set the program name. */ +#if defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_NAME + program_invocation_name = arg; +#endif + /* [Note that some systems only have PROGRAM_INVOCATION_SHORT_NAME (aka + __PROGNAME), in which case, PROGRAM_INVOCATION_NAME is just defined + to be that, so we have to be a bit careful here.] */ + + /* Update what we use for messages. */ + state->name = __argp_base_name (arg); + +#if defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME + program_invocation_short_name = state->name; +#endif + + if ((state->flags & (ARGP_PARSE_ARGV0 | ARGP_NO_ERRS)) + == ARGP_PARSE_ARGV0) + /* Update what getopt uses too. */ + state->argv[0] = arg; + + break; + + case OPT_HANG: + _argp_hang = atoi (arg ? arg : "3600"); + while (_argp_hang-- > 0) + __sleep (1); + break; + + default: + return EBADKEY; + } + return 0; +} + +static const struct argp argp_default_argp = + {argp_default_options, &argp_default_parser, NULL, NULL, NULL, NULL, "libc"}; + + +static const struct argp_option argp_version_options[] = +{ + {"version", 'V', 0, 0, N_("print program version"), -1}, + {NULL, 0, 0, 0, NULL, 0} +}; + +static error_t +argp_version_parser (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'V': + if (argp_program_version_hook) + (*argp_program_version_hook) (state->out_stream, state); + else if (argp_program_version) + fprintf (state->out_stream, "%s\n", argp_program_version); + else + __argp_error (state, dgettext (state->root_argp->argp_domain, + "(PROGRAM ERROR) No version known!?")); + if (! (state->flags & ARGP_NO_EXIT)) + exit (0); + break; + default: + return EBADKEY; + } + return 0; +} + +static const struct argp argp_version_argp = + {argp_version_options, &argp_version_parser, NULL, NULL, NULL, NULL, "libc"}; + +/* Returns the offset into the getopt long options array LONG_OPTIONS of a + long option with called NAME, or -1 if none is found. Passing NULL as + NAME will return the number of options. */ +static int +find_long_option (struct option *long_options, const char *name) +{ + struct option *l = long_options; + while (l->name != NULL) + if (name != NULL && strcmp (l->name, name) == 0) + return l - long_options; + else + l++; + if (name == NULL) + return l - long_options; + else + return -1; +} + + +/* The state of a `group' during parsing. Each group corresponds to a + particular argp structure from the tree of such descending from the top + level argp passed to argp_parse. */ +struct group +{ + /* This group's parsing function. */ + argp_parser_t parser; + + /* Which argp this group is from. */ + const struct argp *argp; + + /* Points to the point in SHORT_OPTS corresponding to the end of the short + options for this group. We use it to determine from which group a + particular short options is from. */ + char *short_end; + + /* The number of non-option args sucessfully handled by this parser. */ + unsigned args_processed; + + /* This group's parser's parent's group. */ + struct group *parent; + unsigned parent_index; /* And the our position in the parent. */ + + /* These fields are swapped into and out of the state structure when + calling this group's parser. */ + void *input, **child_inputs; + void *hook; +}; + +/* Call GROUP's parser with KEY and ARG, swapping any group-specific info + from STATE before calling, and back into state afterwards. If GROUP has + no parser, EBADKEY is returned. */ +static error_t +group_parse (struct group *group, struct argp_state *state, int key, char *arg) +{ + if (group->parser) + { + error_t err; + state->hook = group->hook; + state->input = group->input; + state->child_inputs = group->child_inputs; + state->arg_num = group->args_processed; + err = (*group->parser)(key, arg, state); + group->hook = state->hook; + return err; + } + else + return EBADKEY; +} + +struct parser +{ + const struct argp *argp; + + /* SHORT_OPTS is the getopt short options string for the union of all the + groups of options. */ + char *short_opts; + /* LONG_OPTS is the array of getop long option structures for the union of + all the groups of options. */ + struct option *long_opts; + /* OPT_DATA is the getopt data used for the re-entrant getopt. */ + struct _getopt_data opt_data; + + /* States of the various parsing groups. */ + struct group *groups; + /* The end of the GROUPS array. */ + struct group *egroup; + /* An vector containing storage for the CHILD_INPUTS field in all groups. */ + void **child_inputs; + + /* True if we think using getopt is still useful; if false, then + remaining arguments are just passed verbatim with ARGP_KEY_ARG. This is + cleared whenever getopt returns KEY_END, but may be set again if the user + moves the next argument pointer backwards. */ + int try_getopt; + + /* State block supplied to parsing routines. */ + struct argp_state state; + + /* Memory used by this parser. */ + void *storage; +}; + +/* The next usable entries in the various parser tables being filled in by + convert_options. */ +struct parser_convert_state +{ + struct parser *parser; + char *short_end; + struct option *long_end; + void **child_inputs_end; +}; + +/* Converts all options in ARGP (which is put in GROUP) and ancestors + into getopt options stored in SHORT_OPTS and LONG_OPTS; SHORT_END and + CVT->LONG_END are the points at which new options are added. Returns the + next unused group entry. CVT holds state used during the conversion. */ +static struct group * +convert_options (const struct argp *argp, + struct group *parent, unsigned parent_index, + struct group *group, struct parser_convert_state *cvt) +{ + /* REAL is the most recent non-alias value of OPT. */ + const struct argp_option *real = argp->options; + const struct argp_child *children = argp->children; + + if (real || argp->parser) + { + const struct argp_option *opt; + + if (real) + for (opt = real; !__option_is_end (opt); opt++) + { + if (! (opt->flags & OPTION_ALIAS)) + /* OPT isn't an alias, so we can use values from it. */ + real = opt; + + if (! (real->flags & OPTION_DOC)) + /* A real option (not just documentation). */ + { + if (__option_is_short (opt)) + /* OPT can be used as a short option. */ + { + *cvt->short_end++ = opt->key; + if (real->arg) + { + *cvt->short_end++ = ':'; + if (real->flags & OPTION_ARG_OPTIONAL) + *cvt->short_end++ = ':'; + } + *cvt->short_end = '\0'; /* keep 0 terminated */ + } + + if (opt->name + && find_long_option (cvt->parser->long_opts, opt->name) < 0) + /* OPT can be used as a long option. */ + { + cvt->long_end->name = opt->name; + cvt->long_end->has_arg = + (real->arg + ? (real->flags & OPTION_ARG_OPTIONAL + ? optional_argument + : required_argument) + : no_argument); + cvt->long_end->flag = 0; + /* we add a disambiguating code to all the user's + values (which is removed before we actually call + the function to parse the value); this means that + the user loses use of the high 8 bits in all his + values (the sign of the lower bits is preserved + however)... */ + cvt->long_end->val = + ((opt->key ? opt->key : real->key) & USER_MASK) + + (((group - cvt->parser->groups) + 1) << USER_BITS); + + /* Keep the LONG_OPTS list terminated. */ + (++cvt->long_end)->name = NULL; + } + } + } + + group->parser = argp->parser; + group->argp = argp; + group->short_end = cvt->short_end; + group->args_processed = 0; + group->parent = parent; + group->parent_index = parent_index; + group->input = 0; + group->hook = 0; + group->child_inputs = 0; + + if (children) + /* Assign GROUP's CHILD_INPUTS field some space from + CVT->child_inputs_end.*/ + { + unsigned num_children = 0; + while (children[num_children].argp) + num_children++; + group->child_inputs = cvt->child_inputs_end; + cvt->child_inputs_end += num_children; + } + + parent = group++; + } + else + parent = 0; + + if (children) + { + unsigned index = 0; + while (children->argp) + group = + convert_options (children++->argp, parent, index++, group, cvt); + } + + return group; +} + +/* Find the merged set of getopt options, with keys appropiately prefixed. */ +static void +parser_convert (struct parser *parser, const struct argp *argp, int flags) +{ + struct parser_convert_state cvt; + + cvt.parser = parser; + cvt.short_end = parser->short_opts; + cvt.long_end = parser->long_opts; + cvt.child_inputs_end = parser->child_inputs; + + if (flags & ARGP_IN_ORDER) + *cvt.short_end++ = '-'; + else if (flags & ARGP_NO_ARGS) + *cvt.short_end++ = '+'; + *cvt.short_end = '\0'; + + cvt.long_end->name = NULL; + + parser->argp = argp; + + if (argp) + parser->egroup = convert_options (argp, 0, 0, parser->groups, &cvt); + else + parser->egroup = parser->groups; /* No parsers at all! */ +} + +/* Lengths of various parser fields which we will allocated. */ +struct parser_sizes +{ + size_t short_len; /* Getopt short options string. */ + size_t long_len; /* Getopt long options vector. */ + size_t num_groups; /* Group structures we allocate. */ + size_t num_child_inputs; /* Child input slots. */ +}; + +/* For ARGP, increments the NUM_GROUPS field in SZS by the total number of + argp structures descended from it, and the SHORT_LEN & LONG_LEN fields by + the maximum lengths of the resulting merged getopt short options string and + long-options array, respectively. */ +static void +calc_sizes (const struct argp *argp, struct parser_sizes *szs) +{ + const struct argp_child *child = argp->children; + const struct argp_option *opt = argp->options; + + if (opt || argp->parser) + { + szs->num_groups++; + if (opt) + { + int num_opts = 0; + while (!__option_is_end (opt++)) + num_opts++; + szs->short_len += num_opts * 3; /* opt + up to 2 `:'s */ + szs->long_len += num_opts; + } + } + + if (child) + while (child->argp) + { + calc_sizes ((child++)->argp, szs); + szs->num_child_inputs++; + } +} + +/* Initializes PARSER to parse ARGP in a manner described by FLAGS. */ +static error_t +parser_init (struct parser *parser, const struct argp *argp, + int argc, char **argv, int flags, void *input) +{ + error_t err = 0; + struct group *group; + struct parser_sizes szs; + struct _getopt_data opt_data = _GETOPT_DATA_INITIALIZER; + char *storage; + size_t glen, gsum; + size_t clen, csum; + size_t llen, lsum; + size_t slen, ssum; + + szs.short_len = (flags & ARGP_NO_ARGS) ? 0 : 1; + szs.long_len = 0; + szs.num_groups = 0; + szs.num_child_inputs = 0; + + if (argp) + calc_sizes (argp, &szs); + + /* Lengths of the various bits of storage used by PARSER. */ + glen = (szs.num_groups + 1) * sizeof (struct group); + clen = szs.num_child_inputs * sizeof (void *); + llen = (szs.long_len + 1) * sizeof (struct option); + slen = szs.short_len + 1; + + /* Sums of previous lengths, properly aligned. There's no need to + align gsum, since struct group is aligned at least as strictly as + void * (since it contains a void * member). And there's no need + to align lsum, since struct option is aligned at least as + strictly as char. */ + gsum = glen; + csum = alignto (gsum + clen, alignof (struct option)); + lsum = csum + llen; + ssum = lsum + slen; + + parser->storage = malloc (ssum); + if (! parser->storage) + return ENOMEM; + + storage = parser->storage; + parser->groups = parser->storage; + parser->child_inputs = (void **) (storage + gsum); + parser->long_opts = (struct option *) (storage + csum); + parser->short_opts = storage + lsum; + parser->opt_data = opt_data; + + memset (parser->child_inputs, 0, clen); + parser_convert (parser, argp, flags); + + memset (&parser->state, 0, sizeof (struct argp_state)); + parser->state.root_argp = parser->argp; + parser->state.argc = argc; + parser->state.argv = argv; + parser->state.flags = flags; + parser->state.err_stream = stderr; + parser->state.out_stream = stdout; + parser->state.next = 0; /* Tell getopt to initialize. */ + parser->state.pstate = parser; + + parser->try_getopt = 1; + + /* Call each parser for the first time, giving it a chance to propagate + values to child parsers. */ + if (parser->groups < parser->egroup) + parser->groups->input = input; + for (group = parser->groups; + group < parser->egroup && (!err || err == EBADKEY); + group++) + { + if (group->parent) + /* If a child parser, get the initial input value from the parent. */ + group->input = group->parent->child_inputs[group->parent_index]; + + if (!group->parser + && group->argp->children && group->argp->children->argp) + /* For the special case where no parsing function is supplied for an + argp, propagate its input to its first child, if any (this just + makes very simple wrapper argps more convenient). */ + group->child_inputs[0] = group->input; + + err = group_parse (group, &parser->state, ARGP_KEY_INIT, 0); + } + if (err == EBADKEY) + err = 0; /* Some parser didn't understand. */ + + if (err) + return err; + + if (parser->state.flags & ARGP_NO_ERRS) + { + parser->opt_data.opterr = 0; + if (parser->state.flags & ARGP_PARSE_ARGV0) + /* getopt always skips ARGV[0], so we have to fake it out. As long + as OPTERR is 0, then it shouldn't actually try to access it. */ + parser->state.argv--, parser->state.argc++; + } + else + parser->opt_data.opterr = 1; /* Print error messages. */ + + if (parser->state.argv == argv && argv[0]) + /* There's an argv[0]; use it for messages. */ + parser->state.name = __argp_base_name (argv[0]); + else + parser->state.name = __argp_short_program_name (); + + return 0; +} + +/* Free any storage consumed by PARSER (but not PARSER itself). */ +static error_t +parser_finalize (struct parser *parser, + error_t err, int arg_ebadkey, int *end_index) +{ + struct group *group; + + if (err == EBADKEY && arg_ebadkey) + /* Suppress errors generated by unparsed arguments. */ + err = 0; + + if (! err) + { + if (parser->state.next == parser->state.argc) + /* We successfully parsed all arguments! Call all the parsers again, + just a few more times... */ + { + for (group = parser->groups; + group < parser->egroup && (!err || err==EBADKEY); + group++) + if (group->args_processed == 0) + err = group_parse (group, &parser->state, ARGP_KEY_NO_ARGS, 0); + for (group = parser->egroup - 1; + group >= parser->groups && (!err || err==EBADKEY); + group--) + err = group_parse (group, &parser->state, ARGP_KEY_END, 0); + + if (err == EBADKEY) + err = 0; /* Some parser didn't understand. */ + + /* Tell the user that all arguments are parsed. */ + if (end_index) + *end_index = parser->state.next; + } + else if (end_index) + /* Return any remaining arguments to the user. */ + *end_index = parser->state.next; + else + /* No way to return the remaining arguments, they must be bogus. */ + { + if (!(parser->state.flags & ARGP_NO_ERRS) + && parser->state.err_stream) + fprintf (parser->state.err_stream, + dgettext (parser->argp->argp_domain, + "%s: Too many arguments\n"), + parser->state.name); + err = EBADKEY; + } + } + + /* Okay, we're all done, with either an error or success; call the parsers + to indicate which one. */ + + if (err) + { + /* Maybe print an error message. */ + if (err == EBADKEY) + /* An appropriate message describing what the error was should have + been printed earlier. */ + __argp_state_help (&parser->state, parser->state.err_stream, + ARGP_HELP_STD_ERR); + + /* Since we didn't exit, give each parser an error indication. */ + for (group = parser->groups; group < parser->egroup; group++) + group_parse (group, &parser->state, ARGP_KEY_ERROR, 0); + } + else + /* Notify parsers of success, and propagate back values from parsers. */ + { + /* We pass over the groups in reverse order so that child groups are + given a chance to do there processing before passing back a value to + the parent. */ + for (group = parser->egroup - 1 + ; group >= parser->groups && (!err || err == EBADKEY) + ; group--) + err = group_parse (group, &parser->state, ARGP_KEY_SUCCESS, 0); + if (err == EBADKEY) + err = 0; /* Some parser didn't understand. */ + } + + /* Call parsers once more, to do any final cleanup. Errors are ignored. */ + for (group = parser->egroup - 1; group >= parser->groups; group--) + group_parse (group, &parser->state, ARGP_KEY_FINI, 0); + + if (err == EBADKEY) + err = EINVAL; + + free (parser->storage); + + return err; +} + +/* Call the user parsers to parse the non-option argument VAL, at the current + position, returning any error. The state NEXT pointer is assumed to have + been adjusted (by getopt) to point after this argument; this function will + adjust it correctly to reflect however many args actually end up being + consumed. */ +static error_t +parser_parse_arg (struct parser *parser, char *val) +{ + /* Save the starting value of NEXT, first adjusting it so that the arg + we're parsing is again the front of the arg vector. */ + int index = --parser->state.next; + error_t err = EBADKEY; + struct group *group; + int key = 0; /* Which of ARGP_KEY_ARG[S] we used. */ + + /* Try to parse the argument in each parser. */ + for (group = parser->groups + ; group < parser->egroup && err == EBADKEY + ; group++) + { + parser->state.next++; /* For ARGP_KEY_ARG, consume the arg. */ + key = ARGP_KEY_ARG; + err = group_parse (group, &parser->state, key, val); + + if (err == EBADKEY) + /* This parser doesn't like ARGP_KEY_ARG; try ARGP_KEY_ARGS instead. */ + { + parser->state.next--; /* For ARGP_KEY_ARGS, put back the arg. */ + key = ARGP_KEY_ARGS; + err = group_parse (group, &parser->state, key, 0); + } + } + + if (! err) + { + if (key == ARGP_KEY_ARGS) + /* The default for ARGP_KEY_ARGS is to assume that if NEXT isn't + changed by the user, *all* arguments should be considered + consumed. */ + parser->state.next = parser->state.argc; + + if (parser->state.next > index) + /* Remember that we successfully processed a non-option + argument -- but only if the user hasn't gotten tricky and set + the clock back. */ + (--group)->args_processed += (parser->state.next - index); + else + /* The user wants to reparse some args, give getopt another try. */ + parser->try_getopt = 1; + } + + return err; +} + +/* Call the user parsers to parse the option OPT, with argument VAL, at the + current position, returning any error. */ +static error_t +parser_parse_opt (struct parser *parser, int opt, char *val) +{ + /* The group key encoded in the high bits; 0 for short opts or + group_number + 1 for long opts. */ + int group_key = opt >> USER_BITS; + error_t err = EBADKEY; + + if (group_key == 0) + /* A short option. By comparing OPT's position in SHORT_OPTS to the + various starting positions in each group's SHORT_END field, we can + determine which group OPT came from. */ + { + struct group *group; + char *short_index = strchr (parser->short_opts, opt); + + if (short_index) + for (group = parser->groups; group < parser->egroup; group++) + if (group->short_end > short_index) + { + err = group_parse (group, &parser->state, opt, + parser->opt_data.optarg); + break; + } + } + else + /* A long option. We use shifts instead of masking for extracting + the user value in order to preserve the sign. */ + err = + group_parse (&parser->groups[group_key - 1], &parser->state, + (opt << GROUP_BITS) >> GROUP_BITS, + parser->opt_data.optarg); + + if (err == EBADKEY) + /* At least currently, an option not recognized is an error in the + parser, because we pre-compute which parser is supposed to deal + with each option. */ + { + static const char bad_key_err[] = + N_("(PROGRAM ERROR) Option should have been recognized!?"); + if (group_key == 0) + __argp_error (&parser->state, "-%c: %s", opt, + dgettext (parser->argp->argp_domain, bad_key_err)); + else + { + struct option *long_opt = parser->long_opts; + while (long_opt->val != opt && long_opt->name) + long_opt++; + __argp_error (&parser->state, "--%s: %s", + long_opt->name ? long_opt->name : "???", + dgettext (parser->argp->argp_domain, bad_key_err)); + } + } + + return err; +} + +/* Parse the next argument in PARSER (as indicated by PARSER->state.next). + Any error from the parsers is returned, and *ARGP_EBADKEY indicates + whether a value of EBADKEY is due to an unrecognized argument (which is + generally not fatal). */ +static error_t +parser_parse_next (struct parser *parser, int *arg_ebadkey) +{ + int opt; + error_t err = 0; + + if (parser->state.quoted && parser->state.next < parser->state.quoted) + /* The next argument pointer has been moved to before the quoted + region, so pretend we never saw the quoting `--', and give getopt + another chance. If the user hasn't removed it, getopt will just + process it again. */ + parser->state.quoted = 0; + + if (parser->try_getopt && !parser->state.quoted) + /* Give getopt a chance to parse this. */ + { + /* Put it back in OPTIND for getopt. */ + parser->opt_data.optind = parser->state.next; + /* Distinguish KEY_ERR from a real option. */ + parser->opt_data.optopt = KEY_END; + if (parser->state.flags & ARGP_LONG_ONLY) + opt = _getopt_long_only_r (parser->state.argc, parser->state.argv, + parser->short_opts, parser->long_opts, 0, + &parser->opt_data); + else + opt = _getopt_long_r (parser->state.argc, parser->state.argv, + parser->short_opts, parser->long_opts, 0, + &parser->opt_data); + /* And see what getopt did. */ + parser->state.next = parser->opt_data.optind; + + if (opt == KEY_END) + /* Getopt says there are no more options, so stop using + getopt; we'll continue if necessary on our own. */ + { + parser->try_getopt = 0; + if (parser->state.next > 1 + && strcmp (parser->state.argv[parser->state.next - 1], QUOTE) + == 0) + /* Not only is this the end of the options, but it's a + `quoted' region, which may have args that *look* like + options, so we definitely shouldn't try to use getopt past + here, whatever happens. */ + parser->state.quoted = parser->state.next; + } + else if (opt == KEY_ERR && parser->opt_data.optopt != KEY_END) + /* KEY_ERR can have the same value as a valid user short + option, but in the case of a real error, getopt sets OPTOPT + to the offending character, which can never be KEY_END. */ + { + *arg_ebadkey = 0; + return EBADKEY; + } + } + else + opt = KEY_END; + + if (opt == KEY_END) + { + /* We're past what getopt considers the options. */ + if (parser->state.next >= parser->state.argc + || (parser->state.flags & ARGP_NO_ARGS)) + /* Indicate that we're done. */ + { + *arg_ebadkey = 1; + return EBADKEY; + } + else + /* A non-option arg; simulate what getopt might have done. */ + { + opt = KEY_ARG; + parser->opt_data.optarg = parser->state.argv[parser->state.next++]; + } + } + + if (opt == KEY_ARG) + /* A non-option argument; try each parser in turn. */ + err = parser_parse_arg (parser, parser->opt_data.optarg); + else + err = parser_parse_opt (parser, opt, parser->opt_data.optarg); + + if (err == EBADKEY) + *arg_ebadkey = (opt == KEY_END || opt == KEY_ARG); + + return err; +} + +/* Parse the options strings in ARGC & ARGV according to the argp in ARGP. + FLAGS is one of the ARGP_ flags above. If END_INDEX is non-NULL, the + index in ARGV of the first unparsed option is returned in it. If an + unknown option is present, EINVAL is returned; if some parser routine + returned a non-zero value, it is returned; otherwise 0 is returned. */ +error_t +__argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags, + int *end_index, void *input) +{ + error_t err; + struct parser parser; + + /* If true, then err == EBADKEY is a result of a non-option argument failing + to be parsed (which in some cases isn't actually an error). */ + int arg_ebadkey = 0; + +#ifndef _LIBC + if (!(flags & ARGP_PARSE_ARGV0)) + { +#ifdef HAVE_DECL_PROGRAM_INVOCATION_NAME + if (!program_invocation_name) + program_invocation_name = argv[0]; +#endif +#ifdef HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME + if (!program_invocation_short_name) + program_invocation_short_name = __argp_base_name (argv[0]); +#endif + } +#endif + + if (! (flags & ARGP_NO_HELP)) + /* Add our own options. */ + { + struct argp_child *child = alloca (4 * sizeof (struct argp_child)); + struct argp *top_argp = alloca (sizeof (struct argp)); + + /* TOP_ARGP has no options, it just serves to group the user & default + argps. */ + memset (top_argp, 0, sizeof (*top_argp)); + top_argp->children = child; + + memset (child, 0, 4 * sizeof (struct argp_child)); + + if (argp) + (child++)->argp = argp; + (child++)->argp = &argp_default_argp; + if (argp_program_version || argp_program_version_hook) + (child++)->argp = &argp_version_argp; + child->argp = 0; + + argp = top_argp; + } + + /* Construct a parser for these arguments. */ + err = parser_init (&parser, argp, argc, argv, flags, input); + + if (! err) + /* Parse! */ + { + while (! err) + err = parser_parse_next (&parser, &arg_ebadkey); + err = parser_finalize (&parser, err, arg_ebadkey, end_index); + } + + return err; +} +#ifdef weak_alias +weak_alias (__argp_parse, argp_parse) +#endif + +/* Return the input field for ARGP in the parser corresponding to STATE; used + by the help routines. */ +void * +__argp_input (const struct argp *argp, const struct argp_state *state) +{ + if (state) + { + struct group *group; + struct parser *parser = state->pstate; + + for (group = parser->groups; group < parser->egroup; group++) + if (group->argp == argp) + return group->input; + } + + return 0; +} +#ifdef weak_alias +weak_alias (__argp_input, _argp_input) +#endif diff --git a/gnulib/argp-pin.c b/gnulib/argp-pin.c new file mode 100644 index 000000000..eda4d958e --- /dev/null +++ b/gnulib/argp-pin.c @@ -0,0 +1,27 @@ +/* Full and short program names for argp module + Copyright (C) 2005, 2009, 2010 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifndef HAVE_PROGRAM_INVOCATION_SHORT_NAME +char *program_invocation_short_name = 0; +#endif +#ifndef HAVE_PROGRAM_INVOCATION_NAME +char *program_invocation_name = 0; +#endif + diff --git a/gnulib/argp-pv.c b/gnulib/argp-pv.c new file mode 100644 index 000000000..e3227d322 --- /dev/null +++ b/gnulib/argp-pv.c @@ -0,0 +1,34 @@ +/* Default definition for ARGP_PROGRAM_VERSION. + Copyright (C) 1996, 1997, 1999, 2006, 2009, 2010 Free Software Foundation, + Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* If set by the user program to a non-zero value, then a default option + --version is added (unless the ARGP_NO_HELP flag is used), which will + print this string followed by a newline and exit (unless the + ARGP_NO_EXIT flag is used). Overridden by ARGP_PROGRAM_VERSION_HOOK. */ +const char *argp_program_version +/* This variable should be zero-initialized. On most systems, putting it into + BSS is sufficient. Not so on MacOS X 10.3 and 10.4, see + + . */ +#if defined __ELF__ + /* On ELF systems, variables in BSS behave well. */ +#else + = (const char *) 0 +#endif + ; diff --git a/gnulib/argp-pvh.c b/gnulib/argp-pvh.c new file mode 100644 index 000000000..fb98fc21c --- /dev/null +++ b/gnulib/argp-pvh.c @@ -0,0 +1,31 @@ +/* Default definition for ARGP_PROGRAM_VERSION_HOOK. + Copyright (C) 1996, 1997, 1999, 2004, 2009, 2010 Free Software Foundation, + Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "argp.h" + +/* If set by the user program to a non-zero value, then a default option + --version is added (unless the ARGP_NO_HELP flag is used), which calls + this function with a stream to print the version to and a pointer to the + current parsing state, and then exits (unless the ARGP_NO_EXIT flag is + used). This variable takes precedent over ARGP_PROGRAM_VERSION. */ +void (*argp_program_version_hook) (FILE *stream, struct argp_state *state) = NULL; diff --git a/gnulib/argp-version-etc.c b/gnulib/argp-version-etc.c new file mode 100644 index 000000000..f500a8f56 --- /dev/null +++ b/gnulib/argp-version-etc.c @@ -0,0 +1,38 @@ +/* Version hook for Argp. + Copyright (C) 2009, 2010 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include +#include +#include +#include + +static const char *program_canonical_name; +static const char * const *program_authors; + +static void +version_etc_hook (FILE *stream, struct argp_state *state) +{ + version_etc_ar (stream, program_canonical_name, PACKAGE_NAME, VERSION, + program_authors); +} + +void +argp_version_setup (const char *name, const char * const *authors) +{ + argp_program_version_hook = version_etc_hook; + program_canonical_name = name; + program_authors = authors; +} diff --git a/gnulib/argp-version-etc.h b/gnulib/argp-version-etc.h new file mode 100644 index 000000000..7c12c0181 --- /dev/null +++ b/gnulib/argp-version-etc.h @@ -0,0 +1,40 @@ +/* Version hook for Argp. + Copyright (C) 2009, 2010 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _ARGP_VERSION_ETC_H +#define _ARGP_VERSION_ETC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Setup standard display of the version information for the `--version' + option. NAME is the canonical program name, and AUTHORS is a NULL- + terminated array of author names. At least one author name must be + given. + + If NAME is NULL, the package name (as given by the PACKAGE macro) + is asumed to be the name of the program. + + This function is intended to be called before argp_parse(). +*/ +extern void argp_version_setup (const char *name, const char * const *authors); + +#ifdef __cplusplus +} +#endif + +#endif /* _ARGP_VERSION_ETC_H */ diff --git a/gnulib/argp-xinl.c b/gnulib/argp-xinl.c new file mode 100644 index 000000000..6e7e20bba --- /dev/null +++ b/gnulib/argp-xinl.c @@ -0,0 +1,42 @@ +/* Real definitions for extern inline functions in argp.h + Copyright (C) 1997, 1998, 2004, 2009, 2010 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if defined _LIBC || defined HAVE_FEATURES_H +# include +#endif + +#ifndef __USE_EXTERN_INLINES +# define __USE_EXTERN_INLINES 1 +#endif +#define ARGP_EI +#undef __OPTIMIZE__ +#define __OPTIMIZE__ 1 +#include "argp.h" + +/* Add weak aliases. */ +#if _LIBC - 0 && defined (weak_alias) + +weak_alias (__argp_usage, argp_usage) +weak_alias (__option_is_short, _option_is_short) +weak_alias (__option_is_end, _option_is_end) + +#endif diff --git a/gnulib/argp.h b/gnulib/argp.h new file mode 100644 index 000000000..3667224a9 --- /dev/null +++ b/gnulib/argp.h @@ -0,0 +1,645 @@ +/* Hierarchial argument parsing, layered over getopt. + Copyright (C) 1995-1999, 2003-2010 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _ARGP_H +#define _ARGP_H + +#include +#include +#include +#include + +#define __need_error_t +#include + +#ifndef __THROW +# define __THROW +#endif +#ifndef __NTH +# define __NTH(fct) fct __THROW +#endif + +#ifndef __attribute__ +/* The __attribute__ feature is available in gcc versions 2.5 and later. + The __-protected variants of the attributes 'format' and 'printf' are + accepted by gcc versions 2.6.4 (effectively 2.7) and later. + We enable __attribute__ only if these are supported too, because + gnulib and libintl do '#define printf __printf__' when they override + the 'printf' function. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __attribute__(Spec) /* empty */ +# endif +#endif + +/* GCC 2.95 and later have "__restrict"; C99 compilers have + "restrict", and "configure" may have defined "restrict". + Other compilers use __restrict, __restrict__, and _Restrict, and + 'configure' might #define 'restrict' to those words. */ +#ifndef __restrict +# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__)) +# if 199901L <= __STDC_VERSION__ +# define __restrict restrict +# else +# define __restrict +# endif +# endif +#endif + +#ifndef __error_t_defined +typedef int error_t; +# define __error_t_defined +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* A description of a particular option. A pointer to an array of + these is passed in the OPTIONS field of an argp structure. Each option + entry can correspond to one long option and/or one short option; more + names for the same option can be added by following an entry in an option + array with options having the OPTION_ALIAS flag set. */ +struct argp_option +{ + /* The long option name. For more than one name for the same option, you + can use following options with the OPTION_ALIAS flag set. */ + const char *name; + + /* What key is returned for this option. If > 0 and printable, then it's + also accepted as a short option. */ + int key; + + /* If non-NULL, this is the name of the argument associated with this + option, which is required unless the OPTION_ARG_OPTIONAL flag is set. */ + const char *arg; + + /* OPTION_ flags. */ + int flags; + + /* The doc string for this option. If both NAME and KEY are 0, This string + will be printed outdented from the normal option column, making it + useful as a group header (it will be the first thing printed in its + group); in this usage, it's conventional to end the string with a `:'. + + Write the initial value as N_("TEXT") if you want xgettext to collect + it into a POT file. */ + const char *doc; + + /* The group this option is in. In a long help message, options are sorted + alphabetically within each group, and the groups presented in the order + 0, 1, 2, ..., n, -m, ..., -2, -1. Every entry in an options array with + if this field 0 will inherit the group number of the previous entry, or + zero if it's the first one, unless its a group header (NAME and KEY both + 0), in which case, the previous entry + 1 is the default. Automagic + options such as --help are put into group -1. */ + int group; +}; + +/* The argument associated with this option is optional. */ +#define OPTION_ARG_OPTIONAL 0x1 + +/* This option isn't displayed in any help messages. */ +#define OPTION_HIDDEN 0x2 + +/* This option is an alias for the closest previous non-alias option. This + means that it will be displayed in the same help entry, and will inherit + fields other than NAME and KEY from the aliased option. */ +#define OPTION_ALIAS 0x4 + +/* This option isn't actually an option (and so should be ignored by the + actual option parser), but rather an arbitrary piece of documentation that + should be displayed in much the same manner as the options. If this flag + is set, then the option NAME field is displayed unmodified (e.g., no `--' + prefix is added) at the left-margin (where a *short* option would normally + be displayed), and the documentation string in the normal place. The NAME + field will be translated using gettext, unless OPTION_NO_TRANS is set (see + below). For purposes of sorting, any leading whitespace and punctuation is + ignored, except that if the first non-whitespace character is not `-', this + entry is displayed after all options (and OPTION_DOC entries with a leading + `-') in the same group. */ +#define OPTION_DOC 0x8 + +/* This option shouldn't be included in `long' usage messages (but is still + included in help messages). This is mainly intended for options that are + completely documented in an argp's ARGS_DOC field, in which case including + the option in the generic usage list would be redundant. For instance, + if ARGS_DOC is "FOO BAR\n-x BLAH", and the `-x' option's purpose is to + distinguish these two cases, -x should probably be marked + OPTION_NO_USAGE. */ +#define OPTION_NO_USAGE 0x10 + +/* Valid only in conjunction with OPTION_DOC. This option disables translation + of option name. */ +#define OPTION_NO_TRANS 0x20 + + +struct argp; /* fwd declare this type */ +struct argp_state; /* " */ +struct argp_child; /* " */ + +/* The type of a pointer to an argp parsing function. */ +typedef error_t (*argp_parser_t) (int key, char *arg, + struct argp_state *state); + +/* What to return for unrecognized keys. For special ARGP_KEY_ keys, such + returns will simply be ignored. For user keys, this error will be turned + into EINVAL (if the call to argp_parse is such that errors are propagated + back to the user instead of exiting); returning EINVAL itself would result + in an immediate stop to parsing in *all* cases. */ +#define ARGP_ERR_UNKNOWN E2BIG /* Hurd should never need E2BIG. XXX */ + +/* Special values for the KEY argument to an argument parsing function. + ARGP_ERR_UNKNOWN should be returned if they aren't understood. + + The sequence of keys to a parsing function is either (where each + uppercased word should be prefixed by `ARGP_KEY_' and opt is a user key): + + INIT opt... NO_ARGS END SUCCESS -- No non-option arguments at all + or INIT (opt | ARG)... END SUCCESS -- All non-option args parsed + or INIT (opt | ARG)... SUCCESS -- Some non-option arg unrecognized + + The third case is where every parser returned ARGP_KEY_UNKNOWN for an + argument, in which case parsing stops at that argument (returning the + unparsed arguments to the caller of argp_parse if requested, or stopping + with an error message if not). + + If an error occurs (either detected by argp, or because the parsing + function returned an error value), then the parser is called with + ARGP_KEY_ERROR, and no further calls are made. */ + +/* This is not an option at all, but rather a command line argument. If a + parser receiving this key returns success, the fact is recorded, and the + ARGP_KEY_NO_ARGS case won't be used. HOWEVER, if while processing the + argument, a parser function decrements the NEXT field of the state it's + passed, the option won't be considered processed; this is to allow you to + actually modify the argument (perhaps into an option), and have it + processed again. */ +#define ARGP_KEY_ARG 0 +/* There are remaining arguments not parsed by any parser, which may be found + starting at (STATE->argv + STATE->next). If success is returned, but + STATE->next left untouched, it's assumed that all arguments were consume, + otherwise, the parser should adjust STATE->next to reflect any arguments + consumed. */ +#define ARGP_KEY_ARGS 0x1000006 +/* There are no more command line arguments at all. */ +#define ARGP_KEY_END 0x1000001 +/* Because it's common to want to do some special processing if there aren't + any non-option args, user parsers are called with this key if they didn't + successfully process any non-option arguments. Called just before + ARGP_KEY_END (where more general validity checks on previously parsed + arguments can take place). */ +#define ARGP_KEY_NO_ARGS 0x1000002 +/* Passed in before any parsing is done. Afterwards, the values of each + element of the CHILD_INPUT field, if any, in the state structure is + copied to each child's state to be the initial value of the INPUT field. */ +#define ARGP_KEY_INIT 0x1000003 +/* Use after all other keys, including SUCCESS & END. */ +#define ARGP_KEY_FINI 0x1000007 +/* Passed in when parsing has successfully been completed (even if there are + still arguments remaining). */ +#define ARGP_KEY_SUCCESS 0x1000004 +/* Passed in if an error occurs. */ +#define ARGP_KEY_ERROR 0x1000005 + +/* An argp structure contains a set of options declarations, a function to + deal with parsing one, documentation string, a possible vector of child + argp's, and perhaps a function to filter help output. When actually + parsing options, getopt is called with the union of all the argp + structures chained together through their CHILD pointers, with conflicts + being resolved in favor of the first occurrence in the chain. */ +struct argp +{ + /* An array of argp_option structures, terminated by an entry with both + NAME and KEY having a value of 0. */ + const struct argp_option *options; + + /* What to do with an option from this structure. KEY is the key + associated with the option, and ARG is any associated argument (NULL if + none was supplied). If KEY isn't understood, ARGP_ERR_UNKNOWN should be + returned. If a non-zero, non-ARGP_ERR_UNKNOWN value is returned, then + parsing is stopped immediately, and that value is returned from + argp_parse(). For special (non-user-supplied) values of KEY, see the + ARGP_KEY_ definitions below. */ + argp_parser_t parser; + + /* A string describing what other arguments are wanted by this program. It + is only used by argp_usage to print the `Usage:' message. If it + contains newlines, the strings separated by them are considered + alternative usage patterns, and printed on separate lines (lines after + the first are prefix by ` or: ' instead of `Usage:'). */ + const char *args_doc; + + /* If non-NULL, a string containing extra text to be printed before and + after the options in a long help message (separated by a vertical tab + `\v' character). + Write the initial value as N_("BEFORE-TEXT") "\v" N_("AFTER-TEXT") if + you want xgettext to collect the two pieces of text into a POT file. */ + const char *doc; + + /* A vector of argp_children structures, terminated by a member with a 0 + argp field, pointing to child argps should be parsed with this one. Any + conflicts are resolved in favor of this argp, or early argps in the + CHILDREN list. This field is useful if you use libraries that supply + their own argp structure, which you want to use in conjunction with your + own. */ + const struct argp_child *children; + + /* If non-zero, this should be a function to filter the output of help + messages. KEY is either a key from an option, in which case TEXT is + that option's help text, or a special key from the ARGP_KEY_HELP_ + defines, below, describing which other help text TEXT is. The function + should return either TEXT, if it should be used as-is, a replacement + string, which should be malloced, and will be freed by argp, or NULL, + meaning `print nothing'. The value for TEXT is *after* any translation + has been done, so if any of the replacement text also needs translation, + that should be done by the filter function. INPUT is either the input + supplied to argp_parse, or NULL, if argp_help was called directly. */ + char *(*help_filter) (int __key, const char *__text, void *__input); + + /* If non-zero the strings used in the argp library are translated using + the domain described by this string. Otherwise the currently installed + default domain is used. */ + const char *argp_domain; +}; + +/* Possible KEY arguments to a help filter function. */ +#define ARGP_KEY_HELP_PRE_DOC 0x2000001 /* Help text preceeding options. */ +#define ARGP_KEY_HELP_POST_DOC 0x2000002 /* Help text following options. */ +#define ARGP_KEY_HELP_HEADER 0x2000003 /* Option header string. */ +#define ARGP_KEY_HELP_EXTRA 0x2000004 /* After all other documentation; + TEXT is NULL for this key. */ +/* Explanatory note emitted when duplicate option arguments have been + suppressed. */ +#define ARGP_KEY_HELP_DUP_ARGS_NOTE 0x2000005 +#define ARGP_KEY_HELP_ARGS_DOC 0x2000006 /* Argument doc string. */ + +/* When an argp has a non-zero CHILDREN field, it should point to a vector of + argp_child structures, each of which describes a subsidiary argp. */ +struct argp_child +{ + /* The child parser. */ + const struct argp *argp; + + /* Flags for this child. */ + int flags; + + /* If non-zero, an optional header to be printed in help output before the + child options. As a side-effect, a non-zero value forces the child + options to be grouped together; to achieve this effect without actually + printing a header string, use a value of "". */ + const char *header; + + /* Where to group the child options relative to the other (`consolidated') + options in the parent argp; the values are the same as the GROUP field + in argp_option structs, but all child-groupings follow parent options at + a particular group level. If both this field and HEADER are zero, then + they aren't grouped at all, but rather merged with the parent options + (merging the child's grouping levels with the parents). */ + int group; +}; + +/* Parsing state. This is provided to parsing functions called by argp, + which may examine and, as noted, modify fields. */ +struct argp_state +{ + /* The top level ARGP being parsed. */ + const struct argp *root_argp; + + /* The argument vector being parsed. May be modified. */ + int argc; + char **argv; + + /* The index in ARGV of the next arg that to be parsed. May be modified. */ + int next; + + /* The flags supplied to argp_parse. May be modified. */ + unsigned flags; + + /* While calling a parsing function with a key of ARGP_KEY_ARG, this is the + number of the current arg, starting at zero, and incremented after each + such call returns. At all other times, this is the number of such + arguments that have been processed. */ + unsigned arg_num; + + /* If non-zero, the index in ARGV of the first argument following a special + `--' argument (which prevents anything following being interpreted as an + option). Only set once argument parsing has proceeded past this point. */ + int quoted; + + /* An arbitrary pointer passed in from the user. */ + void *input; + /* Values to pass to child parsers. This vector will be the same length as + the number of children for the current parser. */ + void **child_inputs; + + /* For the parser's use. Initialized to 0. */ + void *hook; + + /* The name used when printing messages. This is initialized to ARGV[0], + or PROGRAM_INVOCATION_NAME if that is unavailable. */ + char *name; + + /* Streams used when argp prints something. */ + FILE *err_stream; /* For errors; initialized to stderr. */ + FILE *out_stream; /* For information; initialized to stdout. */ + + void *pstate; /* Private, for use by argp. */ +}; + +/* Flags for argp_parse (note that the defaults are those that are + convenient for program command line parsing): */ + +/* Don't ignore the first element of ARGV. Normally (and always unless + ARGP_NO_ERRS is set) the first element of the argument vector is + skipped for option parsing purposes, as it corresponds to the program name + in a command line. */ +#define ARGP_PARSE_ARGV0 0x01 + +/* Don't print error messages for unknown options to stderr; unless this flag + is set, ARGP_PARSE_ARGV0 is ignored, as ARGV[0] is used as the program + name in the error messages. This flag implies ARGP_NO_EXIT (on the + assumption that silent exiting upon errors is bad behaviour). */ +#define ARGP_NO_ERRS 0x02 + +/* Don't parse any non-option args. Normally non-option args are parsed by + calling the parse functions with a key of ARGP_KEY_ARG, and the actual arg + as the value. Since it's impossible to know which parse function wants to + handle it, each one is called in turn, until one returns 0 or an error + other than ARGP_ERR_UNKNOWN; if an argument is handled by no one, the + argp_parse returns prematurely (but with a return value of 0). If all + args have been parsed without error, all parsing functions are called one + last time with a key of ARGP_KEY_END. This flag needn't normally be set, + as the normal behavior is to stop parsing as soon as some argument can't + be handled. */ +#define ARGP_NO_ARGS 0x04 + +/* Parse options and arguments in the same order they occur on the command + line -- normally they're rearranged so that all options come first. */ +#define ARGP_IN_ORDER 0x08 + +/* Don't provide the standard long option --help, which causes usage and + option help information to be output to stdout, and exit (0) called. */ +#define ARGP_NO_HELP 0x10 + +/* Don't exit on errors (they may still result in error messages). */ +#define ARGP_NO_EXIT 0x20 + +/* Use the gnu getopt `long-only' rules for parsing arguments. */ +#define ARGP_LONG_ONLY 0x40 + +/* Turns off any message-printing/exiting options. */ +#define ARGP_SILENT (ARGP_NO_EXIT | ARGP_NO_ERRS | ARGP_NO_HELP) + +/* Parse the options strings in ARGC & ARGV according to the options in ARGP. + FLAGS is one of the ARGP_ flags above. If ARG_INDEX is non-NULL, the + index in ARGV of the first unparsed option is returned in it. If an + unknown option is present, ARGP_ERR_UNKNOWN is returned; if some parser + routine returned a non-zero value, it is returned; otherwise 0 is + returned. This function may also call exit unless the ARGP_NO_HELP flag + is set. INPUT is a pointer to a value to be passed in to the parser. */ +extern error_t argp_parse (const struct argp *__restrict __argp, + int /*argc*/, char **__restrict /*argv*/, + unsigned __flags, int *__restrict __arg_index, + void *__restrict __input); +extern error_t __argp_parse (const struct argp *__restrict __argp, + int /*argc*/, char **__restrict /*argv*/, + unsigned __flags, int *__restrict __arg_index, + void *__restrict __input); + +/* Global variables. */ + +/* GNULIB makes sure both program_invocation_name and + program_invocation_short_name are available */ +#ifdef GNULIB_PROGRAM_INVOCATION_NAME +extern char *program_invocation_name; +# undef HAVE_DECL_PROGRAM_INVOCATION_NAME +# define HAVE_DECL_PROGRAM_INVOCATION_NAME 1 +#endif + +#ifdef GNULIB_PROGRAM_INVOCATION_SHORT_NAME +extern char *program_invocation_short_name; +# undef HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME +# define HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME 1 +#endif + +/* If defined or set by the user program to a non-zero value, then a default + option --version is added (unless the ARGP_NO_HELP flag is used), which + will print this string followed by a newline and exit (unless the + ARGP_NO_EXIT flag is used). Overridden by ARGP_PROGRAM_VERSION_HOOK. */ +extern const char *argp_program_version; + +/* If defined or set by the user program to a non-zero value, then a default + option --version is added (unless the ARGP_NO_HELP flag is used), which + calls this function with a stream to print the version to and a pointer to + the current parsing state, and then exits (unless the ARGP_NO_EXIT flag is + used). This variable takes precedent over ARGP_PROGRAM_VERSION. */ +extern void (*argp_program_version_hook) (FILE *__restrict __stream, + struct argp_state *__restrict + __state); + +/* If defined or set by the user program, it should point to string that is + the bug-reporting address for the program. It will be printed by + argp_help if the ARGP_HELP_BUG_ADDR flag is set (as it is by various + standard help messages), embedded in a sentence that says something like + `Report bugs to ADDR.'. */ +extern const char *argp_program_bug_address; + +/* The exit status that argp will use when exiting due to a parsing error. + If not defined or set by the user program, this defaults to EX_USAGE from + . */ +extern error_t argp_err_exit_status; + +/* Flags for argp_help. */ +#define ARGP_HELP_USAGE 0x01 /* a Usage: message. */ +#define ARGP_HELP_SHORT_USAGE 0x02 /* " but don't actually print options. */ +#define ARGP_HELP_SEE 0x04 /* a `Try ... for more help' message. */ +#define ARGP_HELP_LONG 0x08 /* a long help message. */ +#define ARGP_HELP_PRE_DOC 0x10 /* doc string preceding long help. */ +#define ARGP_HELP_POST_DOC 0x20 /* doc string following long help. */ +#define ARGP_HELP_DOC (ARGP_HELP_PRE_DOC | ARGP_HELP_POST_DOC) +#define ARGP_HELP_BUG_ADDR 0x40 /* bug report address */ +#define ARGP_HELP_LONG_ONLY 0x80 /* modify output appropriately to + reflect ARGP_LONG_ONLY mode. */ + +/* These ARGP_HELP flags are only understood by argp_state_help. */ +#define ARGP_HELP_EXIT_ERR 0x100 /* Call exit(1) instead of returning. */ +#define ARGP_HELP_EXIT_OK 0x200 /* Call exit(0) instead of returning. */ + +/* The standard thing to do after a program command line parsing error, if an + error message has already been printed. */ +#define ARGP_HELP_STD_ERR \ + (ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR) +/* The standard thing to do after a program command line parsing error, if no + more specific error message has been printed. */ +#define ARGP_HELP_STD_USAGE \ + (ARGP_HELP_SHORT_USAGE | ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR) +/* The standard thing to do in response to a --help option. */ +#define ARGP_HELP_STD_HELP \ + (ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_EXIT_OK \ + | ARGP_HELP_DOC | ARGP_HELP_BUG_ADDR) + +/* Output a usage message for ARGP to STREAM. FLAGS are from the set + ARGP_HELP_*. */ +extern void argp_help (const struct argp *__restrict __argp, + FILE *__restrict __stream, + unsigned __flags, char *__restrict __name); +extern void __argp_help (const struct argp *__restrict __argp, + FILE *__restrict __stream, unsigned __flags, + char *__name); + +/* The following routines are intended to be called from within an argp + parsing routine (thus taking an argp_state structure as the first + argument). They may or may not print an error message and exit, depending + on the flags in STATE -- in any case, the caller should be prepared for + them *not* to exit, and should return an appropiate error after calling + them. [argp_usage & argp_error should probably be called argp_state_..., + but they're used often enough that they should be short] */ + +/* Output, if appropriate, a usage message for STATE to STREAM. FLAGS are + from the set ARGP_HELP_*. */ +extern void argp_state_help (const struct argp_state *__restrict __state, + FILE *__restrict __stream, + unsigned int __flags); +extern void __argp_state_help (const struct argp_state *__restrict __state, + FILE *__restrict __stream, + unsigned int __flags); + +#if _LIBC || !defined __USE_EXTERN_INLINES +/* Possibly output the standard usage message for ARGP to stderr and exit. */ +extern void argp_usage (const struct argp_state *__state); +extern void __argp_usage (const struct argp_state *__state); +#endif + +/* If appropriate, print the printf string FMT and following args, preceded + by the program name and `:', to stderr, and followed by a `Try ... --help' + message, then exit (1). */ +extern void argp_error (const struct argp_state *__restrict __state, + const char *__restrict __fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); +extern void __argp_error (const struct argp_state *__restrict __state, + const char *__restrict __fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); + +/* Similar to the standard gnu error-reporting function error(), but will + respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print + to STATE->err_stream. This is useful for argument parsing code that is + shared between program startup (when exiting is desired) and runtime + option parsing (when typically an error code is returned instead). The + difference between this function and argp_error is that the latter is for + *parsing errors*, and the former is for other problems that occur during + parsing but don't reflect a (syntactic) problem with the input. */ +extern void argp_failure (const struct argp_state *__restrict __state, + int __status, int __errnum, + const char *__restrict __fmt, ...) + __attribute__ ((__format__ (__printf__, 4, 5))); +extern void __argp_failure (const struct argp_state *__restrict __state, + int __status, int __errnum, + const char *__restrict __fmt, ...) + __attribute__ ((__format__ (__printf__, 4, 5))); + +#if _LIBC || !defined __USE_EXTERN_INLINES +/* Returns true if the option OPT is a valid short option. */ +extern int _option_is_short (const struct argp_option *__opt) __THROW; +extern int __option_is_short (const struct argp_option *__opt) __THROW; + +/* Returns true if the option OPT is in fact the last (unused) entry in an + options array. */ +extern int _option_is_end (const struct argp_option *__opt) __THROW; +extern int __option_is_end (const struct argp_option *__opt) __THROW; +#endif + +/* Return the input field for ARGP in the parser corresponding to STATE; used + by the help routines. */ +extern void *_argp_input (const struct argp *__restrict __argp, + const struct argp_state *__restrict __state) + __THROW; +extern void *__argp_input (const struct argp *__restrict __argp, + const struct argp_state *__restrict __state) + __THROW; + +#ifdef __USE_EXTERN_INLINES + +# if !_LIBC +# define __argp_usage argp_usage +# define __argp_state_help argp_state_help +# define __option_is_short _option_is_short +# define __option_is_end _option_is_end +# endif + +# ifndef ARGP_EI +# ifdef __GNUC__ + /* GCC 4.3 and above with -std=c99 or -std=gnu99 implements ISO C99 + inline semantics, unless -fgnu89-inline is used. It defines a macro + __GNUC_STDC_INLINE__ to indicate this situation or a macro + __GNUC_GNU_INLINE__ to indicate the opposite situation. + GCC 4.2 with -std=c99 or -std=gnu99 implements the GNU C inline + semantics but warns, unless -fgnu89-inline is used: + warning: C99 inline functions are not supported; using GNU89 + warning: to disable this warning use -fgnu89-inline or the gnu_inline function attribute + It defines a macro __GNUC_GNU_INLINE__ to indicate this situation. */ +# if defined __GNUC_STDC_INLINE__ +# define ARGP_EI __inline__ +# elif defined __GNUC_GNU_INLINE__ +# define ARGP_EI extern __inline__ __attribute__ ((__gnu_inline__)) +# else +# define ARGP_EI extern __inline__ +# endif +# else + /* With other compilers, assume the ISO C99 meaning of 'inline', if + the compiler supports 'inline' at all. */ +# define ARGP_EI inline +# endif +# endif + +ARGP_EI void +__argp_usage (const struct argp_state *__state) +{ + __argp_state_help (__state, stderr, ARGP_HELP_STD_USAGE); +} + +ARGP_EI int +__NTH (__option_is_short (const struct argp_option *__opt)) +{ + if (__opt->flags & OPTION_DOC) + return 0; + else + { + int __key = __opt->key; + return __key > 0 && __key <= UCHAR_MAX && isprint (__key); + } +} + +ARGP_EI int +__NTH (__option_is_end (const struct argp_option *__opt)) +{ + return !__opt->key && !__opt->name && !__opt->doc && !__opt->group; +} + +# if !_LIBC +# undef __argp_usage +# undef __argp_state_help +# undef __option_is_short +# undef __option_is_end +# endif +#endif /* Use extern inlines. */ + +#ifdef __cplusplus +} +#endif + +#endif /* argp.h */ diff --git a/gnulib/error.c b/gnulib/error.c index af2287b27..c79e8d42c 100644 --- a/gnulib/error.c +++ b/gnulib/error.c @@ -1,5 +1,5 @@ /* Error handler for noninteractive utilities - Copyright (C) 1990-1998, 2000-2007, 2009 Free Software Foundation, Inc. + Copyright (C) 1990-1998, 2000-2007, 2009-2010 Free Software Foundation, Inc. This file is part of the GNU C Library. This program is free software: you can redistribute it and/or modify @@ -70,8 +70,8 @@ unsigned int error_message_count; extern void __error (int status, int errnum, const char *message, ...) __attribute__ ((__format__ (__printf__, 3, 4))); extern void __error_at_line (int status, int errnum, const char *file_name, - unsigned int line_number, const char *message, - ...) + unsigned int line_number, const char *message, + ...) __attribute__ ((__format__ (__printf__, 5, 6)));; # define error __error # define error_at_line __error_at_line @@ -86,6 +86,7 @@ extern void __error_at_line (int status, int errnum, const char *file_name, #else /* not _LIBC */ # include +# include # if !HAVE_DECL_STRERROR_R && STRERROR_R_CHAR_P # ifndef HAVE_DECL_STRERROR_R @@ -100,8 +101,33 @@ extern char *program_name; # if HAVE_STRERROR_R || defined strerror_r # define __strerror_r strerror_r -# endif /* HAVE_STRERROR_R || defined strerror_r */ -#endif /* not _LIBC */ +# endif /* HAVE_STRERROR_R || defined strerror_r */ +#endif /* not _LIBC */ + +static inline void +flush_stdout (void) +{ +#if !_LIBC && defined F_GETFL + int stdout_fd; + +# if GNULIB_FREOPEN_SAFER + /* Use of gnulib's freopen-safer module normally ensures that + fileno (stdout) == 1 + whenever stdout is open. */ + stdout_fd = STDOUT_FILENO; +# else + /* POSIX states that fileno (stdout) after fclose is unspecified. But in + practice it is not a problem, because stdout is statically allocated and + the fd of a FILE stream is stored as a field in its allocated memory. */ + stdout_fd = fileno (stdout); +# endif + /* POSIX states that fflush (stdout) after fclose is unspecified; it + is safe in glibc, but not on all other platforms. fflush (NULL) + is always defined, but too draconian. */ + if (0 <= stdout_fd && 0 <= fcntl (stdout_fd, F_GETFL)) +#endif + fflush (stdout); +} static void print_errno_message (int errnum) @@ -149,58 +175,58 @@ error_tail (int status, int errnum, const char *message, va_list args) bool use_malloc = false; while (1) - { - if (__libc_use_alloca (len * sizeof (wchar_t))) - wmessage = (wchar_t *) alloca (len * sizeof (wchar_t)); - else - { - if (!use_malloc) - wmessage = NULL; + { + if (__libc_use_alloca (len * sizeof (wchar_t))) + wmessage = (wchar_t *) alloca (len * sizeof (wchar_t)); + else + { + if (!use_malloc) + wmessage = NULL; - wchar_t *p = (wchar_t *) realloc (wmessage, - len * sizeof (wchar_t)); - if (p == NULL) - { - free (wmessage); - fputws_unlocked (L"out of memory\n", stderr); - return; - } - wmessage = p; - use_malloc = true; - } + wchar_t *p = (wchar_t *) realloc (wmessage, + len * sizeof (wchar_t)); + if (p == NULL) + { + free (wmessage); + fputws_unlocked (L"out of memory\n", stderr); + return; + } + wmessage = p; + use_malloc = true; + } - memset (&st, '\0', sizeof (st)); - tmp = message; + memset (&st, '\0', sizeof (st)); + tmp = message; - res = mbsrtowcs (wmessage, &tmp, len, &st); - if (res != len) - break; + res = mbsrtowcs (wmessage, &tmp, len, &st); + if (res != len) + break; - if (__builtin_expect (len >= SIZE_MAX / 2, 0)) - { - /* This really should not happen if everything is fine. */ - res = (size_t) -1; - break; - } + if (__builtin_expect (len >= SIZE_MAX / 2, 0)) + { + /* This really should not happen if everything is fine. */ + res = (size_t) -1; + break; + } - len *= 2; - } + len *= 2; + } if (res == (size_t) -1) - { - /* The string cannot be converted. */ - if (use_malloc) - { - free (wmessage); - use_malloc = false; - } - wmessage = (wchar_t *) L"???"; - } + { + /* The string cannot be converted. */ + if (use_malloc) + { + free (wmessage); + use_malloc = false; + } + wmessage = (wchar_t *) L"???"; + } __vfwprintf (stderr, wmessage, args); if (use_malloc) - free (wmessage); + free (wmessage); } else #endif @@ -235,16 +261,10 @@ error (int status, int errnum, const char *message, ...) cancellation. Therefore disable cancellation for now. */ int state = PTHREAD_CANCEL_ENABLE; __libc_ptf_call (pthread_setcancelstate, (PTHREAD_CANCEL_DISABLE, &state), - 0); + 0); #endif -#if !_LIBC && defined F_GETFL - /* POSIX states that fflush (stdout) after fclose is unspecified; it - is safe in glibc, but not on all other platforms. fflush (NULL) - is always defined, but too draconian. */ - if (0 <= fcntl (1, F_GETFL)) -#endif - fflush (stdout); + flush_stdout (); #ifdef _LIBC _IO_flockfile (stderr); #endif @@ -276,7 +296,7 @@ int error_one_per_line; void error_at_line (int status, int errnum, const char *file_name, - unsigned int line_number, const char *message, ...) + unsigned int line_number, const char *message, ...) { va_list args; @@ -286,10 +306,10 @@ error_at_line (int status, int errnum, const char *file_name, static unsigned int old_line_number; if (old_line_number == line_number - && (file_name == old_file_name - || strcmp (old_file_name, file_name) == 0)) - /* Simply return and print nothing. */ - return; + && (file_name == old_file_name + || strcmp (old_file_name, file_name) == 0)) + /* Simply return and print nothing. */ + return; old_file_name = file_name; old_line_number = line_number; @@ -300,16 +320,10 @@ error_at_line (int status, int errnum, const char *file_name, cancellation. Therefore disable cancellation for now. */ int state = PTHREAD_CANCEL_ENABLE; __libc_ptf_call (pthread_setcancelstate, (PTHREAD_CANCEL_DISABLE, &state), - 0); + 0); #endif -#if !_LIBC && defined F_GETFL - /* POSIX states that fflush (stdout) after fclose is unspecified; it - is safe in glibc, but not on all other platforms. fflush (NULL) - is always defined, but too draconian. */ - if (0 <= fcntl (1, F_GETFL)) -#endif - fflush (stdout); + flush_stdout (); #ifdef _LIBC _IO_flockfile (stderr); #endif @@ -326,10 +340,10 @@ error_at_line (int status, int errnum, const char *file_name, #if _LIBC __fxprintf (NULL, file_name != NULL ? "%s:%d: " : " ", - file_name, line_number); + file_name, line_number); #else fprintf (stderr, file_name != NULL ? "%s:%d: " : " ", - file_name, line_number); + file_name, line_number); #endif va_start (args, message); diff --git a/gnulib/error.h b/gnulib/error.h index 6d4968114..9deef02d2 100644 --- a/gnulib/error.h +++ b/gnulib/error.h @@ -1,5 +1,6 @@ /* Declaration for error-reporting function - Copyright (C) 1995, 1996, 1997, 2003, 2006, 2008 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1997, 2003, 2006, 2008, 2009, 2010 Free Software + Foundation, Inc. This file is part of the GNU C Library. This program is free software: you can redistribute it and/or modify @@ -19,19 +20,18 @@ #define _ERROR_H 1 #ifndef __attribute__ -/* This feature is available in gcc versions 2.5 and later. */ -# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) -# define __attribute__(Spec) /* empty */ -# endif -/* The __-protected variants of `format' and `printf' attributes - are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */ +/* The __attribute__ feature is available in gcc versions 2.5 and later. + The __-protected variants of the attributes 'format' and 'printf' are + accepted by gcc versions 2.6.4 (effectively 2.7) and later. + We enable __attribute__ only if these are supported too, because + gnulib and libintl do '#define printf __printf__' when they override + the 'printf' function. */ # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) -# define __format__ format -# define __printf__ printf +# define __attribute__(Spec) /* empty */ # endif #endif -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif @@ -43,7 +43,7 @@ extern void error (int __status, int __errnum, const char *__format, ...) __attribute__ ((__format__ (__printf__, 3, 4))); extern void error_at_line (int __status, int __errnum, const char *__fname, - unsigned int __lineno, const char *__format, ...) + unsigned int __lineno, const char *__format, ...) __attribute__ ((__format__ (__printf__, 5, 6))); /* If NULL, error will flush stdout, then print on stderr the program @@ -58,7 +58,7 @@ extern unsigned int error_message_count; variable controls whether this mode is selected or not. */ extern int error_one_per_line; -#ifdef __cplusplus +#ifdef __cplusplus } #endif diff --git a/gnulib/fnmatch.c b/gnulib/fnmatch.c index 48bc8b5d2..f15dbb806 100644 --- a/gnulib/fnmatch.c +++ b/gnulib/fnmatch.c @@ -1,5 +1,5 @@ -/* Copyright (C) 1991,1992,1993,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007 - Free Software Foundation, Inc. +/* Copyright (C) 1991, 1992, 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, + 2003, 2004, 2005, 2006, 2007, 2009, 2010 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 @@ -21,7 +21,7 @@ /* Enable GNU extensions in fnmatch.h. */ #ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 +# define _GNU_SOURCE 1 #endif #if ! defined __builtin_expect && __GNUC__ < 3 @@ -89,7 +89,7 @@ extern int fnmatch (const char *pattern, const char *string, int flags); # define isblank(c) ((c) == ' ' || (c) == '\t') # endif -# define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) +# define STREQ(s1, s2) (strcmp (s1, s2) == 0) # if defined _LIBC || WIDE_CHAR_SUPPORT /* The GNU C library provides support for user-defined character classes @@ -109,25 +109,25 @@ extern int fnmatch (const char *pattern, const char *string, int flags); # endif # ifdef _LIBC -# define ISWCTYPE(WC, WT) __iswctype (WC, WT) +# define ISWCTYPE(WC, WT) __iswctype (WC, WT) # else -# define ISWCTYPE(WC, WT) iswctype (WC, WT) +# define ISWCTYPE(WC, WT) iswctype (WC, WT) # endif # if (HAVE_MBSTATE_T && HAVE_MBSRTOWCS) || _LIBC /* In this case we are implementing the multibyte character handling. */ -# define HANDLE_MULTIBYTE 1 +# define HANDLE_MULTIBYTE 1 # endif # else # define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */ -# define IS_CHAR_CLASS(string) \ - (STREQ (string, "alpha") || STREQ (string, "upper") \ - || STREQ (string, "lower") || STREQ (string, "digit") \ - || STREQ (string, "alnum") || STREQ (string, "xdigit") \ - || STREQ (string, "space") || STREQ (string, "print") \ - || STREQ (string, "punct") || STREQ (string, "graph") \ +# define IS_CHAR_CLASS(string) \ + (STREQ (string, "alpha") || STREQ (string, "upper") \ + || STREQ (string, "lower") || STREQ (string, "digit") \ + || STREQ (string, "alnum") || STREQ (string, "xdigit") \ + || STREQ (string, "space") || STREQ (string, "print") \ + || STREQ (string, "punct") || STREQ (string, "graph") \ || STREQ (string, "cntrl") || STREQ (string, "blank")) # endif @@ -145,17 +145,17 @@ static int posixly_correct; /* Note that this evaluates C many times. */ # define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c)) -# define CHAR char -# define UCHAR unsigned char -# define INT int -# define FCT internal_fnmatch -# define EXT ext_match -# define END end_pattern -# define L_(CS) CS +# define CHAR char +# define UCHAR unsigned char +# define INT int +# define FCT internal_fnmatch +# define EXT ext_match +# define END end_pattern +# define L_(CS) CS # ifdef _LIBC -# define BTOWC(C) __btowc (C) +# define BTOWC(C) __btowc (C) # else -# define BTOWC(C) btowc (C) +# define BTOWC(C) btowc (C) # endif # define STRLEN(S) strlen (S) # define STRCAT(D, S) strcat (D, S) @@ -175,14 +175,14 @@ static int posixly_correct; # if HANDLE_MULTIBYTE # define FOLD(c) ((flags & FNM_CASEFOLD) ? towlower (c) : (c)) -# define CHAR wchar_t -# define UCHAR wint_t -# define INT wint_t -# define FCT internal_fnwmatch -# define EXT ext_wmatch -# define END end_wpattern -# define L_(CS) L##CS -# define BTOWC(C) (C) +# define CHAR wchar_t +# define UCHAR wint_t +# define INT wint_t +# define FCT internal_fnwmatch +# define EXT ext_wmatch +# define END end_wpattern +# define L_(CS) L##CS +# define BTOWC(C) (C) # ifdef _LIBC # define STRLEN(S) __wcslen (S) # define STRCAT(D, S) __wcscat (D, S) @@ -218,40 +218,40 @@ is_char_class (const wchar_t *wcs) /* Test for a printable character from the portable character set. */ # ifdef _LIBC if (*wcs < 0x20 || *wcs > 0x7e - || *wcs == 0x24 || *wcs == 0x40 || *wcs == 0x60) - return (wctype_t) 0; + || *wcs == 0x24 || *wcs == 0x40 || *wcs == 0x60) + return (wctype_t) 0; # else switch (*wcs) - { - case L' ': case L'!': case L'"': case L'#': case L'%': - case L'&': case L'\'': case L'(': case L')': case L'*': - case L'+': case L',': case L'-': case L'.': case L'/': - case L'0': case L'1': case L'2': case L'3': case L'4': - case L'5': case L'6': case L'7': case L'8': case L'9': - case L':': case L';': case L'<': case L'=': case L'>': - case L'?': - case L'A': case L'B': case L'C': case L'D': case L'E': - case L'F': case L'G': case L'H': case L'I': case L'J': - case L'K': case L'L': case L'M': case L'N': case L'O': - case L'P': case L'Q': case L'R': case L'S': case L'T': - case L'U': case L'V': case L'W': case L'X': case L'Y': - case L'Z': - case L'[': case L'\\': case L']': case L'^': case L'_': - case L'a': case L'b': case L'c': case L'd': case L'e': - case L'f': case L'g': case L'h': case L'i': case L'j': - case L'k': case L'l': case L'm': case L'n': case L'o': - case L'p': case L'q': case L'r': case L's': case L't': - case L'u': case L'v': case L'w': case L'x': case L'y': - case L'z': case L'{': case L'|': case L'}': case L'~': - break; - default: - return (wctype_t) 0; - } + { + case L' ': case L'!': case L'"': case L'#': case L'%': + case L'&': case L'\'': case L'(': case L')': case L'*': + case L'+': case L',': case L'-': case L'.': case L'/': + case L'0': case L'1': case L'2': case L'3': case L'4': + case L'5': case L'6': case L'7': case L'8': case L'9': + case L':': case L';': case L'<': case L'=': case L'>': + case L'?': + case L'A': case L'B': case L'C': case L'D': case L'E': + case L'F': case L'G': case L'H': case L'I': case L'J': + case L'K': case L'L': case L'M': case L'N': case L'O': + case L'P': case L'Q': case L'R': case L'S': case L'T': + case L'U': case L'V': case L'W': case L'X': case L'Y': + case L'Z': + case L'[': case L'\\': case L']': case L'^': case L'_': + case L'a': case L'b': case L'c': case L'd': case L'e': + case L'f': case L'g': case L'h': case L'i': case L'j': + case L'k': case L'l': case L'm': case L'n': case L'o': + case L'p': case L'q': case L'r': case L's': case L't': + case L'u': case L'v': case L'w': case L'x': case L'y': + case L'z': case L'{': case L'|': case L'}': case L'~': + break; + default: + return (wctype_t) 0; + } # endif /* Avoid overrunning the buffer. */ if (cp == s + CHAR_CLASS_MAX_LENGTH) - return (wctype_t) 0; + return (wctype_t) 0; *cp++ = (char) *wcs++; } @@ -287,58 +287,58 @@ fnmatch (const char *pattern, const char *string, int flags) int res; /* Calculate the size needed to convert the strings to - wide characters. */ + wide characters. */ memset (&ps, '\0', sizeof (ps)); patsize = mbsrtowcs (NULL, &pattern, 0, &ps) + 1; if (__builtin_expect (patsize != 0, 1)) - { - assert (mbsinit (&ps)); - strsize = mbsrtowcs (NULL, &string, 0, &ps) + 1; - if (__builtin_expect (strsize != 0, 1)) - { - assert (mbsinit (&ps)); - totsize = patsize + strsize; - if (__builtin_expect (! (patsize <= totsize - && totsize <= SIZE_MAX / sizeof (wchar_t)), - 0)) - { - errno = ENOMEM; - return -1; - } + { + assert (mbsinit (&ps)); + strsize = mbsrtowcs (NULL, &string, 0, &ps) + 1; + if (__builtin_expect (strsize != 0, 1)) + { + assert (mbsinit (&ps)); + totsize = patsize + strsize; + if (__builtin_expect (! (patsize <= totsize + && totsize <= SIZE_MAX / sizeof (wchar_t)), + 0)) + { + errno = ENOMEM; + return -1; + } - /* Allocate room for the wide characters. */ - if (__builtin_expect (totsize < ALLOCA_LIMIT, 1)) - wpattern = (wchar_t *) alloca (totsize * sizeof (wchar_t)); - else - { - wpattern = malloc (totsize * sizeof (wchar_t)); - if (__builtin_expect (! wpattern, 0)) - { - errno = ENOMEM; - return -1; - } - } - wstring = wpattern + patsize; + /* Allocate room for the wide characters. */ + if (__builtin_expect (totsize < ALLOCA_LIMIT, 1)) + wpattern = (wchar_t *) alloca (totsize * sizeof (wchar_t)); + else + { + wpattern = malloc (totsize * sizeof (wchar_t)); + if (__builtin_expect (! wpattern, 0)) + { + errno = ENOMEM; + return -1; + } + } + wstring = wpattern + patsize; - /* Convert the strings into wide characters. */ - mbsrtowcs (wpattern, &pattern, patsize, &ps); - assert (mbsinit (&ps)); - mbsrtowcs (wstring, &string, strsize, &ps); + /* Convert the strings into wide characters. */ + mbsrtowcs (wpattern, &pattern, patsize, &ps); + assert (mbsinit (&ps)); + mbsrtowcs (wstring, &string, strsize, &ps); - res = internal_fnwmatch (wpattern, wstring, wstring + strsize - 1, - flags & FNM_PERIOD, flags); + res = internal_fnwmatch (wpattern, wstring, wstring + strsize - 1, + flags & FNM_PERIOD, flags); - if (__builtin_expect (! (totsize < ALLOCA_LIMIT), 0)) - free (wpattern); - return res; - } - } + if (__builtin_expect (! (totsize < ALLOCA_LIMIT), 0)) + free (wpattern); + return res; + } + } } # endif /* HANDLE_MULTIBYTE */ return internal_fnmatch (pattern, string, string + strlen (string), - flags & FNM_PERIOD, flags); + flags & FNM_PERIOD, flags); } # ifdef _LIBC @@ -351,4 +351,4 @@ compat_symbol (libc, __fnmatch_old, fnmatch, GLIBC_2_0); libc_hidden_ver (__fnmatch, fnmatch) # endif -#endif /* _LIBC or not __GNU_LIBRARY__. */ +#endif /* _LIBC or not __GNU_LIBRARY__. */ diff --git a/gnulib/fnmatch_loop.c b/gnulib/fnmatch_loop.c index c2182ffb0..8cd444404 100644 --- a/gnulib/fnmatch_loop.c +++ b/gnulib/fnmatch_loop.c @@ -1,5 +1,5 @@ -/* Copyright (C) 1991,1992,1993,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006 - Free Software Foundation, Inc. +/* Copyright (C) 1991, 1992, 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, + 2003, 2004, 2005, 2006, 2009, 2010 Free Software Foundation, Inc. This file is part of the GNU C Library. This program is free software; you can redistribute it and/or modify @@ -19,7 +19,7 @@ /* Match STRING against the file name pattern PATTERN, returning zero if it matches, nonzero if not. */ static int EXT (INT opt, const CHAR *pattern, const CHAR *string, - const CHAR *string_end, bool no_leading_period, int flags) + const CHAR *string_end, bool no_leading_period, int flags) internal_function; static const CHAR *END (const CHAR *patternp) internal_function; @@ -46,310 +46,310 @@ FCT (const CHAR *pattern, const CHAR *string, const CHAR *string_end, c = FOLD (c); switch (c) - { - case L_('?'): - if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(') - { - int res; + { + case L_('?'): + if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(') + { + int res; - res = EXT (c, p, n, string_end, no_leading_period, - flags); - if (res != -1) - return res; - } + res = EXT (c, p, n, string_end, no_leading_period, + flags); + if (res != -1) + return res; + } - if (n == string_end) - return FNM_NOMATCH; - else if (*n == L_('/') && (flags & FNM_FILE_NAME)) - return FNM_NOMATCH; - else if (*n == L_('.') && no_leading_period) - return FNM_NOMATCH; - break; + if (n == string_end) + return FNM_NOMATCH; + else if (*n == L_('/') && (flags & FNM_FILE_NAME)) + return FNM_NOMATCH; + else if (*n == L_('.') && no_leading_period) + return FNM_NOMATCH; + break; - case L_('\\'): - if (!(flags & FNM_NOESCAPE)) - { - c = *p++; - if (c == L_('\0')) - /* Trailing \ loses. */ - return FNM_NOMATCH; - c = FOLD (c); - } - if (n == string_end || FOLD ((UCHAR) *n) != c) - return FNM_NOMATCH; - break; + case L_('\\'): + if (!(flags & FNM_NOESCAPE)) + { + c = *p++; + if (c == L_('\0')) + /* Trailing \ loses. */ + return FNM_NOMATCH; + c = FOLD (c); + } + if (n == string_end || FOLD ((UCHAR) *n) != c) + return FNM_NOMATCH; + break; - case L_('*'): - if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(') - { - int res; + case L_('*'): + if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(') + { + int res; - res = EXT (c, p, n, string_end, no_leading_period, - flags); - if (res != -1) - return res; - } + res = EXT (c, p, n, string_end, no_leading_period, + flags); + if (res != -1) + return res; + } - if (n != string_end && *n == L_('.') && no_leading_period) - return FNM_NOMATCH; + if (n != string_end && *n == L_('.') && no_leading_period) + return FNM_NOMATCH; - for (c = *p++; c == L_('?') || c == L_('*'); c = *p++) - { - if (*p == L_('(') && (flags & FNM_EXTMATCH) != 0) - { - const CHAR *endp = END (p); - if (endp != p) - { - /* This is a pattern. Skip over it. */ - p = endp; - continue; - } - } + for (c = *p++; c == L_('?') || c == L_('*'); c = *p++) + { + if (*p == L_('(') && (flags & FNM_EXTMATCH) != 0) + { + const CHAR *endp = END (p); + if (endp != p) + { + /* This is a pattern. Skip over it. */ + p = endp; + continue; + } + } - if (c == L_('?')) - { - /* A ? needs to match one character. */ - if (n == string_end) - /* There isn't another character; no match. */ - return FNM_NOMATCH; - else if (*n == L_('/') - && __builtin_expect (flags & FNM_FILE_NAME, 0)) - /* A slash does not match a wildcard under - FNM_FILE_NAME. */ - return FNM_NOMATCH; - else - /* One character of the string is consumed in matching - this ? wildcard, so *??? won't match if there are - less than three characters. */ - ++n; - } - } + if (c == L_('?')) + { + /* A ? needs to match one character. */ + if (n == string_end) + /* There isn't another character; no match. */ + return FNM_NOMATCH; + else if (*n == L_('/') + && __builtin_expect (flags & FNM_FILE_NAME, 0)) + /* A slash does not match a wildcard under + FNM_FILE_NAME. */ + return FNM_NOMATCH; + else + /* One character of the string is consumed in matching + this ? wildcard, so *??? won't match if there are + less than three characters. */ + ++n; + } + } - if (c == L_('\0')) - /* The wildcard(s) is/are the last element of the pattern. - If the name is a file name and contains another slash - this means it cannot match, unless the FNM_LEADING_DIR - flag is set. */ - { - int result = (flags & FNM_FILE_NAME) == 0 ? 0 : FNM_NOMATCH; + if (c == L_('\0')) + /* The wildcard(s) is/are the last element of the pattern. + If the name is a file name and contains another slash + this means it cannot match, unless the FNM_LEADING_DIR + flag is set. */ + { + int result = (flags & FNM_FILE_NAME) == 0 ? 0 : FNM_NOMATCH; - if (flags & FNM_FILE_NAME) - { - if (flags & FNM_LEADING_DIR) - result = 0; - else - { - if (MEMCHR (n, L_('/'), string_end - n) == NULL) - result = 0; - } - } + if (flags & FNM_FILE_NAME) + { + if (flags & FNM_LEADING_DIR) + result = 0; + else + { + if (MEMCHR (n, L_('/'), string_end - n) == NULL) + result = 0; + } + } - return result; - } - else - { - const CHAR *endp; + return result; + } + else + { + const CHAR *endp; - endp = MEMCHR (n, (flags & FNM_FILE_NAME) ? L_('/') : L_('\0'), - string_end - n); - if (endp == NULL) - endp = string_end; + endp = MEMCHR (n, (flags & FNM_FILE_NAME) ? L_('/') : L_('\0'), + string_end - n); + if (endp == NULL) + endp = string_end; - if (c == L_('[') - || (__builtin_expect (flags & FNM_EXTMATCH, 0) != 0 - && (c == L_('@') || c == L_('+') || c == L_('!')) - && *p == L_('('))) - { - int flags2 = ((flags & FNM_FILE_NAME) - ? flags : (flags & ~FNM_PERIOD)); - bool no_leading_period2 = no_leading_period; + if (c == L_('[') + || (__builtin_expect (flags & FNM_EXTMATCH, 0) != 0 + && (c == L_('@') || c == L_('+') || c == L_('!')) + && *p == L_('('))) + { + int flags2 = ((flags & FNM_FILE_NAME) + ? flags : (flags & ~FNM_PERIOD)); + bool no_leading_period2 = no_leading_period; - for (--p; n < endp; ++n, no_leading_period2 = false) - if (FCT (p, n, string_end, no_leading_period2, flags2) - == 0) - return 0; - } - else if (c == L_('/') && (flags & FNM_FILE_NAME)) - { - while (n < string_end && *n != L_('/')) - ++n; - if (n < string_end && *n == L_('/') - && (FCT (p, n + 1, string_end, flags & FNM_PERIOD, flags) - == 0)) - return 0; - } - else - { - int flags2 = ((flags & FNM_FILE_NAME) - ? flags : (flags & ~FNM_PERIOD)); - int no_leading_period2 = no_leading_period; + for (--p; n < endp; ++n, no_leading_period2 = false) + if (FCT (p, n, string_end, no_leading_period2, flags2) + == 0) + return 0; + } + else if (c == L_('/') && (flags & FNM_FILE_NAME)) + { + while (n < string_end && *n != L_('/')) + ++n; + if (n < string_end && *n == L_('/') + && (FCT (p, n + 1, string_end, flags & FNM_PERIOD, flags) + == 0)) + return 0; + } + else + { + int flags2 = ((flags & FNM_FILE_NAME) + ? flags : (flags & ~FNM_PERIOD)); + int no_leading_period2 = no_leading_period; - if (c == L_('\\') && !(flags & FNM_NOESCAPE)) - c = *p; - c = FOLD (c); - for (--p; n < endp; ++n, no_leading_period2 = false) - if (FOLD ((UCHAR) *n) == c - && (FCT (p, n, string_end, no_leading_period2, flags2) - == 0)) - return 0; - } - } + if (c == L_('\\') && !(flags & FNM_NOESCAPE)) + c = *p; + c = FOLD (c); + for (--p; n < endp; ++n, no_leading_period2 = false) + if (FOLD ((UCHAR) *n) == c + && (FCT (p, n, string_end, no_leading_period2, flags2) + == 0)) + return 0; + } + } - /* If we come here no match is possible with the wildcard. */ - return FNM_NOMATCH; + /* If we come here no match is possible with the wildcard. */ + return FNM_NOMATCH; - case L_('['): - { - /* Nonzero if the sense of the character class is inverted. */ - register bool not; - CHAR cold; - UCHAR fn; + case L_('['): + { + /* Nonzero if the sense of the character class is inverted. */ + register bool not; + CHAR cold; + UCHAR fn; - if (posixly_correct == 0) - posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1; + if (posixly_correct == 0) + posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1; - if (n == string_end) - return FNM_NOMATCH; + if (n == string_end) + return FNM_NOMATCH; - if (*n == L_('.') && no_leading_period) - return FNM_NOMATCH; + if (*n == L_('.') && no_leading_period) + return FNM_NOMATCH; - if (*n == L_('/') && (flags & FNM_FILE_NAME)) - /* `/' cannot be matched. */ - return FNM_NOMATCH; + if (*n == L_('/') && (flags & FNM_FILE_NAME)) + /* `/' cannot be matched. */ + return FNM_NOMATCH; - not = (*p == L_('!') || (posixly_correct < 0 && *p == L_('^'))); - if (not) - ++p; + not = (*p == L_('!') || (posixly_correct < 0 && *p == L_('^'))); + if (not) + ++p; - fn = FOLD ((UCHAR) *n); + fn = FOLD ((UCHAR) *n); - c = *p++; - for (;;) - { - if (!(flags & FNM_NOESCAPE) && c == L_('\\')) - { - if (*p == L_('\0')) - return FNM_NOMATCH; - c = FOLD ((UCHAR) *p); - ++p; + c = *p++; + for (;;) + { + if (!(flags & FNM_NOESCAPE) && c == L_('\\')) + { + if (*p == L_('\0')) + return FNM_NOMATCH; + c = FOLD ((UCHAR) *p); + ++p; - goto normal_bracket; - } - else if (c == L_('[') && *p == L_(':')) - { - /* Leave room for the null. */ - CHAR str[CHAR_CLASS_MAX_LENGTH + 1]; - size_t c1 = 0; + goto normal_bracket; + } + else if (c == L_('[') && *p == L_(':')) + { + /* Leave room for the null. */ + CHAR str[CHAR_CLASS_MAX_LENGTH + 1]; + size_t c1 = 0; #if defined _LIBC || WIDE_CHAR_SUPPORT - wctype_t wt; + wctype_t wt; #endif - const CHAR *startp = p; + const CHAR *startp = p; - for (;;) - { - if (c1 == CHAR_CLASS_MAX_LENGTH) - /* The name is too long and therefore the pattern - is ill-formed. */ - return FNM_NOMATCH; + for (;;) + { + if (c1 == CHAR_CLASS_MAX_LENGTH) + /* The name is too long and therefore the pattern + is ill-formed. */ + return FNM_NOMATCH; - c = *++p; - if (c == L_(':') && p[1] == L_(']')) - { - p += 2; - break; - } - if (c < L_('a') || c >= L_('z')) - { - /* This cannot possibly be a character class name. - Match it as a normal range. */ - p = startp; - c = L_('['); - goto normal_bracket; - } - str[c1++] = c; - } - str[c1] = L_('\0'); + c = *++p; + if (c == L_(':') && p[1] == L_(']')) + { + p += 2; + break; + } + if (c < L_('a') || c >= L_('z')) + { + /* This cannot possibly be a character class name. + Match it as a normal range. */ + p = startp; + c = L_('['); + goto normal_bracket; + } + str[c1++] = c; + } + str[c1] = L_('\0'); #if defined _LIBC || WIDE_CHAR_SUPPORT - wt = IS_CHAR_CLASS (str); - if (wt == 0) - /* Invalid character class name. */ - return FNM_NOMATCH; + wt = IS_CHAR_CLASS (str); + if (wt == 0) + /* Invalid character class name. */ + return FNM_NOMATCH; # if defined _LIBC && ! WIDE_CHAR_VERSION - /* The following code is glibc specific but does - there a good job in speeding up the code since - we can avoid the btowc() call. */ - if (_ISCTYPE ((UCHAR) *n, wt)) - goto matched; + /* The following code is glibc specific but does + there a good job in speeding up the code since + we can avoid the btowc() call. */ + if (_ISCTYPE ((UCHAR) *n, wt)) + goto matched; # else - if (ISWCTYPE (BTOWC ((UCHAR) *n), wt)) - goto matched; + if (ISWCTYPE (BTOWC ((UCHAR) *n), wt)) + goto matched; # endif #else - if ((STREQ (str, L_("alnum")) && isalnum ((UCHAR) *n)) - || (STREQ (str, L_("alpha")) && isalpha ((UCHAR) *n)) - || (STREQ (str, L_("blank")) && isblank ((UCHAR) *n)) - || (STREQ (str, L_("cntrl")) && iscntrl ((UCHAR) *n)) - || (STREQ (str, L_("digit")) && isdigit ((UCHAR) *n)) - || (STREQ (str, L_("graph")) && isgraph ((UCHAR) *n)) - || (STREQ (str, L_("lower")) && islower ((UCHAR) *n)) - || (STREQ (str, L_("print")) && isprint ((UCHAR) *n)) - || (STREQ (str, L_("punct")) && ispunct ((UCHAR) *n)) - || (STREQ (str, L_("space")) && isspace ((UCHAR) *n)) - || (STREQ (str, L_("upper")) && isupper ((UCHAR) *n)) - || (STREQ (str, L_("xdigit")) && isxdigit ((UCHAR) *n))) - goto matched; + if ((STREQ (str, L_("alnum")) && isalnum ((UCHAR) *n)) + || (STREQ (str, L_("alpha")) && isalpha ((UCHAR) *n)) + || (STREQ (str, L_("blank")) && isblank ((UCHAR) *n)) + || (STREQ (str, L_("cntrl")) && iscntrl ((UCHAR) *n)) + || (STREQ (str, L_("digit")) && isdigit ((UCHAR) *n)) + || (STREQ (str, L_("graph")) && isgraph ((UCHAR) *n)) + || (STREQ (str, L_("lower")) && islower ((UCHAR) *n)) + || (STREQ (str, L_("print")) && isprint ((UCHAR) *n)) + || (STREQ (str, L_("punct")) && ispunct ((UCHAR) *n)) + || (STREQ (str, L_("space")) && isspace ((UCHAR) *n)) + || (STREQ (str, L_("upper")) && isupper ((UCHAR) *n)) + || (STREQ (str, L_("xdigit")) && isxdigit ((UCHAR) *n))) + goto matched; #endif - c = *p++; - } + c = *p++; + } #ifdef _LIBC - else if (c == L_('[') && *p == L_('=')) - { - UCHAR str[1]; - uint32_t nrules = - _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); - const CHAR *startp = p; + else if (c == L_('[') && *p == L_('=')) + { + UCHAR str[1]; + uint32_t nrules = + _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + const CHAR *startp = p; - c = *++p; - if (c == L_('\0')) - { - p = startp; - c = L_('['); - goto normal_bracket; - } - str[0] = c; + c = *++p; + if (c == L_('\0')) + { + p = startp; + c = L_('['); + goto normal_bracket; + } + str[0] = c; - c = *++p; - if (c != L_('=') || p[1] != L_(']')) - { - p = startp; - c = L_('['); - goto normal_bracket; - } - p += 2; + c = *++p; + if (c != L_('=') || p[1] != L_(']')) + { + p = startp; + c = L_('['); + goto normal_bracket; + } + p += 2; - if (nrules == 0) - { - if ((UCHAR) *n == str[0]) - goto matched; - } - else - { - const int32_t *table; + if (nrules == 0) + { + if ((UCHAR) *n == str[0]) + goto matched; + } + else + { + const int32_t *table; # if WIDE_CHAR_VERSION - const int32_t *weights; - const int32_t *extra; + const int32_t *weights; + const int32_t *extra; # else - const unsigned char *weights; - const unsigned char *extra; + const unsigned char *weights; + const unsigned char *extra; # endif - const int32_t *indirect; - int32_t idx; - const UCHAR *cp = (const UCHAR *) str; + const int32_t *indirect; + int32_t idx; + const UCHAR *cp = (const UCHAR *) str; - /* This #include defines a local function! */ + /* This #include defines a local function! */ # if WIDE_CHAR_VERSION # include # else @@ -357,605 +357,610 @@ FCT (const CHAR *pattern, const CHAR *string, const CHAR *string_end, # endif # if WIDE_CHAR_VERSION - table = (const int32_t *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEWC); - weights = (const int32_t *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTWC); - extra = (const int32_t *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAWC); - indirect = (const int32_t *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTWC); + table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEWC); + weights = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTWC); + extra = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAWC); + indirect = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTWC); # else - table = (const int32_t *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); - weights = (const unsigned char *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB); - extra = (const unsigned char *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); - indirect = (const int32_t *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB); + table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + weights = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB); + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB); # endif - idx = findidx (&cp); - if (idx != 0) - { - /* We found a table entry. Now see whether the - character we are currently at has the same - equivalance class value. */ - int len = weights[idx]; - int32_t idx2; - const UCHAR *np = (const UCHAR *) n; + idx = findidx (&cp); + if (idx != 0) + { + /* We found a table entry. Now see whether the + character we are currently at has the same + equivalance class value. */ + int len = weights[idx & 0xffffff]; + int32_t idx2; + const UCHAR *np = (const UCHAR *) n; - idx2 = findidx (&np); - if (idx2 != 0 && len == weights[idx2]) - { - int cnt = 0; + idx2 = findidx (&np); + if (idx2 != 0 + && (idx >> 24) == (idx2 >> 24) + && len == weights[idx2 & 0xffffff]) + { + int cnt = 0; - while (cnt < len - && (weights[idx + 1 + cnt] - == weights[idx2 + 1 + cnt])) - ++cnt; + idx &= 0xffffff; + idx2 &= 0xffffff; - if (cnt == len) - goto matched; - } - } - } + while (cnt < len + && (weights[idx + 1 + cnt] + == weights[idx2 + 1 + cnt])) + ++cnt; - c = *p++; - } + if (cnt == len) + goto matched; + } + } + } + + c = *p++; + } #endif - else if (c == L_('\0')) - /* [ (unterminated) loses. */ - return FNM_NOMATCH; - else - { - bool is_range = false; + else if (c == L_('\0')) + /* [ (unterminated) loses. */ + return FNM_NOMATCH; + else + { + bool is_range = false; #ifdef _LIBC - bool is_seqval = false; + bool is_seqval = false; - if (c == L_('[') && *p == L_('.')) - { - uint32_t nrules = - _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); - const CHAR *startp = p; - size_t c1 = 0; + if (c == L_('[') && *p == L_('.')) + { + uint32_t nrules = + _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + const CHAR *startp = p; + size_t c1 = 0; - while (1) - { - c = *++p; - if (c == L_('.') && p[1] == L_(']')) - { - p += 2; - break; - } - if (c == '\0') - return FNM_NOMATCH; - ++c1; - } + while (1) + { + c = *++p; + if (c == L_('.') && p[1] == L_(']')) + { + p += 2; + break; + } + if (c == '\0') + return FNM_NOMATCH; + ++c1; + } - /* We have to handling the symbols differently in - ranges since then the collation sequence is - important. */ - is_range = *p == L_('-') && p[1] != L_('\0'); + /* We have to handling the symbols differently in + ranges since then the collation sequence is + important. */ + is_range = *p == L_('-') && p[1] != L_('\0'); - if (nrules == 0) - { - /* There are no names defined in the collation - data. Therefore we only accept the trivial - names consisting of the character itself. */ - if (c1 != 1) - return FNM_NOMATCH; + if (nrules == 0) + { + /* There are no names defined in the collation + data. Therefore we only accept the trivial + names consisting of the character itself. */ + if (c1 != 1) + return FNM_NOMATCH; - if (!is_range && *n == startp[1]) - goto matched; + if (!is_range && *n == startp[1]) + goto matched; - cold = startp[1]; - c = *p++; - } - else - { - int32_t table_size; - const int32_t *symb_table; + cold = startp[1]; + c = *p++; + } + else + { + int32_t table_size; + const int32_t *symb_table; # ifdef WIDE_CHAR_VERSION - char str[c1]; - size_t strcnt; + char str[c1]; + size_t strcnt; # else # define str (startp + 1) # endif - const unsigned char *extra; - int32_t idx; - int32_t elem; - int32_t second; - int32_t hash; + const unsigned char *extra; + int32_t idx; + int32_t elem; + int32_t second; + int32_t hash; # ifdef WIDE_CHAR_VERSION - /* We have to convert the name to a single-byte - string. This is possible since the names - consist of ASCII characters and the internal - representation is UCS4. */ - for (strcnt = 0; strcnt < c1; ++strcnt) - str[strcnt] = startp[1 + strcnt]; + /* We have to convert the name to a single-byte + string. This is possible since the names + consist of ASCII characters and the internal + representation is UCS4. */ + for (strcnt = 0; strcnt < c1; ++strcnt) + str[strcnt] = startp[1 + strcnt]; # endif - table_size = - _NL_CURRENT_WORD (LC_COLLATE, - _NL_COLLATE_SYMB_HASH_SIZEMB); - symb_table = (const int32_t *) - _NL_CURRENT (LC_COLLATE, - _NL_COLLATE_SYMB_TABLEMB); - extra = (const unsigned char *) - _NL_CURRENT (LC_COLLATE, - _NL_COLLATE_SYMB_EXTRAMB); + table_size = + _NL_CURRENT_WORD (LC_COLLATE, + _NL_COLLATE_SYMB_HASH_SIZEMB); + symb_table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_SYMB_TABLEMB); + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_SYMB_EXTRAMB); - /* Locate the character in the hashing table. */ - hash = elem_hash (str, c1); + /* Locate the character in the hashing table. */ + hash = elem_hash (str, c1); - idx = 0; - elem = hash % table_size; - if (symb_table[2 * elem] != 0) - { - second = hash % (table_size - 2) + 1; + idx = 0; + elem = hash % table_size; + if (symb_table[2 * elem] != 0) + { + second = hash % (table_size - 2) + 1; - do - { - /* First compare the hashing value. */ - if (symb_table[2 * elem] == hash - && (c1 - == extra[symb_table[2 * elem + 1]]) - && memcmp (str, - &extra[symb_table[2 * elem - + 1] - + 1], c1) == 0) - { - /* Yep, this is the entry. */ - idx = symb_table[2 * elem + 1]; - idx += 1 + extra[idx]; - break; - } + do + { + /* First compare the hashing value. */ + if (symb_table[2 * elem] == hash + && (c1 + == extra[symb_table[2 * elem + 1]]) + && memcmp (str, + &extra[symb_table[2 * elem + + 1] + + 1], c1) == 0) + { + /* Yep, this is the entry. */ + idx = symb_table[2 * elem + 1]; + idx += 1 + extra[idx]; + break; + } - /* Next entry. */ - elem += second; - } - while (symb_table[2 * elem] != 0); - } + /* Next entry. */ + elem += second; + } + while (symb_table[2 * elem] != 0); + } - if (symb_table[2 * elem] != 0) - { - /* Compare the byte sequence but only if - this is not part of a range. */ + if (symb_table[2 * elem] != 0) + { + /* Compare the byte sequence but only if + this is not part of a range. */ # ifdef WIDE_CHAR_VERSION - int32_t *wextra; + int32_t *wextra; - idx += 1 + extra[idx]; - /* Adjust for the alignment. */ - idx = (idx + 3) & ~3; + idx += 1 + extra[idx]; + /* Adjust for the alignment. */ + idx = (idx + 3) & ~3; - wextra = (int32_t *) &extra[idx + 4]; + wextra = (int32_t *) &extra[idx + 4]; # endif - if (! is_range) - { + if (! is_range) + { # ifdef WIDE_CHAR_VERSION - for (c1 = 0; - (int32_t) c1 < wextra[idx]; - ++c1) - if (n[c1] != wextra[1 + c1]) - break; + for (c1 = 0; + (int32_t) c1 < wextra[idx]; + ++c1) + if (n[c1] != wextra[1 + c1]) + break; - if ((int32_t) c1 == wextra[idx]) - goto matched; + if ((int32_t) c1 == wextra[idx]) + goto matched; # else - for (c1 = 0; c1 < extra[idx]; ++c1) - if (n[c1] != extra[1 + c1]) - break; + for (c1 = 0; c1 < extra[idx]; ++c1) + if (n[c1] != extra[1 + c1]) + break; - if (c1 == extra[idx]) - goto matched; + if (c1 == extra[idx]) + goto matched; # endif - } + } - /* Get the collation sequence value. */ - is_seqval = true; + /* Get the collation sequence value. */ + is_seqval = true; # ifdef WIDE_CHAR_VERSION - cold = wextra[1 + wextra[idx]]; + cold = wextra[1 + wextra[idx]]; # else - /* Adjust for the alignment. */ - idx += 1 + extra[idx]; - idx = (idx + 3) & ~4; - cold = *((int32_t *) &extra[idx]); + /* Adjust for the alignment. */ + idx += 1 + extra[idx]; + idx = (idx + 3) & ~4; + cold = *((int32_t *) &extra[idx]); # endif - c = *p++; - } - else if (c1 == 1) - { - /* No valid character. Match it as a - single byte. */ - if (!is_range && *n == str[0]) - goto matched; + c = *p++; + } + else if (c1 == 1) + { + /* No valid character. Match it as a + single byte. */ + if (!is_range && *n == str[0]) + goto matched; - cold = str[0]; - c = *p++; - } - else - return FNM_NOMATCH; - } - } - else + cold = str[0]; + c = *p++; + } + else + return FNM_NOMATCH; + } + } + else # undef str #endif - { - c = FOLD (c); - normal_bracket: + { + c = FOLD (c); + normal_bracket: - /* We have to handling the symbols differently in - ranges since then the collation sequence is - important. */ - is_range = (*p == L_('-') && p[1] != L_('\0') - && p[1] != L_(']')); + /* We have to handling the symbols differently in + ranges since then the collation sequence is + important. */ + is_range = (*p == L_('-') && p[1] != L_('\0') + && p[1] != L_(']')); - if (!is_range && c == fn) - goto matched; + if (!is_range && c == fn) + goto matched; #if _LIBC - /* This is needed if we goto normal_bracket; from - outside of is_seqval's scope. */ - is_seqval = false; + /* This is needed if we goto normal_bracket; from + outside of is_seqval's scope. */ + is_seqval = false; #endif - cold = c; - c = *p++; - } + cold = c; + c = *p++; + } - if (c == L_('-') && *p != L_(']')) - { + if (c == L_('-') && *p != L_(']')) + { #if _LIBC - /* We have to find the collation sequence - value for C. Collation sequence is nothing - we can regularly access. The sequence - value is defined by the order in which the - definitions of the collation values for the - various characters appear in the source - file. A strange concept, nowhere - documented. */ - uint32_t fcollseq; - uint32_t lcollseq; - UCHAR cend = *p++; + /* We have to find the collation sequence + value for C. Collation sequence is nothing + we can regularly access. The sequence + value is defined by the order in which the + definitions of the collation values for the + various characters appear in the source + file. A strange concept, nowhere + documented. */ + uint32_t fcollseq; + uint32_t lcollseq; + UCHAR cend = *p++; # ifdef WIDE_CHAR_VERSION - /* Search in the `names' array for the characters. */ - fcollseq = __collseq_table_lookup (collseq, fn); - if (fcollseq == ~((uint32_t) 0)) - /* XXX We don't know anything about the character - we are supposed to match. This means we are - failing. */ - goto range_not_matched; + /* Search in the `names' array for the characters. */ + fcollseq = __collseq_table_lookup (collseq, fn); + if (fcollseq == ~((uint32_t) 0)) + /* XXX We don't know anything about the character + we are supposed to match. This means we are + failing. */ + goto range_not_matched; - if (is_seqval) - lcollseq = cold; - else - lcollseq = __collseq_table_lookup (collseq, cold); + if (is_seqval) + lcollseq = cold; + else + lcollseq = __collseq_table_lookup (collseq, cold); # else - fcollseq = collseq[fn]; - lcollseq = is_seqval ? cold : collseq[(UCHAR) cold]; + fcollseq = collseq[fn]; + lcollseq = is_seqval ? cold : collseq[(UCHAR) cold]; # endif - is_seqval = false; - if (cend == L_('[') && *p == L_('.')) - { - uint32_t nrules = - _NL_CURRENT_WORD (LC_COLLATE, - _NL_COLLATE_NRULES); - const CHAR *startp = p; - size_t c1 = 0; + is_seqval = false; + if (cend == L_('[') && *p == L_('.')) + { + uint32_t nrules = + _NL_CURRENT_WORD (LC_COLLATE, + _NL_COLLATE_NRULES); + const CHAR *startp = p; + size_t c1 = 0; - while (1) - { - c = *++p; - if (c == L_('.') && p[1] == L_(']')) - { - p += 2; - break; - } - if (c == '\0') - return FNM_NOMATCH; - ++c1; - } + while (1) + { + c = *++p; + if (c == L_('.') && p[1] == L_(']')) + { + p += 2; + break; + } + if (c == '\0') + return FNM_NOMATCH; + ++c1; + } - if (nrules == 0) - { - /* There are no names defined in the - collation data. Therefore we only - accept the trivial names consisting - of the character itself. */ - if (c1 != 1) - return FNM_NOMATCH; + if (nrules == 0) + { + /* There are no names defined in the + collation data. Therefore we only + accept the trivial names consisting + of the character itself. */ + if (c1 != 1) + return FNM_NOMATCH; - cend = startp[1]; - } - else - { - int32_t table_size; - const int32_t *symb_table; + cend = startp[1]; + } + else + { + int32_t table_size; + const int32_t *symb_table; # ifdef WIDE_CHAR_VERSION - char str[c1]; - size_t strcnt; + char str[c1]; + size_t strcnt; # else # define str (startp + 1) # endif - const unsigned char *extra; - int32_t idx; - int32_t elem; - int32_t second; - int32_t hash; + const unsigned char *extra; + int32_t idx; + int32_t elem; + int32_t second; + int32_t hash; # ifdef WIDE_CHAR_VERSION - /* We have to convert the name to a single-byte - string. This is possible since the names - consist of ASCII characters and the internal - representation is UCS4. */ - for (strcnt = 0; strcnt < c1; ++strcnt) - str[strcnt] = startp[1 + strcnt]; + /* We have to convert the name to a single-byte + string. This is possible since the names + consist of ASCII characters and the internal + representation is UCS4. */ + for (strcnt = 0; strcnt < c1; ++strcnt) + str[strcnt] = startp[1 + strcnt]; # endif - table_size = - _NL_CURRENT_WORD (LC_COLLATE, - _NL_COLLATE_SYMB_HASH_SIZEMB); - symb_table = (const int32_t *) - _NL_CURRENT (LC_COLLATE, - _NL_COLLATE_SYMB_TABLEMB); - extra = (const unsigned char *) - _NL_CURRENT (LC_COLLATE, - _NL_COLLATE_SYMB_EXTRAMB); + table_size = + _NL_CURRENT_WORD (LC_COLLATE, + _NL_COLLATE_SYMB_HASH_SIZEMB); + symb_table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_SYMB_TABLEMB); + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_SYMB_EXTRAMB); - /* Locate the character in the hashing + /* Locate the character in the hashing table. */ - hash = elem_hash (str, c1); + hash = elem_hash (str, c1); - idx = 0; - elem = hash % table_size; - if (symb_table[2 * elem] != 0) - { - second = hash % (table_size - 2) + 1; + idx = 0; + elem = hash % table_size; + if (symb_table[2 * elem] != 0) + { + second = hash % (table_size - 2) + 1; - do - { - /* First compare the hashing value. */ - if (symb_table[2 * elem] == hash - && (c1 - == extra[symb_table[2 * elem + 1]]) - && memcmp (str, - &extra[symb_table[2 * elem + 1] - + 1], c1) == 0) - { - /* Yep, this is the entry. */ - idx = symb_table[2 * elem + 1]; - idx += 1 + extra[idx]; - break; - } + do + { + /* First compare the hashing value. */ + if (symb_table[2 * elem] == hash + && (c1 + == extra[symb_table[2 * elem + 1]]) + && memcmp (str, + &extra[symb_table[2 * elem + 1] + + 1], c1) == 0) + { + /* Yep, this is the entry. */ + idx = symb_table[2 * elem + 1]; + idx += 1 + extra[idx]; + break; + } - /* Next entry. */ - elem += second; - } - while (symb_table[2 * elem] != 0); - } + /* Next entry. */ + elem += second; + } + while (symb_table[2 * elem] != 0); + } - if (symb_table[2 * elem] != 0) - { - /* Compare the byte sequence but only if - this is not part of a range. */ + if (symb_table[2 * elem] != 0) + { + /* Compare the byte sequence but only if + this is not part of a range. */ # ifdef WIDE_CHAR_VERSION - int32_t *wextra; + int32_t *wextra; - idx += 1 + extra[idx]; - /* Adjust for the alignment. */ - idx = (idx + 3) & ~4; + idx += 1 + extra[idx]; + /* Adjust for the alignment. */ + idx = (idx + 3) & ~4; - wextra = (int32_t *) &extra[idx + 4]; + wextra = (int32_t *) &extra[idx + 4]; # endif - /* Get the collation sequence value. */ - is_seqval = true; + /* Get the collation sequence value. */ + is_seqval = true; # ifdef WIDE_CHAR_VERSION - cend = wextra[1 + wextra[idx]]; + cend = wextra[1 + wextra[idx]]; # else - /* Adjust for the alignment. */ - idx += 1 + extra[idx]; - idx = (idx + 3) & ~4; - cend = *((int32_t *) &extra[idx]); + /* Adjust for the alignment. */ + idx += 1 + extra[idx]; + idx = (idx + 3) & ~4; + cend = *((int32_t *) &extra[idx]); # endif - } - else if (symb_table[2 * elem] != 0 && c1 == 1) - { - cend = str[0]; - c = *p++; - } - else - return FNM_NOMATCH; - } + } + else if (symb_table[2 * elem] != 0 && c1 == 1) + { + cend = str[0]; + c = *p++; + } + else + return FNM_NOMATCH; + } # undef str - } - else - { - if (!(flags & FNM_NOESCAPE) && cend == L_('\\')) - cend = *p++; - if (cend == L_('\0')) - return FNM_NOMATCH; - cend = FOLD (cend); - } + } + else + { + if (!(flags & FNM_NOESCAPE) && cend == L_('\\')) + cend = *p++; + if (cend == L_('\0')) + return FNM_NOMATCH; + cend = FOLD (cend); + } - /* XXX It is not entirely clear to me how to handle - characters which are not mentioned in the - collation specification. */ - if ( + /* XXX It is not entirely clear to me how to handle + characters which are not mentioned in the + collation specification. */ + if ( # ifdef WIDE_CHAR_VERSION - lcollseq == 0xffffffff || + lcollseq == 0xffffffff || # endif - lcollseq <= fcollseq) - { - /* We have to look at the upper bound. */ - uint32_t hcollseq; + lcollseq <= fcollseq) + { + /* We have to look at the upper bound. */ + uint32_t hcollseq; - if (is_seqval) - hcollseq = cend; - else - { + if (is_seqval) + hcollseq = cend; + else + { # ifdef WIDE_CHAR_VERSION - hcollseq = - __collseq_table_lookup (collseq, cend); - if (hcollseq == ~((uint32_t) 0)) - { - /* Hum, no information about the upper - bound. The matching succeeds if the - lower bound is matched exactly. */ - if (lcollseq != fcollseq) - goto range_not_matched; + hcollseq = + __collseq_table_lookup (collseq, cend); + if (hcollseq == ~((uint32_t) 0)) + { + /* Hum, no information about the upper + bound. The matching succeeds if the + lower bound is matched exactly. */ + if (lcollseq != fcollseq) + goto range_not_matched; - goto matched; - } + goto matched; + } # else - hcollseq = collseq[cend]; + hcollseq = collseq[cend]; # endif - } + } - if (lcollseq <= hcollseq && fcollseq <= hcollseq) - goto matched; - } + if (lcollseq <= hcollseq && fcollseq <= hcollseq) + goto matched; + } # ifdef WIDE_CHAR_VERSION - range_not_matched: + range_not_matched: # endif #else - /* We use a boring value comparison of the character - values. This is better than comparing using - `strcoll' since the latter would have surprising - and sometimes fatal consequences. */ - UCHAR cend = *p++; + /* We use a boring value comparison of the character + values. This is better than comparing using + `strcoll' since the latter would have surprising + and sometimes fatal consequences. */ + UCHAR cend = *p++; - if (!(flags & FNM_NOESCAPE) && cend == L_('\\')) - cend = *p++; - if (cend == L_('\0')) - return FNM_NOMATCH; + if (!(flags & FNM_NOESCAPE) && cend == L_('\\')) + cend = *p++; + if (cend == L_('\0')) + return FNM_NOMATCH; - /* It is a range. */ - if (cold <= fn && fn <= cend) - goto matched; + /* It is a range. */ + if (cold <= fn && fn <= cend) + goto matched; #endif - c = *p++; - } - } + c = *p++; + } + } - if (c == L_(']')) - break; - } + if (c == L_(']')) + break; + } - if (!not) - return FNM_NOMATCH; - break; + if (!not) + return FNM_NOMATCH; + break; - matched: - /* Skip the rest of the [...] that already matched. */ - do - { - ignore_next: - c = *p++; + matched: + /* Skip the rest of the [...] that already matched. */ + do + { + ignore_next: + c = *p++; - if (c == L_('\0')) - /* [... (unterminated) loses. */ - return FNM_NOMATCH; + if (c == L_('\0')) + /* [... (unterminated) loses. */ + return FNM_NOMATCH; - if (!(flags & FNM_NOESCAPE) && c == L_('\\')) - { - if (*p == L_('\0')) - return FNM_NOMATCH; - /* XXX 1003.2d11 is unclear if this is right. */ - ++p; - } - else if (c == L_('[') && *p == L_(':')) - { - int c1 = 0; - const CHAR *startp = p; + if (!(flags & FNM_NOESCAPE) && c == L_('\\')) + { + if (*p == L_('\0')) + return FNM_NOMATCH; + /* XXX 1003.2d11 is unclear if this is right. */ + ++p; + } + else if (c == L_('[') && *p == L_(':')) + { + int c1 = 0; + const CHAR *startp = p; - while (1) - { - c = *++p; - if (++c1 == CHAR_CLASS_MAX_LENGTH) - return FNM_NOMATCH; + while (1) + { + c = *++p; + if (++c1 == CHAR_CLASS_MAX_LENGTH) + return FNM_NOMATCH; - if (*p == L_(':') && p[1] == L_(']')) - break; + if (*p == L_(':') && p[1] == L_(']')) + break; - if (c < L_('a') || c >= L_('z')) - { - p = startp; - goto ignore_next; - } - } - p += 2; - c = *p++; - } - else if (c == L_('[') && *p == L_('=')) - { - c = *++p; - if (c == L_('\0')) - return FNM_NOMATCH; - c = *++p; - if (c != L_('=') || p[1] != L_(']')) - return FNM_NOMATCH; - p += 2; - c = *p++; - } - else if (c == L_('[') && *p == L_('.')) - { - ++p; - while (1) - { - c = *++p; - if (c == '\0') - return FNM_NOMATCH; + if (c < L_('a') || c >= L_('z')) + { + p = startp; + goto ignore_next; + } + } + p += 2; + c = *p++; + } + else if (c == L_('[') && *p == L_('=')) + { + c = *++p; + if (c == L_('\0')) + return FNM_NOMATCH; + c = *++p; + if (c != L_('=') || p[1] != L_(']')) + return FNM_NOMATCH; + p += 2; + c = *p++; + } + else if (c == L_('[') && *p == L_('.')) + { + ++p; + while (1) + { + c = *++p; + if (c == '\0') + return FNM_NOMATCH; - if (*p == L_('.') && p[1] == L_(']')) - break; - } - p += 2; - c = *p++; - } - } - while (c != L_(']')); - if (not) - return FNM_NOMATCH; - } - break; + if (*p == L_('.') && p[1] == L_(']')) + break; + } + p += 2; + c = *p++; + } + } + while (c != L_(']')); + if (not) + return FNM_NOMATCH; + } + break; - case L_('+'): - case L_('@'): - case L_('!'): - if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(') - { - int res; + case L_('+'): + case L_('@'): + case L_('!'): + if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(') + { + int res; - res = EXT (c, p, n, string_end, no_leading_period, flags); - if (res != -1) - return res; - } - goto normal_match; + res = EXT (c, p, n, string_end, no_leading_period, flags); + if (res != -1) + return res; + } + goto normal_match; - case L_('/'): - if (NO_LEADING_PERIOD (flags)) - { - if (n == string_end || c != (UCHAR) *n) - return FNM_NOMATCH; + case L_('/'): + if (NO_LEADING_PERIOD (flags)) + { + if (n == string_end || c != (UCHAR) *n) + return FNM_NOMATCH; - new_no_leading_period = true; - break; - } - /* FALLTHROUGH */ - default: - normal_match: - if (n == string_end || c != FOLD ((UCHAR) *n)) - return FNM_NOMATCH; - } + new_no_leading_period = true; + break; + } + /* FALLTHROUGH */ + default: + normal_match: + if (n == string_end || c != FOLD ((UCHAR) *n)) + return FNM_NOMATCH; + } no_leading_period = new_no_leading_period; ++n; @@ -984,25 +989,25 @@ END (const CHAR *pattern) return pattern; else if (*p == L_('[')) { - /* Handle brackets special. */ - if (posixly_correct == 0) - posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1; + /* Handle brackets special. */ + if (posixly_correct == 0) + posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1; - /* Skip the not sign. We have to recognize it because of a possibly - following ']'. */ - if (*++p == L_('!') || (posixly_correct < 0 && *p == L_('^'))) - ++p; - /* A leading ']' is recognized as such. */ - if (*p == L_(']')) - ++p; - /* Skip over all characters of the list. */ - while (*p != L_(']')) - if (*p++ == L_('\0')) - /* This is no valid pattern. */ - return pattern; + /* Skip the not sign. We have to recognize it because of a possibly + following ']'. */ + if (*++p == L_('!') || (posixly_correct < 0 && *p == L_('^'))) + ++p; + /* A leading ']' is recognized as such. */ + if (*p == L_(']')) + ++p; + /* Skip over all characters of the list. */ + while (*p != L_(']')) + if (*p++ == L_('\0')) + /* This is no valid pattern. */ + return pattern; } else if ((*p == L_('?') || *p == L_('*') || *p == L_('+') || *p == L_('@') - || *p == L_('!')) && p[1] == L_('(')) + || *p == L_('!')) && p[1] == L_('(')) p = END (p + 1); else if (*p == L_(')')) break; @@ -1037,64 +1042,63 @@ EXT (INT opt, const CHAR *pattern, const CHAR *string, const CHAR *string_end, return -1; else if (*p == L_('[')) { - /* Handle brackets special. */ - if (posixly_correct == 0) - posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1; + /* Handle brackets special. */ + if (posixly_correct == 0) + posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1; - /* Skip the not sign. We have to recognize it because of a possibly - following ']'. */ - if (*++p == L_('!') || (posixly_correct < 0 && *p == L_('^'))) - ++p; - /* A leading ']' is recognized as such. */ - if (*p == L_(']')) - ++p; - /* Skip over all characters of the list. */ - while (*p != L_(']')) - if (*p++ == L_('\0')) - /* This is no valid pattern. */ - return -1; + /* Skip the not sign. We have to recognize it because of a possibly + following ']'. */ + if (*++p == L_('!') || (posixly_correct < 0 && *p == L_('^'))) + ++p; + /* A leading ']' is recognized as such. */ + if (*p == L_(']')) + ++p; + /* Skip over all characters of the list. */ + while (*p != L_(']')) + if (*p++ == L_('\0')) + /* This is no valid pattern. */ + return -1; } else if ((*p == L_('?') || *p == L_('*') || *p == L_('+') || *p == L_('@') - || *p == L_('!')) && p[1] == L_('(')) + || *p == L_('!')) && p[1] == L_('(')) /* Remember the nesting level. */ ++level; else if (*p == L_(')')) { - if (level-- == 0) - { - /* This means we found the end of the pattern. */ + if (level-- == 0) + { + /* This means we found the end of the pattern. */ #define NEW_PATTERN \ - struct patternlist *newp; \ - size_t plen; \ - size_t plensize; \ - size_t newpsize; \ - \ - assert (p > startp); \ - plen = (opt == L_('?') || opt == L_('@') \ - ? pattern_len \ - : (unsigned) (p - startp) + 1); \ - plensize = plen * sizeof (CHAR); \ - newpsize = offsetof (struct patternlist, str) + plensize; \ - if ((size_t) -1 / sizeof (CHAR) < plen \ - || newpsize < offsetof (struct patternlist, str) \ - || ALLOCA_LIMIT <= newpsize) \ - return -1; \ - newp = (struct patternlist *) alloca (newpsize); \ - *((CHAR *) MEMPCPY (newp->str, startp, p - startp)) = L_('\0'); \ - newp->next = NULL; \ - *lastp = newp; \ - lastp = &newp->next - NEW_PATTERN; - break; - } + struct patternlist *newp; \ + size_t plen; \ + size_t plensize; \ + size_t newpsize; \ + \ + plen = (opt == L_('?') || opt == L_('@') \ + ? pattern_len \ + : p - startp + 1UL); \ + plensize = plen * sizeof (CHAR); \ + newpsize = offsetof (struct patternlist, str) + plensize; \ + if ((size_t) -1 / sizeof (CHAR) < plen \ + || newpsize < offsetof (struct patternlist, str) \ + || ALLOCA_LIMIT <= newpsize) \ + return -1; \ + newp = (struct patternlist *) alloca (newpsize); \ + *((CHAR *) MEMPCPY (newp->str, startp, p - startp)) = L_('\0'); \ + newp->next = NULL; \ + *lastp = newp; \ + lastp = &newp->next + NEW_PATTERN; + break; + } } else if (*p == L_('|')) { - if (level == 0) - { - NEW_PATTERN; - startp = p + 1; - } + if (level == 0) + { + NEW_PATTERN; + startp = p + 1; + } } assert (list != NULL); assert (p[-1] == L_(')')); @@ -1104,36 +1108,36 @@ EXT (INT opt, const CHAR *pattern, const CHAR *string, const CHAR *string_end, { case L_('*'): if (FCT (p, string, string_end, no_leading_period, flags) == 0) - return 0; + return 0; /* FALLTHROUGH */ case L_('+'): do - { - for (rs = string; rs <= string_end; ++rs) - /* First match the prefix with the current pattern with the - current pattern. */ - if (FCT (list->str, string, rs, no_leading_period, - flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) == 0 - /* This was successful. Now match the rest with the rest - of the pattern. */ - && (FCT (p, rs, string_end, - rs == string - ? no_leading_period - : rs[-1] == '/' && NO_LEADING_PERIOD (flags), - flags & FNM_FILE_NAME - ? flags : flags & ~FNM_PERIOD) == 0 - /* This didn't work. Try the whole pattern. */ - || (rs != string - && FCT (pattern - 1, rs, string_end, - rs == string - ? no_leading_period - : rs[-1] == '/' && NO_LEADING_PERIOD (flags), - flags & FNM_FILE_NAME - ? flags : flags & ~FNM_PERIOD) == 0))) - /* It worked. Signal success. */ - return 0; - } + { + for (rs = string; rs <= string_end; ++rs) + /* First match the prefix with the current pattern with the + current pattern. */ + if (FCT (list->str, string, rs, no_leading_period, + flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) == 0 + /* This was successful. Now match the rest with the rest + of the pattern. */ + && (FCT (p, rs, string_end, + rs == string + ? no_leading_period + : rs[-1] == '/' && NO_LEADING_PERIOD (flags), + flags & FNM_FILE_NAME + ? flags : flags & ~FNM_PERIOD) == 0 + /* This didn't work. Try the whole pattern. */ + || (rs != string + && FCT (pattern - 1, rs, string_end, + rs == string + ? no_leading_period + : rs[-1] == '/' && NO_LEADING_PERIOD (flags), + flags & FNM_FILE_NAME + ? flags : flags & ~FNM_PERIOD) == 0))) + /* It worked. Signal success. */ + return 0; + } while ((list = list->next) != NULL); /* None of the patterns lead to a match. */ @@ -1141,20 +1145,20 @@ EXT (INT opt, const CHAR *pattern, const CHAR *string, const CHAR *string_end, case L_('?'): if (FCT (p, string, string_end, no_leading_period, flags) == 0) - return 0; + return 0; /* FALLTHROUGH */ case L_('@'): do - /* I cannot believe it but `strcat' is actually acceptable - here. Match the entire string with the prefix from the - pattern list and the rest of the pattern following the - pattern list. */ - if (FCT (STRCAT (list->str, p), string, string_end, - no_leading_period, - flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) == 0) - /* It worked. Signal success. */ - return 0; + /* I cannot believe it but `strcat' is actually acceptable + here. Match the entire string with the prefix from the + pattern list and the rest of the pattern following the + pattern list. */ + if (FCT (STRCAT (list->str, p), string, string_end, + no_leading_period, + flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) == 0) + /* It worked. Signal success. */ + return 0; while ((list = list->next) != NULL); /* None of the patterns lead to a match. */ @@ -1162,28 +1166,28 @@ EXT (INT opt, const CHAR *pattern, const CHAR *string, const CHAR *string_end, case L_('!'): for (rs = string; rs <= string_end; ++rs) - { - struct patternlist *runp; + { + struct patternlist *runp; - for (runp = list; runp != NULL; runp = runp->next) - if (FCT (runp->str, string, rs, no_leading_period, - flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) == 0) - break; + for (runp = list; runp != NULL; runp = runp->next) + if (FCT (runp->str, string, rs, no_leading_period, + flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) == 0) + break; - /* If none of the patterns matched see whether the rest does. */ - if (runp == NULL - && (FCT (p, rs, string_end, - rs == string - ? no_leading_period - : rs[-1] == '/' && NO_LEADING_PERIOD (flags), - flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) - == 0)) - /* This is successful. */ - return 0; - } + /* If none of the patterns matched see whether the rest does. */ + if (runp == NULL + && (FCT (p, rs, string_end, + rs == string + ? no_leading_period + : rs[-1] == '/' && NO_LEADING_PERIOD (flags), + flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) + == 0)) + /* This is successful. */ + return 0; + } /* None of the patterns together with the rest of the pattern - lead to a match. */ + lead to a match. */ return FNM_NOMATCH; default: diff --git a/gnulib/getdelim.c b/gnulib/getdelim.c index 12f2167c9..c0240900c 100644 --- a/gnulib/getdelim.c +++ b/gnulib/getdelim.c @@ -1,6 +1,6 @@ /* getdelim.c --- Implementation of replacement getdelim function. - Copyright (C) 1994, 1996, 1997, 1998, 2001, 2003, 2005, 2006, 2007, - 2008, 2009 Free Software Foundation, Inc. + Copyright (C) 1994, 1996, 1997, 1998, 2001, 2003, 2005, 2006, 2007, 2008, + 2009, 2010 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 @@ -21,6 +21,10 @@ #include +/* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc + optimizes away the lineptr == NULL || n == NULL || fp == NULL tests below. */ +#define _GL_ARG_NONNULL(params) + #include #include @@ -34,15 +38,15 @@ #if USE_UNLOCKED_IO # include "unlocked-io.h" -# define getc_maybe_unlocked(fp) getc(fp) +# define getc_maybe_unlocked(fp) getc(fp) #elif !HAVE_FLOCKFILE || !HAVE_FUNLOCKFILE || !HAVE_DECL_GETC_UNLOCKED # undef flockfile # undef funlockfile # define flockfile(x) ((void) 0) # define funlockfile(x) ((void) 0) -# define getc_maybe_unlocked(fp) getc(fp) +# define getc_maybe_unlocked(fp) getc(fp) #else -# define getc_maybe_unlocked(fp) getc_unlocked(fp) +# define getc_maybe_unlocked(fp) getc_unlocked(fp) #endif /* Read up to (and including) a DELIMITER from FP into *LINEPTR (and @@ -71,10 +75,10 @@ getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp) *n = 120; new_lineptr = (char *) realloc (*lineptr, *n); if (new_lineptr == NULL) - { - result = -1; - goto unlock_return; - } + { + result = -1; + goto unlock_return; + } *lineptr = new_lineptr; } @@ -84,44 +88,44 @@ getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp) i = getc_maybe_unlocked (fp); if (i == EOF) - { - result = -1; - break; - } + { + result = -1; + break; + } /* Make enough space for len+1 (for final NUL) bytes. */ if (cur_len + 1 >= *n) - { - size_t needed_max = - SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX; - size_t needed = 2 * *n + 1; /* Be generous. */ - char *new_lineptr; + { + size_t needed_max = + SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX; + size_t needed = 2 * *n + 1; /* Be generous. */ + char *new_lineptr; - if (needed_max < needed) - needed = needed_max; - if (cur_len + 1 >= needed) - { - result = -1; - errno = EOVERFLOW; - goto unlock_return; - } + if (needed_max < needed) + needed = needed_max; + if (cur_len + 1 >= needed) + { + result = -1; + errno = EOVERFLOW; + goto unlock_return; + } - new_lineptr = (char *) realloc (*lineptr, needed); - if (new_lineptr == NULL) - { - result = -1; - goto unlock_return; - } + new_lineptr = (char *) realloc (*lineptr, needed); + if (new_lineptr == NULL) + { + result = -1; + goto unlock_return; + } - *lineptr = new_lineptr; - *n = needed; - } + *lineptr = new_lineptr; + *n = needed; + } (*lineptr)[cur_len] = i; cur_len++; if (i == delimiter) - break; + break; } (*lineptr)[cur_len] = '\0'; result = cur_len ? cur_len : result; diff --git a/gnulib/getline.c b/gnulib/getline.c index eb8f055a8..6cf187be2 100644 --- a/gnulib/getline.c +++ b/gnulib/getline.c @@ -1,5 +1,5 @@ /* getline.c --- Implementation of replacement getline function. - Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. + Copyright (C) 2005, 2006, 2007, 2009, 2010 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 diff --git a/gnulib/getopt.c b/gnulib/getopt.c index f1e6d1f7c..aaabc8d19 100644 --- a/gnulib/getopt.c +++ b/gnulib/getopt.c @@ -1,9 +1,9 @@ /* Getopt for GNU. - NOTE: getopt is now part of the C library, so if you don't know what + NOTE: getopt is part of the C library, so if you don't know what "Keep this file name-space clean" means, talk to drepper@gnu.org before changing it! - Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001,2002,2003,2004,2006,2008 - Free Software Foundation, Inc. + Copyright (C) 1987-1996, 1998-2004, 2006, 2008-2010 Free Software + Foundation, Inc. This file is part of the GNU C Library. This program is free software: you can redistribute it and/or modify @@ -41,12 +41,9 @@ # include #endif -#ifndef attribute_hidden -# define attribute_hidden -#endif - -/* Unlike standard Unix `getopt', functions like `getopt_long' - let the user intersperse the options with the other arguments. +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. As `getopt_long' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus @@ -54,7 +51,7 @@ Using `getopt' or setting the environment variable POSIXLY_CORRECT disables permutation. - Then the application's behavior is completely standard. + Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ @@ -121,18 +118,18 @@ extern char *__getopt_nonoption_flags; # ifdef USE_NONOPTION_FLAGS # define SWAP_FLAGS(ch1, ch2) \ - if (d->__nonoption_flags_len > 0) \ - { \ - char __tmp = __getopt_nonoption_flags[ch1]; \ - __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ - __getopt_nonoption_flags[ch2] = __tmp; \ + if (d->__nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ } # else # define SWAP_FLAGS(ch1, ch2) # endif -#else /* !_LIBC */ +#else /* !_LIBC */ # define SWAP_FLAGS(ch1, ch2) -#endif /* _LIBC */ +#endif /* _LIBC */ /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) @@ -163,57 +160,57 @@ exchange (char **argv, struct _getopt_data *d) if (d->__nonoption_flags_len > 0 && top >= d->__nonoption_flags_max_len) { /* We must extend the array. The user plays games with us and - presents new arguments. */ + presents new arguments. */ char *new_str = malloc (top + 1); if (new_str == NULL) - d->__nonoption_flags_len = d->__nonoption_flags_max_len = 0; + d->__nonoption_flags_len = d->__nonoption_flags_max_len = 0; else - { - memset (__mempcpy (new_str, __getopt_nonoption_flags, - d->__nonoption_flags_max_len), - '\0', top + 1 - d->__nonoption_flags_max_len); - d->__nonoption_flags_max_len = top + 1; - __getopt_nonoption_flags = new_str; - } + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + d->__nonoption_flags_max_len), + '\0', top + 1 - d->__nonoption_flags_max_len); + d->__nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } } #endif while (top > middle && middle > bottom) { if (top - middle > middle - bottom) - { - /* Bottom segment is the short one. */ - int len = middle - bottom; - register int i; + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } else - { - /* Top segment is the short one. */ - int len = top - middle; - register int i; + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - SWAP_FLAGS (bottom + i, middle + i); - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } } /* Update records for the slots the non-options now occupy. */ @@ -225,8 +222,9 @@ exchange (char **argv, struct _getopt_data *d) /* Initialize the internal data when the first call is made. */ static const char * -_getopt_initialize (int argc, char **argv, const char *optstring, - int posixly_correct, struct _getopt_data *d) +_getopt_initialize (int argc _GL_UNUSED, + char **argv _GL_UNUSED, const char *optstring, + struct _getopt_data *d, int posixly_correct) { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped @@ -260,25 +258,25 @@ _getopt_initialize (int argc, char **argv, const char *optstring, && argc == __libc_argc && argv == __libc_argv) { if (d->__nonoption_flags_max_len == 0) - { - if (__getopt_nonoption_flags == NULL - || __getopt_nonoption_flags[0] == '\0') - d->__nonoption_flags_max_len = -1; - else - { - const char *orig_str = __getopt_nonoption_flags; - int len = d->__nonoption_flags_max_len = strlen (orig_str); - if (d->__nonoption_flags_max_len < argc) - d->__nonoption_flags_max_len = argc; - __getopt_nonoption_flags = - (char *) malloc (d->__nonoption_flags_max_len); - if (__getopt_nonoption_flags == NULL) - d->__nonoption_flags_max_len = -1; - else - memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), - '\0', d->__nonoption_flags_max_len - len); - } - } + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + d->__nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = d->__nonoption_flags_max_len = strlen (orig_str); + if (d->__nonoption_flags_max_len < argc) + d->__nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (d->__nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + d->__nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', d->__nonoption_flags_max_len - len); + } + } d->__nonoption_flags_len = d->__nonoption_flags_max_len; } else @@ -330,6 +328,10 @@ _getopt_initialize (int argc, char **argv, const char *optstring, `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. @@ -338,15 +340,12 @@ _getopt_initialize (int argc, char **argv, const char *optstring, recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce - long-named options. - - If POSIXLY_CORRECT is nonzero, behave as if the POSIXLY_CORRECT - environment variable were set. */ + long-named options. */ int _getopt_internal_r (int argc, char **argv, const char *optstring, - const struct option *longopts, int *longind, - int long_only, int posixly_correct, struct _getopt_data *d) + const struct option *longopts, int *longind, + int long_only, struct _getopt_data *d, int posixly_correct) { int print_errors = d->opterr; if (optstring[0] == ':') @@ -360,9 +359,9 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, if (d->optind == 0 || !d->__initialized) { if (d->optind == 0) - d->optind = 1; /* Don't scan ARGV[0], the program name. */ - optstring = _getopt_initialize (argc, argv, optstring, - posixly_correct, d); + d->optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring, d, + posixly_correct); d->__initialized = 1; } @@ -372,8 +371,8 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, is only used when the used in the GNU libc. */ #if defined _LIBC && defined USE_NONOPTION_FLAGS # define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0' \ - || (d->optind < d->__nonoption_flags_len \ - && __getopt_nonoption_flags[d->optind] == '1')) + || (d->optind < d->__nonoption_flags_len \ + && __getopt_nonoption_flags[d->optind] == '1')) #else # define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0') #endif @@ -383,78 +382,78 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, /* Advance to the next ARGV-element. */ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been - moved back by the user (who may also have changed the arguments). */ + moved back by the user (who may also have changed the arguments). */ if (d->__last_nonopt > d->optind) - d->__last_nonopt = d->optind; + d->__last_nonopt = d->optind; if (d->__first_nonopt > d->optind) - d->__first_nonopt = d->optind; + d->__first_nonopt = d->optind; if (d->__ordering == PERMUTE) - { - /* If we have just processed some options following some non-options, - exchange them so that the options come first. */ + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ - if (d->__first_nonopt != d->__last_nonopt - && d->__last_nonopt != d->optind) - exchange ((char **) argv, d); - else if (d->__last_nonopt != d->optind) - d->__first_nonopt = d->optind; + if (d->__first_nonopt != d->__last_nonopt + && d->__last_nonopt != d->optind) + exchange ((char **) argv, d); + else if (d->__last_nonopt != d->optind) + d->__first_nonopt = d->optind; - /* Skip any additional non-options - and extend the range of non-options previously skipped. */ + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ - while (d->optind < argc && NONOPTION_P) - d->optind++; - d->__last_nonopt = d->optind; - } + while (d->optind < argc && NONOPTION_P) + d->optind++; + d->__last_nonopt = d->optind; + } /* The special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an option, - then skip everything else like a non-option. */ + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ if (d->optind != argc && !strcmp (argv[d->optind], "--")) - { - d->optind++; + { + d->optind++; - if (d->__first_nonopt != d->__last_nonopt - && d->__last_nonopt != d->optind) - exchange ((char **) argv, d); - else if (d->__first_nonopt == d->__last_nonopt) - d->__first_nonopt = d->optind; - d->__last_nonopt = argc; + if (d->__first_nonopt != d->__last_nonopt + && d->__last_nonopt != d->optind) + exchange ((char **) argv, d); + else if (d->__first_nonopt == d->__last_nonopt) + d->__first_nonopt = d->optind; + d->__last_nonopt = argc; - d->optind = argc; - } + d->optind = argc; + } /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ + and back over any non-options that we skipped and permuted. */ if (d->optind == argc) - { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest them. */ - if (d->__first_nonopt != d->__last_nonopt) - d->optind = d->__first_nonopt; - return -1; - } + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (d->__first_nonopt != d->__last_nonopt) + d->optind = d->__first_nonopt; + return -1; + } /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it by. */ + either stop the scan or describe it to the caller and pass it by. */ if (NONOPTION_P) - { - if (d->__ordering == REQUIRE_ORDER) - return -1; - d->optarg = argv[d->optind++]; - return 1; - } + { + if (d->__ordering == REQUIRE_ORDER) + return -1; + d->optarg = argv[d->optind++]; + return 1; + } /* We have found another option-ARGV-element. - Skip the initial punctuation. */ + Skip the initial punctuation. */ d->__nextchar = (argv[d->optind] + 1 - + (longopts != NULL && argv[d->optind][1] == '-')); + + (longopts != NULL && argv[d->optind][1] == '-')); } /* Decode the current option-ARGV-element. */ @@ -474,8 +473,8 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, if (longopts != NULL && (argv[d->optind][1] == '-' - || (long_only && (argv[d->optind][2] - || !strchr (optstring, argv[d->optind][1]))))) + || (long_only && (argv[d->optind][2] + || !strchr (optstring, argv[d->optind][1]))))) { char *nameend; const struct option *p; @@ -486,251 +485,251 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, int option_index; for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; + /* Do nothing. */ ; /* Test all long options for either exact match - or abbreviated matches. */ + or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) - { - if ((unsigned int) (nameend - d->__nextchar) - == (unsigned int) strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else if (long_only - || pfound->has_arg != p->has_arg - || pfound->flag != p->flag - || pfound->val != p->val) - /* Second or later nonexact match found. */ - ambig = 1; - } + if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) + { + if ((unsigned int) (nameend - d->__nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else if (long_only + || pfound->has_arg != p->has_arg + || pfound->flag != p->flag + || pfound->val != p->val) + /* Second or later nonexact match found. */ + ambig = 1; + } if (ambig && !exact) - { - if (print_errors) - { + { + if (print_errors) + { #if defined _LIBC && defined USE_IN_LIBIO - char *buf; + char *buf; - if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"), - argv[0], argv[d->optind]) >= 0) - { - _IO_flockfile (stderr); + if (__asprintf (&buf, _("%s: option '%s' is ambiguous\n"), + argv[0], argv[d->optind]) >= 0) + { + _IO_flockfile (stderr); - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - __fxprintf (NULL, "%s", buf); + __fxprintf (NULL, "%s", buf); - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); - free (buf); - } + free (buf); + } #else - fprintf (stderr, _("%s: option `%s' is ambiguous\n"), - argv[0], argv[d->optind]); + fprintf (stderr, _("%s: option '%s' is ambiguous\n"), + argv[0], argv[d->optind]); #endif - } - d->__nextchar += strlen (d->__nextchar); - d->optind++; - d->optopt = 0; - return '?'; - } + } + d->__nextchar += strlen (d->__nextchar); + d->optind++; + d->optopt = 0; + return '?'; + } if (pfound != NULL) - { - option_index = indfound; - d->optind++; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - d->optarg = nameend + 1; - else - { - if (print_errors) - { + { + option_index = indfound; + d->optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + d->optarg = nameend + 1; + else + { + if (print_errors) + { #if defined _LIBC && defined USE_IN_LIBIO - char *buf; - int n; + char *buf; + int n; #endif - if (argv[d->optind - 1][1] == '-') - { - /* --option */ + if (argv[d->optind - 1][1] == '-') + { + /* --option */ #if defined _LIBC && defined USE_IN_LIBIO - n = __asprintf (&buf, _("\ -%s: option `--%s' doesn't allow an argument\n"), - argv[0], pfound->name); + n = __asprintf (&buf, _("\ +%s: option '--%s' doesn't allow an argument\n"), + argv[0], pfound->name); #else - fprintf (stderr, _("\ -%s: option `--%s' doesn't allow an argument\n"), - argv[0], pfound->name); + fprintf (stderr, _("\ +%s: option '--%s' doesn't allow an argument\n"), + argv[0], pfound->name); #endif - } - else - { - /* +option or -option */ + } + else + { + /* +option or -option */ #if defined _LIBC && defined USE_IN_LIBIO - n = __asprintf (&buf, _("\ -%s: option `%c%s' doesn't allow an argument\n"), - argv[0], argv[d->optind - 1][0], - pfound->name); + n = __asprintf (&buf, _("\ +%s: option '%c%s' doesn't allow an argument\n"), + argv[0], argv[d->optind - 1][0], + pfound->name); #else - fprintf (stderr, _("\ -%s: option `%c%s' doesn't allow an argument\n"), - argv[0], argv[d->optind - 1][0], - pfound->name); + fprintf (stderr, _("\ +%s: option '%c%s' doesn't allow an argument\n"), + argv[0], argv[d->optind - 1][0], + pfound->name); #endif - } + } #if defined _LIBC && defined USE_IN_LIBIO - if (n >= 0) - { - _IO_flockfile (stderr); + if (n >= 0) + { + _IO_flockfile (stderr); - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 - |= _IO_FLAGS2_NOTCANCEL; + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 + |= _IO_FLAGS2_NOTCANCEL; - __fxprintf (NULL, "%s", buf); + __fxprintf (NULL, "%s", buf); - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); - free (buf); - } + free (buf); + } #endif - } + } - d->__nextchar += strlen (d->__nextchar); + d->__nextchar += strlen (d->__nextchar); - d->optopt = pfound->val; - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (d->optind < argc) - d->optarg = argv[d->optind++]; - else - { - if (print_errors) - { + d->optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (d->optind < argc) + d->optarg = argv[d->optind++]; + else + { + if (print_errors) + { #if defined _LIBC && defined USE_IN_LIBIO - char *buf; + char *buf; - if (__asprintf (&buf, _("\ -%s: option `%s' requires an argument\n"), - argv[0], argv[d->optind - 1]) >= 0) - { - _IO_flockfile (stderr); + if (__asprintf (&buf, _("\ +%s: option '%s' requires an argument\n"), + argv[0], argv[d->optind - 1]) >= 0) + { + _IO_flockfile (stderr); - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 - |= _IO_FLAGS2_NOTCANCEL; + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 + |= _IO_FLAGS2_NOTCANCEL; - __fxprintf (NULL, "%s", buf); + __fxprintf (NULL, "%s", buf); - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); - free (buf); - } + free (buf); + } #else - fprintf (stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[d->optind - 1]); + fprintf (stderr, + _("%s: option '%s' requires an argument\n"), + argv[0], argv[d->optind - 1]); #endif - } - d->__nextchar += strlen (d->__nextchar); - d->optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - d->__nextchar += strlen (d->__nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } + } + d->__nextchar += strlen (d->__nextchar); + d->optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + d->__nextchar += strlen (d->__nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } /* Can't find it as a long option. If this is not getopt_long_only, - or the option starts with '--' or is not a valid short - option, then it's an error. - Otherwise interpret it as a short option. */ + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ if (!long_only || argv[d->optind][1] == '-' - || strchr (optstring, *d->__nextchar) == NULL) - { - if (print_errors) - { + || strchr (optstring, *d->__nextchar) == NULL) + { + if (print_errors) + { #if defined _LIBC && defined USE_IN_LIBIO - char *buf; - int n; + char *buf; + int n; #endif - if (argv[d->optind][1] == '-') - { - /* --option */ + if (argv[d->optind][1] == '-') + { + /* --option */ #if defined _LIBC && defined USE_IN_LIBIO - n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"), - argv[0], d->__nextchar); + n = __asprintf (&buf, _("%s: unrecognized option '--%s'\n"), + argv[0], d->__nextchar); #else - fprintf (stderr, _("%s: unrecognized option `--%s'\n"), - argv[0], d->__nextchar); + fprintf (stderr, _("%s: unrecognized option '--%s'\n"), + argv[0], d->__nextchar); #endif - } - else - { - /* +option or -option */ + } + else + { + /* +option or -option */ #if defined _LIBC && defined USE_IN_LIBIO - n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"), - argv[0], argv[d->optind][0], d->__nextchar); + n = __asprintf (&buf, _("%s: unrecognized option '%c%s'\n"), + argv[0], argv[d->optind][0], d->__nextchar); #else - fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), - argv[0], argv[d->optind][0], d->__nextchar); + fprintf (stderr, _("%s: unrecognized option '%c%s'\n"), + argv[0], argv[d->optind][0], d->__nextchar); #endif - } + } #if defined _LIBC && defined USE_IN_LIBIO - if (n >= 0) - { - _IO_flockfile (stderr); + if (n >= 0) + { + _IO_flockfile (stderr); - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - __fxprintf (NULL, "%s", buf); + __fxprintf (NULL, "%s", buf); - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); - free (buf); - } + free (buf); + } #endif - } - d->__nextchar = (char *) ""; - d->optind++; - d->optopt = 0; - return '?'; - } + } + d->__nextchar = (char *) ""; + d->optind++; + d->optopt = 0; + return '?'; + } } /* Look at and handle the next short option-character. */ @@ -745,335 +744,321 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, if (temp == NULL || c == ':') { - if (print_errors) - { + if (print_errors) + { #if defined _LIBC && defined USE_IN_LIBIO - char *buf; - int n; + char *buf; + int n; #endif - if (d->__posixly_correct) - { - /* 1003.2 specifies the format of this message. */ #if defined _LIBC && defined USE_IN_LIBIO - n = __asprintf (&buf, _("%s: illegal option -- %c\n"), - argv[0], c); + n = __asprintf (&buf, _("%s: invalid option -- '%c'\n"), + argv[0], c); #else - fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); + fprintf (stderr, _("%s: invalid option -- '%c'\n"), argv[0], c); #endif - } - else - { -#if defined _LIBC && defined USE_IN_LIBIO - n = __asprintf (&buf, _("%s: invalid option -- %c\n"), - argv[0], c); -#else - fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); -#endif - } #if defined _LIBC && defined USE_IN_LIBIO - if (n >= 0) - { - _IO_flockfile (stderr); + if (n >= 0) + { + _IO_flockfile (stderr); - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - __fxprintf (NULL, "%s", buf); + __fxprintf (NULL, "%s", buf); - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); - free (buf); - } + free (buf); + } #endif - } - d->optopt = c; - return '?'; + } + d->optopt = c; + return '?'; } /* Convenience. Treat POSIX -W foo same as long option --foo */ if (temp[0] == 'W' && temp[1] == ';') { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = 0; - int option_index; + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; - /* This is an option that requires an argument. */ - if (*d->__nextchar != '\0') - { - d->optarg = d->__nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - d->optind++; - } - else if (d->optind == argc) - { - if (print_errors) - { - /* 1003.2 specifies the format of this message. */ + /* This is an option that requires an argument. */ + if (*d->__nextchar != '\0') + { + d->optarg = d->__nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + d->optind++; + } + else if (d->optind == argc) + { + if (print_errors) + { #if defined _LIBC && defined USE_IN_LIBIO - char *buf; + char *buf; - if (__asprintf (&buf, - _("%s: option requires an argument -- %c\n"), - argv[0], c) >= 0) - { - _IO_flockfile (stderr); + if (__asprintf (&buf, + _("%s: option requires an argument -- '%c'\n"), + argv[0], c) >= 0) + { + _IO_flockfile (stderr); - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - __fxprintf (NULL, "%s", buf); + __fxprintf (NULL, "%s", buf); - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); - free (buf); - } + free (buf); + } #else - fprintf (stderr, _("%s: option requires an argument -- %c\n"), - argv[0], c); + fprintf (stderr, + _("%s: option requires an argument -- '%c'\n"), + argv[0], c); #endif - } - d->optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - return c; - } - else - /* We already incremented `d->optind' once; - increment it again when taking next ARGV-elt as argument. */ - d->optarg = argv[d->optind++]; + } + d->optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `d->optind' once; + increment it again when taking next ARGV-elt as argument. */ + d->optarg = argv[d->optind++]; - /* optarg is now the argument, see if it's in the - table of longopts. */ + /* optarg is now the argument, see if it's in the + table of longopts. */ - for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '='; - nameend++) - /* Do nothing. */ ; + for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '='; + nameend++) + /* Do nothing. */ ; - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) - { - if ((unsigned int) (nameend - d->__nextchar) == strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second or later nonexact match found. */ - ambig = 1; - } - if (ambig && !exact) - { - if (print_errors) - { + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) + { + if ((unsigned int) (nameend - d->__nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (print_errors) + { #if defined _LIBC && defined USE_IN_LIBIO - char *buf; + char *buf; - if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"), - argv[0], argv[d->optind]) >= 0) - { - _IO_flockfile (stderr); + if (__asprintf (&buf, _("%s: option '-W %s' is ambiguous\n"), + argv[0], argv[d->optind]) >= 0) + { + _IO_flockfile (stderr); - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - __fxprintf (NULL, "%s", buf); + __fxprintf (NULL, "%s", buf); - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); - free (buf); - } + free (buf); + } #else - fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), - argv[0], argv[d->optind]); + fprintf (stderr, _("%s: option '-W %s' is ambiguous\n"), + argv[0], argv[d->optind]); #endif - } - d->__nextchar += strlen (d->__nextchar); - d->optind++; - return '?'; - } - if (pfound != NULL) - { - option_index = indfound; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - d->optarg = nameend + 1; - else - { - if (print_errors) - { + } + d->__nextchar += strlen (d->__nextchar); + d->optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + d->optarg = nameend + 1; + else + { + if (print_errors) + { #if defined _LIBC && defined USE_IN_LIBIO - char *buf; + char *buf; - if (__asprintf (&buf, _("\ -%s: option `-W %s' doesn't allow an argument\n"), - argv[0], pfound->name) >= 0) - { - _IO_flockfile (stderr); + if (__asprintf (&buf, _("\ +%s: option '-W %s' doesn't allow an argument\n"), + argv[0], pfound->name) >= 0) + { + _IO_flockfile (stderr); - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 - |= _IO_FLAGS2_NOTCANCEL; + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 + |= _IO_FLAGS2_NOTCANCEL; - __fxprintf (NULL, "%s", buf); + __fxprintf (NULL, "%s", buf); - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); - free (buf); - } + free (buf); + } #else - fprintf (stderr, _("\ -%s: option `-W %s' doesn't allow an argument\n"), - argv[0], pfound->name); + fprintf (stderr, _("\ +%s: option '-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); #endif - } + } - d->__nextchar += strlen (d->__nextchar); - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (d->optind < argc) - d->optarg = argv[d->optind++]; - else - { - if (print_errors) - { + d->__nextchar += strlen (d->__nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (d->optind < argc) + d->optarg = argv[d->optind++]; + else + { + if (print_errors) + { #if defined _LIBC && defined USE_IN_LIBIO - char *buf; + char *buf; - if (__asprintf (&buf, _("\ -%s: option `%s' requires an argument\n"), - argv[0], argv[d->optind - 1]) >= 0) - { - _IO_flockfile (stderr); + if (__asprintf (&buf, _("\ +%s: option '%s' requires an argument\n"), + argv[0], argv[d->optind - 1]) >= 0) + { + _IO_flockfile (stderr); - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 - |= _IO_FLAGS2_NOTCANCEL; + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 + |= _IO_FLAGS2_NOTCANCEL; - __fxprintf (NULL, "%s", buf); + __fxprintf (NULL, "%s", buf); - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); - free (buf); - } + free (buf); + } #else - fprintf (stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[d->optind - 1]); + fprintf (stderr, + _("%s: option '%s' requires an argument\n"), + argv[0], argv[d->optind - 1]); #endif - } - d->__nextchar += strlen (d->__nextchar); - return optstring[0] == ':' ? ':' : '?'; - } - } - d->__nextchar += strlen (d->__nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - d->__nextchar = NULL; - return 'W'; /* Let the application handle it. */ + } + d->__nextchar += strlen (d->__nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + d->__nextchar += strlen (d->__nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + d->__nextchar = NULL; + return 'W'; /* Let the application handle it. */ } if (temp[1] == ':') { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*d->__nextchar != '\0') - { - d->optarg = d->__nextchar; - d->optind++; - } - else - d->optarg = NULL; - d->__nextchar = NULL; - } - else - { - /* This is an option that requires an argument. */ - if (*d->__nextchar != '\0') - { - d->optarg = d->__nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - d->optind++; - } - else if (d->optind == argc) - { - if (print_errors) - { - /* 1003.2 specifies the format of this message. */ + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*d->__nextchar != '\0') + { + d->optarg = d->__nextchar; + d->optind++; + } + else + d->optarg = NULL; + d->__nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*d->__nextchar != '\0') + { + d->optarg = d->__nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + d->optind++; + } + else if (d->optind == argc) + { + if (print_errors) + { #if defined _LIBC && defined USE_IN_LIBIO - char *buf; + char *buf; - if (__asprintf (&buf, _("\ -%s: option requires an argument -- %c\n"), - argv[0], c) >= 0) - { - _IO_flockfile (stderr); + if (__asprintf (&buf, _("\ +%s: option requires an argument -- '%c'\n"), + argv[0], c) >= 0) + { + _IO_flockfile (stderr); - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - __fxprintf (NULL, "%s", buf); + __fxprintf (NULL, "%s", buf); - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); - free (buf); - } + free (buf); + } #else - fprintf (stderr, - _("%s: option requires an argument -- %c\n"), - argv[0], c); + fprintf (stderr, + _("%s: option requires an argument -- '%c'\n"), + argv[0], c); #endif - } - d->optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - d->optarg = argv[d->optind++]; - d->__nextchar = NULL; - } + } + d->optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + d->optarg = argv[d->optind++]; + d->__nextchar = NULL; + } } return c; } @@ -1081,16 +1066,17 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, int _getopt_internal (int argc, char **argv, const char *optstring, - const struct option *longopts, int *longind, - int long_only, int posixly_correct) + const struct option *longopts, int *longind, int long_only, + int posixly_correct) { int result; getopt_data.optind = optind; getopt_data.opterr = opterr; - result = _getopt_internal_r (argc, argv, optstring, longopts, longind, - long_only, posixly_correct, &getopt_data); + result = _getopt_internal_r (argc, argv, optstring, longopts, + longind, long_only, &getopt_data, + posixly_correct); optind = getopt_data.optind; optarg = getopt_data.optarg; @@ -1110,10 +1096,23 @@ enum { POSIXLY_CORRECT = 1 }; int getopt (int argc, char *const *argv, const char *optstring) { - return _getopt_internal (argc, (char **) argv, optstring, NULL, NULL, 0, - POSIXLY_CORRECT); + return _getopt_internal (argc, (char **) argv, optstring, + (const struct option *) 0, + (int *) 0, + 0, POSIXLY_CORRECT); } +#ifdef _LIBC +int +__posix_getopt (int argc, char *const *argv, const char *optstring) +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0, 1); +} +#endif + #ifdef TEST @@ -1132,51 +1131,51 @@ main (int argc, char **argv) c = getopt (argc, argv, "abc:d:0123456789"); if (c == -1) - break; + break; switch (c) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf ("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf ("option %c\n", c); - break; + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; - case 'a': - printf ("option a\n"); - break; + case 'a': + printf ("option a\n"); + break; - case 'b': - printf ("option b\n"); - break; + case 'b': + printf ("option b\n"); + break; - case 'c': - printf ("option c with value `%s'\n", optarg); - break; + case 'c': + printf ("option c with value '%s'\n", optarg); + break; - case '?': - break; + case '?': + break; - default: - printf ("?? getopt returned character code 0%o ??\n", c); - } + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) - printf ("%s ", argv[optind++]); + printf ("%s ", argv[optind++]); printf ("\n"); } diff --git a/gnulib/getopt1.c b/gnulib/getopt1.c index d6a3ecf4e..046d69f94 100644 --- a/gnulib/getopt1.c +++ b/gnulib/getopt1.c @@ -1,6 +1,6 @@ /* getopt_long and getopt_long_only entry points for GNU getopt. - Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98,2004,2006 - Free Software Foundation, Inc. + Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, + 1998, 2004, 2006, 2009, 2010 Free Software Foundation, Inc. This file is part of the GNU C Library. This program is free software: you can redistribute it and/or modify @@ -32,25 +32,25 @@ #include #endif -#ifndef NULL +#ifndef NULL #define NULL 0 #endif int getopt_long (int argc, char *__getopt_argv_const *argv, const char *options, - const struct option *long_options, int *opt_index) + const struct option *long_options, int *opt_index) { return _getopt_internal (argc, (char **) argv, options, long_options, - opt_index, 0, 0); + opt_index, 0, 0); } int _getopt_long_r (int argc, char **argv, const char *options, - const struct option *long_options, int *opt_index, - struct _getopt_data *d) + const struct option *long_options, int *opt_index, + struct _getopt_data *d) { return _getopt_internal_r (argc, argv, options, long_options, opt_index, - 0, 0, d); + 0, d, 0); } /* Like getopt_long, but '-' as well as '--' can indicate a long option. @@ -60,20 +60,20 @@ _getopt_long_r (int argc, char **argv, const char *options, int getopt_long_only (int argc, char *__getopt_argv_const *argv, - const char *options, - const struct option *long_options, int *opt_index) + const char *options, + const struct option *long_options, int *opt_index) { return _getopt_internal (argc, (char **) argv, options, long_options, - opt_index, 1, 0); + opt_index, 1, 0); } int _getopt_long_only_r (int argc, char **argv, const char *options, - const struct option *long_options, int *opt_index, - struct _getopt_data *d) + const struct option *long_options, int *opt_index, + struct _getopt_data *d) { return _getopt_internal_r (argc, argv, options, long_options, opt_index, - 1, 0, d); + 1, d, 0); } @@ -91,76 +91,76 @@ main (int argc, char **argv) { int this_option_optind = optind ? optind : 1; int option_index = 0; - static struct option long_options[] = + static const struct option long_options[] = { - {"add", 1, 0, 0}, - {"append", 0, 0, 0}, - {"delete", 1, 0, 0}, - {"verbose", 0, 0, 0}, - {"create", 0, 0, 0}, - {"file", 1, 0, 0}, - {0, 0, 0, 0} + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} }; c = getopt_long (argc, argv, "abc:d:0123456789", - long_options, &option_index); + long_options, &option_index); if (c == -1) - break; + break; switch (c) - { - case 0: - printf ("option %s", long_options[option_index].name); - if (optarg) - printf (" with arg %s", optarg); - printf ("\n"); - break; + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf ("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf ("option %c\n", c); - break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; - case 'a': - printf ("option a\n"); - break; + case 'a': + printf ("option a\n"); + break; - case 'b': - printf ("option b\n"); - break; + case 'b': + printf ("option b\n"); + break; - case 'c': - printf ("option c with value `%s'\n", optarg); - break; + case 'c': + printf ("option c with value `%s'\n", optarg); + break; - case 'd': - printf ("option d with value `%s'\n", optarg); - break; + case 'd': + printf ("option d with value `%s'\n", optarg); + break; - case '?': - break; + case '?': + break; - default: - printf ("?? getopt returned character code 0%o ??\n", c); - } + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) - printf ("%s ", argv[optind++]); + printf ("%s ", argv[optind++]); printf ("\n"); } diff --git a/gnulib/getopt_int.h b/gnulib/getopt_int.h index 3c6628bb9..169def5b2 100644 --- a/gnulib/getopt_int.h +++ b/gnulib/getopt_int.h @@ -1,6 +1,6 @@ /* Internal declarations for getopt. - Copyright (C) 1989-1994,1996-1999,2001,2003,2004 - Free Software Foundation, Inc. + Copyright (C) 1989-1994, 1996-1999, 2001, 2003-2004, 2009-2010 Free Software + Foundation, Inc. This file is part of the GNU C Library. This program is free software: you can redistribute it and/or modify @@ -17,12 +17,14 @@ along with this program. If not, see . */ #ifndef _GETOPT_INT_H -#define _GETOPT_INT_H 1 +#define _GETOPT_INT_H 1 + +#include extern int _getopt_internal (int ___argc, char **___argv, - const char *__shortopts, - const struct option *__longopts, int *__longind, - int __long_only, int __posixly_correct); + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only, int __posixly_correct); /* Reentrant versions which can handle parsing multiple argument @@ -108,23 +110,23 @@ struct _getopt_data /* The initializer is necessary to set OPTIND and OPTERR to their default values and to clear the initialization flag. */ -#define _GETOPT_DATA_INITIALIZER { 1, 1 } +#define _GETOPT_DATA_INITIALIZER { 1, 1 } extern int _getopt_internal_r (int ___argc, char **___argv, - const char *__shortopts, - const struct option *__longopts, int *__longind, - int __long_only, int __posixly_correct, - struct _getopt_data *__data); + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only, struct _getopt_data *__data, + int __posixly_correct); extern int _getopt_long_r (int ___argc, char **___argv, - const char *__shortopts, - const struct option *__longopts, int *__longind, - struct _getopt_data *__data); + const char *__shortopts, + const struct option *__longopts, int *__longind, + struct _getopt_data *__data); extern int _getopt_long_only_r (int ___argc, char **___argv, - const char *__shortopts, - const struct option *__longopts, - int *__longind, - struct _getopt_data *__data); + const char *__shortopts, + const struct option *__longopts, + int *__longind, + struct _getopt_data *__data); #endif /* getopt_int.h */ diff --git a/gnulib/gettext.h b/gnulib/gettext.h index 9d76ec9af..6a069c448 100644 --- a/gnulib/gettext.h +++ b/gnulib/gettext.h @@ -1,5 +1,6 @@ /* Convenience header for conditional use of GNU . - Copyright (C) 1995-1998, 2000-2002, 2004-2006 Free Software Foundation, Inc. + Copyright (C) 1995-1998, 2000-2002, 2004-2006, 2009-2010 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 @@ -63,21 +64,30 @@ for invalid uses of the value returned from these functions. On pre-ANSI systems without 'const', the config.h file is supposed to contain "#define const". */ +# undef gettext # define gettext(Msgid) ((const char *) (Msgid)) +# undef dgettext # define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid)) +# undef dcgettext # define dcgettext(Domainname, Msgid, Category) \ ((void) (Category), dgettext (Domainname, Msgid)) +# undef ngettext # define ngettext(Msgid1, Msgid2, N) \ ((N) == 1 \ ? ((void) (Msgid2), (const char *) (Msgid1)) \ : ((void) (Msgid1), (const char *) (Msgid2))) +# undef dngettext # define dngettext(Domainname, Msgid1, Msgid2, N) \ ((void) (Domainname), ngettext (Msgid1, Msgid2, N)) +# undef dcngettext # define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ - ((void) (Category), dngettext(Domainname, Msgid1, Msgid2, N)) + ((void) (Category), dngettext (Domainname, Msgid1, Msgid2, N)) +# undef textdomain # define textdomain(Domainname) ((const char *) (Domainname)) +# undef bindtextdomain # define bindtextdomain(Domainname, Dirname) \ ((void) (Domainname), (const char *) (Dirname)) +# undef bind_textdomain_codeset # define bind_textdomain_codeset(Domainname, Codeset) \ ((void) (Domainname), (const char *) (Codeset)) @@ -131,8 +141,8 @@ inline #endif static const char * pgettext_aux (const char *domain, - const char *msg_ctxt_id, const char *msgid, - int category) + const char *msg_ctxt_id, const char *msgid, + int category) { const char *translation = dcgettext (domain, msg_ctxt_id, category); if (translation == msg_ctxt_id) @@ -150,9 +160,9 @@ inline #endif static const char * npgettext_aux (const char *domain, - const char *msg_ctxt_id, const char *msgid, - const char *msgid_plural, unsigned long int n, - int category) + const char *msg_ctxt_id, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) { const char *translation = dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); @@ -190,8 +200,8 @@ inline #endif static const char * dcpgettext_expr (const char *domain, - const char *msgctxt, const char *msgid, - int category) + const char *msgctxt, const char *msgid, + int category) { size_t msgctxt_len = strlen (msgctxt) + 1; size_t msgid_len = strlen (msgid) + 1; @@ -213,10 +223,10 @@ dcpgettext_expr (const char *domain, translation = dcgettext (domain, msg_ctxt_id, category); #if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS if (msg_ctxt_id != buf) - free (msg_ctxt_id); + free (msg_ctxt_id); #endif if (translation != msg_ctxt_id) - return translation; + return translation; } return msgid; } @@ -235,9 +245,9 @@ inline #endif static const char * dcnpgettext_expr (const char *domain, - const char *msgctxt, const char *msgid, - const char *msgid_plural, unsigned long int n, - int category) + const char *msgctxt, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) { size_t msgctxt_len = strlen (msgctxt) + 1; size_t msgid_len = strlen (msgid) + 1; @@ -259,10 +269,10 @@ dcnpgettext_expr (const char *domain, translation = dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); #if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS if (msg_ctxt_id != buf) - free (msg_ctxt_id); + free (msg_ctxt_id); #endif if (!(translation == msg_ctxt_id || translation == msgid_plural)) - return translation; + return translation; } return (n == 1 ? msgid : msgid_plural); } diff --git a/gnulib/progname.c b/gnulib/progname.c index bfa374a52..1415e6a55 100644 --- a/gnulib/progname.c +++ b/gnulib/progname.c @@ -1,6 +1,6 @@ /* Program name management. - Copyright (C) 2001-2003, 2005-2009 Free Software Foundation, Inc. - Written by Bruno Haible , 2001. + Copyright (C) 2001-2003, 2005-2010 Free Software Foundation, Inc. + Written by Bruno Haible , 2001. 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 @@ -23,6 +23,8 @@ #include "progname.h" #include /* get program_invocation_name declaration */ +#include +#include #include @@ -30,7 +32,9 @@ To be initialized by main(). */ const char *program_name = NULL; -/* Set program_name, based on argv[0]. */ +/* Set program_name, based on argv[0]. + argv0 must be a string allocated with indefinite extent, and must not be + modified after this call. */ void set_program_name (const char *argv0) { @@ -42,20 +46,30 @@ set_program_name (const char *argv0) const char *slash; const char *base; + /* Sanity check. POSIX requires the invoking process to pass a non-NULL + argv[0]. */ + if (argv0 == NULL) + { + /* It's a bug in the invoking program. Help diagnosing it. */ + fputs ("A NULL argv[0] was passed through an exec system call.\n", + stderr); + abort (); + } + slash = strrchr (argv0, '/'); base = (slash != NULL ? slash + 1 : argv0); if (base - argv0 >= 7 && strncmp (base - 7, "/.libs/", 7) == 0) { argv0 = base; if (strncmp (base, "lt-", 3) == 0) - { - argv0 = base + 3; - /* On glibc systems, remove the "lt-" prefix from the variable - program_invocation_short_name. */ + { + argv0 = base + 3; + /* On glibc systems, remove the "lt-" prefix from the variable + program_invocation_short_name. */ #if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME - program_invocation_short_name = (char *) argv0; + program_invocation_short_name = (char *) argv0; #endif - } + } } /* But don't strip off a leading / in general, because when the user diff --git a/gnulib/progname.h b/gnulib/progname.h index 82615c6bc..5ba303bd0 100644 --- a/gnulib/progname.h +++ b/gnulib/progname.h @@ -1,6 +1,6 @@ /* Program name management. - Copyright (C) 2001-2004, 2006 Free Software Foundation, Inc. - Written by Bruno Haible , 2001. + Copyright (C) 2001-2004, 2006, 2009-2010 Free Software Foundation, Inc. + Written by Bruno Haible , 2001. 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 @@ -31,7 +31,9 @@ extern "C" { /* String containing name the program is called with. */ extern const char *program_name; -/* Set program_name, based on argv[0]. */ +/* Set program_name, based on argv[0]. + argv0 must be a string allocated with indefinite extent, and must not be + modified after this call. */ extern void set_program_name (const char *argv0); #if ENABLE_RELOCATABLE @@ -39,8 +41,8 @@ extern void set_program_name (const char *argv0); /* Set program_name, based on argv[0], and original installation prefix and directory, for relocatability. */ extern void set_program_name_and_installdir (const char *argv0, - const char *orig_installprefix, - const char *orig_installdir); + const char *orig_installprefix, + const char *orig_installdir); #undef set_program_name #define set_program_name(ARG0) \ set_program_name_and_installdir (ARG0, INSTALLPREFIX, INSTALLDIR) diff --git a/gnulib/regcomp.c b/gnulib/regcomp.c new file mode 100644 index 000000000..7eff56925 --- /dev/null +++ b/gnulib/regcomp.c @@ -0,0 +1,3877 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free + Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + 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 + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern, + size_t length, reg_syntax_t syntax); +static void re_compile_fastmap_iter (regex_t *bufp, + const re_dfastate_t *init_state, + char *fastmap); +static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len); +#ifdef RE_ENABLE_I18N +static void free_charset (re_charset_t *cset); +#endif /* RE_ENABLE_I18N */ +static void free_workarea_compile (regex_t *preg); +static reg_errcode_t create_initial_state (re_dfa_t *dfa); +#ifdef RE_ENABLE_I18N +static void optimize_utf8 (re_dfa_t *dfa); +#endif +static reg_errcode_t analyze (regex_t *preg); +static reg_errcode_t preorder (bin_tree_t *root, + reg_errcode_t (fn (void *, bin_tree_t *)), + void *extra); +static reg_errcode_t postorder (bin_tree_t *root, + reg_errcode_t (fn (void *, bin_tree_t *)), + void *extra); +static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node); +static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node); +static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg, + bin_tree_t *node); +static reg_errcode_t calc_first (void *extra, bin_tree_t *node); +static reg_errcode_t calc_next (void *extra, bin_tree_t *node); +static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node); +static Idx duplicate_node (re_dfa_t *dfa, Idx org_idx, unsigned int constraint); +static Idx search_duplicated_node (const re_dfa_t *dfa, Idx org_node, + unsigned int constraint); +static reg_errcode_t calc_eclosure (re_dfa_t *dfa); +static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, + Idx node, bool root); +static reg_errcode_t calc_inveclosure (re_dfa_t *dfa); +static Idx fetch_number (re_string_t *input, re_token_t *token, + reg_syntax_t syntax); +static int peek_token (re_token_t *token, re_string_t *input, + reg_syntax_t syntax) internal_function; +static bin_tree_t *parse (re_string_t *regexp, regex_t *preg, + reg_syntax_t syntax, reg_errcode_t *err); +static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + Idx nest, reg_errcode_t *err); +static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + Idx nest, reg_errcode_t *err); +static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + Idx nest, reg_errcode_t *err); +static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + Idx nest, reg_errcode_t *err); +static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp, + re_dfa_t *dfa, re_token_t *token, + reg_syntax_t syntax, reg_errcode_t *err); +static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, + re_token_t *token, reg_syntax_t syntax, + reg_errcode_t *err); +static reg_errcode_t parse_bracket_element (bracket_elem_t *elem, + re_string_t *regexp, + re_token_t *token, int token_len, + re_dfa_t *dfa, + reg_syntax_t syntax, + bool accept_hyphen); +static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem, + re_string_t *regexp, + re_token_t *token); +#ifdef RE_ENABLE_I18N +static reg_errcode_t build_equiv_class (bitset_t sbcset, + re_charset_t *mbcset, + Idx *equiv_class_alloc, + const unsigned char *name); +static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans, + bitset_t sbcset, + re_charset_t *mbcset, + Idx *char_class_alloc, + const unsigned char *class_name, + reg_syntax_t syntax); +#else /* not RE_ENABLE_I18N */ +static reg_errcode_t build_equiv_class (bitset_t sbcset, + const unsigned char *name); +static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans, + bitset_t sbcset, + const unsigned char *class_name, + reg_syntax_t syntax); +#endif /* not RE_ENABLE_I18N */ +static bin_tree_t *build_charclass_op (re_dfa_t *dfa, + RE_TRANSLATE_TYPE trans, + const unsigned char *class_name, + const unsigned char *extra, + bool non_match, reg_errcode_t *err); +static bin_tree_t *create_tree (re_dfa_t *dfa, + bin_tree_t *left, bin_tree_t *right, + re_token_type_t type); +static bin_tree_t *create_token_tree (re_dfa_t *dfa, + bin_tree_t *left, bin_tree_t *right, + const re_token_t *token); +static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa); +static void free_token (re_token_t *node); +static reg_errcode_t free_tree (void *extra, bin_tree_t *node); +static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node); + +/* This table gives an error message for each of the error codes listed + in regex.h. Obviously the order here has to be same as there. + POSIX doesn't require that we do anything for REG_NOERROR, + but why not be nice? */ + +static const char __re_error_msgid[] = + { +#define REG_NOERROR_IDX 0 + gettext_noop ("Success") /* REG_NOERROR */ + "\0" +#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success") + gettext_noop ("No match") /* REG_NOMATCH */ + "\0" +#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match") + gettext_noop ("Invalid regular expression") /* REG_BADPAT */ + "\0" +#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression") + gettext_noop ("Invalid collation character") /* REG_ECOLLATE */ + "\0" +#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character") + gettext_noop ("Invalid character class name") /* REG_ECTYPE */ + "\0" +#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name") + gettext_noop ("Trailing backslash") /* REG_EESCAPE */ + "\0" +#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash") + gettext_noop ("Invalid back reference") /* REG_ESUBREG */ + "\0" +#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference") + gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */ + "\0" +#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^") + gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */ + "\0" +#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(") + gettext_noop ("Unmatched \\{") /* REG_EBRACE */ + "\0" +#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{") + gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */ + "\0" +#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}") + gettext_noop ("Invalid range end") /* REG_ERANGE */ + "\0" +#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end") + gettext_noop ("Memory exhausted") /* REG_ESPACE */ + "\0" +#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted") + gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */ + "\0" +#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression") + gettext_noop ("Premature end of regular expression") /* REG_EEND */ + "\0" +#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression") + gettext_noop ("Regular expression too big") /* REG_ESIZE */ + "\0" +#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big") + gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */ + }; + +static const size_t __re_error_msgid_idx[] = + { + REG_NOERROR_IDX, + REG_NOMATCH_IDX, + REG_BADPAT_IDX, + REG_ECOLLATE_IDX, + REG_ECTYPE_IDX, + REG_EESCAPE_IDX, + REG_ESUBREG_IDX, + REG_EBRACK_IDX, + REG_EPAREN_IDX, + REG_EBRACE_IDX, + REG_BADBR_IDX, + REG_ERANGE_IDX, + REG_ESPACE_IDX, + REG_BADRPT_IDX, + REG_EEND_IDX, + REG_ESIZE_IDX, + REG_ERPAREN_IDX + }; + +/* Entry points for GNU code. */ + +/* re_compile_pattern is the GNU regular expression compiler: it + compiles PATTERN (of length LENGTH) and puts the result in BUFP. + Returns 0 if the pattern was valid, otherwise an error string. + + Assumes the `allocated' (and perhaps `buffer') and `translate' fields + are set in BUFP on entry. */ + +#ifdef _LIBC +const char * +re_compile_pattern (pattern, length, bufp) + const char *pattern; + size_t length; + struct re_pattern_buffer *bufp; +#else /* size_t might promote */ +const char * +re_compile_pattern (const char *pattern, size_t length, + struct re_pattern_buffer *bufp) +#endif +{ + reg_errcode_t ret; + + /* And GNU code determines whether or not to get register information + by passing null for the REGS argument to re_match, etc., not by + setting no_sub, unless RE_NO_SUB is set. */ + bufp->no_sub = !!(re_syntax_options & RE_NO_SUB); + + /* Match anchors at newline. */ + bufp->newline_anchor = 1; + + ret = re_compile_internal (bufp, pattern, length, re_syntax_options); + + if (!ret) + return NULL; + return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]); +} +#ifdef _LIBC +weak_alias (__re_compile_pattern, re_compile_pattern) +#endif + +/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can + also be assigned to arbitrarily: each pattern buffer stores its own + syntax, so it can be changed between regex compilations. */ +/* This has no initializer because initialized variables in Emacs + become read-only after dumping. */ +reg_syntax_t re_syntax_options; + + +/* Specify the precise syntax of regexps for compilation. This provides + for compatibility for various utilities which historically have + different, incompatible syntaxes. + + The argument SYNTAX is a bit mask comprised of the various bits + defined in regex.h. We return the old syntax. */ + +reg_syntax_t +re_set_syntax (syntax) + reg_syntax_t syntax; +{ + reg_syntax_t ret = re_syntax_options; + + re_syntax_options = syntax; + return ret; +} +#ifdef _LIBC +weak_alias (__re_set_syntax, re_set_syntax) +#endif + +int +re_compile_fastmap (bufp) + struct re_pattern_buffer *bufp; +{ + re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; + char *fastmap = bufp->fastmap; + + memset (fastmap, '\0', sizeof (char) * SBC_MAX); + re_compile_fastmap_iter (bufp, dfa->init_state, fastmap); + if (dfa->init_state != dfa->init_state_word) + re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap); + if (dfa->init_state != dfa->init_state_nl) + re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap); + if (dfa->init_state != dfa->init_state_begbuf) + re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap); + bufp->fastmap_accurate = 1; + return 0; +} +#ifdef _LIBC +weak_alias (__re_compile_fastmap, re_compile_fastmap) +#endif + +static inline void +__attribute ((always_inline)) +re_set_fastmap (char *fastmap, bool icase, int ch) +{ + fastmap[ch] = 1; + if (icase) + fastmap[tolower (ch)] = 1; +} + +/* Helper function for re_compile_fastmap. + Compile fastmap for the initial_state INIT_STATE. */ + +static void +re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state, + char *fastmap) +{ + re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; + Idx node_cnt; + bool icase = (dfa->mb_cur_max == 1 && (bufp->syntax & RE_ICASE)); + for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt) + { + Idx node = init_state->nodes.elems[node_cnt]; + re_token_type_t type = dfa->nodes[node].type; + + if (type == CHARACTER) + { + re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c); +#ifdef RE_ENABLE_I18N + if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1) + { + unsigned char buf[MB_LEN_MAX]; + unsigned char *p; + wchar_t wc; + mbstate_t state; + + p = buf; + *p++ = dfa->nodes[node].opr.c; + while (++node < dfa->nodes_len + && dfa->nodes[node].type == CHARACTER + && dfa->nodes[node].mb_partial) + *p++ = dfa->nodes[node].opr.c; + memset (&state, '\0', sizeof (state)); + if (__mbrtowc (&wc, (const char *) buf, p - buf, + &state) == p - buf + && (__wcrtomb ((char *) buf, towlower (wc), &state) + != (size_t) -1)) + re_set_fastmap (fastmap, false, buf[0]); + } +#endif + } + else if (type == SIMPLE_BRACKET) + { + int i, ch; + for (i = 0, ch = 0; i < BITSET_WORDS; ++i) + { + int j; + bitset_word_t w = dfa->nodes[node].opr.sbcset[i]; + for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) + if (w & ((bitset_word_t) 1 << j)) + re_set_fastmap (fastmap, icase, ch); + } + } +#ifdef RE_ENABLE_I18N + else if (type == COMPLEX_BRACKET) + { + re_charset_t *cset = dfa->nodes[node].opr.mbcset; + Idx i; + +# ifdef _LIBC + /* See if we have to try all bytes which start multiple collation + elements. + e.g. In da_DK, we want to catch 'a' since "aa" is a valid + collation element, and don't catch 'b' since 'b' is + the only collation element which starts from 'b' (and + it is caught by SIMPLE_BRACKET). */ + if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0 + && (cset->ncoll_syms || cset->nranges)) + { + const int32_t *table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + for (i = 0; i < SBC_MAX; ++i) + if (table[i] < 0) + re_set_fastmap (fastmap, icase, i); + } +# endif /* _LIBC */ + + /* See if we have to start the match at all multibyte characters, + i.e. where we would not find an invalid sequence. This only + applies to multibyte character sets; for single byte character + sets, the SIMPLE_BRACKET again suffices. */ + if (dfa->mb_cur_max > 1 + && (cset->nchar_classes || cset->non_match || cset->nranges +# ifdef _LIBC + || cset->nequiv_classes +# endif /* _LIBC */ + )) + { + unsigned char c = 0; + do + { + mbstate_t mbs; + memset (&mbs, 0, sizeof (mbs)); + if (__mbrtowc (NULL, (char *) &c, 1, &mbs) == (size_t) -2) + re_set_fastmap (fastmap, false, (int) c); + } + while (++c != 0); + } + + else + { + /* ... Else catch all bytes which can start the mbchars. */ + for (i = 0; i < cset->nmbchars; ++i) + { + char buf[256]; + mbstate_t state; + memset (&state, '\0', sizeof (state)); + if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1) + re_set_fastmap (fastmap, icase, *(unsigned char *) buf); + if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1) + { + if (__wcrtomb (buf, towlower (cset->mbchars[i]), &state) + != (size_t) -1) + re_set_fastmap (fastmap, false, *(unsigned char *) buf); + } + } + } + } +#endif /* RE_ENABLE_I18N */ + else if (type == OP_PERIOD +#ifdef RE_ENABLE_I18N + || type == OP_UTF8_PERIOD +#endif /* RE_ENABLE_I18N */ + || type == END_OF_RE) + { + memset (fastmap, '\1', sizeof (char) * SBC_MAX); + if (type == END_OF_RE) + bufp->can_be_null = 1; + return; + } + } +} + +/* Entry point for POSIX code. */ +/* regcomp takes a regular expression as a string and compiles it. + + PREG is a regex_t *. We do not expect any fields to be initialized, + since POSIX says we shouldn't. Thus, we set + + `buffer' to the compiled pattern; + `used' to the length of the compiled pattern; + `syntax' to RE_SYNTAX_POSIX_EXTENDED if the + REG_EXTENDED bit in CFLAGS is set; otherwise, to + RE_SYNTAX_POSIX_BASIC; + `newline_anchor' to REG_NEWLINE being set in CFLAGS; + `fastmap' to an allocated space for the fastmap; + `fastmap_accurate' to zero; + `re_nsub' to the number of subexpressions in PATTERN. + + PATTERN is the address of the pattern string. + + CFLAGS is a series of bits which affect compilation. + + If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we + use POSIX basic syntax. + + If REG_NEWLINE is set, then . and [^...] don't match newline. + Also, regexec will try a match beginning after every newline. + + If REG_ICASE is set, then we considers upper- and lowercase + versions of letters to be equivalent when matching. + + If REG_NOSUB is set, then when PREG is passed to regexec, that + routine will report only success or failure, and nothing about the + registers. + + It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for + the return codes and their meanings.) */ + +int +regcomp (preg, pattern, cflags) + regex_t *_Restrict_ preg; + const char *_Restrict_ pattern; + int cflags; +{ + reg_errcode_t ret; + reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED + : RE_SYNTAX_POSIX_BASIC); + + preg->buffer = NULL; + preg->allocated = 0; + preg->used = 0; + + /* Try to allocate space for the fastmap. */ + preg->fastmap = re_malloc (char, SBC_MAX); + if (BE (preg->fastmap == NULL, 0)) + return REG_ESPACE; + + syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0; + + /* If REG_NEWLINE is set, newlines are treated differently. */ + if (cflags & REG_NEWLINE) + { /* REG_NEWLINE implies neither . nor [^...] match newline. */ + syntax &= ~RE_DOT_NEWLINE; + syntax |= RE_HAT_LISTS_NOT_NEWLINE; + /* It also changes the matching behavior. */ + preg->newline_anchor = 1; + } + else + preg->newline_anchor = 0; + preg->no_sub = !!(cflags & REG_NOSUB); + preg->translate = NULL; + + ret = re_compile_internal (preg, pattern, strlen (pattern), syntax); + + /* POSIX doesn't distinguish between an unmatched open-group and an + unmatched close-group: both are REG_EPAREN. */ + if (ret == REG_ERPAREN) + ret = REG_EPAREN; + + /* We have already checked preg->fastmap != NULL. */ + if (BE (ret == REG_NOERROR, 1)) + /* Compute the fastmap now, since regexec cannot modify the pattern + buffer. This function never fails in this implementation. */ + (void) re_compile_fastmap (preg); + else + { + /* Some error occurred while compiling the expression. */ + re_free (preg->fastmap); + preg->fastmap = NULL; + } + + return (int) ret; +} +#ifdef _LIBC +weak_alias (__regcomp, regcomp) +#endif + +/* Returns a message corresponding to an error code, ERRCODE, returned + from either regcomp or regexec. We don't use PREG here. */ + +#ifdef _LIBC +size_t +regerror (errcode, preg, errbuf, errbuf_size) + int errcode; + const regex_t *_Restrict_ preg; + char *_Restrict_ errbuf; + size_t errbuf_size; +#else /* size_t might promote */ +size_t +regerror (int errcode, const regex_t *_Restrict_ preg, + char *_Restrict_ errbuf, size_t errbuf_size) +#endif +{ + const char *msg; + size_t msg_size; + + if (BE (errcode < 0 + || errcode >= (int) (sizeof (__re_error_msgid_idx) + / sizeof (__re_error_msgid_idx[0])), 0)) + /* Only error codes returned by the rest of the code should be passed + to this routine. If we are given anything else, or if other regex + code generates an invalid error code, then the program has a bug. + Dump core so we can fix it. */ + abort (); + + msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]); + + msg_size = strlen (msg) + 1; /* Includes the null. */ + + if (BE (errbuf_size != 0, 1)) + { + size_t cpy_size = msg_size; + if (BE (msg_size > errbuf_size, 0)) + { + cpy_size = errbuf_size - 1; + errbuf[cpy_size] = '\0'; + } + memcpy (errbuf, msg, cpy_size); + } + + return msg_size; +} +#ifdef _LIBC +weak_alias (__regerror, regerror) +#endif + + +#ifdef RE_ENABLE_I18N +/* This static array is used for the map to single-byte characters when + UTF-8 is used. Otherwise we would allocate memory just to initialize + it the same all the time. UTF-8 is the preferred encoding so this is + a worthwhile optimization. */ +static const bitset_t utf8_sb_map = +{ + /* Set the first 128 bits. */ +# if 4 * BITSET_WORD_BITS < ASCII_CHARS +# error "bitset_word_t is narrower than 32 bits" +# elif 3 * BITSET_WORD_BITS < ASCII_CHARS + BITSET_WORD_MAX, BITSET_WORD_MAX, BITSET_WORD_MAX, +# elif 2 * BITSET_WORD_BITS < ASCII_CHARS + BITSET_WORD_MAX, BITSET_WORD_MAX, +# elif 1 * BITSET_WORD_BITS < ASCII_CHARS + BITSET_WORD_MAX, +# endif + (BITSET_WORD_MAX + >> (SBC_MAX % BITSET_WORD_BITS == 0 + ? 0 + : BITSET_WORD_BITS - SBC_MAX % BITSET_WORD_BITS)) +}; +#endif + + +static void +free_dfa_content (re_dfa_t *dfa) +{ + Idx i, j; + + if (dfa->nodes) + for (i = 0; i < dfa->nodes_len; ++i) + free_token (dfa->nodes + i); + re_free (dfa->nexts); + for (i = 0; i < dfa->nodes_len; ++i) + { + if (dfa->eclosures != NULL) + re_node_set_free (dfa->eclosures + i); + if (dfa->inveclosures != NULL) + re_node_set_free (dfa->inveclosures + i); + if (dfa->edests != NULL) + re_node_set_free (dfa->edests + i); + } + re_free (dfa->edests); + re_free (dfa->eclosures); + re_free (dfa->inveclosures); + re_free (dfa->nodes); + + if (dfa->state_table) + for (i = 0; i <= dfa->state_hash_mask; ++i) + { + struct re_state_table_entry *entry = dfa->state_table + i; + for (j = 0; j < entry->num; ++j) + { + re_dfastate_t *state = entry->array[j]; + free_state (state); + } + re_free (entry->array); + } + re_free (dfa->state_table); +#ifdef RE_ENABLE_I18N + if (dfa->sb_char != utf8_sb_map) + re_free (dfa->sb_char); +#endif + re_free (dfa->subexp_map); +#ifdef DEBUG + re_free (dfa->re_str); +#endif + + re_free (dfa); +} + + +/* Free dynamically allocated space used by PREG. */ + +void +regfree (preg) + regex_t *preg; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + if (BE (dfa != NULL, 1)) + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + + re_free (preg->fastmap); + preg->fastmap = NULL; + + re_free (preg->translate); + preg->translate = NULL; +} +#ifdef _LIBC +weak_alias (__regfree, regfree) +#endif + +/* Entry points compatible with 4.2 BSD regex library. We don't define + them unless specifically requested. */ + +#if defined _REGEX_RE_COMP || defined _LIBC + +/* BSD has one and only one pattern buffer. */ +static struct re_pattern_buffer re_comp_buf; + +char * +# ifdef _LIBC +/* Make these definitions weak in libc, so POSIX programs can redefine + these names if they don't use our functions, and still use + regcomp/regexec above without link errors. */ +weak_function +# endif +re_comp (s) + const char *s; +{ + reg_errcode_t ret; + char *fastmap; + + if (!s) + { + if (!re_comp_buf.buffer) + return gettext ("No previous regular expression"); + return 0; + } + + if (re_comp_buf.buffer) + { + fastmap = re_comp_buf.fastmap; + re_comp_buf.fastmap = NULL; + __regfree (&re_comp_buf); + memset (&re_comp_buf, '\0', sizeof (re_comp_buf)); + re_comp_buf.fastmap = fastmap; + } + + if (re_comp_buf.fastmap == NULL) + { + re_comp_buf.fastmap = (char *) malloc (SBC_MAX); + if (re_comp_buf.fastmap == NULL) + return (char *) gettext (__re_error_msgid + + __re_error_msgid_idx[(int) REG_ESPACE]); + } + + /* Since `re_exec' always passes NULL for the `regs' argument, we + don't need to initialize the pattern buffer fields which affect it. */ + + /* Match anchors at newlines. */ + re_comp_buf.newline_anchor = 1; + + ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options); + + if (!ret) + return NULL; + + /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */ + return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]); +} + +#ifdef _LIBC +libc_freeres_fn (free_mem) +{ + __regfree (&re_comp_buf); +} +#endif + +#endif /* _REGEX_RE_COMP */ + +/* Internal entry point. + Compile the regular expression PATTERN, whose length is LENGTH. + SYNTAX indicate regular expression's syntax. */ + +static reg_errcode_t +re_compile_internal (regex_t *preg, const char * pattern, size_t length, + reg_syntax_t syntax) +{ + reg_errcode_t err = REG_NOERROR; + re_dfa_t *dfa; + re_string_t regexp; + + /* Initialize the pattern buffer. */ + preg->fastmap_accurate = 0; + preg->syntax = syntax; + preg->not_bol = preg->not_eol = 0; + preg->used = 0; + preg->re_nsub = 0; + preg->can_be_null = 0; + preg->regs_allocated = REGS_UNALLOCATED; + + /* Initialize the dfa. */ + dfa = (re_dfa_t *) preg->buffer; + if (BE (preg->allocated < sizeof (re_dfa_t), 0)) + { + /* If zero allocated, but buffer is non-null, try to realloc + enough space. This loses if buffer's address is bogus, but + that is the user's responsibility. If ->buffer is NULL this + is a simple allocation. */ + dfa = re_realloc (preg->buffer, re_dfa_t, 1); + if (dfa == NULL) + return REG_ESPACE; + preg->allocated = sizeof (re_dfa_t); + preg->buffer = (unsigned char *) dfa; + } + preg->used = sizeof (re_dfa_t); + + err = init_dfa (dfa, length); + if (BE (err != REG_NOERROR, 0)) + { + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + return err; + } +#ifdef DEBUG + /* Note: length+1 will not overflow since it is checked in init_dfa. */ + dfa->re_str = re_malloc (char, length + 1); + strncpy (dfa->re_str, pattern, length + 1); +#endif + + __libc_lock_init (dfa->lock); + + err = re_string_construct (®exp, pattern, length, preg->translate, + (syntax & RE_ICASE) != 0, dfa); + if (BE (err != REG_NOERROR, 0)) + { + re_compile_internal_free_return: + free_workarea_compile (preg); + re_string_destruct (®exp); + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + return err; + } + + /* Parse the regular expression, and build a structure tree. */ + preg->re_nsub = 0; + dfa->str_tree = parse (®exp, preg, syntax, &err); + if (BE (dfa->str_tree == NULL, 0)) + goto re_compile_internal_free_return; + + /* Analyze the tree and create the nfa. */ + err = analyze (preg); + if (BE (err != REG_NOERROR, 0)) + goto re_compile_internal_free_return; + +#ifdef RE_ENABLE_I18N + /* If possible, do searching in single byte encoding to speed things up. */ + if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL) + optimize_utf8 (dfa); +#endif + + /* Then create the initial state of the dfa. */ + err = create_initial_state (dfa); + + /* Release work areas. */ + free_workarea_compile (preg); + re_string_destruct (®exp); + + if (BE (err != REG_NOERROR, 0)) + { + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + } + + return err; +} + +/* Initialize DFA. We use the length of the regular expression PAT_LEN + as the initial length of some arrays. */ + +static reg_errcode_t +init_dfa (re_dfa_t *dfa, size_t pat_len) +{ + __re_size_t table_size; +#ifndef _LIBC + char *codeset_name; +#endif +#ifdef RE_ENABLE_I18N + size_t max_i18n_object_size = MAX (sizeof (wchar_t), sizeof (wctype_t)); +#else + size_t max_i18n_object_size = 0; +#endif + size_t max_object_size = + MAX (sizeof (struct re_state_table_entry), + MAX (sizeof (re_token_t), + MAX (sizeof (re_node_set), + MAX (sizeof (regmatch_t), + max_i18n_object_size)))); + + memset (dfa, '\0', sizeof (re_dfa_t)); + + /* Force allocation of str_tree_storage the first time. */ + dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE; + + /* Avoid overflows. The extra "/ 2" is for the table_size doubling + calculation below, and for similar doubling calculations + elsewhere. And it's <= rather than <, because some of the + doubling calculations add 1 afterwards. */ + if (BE (SIZE_MAX / max_object_size / 2 <= pat_len, 0)) + return REG_ESPACE; + + dfa->nodes_alloc = pat_len + 1; + dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc); + + /* table_size = 2 ^ ceil(log pat_len) */ + for (table_size = 1; ; table_size <<= 1) + if (table_size > pat_len) + break; + + dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size); + dfa->state_hash_mask = table_size - 1; + + dfa->mb_cur_max = MB_CUR_MAX; +#ifdef _LIBC + if (dfa->mb_cur_max == 6 + && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0) + dfa->is_utf8 = 1; + dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII) + != 0); +#else + codeset_name = nl_langinfo (CODESET); + if (strcasecmp (codeset_name, "UTF-8") == 0 + || strcasecmp (codeset_name, "UTF8") == 0) + dfa->is_utf8 = 1; + + /* We check exhaustively in the loop below if this charset is a + superset of ASCII. */ + dfa->map_notascii = 0; +#endif + +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + if (dfa->is_utf8) + dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map; + else + { + int i, j, ch; + + dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); + if (BE (dfa->sb_char == NULL, 0)) + return REG_ESPACE; + + /* Set the bits corresponding to single byte chars. */ + for (i = 0, ch = 0; i < BITSET_WORDS; ++i) + for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) + { + wint_t wch = __btowc (ch); + if (wch != WEOF) + dfa->sb_char[i] |= (bitset_word_t) 1 << j; +# ifndef _LIBC + if (isascii (ch) && wch != ch) + dfa->map_notascii = 1; +# endif + } + } + } +#endif + + if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0)) + return REG_ESPACE; + return REG_NOERROR; +} + +/* Initialize WORD_CHAR table, which indicate which character is + "word". In this case "word" means that it is the word construction + character used by some operators like "\<", "\>", etc. */ + +static void +internal_function +init_word_char (re_dfa_t *dfa) +{ + int i, j, ch; + dfa->word_ops_used = 1; + for (i = 0, ch = 0; i < BITSET_WORDS; ++i) + for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) + if (isalnum (ch) || ch == '_') + dfa->word_char[i] |= (bitset_word_t) 1 << j; +} + +/* Free the work area which are only used while compiling. */ + +static void +free_workarea_compile (regex_t *preg) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_storage_t *storage, *next; + for (storage = dfa->str_tree_storage; storage; storage = next) + { + next = storage->next; + re_free (storage); + } + dfa->str_tree_storage = NULL; + dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE; + dfa->str_tree = NULL; + re_free (dfa->org_indices); + dfa->org_indices = NULL; +} + +/* Create initial states for all contexts. */ + +static reg_errcode_t +create_initial_state (re_dfa_t *dfa) +{ + Idx first, i; + reg_errcode_t err; + re_node_set init_nodes; + + /* Initial states have the epsilon closure of the node which is + the first node of the regular expression. */ + first = dfa->str_tree->first->node_idx; + dfa->init_node = first; + err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* The back-references which are in initial states can epsilon transit, + since in this case all of the subexpressions can be null. + Then we add epsilon closures of the nodes which are the next nodes of + the back-references. */ + if (dfa->nbackref > 0) + for (i = 0; i < init_nodes.nelem; ++i) + { + Idx node_idx = init_nodes.elems[i]; + re_token_type_t type = dfa->nodes[node_idx].type; + + Idx clexp_idx; + if (type != OP_BACK_REF) + continue; + for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx) + { + re_token_t *clexp_node; + clexp_node = dfa->nodes + init_nodes.elems[clexp_idx]; + if (clexp_node->type == OP_CLOSE_SUBEXP + && clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx) + break; + } + if (clexp_idx == init_nodes.nelem) + continue; + + if (type == OP_BACK_REF) + { + Idx dest_idx = dfa->edests[node_idx].elems[0]; + if (!re_node_set_contains (&init_nodes, dest_idx)) + { + reg_errcode_t merge_err + = re_node_set_merge (&init_nodes, dfa->eclosures + dest_idx); + if (merge_err != REG_NOERROR) + return merge_err; + i = 0; + } + } + } + + /* It must be the first time to invoke acquire_state. */ + dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0); + /* We don't check ERR here, since the initial state must not be NULL. */ + if (BE (dfa->init_state == NULL, 0)) + return err; + if (dfa->init_state->has_constraint) + { + dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes, + CONTEXT_WORD); + dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes, + CONTEXT_NEWLINE); + dfa->init_state_begbuf = re_acquire_state_context (&err, dfa, + &init_nodes, + CONTEXT_NEWLINE + | CONTEXT_BEGBUF); + if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL + || dfa->init_state_begbuf == NULL, 0)) + return err; + } + else + dfa->init_state_word = dfa->init_state_nl + = dfa->init_state_begbuf = dfa->init_state; + + re_node_set_free (&init_nodes); + return REG_NOERROR; +} + +#ifdef RE_ENABLE_I18N +/* If it is possible to do searching in single byte encoding instead of UTF-8 + to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change + DFA nodes where needed. */ + +static void +optimize_utf8 (re_dfa_t *dfa) +{ + Idx node; + int i; + bool mb_chars = false; + bool has_period = false; + + for (node = 0; node < dfa->nodes_len; ++node) + switch (dfa->nodes[node].type) + { + case CHARACTER: + if (dfa->nodes[node].opr.c >= ASCII_CHARS) + mb_chars = true; + break; + case ANCHOR: + switch (dfa->nodes[node].opr.ctx_type) + { + case LINE_FIRST: + case LINE_LAST: + case BUF_FIRST: + case BUF_LAST: + break; + default: + /* Word anchors etc. cannot be handled. It's okay to test + opr.ctx_type since constraints (for all DFA nodes) are + created by ORing one or more opr.ctx_type values. */ + return; + } + break; + case OP_PERIOD: + has_period = true; + break; + case OP_BACK_REF: + case OP_ALT: + case END_OF_RE: + case OP_DUP_ASTERISK: + case OP_OPEN_SUBEXP: + case OP_CLOSE_SUBEXP: + break; + case COMPLEX_BRACKET: + return; + case SIMPLE_BRACKET: + /* Just double check. */ + { + int rshift = (ASCII_CHARS % BITSET_WORD_BITS == 0 + ? 0 + : BITSET_WORD_BITS - ASCII_CHARS % BITSET_WORD_BITS); + for (i = ASCII_CHARS / BITSET_WORD_BITS; i < BITSET_WORDS; ++i) + { + if (dfa->nodes[node].opr.sbcset[i] >> rshift != 0) + return; + rshift = 0; + } + } + break; + default: + abort (); + } + + if (mb_chars || has_period) + for (node = 0; node < dfa->nodes_len; ++node) + { + if (dfa->nodes[node].type == CHARACTER + && dfa->nodes[node].opr.c >= ASCII_CHARS) + dfa->nodes[node].mb_partial = 0; + else if (dfa->nodes[node].type == OP_PERIOD) + dfa->nodes[node].type = OP_UTF8_PERIOD; + } + + /* The search can be in single byte locale. */ + dfa->mb_cur_max = 1; + dfa->is_utf8 = 0; + dfa->has_mb_node = dfa->nbackref > 0 || has_period; +} +#endif + +/* Analyze the structure tree, and calculate "first", "next", "edest", + "eclosure", and "inveclosure". */ + +static reg_errcode_t +analyze (regex_t *preg) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + reg_errcode_t ret; + + /* Allocate arrays. */ + dfa->nexts = re_malloc (Idx, dfa->nodes_alloc); + dfa->org_indices = re_malloc (Idx, dfa->nodes_alloc); + dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc); + dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc); + if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL + || dfa->eclosures == NULL, 0)) + return REG_ESPACE; + + dfa->subexp_map = re_malloc (Idx, preg->re_nsub); + if (dfa->subexp_map != NULL) + { + Idx i; + for (i = 0; i < preg->re_nsub; i++) + dfa->subexp_map[i] = i; + preorder (dfa->str_tree, optimize_subexps, dfa); + for (i = 0; i < preg->re_nsub; i++) + if (dfa->subexp_map[i] != i) + break; + if (i == preg->re_nsub) + { + free (dfa->subexp_map); + dfa->subexp_map = NULL; + } + } + + ret = postorder (dfa->str_tree, lower_subexps, preg); + if (BE (ret != REG_NOERROR, 0)) + return ret; + ret = postorder (dfa->str_tree, calc_first, dfa); + if (BE (ret != REG_NOERROR, 0)) + return ret; + preorder (dfa->str_tree, calc_next, dfa); + ret = preorder (dfa->str_tree, link_nfa_nodes, dfa); + if (BE (ret != REG_NOERROR, 0)) + return ret; + ret = calc_eclosure (dfa); + if (BE (ret != REG_NOERROR, 0)) + return ret; + + /* We only need this during the prune_impossible_nodes pass in regexec.c; + skip it if p_i_n will not run, as calc_inveclosure can be quadratic. */ + if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match) + || dfa->nbackref) + { + dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len); + if (BE (dfa->inveclosures == NULL, 0)) + return REG_ESPACE; + ret = calc_inveclosure (dfa); + } + + return ret; +} + +/* Our parse trees are very unbalanced, so we cannot use a stack to + implement parse tree visits. Instead, we use parent pointers and + some hairy code in these two functions. */ +static reg_errcode_t +postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), + void *extra) +{ + bin_tree_t *node, *prev; + + for (node = root; ; ) + { + /* Descend down the tree, preferably to the left (or to the right + if that's the only child). */ + while (node->left || node->right) + if (node->left) + node = node->left; + else + node = node->right; + + do + { + reg_errcode_t err = fn (extra, node); + if (BE (err != REG_NOERROR, 0)) + return err; + if (node->parent == NULL) + return REG_NOERROR; + prev = node; + node = node->parent; + } + /* Go up while we have a node that is reached from the right. */ + while (node->right == prev || node->right == NULL); + node = node->right; + } +} + +static reg_errcode_t +preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), + void *extra) +{ + bin_tree_t *node; + + for (node = root; ; ) + { + reg_errcode_t err = fn (extra, node); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* Go to the left node, or up and to the right. */ + if (node->left) + node = node->left; + else + { + bin_tree_t *prev = NULL; + while (node->right == prev || node->right == NULL) + { + prev = node; + node = node->parent; + if (!node) + return REG_NOERROR; + } + node = node->right; + } + } +} + +/* Optimization pass: if a SUBEXP is entirely contained, strip it and tell + re_search_internal to map the inner one's opr.idx to this one's. Adjust + backreferences as well. Requires a preorder visit. */ +static reg_errcode_t +optimize_subexps (void *extra, bin_tree_t *node) +{ + re_dfa_t *dfa = (re_dfa_t *) extra; + + if (node->token.type == OP_BACK_REF && dfa->subexp_map) + { + int idx = node->token.opr.idx; + node->token.opr.idx = dfa->subexp_map[idx]; + dfa->used_bkref_map |= 1 << node->token.opr.idx; + } + + else if (node->token.type == SUBEXP + && node->left && node->left->token.type == SUBEXP) + { + Idx other_idx = node->left->token.opr.idx; + + node->left = node->left->left; + if (node->left) + node->left->parent = node; + + dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx]; + if (other_idx < BITSET_WORD_BITS) + dfa->used_bkref_map &= ~((bitset_word_t) 1 << other_idx); + } + + return REG_NOERROR; +} + +/* Lowering pass: Turn each SUBEXP node into the appropriate concatenation + of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP. */ +static reg_errcode_t +lower_subexps (void *extra, bin_tree_t *node) +{ + regex_t *preg = (regex_t *) extra; + reg_errcode_t err = REG_NOERROR; + + if (node->left && node->left->token.type == SUBEXP) + { + node->left = lower_subexp (&err, preg, node->left); + if (node->left) + node->left->parent = node; + } + if (node->right && node->right->token.type == SUBEXP) + { + node->right = lower_subexp (&err, preg, node->right); + if (node->right) + node->right->parent = node; + } + + return err; +} + +static bin_tree_t * +lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *body = node->left; + bin_tree_t *op, *cls, *tree1, *tree; + + if (preg->no_sub + /* We do not optimize empty subexpressions, because otherwise we may + have bad CONCAT nodes with NULL children. This is obviously not + very common, so we do not lose much. An example that triggers + this case is the sed "script" /\(\)/x. */ + && node->left != NULL + && (node->token.opr.idx >= BITSET_WORD_BITS + || !(dfa->used_bkref_map + & ((bitset_word_t) 1 << node->token.opr.idx)))) + return node->left; + + /* Convert the SUBEXP node to the concatenation of an + OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP. */ + op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP); + cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP); + tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls; + tree = create_tree (dfa, op, tree1, CONCAT); + if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + + op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx; + op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp; + return tree; +} + +/* Pass 1 in building the NFA: compute FIRST and create unlinked automaton + nodes. Requires a postorder visit. */ +static reg_errcode_t +calc_first (void *extra, bin_tree_t *node) +{ + re_dfa_t *dfa = (re_dfa_t *) extra; + if (node->token.type == CONCAT) + { + node->first = node->left->first; + node->node_idx = node->left->node_idx; + } + else + { + node->first = node; + node->node_idx = re_dfa_add_node (dfa, node->token); + if (BE (node->node_idx == REG_MISSING, 0)) + return REG_ESPACE; + if (node->token.type == ANCHOR) + dfa->nodes[node->node_idx].constraint = node->token.opr.ctx_type; + } + return REG_NOERROR; +} + +/* Pass 2: compute NEXT on the tree. Preorder visit. */ +static reg_errcode_t +calc_next (void *extra, bin_tree_t *node) +{ + switch (node->token.type) + { + case OP_DUP_ASTERISK: + node->left->next = node; + break; + case CONCAT: + node->left->next = node->right->first; + node->right->next = node->next; + break; + default: + if (node->left) + node->left->next = node->next; + if (node->right) + node->right->next = node->next; + break; + } + return REG_NOERROR; +} + +/* Pass 3: link all DFA nodes to their NEXT node (any order will do). */ +static reg_errcode_t +link_nfa_nodes (void *extra, bin_tree_t *node) +{ + re_dfa_t *dfa = (re_dfa_t *) extra; + Idx idx = node->node_idx; + reg_errcode_t err = REG_NOERROR; + + switch (node->token.type) + { + case CONCAT: + break; + + case END_OF_RE: + assert (node->next == NULL); + break; + + case OP_DUP_ASTERISK: + case OP_ALT: + { + Idx left, right; + dfa->has_plural_match = 1; + if (node->left != NULL) + left = node->left->first->node_idx; + else + left = node->next->node_idx; + if (node->right != NULL) + right = node->right->first->node_idx; + else + right = node->next->node_idx; + assert (REG_VALID_INDEX (left)); + assert (REG_VALID_INDEX (right)); + err = re_node_set_init_2 (dfa->edests + idx, left, right); + } + break; + + case ANCHOR: + case OP_OPEN_SUBEXP: + case OP_CLOSE_SUBEXP: + err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx); + break; + + case OP_BACK_REF: + dfa->nexts[idx] = node->next->node_idx; + if (node->token.type == OP_BACK_REF) + err = re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]); + break; + + default: + assert (!IS_EPSILON_NODE (node->token.type)); + dfa->nexts[idx] = node->next->node_idx; + break; + } + + return err; +} + +/* Duplicate the epsilon closure of the node ROOT_NODE. + Note that duplicated nodes have constraint INIT_CONSTRAINT in addition + to their own constraint. */ + +static reg_errcode_t +internal_function +duplicate_node_closure (re_dfa_t *dfa, Idx top_org_node, Idx top_clone_node, + Idx root_node, unsigned int init_constraint) +{ + Idx org_node, clone_node; + bool ok; + unsigned int constraint = init_constraint; + for (org_node = top_org_node, clone_node = top_clone_node;;) + { + Idx org_dest, clone_dest; + if (dfa->nodes[org_node].type == OP_BACK_REF) + { + /* If the back reference epsilon-transit, its destination must + also have the constraint. Then duplicate the epsilon closure + of the destination of the back reference, and store it in + edests of the back reference. */ + org_dest = dfa->nexts[org_node]; + re_node_set_empty (dfa->edests + clone_node); + clone_dest = duplicate_node (dfa, org_dest, constraint); + if (BE (clone_dest == REG_MISSING, 0)) + return REG_ESPACE; + dfa->nexts[clone_node] = dfa->nexts[org_node]; + ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (! ok, 0)) + return REG_ESPACE; + } + else if (dfa->edests[org_node].nelem == 0) + { + /* In case of the node can't epsilon-transit, don't duplicate the + destination and store the original destination as the + destination of the node. */ + dfa->nexts[clone_node] = dfa->nexts[org_node]; + break; + } + else if (dfa->edests[org_node].nelem == 1) + { + /* In case of the node can epsilon-transit, and it has only one + destination. */ + org_dest = dfa->edests[org_node].elems[0]; + re_node_set_empty (dfa->edests + clone_node); + /* If the node is root_node itself, it means the epsilon closure + has a loop. Then tie it to the destination of the root_node. */ + if (org_node == root_node && clone_node != org_node) + { + ok = re_node_set_insert (dfa->edests + clone_node, org_dest); + if (BE (! ok, 0)) + return REG_ESPACE; + break; + } + /* In case the node has another constraint, append it. */ + constraint |= dfa->nodes[org_node].constraint; + clone_dest = duplicate_node (dfa, org_dest, constraint); + if (BE (clone_dest == REG_MISSING, 0)) + return REG_ESPACE; + ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (! ok, 0)) + return REG_ESPACE; + } + else /* dfa->edests[org_node].nelem == 2 */ + { + /* In case of the node can epsilon-transit, and it has two + destinations. In the bin_tree_t and DFA, that's '|' and '*'. */ + org_dest = dfa->edests[org_node].elems[0]; + re_node_set_empty (dfa->edests + clone_node); + /* Search for a duplicated node which satisfies the constraint. */ + clone_dest = search_duplicated_node (dfa, org_dest, constraint); + if (clone_dest == REG_MISSING) + { + /* There is no such duplicated node, create a new one. */ + reg_errcode_t err; + clone_dest = duplicate_node (dfa, org_dest, constraint); + if (BE (clone_dest == REG_MISSING, 0)) + return REG_ESPACE; + ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (! ok, 0)) + return REG_ESPACE; + err = duplicate_node_closure (dfa, org_dest, clone_dest, + root_node, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + } + else + { + /* There is a duplicated node which satisfies the constraint, + use it to avoid infinite loop. */ + ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (! ok, 0)) + return REG_ESPACE; + } + + org_dest = dfa->edests[org_node].elems[1]; + clone_dest = duplicate_node (dfa, org_dest, constraint); + if (BE (clone_dest == REG_MISSING, 0)) + return REG_ESPACE; + ok = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (! ok, 0)) + return REG_ESPACE; + } + org_node = org_dest; + clone_node = clone_dest; + } + return REG_NOERROR; +} + +/* Search for a node which is duplicated from the node ORG_NODE, and + satisfies the constraint CONSTRAINT. */ + +static Idx +search_duplicated_node (const re_dfa_t *dfa, Idx org_node, + unsigned int constraint) +{ + Idx idx; + for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx) + { + if (org_node == dfa->org_indices[idx] + && constraint == dfa->nodes[idx].constraint) + return idx; /* Found. */ + } + return REG_MISSING; /* Not found. */ +} + +/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT. + Return the index of the new node, or REG_MISSING if insufficient storage is + available. */ + +static Idx +duplicate_node (re_dfa_t *dfa, Idx org_idx, unsigned int constraint) +{ + Idx dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]); + if (BE (dup_idx != REG_MISSING, 1)) + { + dfa->nodes[dup_idx].constraint = constraint; + dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].constraint; + dfa->nodes[dup_idx].duplicated = 1; + + /* Store the index of the original node. */ + dfa->org_indices[dup_idx] = org_idx; + } + return dup_idx; +} + +static reg_errcode_t +calc_inveclosure (re_dfa_t *dfa) +{ + Idx src, idx; + bool ok; + for (idx = 0; idx < dfa->nodes_len; ++idx) + re_node_set_init_empty (dfa->inveclosures + idx); + + for (src = 0; src < dfa->nodes_len; ++src) + { + Idx *elems = dfa->eclosures[src].elems; + for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx) + { + ok = re_node_set_insert_last (dfa->inveclosures + elems[idx], src); + if (BE (! ok, 0)) + return REG_ESPACE; + } + } + + return REG_NOERROR; +} + +/* Calculate "eclosure" for all the node in DFA. */ + +static reg_errcode_t +calc_eclosure (re_dfa_t *dfa) +{ + Idx node_idx; + bool incomplete; +#ifdef DEBUG + assert (dfa->nodes_len > 0); +#endif + incomplete = false; + /* For each nodes, calculate epsilon closure. */ + for (node_idx = 0; ; ++node_idx) + { + reg_errcode_t err; + re_node_set eclosure_elem; + if (node_idx == dfa->nodes_len) + { + if (!incomplete) + break; + incomplete = false; + node_idx = 0; + } + +#ifdef DEBUG + assert (dfa->eclosures[node_idx].nelem != REG_MISSING); +#endif + + /* If we have already calculated, skip it. */ + if (dfa->eclosures[node_idx].nelem != 0) + continue; + /* Calculate epsilon closure of `node_idx'. */ + err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, true); + if (BE (err != REG_NOERROR, 0)) + return err; + + if (dfa->eclosures[node_idx].nelem == 0) + { + incomplete = true; + re_node_set_free (&eclosure_elem); + } + } + return REG_NOERROR; +} + +/* Calculate epsilon closure of NODE. */ + +static reg_errcode_t +calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, Idx node, bool root) +{ + reg_errcode_t err; + Idx i; + re_node_set eclosure; + bool ok; + bool incomplete = false; + err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* This indicates that we are calculating this node now. + We reference this value to avoid infinite loop. */ + dfa->eclosures[node].nelem = REG_MISSING; + + /* If the current node has constraints, duplicate all nodes + since they must inherit the constraints. */ + if (dfa->nodes[node].constraint + && dfa->edests[node].nelem + && !dfa->nodes[dfa->edests[node].elems[0]].duplicated) + { + err = duplicate_node_closure (dfa, node, node, node, + dfa->nodes[node].constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + /* Expand each epsilon destination nodes. */ + if (IS_EPSILON_NODE(dfa->nodes[node].type)) + for (i = 0; i < dfa->edests[node].nelem; ++i) + { + re_node_set eclosure_elem; + Idx edest = dfa->edests[node].elems[i]; + /* If calculating the epsilon closure of `edest' is in progress, + return intermediate result. */ + if (dfa->eclosures[edest].nelem == REG_MISSING) + { + incomplete = true; + continue; + } + /* If we haven't calculated the epsilon closure of `edest' yet, + calculate now. Otherwise use calculated epsilon closure. */ + if (dfa->eclosures[edest].nelem == 0) + { + err = calc_eclosure_iter (&eclosure_elem, dfa, edest, false); + if (BE (err != REG_NOERROR, 0)) + return err; + } + else + eclosure_elem = dfa->eclosures[edest]; + /* Merge the epsilon closure of `edest'. */ + err = re_node_set_merge (&eclosure, &eclosure_elem); + if (BE (err != REG_NOERROR, 0)) + return err; + /* If the epsilon closure of `edest' is incomplete, + the epsilon closure of this node is also incomplete. */ + if (dfa->eclosures[edest].nelem == 0) + { + incomplete = true; + re_node_set_free (&eclosure_elem); + } + } + + /* An epsilon closure includes itself. */ + ok = re_node_set_insert (&eclosure, node); + if (BE (! ok, 0)) + return REG_ESPACE; + if (incomplete && !root) + dfa->eclosures[node].nelem = 0; + else + dfa->eclosures[node] = eclosure; + *new_set = eclosure; + return REG_NOERROR; +} + +/* Functions for token which are used in the parser. */ + +/* Fetch a token from INPUT. + We must not use this function inside bracket expressions. */ + +static void +internal_function +fetch_token (re_token_t *result, re_string_t *input, reg_syntax_t syntax) +{ + re_string_skip_bytes (input, peek_token (result, input, syntax)); +} + +/* Peek a token from INPUT, and return the length of the token. + We must not use this function inside bracket expressions. */ + +static int +internal_function +peek_token (re_token_t *token, re_string_t *input, reg_syntax_t syntax) +{ + unsigned char c; + + if (re_string_eoi (input)) + { + token->type = END_OF_RE; + return 0; + } + + c = re_string_peek_byte (input, 0); + token->opr.c = c; + + token->word_char = 0; +#ifdef RE_ENABLE_I18N + token->mb_partial = 0; + if (input->mb_cur_max > 1 && + !re_string_first_byte (input, re_string_cur_idx (input))) + { + token->type = CHARACTER; + token->mb_partial = 1; + return 1; + } +#endif + if (c == '\\') + { + unsigned char c2; + if (re_string_cur_idx (input) + 1 >= re_string_length (input)) + { + token->type = BACK_SLASH; + return 1; + } + + c2 = re_string_peek_byte_case (input, 1); + token->opr.c = c2; + token->type = CHARACTER; +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1) + { + wint_t wc = re_string_wchar_at (input, + re_string_cur_idx (input) + 1); + token->word_char = IS_WIDE_WORD_CHAR (wc) != 0; + } + else +#endif + token->word_char = IS_WORD_CHAR (c2) != 0; + + switch (c2) + { + case '|': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR)) + token->type = OP_ALT; + break; + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + if (!(syntax & RE_NO_BK_REFS)) + { + token->type = OP_BACK_REF; + token->opr.idx = c2 - '1'; + } + break; + case '<': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = WORD_FIRST; + } + break; + case '>': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = WORD_LAST; + } + break; + case 'b': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = WORD_DELIM; + } + break; + case 'B': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = NOT_WORD_DELIM; + } + break; + case 'w': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_WORD; + break; + case 'W': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_NOTWORD; + break; + case 's': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_SPACE; + break; + case 'S': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_NOTSPACE; + break; + case '`': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = BUF_FIRST; + } + break; + case '\'': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = BUF_LAST; + } + break; + case '(': + if (!(syntax & RE_NO_BK_PARENS)) + token->type = OP_OPEN_SUBEXP; + break; + case ')': + if (!(syntax & RE_NO_BK_PARENS)) + token->type = OP_CLOSE_SUBEXP; + break; + case '+': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_PLUS; + break; + case '?': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_QUESTION; + break; + case '{': + if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES))) + token->type = OP_OPEN_DUP_NUM; + break; + case '}': + if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES))) + token->type = OP_CLOSE_DUP_NUM; + break; + default: + break; + } + return 2; + } + + token->type = CHARACTER; +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1) + { + wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input)); + token->word_char = IS_WIDE_WORD_CHAR (wc) != 0; + } + else +#endif + token->word_char = IS_WORD_CHAR (token->opr.c); + + switch (c) + { + case '\n': + if (syntax & RE_NEWLINE_ALT) + token->type = OP_ALT; + break; + case '|': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR)) + token->type = OP_ALT; + break; + case '*': + token->type = OP_DUP_ASTERISK; + break; + case '+': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_PLUS; + break; + case '?': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_QUESTION; + break; + case '{': + if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + token->type = OP_OPEN_DUP_NUM; + break; + case '}': + if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + token->type = OP_CLOSE_DUP_NUM; + break; + case '(': + if (syntax & RE_NO_BK_PARENS) + token->type = OP_OPEN_SUBEXP; + break; + case ')': + if (syntax & RE_NO_BK_PARENS) + token->type = OP_CLOSE_SUBEXP; + break; + case '[': + token->type = OP_OPEN_BRACKET; + break; + case '.': + token->type = OP_PERIOD; + break; + case '^': + if (!(syntax & (RE_CONTEXT_INDEP_ANCHORS | RE_CARET_ANCHORS_HERE)) && + re_string_cur_idx (input) != 0) + { + char prev = re_string_peek_byte (input, -1); + if (!(syntax & RE_NEWLINE_ALT) || prev != '\n') + break; + } + token->type = ANCHOR; + token->opr.ctx_type = LINE_FIRST; + break; + case '$': + if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) && + re_string_cur_idx (input) + 1 != re_string_length (input)) + { + re_token_t next; + re_string_skip_bytes (input, 1); + peek_token (&next, input, syntax); + re_string_skip_bytes (input, -1); + if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP) + break; + } + token->type = ANCHOR; + token->opr.ctx_type = LINE_LAST; + break; + default: + break; + } + return 1; +} + +/* Peek a token from INPUT, and return the length of the token. + We must not use this function out of bracket expressions. */ + +static int +internal_function +peek_token_bracket (re_token_t *token, re_string_t *input, reg_syntax_t syntax) +{ + unsigned char c; + if (re_string_eoi (input)) + { + token->type = END_OF_RE; + return 0; + } + c = re_string_peek_byte (input, 0); + token->opr.c = c; + +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1 && + !re_string_first_byte (input, re_string_cur_idx (input))) + { + token->type = CHARACTER; + return 1; + } +#endif /* RE_ENABLE_I18N */ + + if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) + && re_string_cur_idx (input) + 1 < re_string_length (input)) + { + /* In this case, '\' escape a character. */ + unsigned char c2; + re_string_skip_bytes (input, 1); + c2 = re_string_peek_byte (input, 0); + token->opr.c = c2; + token->type = CHARACTER; + return 1; + } + if (c == '[') /* '[' is a special char in a bracket exps. */ + { + unsigned char c2; + int token_len; + if (re_string_cur_idx (input) + 1 < re_string_length (input)) + c2 = re_string_peek_byte (input, 1); + else + c2 = 0; + token->opr.c = c2; + token_len = 2; + switch (c2) + { + case '.': + token->type = OP_OPEN_COLL_ELEM; + break; + case '=': + token->type = OP_OPEN_EQUIV_CLASS; + break; + case ':': + if (syntax & RE_CHAR_CLASSES) + { + token->type = OP_OPEN_CHAR_CLASS; + break; + } + /* else fall through. */ + default: + token->type = CHARACTER; + token->opr.c = c; + token_len = 1; + break; + } + return token_len; + } + switch (c) + { + case '-': + token->type = OP_CHARSET_RANGE; + break; + case ']': + token->type = OP_CLOSE_BRACKET; + break; + case '^': + token->type = OP_NON_MATCH_LIST; + break; + default: + token->type = CHARACTER; + } + return 1; +} + +/* Functions for parser. */ + +/* Entry point of the parser. + Parse the regular expression REGEXP and return the structure tree. + If an error is occured, ERR is set by error code, and return NULL. + This function build the following tree, from regular expression : + CAT + / \ + / \ + EOR + + CAT means concatenation. + EOR means end of regular expression. */ + +static bin_tree_t * +parse (re_string_t *regexp, regex_t *preg, reg_syntax_t syntax, + reg_errcode_t *err) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree, *eor, *root; + re_token_t current_token; + dfa->syntax = syntax; + fetch_token (¤t_token, regexp, syntax | RE_CARET_ANCHORS_HERE); + tree = parse_reg_exp (regexp, preg, ¤t_token, syntax, 0, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + eor = create_tree (dfa, NULL, NULL, END_OF_RE); + if (tree != NULL) + root = create_tree (dfa, tree, eor, CONCAT); + else + root = eor; + if (BE (eor == NULL || root == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + return root; +} + +/* This function build the following tree, from regular expression + |: + ALT + / \ + / \ + + + ALT means alternative, which represents the operator `|'. */ + +static bin_tree_t * +parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token, + reg_syntax_t syntax, Idx nest, reg_errcode_t *err) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree, *branch = NULL; + tree = parse_branch (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + + while (token->type == OP_ALT) + { + fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE); + if (token->type != OP_ALT && token->type != END_OF_RE + && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) + { + branch = parse_branch (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && branch == NULL, 0)) + return NULL; + } + else + branch = NULL; + tree = create_tree (dfa, tree, branch, OP_ALT); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + return tree; +} + +/* This function build the following tree, from regular expression + : + CAT + / \ + / \ + + + CAT means concatenation. */ + +static bin_tree_t * +parse_branch (re_string_t *regexp, regex_t *preg, re_token_t *token, + reg_syntax_t syntax, Idx nest, reg_errcode_t *err) +{ + bin_tree_t *tree, *expr; + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + tree = parse_expression (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + + while (token->type != OP_ALT && token->type != END_OF_RE + && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) + { + expr = parse_expression (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && expr == NULL, 0)) + { + return NULL; + } + if (tree != NULL && expr != NULL) + { + tree = create_tree (dfa, tree, expr, CONCAT); + if (tree == NULL) + { + *err = REG_ESPACE; + return NULL; + } + } + else if (tree == NULL) + tree = expr; + /* Otherwise expr == NULL, we don't need to create new tree. */ + } + return tree; +} + +/* This function build the following tree, from regular expression a*: + * + | + a +*/ + +static bin_tree_t * +parse_expression (re_string_t *regexp, regex_t *preg, re_token_t *token, + reg_syntax_t syntax, Idx nest, reg_errcode_t *err) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree; + switch (token->type) + { + case CHARACTER: + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + while (!re_string_eoi (regexp) + && !re_string_first_byte (regexp, re_string_cur_idx (regexp))) + { + bin_tree_t *mbc_remain; + fetch_token (token, regexp, syntax); + mbc_remain = create_token_tree (dfa, NULL, NULL, token); + tree = create_tree (dfa, tree, mbc_remain, CONCAT); + if (BE (mbc_remain == NULL || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + } +#endif + break; + case OP_OPEN_SUBEXP: + tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_OPEN_BRACKET: + tree = parse_bracket_exp (regexp, dfa, token, syntax, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_BACK_REF: + if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1)) + { + *err = REG_ESUBREG; + return NULL; + } + dfa->used_bkref_map |= 1 << token->opr.idx; + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + ++dfa->nbackref; + dfa->has_mb_node = 1; + break; + case OP_OPEN_DUP_NUM: + if (syntax & RE_CONTEXT_INVALID_DUP) + { + *err = REG_BADRPT; + return NULL; + } + /* FALLTHROUGH */ + case OP_DUP_ASTERISK: + case OP_DUP_PLUS: + case OP_DUP_QUESTION: + if (syntax & RE_CONTEXT_INVALID_OPS) + { + *err = REG_BADRPT; + return NULL; + } + else if (syntax & RE_CONTEXT_INDEP_OPS) + { + fetch_token (token, regexp, syntax); + return parse_expression (regexp, preg, token, syntax, nest, err); + } + /* else fall through */ + case OP_CLOSE_SUBEXP: + if ((token->type == OP_CLOSE_SUBEXP) && + !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)) + { + *err = REG_ERPAREN; + return NULL; + } + /* else fall through */ + case OP_CLOSE_DUP_NUM: + /* We treat it as a normal character. */ + + /* Then we can these characters as normal characters. */ + token->type = CHARACTER; + /* mb_partial and word_char bits should be initialized already + by peek_token. */ + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + break; + case ANCHOR: + if ((token->opr.ctx_type + & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST)) + && dfa->word_ops_used == 0) + init_word_char (dfa); + if (token->opr.ctx_type == WORD_DELIM + || token->opr.ctx_type == NOT_WORD_DELIM) + { + bin_tree_t *tree_first, *tree_last; + if (token->opr.ctx_type == WORD_DELIM) + { + token->opr.ctx_type = WORD_FIRST; + tree_first = create_token_tree (dfa, NULL, NULL, token); + token->opr.ctx_type = WORD_LAST; + } + else + { + token->opr.ctx_type = INSIDE_WORD; + tree_first = create_token_tree (dfa, NULL, NULL, token); + token->opr.ctx_type = INSIDE_NOTWORD; + } + tree_last = create_token_tree (dfa, NULL, NULL, token); + tree = create_tree (dfa, tree_first, tree_last, OP_ALT); + if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + else + { + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + /* We must return here, since ANCHORs can't be followed + by repetition operators. + eg. RE"^*" is invalid or "", + it must not be "". */ + fetch_token (token, regexp, syntax); + return tree; + case OP_PERIOD: + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + if (dfa->mb_cur_max > 1) + dfa->has_mb_node = 1; + break; + case OP_WORD: + case OP_NOTWORD: + tree = build_charclass_op (dfa, regexp->trans, + (const unsigned char *) "alnum", + (const unsigned char *) "_", + token->type == OP_NOTWORD, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_SPACE: + case OP_NOTSPACE: + tree = build_charclass_op (dfa, regexp->trans, + (const unsigned char *) "space", + (const unsigned char *) "", + token->type == OP_NOTSPACE, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_ALT: + case END_OF_RE: + return NULL; + case BACK_SLASH: + *err = REG_EESCAPE; + return NULL; + default: + /* Must not happen? */ +#ifdef DEBUG + assert (0); +#endif + return NULL; + } + fetch_token (token, regexp, syntax); + + while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS + || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM) + { + tree = parse_dup_op (tree, regexp, dfa, token, syntax, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + /* In BRE consecutive duplications are not allowed. */ + if ((syntax & RE_CONTEXT_INVALID_DUP) + && (token->type == OP_DUP_ASTERISK + || token->type == OP_OPEN_DUP_NUM)) + { + *err = REG_BADRPT; + return NULL; + } + } + + return tree; +} + +/* This function build the following tree, from regular expression + (): + SUBEXP + | + +*/ + +static bin_tree_t * +parse_sub_exp (re_string_t *regexp, regex_t *preg, re_token_t *token, + reg_syntax_t syntax, Idx nest, reg_errcode_t *err) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree; + size_t cur_nsub; + cur_nsub = preg->re_nsub++; + + fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE); + + /* The subexpression may be a null string. */ + if (token->type == OP_CLOSE_SUBEXP) + tree = NULL; + else + { + tree = parse_reg_exp (regexp, preg, token, syntax, nest, err); + if (BE (*err == REG_NOERROR && token->type != OP_CLOSE_SUBEXP, 0)) + *err = REG_EPAREN; + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } + + if (cur_nsub <= '9' - '1') + dfa->completed_bkref_map |= 1 << cur_nsub; + + tree = create_tree (dfa, tree, NULL, SUBEXP); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + tree->token.opr.idx = cur_nsub; + return tree; +} + +/* This function parse repetition operators like "*", "+", "{1,3}" etc. */ + +static bin_tree_t * +parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa, + re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err) +{ + bin_tree_t *tree = NULL, *old_tree = NULL; + Idx i, start, end, start_idx = re_string_cur_idx (regexp); + re_token_t start_token = *token; + + if (token->type == OP_OPEN_DUP_NUM) + { + end = 0; + start = fetch_number (regexp, token, syntax); + if (start == REG_MISSING) + { + if (token->type == CHARACTER && token->opr.c == ',') + start = 0; /* We treat "{,m}" as "{0,m}". */ + else + { + *err = REG_BADBR; /* {} is invalid. */ + return NULL; + } + } + if (BE (start != REG_ERROR, 1)) + { + /* We treat "{n}" as "{n,n}". */ + end = ((token->type == OP_CLOSE_DUP_NUM) ? start + : ((token->type == CHARACTER && token->opr.c == ',') + ? fetch_number (regexp, token, syntax) : REG_ERROR)); + } + if (BE (start == REG_ERROR || end == REG_ERROR, 0)) + { + /* Invalid sequence. */ + if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0)) + { + if (token->type == END_OF_RE) + *err = REG_EBRACE; + else + *err = REG_BADBR; + + return NULL; + } + + /* If the syntax bit is set, rollback. */ + re_string_set_index (regexp, start_idx); + *token = start_token; + token->type = CHARACTER; + /* mb_partial and word_char bits should be already initialized by + peek_token. */ + return elem; + } + + if (BE ((end != REG_MISSING && start > end) + || token->type != OP_CLOSE_DUP_NUM, 0)) + { + /* First number greater than second. */ + *err = REG_BADBR; + return NULL; + } + } + else + { + start = (token->type == OP_DUP_PLUS) ? 1 : 0; + end = (token->type == OP_DUP_QUESTION) ? 1 : REG_MISSING; + } + + fetch_token (token, regexp, syntax); + + if (BE (elem == NULL, 0)) + return NULL; + if (BE (start == 0 && end == 0, 0)) + { + postorder (elem, free_tree, NULL); + return NULL; + } + + /* Extract "{n,m}" to "...{0,}". */ + if (BE (start > 0, 0)) + { + tree = elem; + for (i = 2; i <= start; ++i) + { + elem = duplicate_tree (elem, dfa); + tree = create_tree (dfa, tree, elem, CONCAT); + if (BE (elem == NULL || tree == NULL, 0)) + goto parse_dup_op_espace; + } + + if (start == end) + return tree; + + /* Duplicate ELEM before it is marked optional. */ + elem = duplicate_tree (elem, dfa); + old_tree = tree; + } + else + old_tree = NULL; + + if (elem->token.type == SUBEXP) + postorder (elem, mark_opt_subexp, (void *) (long) elem->token.opr.idx); + + tree = create_tree (dfa, elem, NULL, + (end == REG_MISSING ? OP_DUP_ASTERISK : OP_ALT)); + if (BE (tree == NULL, 0)) + goto parse_dup_op_espace; + +/* From gnulib's "intprops.h": + True if the arithmetic type T is signed. */ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + + /* This loop is actually executed only when end != REG_MISSING, + to rewrite {0,n} as ((...?)?)?... We have + already created the start+1-th copy. */ + if (TYPE_SIGNED (Idx) || end != REG_MISSING) + for (i = start + 2; i <= end; ++i) + { + elem = duplicate_tree (elem, dfa); + tree = create_tree (dfa, tree, elem, CONCAT); + if (BE (elem == NULL || tree == NULL, 0)) + goto parse_dup_op_espace; + + tree = create_tree (dfa, tree, NULL, OP_ALT); + if (BE (tree == NULL, 0)) + goto parse_dup_op_espace; + } + + if (old_tree) + tree = create_tree (dfa, old_tree, tree, CONCAT); + + return tree; + + parse_dup_op_espace: + *err = REG_ESPACE; + return NULL; +} + +/* Size of the names for collating symbol/equivalence_class/character_class. + I'm not sure, but maybe enough. */ +#define BRACKET_NAME_BUF_SIZE 32 + +#ifndef _LIBC + /* Local function for parse_bracket_exp only used in case of NOT _LIBC. + Build the range expression which starts from START_ELEM, and ends + at END_ELEM. The result are written to MBCSET and SBCSET. + RANGE_ALLOC is the allocated size of mbcset->range_starts, and + mbcset->range_ends, is a pointer argument sinse we may + update it. */ + +static reg_errcode_t +internal_function +# ifdef RE_ENABLE_I18N +build_range_exp (const reg_syntax_t syntax, + bitset_t sbcset, + re_charset_t *mbcset, + Idx *range_alloc, + const bracket_elem_t *start_elem, + const bracket_elem_t *end_elem) +# else /* not RE_ENABLE_I18N */ +build_range_exp (const reg_syntax_t syntax, + bitset_t sbcset, + const bracket_elem_t *start_elem, + const bracket_elem_t *end_elem) +# endif /* not RE_ENABLE_I18N */ +{ + unsigned int start_ch, end_ch; + /* Equivalence Classes and Character Classes can't be a range start/end. */ + if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS + || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS, + 0)) + return REG_ERANGE; + + /* We can handle no multi character collating elements without libc + support. */ + if (BE ((start_elem->type == COLL_SYM + && strlen ((char *) start_elem->opr.name) > 1) + || (end_elem->type == COLL_SYM + && strlen ((char *) end_elem->opr.name) > 1), 0)) + return REG_ECOLLATE; + +# ifdef RE_ENABLE_I18N + { + wchar_t wc; + wint_t start_wc; + wint_t end_wc; + wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'}; + + start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch + : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0] + : 0)); + end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch + : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0] + : 0)); + start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM) + ? __btowc (start_ch) : start_elem->opr.wch); + end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM) + ? __btowc (end_ch) : end_elem->opr.wch); + if (start_wc == WEOF || end_wc == WEOF) + return REG_ECOLLATE; + cmp_buf[0] = start_wc; + cmp_buf[4] = end_wc; + + if (BE ((syntax & RE_NO_EMPTY_RANGES) + && wcscoll (cmp_buf, cmp_buf + 4) > 0, 0)) + return REG_ERANGE; + + /* Got valid collation sequence values, add them as a new entry. + However, for !_LIBC we have no collation elements: if the + character set is single byte, the single byte character set + that we build below suffices. parse_bracket_exp passes + no MBCSET if dfa->mb_cur_max == 1. */ + if (mbcset) + { + /* Check the space of the arrays. */ + if (BE (*range_alloc == mbcset->nranges, 0)) + { + /* There is not enough space, need realloc. */ + wchar_t *new_array_start, *new_array_end; + Idx new_nranges; + + /* +1 in case of mbcset->nranges is 0. */ + new_nranges = 2 * mbcset->nranges + 1; + /* Use realloc since mbcset->range_starts and mbcset->range_ends + are NULL if *range_alloc == 0. */ + new_array_start = re_realloc (mbcset->range_starts, wchar_t, + new_nranges); + new_array_end = re_realloc (mbcset->range_ends, wchar_t, + new_nranges); + + if (BE (new_array_start == NULL || new_array_end == NULL, 0)) + return REG_ESPACE; + + mbcset->range_starts = new_array_start; + mbcset->range_ends = new_array_end; + *range_alloc = new_nranges; + } + + mbcset->range_starts[mbcset->nranges] = start_wc; + mbcset->range_ends[mbcset->nranges++] = end_wc; + } + + /* Build the table for single byte characters. */ + for (wc = 0; wc < SBC_MAX; ++wc) + { + cmp_buf[2] = wc; + if (wcscoll (cmp_buf, cmp_buf + 2) <= 0 + && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0) + bitset_set (sbcset, wc); + } + } +# else /* not RE_ENABLE_I18N */ + { + unsigned int ch; + start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch + : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0] + : 0)); + end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch + : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0] + : 0)); + if (start_ch > end_ch) + return REG_ERANGE; + /* Build the table for single byte characters. */ + for (ch = 0; ch < SBC_MAX; ++ch) + if (start_ch <= ch && ch <= end_ch) + bitset_set (sbcset, ch); + } +# endif /* not RE_ENABLE_I18N */ + return REG_NOERROR; +} +#endif /* not _LIBC */ + +#ifndef _LIBC +/* Helper function for parse_bracket_exp only used in case of NOT _LIBC.. + Build the collating element which is represented by NAME. + The result are written to MBCSET and SBCSET. + COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a + pointer argument since we may update it. */ + +static reg_errcode_t +internal_function +build_collating_symbol (bitset_t sbcset, +# ifdef RE_ENABLE_I18N + re_charset_t *mbcset, Idx *coll_sym_alloc, +# endif + const unsigned char *name) +{ + size_t name_len = strlen ((const char *) name); + if (BE (name_len != 1, 0)) + return REG_ECOLLATE; + else + { + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } +} +#endif /* not _LIBC */ + +/* This function parse bracket expression like "[abc]", "[a-c]", + "[[.a-a.]]" etc. */ + +static bin_tree_t * +parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token, + reg_syntax_t syntax, reg_errcode_t *err) +{ +#ifdef _LIBC + const unsigned char *collseqmb; + const char *collseqwc; + uint32_t nrules; + int32_t table_size; + const int32_t *symb_table; + const unsigned char *extra; + + /* Local function for parse_bracket_exp used in _LIBC environement. + Seek the collating symbol entry correspondings to NAME. + Return the index of the symbol in the SYMB_TABLE. */ + + auto inline int32_t + __attribute ((always_inline)) + seek_collating_symbol_entry (name, name_len) + const unsigned char *name; + size_t name_len; + { + int32_t hash = elem_hash ((const char *) name, name_len); + int32_t elem = hash % table_size; + if (symb_table[2 * elem] != 0) + { + int32_t second = hash % (table_size - 2) + 1; + + do + { + /* First compare the hashing value. */ + if (symb_table[2 * elem] == hash + /* Compare the length of the name. */ + && name_len == extra[symb_table[2 * elem + 1]] + /* Compare the name. */ + && memcmp (name, &extra[symb_table[2 * elem + 1] + 1], + name_len) == 0) + { + /* Yep, this is the entry. */ + break; + } + + /* Next entry. */ + elem += second; + } + while (symb_table[2 * elem] != 0); + } + return elem; + } + + /* Local function for parse_bracket_exp used in _LIBC environment. + Look up the collation sequence value of BR_ELEM. + Return the value if succeeded, UINT_MAX otherwise. */ + + auto inline unsigned int + __attribute ((always_inline)) + lookup_collation_sequence_value (br_elem) + bracket_elem_t *br_elem; + { + if (br_elem->type == SB_CHAR) + { + /* + if (MB_CUR_MAX == 1) + */ + if (nrules == 0) + return collseqmb[br_elem->opr.ch]; + else + { + wint_t wc = __btowc (br_elem->opr.ch); + return __collseq_table_lookup (collseqwc, wc); + } + } + else if (br_elem->type == MB_CHAR) + { + if (nrules != 0) + return __collseq_table_lookup (collseqwc, br_elem->opr.wch); + } + else if (br_elem->type == COLL_SYM) + { + size_t sym_name_len = strlen ((char *) br_elem->opr.name); + if (nrules != 0) + { + int32_t elem, idx; + elem = seek_collating_symbol_entry (br_elem->opr.name, + sym_name_len); + if (symb_table[2 * elem] != 0) + { + /* We found the entry. */ + idx = symb_table[2 * elem + 1]; + /* Skip the name of collating element name. */ + idx += 1 + extra[idx]; + /* Skip the byte sequence of the collating element. */ + idx += 1 + extra[idx]; + /* Adjust for the alignment. */ + idx = (idx + 3) & ~3; + /* Skip the multibyte collation sequence value. */ + idx += sizeof (unsigned int); + /* Skip the wide char sequence of the collating element. */ + idx += sizeof (unsigned int) * + (1 + *(unsigned int *) (extra + idx)); + /* Return the collation sequence value. */ + return *(unsigned int *) (extra + idx); + } + else if (symb_table[2 * elem] == 0 && sym_name_len == 1) + { + /* No valid character. Match it as a single byte + character. */ + return collseqmb[br_elem->opr.name[0]]; + } + } + else if (sym_name_len == 1) + return collseqmb[br_elem->opr.name[0]]; + } + return UINT_MAX; + } + + /* Local function for parse_bracket_exp used in _LIBC environement. + Build the range expression which starts from START_ELEM, and ends + at END_ELEM. The result are written to MBCSET and SBCSET. + RANGE_ALLOC is the allocated size of mbcset->range_starts, and + mbcset->range_ends, is a pointer argument sinse we may + update it. */ + + auto inline reg_errcode_t + __attribute ((always_inline)) + build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem) + re_charset_t *mbcset; + Idx *range_alloc; + bitset_t sbcset; + bracket_elem_t *start_elem, *end_elem; + { + unsigned int ch; + uint32_t start_collseq; + uint32_t end_collseq; + + /* Equivalence Classes and Character Classes can't be a range + start/end. */ + if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS + || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS, + 0)) + return REG_ERANGE; + + start_collseq = lookup_collation_sequence_value (start_elem); + end_collseq = lookup_collation_sequence_value (end_elem); + /* Check start/end collation sequence values. */ + if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0)) + return REG_ECOLLATE; + if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0)) + return REG_ERANGE; + + /* Got valid collation sequence values, add them as a new entry. + However, if we have no collation elements, and the character set + is single byte, the single byte character set that we + build below suffices. */ + if (nrules > 0 || dfa->mb_cur_max > 1) + { + /* Check the space of the arrays. */ + if (BE (*range_alloc == mbcset->nranges, 0)) + { + /* There is not enough space, need realloc. */ + uint32_t *new_array_start; + uint32_t *new_array_end; + Idx new_nranges; + + /* +1 in case of mbcset->nranges is 0. */ + new_nranges = 2 * mbcset->nranges + 1; + new_array_start = re_realloc (mbcset->range_starts, uint32_t, + new_nranges); + new_array_end = re_realloc (mbcset->range_ends, uint32_t, + new_nranges); + + if (BE (new_array_start == NULL || new_array_end == NULL, 0)) + return REG_ESPACE; + + mbcset->range_starts = new_array_start; + mbcset->range_ends = new_array_end; + *range_alloc = new_nranges; + } + + mbcset->range_starts[mbcset->nranges] = start_collseq; + mbcset->range_ends[mbcset->nranges++] = end_collseq; + } + + /* Build the table for single byte characters. */ + for (ch = 0; ch < SBC_MAX; ch++) + { + uint32_t ch_collseq; + /* + if (MB_CUR_MAX == 1) + */ + if (nrules == 0) + ch_collseq = collseqmb[ch]; + else + ch_collseq = __collseq_table_lookup (collseqwc, __btowc (ch)); + if (start_collseq <= ch_collseq && ch_collseq <= end_collseq) + bitset_set (sbcset, ch); + } + return REG_NOERROR; + } + + /* Local function for parse_bracket_exp used in _LIBC environement. + Build the collating element which is represented by NAME. + The result are written to MBCSET and SBCSET. + COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a + pointer argument sinse we may update it. */ + + auto inline reg_errcode_t + __attribute ((always_inline)) + build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name) + re_charset_t *mbcset; + Idx *coll_sym_alloc; + bitset_t sbcset; + const unsigned char *name; + { + int32_t elem, idx; + size_t name_len = strlen ((const char *) name); + if (nrules != 0) + { + elem = seek_collating_symbol_entry (name, name_len); + if (symb_table[2 * elem] != 0) + { + /* We found the entry. */ + idx = symb_table[2 * elem + 1]; + /* Skip the name of collating element name. */ + idx += 1 + extra[idx]; + } + else if (symb_table[2 * elem] == 0 && name_len == 1) + { + /* No valid character, treat it as a normal + character. */ + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } + else + return REG_ECOLLATE; + + /* Got valid collation sequence, add it as a new entry. */ + /* Check the space of the arrays. */ + if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0)) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->ncoll_syms is 0. */ + Idx new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1; + /* Use realloc since mbcset->coll_syms is NULL + if *alloc == 0. */ + int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t, + new_coll_sym_alloc); + if (BE (new_coll_syms == NULL, 0)) + return REG_ESPACE; + mbcset->coll_syms = new_coll_syms; + *coll_sym_alloc = new_coll_sym_alloc; + } + mbcset->coll_syms[mbcset->ncoll_syms++] = idx; + return REG_NOERROR; + } + else + { + if (BE (name_len != 1, 0)) + return REG_ECOLLATE; + else + { + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } + } + } +#endif + + re_token_t br_token; + re_bitset_ptr_t sbcset; +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; + Idx coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0; + Idx equiv_class_alloc = 0, char_class_alloc = 0; +#endif /* not RE_ENABLE_I18N */ + bool non_match = false; + bin_tree_t *work_tree; + int token_len; + bool first_round = true; +#ifdef _LIBC + collseqmb = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB); + nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules) + { + /* + if (MB_CUR_MAX > 1) + */ + collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC); + table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB); + symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_SYMB_TABLEMB); + extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_SYMB_EXTRAMB); + } +#endif + sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); +#ifdef RE_ENABLE_I18N + mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); +#endif /* RE_ENABLE_I18N */ +#ifdef RE_ENABLE_I18N + if (BE (sbcset == NULL || mbcset == NULL, 0)) +#else + if (BE (sbcset == NULL, 0)) +#endif /* RE_ENABLE_I18N */ + { + *err = REG_ESPACE; + return NULL; + } + + token_len = peek_token_bracket (token, regexp, syntax); + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_BADPAT; + goto parse_bracket_exp_free_return; + } + if (token->type == OP_NON_MATCH_LIST) + { +#ifdef RE_ENABLE_I18N + mbcset->non_match = 1; +#endif /* not RE_ENABLE_I18N */ + non_match = true; + if (syntax & RE_HAT_LISTS_NOT_NEWLINE) + bitset_set (sbcset, '\n'); + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + token_len = peek_token_bracket (token, regexp, syntax); + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_BADPAT; + goto parse_bracket_exp_free_return; + } + } + + /* We treat the first ']' as a normal character. */ + if (token->type == OP_CLOSE_BRACKET) + token->type = CHARACTER; + + while (1) + { + bracket_elem_t start_elem, end_elem; + unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE]; + unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE]; + reg_errcode_t ret; + int token_len2 = 0; + bool is_range_exp = false; + re_token_t token2; + + start_elem.opr.name = start_name_buf; + ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa, + syntax, first_round); + if (BE (ret != REG_NOERROR, 0)) + { + *err = ret; + goto parse_bracket_exp_free_return; + } + first_round = false; + + /* Get information about the next token. We need it in any case. */ + token_len = peek_token_bracket (token, regexp, syntax); + + /* Do not check for ranges if we know they are not allowed. */ + if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS) + { + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_EBRACK; + goto parse_bracket_exp_free_return; + } + if (token->type == OP_CHARSET_RANGE) + { + re_string_skip_bytes (regexp, token_len); /* Skip '-'. */ + token_len2 = peek_token_bracket (&token2, regexp, syntax); + if (BE (token2.type == END_OF_RE, 0)) + { + *err = REG_EBRACK; + goto parse_bracket_exp_free_return; + } + if (token2.type == OP_CLOSE_BRACKET) + { + /* We treat the last '-' as a normal character. */ + re_string_skip_bytes (regexp, -token_len); + token->type = CHARACTER; + } + else + is_range_exp = true; + } + } + + if (is_range_exp == true) + { + end_elem.opr.name = end_name_buf; + ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2, + dfa, syntax, true); + if (BE (ret != REG_NOERROR, 0)) + { + *err = ret; + goto parse_bracket_exp_free_return; + } + + token_len = peek_token_bracket (token, regexp, syntax); + +#ifdef _LIBC + *err = build_range_exp (sbcset, mbcset, &range_alloc, + &start_elem, &end_elem); +#else +# ifdef RE_ENABLE_I18N + *err = build_range_exp (syntax, sbcset, + dfa->mb_cur_max > 1 ? mbcset : NULL, + &range_alloc, &start_elem, &end_elem); +# else + *err = build_range_exp (syntax, sbcset, &start_elem, &end_elem); +# endif +#endif /* RE_ENABLE_I18N */ + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + } + else + { + switch (start_elem.type) + { + case SB_CHAR: + bitset_set (sbcset, start_elem.opr.ch); + break; +#ifdef RE_ENABLE_I18N + case MB_CHAR: + /* Check whether the array has enough space. */ + if (BE (mbchar_alloc == mbcset->nmbchars, 0)) + { + wchar_t *new_mbchars; + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nmbchars is 0. */ + mbchar_alloc = 2 * mbcset->nmbchars + 1; + /* Use realloc since array is NULL if *alloc == 0. */ + new_mbchars = re_realloc (mbcset->mbchars, wchar_t, + mbchar_alloc); + if (BE (new_mbchars == NULL, 0)) + goto parse_bracket_exp_espace; + mbcset->mbchars = new_mbchars; + } + mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch; + break; +#endif /* RE_ENABLE_I18N */ + case EQUIV_CLASS: + *err = build_equiv_class (sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &equiv_class_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + case COLL_SYM: + *err = build_collating_symbol (sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &coll_sym_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + case CHAR_CLASS: + *err = build_charclass (regexp->trans, sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &char_class_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name, syntax); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + default: + assert (0); + break; + } + } + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_EBRACK; + goto parse_bracket_exp_free_return; + } + if (token->type == OP_CLOSE_BRACKET) + break; + } + + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + + /* If it is non-matching list. */ + if (non_match) + bitset_not (sbcset); + +#ifdef RE_ENABLE_I18N + /* Ensure only single byte characters are set. */ + if (dfa->mb_cur_max > 1) + bitset_mask (sbcset, dfa->sb_char); + + if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes + || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes + || mbcset->non_match))) + { + bin_tree_t *mbc_tree; + int sbc_idx; + /* Build a tree for complex bracket. */ + dfa->has_mb_node = 1; + br_token.type = COMPLEX_BRACKET; + br_token.opr.mbcset = mbcset; + mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (mbc_tree == NULL, 0)) + goto parse_bracket_exp_espace; + for (sbc_idx = 0; sbc_idx < BITSET_WORDS; ++sbc_idx) + if (sbcset[sbc_idx]) + break; + /* If there are no bits set in sbcset, there is no point + of having both SIMPLE_BRACKET and COMPLEX_BRACKET. */ + if (sbc_idx < BITSET_WORDS) + { + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; + br_token.opr.sbcset = sbcset; + work_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (work_tree == NULL, 0)) + goto parse_bracket_exp_espace; + + /* Then join them by ALT node. */ + work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT); + if (BE (work_tree == NULL, 0)) + goto parse_bracket_exp_espace; + } + else + { + re_free (sbcset); + work_tree = mbc_tree; + } + } + else +#endif /* not RE_ENABLE_I18N */ + { +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; + br_token.opr.sbcset = sbcset; + work_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (work_tree == NULL, 0)) + goto parse_bracket_exp_espace; + } + return work_tree; + + parse_bracket_exp_espace: + *err = REG_ESPACE; + parse_bracket_exp_free_return: + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + return NULL; +} + +/* Parse an element in the bracket expression. */ + +static reg_errcode_t +parse_bracket_element (bracket_elem_t *elem, re_string_t *regexp, + re_token_t *token, int token_len, re_dfa_t *dfa, + reg_syntax_t syntax, bool accept_hyphen) +{ +#ifdef RE_ENABLE_I18N + int cur_char_size; + cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp)); + if (cur_char_size > 1) + { + elem->type = MB_CHAR; + elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp)); + re_string_skip_bytes (regexp, cur_char_size); + return REG_NOERROR; + } +#endif /* RE_ENABLE_I18N */ + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS + || token->type == OP_OPEN_EQUIV_CLASS) + return parse_bracket_symbol (elem, regexp, token); + if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen) + { + /* A '-' must only appear as anything but a range indicator before + the closing bracket. Everything else is an error. */ + re_token_t token2; + (void) peek_token_bracket (&token2, regexp, syntax); + if (token2.type != OP_CLOSE_BRACKET) + /* The actual error value is not standardized since this whole + case is undefined. But ERANGE makes good sense. */ + return REG_ERANGE; + } + elem->type = SB_CHAR; + elem->opr.ch = token->opr.c; + return REG_NOERROR; +} + +/* Parse a bracket symbol in the bracket expression. Bracket symbols are + such as [::], [..], and + [==]. */ + +static reg_errcode_t +parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp, + re_token_t *token) +{ + unsigned char ch, delim = token->opr.c; + int i = 0; + if (re_string_eoi(regexp)) + return REG_EBRACK; + for (;; ++i) + { + if (i >= BRACKET_NAME_BUF_SIZE) + return REG_EBRACK; + if (token->type == OP_OPEN_CHAR_CLASS) + ch = re_string_fetch_byte_case (regexp); + else + ch = re_string_fetch_byte (regexp); + if (re_string_eoi(regexp)) + return REG_EBRACK; + if (ch == delim && re_string_peek_byte (regexp, 0) == ']') + break; + elem->opr.name[i] = ch; + } + re_string_skip_bytes (regexp, 1); + elem->opr.name[i] = '\0'; + switch (token->type) + { + case OP_OPEN_COLL_ELEM: + elem->type = COLL_SYM; + break; + case OP_OPEN_EQUIV_CLASS: + elem->type = EQUIV_CLASS; + break; + case OP_OPEN_CHAR_CLASS: + elem->type = CHAR_CLASS; + break; + default: + break; + } + return REG_NOERROR; +} + + /* Helper function for parse_bracket_exp. + Build the equivalence class which is represented by NAME. + The result are written to MBCSET and SBCSET. + EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes, + is a pointer argument sinse we may update it. */ + +static reg_errcode_t +#ifdef RE_ENABLE_I18N +build_equiv_class (bitset_t sbcset, re_charset_t *mbcset, + Idx *equiv_class_alloc, const unsigned char *name) +#else /* not RE_ENABLE_I18N */ +build_equiv_class (bitset_t sbcset, const unsigned char *name) +#endif /* not RE_ENABLE_I18N */ +{ +#ifdef _LIBC + uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules != 0) + { + const int32_t *table, *indirect; + const unsigned char *weights, *extra, *cp; + unsigned char char_buf[2]; + int32_t idx1, idx2; + unsigned int ch; + size_t len; + /* This #include defines a local function! */ +# include + /* Calculate the index for equivalence class. */ + cp = name; + table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_WEIGHTMB); + extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_INDIRECTMB); + idx1 = findidx (&cp); + if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0)) + /* This isn't a valid character. */ + return REG_ECOLLATE; + + /* Build single byte matcing table for this equivalence class. */ + char_buf[1] = (unsigned char) '\0'; + len = weights[idx1 & 0xffffff]; + for (ch = 0; ch < SBC_MAX; ++ch) + { + char_buf[0] = ch; + cp = char_buf; + idx2 = findidx (&cp); +/* + idx2 = table[ch]; +*/ + if (idx2 == 0) + /* This isn't a valid character. */ + continue; + /* Compare only if the length matches and the collation rule + index is the same. */ + if (len == weights[idx2 & 0xffffff] && (idx1 >> 24) == (idx2 >> 24)) + { + int cnt = 0; + + while (cnt <= len && + weights[(idx1 & 0xffffff) + 1 + cnt] + == weights[(idx2 & 0xffffff) + 1 + cnt]) + ++cnt; + + if (cnt > len) + bitset_set (sbcset, ch); + } + } + /* Check whether the array has enough space. */ + if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0)) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nequiv_classes is 0. */ + Idx new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1; + /* Use realloc since the array is NULL if *alloc == 0. */ + int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes, + int32_t, + new_equiv_class_alloc); + if (BE (new_equiv_classes == NULL, 0)) + return REG_ESPACE; + mbcset->equiv_classes = new_equiv_classes; + *equiv_class_alloc = new_equiv_class_alloc; + } + mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1; + } + else +#endif /* _LIBC */ + { + if (BE (strlen ((const char *) name) != 1, 0)) + return REG_ECOLLATE; + bitset_set (sbcset, *name); + } + return REG_NOERROR; +} + + /* Helper function for parse_bracket_exp. + Build the character class which is represented by NAME. + The result are written to MBCSET and SBCSET. + CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes, + is a pointer argument sinse we may update it. */ + +static reg_errcode_t +#ifdef RE_ENABLE_I18N +build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, + re_charset_t *mbcset, Idx *char_class_alloc, + const unsigned char *class_name, reg_syntax_t syntax) +#else /* not RE_ENABLE_I18N */ +build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, + const unsigned char *class_name, reg_syntax_t syntax) +#endif /* not RE_ENABLE_I18N */ +{ + int i; + const char *name = (const char *) class_name; + + /* In case of REG_ICASE "upper" and "lower" match the both of + upper and lower cases. */ + if ((syntax & RE_ICASE) + && (strcmp (name, "upper") == 0 || strcmp (name, "lower") == 0)) + name = "alpha"; + +#ifdef RE_ENABLE_I18N + /* Check the space of the arrays. */ + if (BE (*char_class_alloc == mbcset->nchar_classes, 0)) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nchar_classes is 0. */ + Idx new_char_class_alloc = 2 * mbcset->nchar_classes + 1; + /* Use realloc since array is NULL if *alloc == 0. */ + wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t, + new_char_class_alloc); + if (BE (new_char_classes == NULL, 0)) + return REG_ESPACE; + mbcset->char_classes = new_char_classes; + *char_class_alloc = new_char_class_alloc; + } + mbcset->char_classes[mbcset->nchar_classes++] = __wctype (name); +#endif /* RE_ENABLE_I18N */ + +#define BUILD_CHARCLASS_LOOP(ctype_func) \ + do { \ + if (BE (trans != NULL, 0)) \ + { \ + for (i = 0; i < SBC_MAX; ++i) \ + if (ctype_func (i)) \ + bitset_set (sbcset, trans[i]); \ + } \ + else \ + { \ + for (i = 0; i < SBC_MAX; ++i) \ + if (ctype_func (i)) \ + bitset_set (sbcset, i); \ + } \ + } while (0) + + if (strcmp (name, "alnum") == 0) + BUILD_CHARCLASS_LOOP (isalnum); + else if (strcmp (name, "cntrl") == 0) + BUILD_CHARCLASS_LOOP (iscntrl); + else if (strcmp (name, "lower") == 0) + BUILD_CHARCLASS_LOOP (islower); + else if (strcmp (name, "space") == 0) + BUILD_CHARCLASS_LOOP (isspace); + else if (strcmp (name, "alpha") == 0) + BUILD_CHARCLASS_LOOP (isalpha); + else if (strcmp (name, "digit") == 0) + BUILD_CHARCLASS_LOOP (isdigit); + else if (strcmp (name, "print") == 0) + BUILD_CHARCLASS_LOOP (isprint); + else if (strcmp (name, "upper") == 0) + BUILD_CHARCLASS_LOOP (isupper); + else if (strcmp (name, "blank") == 0) + BUILD_CHARCLASS_LOOP (isblank); + else if (strcmp (name, "graph") == 0) + BUILD_CHARCLASS_LOOP (isgraph); + else if (strcmp (name, "punct") == 0) + BUILD_CHARCLASS_LOOP (ispunct); + else if (strcmp (name, "xdigit") == 0) + BUILD_CHARCLASS_LOOP (isxdigit); + else + return REG_ECTYPE; + + return REG_NOERROR; +} + +static bin_tree_t * +build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans, + const unsigned char *class_name, + const unsigned char *extra, bool non_match, + reg_errcode_t *err) +{ + re_bitset_ptr_t sbcset; +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; + Idx alloc = 0; +#endif /* not RE_ENABLE_I18N */ + reg_errcode_t ret; + re_token_t br_token; + bin_tree_t *tree; + + sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); +#ifdef RE_ENABLE_I18N + mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); +#endif /* RE_ENABLE_I18N */ + +#ifdef RE_ENABLE_I18N + if (BE (sbcset == NULL || mbcset == NULL, 0)) +#else /* not RE_ENABLE_I18N */ + if (BE (sbcset == NULL, 0)) +#endif /* not RE_ENABLE_I18N */ + { + *err = REG_ESPACE; + return NULL; + } + + if (non_match) + { +#ifdef RE_ENABLE_I18N + mbcset->non_match = 1; +#endif /* not RE_ENABLE_I18N */ + } + + /* We don't care the syntax in this case. */ + ret = build_charclass (trans, sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &alloc, +#endif /* RE_ENABLE_I18N */ + class_name, 0); + + if (BE (ret != REG_NOERROR, 0)) + { + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + *err = ret; + return NULL; + } + /* \w match '_' also. */ + for (; *extra; extra++) + bitset_set (sbcset, *extra); + + /* If it is non-matching list. */ + if (non_match) + bitset_not (sbcset); + +#ifdef RE_ENABLE_I18N + /* Ensure only single byte characters are set. */ + if (dfa->mb_cur_max > 1) + bitset_mask (sbcset, dfa->sb_char); +#endif + + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; + br_token.opr.sbcset = sbcset; + tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (tree == NULL, 0)) + goto build_word_op_espace; + +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + bin_tree_t *mbc_tree; + /* Build a tree for complex bracket. */ + br_token.type = COMPLEX_BRACKET; + br_token.opr.mbcset = mbcset; + dfa->has_mb_node = 1; + mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (mbc_tree == NULL, 0)) + goto build_word_op_espace; + /* Then join them by ALT node. */ + tree = create_tree (dfa, tree, mbc_tree, OP_ALT); + if (BE (mbc_tree != NULL, 1)) + return tree; + } + else + { + free_charset (mbcset); + return tree; + } +#else /* not RE_ENABLE_I18N */ + return tree; +#endif /* not RE_ENABLE_I18N */ + + build_word_op_espace: + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + *err = REG_ESPACE; + return NULL; +} + +/* This is intended for the expressions like "a{1,3}". + Fetch a number from `input', and return the number. + Return REG_MISSING if the number field is empty like "{,1}". + Return REG_ERROR if an error occurred. */ + +static Idx +fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax) +{ + Idx num = REG_MISSING; + unsigned char c; + while (1) + { + fetch_token (token, input, syntax); + c = token->opr.c; + if (BE (token->type == END_OF_RE, 0)) + return REG_ERROR; + if (token->type == OP_CLOSE_DUP_NUM || c == ',') + break; + num = ((token->type != CHARACTER || c < '0' || '9' < c + || num == REG_ERROR) + ? REG_ERROR + : ((num == REG_MISSING) ? c - '0' : num * 10 + c - '0')); + num = (num > RE_DUP_MAX) ? REG_ERROR : num; + } + return num; +} + +#ifdef RE_ENABLE_I18N +static void +free_charset (re_charset_t *cset) +{ + re_free (cset->mbchars); +# ifdef _LIBC + re_free (cset->coll_syms); + re_free (cset->equiv_classes); + re_free (cset->range_starts); + re_free (cset->range_ends); +# endif + re_free (cset->char_classes); + re_free (cset); +} +#endif /* RE_ENABLE_I18N */ + +/* Functions for binary tree operation. */ + +/* Create a tree node. */ + +static bin_tree_t * +create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, + re_token_type_t type) +{ + re_token_t t; + t.type = type; + return create_token_tree (dfa, left, right, &t); +} + +static bin_tree_t * +create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, + const re_token_t *token) +{ + bin_tree_t *tree; + if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0)) + { + bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1); + + if (storage == NULL) + return NULL; + storage->next = dfa->str_tree_storage; + dfa->str_tree_storage = storage; + dfa->str_tree_storage_idx = 0; + } + tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++]; + + tree->parent = NULL; + tree->left = left; + tree->right = right; + tree->token = *token; + tree->token.duplicated = 0; + tree->token.opt_subexp = 0; + tree->first = NULL; + tree->next = NULL; + tree->node_idx = REG_MISSING; + + if (left != NULL) + left->parent = tree; + if (right != NULL) + right->parent = tree; + return tree; +} + +/* Mark the tree SRC as an optional subexpression. + To be called from preorder or postorder. */ + +static reg_errcode_t +mark_opt_subexp (void *extra, bin_tree_t *node) +{ + Idx idx = (Idx) (long) extra; + if (node->token.type == SUBEXP && node->token.opr.idx == idx) + node->token.opt_subexp = 1; + + return REG_NOERROR; +} + +/* Free the allocated memory inside NODE. */ + +static void +free_token (re_token_t *node) +{ +#ifdef RE_ENABLE_I18N + if (node->type == COMPLEX_BRACKET && node->duplicated == 0) + free_charset (node->opr.mbcset); + else +#endif /* RE_ENABLE_I18N */ + if (node->type == SIMPLE_BRACKET && node->duplicated == 0) + re_free (node->opr.sbcset); +} + +/* Worker function for tree walking. Free the allocated memory inside NODE + and its children. */ + +static reg_errcode_t +free_tree (void *extra, bin_tree_t *node) +{ + free_token (&node->token); + return REG_NOERROR; +} + + +/* Duplicate the node SRC, and return new node. This is a preorder + visit similar to the one implemented by the generic visitor, but + we need more infrastructure to maintain two parallel trees --- so, + it's easier to duplicate. */ + +static bin_tree_t * +duplicate_tree (const bin_tree_t *root, re_dfa_t *dfa) +{ + const bin_tree_t *node; + bin_tree_t *dup_root; + bin_tree_t **p_new = &dup_root, *dup_node = root->parent; + + for (node = root; ; ) + { + /* Create a new tree and link it back to the current parent. */ + *p_new = create_token_tree (dfa, NULL, NULL, &node->token); + if (*p_new == NULL) + return NULL; + (*p_new)->parent = dup_node; + (*p_new)->token.duplicated = 1; + dup_node = *p_new; + + /* Go to the left node, or up and to the right. */ + if (node->left) + { + node = node->left; + p_new = &dup_node->left; + } + else + { + const bin_tree_t *prev = NULL; + while (node->right == prev || node->right == NULL) + { + prev = node; + node = node->parent; + dup_node = dup_node->parent; + if (!node) + return dup_root; + } + node = node->right; + p_new = &dup_node->right; + } + } +} diff --git a/gnulib/regex.c b/gnulib/regex.c new file mode 100644 index 000000000..904fe62c8 --- /dev/null +++ b/gnulib/regex.c @@ -0,0 +1,72 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2005, 2006, 2009, 2010 Free Software Foundation, + Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + 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 + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Make sure noone compiles this code with a C++ compiler. */ +#if defined __cplusplus && defined _LIBC +# error "This is C code, use a C compiler" +#endif + +#ifdef _LIBC +/* We have to keep the namespace clean. */ +# define regfree(preg) __regfree (preg) +# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef) +# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags) +# define regerror(errcode, preg, errbuf, errbuf_size) \ + __regerror(errcode, preg, errbuf, errbuf_size) +# define re_set_registers(bu, re, nu, st, en) \ + __re_set_registers (bu, re, nu, st, en) +# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \ + __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) +# define re_match(bufp, string, size, pos, regs) \ + __re_match (bufp, string, size, pos, regs) +# define re_search(bufp, string, size, startpos, range, regs) \ + __re_search (bufp, string, size, startpos, range, regs) +# define re_compile_pattern(pattern, length, bufp) \ + __re_compile_pattern (pattern, length, bufp) +# define re_set_syntax(syntax) __re_set_syntax (syntax) +# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \ + __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop) +# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp) + +# include "../locale/localeinfo.h" +#endif + +/* On some systems, limits.h sets RE_DUP_MAX to a lower value than + GNU regex allows. Include it before , which correctly + #undefs RE_DUP_MAX and sets it to the right value. */ +#include + +#include +#include "regex_internal.h" + +#include "regex_internal.c" +#include "regcomp.c" +#include "regexec.c" + +/* Binary backward compatibility. */ +#if _LIBC +# include +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3) +link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.") +int re_max_failures = 2000; +# endif +#endif diff --git a/gnulib/regex.h b/gnulib/regex.h new file mode 100644 index 000000000..594d5e6aa --- /dev/null +++ b/gnulib/regex.h @@ -0,0 +1,676 @@ +/* Definitions for data structures and routines for the regular + expression library. + Copyright (C) 1985, 1989, 1990, 1991, 1992, 1993, 1995, 1996, 1997, 1998, + 2000, 2001, 2002, 2003, 2005, 2006, 2009, 2010 Free Software Foundation, + Inc. + This file is part of the GNU C Library. + + 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 + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _REGEX_H +#define _REGEX_H 1 + +#include + +/* Allow the use in C++ code. */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Define __USE_GNU_REGEX to declare GNU extensions that violate the + POSIX name space rules. */ +#undef __USE_GNU_REGEX +#if (defined _GNU_SOURCE \ + || (!defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE \ + && !defined _XOPEN_SOURCE)) +# define __USE_GNU_REGEX 1 +#endif + +#ifdef _REGEX_LARGE_OFFSETS + +/* Use types and values that are wide enough to represent signed and + unsigned byte offsets in memory. This currently works only when + the regex code is used outside of the GNU C library; it is not yet + supported within glibc itself, and glibc users should not define + _REGEX_LARGE_OFFSETS. */ + +/* The type of the offset of a byte within a string. + For historical reasons POSIX 1003.1-2004 requires that regoff_t be + at least as wide as off_t. However, many common POSIX platforms set + regoff_t to the more-sensible ssize_t and the Open Group has + signalled its intention to change the requirement to be that + regoff_t be at least as wide as ptrdiff_t and ssize_t; see XBD ERN + 60 (2005-08-25). We don't know of any hosts where ssize_t or + ptrdiff_t is wider than ssize_t, so ssize_t is safe. */ +typedef ssize_t regoff_t; + +/* The type of nonnegative object indexes. Traditionally, GNU regex + uses 'int' for these. Code that uses __re_idx_t should work + regardless of whether the type is signed. */ +typedef size_t __re_idx_t; + +/* The type of object sizes. */ +typedef size_t __re_size_t; + +/* The type of object sizes, in places where the traditional code + uses unsigned long int. */ +typedef size_t __re_long_size_t; + +#else + +/* Use types that are binary-compatible with the traditional GNU regex + implementation, which mishandles strings longer than INT_MAX. */ + +typedef int regoff_t; +typedef int __re_idx_t; +typedef unsigned int __re_size_t; +typedef unsigned long int __re_long_size_t; + +#endif + +/* The following two types have to be signed and unsigned integer type + wide enough to hold a value of a pointer. For most ANSI compilers + ptrdiff_t and size_t should be likely OK. Still size of these two + types is 2 for Microsoft C. Ugh... */ +typedef long int s_reg_t; +typedef unsigned long int active_reg_t; + +/* The following bits are used to determine the regexp syntax we + recognize. The set/not-set meanings are chosen so that Emacs syntax + remains the value 0. The bits are given in alphabetical order, and + the definitions shifted by one from the previous bit; thus, when we + add or remove a bit, only one other definition need change. */ +typedef unsigned long int reg_syntax_t; + +#ifdef __USE_GNU_REGEX + +/* If this bit is not set, then \ inside a bracket expression is literal. + If set, then such a \ quotes the following character. */ +# define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1) + +/* If this bit is not set, then + and ? are operators, and \+ and \? are + literals. + If set, then \+ and \? are operators and + and ? are literals. */ +# define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) + +/* If this bit is set, then character classes are supported. They are: + [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], + [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. + If not set, then character classes are not supported. */ +# define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) + +/* If this bit is set, then ^ and $ are always anchors (outside bracket + expressions, of course). + If this bit is not set, then it depends: + ^ is an anchor if it is at the beginning of a regular + expression or after an open-group or an alternation operator; + $ is an anchor if it is at the end of a regular expression, or + before a close-group or an alternation operator. + + This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because + POSIX draft 11.2 says that * etc. in leading positions is undefined. + We already implemented a previous draft which made those constructs + invalid, though, so we haven't changed the code back. */ +# define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) + +/* If this bit is set, then special characters are always special + regardless of where they are in the pattern. + If this bit is not set, then special characters are special only in + some contexts; otherwise they are ordinary. Specifically, + * + ? and intervals are only special when not after the beginning, + open-group, or alternation operator. */ +# define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) + +/* If this bit is set, then *, +, ?, and { cannot be first in an re or + immediately after an alternation or begin-group operator. */ +# define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) + +/* If this bit is set, then . matches newline. + If not set, then it doesn't. */ +# define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) + +/* If this bit is set, then . doesn't match NUL. + If not set, then it does. */ +# define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) + +/* If this bit is set, nonmatching lists [^...] do not match newline. + If not set, they do. */ +# define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) + +/* If this bit is set, either \{...\} or {...} defines an + interval, depending on RE_NO_BK_BRACES. + If not set, \{, \}, {, and } are literals. */ +# define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) + +/* If this bit is set, +, ? and | aren't recognized as operators. + If not set, they are. */ +# define RE_LIMITED_OPS (RE_INTERVALS << 1) + +/* If this bit is set, newline is an alternation operator. + If not set, newline is literal. */ +# define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) + +/* If this bit is set, then `{...}' defines an interval, and \{ and \} + are literals. + If not set, then `\{...\}' defines an interval. */ +# define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) + +/* If this bit is set, (...) defines a group, and \( and \) are literals. + If not set, \(...\) defines a group, and ( and ) are literals. */ +# define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) + +/* If this bit is set, then \ matches . + If not set, then \ is a back-reference. */ +# define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) + +/* If this bit is set, then | is an alternation operator, and \| is literal. + If not set, then \| is an alternation operator, and | is literal. */ +# define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) + +/* If this bit is set, then an ending range point collating higher + than the starting range point, as in [z-a], is invalid. + If not set, then when ending range point collates higher than the + starting range point, the range is ignored. */ +# define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) + +/* If this bit is set, then an unmatched ) is ordinary. + If not set, then an unmatched ) is invalid. */ +# define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) + +/* If this bit is set, succeed as soon as we match the whole pattern, + without further backtracking. */ +# define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1) + +/* If this bit is set, do not process the GNU regex operators. + If not set, then the GNU regex operators are recognized. */ +# define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1) + +/* If this bit is set, turn on internal regex debugging. + If not set, and debugging was on, turn it off. + This only works if regex.c is compiled -DDEBUG. + We define this bit always, so that all that's needed to turn on + debugging is to recompile regex.c; the calling code can always have + this bit set, and it won't affect anything in the normal case. */ +# define RE_DEBUG (RE_NO_GNU_OPS << 1) + +/* If this bit is set, a syntactically invalid interval is treated as + a string of ordinary characters. For example, the ERE 'a{1' is + treated as 'a\{1'. */ +# define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1) + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +# define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1) + +/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only + for ^, because it is difficult to scan the regex backwards to find + whether ^ should be special. */ +# define RE_CARET_ANCHORS_HERE (RE_ICASE << 1) + +/* If this bit is set, then \{ cannot be first in an bre or + immediately after an alternation or begin-group operator. */ +# define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1) + +/* If this bit is set, then no_sub will be set to 1 during + re_compile_pattern. */ +# define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1) + +#endif /* defined __USE_GNU_REGEX */ + +/* This global variable defines the particular regexp syntax to use (for + some interfaces). When a regexp is compiled, the syntax used is + stored in the pattern buffer, so changing this does not affect + already-compiled regexps. */ +extern reg_syntax_t re_syntax_options; + +#ifdef __USE_GNU_REGEX +/* Define combinations of the above bits for the standard possibilities. + (The [[[ comments delimit what gets put into the Texinfo file, so + don't delete them!) */ +/* [[[begin syntaxes]]] */ +# define RE_SYNTAX_EMACS 0 + +# define RE_SYNTAX_AWK \ + (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ + | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \ + | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS) + +# define RE_SYNTAX_GNU_AWK \ + ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG) \ + & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS \ + | RE_CONTEXT_INVALID_OPS )) + +# define RE_SYNTAX_POSIX_AWK \ + (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \ + | RE_INTERVALS | RE_NO_GNU_OPS) + +# define RE_SYNTAX_GREP \ + (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ + | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ + | RE_NEWLINE_ALT) + +# define RE_SYNTAX_EGREP \ + (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ + | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ + | RE_NO_BK_VBAR) + +# define RE_SYNTAX_POSIX_EGREP \ + (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \ + | RE_INVALID_INTERVAL_ORD) + +/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ +# define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC + +# define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC + +/* Syntax bits common to both basic and extended POSIX regex syntax. */ +# define _RE_SYNTAX_POSIX_COMMON \ + (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ + | RE_INTERVALS | RE_NO_EMPTY_RANGES) + +# define RE_SYNTAX_POSIX_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP) + +/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes + RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this + isn't minimal, since other operators, such as \`, aren't disabled. */ +# define RE_SYNTAX_POSIX_MINIMAL_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) + +# define RE_SYNTAX_POSIX_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD) + +/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is + removed and RE_NO_BK_REFS is added. */ +# define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) +/* [[[end syntaxes]]] */ + +#endif /* defined __USE_GNU_REGEX */ + +#ifdef __USE_GNU_REGEX + +/* Maximum number of duplicates an interval can allow. POSIX-conforming + systems might define this in , but we want our + value, so remove any previous define. */ +# ifdef RE_DUP_MAX +# undef RE_DUP_MAX +# endif + +/* RE_DUP_MAX is 2**15 - 1 because an earlier implementation stored + the counter as a 2-byte signed integer. This is no longer true, so + RE_DUP_MAX could be increased to (INT_MAX / 10 - 1), or to + ((SIZE_MAX - 2) / 10 - 1) if _REGEX_LARGE_OFFSETS is defined. + However, there would be a huge performance problem if someone + actually used a pattern like a\{214748363\}, so RE_DUP_MAX retains + its historical value. */ +# define RE_DUP_MAX (0x7fff) + +#endif /* defined __USE_GNU_REGEX */ + + +/* POSIX `cflags' bits (i.e., information for `regcomp'). */ + +/* If this bit is set, then use extended regular expression syntax. + If not set, then use basic regular expression syntax. */ +#define REG_EXTENDED 1 + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define REG_ICASE (1 << 1) + +/* If this bit is set, then anchors do not match at newline + characters in the string. + If not set, then anchors do match at newlines. */ +#define REG_NEWLINE (1 << 2) + +/* If this bit is set, then report only success or fail in regexec. + If not set, then returns differ between not matching and errors. */ +#define REG_NOSUB (1 << 3) + + +/* POSIX `eflags' bits (i.e., information for regexec). */ + +/* If this bit is set, then the beginning-of-line operator doesn't match + the beginning of the string (presumably because it's not the + beginning of a line). + If not set, then the beginning-of-line operator does match the + beginning of the string. */ +#define REG_NOTBOL 1 + +/* Like REG_NOTBOL, except for the end-of-line. */ +#define REG_NOTEOL (1 << 1) + +/* Use PMATCH[0] to delimit the start and end of the search in the + buffer. */ +#define REG_STARTEND (1 << 2) + + +/* If any error codes are removed, changed, or added, update the + `__re_error_msgid' table in regcomp.c. */ + +typedef enum +{ + _REG_ENOSYS = -1, /* This will never happen for this implementation. */ + _REG_NOERROR = 0, /* Success. */ + _REG_NOMATCH, /* Didn't find a match (for regexec). */ + + /* POSIX regcomp return error codes. (In the order listed in the + standard.) */ + _REG_BADPAT, /* Invalid pattern. */ + _REG_ECOLLATE, /* Invalid collating element. */ + _REG_ECTYPE, /* Invalid character class name. */ + _REG_EESCAPE, /* Trailing backslash. */ + _REG_ESUBREG, /* Invalid back reference. */ + _REG_EBRACK, /* Unmatched left bracket. */ + _REG_EPAREN, /* Parenthesis imbalance. */ + _REG_EBRACE, /* Unmatched \{. */ + _REG_BADBR, /* Invalid contents of \{\}. */ + _REG_ERANGE, /* Invalid range end. */ + _REG_ESPACE, /* Ran out of memory. */ + _REG_BADRPT, /* No preceding re for repetition op. */ + + /* Error codes we've added. */ + _REG_EEND, /* Premature end. */ + _REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ + _REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ +} reg_errcode_t; + +#ifdef _XOPEN_SOURCE +# define REG_ENOSYS _REG_ENOSYS +#endif +#define REG_NOERROR _REG_NOERROR +#define REG_NOMATCH _REG_NOMATCH +#define REG_BADPAT _REG_BADPAT +#define REG_ECOLLATE _REG_ECOLLATE +#define REG_ECTYPE _REG_ECTYPE +#define REG_EESCAPE _REG_EESCAPE +#define REG_ESUBREG _REG_ESUBREG +#define REG_EBRACK _REG_EBRACK +#define REG_EPAREN _REG_EPAREN +#define REG_EBRACE _REG_EBRACE +#define REG_BADBR _REG_BADBR +#define REG_ERANGE _REG_ERANGE +#define REG_ESPACE _REG_ESPACE +#define REG_BADRPT _REG_BADRPT +#define REG_EEND _REG_EEND +#define REG_ESIZE _REG_ESIZE +#define REG_ERPAREN _REG_ERPAREN + +/* struct re_pattern_buffer normally uses member names like `buffer' + that POSIX does not allow. In POSIX mode these members have names + with leading `re_' (e.g., `re_buffer'). */ +#ifdef __USE_GNU_REGEX +# define _REG_RE_NAME(id) id +# define _REG_RM_NAME(id) id +#else +# define _REG_RE_NAME(id) re_##id +# define _REG_RM_NAME(id) rm_##id +#endif + +/* The user can specify the type of the re_translate member by + defining the macro RE_TRANSLATE_TYPE, which defaults to unsigned + char *. This pollutes the POSIX name space, so in POSIX mode just + use unsigned char *. */ +#ifdef __USE_GNU_REGEX +# ifndef RE_TRANSLATE_TYPE +# define RE_TRANSLATE_TYPE unsigned char * +# endif +# define REG_TRANSLATE_TYPE RE_TRANSLATE_TYPE +#else +# define REG_TRANSLATE_TYPE unsigned char * +#endif + +/* This data structure represents a compiled pattern. Before calling + the pattern compiler, the fields `buffer', `allocated', `fastmap', + `translate', and `no_sub' can be set. After the pattern has been + compiled, the `re_nsub' field is available. All other fields are + private to the regex routines. */ + +struct re_pattern_buffer +{ + /* Space that holds the compiled pattern. It is declared as + `unsigned char *' because its elements are sometimes used as + array indexes. */ + unsigned char *_REG_RE_NAME (buffer); + + /* Number of bytes to which `buffer' points. */ + __re_long_size_t _REG_RE_NAME (allocated); + + /* Number of bytes actually used in `buffer'. */ + __re_long_size_t _REG_RE_NAME (used); + + /* Syntax setting with which the pattern was compiled. */ + reg_syntax_t _REG_RE_NAME (syntax); + + /* Pointer to a fastmap, if any, otherwise zero. re_search uses the + fastmap, if there is one, to skip over impossible starting points + for matches. */ + char *_REG_RE_NAME (fastmap); + + /* Either a translate table to apply to all characters before + comparing them, or zero for no translation. The translation is + applied to a pattern when it is compiled and to a string when it + is matched. */ + REG_TRANSLATE_TYPE _REG_RE_NAME (translate); + + /* Number of subexpressions found by the compiler. */ + size_t re_nsub; + + /* Zero if this pattern cannot match the empty string, one else. + Well, in truth it's used only in `re_search_2', to see whether or + not we should use the fastmap, so we don't set this absolutely + perfectly; see `re_compile_fastmap' (the `duplicate' case). */ + unsigned int _REG_RE_NAME (can_be_null) : 1; + + /* If REGS_UNALLOCATED, allocate space in the `regs' structure + for `max (RE_NREGS, re_nsub + 1)' groups. + If REGS_REALLOCATE, reallocate space if necessary. + If REGS_FIXED, use what's there. */ +#ifdef __USE_GNU_REGEX +# define REGS_UNALLOCATED 0 +# define REGS_REALLOCATE 1 +# define REGS_FIXED 2 +#endif + unsigned int _REG_RE_NAME (regs_allocated) : 2; + + /* Set to zero when `regex_compile' compiles a pattern; set to one + by `re_compile_fastmap' if it updates the fastmap. */ + unsigned int _REG_RE_NAME (fastmap_accurate) : 1; + + /* If set, `re_match_2' does not return information about + subexpressions. */ + unsigned int _REG_RE_NAME (no_sub) : 1; + + /* If set, a beginning-of-line anchor doesn't match at the beginning + of the string. */ + unsigned int _REG_RE_NAME (not_bol) : 1; + + /* Similarly for an end-of-line anchor. */ + unsigned int _REG_RE_NAME (not_eol) : 1; + + /* If true, an anchor at a newline matches. */ + unsigned int _REG_RE_NAME (newline_anchor) : 1; + +/* [[[end pattern_buffer]]] */ +}; + +typedef struct re_pattern_buffer regex_t; + +/* This is the structure we store register match data in. See + regex.texinfo for a full description of what registers match. */ +struct re_registers +{ + __re_size_t _REG_RM_NAME (num_regs); + regoff_t *_REG_RM_NAME (start); + regoff_t *_REG_RM_NAME (end); +}; + + +/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, + `re_match_2' returns information about at least this many registers + the first time a `regs' structure is passed. */ +#if !defined RE_NREGS && defined __USE_GNU_REGEX +# define RE_NREGS 30 +#endif + + +/* POSIX specification for registers. Aside from the different names than + `re_registers', POSIX uses an array of structures, instead of a + structure of arrays. */ +typedef struct +{ + regoff_t rm_so; /* Byte offset from string's start to substring's start. */ + regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ +} regmatch_t; + +/* Declarations for routines. */ + +/* Sets the current default syntax to SYNTAX, and return the old syntax. + You can also simply assign to the `re_syntax_options' variable. */ +extern reg_syntax_t re_set_syntax (reg_syntax_t __syntax); + +/* Compile the regular expression PATTERN, with length LENGTH + and syntax given by the global `re_syntax_options', into the buffer + BUFFER. Return NULL if successful, and an error string if not. */ +extern const char *re_compile_pattern (const char *__pattern, size_t __length, + struct re_pattern_buffer *__buffer); + + +/* Compile a fastmap for the compiled pattern in BUFFER; used to + accelerate searches. Return 0 if successful and -2 if was an + internal error. */ +extern int re_compile_fastmap (struct re_pattern_buffer *__buffer); + + +/* Search in the string STRING (with length LENGTH) for the pattern + compiled into BUFFER. Start searching at position START, for RANGE + characters. Return the starting position of the match, -1 for no + match, or -2 for an internal error. Also return register + information in REGS (if REGS and BUFFER->no_sub are nonzero). */ +extern regoff_t re_search (struct re_pattern_buffer *__buffer, + const char *__string, __re_idx_t __length, + __re_idx_t __start, regoff_t __range, + struct re_registers *__regs); + + +/* Like `re_search', but search in the concatenation of STRING1 and + STRING2. Also, stop searching at index START + STOP. */ +extern regoff_t re_search_2 (struct re_pattern_buffer *__buffer, + const char *__string1, __re_idx_t __length1, + const char *__string2, __re_idx_t __length2, + __re_idx_t __start, regoff_t __range, + struct re_registers *__regs, + __re_idx_t __stop); + + +/* Like `re_search', but return how many characters in STRING the regexp + in BUFFER matched, starting at position START. */ +extern regoff_t re_match (struct re_pattern_buffer *__buffer, + const char *__string, __re_idx_t __length, + __re_idx_t __start, struct re_registers *__regs); + + +/* Relates to `re_match' as `re_search_2' relates to `re_search'. */ +extern regoff_t re_match_2 (struct re_pattern_buffer *__buffer, + const char *__string1, __re_idx_t __length1, + const char *__string2, __re_idx_t __length2, + __re_idx_t __start, struct re_registers *__regs, + __re_idx_t __stop); + + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using BUFFER and REGS will use this memory + for recording register information. STARTS and ENDS must be + allocated with malloc, and must each be at least `NUM_REGS * sizeof + (regoff_t)' bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ +extern void re_set_registers (struct re_pattern_buffer *__buffer, + struct re_registers *__regs, + __re_size_t __num_regs, + regoff_t *__starts, regoff_t *__ends); + +#if defined _REGEX_RE_COMP || defined _LIBC +# ifndef _CRAY +/* 4.2 bsd compatibility. */ +extern char *re_comp (const char *); +extern int re_exec (const char *); +# endif +#endif + +/* GCC 2.95 and later have "__restrict"; C99 compilers have + "restrict", and "configure" may have defined "restrict". + Other compilers use __restrict, __restrict__, and _Restrict, and + 'configure' might #define 'restrict' to those words, so pick a + different name. */ +#ifndef _Restrict_ +# if 199901L <= __STDC_VERSION__ +# define _Restrict_ restrict +# elif 2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__) +# define _Restrict_ __restrict +# else +# define _Restrict_ +# endif +#endif +/* gcc 3.1 and up support the [restrict] syntax. Don't trust + sys/cdefs.h's definition of __restrict_arr, though, as it + mishandles gcc -ansi -pedantic. */ +#ifndef _Restrict_arr_ +# if ((199901L <= __STDC_VERSION__ \ + || ((3 < __GNUC__ || (3 == __GNUC__ && 1 <= __GNUC_MINOR__)) \ + && !__STRICT_ANSI__)) \ + && !defined __GNUG__) +# define _Restrict_arr_ _Restrict_ +# else +# define _Restrict_arr_ +# endif +#endif + +/* POSIX compatibility. */ +extern int regcomp (regex_t *_Restrict_ __preg, + const char *_Restrict_ __pattern, + int __cflags); + +extern int regexec (const regex_t *_Restrict_ __preg, + const char *_Restrict_ __string, size_t __nmatch, + regmatch_t __pmatch[_Restrict_arr_], + int __eflags); + +extern size_t regerror (int __errcode, const regex_t *_Restrict_ __preg, + char *_Restrict_ __errbuf, size_t __errbuf_size); + +extern void regfree (regex_t *__preg); + + +#ifdef __cplusplus +} +#endif /* C++ */ + +#endif /* regex.h */ diff --git a/gnulib/regex_internal.c b/gnulib/regex_internal.c new file mode 100644 index 000000000..378b767d8 --- /dev/null +++ b/gnulib/regex_internal.c @@ -0,0 +1,1740 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free + Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + 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 + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +static void re_string_construct_common (const char *str, Idx len, + re_string_t *pstr, + RE_TRANSLATE_TYPE trans, bool icase, + const re_dfa_t *dfa) internal_function; +static re_dfastate_t *create_ci_newstate (const re_dfa_t *dfa, + const re_node_set *nodes, + re_hashval_t hash) internal_function; +static re_dfastate_t *create_cd_newstate (const re_dfa_t *dfa, + const re_node_set *nodes, + unsigned int context, + re_hashval_t hash) internal_function; + +/* Functions for string operation. */ + +/* This function allocate the buffers. It is necessary to call + re_string_reconstruct before using the object. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +re_string_allocate (re_string_t *pstr, const char *str, Idx len, Idx init_len, + RE_TRANSLATE_TYPE trans, bool icase, const re_dfa_t *dfa) +{ + reg_errcode_t ret; + Idx init_buf_len; + + /* Ensure at least one character fits into the buffers. */ + if (init_len < dfa->mb_cur_max) + init_len = dfa->mb_cur_max; + init_buf_len = (len + 1 < init_len) ? len + 1: init_len; + re_string_construct_common (str, len, pstr, trans, icase, dfa); + + ret = re_string_realloc_buffers (pstr, init_buf_len); + if (BE (ret != REG_NOERROR, 0)) + return ret; + + pstr->word_char = dfa->word_char; + pstr->word_ops_used = dfa->word_ops_used; + pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str; + pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len; + pstr->valid_raw_len = pstr->valid_len; + return REG_NOERROR; +} + +/* This function allocate the buffers, and initialize them. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +re_string_construct (re_string_t *pstr, const char *str, Idx len, + RE_TRANSLATE_TYPE trans, bool icase, const re_dfa_t *dfa) +{ + reg_errcode_t ret; + memset (pstr, '\0', sizeof (re_string_t)); + re_string_construct_common (str, len, pstr, trans, icase, dfa); + + if (len > 0) + { + ret = re_string_realloc_buffers (pstr, len + 1); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str; + + if (icase) + { +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + while (1) + { + ret = build_wcs_upper_buffer (pstr); + if (BE (ret != REG_NOERROR, 0)) + return ret; + if (pstr->valid_raw_len >= len) + break; + if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max) + break; + ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + } + else +#endif /* RE_ENABLE_I18N */ + build_upper_buffer (pstr); + } + else + { +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + build_wcs_buffer (pstr); + else +#endif /* RE_ENABLE_I18N */ + { + if (trans != NULL) + re_string_translate_buffer (pstr); + else + { + pstr->valid_len = pstr->bufs_len; + pstr->valid_raw_len = pstr->bufs_len; + } + } + } + + return REG_NOERROR; +} + +/* Helper functions for re_string_allocate, and re_string_construct. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +re_string_realloc_buffers (re_string_t *pstr, Idx new_buf_len) +{ +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + wint_t *new_wcs; + + /* Avoid overflow. */ + size_t max_object_size = MAX (sizeof (wint_t), sizeof (Idx)); + if (BE (SIZE_MAX / max_object_size < new_buf_len, 0)) + return REG_ESPACE; + + new_wcs = re_realloc (pstr->wcs, wint_t, new_buf_len); + if (BE (new_wcs == NULL, 0)) + return REG_ESPACE; + pstr->wcs = new_wcs; + if (pstr->offsets != NULL) + { + Idx *new_offsets = re_realloc (pstr->offsets, Idx, new_buf_len); + if (BE (new_offsets == NULL, 0)) + return REG_ESPACE; + pstr->offsets = new_offsets; + } + } +#endif /* RE_ENABLE_I18N */ + if (pstr->mbs_allocated) + { + unsigned char *new_mbs = re_realloc (pstr->mbs, unsigned char, + new_buf_len); + if (BE (new_mbs == NULL, 0)) + return REG_ESPACE; + pstr->mbs = new_mbs; + } + pstr->bufs_len = new_buf_len; + return REG_NOERROR; +} + + +static void +internal_function +re_string_construct_common (const char *str, Idx len, re_string_t *pstr, + RE_TRANSLATE_TYPE trans, bool icase, + const re_dfa_t *dfa) +{ + pstr->raw_mbs = (const unsigned char *) str; + pstr->len = len; + pstr->raw_len = len; + pstr->trans = trans; + pstr->icase = icase; + pstr->mbs_allocated = (trans != NULL || icase); + pstr->mb_cur_max = dfa->mb_cur_max; + pstr->is_utf8 = dfa->is_utf8; + pstr->map_notascii = dfa->map_notascii; + pstr->stop = pstr->len; + pstr->raw_stop = pstr->stop; +} + +#ifdef RE_ENABLE_I18N + +/* Build wide character buffer PSTR->WCS. + If the byte sequence of the string are: + (0), (1), (0), (1), + Then wide character buffer will be: + , WEOF , , WEOF , + We use WEOF for padding, they indicate that the position isn't + a first byte of a multibyte character. + + Note that this function assumes PSTR->VALID_LEN elements are already + built and starts from PSTR->VALID_LEN. */ + +static void +internal_function +build_wcs_buffer (re_string_t *pstr) +{ +#ifdef _LIBC + unsigned char buf[MB_LEN_MAX]; + assert (MB_LEN_MAX >= pstr->mb_cur_max); +#else + unsigned char buf[64]; +#endif + mbstate_t prev_st; + Idx byte_idx, end_idx, remain_len; + size_t mbclen; + + /* Build the buffers from pstr->valid_len to either pstr->len or + pstr->bufs_len. */ + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + for (byte_idx = pstr->valid_len; byte_idx < end_idx;) + { + wchar_t wc; + const char *p; + + remain_len = end_idx - byte_idx; + prev_st = pstr->cur_state; + /* Apply the translation if we need. */ + if (BE (pstr->trans != NULL, 0)) + { + int i, ch; + + for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i) + { + ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i]; + buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch]; + } + p = (const char *) buf; + } + else + p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx; + mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state); + if (BE (mbclen == (size_t) -2, 0)) + { + /* The buffer doesn't have enough space, finish to build. */ + pstr->cur_state = prev_st; + break; + } + else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0)) + { + /* We treat these cases as a singlebyte character. */ + mbclen = 1; + wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; + if (BE (pstr->trans != NULL, 0)) + wc = pstr->trans[wc]; + pstr->cur_state = prev_st; + } + + /* Write wide character and padding. */ + pstr->wcs[byte_idx++] = wc; + /* Write paddings. */ + for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) + pstr->wcs[byte_idx++] = WEOF; + } + pstr->valid_len = byte_idx; + pstr->valid_raw_len = byte_idx; +} + +/* Build wide character buffer PSTR->WCS like build_wcs_buffer, + but for REG_ICASE. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +build_wcs_upper_buffer (re_string_t *pstr) +{ + mbstate_t prev_st; + Idx src_idx, byte_idx, end_idx, remain_len; + size_t mbclen; +#ifdef _LIBC + char buf[MB_LEN_MAX]; + assert (MB_LEN_MAX >= pstr->mb_cur_max); +#else + char buf[64]; +#endif + + byte_idx = pstr->valid_len; + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + + /* The following optimization assumes that ASCII characters can be + mapped to wide characters with a simple cast. */ + if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed) + { + while (byte_idx < end_idx) + { + wchar_t wc; + + if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]) + && mbsinit (&pstr->cur_state)) + { + /* In case of a singlebyte character. */ + pstr->mbs[byte_idx] + = toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]); + /* The next step uses the assumption that wchar_t is encoded + ASCII-safe: all ASCII values can be converted like this. */ + pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx]; + ++byte_idx; + continue; + } + + remain_len = end_idx - byte_idx; + prev_st = pstr->cur_state; + mbclen = __mbrtowc (&wc, + ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx + + byte_idx), remain_len, &pstr->cur_state); + if (BE (mbclen < (size_t) -2, 1)) + { + wchar_t wcu = wc; + if (iswlower (wc)) + { + size_t mbcdlen; + + wcu = towupper (wc); + mbcdlen = wcrtomb (buf, wcu, &prev_st); + if (BE (mbclen == mbcdlen, 1)) + memcpy (pstr->mbs + byte_idx, buf, mbclen); + else + { + src_idx = byte_idx; + goto offsets_needed; + } + } + else + memcpy (pstr->mbs + byte_idx, + pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen); + pstr->wcs[byte_idx++] = wcu; + /* Write paddings. */ + for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) + pstr->wcs[byte_idx++] = WEOF; + } + else if (mbclen == (size_t) -1 || mbclen == 0) + { + /* It is an invalid character or '\0'. Just use the byte. */ + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; + pstr->mbs[byte_idx] = ch; + /* And also cast it to wide char. */ + pstr->wcs[byte_idx++] = (wchar_t) ch; + if (BE (mbclen == (size_t) -1, 0)) + pstr->cur_state = prev_st; + } + else + { + /* The buffer doesn't have enough space, finish to build. */ + pstr->cur_state = prev_st; + break; + } + } + pstr->valid_len = byte_idx; + pstr->valid_raw_len = byte_idx; + return REG_NOERROR; + } + else + for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;) + { + wchar_t wc; + const char *p; + offsets_needed: + remain_len = end_idx - byte_idx; + prev_st = pstr->cur_state; + if (BE (pstr->trans != NULL, 0)) + { + int i, ch; + + for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i) + { + ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i]; + buf[i] = pstr->trans[ch]; + } + p = (const char *) buf; + } + else + p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx; + mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state); + if (BE (mbclen < (size_t) -2, 1)) + { + wchar_t wcu = wc; + if (iswlower (wc)) + { + size_t mbcdlen; + + wcu = towupper (wc); + mbcdlen = wcrtomb ((char *) buf, wcu, &prev_st); + if (BE (mbclen == mbcdlen, 1)) + memcpy (pstr->mbs + byte_idx, buf, mbclen); + else if (mbcdlen != (size_t) -1) + { + size_t i; + + if (byte_idx + mbcdlen > pstr->bufs_len) + { + pstr->cur_state = prev_st; + break; + } + + if (pstr->offsets == NULL) + { + pstr->offsets = re_malloc (Idx, pstr->bufs_len); + + if (pstr->offsets == NULL) + return REG_ESPACE; + } + if (!pstr->offsets_needed) + { + for (i = 0; i < (size_t) byte_idx; ++i) + pstr->offsets[i] = i; + pstr->offsets_needed = 1; + } + + memcpy (pstr->mbs + byte_idx, buf, mbcdlen); + pstr->wcs[byte_idx] = wcu; + pstr->offsets[byte_idx] = src_idx; + for (i = 1; i < mbcdlen; ++i) + { + pstr->offsets[byte_idx + i] + = src_idx + (i < mbclen ? i : mbclen - 1); + pstr->wcs[byte_idx + i] = WEOF; + } + pstr->len += mbcdlen - mbclen; + if (pstr->raw_stop > src_idx) + pstr->stop += mbcdlen - mbclen; + end_idx = (pstr->bufs_len > pstr->len) + ? pstr->len : pstr->bufs_len; + byte_idx += mbcdlen; + src_idx += mbclen; + continue; + } + else + memcpy (pstr->mbs + byte_idx, p, mbclen); + } + else + memcpy (pstr->mbs + byte_idx, p, mbclen); + + if (BE (pstr->offsets_needed != 0, 0)) + { + size_t i; + for (i = 0; i < mbclen; ++i) + pstr->offsets[byte_idx + i] = src_idx + i; + } + src_idx += mbclen; + + pstr->wcs[byte_idx++] = wcu; + /* Write paddings. */ + for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) + pstr->wcs[byte_idx++] = WEOF; + } + else if (mbclen == (size_t) -1 || mbclen == 0) + { + /* It is an invalid character or '\0'. Just use the byte. */ + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx]; + + if (BE (pstr->trans != NULL, 0)) + ch = pstr->trans [ch]; + pstr->mbs[byte_idx] = ch; + + if (BE (pstr->offsets_needed != 0, 0)) + pstr->offsets[byte_idx] = src_idx; + ++src_idx; + + /* And also cast it to wide char. */ + pstr->wcs[byte_idx++] = (wchar_t) ch; + if (BE (mbclen == (size_t) -1, 0)) + pstr->cur_state = prev_st; + } + else + { + /* The buffer doesn't have enough space, finish to build. */ + pstr->cur_state = prev_st; + break; + } + } + pstr->valid_len = byte_idx; + pstr->valid_raw_len = src_idx; + return REG_NOERROR; +} + +/* Skip characters until the index becomes greater than NEW_RAW_IDX. + Return the index. */ + +static Idx +internal_function +re_string_skip_chars (re_string_t *pstr, Idx new_raw_idx, wint_t *last_wc) +{ + mbstate_t prev_st; + Idx rawbuf_idx; + size_t mbclen; + wint_t wc = WEOF; + + /* Skip the characters which are not necessary to check. */ + for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len; + rawbuf_idx < new_raw_idx;) + { + wchar_t wc2; + Idx remain_len; + remain_len = pstr->len - rawbuf_idx; + prev_st = pstr->cur_state; + mbclen = __mbrtowc (&wc2, (const char *) pstr->raw_mbs + rawbuf_idx, + remain_len, &pstr->cur_state); + if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0)) + { + /* We treat these cases as a single byte character. */ + if (mbclen == 0 || remain_len == 0) + wc = L'\0'; + else + wc = *(unsigned char *) (pstr->raw_mbs + rawbuf_idx); + mbclen = 1; + pstr->cur_state = prev_st; + } + else + wc = wc2; + /* Then proceed the next character. */ + rawbuf_idx += mbclen; + } + *last_wc = wc; + return rawbuf_idx; +} +#endif /* RE_ENABLE_I18N */ + +/* Build the buffer PSTR->MBS, and apply the translation if we need. + This function is used in case of REG_ICASE. */ + +static void +internal_function +build_upper_buffer (re_string_t *pstr) +{ + Idx char_idx, end_idx; + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + + for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx) + { + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx]; + if (BE (pstr->trans != NULL, 0)) + ch = pstr->trans[ch]; + if (islower (ch)) + pstr->mbs[char_idx] = toupper (ch); + else + pstr->mbs[char_idx] = ch; + } + pstr->valid_len = char_idx; + pstr->valid_raw_len = char_idx; +} + +/* Apply TRANS to the buffer in PSTR. */ + +static void +internal_function +re_string_translate_buffer (re_string_t *pstr) +{ + Idx buf_idx, end_idx; + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + + for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx) + { + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx]; + pstr->mbs[buf_idx] = pstr->trans[ch]; + } + + pstr->valid_len = buf_idx; + pstr->valid_raw_len = buf_idx; +} + +/* This function re-construct the buffers. + Concretely, convert to wide character in case of pstr->mb_cur_max > 1, + convert to upper case in case of REG_ICASE, apply translation. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +re_string_reconstruct (re_string_t *pstr, Idx idx, int eflags) +{ + Idx offset; + + if (BE (pstr->raw_mbs_idx <= idx, 0)) + offset = idx - pstr->raw_mbs_idx; + else + { + /* Reset buffer. */ +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); +#endif /* RE_ENABLE_I18N */ + pstr->len = pstr->raw_len; + pstr->stop = pstr->raw_stop; + pstr->valid_len = 0; + pstr->raw_mbs_idx = 0; + pstr->valid_raw_len = 0; + pstr->offsets_needed = 0; + pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF + : CONTEXT_NEWLINE | CONTEXT_BEGBUF); + if (!pstr->mbs_allocated) + pstr->mbs = (unsigned char *) pstr->raw_mbs; + offset = idx; + } + + if (BE (offset != 0, 1)) + { + /* Should the already checked characters be kept? */ + if (BE (offset < pstr->valid_raw_len, 1)) + { + /* Yes, move them to the front of the buffer. */ +#ifdef RE_ENABLE_I18N + if (BE (pstr->offsets_needed, 0)) + { + Idx low = 0, high = pstr->valid_len, mid; + do + { + mid = (high + low) / 2; + if (pstr->offsets[mid] > offset) + high = mid; + else if (pstr->offsets[mid] < offset) + low = mid + 1; + else + break; + } + while (low < high); + if (pstr->offsets[mid] < offset) + ++mid; + pstr->tip_context = re_string_context_at (pstr, mid - 1, + eflags); + /* This can be quite complicated, so handle specially + only the common and easy case where the character with + different length representation of lower and upper + case is present at or after offset. */ + if (pstr->valid_len > offset + && mid == offset && pstr->offsets[mid] == offset) + { + memmove (pstr->wcs, pstr->wcs + offset, + (pstr->valid_len - offset) * sizeof (wint_t)); + memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset); + pstr->valid_len -= offset; + pstr->valid_raw_len -= offset; + for (low = 0; low < pstr->valid_len; low++) + pstr->offsets[low] = pstr->offsets[low + offset] - offset; + } + else + { + /* Otherwise, just find out how long the partial multibyte + character at offset is and fill it with WEOF/255. */ + pstr->len = pstr->raw_len - idx + offset; + pstr->stop = pstr->raw_stop - idx + offset; + pstr->offsets_needed = 0; + while (mid > 0 && pstr->offsets[mid - 1] == offset) + --mid; + while (mid < pstr->valid_len) + if (pstr->wcs[mid] != WEOF) + break; + else + ++mid; + if (mid == pstr->valid_len) + pstr->valid_len = 0; + else + { + pstr->valid_len = pstr->offsets[mid] - offset; + if (pstr->valid_len) + { + for (low = 0; low < pstr->valid_len; ++low) + pstr->wcs[low] = WEOF; + memset (pstr->mbs, 255, pstr->valid_len); + } + } + pstr->valid_raw_len = pstr->valid_len; + } + } + else +#endif + { + pstr->tip_context = re_string_context_at (pstr, offset - 1, + eflags); +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + memmove (pstr->wcs, pstr->wcs + offset, + (pstr->valid_len - offset) * sizeof (wint_t)); +#endif /* RE_ENABLE_I18N */ + if (BE (pstr->mbs_allocated, 0)) + memmove (pstr->mbs, pstr->mbs + offset, + pstr->valid_len - offset); + pstr->valid_len -= offset; + pstr->valid_raw_len -= offset; +#if DEBUG + assert (pstr->valid_len > 0); +#endif + } + } + else + { +#ifdef RE_ENABLE_I18N + /* No, skip all characters until IDX. */ + Idx prev_valid_len = pstr->valid_len; + + if (BE (pstr->offsets_needed, 0)) + { + pstr->len = pstr->raw_len - idx + offset; + pstr->stop = pstr->raw_stop - idx + offset; + pstr->offsets_needed = 0; + } +#endif + pstr->valid_len = 0; +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + Idx wcs_idx; + wint_t wc = WEOF; + + if (pstr->is_utf8) + { + const unsigned char *raw, *p, *end; + + /* Special case UTF-8. Multi-byte chars start with any + byte other than 0x80 - 0xbf. */ + raw = pstr->raw_mbs + pstr->raw_mbs_idx; + end = raw + (offset - pstr->mb_cur_max); + if (end < pstr->raw_mbs) + end = pstr->raw_mbs; + p = raw + offset - 1; +#ifdef _LIBC + /* We know the wchar_t encoding is UCS4, so for the simple + case, ASCII characters, skip the conversion step. */ + if (isascii (*p) && BE (pstr->trans == NULL, 1)) + { + memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); + /* pstr->valid_len = 0; */ + wc = (wchar_t) *p; + } + else +#endif + for (; p >= end; --p) + if ((*p & 0xc0) != 0x80) + { + mbstate_t cur_state; + wchar_t wc2; + Idx mlen = raw + pstr->len - p; + unsigned char buf[6]; + size_t mbclen; + + if (BE (pstr->trans != NULL, 0)) + { + int i = mlen < 6 ? mlen : 6; + while (--i >= 0) + buf[i] = pstr->trans[p[i]]; + } + /* XXX Don't use mbrtowc, we know which conversion + to use (UTF-8 -> UCS4). */ + memset (&cur_state, 0, sizeof (cur_state)); + mbclen = __mbrtowc (&wc2, (const char *) p, mlen, + &cur_state); + if (raw + offset - p <= mbclen + && mbclen < (size_t) -2) + { + memset (&pstr->cur_state, '\0', + sizeof (mbstate_t)); + pstr->valid_len = mbclen - (raw + offset - p); + wc = wc2; + } + break; + } + } + + if (wc == WEOF) + pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx; + if (wc == WEOF) + pstr->tip_context + = re_string_context_at (pstr, prev_valid_len - 1, eflags); + else + pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0) + && IS_WIDE_WORD_CHAR (wc)) + ? CONTEXT_WORD + : ((IS_WIDE_NEWLINE (wc) + && pstr->newline_anchor) + ? CONTEXT_NEWLINE : 0)); + if (BE (pstr->valid_len, 0)) + { + for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx) + pstr->wcs[wcs_idx] = WEOF; + if (pstr->mbs_allocated) + memset (pstr->mbs, 255, pstr->valid_len); + } + pstr->valid_raw_len = pstr->valid_len; + } + else +#endif /* RE_ENABLE_I18N */ + { + int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1]; + pstr->valid_raw_len = 0; + if (pstr->trans) + c = pstr->trans[c]; + pstr->tip_context = (bitset_contain (pstr->word_char, c) + ? CONTEXT_WORD + : ((IS_NEWLINE (c) && pstr->newline_anchor) + ? CONTEXT_NEWLINE : 0)); + } + } + if (!BE (pstr->mbs_allocated, 0)) + pstr->mbs += offset; + } + pstr->raw_mbs_idx = idx; + pstr->len -= offset; + pstr->stop -= offset; + + /* Then build the buffers. */ +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + if (pstr->icase) + { + reg_errcode_t ret = build_wcs_upper_buffer (pstr); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + else + build_wcs_buffer (pstr); + } + else +#endif /* RE_ENABLE_I18N */ + if (BE (pstr->mbs_allocated, 0)) + { + if (pstr->icase) + build_upper_buffer (pstr); + else if (pstr->trans != NULL) + re_string_translate_buffer (pstr); + } + else + pstr->valid_len = pstr->len; + + pstr->cur_idx = 0; + return REG_NOERROR; +} + +static unsigned char +internal_function __attribute ((pure)) +re_string_peek_byte_case (const re_string_t *pstr, Idx idx) +{ + int ch; + Idx off; + + /* Handle the common (easiest) cases first. */ + if (BE (!pstr->mbs_allocated, 1)) + return re_string_peek_byte (pstr, idx); + +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1 + && ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx)) + return re_string_peek_byte (pstr, idx); +#endif + + off = pstr->cur_idx + idx; +#ifdef RE_ENABLE_I18N + if (pstr->offsets_needed) + off = pstr->offsets[off]; +#endif + + ch = pstr->raw_mbs[pstr->raw_mbs_idx + off]; + +#ifdef RE_ENABLE_I18N + /* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I + this function returns CAPITAL LETTER I instead of first byte of + DOTLESS SMALL LETTER I. The latter would confuse the parser, + since peek_byte_case doesn't advance cur_idx in any way. */ + if (pstr->offsets_needed && !isascii (ch)) + return re_string_peek_byte (pstr, idx); +#endif + + return ch; +} + +static unsigned char +internal_function __attribute ((pure)) +re_string_fetch_byte_case (re_string_t *pstr) +{ + if (BE (!pstr->mbs_allocated, 1)) + return re_string_fetch_byte (pstr); + +#ifdef RE_ENABLE_I18N + if (pstr->offsets_needed) + { + Idx off; + int ch; + + /* For tr_TR.UTF-8 [[:islower:]] there is + [[: CAPITAL LETTER I WITH DOT lower:]] in mbs. Skip + in that case the whole multi-byte character and return + the original letter. On the other side, with + [[: DOTLESS SMALL LETTER I return [[:I, as doing + anything else would complicate things too much. */ + + if (!re_string_first_byte (pstr, pstr->cur_idx)) + return re_string_fetch_byte (pstr); + + off = pstr->offsets[pstr->cur_idx]; + ch = pstr->raw_mbs[pstr->raw_mbs_idx + off]; + + if (! isascii (ch)) + return re_string_fetch_byte (pstr); + + re_string_skip_bytes (pstr, + re_string_char_size_at (pstr, pstr->cur_idx)); + return ch; + } +#endif + + return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++]; +} + +static void +internal_function +re_string_destruct (re_string_t *pstr) +{ +#ifdef RE_ENABLE_I18N + re_free (pstr->wcs); + re_free (pstr->offsets); +#endif /* RE_ENABLE_I18N */ + if (pstr->mbs_allocated) + re_free (pstr->mbs); +} + +/* Return the context at IDX in INPUT. */ + +static unsigned int +internal_function +re_string_context_at (const re_string_t *input, Idx idx, int eflags) +{ + int c; + if (BE (! REG_VALID_INDEX (idx), 0)) + /* In this case, we use the value stored in input->tip_context, + since we can't know the character in input->mbs[-1] here. */ + return input->tip_context; + if (BE (idx == input->len, 0)) + return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF + : CONTEXT_NEWLINE | CONTEXT_ENDBUF); +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1) + { + wint_t wc; + Idx wc_idx = idx; + while(input->wcs[wc_idx] == WEOF) + { +#ifdef DEBUG + /* It must not happen. */ + assert (REG_VALID_INDEX (wc_idx)); +#endif + --wc_idx; + if (! REG_VALID_INDEX (wc_idx)) + return input->tip_context; + } + wc = input->wcs[wc_idx]; + if (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc)) + return CONTEXT_WORD; + return (IS_WIDE_NEWLINE (wc) && input->newline_anchor + ? CONTEXT_NEWLINE : 0); + } + else +#endif + { + c = re_string_byte_at (input, idx); + if (bitset_contain (input->word_char, c)) + return CONTEXT_WORD; + return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0; + } +} + +/* Functions for set operation. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +re_node_set_alloc (re_node_set *set, Idx size) +{ + set->alloc = size; + set->nelem = 0; + set->elems = re_malloc (Idx, size); + if (BE (set->elems == NULL, 0)) + return REG_ESPACE; + return REG_NOERROR; +} + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +re_node_set_init_1 (re_node_set *set, Idx elem) +{ + set->alloc = 1; + set->nelem = 1; + set->elems = re_malloc (Idx, 1); + if (BE (set->elems == NULL, 0)) + { + set->alloc = set->nelem = 0; + return REG_ESPACE; + } + set->elems[0] = elem; + return REG_NOERROR; +} + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +re_node_set_init_2 (re_node_set *set, Idx elem1, Idx elem2) +{ + set->alloc = 2; + set->elems = re_malloc (Idx, 2); + if (BE (set->elems == NULL, 0)) + return REG_ESPACE; + if (elem1 == elem2) + { + set->nelem = 1; + set->elems[0] = elem1; + } + else + { + set->nelem = 2; + if (elem1 < elem2) + { + set->elems[0] = elem1; + set->elems[1] = elem2; + } + else + { + set->elems[0] = elem2; + set->elems[1] = elem1; + } + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +re_node_set_init_copy (re_node_set *dest, const re_node_set *src) +{ + dest->nelem = src->nelem; + if (src->nelem > 0) + { + dest->alloc = dest->nelem; + dest->elems = re_malloc (Idx, dest->alloc); + if (BE (dest->elems == NULL, 0)) + { + dest->alloc = dest->nelem = 0; + return REG_ESPACE; + } + memcpy (dest->elems, src->elems, src->nelem * sizeof (Idx)); + } + else + re_node_set_init_empty (dest); + return REG_NOERROR; +} + +/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. + Note: We assume dest->elems is NULL, when dest->alloc is 0. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +re_node_set_add_intersect (re_node_set *dest, const re_node_set *src1, + const re_node_set *src2) +{ + Idx i1, i2, is, id, delta, sbase; + if (src1->nelem == 0 || src2->nelem == 0) + return REG_NOERROR; + + /* We need dest->nelem + 2 * elems_in_intersection; this is a + conservative estimate. */ + if (src1->nelem + src2->nelem + dest->nelem > dest->alloc) + { + Idx new_alloc = src1->nelem + src2->nelem + dest->alloc; + Idx *new_elems = re_realloc (dest->elems, Idx, new_alloc); + if (BE (new_elems == NULL, 0)) + return REG_ESPACE; + dest->elems = new_elems; + dest->alloc = new_alloc; + } + + /* Find the items in the intersection of SRC1 and SRC2, and copy + into the top of DEST those that are not already in DEST itself. */ + sbase = dest->nelem + src1->nelem + src2->nelem; + i1 = src1->nelem - 1; + i2 = src2->nelem - 1; + id = dest->nelem - 1; + for (;;) + { + if (src1->elems[i1] == src2->elems[i2]) + { + /* Try to find the item in DEST. Maybe we could binary search? */ + while (REG_VALID_INDEX (id) && dest->elems[id] > src1->elems[i1]) + --id; + + if (! REG_VALID_INDEX (id) || dest->elems[id] != src1->elems[i1]) + dest->elems[--sbase] = src1->elems[i1]; + + if (! REG_VALID_INDEX (--i1) || ! REG_VALID_INDEX (--i2)) + break; + } + + /* Lower the highest of the two items. */ + else if (src1->elems[i1] < src2->elems[i2]) + { + if (! REG_VALID_INDEX (--i2)) + break; + } + else + { + if (! REG_VALID_INDEX (--i1)) + break; + } + } + + id = dest->nelem - 1; + is = dest->nelem + src1->nelem + src2->nelem - 1; + delta = is - sbase + 1; + + /* Now copy. When DELTA becomes zero, the remaining + DEST elements are already in place; this is more or + less the same loop that is in re_node_set_merge. */ + dest->nelem += delta; + if (delta > 0 && REG_VALID_INDEX (id)) + for (;;) + { + if (dest->elems[is] > dest->elems[id]) + { + /* Copy from the top. */ + dest->elems[id + delta--] = dest->elems[is--]; + if (delta == 0) + break; + } + else + { + /* Slide from the bottom. */ + dest->elems[id + delta] = dest->elems[id]; + if (! REG_VALID_INDEX (--id)) + break; + } + } + + /* Copy remaining SRC elements. */ + memcpy (dest->elems, dest->elems + sbase, delta * sizeof (Idx)); + + return REG_NOERROR; +} + +/* Calculate the union set of the sets SRC1 and SRC2. And store it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +re_node_set_init_union (re_node_set *dest, const re_node_set *src1, + const re_node_set *src2) +{ + Idx i1, i2, id; + if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0) + { + dest->alloc = src1->nelem + src2->nelem; + dest->elems = re_malloc (Idx, dest->alloc); + if (BE (dest->elems == NULL, 0)) + return REG_ESPACE; + } + else + { + if (src1 != NULL && src1->nelem > 0) + return re_node_set_init_copy (dest, src1); + else if (src2 != NULL && src2->nelem > 0) + return re_node_set_init_copy (dest, src2); + else + re_node_set_init_empty (dest); + return REG_NOERROR; + } + for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;) + { + if (src1->elems[i1] > src2->elems[i2]) + { + dest->elems[id++] = src2->elems[i2++]; + continue; + } + if (src1->elems[i1] == src2->elems[i2]) + ++i2; + dest->elems[id++] = src1->elems[i1++]; + } + if (i1 < src1->nelem) + { + memcpy (dest->elems + id, src1->elems + i1, + (src1->nelem - i1) * sizeof (Idx)); + id += src1->nelem - i1; + } + else if (i2 < src2->nelem) + { + memcpy (dest->elems + id, src2->elems + i2, + (src2->nelem - i2) * sizeof (Idx)); + id += src2->nelem - i2; + } + dest->nelem = id; + return REG_NOERROR; +} + +/* Calculate the union set of the sets DEST and SRC. And store it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +re_node_set_merge (re_node_set *dest, const re_node_set *src) +{ + Idx is, id, sbase, delta; + if (src == NULL || src->nelem == 0) + return REG_NOERROR; + if (dest->alloc < 2 * src->nelem + dest->nelem) + { + Idx new_alloc = 2 * (src->nelem + dest->alloc); + Idx *new_buffer = re_realloc (dest->elems, Idx, new_alloc); + if (BE (new_buffer == NULL, 0)) + return REG_ESPACE; + dest->elems = new_buffer; + dest->alloc = new_alloc; + } + + if (BE (dest->nelem == 0, 0)) + { + dest->nelem = src->nelem; + memcpy (dest->elems, src->elems, src->nelem * sizeof (Idx)); + return REG_NOERROR; + } + + /* Copy into the top of DEST the items of SRC that are not + found in DEST. Maybe we could binary search in DEST? */ + for (sbase = dest->nelem + 2 * src->nelem, + is = src->nelem - 1, id = dest->nelem - 1; + REG_VALID_INDEX (is) && REG_VALID_INDEX (id); ) + { + if (dest->elems[id] == src->elems[is]) + is--, id--; + else if (dest->elems[id] < src->elems[is]) + dest->elems[--sbase] = src->elems[is--]; + else /* if (dest->elems[id] > src->elems[is]) */ + --id; + } + + if (REG_VALID_INDEX (is)) + { + /* If DEST is exhausted, the remaining items of SRC must be unique. */ + sbase -= is + 1; + memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (Idx)); + } + + id = dest->nelem - 1; + is = dest->nelem + 2 * src->nelem - 1; + delta = is - sbase + 1; + if (delta == 0) + return REG_NOERROR; + + /* Now copy. When DELTA becomes zero, the remaining + DEST elements are already in place. */ + dest->nelem += delta; + for (;;) + { + if (dest->elems[is] > dest->elems[id]) + { + /* Copy from the top. */ + dest->elems[id + delta--] = dest->elems[is--]; + if (delta == 0) + break; + } + else + { + /* Slide from the bottom. */ + dest->elems[id + delta] = dest->elems[id]; + if (! REG_VALID_INDEX (--id)) + { + /* Copy remaining SRC elements. */ + memcpy (dest->elems, dest->elems + sbase, + delta * sizeof (Idx)); + break; + } + } + } + + return REG_NOERROR; +} + +/* Insert the new element ELEM to the re_node_set* SET. + SET should not already have ELEM. + Return true if successful. */ + +static bool +internal_function __attribute_warn_unused_result__ +re_node_set_insert (re_node_set *set, Idx elem) +{ + Idx idx; + /* In case the set is empty. */ + if (set->alloc == 0) + return BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1); + + if (BE (set->nelem, 0) == 0) + { + /* We already guaranteed above that set->alloc != 0. */ + set->elems[0] = elem; + ++set->nelem; + return true; + } + + /* Realloc if we need. */ + if (set->alloc == set->nelem) + { + Idx *new_elems; + set->alloc = set->alloc * 2; + new_elems = re_realloc (set->elems, Idx, set->alloc); + if (BE (new_elems == NULL, 0)) + return false; + set->elems = new_elems; + } + + /* Move the elements which follows the new element. Test the + first element separately to skip a check in the inner loop. */ + if (elem < set->elems[0]) + { + idx = 0; + for (idx = set->nelem; idx > 0; idx--) + set->elems[idx] = set->elems[idx - 1]; + } + else + { + for (idx = set->nelem; set->elems[idx - 1] > elem; idx--) + set->elems[idx] = set->elems[idx - 1]; + } + + /* Insert the new element. */ + set->elems[idx] = elem; + ++set->nelem; + return true; +} + +/* Insert the new element ELEM to the re_node_set* SET. + SET should not already have any element greater than or equal to ELEM. + Return true if successful. */ + +static bool +internal_function __attribute_warn_unused_result__ +re_node_set_insert_last (re_node_set *set, Idx elem) +{ + /* Realloc if we need. */ + if (set->alloc == set->nelem) + { + Idx *new_elems; + set->alloc = (set->alloc + 1) * 2; + new_elems = re_realloc (set->elems, Idx, set->alloc); + if (BE (new_elems == NULL, 0)) + return false; + set->elems = new_elems; + } + + /* Insert the new element. */ + set->elems[set->nelem++] = elem; + return true; +} + +/* Compare two node sets SET1 and SET2. + Return true if SET1 and SET2 are equivalent. */ + +static bool +internal_function __attribute ((pure)) +re_node_set_compare (const re_node_set *set1, const re_node_set *set2) +{ + Idx i; + if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem) + return false; + for (i = set1->nelem ; REG_VALID_INDEX (--i) ; ) + if (set1->elems[i] != set2->elems[i]) + return false; + return true; +} + +/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise. */ + +static Idx +internal_function __attribute ((pure)) +re_node_set_contains (const re_node_set *set, Idx elem) +{ + __re_size_t idx, right, mid; + if (! REG_VALID_NONZERO_INDEX (set->nelem)) + return 0; + + /* Binary search the element. */ + idx = 0; + right = set->nelem - 1; + while (idx < right) + { + mid = (idx + right) / 2; + if (set->elems[mid] < elem) + idx = mid + 1; + else + right = mid; + } + return set->elems[idx] == elem ? idx + 1 : 0; +} + +static void +internal_function +re_node_set_remove_at (re_node_set *set, Idx idx) +{ + if (idx < 0 || idx >= set->nelem) + return; + --set->nelem; + for (; idx < set->nelem; idx++) + set->elems[idx] = set->elems[idx + 1]; +} + + +/* Add the token TOKEN to dfa->nodes, and return the index of the token. + Or return REG_MISSING if an error occurred. */ + +static Idx +internal_function +re_dfa_add_node (re_dfa_t *dfa, re_token_t token) +{ + if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0)) + { + size_t new_nodes_alloc = dfa->nodes_alloc * 2; + Idx *new_nexts, *new_indices; + re_node_set *new_edests, *new_eclosures; + re_token_t *new_nodes; + size_t max_object_size = + MAX (sizeof (re_token_t), + MAX (sizeof (re_node_set), + sizeof (Idx))); + + /* Avoid overflows. */ + if (BE (SIZE_MAX / 2 / max_object_size < dfa->nodes_alloc, 0)) + return REG_MISSING; + + new_nodes = re_realloc (dfa->nodes, re_token_t, new_nodes_alloc); + if (BE (new_nodes == NULL, 0)) + return REG_MISSING; + dfa->nodes = new_nodes; + new_nexts = re_realloc (dfa->nexts, Idx, new_nodes_alloc); + new_indices = re_realloc (dfa->org_indices, Idx, new_nodes_alloc); + new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc); + new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc); + if (BE (new_nexts == NULL || new_indices == NULL + || new_edests == NULL || new_eclosures == NULL, 0)) + return REG_MISSING; + dfa->nexts = new_nexts; + dfa->org_indices = new_indices; + dfa->edests = new_edests; + dfa->eclosures = new_eclosures; + dfa->nodes_alloc = new_nodes_alloc; + } + dfa->nodes[dfa->nodes_len] = token; + dfa->nodes[dfa->nodes_len].constraint = 0; +#ifdef RE_ENABLE_I18N + { + int type = token.type; + dfa->nodes[dfa->nodes_len].accept_mb = + (type == OP_PERIOD && dfa->mb_cur_max > 1) || type == COMPLEX_BRACKET; + } +#endif + dfa->nexts[dfa->nodes_len] = REG_MISSING; + re_node_set_init_empty (dfa->edests + dfa->nodes_len); + re_node_set_init_empty (dfa->eclosures + dfa->nodes_len); + return dfa->nodes_len++; +} + +static inline re_hashval_t +internal_function +calc_state_hash (const re_node_set *nodes, unsigned int context) +{ + re_hashval_t hash = nodes->nelem + context; + Idx i; + for (i = 0 ; i < nodes->nelem ; i++) + hash += nodes->elems[i]; + return hash; +} + +/* Search for the state whose node_set is equivalent to NODES. + Return the pointer to the state, if we found it in the DFA. + Otherwise create the new one and return it. In case of an error + return NULL and set the error code in ERR. + Note: - We assume NULL as the invalid state, then it is possible that + return value is NULL and ERR is REG_NOERROR. + - We never return non-NULL value in case of any errors, it is for + optimization. */ + +static re_dfastate_t * +internal_function __attribute_warn_unused_result__ +re_acquire_state (reg_errcode_t *err, const re_dfa_t *dfa, + const re_node_set *nodes) +{ + re_hashval_t hash; + re_dfastate_t *new_state; + struct re_state_table_entry *spot; + Idx i; +#ifdef lint + /* Suppress bogus uninitialized-variable warnings. */ + *err = REG_NOERROR; +#endif + if (BE (nodes->nelem == 0, 0)) + { + *err = REG_NOERROR; + return NULL; + } + hash = calc_state_hash (nodes, 0); + spot = dfa->state_table + (hash & dfa->state_hash_mask); + + for (i = 0 ; i < spot->num ; i++) + { + re_dfastate_t *state = spot->array[i]; + if (hash != state->hash) + continue; + if (re_node_set_compare (&state->nodes, nodes)) + return state; + } + + /* There are no appropriate state in the dfa, create the new one. */ + new_state = create_ci_newstate (dfa, nodes, hash); + if (BE (new_state == NULL, 0)) + *err = REG_ESPACE; + + return new_state; +} + +/* Search for the state whose node_set is equivalent to NODES and + whose context is equivalent to CONTEXT. + Return the pointer to the state, if we found it in the DFA. + Otherwise create the new one and return it. In case of an error + return NULL and set the error code in ERR. + Note: - We assume NULL as the invalid state, then it is possible that + return value is NULL and ERR is REG_NOERROR. + - We never return non-NULL value in case of any errors, it is for + optimization. */ + +static re_dfastate_t * +internal_function __attribute_warn_unused_result__ +re_acquire_state_context (reg_errcode_t *err, const re_dfa_t *dfa, + const re_node_set *nodes, unsigned int context) +{ + re_hashval_t hash; + re_dfastate_t *new_state; + struct re_state_table_entry *spot; + Idx i; +#ifdef lint + /* Suppress bogus uninitialized-variable warnings. */ + *err = REG_NOERROR; +#endif + if (nodes->nelem == 0) + { + *err = REG_NOERROR; + return NULL; + } + hash = calc_state_hash (nodes, context); + spot = dfa->state_table + (hash & dfa->state_hash_mask); + + for (i = 0 ; i < spot->num ; i++) + { + re_dfastate_t *state = spot->array[i]; + if (state->hash == hash + && state->context == context + && re_node_set_compare (state->entrance_nodes, nodes)) + return state; + } + /* There are no appropriate state in `dfa', create the new one. */ + new_state = create_cd_newstate (dfa, nodes, context, hash); + if (BE (new_state == NULL, 0)) + *err = REG_ESPACE; + + return new_state; +} + +/* Finish initialization of the new state NEWSTATE, and using its hash value + HASH put in the appropriate bucket of DFA's state table. Return value + indicates the error code if failed. */ + +static reg_errcode_t +__attribute_warn_unused_result__ +register_state (const re_dfa_t *dfa, re_dfastate_t *newstate, + re_hashval_t hash) +{ + struct re_state_table_entry *spot; + reg_errcode_t err; + Idx i; + + newstate->hash = hash; + err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem); + if (BE (err != REG_NOERROR, 0)) + return REG_ESPACE; + for (i = 0; i < newstate->nodes.nelem; i++) + { + Idx elem = newstate->nodes.elems[i]; + if (!IS_EPSILON_NODE (dfa->nodes[elem].type)) + if (BE (! re_node_set_insert_last (&newstate->non_eps_nodes, elem), 0)) + return REG_ESPACE; + } + + spot = dfa->state_table + (hash & dfa->state_hash_mask); + if (BE (spot->alloc <= spot->num, 0)) + { + Idx new_alloc = 2 * spot->num + 2; + re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *, + new_alloc); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + spot->array = new_array; + spot->alloc = new_alloc; + } + spot->array[spot->num++] = newstate; + return REG_NOERROR; +} + +static void +free_state (re_dfastate_t *state) +{ + re_node_set_free (&state->non_eps_nodes); + re_node_set_free (&state->inveclosure); + if (state->entrance_nodes != &state->nodes) + { + re_node_set_free (state->entrance_nodes); + re_free (state->entrance_nodes); + } + re_node_set_free (&state->nodes); + re_free (state->word_trtable); + re_free (state->trtable); + re_free (state); +} + +/* Create the new state which is independ of contexts. + Return the new state if succeeded, otherwise return NULL. */ + +static re_dfastate_t * +internal_function __attribute_warn_unused_result__ +create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes, + re_hashval_t hash) +{ + Idx i; + reg_errcode_t err; + re_dfastate_t *newstate; + + newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1); + if (BE (newstate == NULL, 0)) + return NULL; + err = re_node_set_init_copy (&newstate->nodes, nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_free (newstate); + return NULL; + } + + newstate->entrance_nodes = &newstate->nodes; + for (i = 0 ; i < nodes->nelem ; i++) + { + re_token_t *node = dfa->nodes + nodes->elems[i]; + re_token_type_t type = node->type; + if (type == CHARACTER && !node->constraint) + continue; +#ifdef RE_ENABLE_I18N + newstate->accept_mb |= node->accept_mb; +#endif /* RE_ENABLE_I18N */ + + /* If the state has the halt node, the state is a halt state. */ + if (type == END_OF_RE) + newstate->halt = 1; + else if (type == OP_BACK_REF) + newstate->has_backref = 1; + else if (type == ANCHOR || node->constraint) + newstate->has_constraint = 1; + } + err = register_state (dfa, newstate, hash); + if (BE (err != REG_NOERROR, 0)) + { + free_state (newstate); + newstate = NULL; + } + return newstate; +} + +/* Create the new state which is depend on the context CONTEXT. + Return the new state if succeeded, otherwise return NULL. */ + +static re_dfastate_t * +internal_function __attribute_warn_unused_result__ +create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes, + unsigned int context, re_hashval_t hash) +{ + Idx i, nctx_nodes = 0; + reg_errcode_t err; + re_dfastate_t *newstate; + + newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1); + if (BE (newstate == NULL, 0)) + return NULL; + err = re_node_set_init_copy (&newstate->nodes, nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_free (newstate); + return NULL; + } + + newstate->context = context; + newstate->entrance_nodes = &newstate->nodes; + + for (i = 0 ; i < nodes->nelem ; i++) + { + re_token_t *node = dfa->nodes + nodes->elems[i]; + re_token_type_t type = node->type; + unsigned int constraint = node->constraint; + + if (type == CHARACTER && !constraint) + continue; +#ifdef RE_ENABLE_I18N + newstate->accept_mb |= node->accept_mb; +#endif /* RE_ENABLE_I18N */ + + /* If the state has the halt node, the state is a halt state. */ + if (type == END_OF_RE) + newstate->halt = 1; + else if (type == OP_BACK_REF) + newstate->has_backref = 1; + + if (constraint) + { + if (newstate->entrance_nodes == &newstate->nodes) + { + newstate->entrance_nodes = re_malloc (re_node_set, 1); + if (BE (newstate->entrance_nodes == NULL, 0)) + { + free_state (newstate); + return NULL; + } + if (re_node_set_init_copy (newstate->entrance_nodes, nodes) + != REG_NOERROR) + return NULL; + nctx_nodes = 0; + newstate->has_constraint = 1; + } + + if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context)) + { + re_node_set_remove_at (&newstate->nodes, i - nctx_nodes); + ++nctx_nodes; + } + } + } + err = register_state (dfa, newstate, hash); + if (BE (err != REG_NOERROR, 0)) + { + free_state (newstate); + newstate = NULL; + } + return newstate; +} diff --git a/gnulib/regex_internal.h b/gnulib/regex_internal.h new file mode 100644 index 000000000..e1b4c61b3 --- /dev/null +++ b/gnulib/regex_internal.h @@ -0,0 +1,871 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free + Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + 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 + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _REGEX_INTERNAL_H +#define _REGEX_INTERNAL_H 1 + +#include +#include +#include +#include +#include +#include + +#include +#ifndef _LIBC +# include "localcharset.h" +#endif +#if defined HAVE_LOCALE_H || defined _LIBC +# include +#endif + +#include +#include +#include +#if defined _LIBC +# include +#else +# define __libc_lock_init(NAME) do { } while (0) +# define __libc_lock_lock(NAME) do { } while (0) +# define __libc_lock_unlock(NAME) do { } while (0) +#endif + +/* In case that the system doesn't have isblank(). */ +#if !defined _LIBC && ! (defined isblank || (HAVE_ISBLANK && HAVE_DECL_ISBLANK)) +# define isblank(ch) ((ch) == ' ' || (ch) == '\t') +#endif + +#ifdef _LIBC +# ifndef _RE_DEFINE_LOCALE_FUNCTIONS +# define _RE_DEFINE_LOCALE_FUNCTIONS 1 +# include +# include +# include +# endif +#endif + +/* This is for other GNU distributions with internationalized messages. */ +#if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC +# include +# ifdef _LIBC +# undef gettext +# define gettext(msgid) \ + INTUSE(__dcgettext) (_libc_intl_domainname, msgid, LC_MESSAGES) +# endif +#else +# define gettext(msgid) (msgid) +#endif + +#ifndef gettext_noop +/* This define is so xgettext can find the internationalizable + strings. */ +# define gettext_noop(String) String +#endif + +/* For loser systems without the definition. */ +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif + +#if (defined MB_CUR_MAX && HAVE_LOCALE_H && HAVE_WCTYPE_H && HAVE_ISWCTYPE && HAVE_WCSCOLL) || _LIBC +# define RE_ENABLE_I18N +#endif + +#if __GNUC__ >= 3 +# define BE(expr, val) __builtin_expect (expr, val) +#else +# define BE(expr, val) (expr) +# ifdef _LIBC +# define inline +# endif +#endif + +/* Number of ASCII characters. */ +#define ASCII_CHARS 0x80 + +/* Number of single byte characters. */ +#define SBC_MAX (UCHAR_MAX + 1) + +#define COLL_ELEM_LEN_MAX 8 + +/* The character which represents newline. */ +#define NEWLINE_CHAR '\n' +#define WIDE_NEWLINE_CHAR L'\n' + +/* Rename to standard API for using out of glibc. */ +#ifndef _LIBC +# define __wctype wctype +# define __iswctype iswctype +# define __btowc btowc +# define __wcrtomb wcrtomb +# define __mbrtowc mbrtowc +# define __regfree regfree +# define attribute_hidden +#endif /* not _LIBC */ + +#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +# define __attribute(arg) __attribute__ (arg) +#else +# define __attribute(arg) +#endif + +typedef __re_idx_t Idx; + +/* Special return value for failure to match. */ +#define REG_MISSING ((Idx) -1) + +/* Special return value for internal error. */ +#define REG_ERROR ((Idx) -2) + +/* Test whether N is a valid index, and is not one of the above. */ +#ifdef _REGEX_LARGE_OFFSETS +# define REG_VALID_INDEX(n) ((Idx) (n) < REG_ERROR) +#else +# define REG_VALID_INDEX(n) (0 <= (n)) +#endif + +/* Test whether N is a valid nonzero index. */ +#ifdef _REGEX_LARGE_OFFSETS +# define REG_VALID_NONZERO_INDEX(n) ((Idx) ((n) - 1) < (Idx) (REG_ERROR - 1)) +#else +# define REG_VALID_NONZERO_INDEX(n) (0 < (n)) +#endif + +/* A hash value, suitable for computing hash tables. */ +typedef __re_size_t re_hashval_t; + +/* An integer used to represent a set of bits. It must be unsigned, + and must be at least as wide as unsigned int. */ +typedef unsigned long int bitset_word_t; +/* All bits set in a bitset_word_t. */ +#define BITSET_WORD_MAX ULONG_MAX + +/* Number of bits in a bitset_word_t. For portability to hosts with + padding bits, do not use '(sizeof (bitset_word_t) * CHAR_BIT)'; + instead, deduce it directly from BITSET_WORD_MAX. Avoid + greater-than-32-bit integers and unconditional shifts by more than + 31 bits, as they're not portable. */ +#if BITSET_WORD_MAX == 0xffffffffUL +# define BITSET_WORD_BITS 32 +#elif BITSET_WORD_MAX >> 31 >> 4 == 1 +# define BITSET_WORD_BITS 36 +#elif BITSET_WORD_MAX >> 31 >> 16 == 1 +# define BITSET_WORD_BITS 48 +#elif BITSET_WORD_MAX >> 31 >> 28 == 1 +# define BITSET_WORD_BITS 60 +#elif BITSET_WORD_MAX >> 31 >> 31 >> 1 == 1 +# define BITSET_WORD_BITS 64 +#elif BITSET_WORD_MAX >> 31 >> 31 >> 9 == 1 +# define BITSET_WORD_BITS 72 +#elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 3 == 1 +# define BITSET_WORD_BITS 128 +#elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 7 == 1 +# define BITSET_WORD_BITS 256 +#elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 7 > 1 +# define BITSET_WORD_BITS 257 /* any value > SBC_MAX will do here */ +# if BITSET_WORD_BITS <= SBC_MAX +# error "Invalid SBC_MAX" +# endif +#else +# error "Add case for new bitset_word_t size" +#endif + +/* Number of bitset_word_t values in a bitset_t. */ +#define BITSET_WORDS ((SBC_MAX + BITSET_WORD_BITS - 1) / BITSET_WORD_BITS) + +typedef bitset_word_t bitset_t[BITSET_WORDS]; +typedef bitset_word_t *re_bitset_ptr_t; +typedef const bitset_word_t *re_const_bitset_ptr_t; + +#define PREV_WORD_CONSTRAINT 0x0001 +#define PREV_NOTWORD_CONSTRAINT 0x0002 +#define NEXT_WORD_CONSTRAINT 0x0004 +#define NEXT_NOTWORD_CONSTRAINT 0x0008 +#define PREV_NEWLINE_CONSTRAINT 0x0010 +#define NEXT_NEWLINE_CONSTRAINT 0x0020 +#define PREV_BEGBUF_CONSTRAINT 0x0040 +#define NEXT_ENDBUF_CONSTRAINT 0x0080 +#define WORD_DELIM_CONSTRAINT 0x0100 +#define NOT_WORD_DELIM_CONSTRAINT 0x0200 + +typedef enum +{ + INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT, + WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT, + WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT, + INSIDE_NOTWORD = PREV_NOTWORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT, + LINE_FIRST = PREV_NEWLINE_CONSTRAINT, + LINE_LAST = NEXT_NEWLINE_CONSTRAINT, + BUF_FIRST = PREV_BEGBUF_CONSTRAINT, + BUF_LAST = NEXT_ENDBUF_CONSTRAINT, + WORD_DELIM = WORD_DELIM_CONSTRAINT, + NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT +} re_context_type; + +typedef struct +{ + Idx alloc; + Idx nelem; + Idx *elems; +} re_node_set; + +typedef enum +{ + NON_TYPE = 0, + + /* Node type, These are used by token, node, tree. */ + CHARACTER = 1, + END_OF_RE = 2, + SIMPLE_BRACKET = 3, + OP_BACK_REF = 4, + OP_PERIOD = 5, +#ifdef RE_ENABLE_I18N + COMPLEX_BRACKET = 6, + OP_UTF8_PERIOD = 7, +#endif /* RE_ENABLE_I18N */ + + /* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used + when the debugger shows values of this enum type. */ +#define EPSILON_BIT 8 + OP_OPEN_SUBEXP = EPSILON_BIT | 0, + OP_CLOSE_SUBEXP = EPSILON_BIT | 1, + OP_ALT = EPSILON_BIT | 2, + OP_DUP_ASTERISK = EPSILON_BIT | 3, + ANCHOR = EPSILON_BIT | 4, + + /* Tree type, these are used only by tree. */ + CONCAT = 16, + SUBEXP = 17, + + /* Token type, these are used only by token. */ + OP_DUP_PLUS = 18, + OP_DUP_QUESTION, + OP_OPEN_BRACKET, + OP_CLOSE_BRACKET, + OP_CHARSET_RANGE, + OP_OPEN_DUP_NUM, + OP_CLOSE_DUP_NUM, + OP_NON_MATCH_LIST, + OP_OPEN_COLL_ELEM, + OP_CLOSE_COLL_ELEM, + OP_OPEN_EQUIV_CLASS, + OP_CLOSE_EQUIV_CLASS, + OP_OPEN_CHAR_CLASS, + OP_CLOSE_CHAR_CLASS, + OP_WORD, + OP_NOTWORD, + OP_SPACE, + OP_NOTSPACE, + BACK_SLASH + +} re_token_type_t; + +#ifdef RE_ENABLE_I18N +typedef struct +{ + /* Multibyte characters. */ + wchar_t *mbchars; + + /* Collating symbols. */ +# ifdef _LIBC + int32_t *coll_syms; +# endif + + /* Equivalence classes. */ +# ifdef _LIBC + int32_t *equiv_classes; +# endif + + /* Range expressions. */ +# ifdef _LIBC + uint32_t *range_starts; + uint32_t *range_ends; +# else /* not _LIBC */ + wchar_t *range_starts; + wchar_t *range_ends; +# endif /* not _LIBC */ + + /* Character classes. */ + wctype_t *char_classes; + + /* If this character set is the non-matching list. */ + unsigned int non_match : 1; + + /* # of multibyte characters. */ + Idx nmbchars; + + /* # of collating symbols. */ + Idx ncoll_syms; + + /* # of equivalence classes. */ + Idx nequiv_classes; + + /* # of range expressions. */ + Idx nranges; + + /* # of character classes. */ + Idx nchar_classes; +} re_charset_t; +#endif /* RE_ENABLE_I18N */ + +typedef struct +{ + union + { + unsigned char c; /* for CHARACTER */ + re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */ +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; /* for COMPLEX_BRACKET */ +#endif /* RE_ENABLE_I18N */ + Idx idx; /* for BACK_REF */ + re_context_type ctx_type; /* for ANCHOR */ + } opr; +#if __GNUC__ >= 2 && !__STRICT_ANSI__ + re_token_type_t type : 8; +#else + re_token_type_t type; +#endif + unsigned int constraint : 10; /* context constraint */ + unsigned int duplicated : 1; + unsigned int opt_subexp : 1; +#ifdef RE_ENABLE_I18N + unsigned int accept_mb : 1; + /* These 2 bits can be moved into the union if needed (e.g. if running out + of bits; move opr.c to opr.c.c and move the flags to opr.c.flags). */ + unsigned int mb_partial : 1; +#endif + unsigned int word_char : 1; +} re_token_t; + +#define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT) + +struct re_string_t +{ + /* Indicate the raw buffer which is the original string passed as an + argument of regexec(), re_search(), etc.. */ + const unsigned char *raw_mbs; + /* Store the multibyte string. In case of "case insensitive mode" like + REG_ICASE, upper cases of the string are stored, otherwise MBS points + the same address that RAW_MBS points. */ + unsigned char *mbs; +#ifdef RE_ENABLE_I18N + /* Store the wide character string which is corresponding to MBS. */ + wint_t *wcs; + Idx *offsets; + mbstate_t cur_state; +#endif + /* Index in RAW_MBS. Each character mbs[i] corresponds to + raw_mbs[raw_mbs_idx + i]. */ + Idx raw_mbs_idx; + /* The length of the valid characters in the buffers. */ + Idx valid_len; + /* The corresponding number of bytes in raw_mbs array. */ + Idx valid_raw_len; + /* The length of the buffers MBS and WCS. */ + Idx bufs_len; + /* The index in MBS, which is updated by re_string_fetch_byte. */ + Idx cur_idx; + /* length of RAW_MBS array. */ + Idx raw_len; + /* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN. */ + Idx len; + /* End of the buffer may be shorter than its length in the cases such + as re_match_2, re_search_2. Then, we use STOP for end of the buffer + instead of LEN. */ + Idx raw_stop; + /* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS. */ + Idx stop; + + /* The context of mbs[0]. We store the context independently, since + the context of mbs[0] may be different from raw_mbs[0], which is + the beginning of the input string. */ + unsigned int tip_context; + /* The translation passed as a part of an argument of re_compile_pattern. */ + RE_TRANSLATE_TYPE trans; + /* Copy of re_dfa_t's word_char. */ + re_const_bitset_ptr_t word_char; + /* true if REG_ICASE. */ + unsigned char icase; + unsigned char is_utf8; + unsigned char map_notascii; + unsigned char mbs_allocated; + unsigned char offsets_needed; + unsigned char newline_anchor; + unsigned char word_ops_used; + int mb_cur_max; +}; +typedef struct re_string_t re_string_t; + + +struct re_dfa_t; +typedef struct re_dfa_t re_dfa_t; + +#ifndef _LIBC +# if defined __i386__ && !defined __EMX__ +# define internal_function __attribute ((regparm (3), stdcall)) +# else +# define internal_function +# endif +#endif + +static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr, + Idx new_buf_len) + internal_function; +#ifdef RE_ENABLE_I18N +static void build_wcs_buffer (re_string_t *pstr) internal_function; +static reg_errcode_t build_wcs_upper_buffer (re_string_t *pstr) + internal_function; +#endif /* RE_ENABLE_I18N */ +static void build_upper_buffer (re_string_t *pstr) internal_function; +static void re_string_translate_buffer (re_string_t *pstr) internal_function; +static unsigned int re_string_context_at (const re_string_t *input, Idx idx, + int eflags) + internal_function __attribute ((pure)); +#define re_string_peek_byte(pstr, offset) \ + ((pstr)->mbs[(pstr)->cur_idx + offset]) +#define re_string_fetch_byte(pstr) \ + ((pstr)->mbs[(pstr)->cur_idx++]) +#define re_string_first_byte(pstr, idx) \ + ((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF) +#define re_string_is_single_byte_char(pstr, idx) \ + ((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \ + || (pstr)->wcs[(idx) + 1] != WEOF)) +#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx) +#define re_string_cur_idx(pstr) ((pstr)->cur_idx) +#define re_string_get_buffer(pstr) ((pstr)->mbs) +#define re_string_length(pstr) ((pstr)->len) +#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx]) +#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx)) +#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx)) + +#include + +#ifndef _LIBC +# if HAVE_ALLOCA +/* The OS usually guarantees only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + allocate anything larger than 4096 bytes. Also care for the possibility + of a few compiler-allocated temporary stack slots. */ +# define __libc_use_alloca(n) ((n) < 4032) +# else +/* alloca is implemented with malloc, so just use malloc. */ +# define __libc_use_alloca(n) 0 +# endif +#endif + +#ifndef MAX +# define MAX(a,b) ((a) < (b) ? (b) : (a)) +#endif + +#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t))) +#define re_realloc(p,t,n) ((t *) realloc (p, (n) * sizeof (t))) +#define re_free(p) free (p) + +struct bin_tree_t +{ + struct bin_tree_t *parent; + struct bin_tree_t *left; + struct bin_tree_t *right; + struct bin_tree_t *first; + struct bin_tree_t *next; + + re_token_t token; + + /* `node_idx' is the index in dfa->nodes, if `type' == 0. + Otherwise `type' indicate the type of this node. */ + Idx node_idx; +}; +typedef struct bin_tree_t bin_tree_t; + +#define BIN_TREE_STORAGE_SIZE \ + ((1024 - sizeof (void *)) / sizeof (bin_tree_t)) + +struct bin_tree_storage_t +{ + struct bin_tree_storage_t *next; + bin_tree_t data[BIN_TREE_STORAGE_SIZE]; +}; +typedef struct bin_tree_storage_t bin_tree_storage_t; + +#define CONTEXT_WORD 1 +#define CONTEXT_NEWLINE (CONTEXT_WORD << 1) +#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1) +#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1) + +#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD) +#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE) +#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF) +#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF) +#define IS_ORDINARY_CONTEXT(c) ((c) == 0) + +#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_') +#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR) +#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_') +#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR) + +#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \ + ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \ + || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \ + || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\ + || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context))) + +#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \ + ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \ + || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \ + || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \ + || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context))) + +struct re_dfastate_t +{ + re_hashval_t hash; + re_node_set nodes; + re_node_set non_eps_nodes; + re_node_set inveclosure; + re_node_set *entrance_nodes; + struct re_dfastate_t **trtable, **word_trtable; + unsigned int context : 4; + unsigned int halt : 1; + /* If this state can accept `multi byte'. + Note that we refer to multibyte characters, and multi character + collating elements as `multi byte'. */ + unsigned int accept_mb : 1; + /* If this state has backreference node(s). */ + unsigned int has_backref : 1; + unsigned int has_constraint : 1; +}; +typedef struct re_dfastate_t re_dfastate_t; + +struct re_state_table_entry +{ + Idx num; + Idx alloc; + re_dfastate_t **array; +}; + +/* Array type used in re_sub_match_last_t and re_sub_match_top_t. */ + +typedef struct +{ + Idx next_idx; + Idx alloc; + re_dfastate_t **array; +} state_array_t; + +/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP. */ + +typedef struct +{ + Idx node; + Idx str_idx; /* The position NODE match at. */ + state_array_t path; +} re_sub_match_last_t; + +/* Store information about the node NODE whose type is OP_OPEN_SUBEXP. + And information about the node, whose type is OP_CLOSE_SUBEXP, + corresponding to NODE is stored in LASTS. */ + +typedef struct +{ + Idx str_idx; + Idx node; + state_array_t *path; + Idx alasts; /* Allocation size of LASTS. */ + Idx nlasts; /* The number of LASTS. */ + re_sub_match_last_t **lasts; +} re_sub_match_top_t; + +struct re_backref_cache_entry +{ + Idx node; + Idx str_idx; + Idx subexp_from; + Idx subexp_to; + char more; + char unused; + unsigned short int eps_reachable_subexps_map; +}; + +typedef struct +{ + /* The string object corresponding to the input string. */ + re_string_t input; +#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) + const re_dfa_t *const dfa; +#else + const re_dfa_t *dfa; +#endif + /* EFLAGS of the argument of regexec. */ + int eflags; + /* Where the matching ends. */ + Idx match_last; + Idx last_node; + /* The state log used by the matcher. */ + re_dfastate_t **state_log; + Idx state_log_top; + /* Back reference cache. */ + Idx nbkref_ents; + Idx abkref_ents; + struct re_backref_cache_entry *bkref_ents; + int max_mb_elem_len; + Idx nsub_tops; + Idx asub_tops; + re_sub_match_top_t **sub_tops; +} re_match_context_t; + +typedef struct +{ + re_dfastate_t **sifted_states; + re_dfastate_t **limited_states; + Idx last_node; + Idx last_str_idx; + re_node_set limits; +} re_sift_context_t; + +struct re_fail_stack_ent_t +{ + Idx idx; + Idx node; + regmatch_t *regs; + re_node_set eps_via_nodes; +}; + +struct re_fail_stack_t +{ + Idx num; + Idx alloc; + struct re_fail_stack_ent_t *stack; +}; + +struct re_dfa_t +{ + re_token_t *nodes; + size_t nodes_alloc; + size_t nodes_len; + Idx *nexts; + Idx *org_indices; + re_node_set *edests; + re_node_set *eclosures; + re_node_set *inveclosures; + struct re_state_table_entry *state_table; + re_dfastate_t *init_state; + re_dfastate_t *init_state_word; + re_dfastate_t *init_state_nl; + re_dfastate_t *init_state_begbuf; + bin_tree_t *str_tree; + bin_tree_storage_t *str_tree_storage; + re_bitset_ptr_t sb_char; + int str_tree_storage_idx; + + /* number of subexpressions `re_nsub' is in regex_t. */ + re_hashval_t state_hash_mask; + Idx init_node; + Idx nbackref; /* The number of backreference in this dfa. */ + + /* Bitmap expressing which backreference is used. */ + bitset_word_t used_bkref_map; + bitset_word_t completed_bkref_map; + + unsigned int has_plural_match : 1; + /* If this dfa has "multibyte node", which is a backreference or + a node which can accept multibyte character or multi character + collating element. */ + unsigned int has_mb_node : 1; + unsigned int is_utf8 : 1; + unsigned int map_notascii : 1; + unsigned int word_ops_used : 1; + int mb_cur_max; + bitset_t word_char; + reg_syntax_t syntax; + Idx *subexp_map; +#ifdef DEBUG + char* re_str; +#endif +#ifdef _LIBC + __libc_lock_define (, lock) +#endif +}; + +#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set)) +#define re_node_set_remove(set,id) \ + (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1)) +#define re_node_set_empty(p) ((p)->nelem = 0) +#define re_node_set_free(set) re_free ((set)->elems) + + +typedef enum +{ + SB_CHAR, + MB_CHAR, + EQUIV_CLASS, + COLL_SYM, + CHAR_CLASS +} bracket_elem_type; + +typedef struct +{ + bracket_elem_type type; + union + { + unsigned char ch; + unsigned char *name; + wchar_t wch; + } opr; +} bracket_elem_t; + + +/* Inline functions for bitset_t operation. */ + +static inline void +bitset_set (bitset_t set, Idx i) +{ + set[i / BITSET_WORD_BITS] |= (bitset_word_t) 1 << i % BITSET_WORD_BITS; +} + +static inline void +bitset_clear (bitset_t set, Idx i) +{ + set[i / BITSET_WORD_BITS] &= ~ ((bitset_word_t) 1 << i % BITSET_WORD_BITS); +} + +static inline bool +bitset_contain (const bitset_t set, Idx i) +{ + return (set[i / BITSET_WORD_BITS] >> i % BITSET_WORD_BITS) & 1; +} + +static inline void +bitset_empty (bitset_t set) +{ + memset (set, '\0', sizeof (bitset_t)); +} + +static inline void +bitset_set_all (bitset_t set) +{ + memset (set, -1, sizeof (bitset_word_t) * (SBC_MAX / BITSET_WORD_BITS)); + if (SBC_MAX % BITSET_WORD_BITS != 0) + set[BITSET_WORDS - 1] = + ((bitset_word_t) 1 << SBC_MAX % BITSET_WORD_BITS) - 1; +} + +static inline void +bitset_copy (bitset_t dest, const bitset_t src) +{ + memcpy (dest, src, sizeof (bitset_t)); +} + +static inline void +bitset_not (bitset_t set) +{ + int bitset_i; + for (bitset_i = 0; bitset_i < SBC_MAX / BITSET_WORD_BITS; ++bitset_i) + set[bitset_i] = ~set[bitset_i]; + if (SBC_MAX % BITSET_WORD_BITS != 0) + set[BITSET_WORDS - 1] = + ((((bitset_word_t) 1 << SBC_MAX % BITSET_WORD_BITS) - 1) + & ~set[BITSET_WORDS - 1]); +} + +static inline void +bitset_merge (bitset_t dest, const bitset_t src) +{ + int bitset_i; + for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) + dest[bitset_i] |= src[bitset_i]; +} + +static inline void +bitset_mask (bitset_t dest, const bitset_t src) +{ + int bitset_i; + for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) + dest[bitset_i] &= src[bitset_i]; +} + +#ifdef RE_ENABLE_I18N +/* Inline functions for re_string. */ +static inline int +internal_function __attribute ((pure)) +re_string_char_size_at (const re_string_t *pstr, Idx idx) +{ + int byte_idx; + if (pstr->mb_cur_max == 1) + return 1; + for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx) + if (pstr->wcs[idx + byte_idx] != WEOF) + break; + return byte_idx; +} + +static inline wint_t +internal_function __attribute ((pure)) +re_string_wchar_at (const re_string_t *pstr, Idx idx) +{ + if (pstr->mb_cur_max == 1) + return (wint_t) pstr->mbs[idx]; + return (wint_t) pstr->wcs[idx]; +} + +static int +internal_function __attribute ((pure)) +re_string_elem_size_at (const re_string_t *pstr, Idx idx) +{ +# ifdef _LIBC + const unsigned char *p, *extra; + const int32_t *table, *indirect; + int32_t tmp; +# include + uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + + if (nrules != 0) + { + table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_INDIRECTMB); + p = pstr->mbs + idx; + tmp = findidx (&p); + return p - pstr->mbs - idx; + } + else +# endif /* _LIBC */ + return 1; +} +#endif /* RE_ENABLE_I18N */ + +#ifndef __GNUC_PREREQ +# if defined __GNUC__ && defined __GNUC_MINOR__ +# define __GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +# else +# define __GNUC_PREREQ(maj, min) 0 +# endif +#endif + +#if __GNUC_PREREQ (3,4) +# undef __attribute_warn_unused_result__ +# define __attribute_warn_unused_result__ \ + __attribute__ ((__warn_unused_result__)) +#else +# define __attribute_warn_unused_result__ /* empty */ +#endif + +#endif /* _REGEX_INTERNAL_H */ diff --git a/gnulib/regexec.c b/gnulib/regexec.c new file mode 100644 index 000000000..9388ac12b --- /dev/null +++ b/gnulib/regexec.c @@ -0,0 +1,4416 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free + Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + 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 + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags, + Idx n) internal_function; +static void match_ctx_clean (re_match_context_t *mctx) internal_function; +static void match_ctx_free (re_match_context_t *cache) internal_function; +static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, Idx node, + Idx str_idx, Idx from, Idx to) + internal_function; +static Idx search_cur_bkref_entry (const re_match_context_t *mctx, Idx str_idx) + internal_function; +static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, Idx node, + Idx str_idx) internal_function; +static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop, + Idx node, Idx str_idx) + internal_function; +static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, + re_dfastate_t **limited_sts, Idx last_node, + Idx last_str_idx) + internal_function; +static reg_errcode_t re_search_internal (const regex_t *preg, + const char *string, Idx length, + Idx start, Idx last_start, Idx stop, + size_t nmatch, regmatch_t pmatch[], + int eflags) internal_function; +static regoff_t re_search_2_stub (struct re_pattern_buffer *bufp, + const char *string1, Idx length1, + const char *string2, Idx length2, + Idx start, regoff_t range, + struct re_registers *regs, + Idx stop, bool ret_len) internal_function; +static regoff_t re_search_stub (struct re_pattern_buffer *bufp, + const char *string, Idx length, Idx start, + regoff_t range, Idx stop, + struct re_registers *regs, + bool ret_len) internal_function; +static unsigned int re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, + Idx nregs, int regs_allocated) + internal_function; +static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx) + internal_function; +static Idx check_matching (re_match_context_t *mctx, bool fl_longest_match, + Idx *p_match_first) internal_function; +static Idx check_halt_state_context (const re_match_context_t *mctx, + const re_dfastate_t *state, Idx idx) + internal_function; +static void update_regs (const re_dfa_t *dfa, regmatch_t *pmatch, + regmatch_t *prev_idx_match, Idx cur_node, + Idx cur_idx, Idx nmatch) internal_function; +static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs, + Idx str_idx, Idx dest_node, Idx nregs, + regmatch_t *regs, + re_node_set *eps_via_nodes) + internal_function; +static reg_errcode_t set_regs (const regex_t *preg, + const re_match_context_t *mctx, + size_t nmatch, regmatch_t *pmatch, + bool fl_backtrack) internal_function; +static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs) + internal_function; + +#ifdef RE_ENABLE_I18N +static int sift_states_iter_mb (const re_match_context_t *mctx, + re_sift_context_t *sctx, + Idx node_idx, Idx str_idx, Idx max_str_idx) + internal_function; +#endif /* RE_ENABLE_I18N */ +static reg_errcode_t sift_states_backward (const re_match_context_t *mctx, + re_sift_context_t *sctx) + internal_function; +static reg_errcode_t build_sifted_states (const re_match_context_t *mctx, + re_sift_context_t *sctx, Idx str_idx, + re_node_set *cur_dest) + internal_function; +static reg_errcode_t update_cur_sifted_state (const re_match_context_t *mctx, + re_sift_context_t *sctx, + Idx str_idx, + re_node_set *dest_nodes) + internal_function; +static reg_errcode_t add_epsilon_src_nodes (const re_dfa_t *dfa, + re_node_set *dest_nodes, + const re_node_set *candidates) + internal_function; +static bool check_dst_limits (const re_match_context_t *mctx, + const re_node_set *limits, + Idx dst_node, Idx dst_idx, Idx src_node, + Idx src_idx) internal_function; +static int check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, + int boundaries, Idx subexp_idx, + Idx from_node, Idx bkref_idx) + internal_function; +static int check_dst_limits_calc_pos (const re_match_context_t *mctx, + Idx limit, Idx subexp_idx, + Idx node, Idx str_idx, + Idx bkref_idx) internal_function; +static reg_errcode_t check_subexp_limits (const re_dfa_t *dfa, + re_node_set *dest_nodes, + const re_node_set *candidates, + re_node_set *limits, + struct re_backref_cache_entry *bkref_ents, + Idx str_idx) internal_function; +static reg_errcode_t sift_states_bkref (const re_match_context_t *mctx, + re_sift_context_t *sctx, + Idx str_idx, const re_node_set *candidates) + internal_function; +static reg_errcode_t merge_state_array (const re_dfa_t *dfa, + re_dfastate_t **dst, + re_dfastate_t **src, Idx num) + internal_function; +static re_dfastate_t *find_recover_state (reg_errcode_t *err, + re_match_context_t *mctx) internal_function; +static re_dfastate_t *transit_state (reg_errcode_t *err, + re_match_context_t *mctx, + re_dfastate_t *state) internal_function; +static re_dfastate_t *merge_state_with_log (reg_errcode_t *err, + re_match_context_t *mctx, + re_dfastate_t *next_state) + internal_function; +static reg_errcode_t check_subexp_matching_top (re_match_context_t *mctx, + re_node_set *cur_nodes, + Idx str_idx) internal_function; +#if 0 +static re_dfastate_t *transit_state_sb (reg_errcode_t *err, + re_match_context_t *mctx, + re_dfastate_t *pstate) + internal_function; +#endif +#ifdef RE_ENABLE_I18N +static reg_errcode_t transit_state_mb (re_match_context_t *mctx, + re_dfastate_t *pstate) + internal_function; +#endif /* RE_ENABLE_I18N */ +static reg_errcode_t transit_state_bkref (re_match_context_t *mctx, + const re_node_set *nodes) + internal_function; +static reg_errcode_t get_subexp (re_match_context_t *mctx, + Idx bkref_node, Idx bkref_str_idx) + internal_function; +static reg_errcode_t get_subexp_sub (re_match_context_t *mctx, + const re_sub_match_top_t *sub_top, + re_sub_match_last_t *sub_last, + Idx bkref_node, Idx bkref_str) + internal_function; +static Idx find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes, + Idx subexp_idx, int type) internal_function; +static reg_errcode_t check_arrival (re_match_context_t *mctx, + state_array_t *path, Idx top_node, + Idx top_str, Idx last_node, Idx last_str, + int type) internal_function; +static reg_errcode_t check_arrival_add_next_nodes (re_match_context_t *mctx, + Idx str_idx, + re_node_set *cur_nodes, + re_node_set *next_nodes) + internal_function; +static reg_errcode_t check_arrival_expand_ecl (const re_dfa_t *dfa, + re_node_set *cur_nodes, + Idx ex_subexp, int type) + internal_function; +static reg_errcode_t check_arrival_expand_ecl_sub (const re_dfa_t *dfa, + re_node_set *dst_nodes, + Idx target, Idx ex_subexp, + int type) internal_function; +static reg_errcode_t expand_bkref_cache (re_match_context_t *mctx, + re_node_set *cur_nodes, Idx cur_str, + Idx subexp_num, int type) + internal_function; +static bool build_trtable (const re_dfa_t *dfa, + re_dfastate_t *state) internal_function; +#ifdef RE_ENABLE_I18N +static int check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx, + const re_string_t *input, Idx idx) + internal_function; +# ifdef _LIBC +static unsigned int find_collation_sequence_value (const unsigned char *mbs, + size_t name_len) + internal_function; +# endif /* _LIBC */ +#endif /* RE_ENABLE_I18N */ +static Idx group_nodes_into_DFAstates (const re_dfa_t *dfa, + const re_dfastate_t *state, + re_node_set *states_node, + bitset_t *states_ch) internal_function; +static bool check_node_accept (const re_match_context_t *mctx, + const re_token_t *node, Idx idx) + internal_function; +static reg_errcode_t extend_buffers (re_match_context_t *mctx) + internal_function; + +/* Entry point for POSIX code. */ + +/* regexec searches for a given pattern, specified by PREG, in the + string STRING. + + If NMATCH is zero or REG_NOSUB was set in the cflags argument to + `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at + least NMATCH elements, and we set them to the offsets of the + corresponding matched substrings. + + EFLAGS specifies `execution flags' which affect matching: if + REG_NOTBOL is set, then ^ does not match at the beginning of the + string; if REG_NOTEOL is set, then $ does not match at the end. + + We return 0 if we find a match and REG_NOMATCH if not. */ + +int +regexec (preg, string, nmatch, pmatch, eflags) + const regex_t *_Restrict_ preg; + const char *_Restrict_ string; + size_t nmatch; + regmatch_t pmatch[_Restrict_arr_]; + int eflags; +{ + reg_errcode_t err; + Idx start, length; +#ifdef _LIBC + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; +#endif + + if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND)) + return REG_BADPAT; + + if (eflags & REG_STARTEND) + { + start = pmatch[0].rm_so; + length = pmatch[0].rm_eo; + } + else + { + start = 0; + length = strlen (string); + } + + __libc_lock_lock (dfa->lock); + if (preg->no_sub) + err = re_search_internal (preg, string, length, start, length, + length, 0, NULL, eflags); + else + err = re_search_internal (preg, string, length, start, length, + length, nmatch, pmatch, eflags); + __libc_lock_unlock (dfa->lock); + return err != REG_NOERROR; +} + +#ifdef _LIBC +# include +versioned_symbol (libc, __regexec, regexec, GLIBC_2_3_4); + +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4) +__typeof__ (__regexec) __compat_regexec; + +int +attribute_compat_text_section +__compat_regexec (const regex_t *_Restrict_ preg, + const char *_Restrict_ string, size_t nmatch, + regmatch_t pmatch[], int eflags) +{ + return regexec (preg, string, nmatch, pmatch, + eflags & (REG_NOTBOL | REG_NOTEOL)); +} +compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0); +# endif +#endif + +/* Entry points for GNU code. */ + +/* re_match, re_search, re_match_2, re_search_2 + + The former two functions operate on STRING with length LENGTH, + while the later two operate on concatenation of STRING1 and STRING2 + with lengths LENGTH1 and LENGTH2, respectively. + + re_match() matches the compiled pattern in BUFP against the string, + starting at index START. + + re_search() first tries matching at index START, then it tries to match + starting from index START + 1, and so on. The last start position tried + is START + RANGE. (Thus RANGE = 0 forces re_search to operate the same + way as re_match().) + + The parameter STOP of re_{match,search}_2 specifies that no match exceeding + the first STOP characters of the concatenation of the strings should be + concerned. + + If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match + and all groups is stored in REGS. (For the "_2" variants, the offsets are + computed relative to the concatenation, not relative to the individual + strings.) + + On success, re_match* functions return the length of the match, re_search* + return the position of the start of the match. Return value -1 means no + match was found and -2 indicates an internal error. */ + +regoff_t +re_match (bufp, string, length, start, regs) + struct re_pattern_buffer *bufp; + const char *string; + Idx length, start; + struct re_registers *regs; +{ + return re_search_stub (bufp, string, length, start, 0, length, regs, true); +} +#ifdef _LIBC +weak_alias (__re_match, re_match) +#endif + +regoff_t +re_search (bufp, string, length, start, range, regs) + struct re_pattern_buffer *bufp; + const char *string; + Idx length, start; + regoff_t range; + struct re_registers *regs; +{ + return re_search_stub (bufp, string, length, start, range, length, regs, + false); +} +#ifdef _LIBC +weak_alias (__re_search, re_search) +#endif + +regoff_t +re_match_2 (bufp, string1, length1, string2, length2, start, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + Idx length1, length2, start, stop; + struct re_registers *regs; +{ + return re_search_2_stub (bufp, string1, length1, string2, length2, + start, 0, regs, stop, true); +} +#ifdef _LIBC +weak_alias (__re_match_2, re_match_2) +#endif + +regoff_t +re_search_2 (bufp, string1, length1, string2, length2, start, range, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + Idx length1, length2, start, stop; + regoff_t range; + struct re_registers *regs; +{ + return re_search_2_stub (bufp, string1, length1, string2, length2, + start, range, regs, stop, false); +} +#ifdef _LIBC +weak_alias (__re_search_2, re_search_2) +#endif + +static regoff_t +internal_function +re_search_2_stub (struct re_pattern_buffer *bufp, + const char *string1, Idx length1, + const char *string2, Idx length2, + Idx start, regoff_t range, struct re_registers *regs, + Idx stop, bool ret_len) +{ + const char *str; + regoff_t rval; + Idx len = length1 + length2; + char *s = NULL; + + if (BE (length1 < 0 || length2 < 0 || stop < 0 || len < length1, 0)) + return -2; + + /* Concatenate the strings. */ + if (length2 > 0) + if (length1 > 0) + { + s = re_malloc (char, len); + + if (BE (s == NULL, 0)) + return -2; +#ifdef _LIBC + memcpy (__mempcpy (s, string1, length1), string2, length2); +#else + memcpy (s, string1, length1); + memcpy (s + length1, string2, length2); +#endif + str = s; + } + else + str = string2; + else + str = string1; + + rval = re_search_stub (bufp, str, len, start, range, stop, regs, + ret_len); + re_free (s); + return rval; +} + +/* The parameters have the same meaning as those of re_search. + Additional parameters: + If RET_LEN is true the length of the match is returned (re_match style); + otherwise the position of the match is returned. */ + +static regoff_t +internal_function +re_search_stub (struct re_pattern_buffer *bufp, + const char *string, Idx length, + Idx start, regoff_t range, Idx stop, struct re_registers *regs, + bool ret_len) +{ + reg_errcode_t result; + regmatch_t *pmatch; + Idx nregs; + regoff_t rval; + int eflags = 0; +#ifdef _LIBC + re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; +#endif + Idx last_start = start + range; + + /* Check for out-of-range. */ + if (BE (start < 0 || start > length, 0)) + return -1; + if (BE (length < last_start || (0 <= range && last_start < start), 0)) + last_start = length; + else if (BE (last_start < 0 || (range < 0 && start <= last_start), 0)) + last_start = 0; + + __libc_lock_lock (dfa->lock); + + eflags |= (bufp->not_bol) ? REG_NOTBOL : 0; + eflags |= (bufp->not_eol) ? REG_NOTEOL : 0; + + /* Compile fastmap if we haven't yet. */ + if (start < last_start && bufp->fastmap != NULL && !bufp->fastmap_accurate) + re_compile_fastmap (bufp); + + if (BE (bufp->no_sub, 0)) + regs = NULL; + + /* We need at least 1 register. */ + if (regs == NULL) + nregs = 1; + else if (BE (bufp->regs_allocated == REGS_FIXED + && regs->num_regs <= bufp->re_nsub, 0)) + { + nregs = regs->num_regs; + if (BE (nregs < 1, 0)) + { + /* Nothing can be copied to regs. */ + regs = NULL; + nregs = 1; + } + } + else + nregs = bufp->re_nsub + 1; + pmatch = re_malloc (regmatch_t, nregs); + if (BE (pmatch == NULL, 0)) + { + rval = -2; + goto out; + } + + result = re_search_internal (bufp, string, length, start, last_start, stop, + nregs, pmatch, eflags); + + rval = 0; + + /* I hope we needn't fill ther regs with -1's when no match was found. */ + if (result != REG_NOERROR) + rval = -1; + else if (regs != NULL) + { + /* If caller wants register contents data back, copy them. */ + bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs, + bufp->regs_allocated); + if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0)) + rval = -2; + } + + if (BE (rval == 0, 1)) + { + if (ret_len) + { + assert (pmatch[0].rm_so == start); + rval = pmatch[0].rm_eo - start; + } + else + rval = pmatch[0].rm_so; + } + re_free (pmatch); + out: + __libc_lock_unlock (dfa->lock); + return rval; +} + +static unsigned int +internal_function +re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, Idx nregs, + int regs_allocated) +{ + int rval = REGS_REALLOCATE; + Idx i; + Idx need_regs = nregs + 1; + /* We need one extra element beyond `num_regs' for the `-1' marker GNU code + uses. */ + + /* Have the register data arrays been allocated? */ + if (regs_allocated == REGS_UNALLOCATED) + { /* No. So allocate them with malloc. */ + regs->start = re_malloc (regoff_t, need_regs); + if (BE (regs->start == NULL, 0)) + return REGS_UNALLOCATED; + regs->end = re_malloc (regoff_t, need_regs); + if (BE (regs->end == NULL, 0)) + { + re_free (regs->start); + return REGS_UNALLOCATED; + } + regs->num_regs = need_regs; + } + else if (regs_allocated == REGS_REALLOCATE) + { /* Yes. If we need more elements than were already + allocated, reallocate them. If we need fewer, just + leave it alone. */ + if (BE (need_regs > regs->num_regs, 0)) + { + regoff_t *new_start = re_realloc (regs->start, regoff_t, need_regs); + regoff_t *new_end; + if (BE (new_start == NULL, 0)) + return REGS_UNALLOCATED; + new_end = re_realloc (regs->end, regoff_t, need_regs); + if (BE (new_end == NULL, 0)) + { + re_free (new_start); + return REGS_UNALLOCATED; + } + regs->start = new_start; + regs->end = new_end; + regs->num_regs = need_regs; + } + } + else + { + assert (regs_allocated == REGS_FIXED); + /* This function may not be called with REGS_FIXED and nregs too big. */ + assert (regs->num_regs >= nregs); + rval = REGS_FIXED; + } + + /* Copy the regs. */ + for (i = 0; i < nregs; ++i) + { + regs->start[i] = pmatch[i].rm_so; + regs->end[i] = pmatch[i].rm_eo; + } + for ( ; i < regs->num_regs; ++i) + regs->start[i] = regs->end[i] = -1; + + return rval; +} + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use + this memory for recording register information. STARTS and ENDS + must be allocated using the malloc library routine, and must each + be at least NUM_REGS * sizeof (regoff_t) bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ + +void +re_set_registers (bufp, regs, num_regs, starts, ends) + struct re_pattern_buffer *bufp; + struct re_registers *regs; + __re_size_t num_regs; + regoff_t *starts, *ends; +{ + if (num_regs) + { + bufp->regs_allocated = REGS_REALLOCATE; + regs->num_regs = num_regs; + regs->start = starts; + regs->end = ends; + } + else + { + bufp->regs_allocated = REGS_UNALLOCATED; + regs->num_regs = 0; + regs->start = regs->end = NULL; + } +} +#ifdef _LIBC +weak_alias (__re_set_registers, re_set_registers) +#endif + +/* Entry points compatible with 4.2 BSD regex library. We don't define + them unless specifically requested. */ + +#if defined _REGEX_RE_COMP || defined _LIBC +int +# ifdef _LIBC +weak_function +# endif +re_exec (s) + const char *s; +{ + return 0 == regexec (&re_comp_buf, s, 0, NULL, 0); +} +#endif /* _REGEX_RE_COMP */ + +/* Internal entry point. */ + +/* Searches for a compiled pattern PREG in the string STRING, whose + length is LENGTH. NMATCH, PMATCH, and EFLAGS have the same + meaning as with regexec. LAST_START is START + RANGE, where + START and RANGE have the same meaning as with re_search. + Return REG_NOERROR if we find a match, and REG_NOMATCH if not, + otherwise return the error code. + Note: We assume front end functions already check ranges. + (0 <= LAST_START && LAST_START <= LENGTH) */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +re_search_internal (const regex_t *preg, + const char *string, Idx length, + Idx start, Idx last_start, Idx stop, + size_t nmatch, regmatch_t pmatch[], + int eflags) +{ + reg_errcode_t err; + const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer; + Idx left_lim, right_lim; + int incr; + bool fl_longest_match; + int match_kind; + Idx match_first; + Idx match_last = REG_MISSING; + Idx extra_nmatch; + bool sb; + int ch; +#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) + re_match_context_t mctx = { .dfa = dfa }; +#else + re_match_context_t mctx; +#endif + char *fastmap = ((preg->fastmap != NULL && preg->fastmap_accurate + && start != last_start && !preg->can_be_null) + ? preg->fastmap : NULL); + RE_TRANSLATE_TYPE t = preg->translate; + +#if !(defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)) + memset (&mctx, '\0', sizeof (re_match_context_t)); + mctx.dfa = dfa; +#endif + + extra_nmatch = (nmatch > preg->re_nsub) ? nmatch - (preg->re_nsub + 1) : 0; + nmatch -= extra_nmatch; + + /* Check if the DFA haven't been compiled. */ + if (BE (preg->used == 0 || dfa->init_state == NULL + || dfa->init_state_word == NULL || dfa->init_state_nl == NULL + || dfa->init_state_begbuf == NULL, 0)) + return REG_NOMATCH; + +#ifdef DEBUG + /* We assume front-end functions already check them. */ + assert (0 <= last_start && last_start <= length); +#endif + + /* If initial states with non-begbuf contexts have no elements, + the regex must be anchored. If preg->newline_anchor is set, + we'll never use init_state_nl, so do not check it. */ + if (dfa->init_state->nodes.nelem == 0 + && dfa->init_state_word->nodes.nelem == 0 + && (dfa->init_state_nl->nodes.nelem == 0 + || !preg->newline_anchor)) + { + if (start != 0 && last_start != 0) + return REG_NOMATCH; + start = last_start = 0; + } + + /* We must check the longest matching, if nmatch > 0. */ + fl_longest_match = (nmatch != 0 || dfa->nbackref); + + err = re_string_allocate (&mctx.input, string, length, dfa->nodes_len + 1, + preg->translate, (preg->syntax & RE_ICASE) != 0, + dfa); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + mctx.input.stop = stop; + mctx.input.raw_stop = stop; + mctx.input.newline_anchor = preg->newline_anchor; + + err = match_ctx_init (&mctx, eflags, dfa->nbackref * 2); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* We will log all the DFA states through which the dfa pass, + if nmatch > 1, or this dfa has "multibyte node", which is a + back-reference or a node which can accept multibyte character or + multi character collating element. */ + if (nmatch > 1 || dfa->has_mb_node) + { + /* Avoid overflow. */ + if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= mctx.input.bufs_len, 0)) + { + err = REG_ESPACE; + goto free_return; + } + + mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1); + if (BE (mctx.state_log == NULL, 0)) + { + err = REG_ESPACE; + goto free_return; + } + } + else + mctx.state_log = NULL; + + match_first = start; + mctx.input.tip_context = (eflags & REG_NOTBOL) ? CONTEXT_BEGBUF + : CONTEXT_NEWLINE | CONTEXT_BEGBUF; + + /* Check incrementally whether of not the input string match. */ + incr = (last_start < start) ? -1 : 1; + left_lim = (last_start < start) ? last_start : start; + right_lim = (last_start < start) ? start : last_start; + sb = dfa->mb_cur_max == 1; + match_kind = + (fastmap + ? ((sb || !(preg->syntax & RE_ICASE || t) ? 4 : 0) + | (start <= last_start ? 2 : 0) + | (t != NULL ? 1 : 0)) + : 8); + + for (;; match_first += incr) + { + err = REG_NOMATCH; + if (match_first < left_lim || right_lim < match_first) + goto free_return; + + /* Advance as rapidly as possible through the string, until we + find a plausible place to start matching. This may be done + with varying efficiency, so there are various possibilities: + only the most common of them are specialized, in order to + save on code size. We use a switch statement for speed. */ + switch (match_kind) + { + case 8: + /* No fastmap. */ + break; + + case 7: + /* Fastmap with single-byte translation, match forward. */ + while (BE (match_first < right_lim, 1) + && !fastmap[t[(unsigned char) string[match_first]]]) + ++match_first; + goto forward_match_found_start_or_reached_end; + + case 6: + /* Fastmap without translation, match forward. */ + while (BE (match_first < right_lim, 1) + && !fastmap[(unsigned char) string[match_first]]) + ++match_first; + + forward_match_found_start_or_reached_end: + if (BE (match_first == right_lim, 0)) + { + ch = match_first >= length + ? 0 : (unsigned char) string[match_first]; + if (!fastmap[t ? t[ch] : ch]) + goto free_return; + } + break; + + case 4: + case 5: + /* Fastmap without multi-byte translation, match backwards. */ + while (match_first >= left_lim) + { + ch = match_first >= length + ? 0 : (unsigned char) string[match_first]; + if (fastmap[t ? t[ch] : ch]) + break; + --match_first; + } + if (match_first < left_lim) + goto free_return; + break; + + default: + /* In this case, we can't determine easily the current byte, + since it might be a component byte of a multibyte + character. Then we use the constructed buffer instead. */ + for (;;) + { + /* If MATCH_FIRST is out of the valid range, reconstruct the + buffers. */ + __re_size_t offset = match_first - mctx.input.raw_mbs_idx; + if (BE (offset >= (__re_size_t) mctx.input.valid_raw_len, 0)) + { + err = re_string_reconstruct (&mctx.input, match_first, + eflags); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + offset = match_first - mctx.input.raw_mbs_idx; + } + /* If MATCH_FIRST is out of the buffer, leave it as '\0'. + Note that MATCH_FIRST must not be smaller than 0. */ + ch = (match_first >= length + ? 0 : re_string_byte_at (&mctx.input, offset)); + if (fastmap[ch]) + break; + match_first += incr; + if (match_first < left_lim || match_first > right_lim) + { + err = REG_NOMATCH; + goto free_return; + } + } + break; + } + + /* Reconstruct the buffers so that the matcher can assume that + the matching starts from the beginning of the buffer. */ + err = re_string_reconstruct (&mctx.input, match_first, eflags); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + +#ifdef RE_ENABLE_I18N + /* Don't consider this char as a possible match start if it part, + yet isn't the head, of a multibyte character. */ + if (!sb && !re_string_first_byte (&mctx.input, 0)) + continue; +#endif + + /* It seems to be appropriate one, then use the matcher. */ + /* We assume that the matching starts from 0. */ + mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0; + match_last = check_matching (&mctx, fl_longest_match, + start <= last_start ? &match_first : NULL); + if (match_last != REG_MISSING) + { + if (BE (match_last == REG_ERROR, 0)) + { + err = REG_ESPACE; + goto free_return; + } + else + { + mctx.match_last = match_last; + if ((!preg->no_sub && nmatch > 1) || dfa->nbackref) + { + re_dfastate_t *pstate = mctx.state_log[match_last]; + mctx.last_node = check_halt_state_context (&mctx, pstate, + match_last); + } + if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match) + || dfa->nbackref) + { + err = prune_impossible_nodes (&mctx); + if (err == REG_NOERROR) + break; + if (BE (err != REG_NOMATCH, 0)) + goto free_return; + match_last = REG_MISSING; + } + else + break; /* We found a match. */ + } + } + + match_ctx_clean (&mctx); + } + +#ifdef DEBUG + assert (match_last != REG_MISSING); + assert (err == REG_NOERROR); +#endif + + /* Set pmatch[] if we need. */ + if (nmatch > 0) + { + Idx reg_idx; + + /* Initialize registers. */ + for (reg_idx = 1; reg_idx < nmatch; ++reg_idx) + pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1; + + /* Set the points where matching start/end. */ + pmatch[0].rm_so = 0; + pmatch[0].rm_eo = mctx.match_last; + /* FIXME: This function should fail if mctx.match_last exceeds + the maximum possible regoff_t value. We need a new error + code REG_OVERFLOW. */ + + if (!preg->no_sub && nmatch > 1) + { + err = set_regs (preg, &mctx, nmatch, pmatch, + dfa->has_plural_match && dfa->nbackref > 0); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + + /* At last, add the offset to the each registers, since we slided + the buffers so that we could assume that the matching starts + from 0. */ + for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) + if (pmatch[reg_idx].rm_so != -1) + { +#ifdef RE_ENABLE_I18N + if (BE (mctx.input.offsets_needed != 0, 0)) + { + pmatch[reg_idx].rm_so = + (pmatch[reg_idx].rm_so == mctx.input.valid_len + ? mctx.input.valid_raw_len + : mctx.input.offsets[pmatch[reg_idx].rm_so]); + pmatch[reg_idx].rm_eo = + (pmatch[reg_idx].rm_eo == mctx.input.valid_len + ? mctx.input.valid_raw_len + : mctx.input.offsets[pmatch[reg_idx].rm_eo]); + } +#else + assert (mctx.input.offsets_needed == 0); +#endif + pmatch[reg_idx].rm_so += match_first; + pmatch[reg_idx].rm_eo += match_first; + } + for (reg_idx = 0; reg_idx < extra_nmatch; ++reg_idx) + { + pmatch[nmatch + reg_idx].rm_so = -1; + pmatch[nmatch + reg_idx].rm_eo = -1; + } + + if (dfa->subexp_map) + for (reg_idx = 0; reg_idx + 1 < nmatch; reg_idx++) + if (dfa->subexp_map[reg_idx] != reg_idx) + { + pmatch[reg_idx + 1].rm_so + = pmatch[dfa->subexp_map[reg_idx] + 1].rm_so; + pmatch[reg_idx + 1].rm_eo + = pmatch[dfa->subexp_map[reg_idx] + 1].rm_eo; + } + } + + free_return: + re_free (mctx.state_log); + if (dfa->nbackref) + match_ctx_free (&mctx); + re_string_destruct (&mctx.input); + return err; +} + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +prune_impossible_nodes (re_match_context_t *mctx) +{ + const re_dfa_t *const dfa = mctx->dfa; + Idx halt_node, match_last; + reg_errcode_t ret; + re_dfastate_t **sifted_states; + re_dfastate_t **lim_states = NULL; + re_sift_context_t sctx; +#ifdef DEBUG + assert (mctx->state_log != NULL); +#endif + match_last = mctx->match_last; + halt_node = mctx->last_node; + + /* Avoid overflow. */ + if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= match_last, 0)) + return REG_ESPACE; + + sifted_states = re_malloc (re_dfastate_t *, match_last + 1); + if (BE (sifted_states == NULL, 0)) + { + ret = REG_ESPACE; + goto free_return; + } + if (dfa->nbackref) + { + lim_states = re_malloc (re_dfastate_t *, match_last + 1); + if (BE (lim_states == NULL, 0)) + { + ret = REG_ESPACE; + goto free_return; + } + while (1) + { + memset (lim_states, '\0', + sizeof (re_dfastate_t *) * (match_last + 1)); + sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, + match_last); + ret = sift_states_backward (mctx, &sctx); + re_node_set_free (&sctx.limits); + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + if (sifted_states[0] != NULL || lim_states[0] != NULL) + break; + do + { + --match_last; + if (! REG_VALID_INDEX (match_last)) + { + ret = REG_NOMATCH; + goto free_return; + } + } while (mctx->state_log[match_last] == NULL + || !mctx->state_log[match_last]->halt); + halt_node = check_halt_state_context (mctx, + mctx->state_log[match_last], + match_last); + } + ret = merge_state_array (dfa, sifted_states, lim_states, + match_last + 1); + re_free (lim_states); + lim_states = NULL; + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + } + else + { + sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last); + ret = sift_states_backward (mctx, &sctx); + re_node_set_free (&sctx.limits); + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + if (sifted_states[0] == NULL) + { + ret = REG_NOMATCH; + goto free_return; + } + } + re_free (mctx->state_log); + mctx->state_log = sifted_states; + sifted_states = NULL; + mctx->last_node = halt_node; + mctx->match_last = match_last; + ret = REG_NOERROR; + free_return: + re_free (sifted_states); + re_free (lim_states); + return ret; +} + +/* Acquire an initial state and return it. + We must select appropriate initial state depending on the context, + since initial states may have constraints like "\<", "^", etc.. */ + +static inline re_dfastate_t * +__attribute ((always_inline)) internal_function +acquire_init_state_context (reg_errcode_t *err, const re_match_context_t *mctx, + Idx idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + if (dfa->init_state->has_constraint) + { + unsigned int context; + context = re_string_context_at (&mctx->input, idx - 1, mctx->eflags); + if (IS_WORD_CONTEXT (context)) + return dfa->init_state_word; + else if (IS_ORDINARY_CONTEXT (context)) + return dfa->init_state; + else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context)) + return dfa->init_state_begbuf; + else if (IS_NEWLINE_CONTEXT (context)) + return dfa->init_state_nl; + else if (IS_BEGBUF_CONTEXT (context)) + { + /* It is relatively rare case, then calculate on demand. */ + return re_acquire_state_context (err, dfa, + dfa->init_state->entrance_nodes, + context); + } + else + /* Must not happen? */ + return dfa->init_state; + } + else + return dfa->init_state; +} + +/* Check whether the regular expression match input string INPUT or not, + and return the index where the matching end. Return REG_MISSING if + there is no match, and return REG_ERROR in case of an error. + FL_LONGEST_MATCH means we want the POSIX longest matching. + If P_MATCH_FIRST is not NULL, and the match fails, it is set to the + next place where we may want to try matching. + Note that the matcher assume that the maching starts from the current + index of the buffer. */ + +static Idx +internal_function __attribute_warn_unused_result__ +check_matching (re_match_context_t *mctx, bool fl_longest_match, + Idx *p_match_first) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + Idx match = 0; + Idx match_last = REG_MISSING; + Idx cur_str_idx = re_string_cur_idx (&mctx->input); + re_dfastate_t *cur_state; + bool at_init_state = p_match_first != NULL; + Idx next_start_idx = cur_str_idx; + + err = REG_NOERROR; + cur_state = acquire_init_state_context (&err, mctx, cur_str_idx); + /* An initial state must not be NULL (invalid). */ + if (BE (cur_state == NULL, 0)) + { + assert (err == REG_ESPACE); + return REG_ERROR; + } + + if (mctx->state_log != NULL) + { + mctx->state_log[cur_str_idx] = cur_state; + + /* Check OP_OPEN_SUBEXP in the initial state in case that we use them + later. E.g. Processing back references. */ + if (BE (dfa->nbackref, 0)) + { + at_init_state = false; + err = check_subexp_matching_top (mctx, &cur_state->nodes, 0); + if (BE (err != REG_NOERROR, 0)) + return err; + + if (cur_state->has_backref) + { + err = transit_state_bkref (mctx, &cur_state->nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + } + + /* If the RE accepts NULL string. */ + if (BE (cur_state->halt, 0)) + { + if (!cur_state->has_constraint + || check_halt_state_context (mctx, cur_state, cur_str_idx)) + { + if (!fl_longest_match) + return cur_str_idx; + else + { + match_last = cur_str_idx; + match = 1; + } + } + } + + while (!re_string_eoi (&mctx->input)) + { + re_dfastate_t *old_state = cur_state; + Idx next_char_idx = re_string_cur_idx (&mctx->input) + 1; + + if (BE (next_char_idx >= mctx->input.bufs_len, 0) + || (BE (next_char_idx >= mctx->input.valid_len, 0) + && mctx->input.valid_len < mctx->input.len)) + { + err = extend_buffers (mctx); + if (BE (err != REG_NOERROR, 0)) + { + assert (err == REG_ESPACE); + return REG_ERROR; + } + } + + cur_state = transit_state (&err, mctx, cur_state); + if (mctx->state_log != NULL) + cur_state = merge_state_with_log (&err, mctx, cur_state); + + if (cur_state == NULL) + { + /* Reached the invalid state or an error. Try to recover a valid + state using the state log, if available and if we have not + already found a valid (even if not the longest) match. */ + if (BE (err != REG_NOERROR, 0)) + return REG_ERROR; + + if (mctx->state_log == NULL + || (match && !fl_longest_match) + || (cur_state = find_recover_state (&err, mctx)) == NULL) + break; + } + + if (BE (at_init_state, 0)) + { + if (old_state == cur_state) + next_start_idx = next_char_idx; + else + at_init_state = false; + } + + if (cur_state->halt) + { + /* Reached a halt state. + Check the halt state can satisfy the current context. */ + if (!cur_state->has_constraint + || check_halt_state_context (mctx, cur_state, + re_string_cur_idx (&mctx->input))) + { + /* We found an appropriate halt state. */ + match_last = re_string_cur_idx (&mctx->input); + match = 1; + + /* We found a match, do not modify match_first below. */ + p_match_first = NULL; + if (!fl_longest_match) + break; + } + } + } + + if (p_match_first) + *p_match_first += next_start_idx; + + return match_last; +} + +/* Check NODE match the current context. */ + +static bool +internal_function +check_halt_node_context (const re_dfa_t *dfa, Idx node, unsigned int context) +{ + re_token_type_t type = dfa->nodes[node].type; + unsigned int constraint = dfa->nodes[node].constraint; + if (type != END_OF_RE) + return false; + if (!constraint) + return true; + if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context)) + return false; + return true; +} + +/* Check the halt state STATE match the current context. + Return 0 if not match, if the node, STATE has, is a halt node and + match the context, return the node. */ + +static Idx +internal_function +check_halt_state_context (const re_match_context_t *mctx, + const re_dfastate_t *state, Idx idx) +{ + Idx i; + unsigned int context; +#ifdef DEBUG + assert (state->halt); +#endif + context = re_string_context_at (&mctx->input, idx, mctx->eflags); + for (i = 0; i < state->nodes.nelem; ++i) + if (check_halt_node_context (mctx->dfa, state->nodes.elems[i], context)) + return state->nodes.elems[i]; + return 0; +} + +/* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA + corresponding to the DFA). + Return the destination node, and update EPS_VIA_NODES; + return REG_MISSING in case of errors. */ + +static Idx +internal_function +proceed_next_node (const re_match_context_t *mctx, Idx nregs, regmatch_t *regs, + Idx *pidx, Idx node, re_node_set *eps_via_nodes, + struct re_fail_stack_t *fs) +{ + const re_dfa_t *const dfa = mctx->dfa; + Idx i; + bool ok; + if (IS_EPSILON_NODE (dfa->nodes[node].type)) + { + re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes; + re_node_set *edests = &dfa->edests[node]; + Idx dest_node; + ok = re_node_set_insert (eps_via_nodes, node); + if (BE (! ok, 0)) + return REG_ERROR; + /* Pick up a valid destination, or return REG_MISSING if none + is found. */ + for (dest_node = REG_MISSING, i = 0; i < edests->nelem; ++i) + { + Idx candidate = edests->elems[i]; + if (!re_node_set_contains (cur_nodes, candidate)) + continue; + if (dest_node == REG_MISSING) + dest_node = candidate; + + else + { + /* In order to avoid infinite loop like "(a*)*", return the second + epsilon-transition if the first was already considered. */ + if (re_node_set_contains (eps_via_nodes, dest_node)) + return candidate; + + /* Otherwise, push the second epsilon-transition on the fail stack. */ + else if (fs != NULL + && push_fail_stack (fs, *pidx, candidate, nregs, regs, + eps_via_nodes)) + return REG_ERROR; + + /* We know we are going to exit. */ + break; + } + } + return dest_node; + } + else + { + Idx naccepted = 0; + re_token_type_t type = dfa->nodes[node].type; + +#ifdef RE_ENABLE_I18N + if (dfa->nodes[node].accept_mb) + naccepted = check_node_accept_bytes (dfa, node, &mctx->input, *pidx); + else +#endif /* RE_ENABLE_I18N */ + if (type == OP_BACK_REF) + { + Idx subexp_idx = dfa->nodes[node].opr.idx + 1; + naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so; + if (fs != NULL) + { + if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1) + return REG_MISSING; + else if (naccepted) + { + char *buf = (char *) re_string_get_buffer (&mctx->input); + if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx, + naccepted) != 0) + return REG_MISSING; + } + } + + if (naccepted == 0) + { + Idx dest_node; + ok = re_node_set_insert (eps_via_nodes, node); + if (BE (! ok, 0)) + return REG_ERROR; + dest_node = dfa->edests[node].elems[0]; + if (re_node_set_contains (&mctx->state_log[*pidx]->nodes, + dest_node)) + return dest_node; + } + } + + if (naccepted != 0 + || check_node_accept (mctx, dfa->nodes + node, *pidx)) + { + Idx dest_node = dfa->nexts[node]; + *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted; + if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL + || !re_node_set_contains (&mctx->state_log[*pidx]->nodes, + dest_node))) + return REG_MISSING; + re_node_set_empty (eps_via_nodes); + return dest_node; + } + } + return REG_MISSING; +} + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +push_fail_stack (struct re_fail_stack_t *fs, Idx str_idx, Idx dest_node, + Idx nregs, regmatch_t *regs, re_node_set *eps_via_nodes) +{ + reg_errcode_t err; + Idx num = fs->num++; + if (fs->num == fs->alloc) + { + struct re_fail_stack_ent_t *new_array; + new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t) + * fs->alloc * 2)); + if (new_array == NULL) + return REG_ESPACE; + fs->alloc *= 2; + fs->stack = new_array; + } + fs->stack[num].idx = str_idx; + fs->stack[num].node = dest_node; + fs->stack[num].regs = re_malloc (regmatch_t, nregs); + if (fs->stack[num].regs == NULL) + return REG_ESPACE; + memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs); + err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes); + return err; +} + +static Idx +internal_function +pop_fail_stack (struct re_fail_stack_t *fs, Idx *pidx, Idx nregs, + regmatch_t *regs, re_node_set *eps_via_nodes) +{ + Idx num = --fs->num; + assert (REG_VALID_INDEX (num)); + *pidx = fs->stack[num].idx; + memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs); + re_node_set_free (eps_via_nodes); + re_free (fs->stack[num].regs); + *eps_via_nodes = fs->stack[num].eps_via_nodes; + return fs->stack[num].node; +} + +/* Set the positions where the subexpressions are starts/ends to registers + PMATCH. + Note: We assume that pmatch[0] is already set, and + pmatch[i].rm_so == pmatch[i].rm_eo == -1 for 0 < i < nmatch. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch, + regmatch_t *pmatch, bool fl_backtrack) +{ + const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer; + Idx idx, cur_node; + re_node_set eps_via_nodes; + struct re_fail_stack_t *fs; + struct re_fail_stack_t fs_body = { 0, 2, NULL }; + regmatch_t *prev_idx_match; + bool prev_idx_match_malloced = false; + +#ifdef DEBUG + assert (nmatch > 1); + assert (mctx->state_log != NULL); +#endif + if (fl_backtrack) + { + fs = &fs_body; + fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc); + if (fs->stack == NULL) + return REG_ESPACE; + } + else + fs = NULL; + + cur_node = dfa->init_node; + re_node_set_init_empty (&eps_via_nodes); + + if (__libc_use_alloca (nmatch * sizeof (regmatch_t))) + prev_idx_match = (regmatch_t *) alloca (nmatch * sizeof (regmatch_t)); + else + { + prev_idx_match = re_malloc (regmatch_t, nmatch); + if (prev_idx_match == NULL) + { + free_fail_stack_return (fs); + return REG_ESPACE; + } + prev_idx_match_malloced = true; + } + memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch); + + for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;) + { + update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch); + + if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node) + { + Idx reg_idx; + if (fs) + { + for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) + if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1) + break; + if (reg_idx == nmatch) + { + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + return free_fail_stack_return (fs); + } + cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch, + &eps_via_nodes); + } + else + { + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + return REG_NOERROR; + } + } + + /* Proceed to next node. */ + cur_node = proceed_next_node (mctx, nmatch, pmatch, &idx, cur_node, + &eps_via_nodes, fs); + + if (BE (! REG_VALID_INDEX (cur_node), 0)) + { + if (BE (cur_node == REG_ERROR, 0)) + { + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + free_fail_stack_return (fs); + return REG_ESPACE; + } + if (fs) + cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch, + &eps_via_nodes); + else + { + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + return REG_NOMATCH; + } + } + } + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + return free_fail_stack_return (fs); +} + +static reg_errcode_t +internal_function +free_fail_stack_return (struct re_fail_stack_t *fs) +{ + if (fs) + { + Idx fs_idx; + for (fs_idx = 0; fs_idx < fs->num; ++fs_idx) + { + re_node_set_free (&fs->stack[fs_idx].eps_via_nodes); + re_free (fs->stack[fs_idx].regs); + } + re_free (fs->stack); + } + return REG_NOERROR; +} + +static void +internal_function +update_regs (const re_dfa_t *dfa, regmatch_t *pmatch, + regmatch_t *prev_idx_match, Idx cur_node, Idx cur_idx, Idx nmatch) +{ + int type = dfa->nodes[cur_node].type; + if (type == OP_OPEN_SUBEXP) + { + Idx reg_num = dfa->nodes[cur_node].opr.idx + 1; + + /* We are at the first node of this sub expression. */ + if (reg_num < nmatch) + { + pmatch[reg_num].rm_so = cur_idx; + pmatch[reg_num].rm_eo = -1; + } + } + else if (type == OP_CLOSE_SUBEXP) + { + Idx reg_num = dfa->nodes[cur_node].opr.idx + 1; + if (reg_num < nmatch) + { + /* We are at the last node of this sub expression. */ + if (pmatch[reg_num].rm_so < cur_idx) + { + pmatch[reg_num].rm_eo = cur_idx; + /* This is a non-empty match or we are not inside an optional + subexpression. Accept this right away. */ + memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch); + } + else + { + if (dfa->nodes[cur_node].opt_subexp + && prev_idx_match[reg_num].rm_so != -1) + /* We transited through an empty match for an optional + subexpression, like (a?)*, and this is not the subexp's + first match. Copy back the old content of the registers + so that matches of an inner subexpression are undone as + well, like in ((a?))*. */ + memcpy (pmatch, prev_idx_match, sizeof (regmatch_t) * nmatch); + else + /* We completed a subexpression, but it may be part of + an optional one, so do not update PREV_IDX_MATCH. */ + pmatch[reg_num].rm_eo = cur_idx; + } + } + } +} + +/* This function checks the STATE_LOG from the SCTX->last_str_idx to 0 + and sift the nodes in each states according to the following rules. + Updated state_log will be wrote to STATE_LOG. + + Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if... + 1. When STR_IDX == MATCH_LAST(the last index in the state_log): + If `a' isn't the LAST_NODE and `a' can't epsilon transit to + the LAST_NODE, we throw away the node `a'. + 2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts + string `s' and transit to `b': + i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw + away the node `a'. + ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is + thrown away, we throw away the node `a'. + 3. When 0 <= STR_IDX < MATCH_LAST and 'a' epsilon transit to 'b': + i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the + node `a'. + ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is thrown away, + we throw away the node `a'. */ + +#define STATE_NODE_CONTAINS(state,node) \ + ((state) != NULL && re_node_set_contains (&(state)->nodes, node)) + +static reg_errcode_t +internal_function +sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx) +{ + reg_errcode_t err; + int null_cnt = 0; + Idx str_idx = sctx->last_str_idx; + re_node_set cur_dest; + +#ifdef DEBUG + assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL); +#endif + + /* Build sifted state_log[str_idx]. It has the nodes which can epsilon + transit to the last_node and the last_node itself. */ + err = re_node_set_init_1 (&cur_dest, sctx->last_node); + if (BE (err != REG_NOERROR, 0)) + return err; + err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* Then check each states in the state_log. */ + while (str_idx > 0) + { + /* Update counters. */ + null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0; + if (null_cnt > mctx->max_mb_elem_len) + { + memset (sctx->sifted_states, '\0', + sizeof (re_dfastate_t *) * str_idx); + re_node_set_free (&cur_dest); + return REG_NOERROR; + } + re_node_set_empty (&cur_dest); + --str_idx; + + if (mctx->state_log[str_idx]) + { + err = build_sifted_states (mctx, sctx, str_idx, &cur_dest); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + + /* Add all the nodes which satisfy the following conditions: + - It can epsilon transit to a node in CUR_DEST. + - It is in CUR_SRC. + And update state_log. */ + err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + err = REG_NOERROR; + free_return: + re_node_set_free (&cur_dest); + return err; +} + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +build_sifted_states (const re_match_context_t *mctx, re_sift_context_t *sctx, + Idx str_idx, re_node_set *cur_dest) +{ + const re_dfa_t *const dfa = mctx->dfa; + const re_node_set *cur_src = &mctx->state_log[str_idx]->non_eps_nodes; + Idx i; + + /* Then build the next sifted state. + We build the next sifted state on `cur_dest', and update + `sifted_states[str_idx]' with `cur_dest'. + Note: + `cur_dest' is the sifted state from `state_log[str_idx + 1]'. + `cur_src' points the node_set of the old `state_log[str_idx]' + (with the epsilon nodes pre-filtered out). */ + for (i = 0; i < cur_src->nelem; i++) + { + Idx prev_node = cur_src->elems[i]; + int naccepted = 0; + bool ok; + +#ifdef DEBUG + re_token_type_t type = dfa->nodes[prev_node].type; + assert (!IS_EPSILON_NODE (type)); +#endif +#ifdef RE_ENABLE_I18N + /* If the node may accept `multi byte'. */ + if (dfa->nodes[prev_node].accept_mb) + naccepted = sift_states_iter_mb (mctx, sctx, prev_node, + str_idx, sctx->last_str_idx); +#endif /* RE_ENABLE_I18N */ + + /* We don't check backreferences here. + See update_cur_sifted_state(). */ + if (!naccepted + && check_node_accept (mctx, dfa->nodes + prev_node, str_idx) + && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1], + dfa->nexts[prev_node])) + naccepted = 1; + + if (naccepted == 0) + continue; + + if (sctx->limits.nelem) + { + Idx to_idx = str_idx + naccepted; + if (check_dst_limits (mctx, &sctx->limits, + dfa->nexts[prev_node], to_idx, + prev_node, str_idx)) + continue; + } + ok = re_node_set_insert (cur_dest, prev_node); + if (BE (! ok, 0)) + return REG_ESPACE; + } + + return REG_NOERROR; +} + +/* Helper functions. */ + +static reg_errcode_t +internal_function +clean_state_log_if_needed (re_match_context_t *mctx, Idx next_state_log_idx) +{ + Idx top = mctx->state_log_top; + + if (next_state_log_idx >= mctx->input.bufs_len + || (next_state_log_idx >= mctx->input.valid_len + && mctx->input.valid_len < mctx->input.len)) + { + reg_errcode_t err; + err = extend_buffers (mctx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + if (top < next_state_log_idx) + { + memset (mctx->state_log + top + 1, '\0', + sizeof (re_dfastate_t *) * (next_state_log_idx - top)); + mctx->state_log_top = next_state_log_idx; + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +merge_state_array (const re_dfa_t *dfa, re_dfastate_t **dst, + re_dfastate_t **src, Idx num) +{ + Idx st_idx; + reg_errcode_t err; + for (st_idx = 0; st_idx < num; ++st_idx) + { + if (dst[st_idx] == NULL) + dst[st_idx] = src[st_idx]; + else if (src[st_idx] != NULL) + { + re_node_set merged_set; + err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes, + &src[st_idx]->nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + dst[st_idx] = re_acquire_state (&err, dfa, &merged_set); + re_node_set_free (&merged_set); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +update_cur_sifted_state (const re_match_context_t *mctx, + re_sift_context_t *sctx, Idx str_idx, + re_node_set *dest_nodes) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err = REG_NOERROR; + const re_node_set *candidates; + candidates = ((mctx->state_log[str_idx] == NULL) ? NULL + : &mctx->state_log[str_idx]->nodes); + + if (dest_nodes->nelem == 0) + sctx->sifted_states[str_idx] = NULL; + else + { + if (candidates) + { + /* At first, add the nodes which can epsilon transit to a node in + DEST_NODE. */ + err = add_epsilon_src_nodes (dfa, dest_nodes, candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* Then, check the limitations in the current sift_context. */ + if (sctx->limits.nelem) + { + err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits, + mctx->bkref_ents, str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + + sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + if (candidates && mctx->state_log[str_idx]->has_backref) + { + err = sift_states_bkref (mctx, sctx, str_idx, candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +add_epsilon_src_nodes (const re_dfa_t *dfa, re_node_set *dest_nodes, + const re_node_set *candidates) +{ + reg_errcode_t err = REG_NOERROR; + Idx i; + + re_dfastate_t *state = re_acquire_state (&err, dfa, dest_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + + if (!state->inveclosure.alloc) + { + err = re_node_set_alloc (&state->inveclosure, dest_nodes->nelem); + if (BE (err != REG_NOERROR, 0)) + return REG_ESPACE; + for (i = 0; i < dest_nodes->nelem; i++) + { + err = re_node_set_merge (&state->inveclosure, + dfa->inveclosures + dest_nodes->elems[i]); + if (BE (err != REG_NOERROR, 0)) + return REG_ESPACE; + } + } + return re_node_set_add_intersect (dest_nodes, candidates, + &state->inveclosure); +} + +static reg_errcode_t +internal_function +sub_epsilon_src_nodes (const re_dfa_t *dfa, Idx node, re_node_set *dest_nodes, + const re_node_set *candidates) +{ + Idx ecl_idx; + reg_errcode_t err; + re_node_set *inv_eclosure = dfa->inveclosures + node; + re_node_set except_nodes; + re_node_set_init_empty (&except_nodes); + for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx) + { + Idx cur_node = inv_eclosure->elems[ecl_idx]; + if (cur_node == node) + continue; + if (IS_EPSILON_NODE (dfa->nodes[cur_node].type)) + { + Idx edst1 = dfa->edests[cur_node].elems[0]; + Idx edst2 = ((dfa->edests[cur_node].nelem > 1) + ? dfa->edests[cur_node].elems[1] : REG_MISSING); + if ((!re_node_set_contains (inv_eclosure, edst1) + && re_node_set_contains (dest_nodes, edst1)) + || (REG_VALID_NONZERO_INDEX (edst2) + && !re_node_set_contains (inv_eclosure, edst2) + && re_node_set_contains (dest_nodes, edst2))) + { + err = re_node_set_add_intersect (&except_nodes, candidates, + dfa->inveclosures + cur_node); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&except_nodes); + return err; + } + } + } + } + for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx) + { + Idx cur_node = inv_eclosure->elems[ecl_idx]; + if (!re_node_set_contains (&except_nodes, cur_node)) + { + Idx idx = re_node_set_contains (dest_nodes, cur_node) - 1; + re_node_set_remove_at (dest_nodes, idx); + } + } + re_node_set_free (&except_nodes); + return REG_NOERROR; +} + +static bool +internal_function +check_dst_limits (const re_match_context_t *mctx, const re_node_set *limits, + Idx dst_node, Idx dst_idx, Idx src_node, Idx src_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + Idx lim_idx, src_pos, dst_pos; + + Idx dst_bkref_idx = search_cur_bkref_entry (mctx, dst_idx); + Idx src_bkref_idx = search_cur_bkref_entry (mctx, src_idx); + for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) + { + Idx subexp_idx; + struct re_backref_cache_entry *ent; + ent = mctx->bkref_ents + limits->elems[lim_idx]; + subexp_idx = dfa->nodes[ent->node].opr.idx; + + dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], + subexp_idx, dst_node, dst_idx, + dst_bkref_idx); + src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], + subexp_idx, src_node, src_idx, + src_bkref_idx); + + /* In case of: + ( ) + ( ) + ( ) */ + if (src_pos == dst_pos) + continue; /* This is unrelated limitation. */ + else + return true; + } + return false; +} + +static int +internal_function +check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries, + Idx subexp_idx, Idx from_node, Idx bkref_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + const re_node_set *eclosures = dfa->eclosures + from_node; + Idx node_idx; + + /* Else, we are on the boundary: examine the nodes on the epsilon + closure. */ + for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx) + { + Idx node = eclosures->elems[node_idx]; + switch (dfa->nodes[node].type) + { + case OP_BACK_REF: + if (bkref_idx != REG_MISSING) + { + struct re_backref_cache_entry *ent = mctx->bkref_ents + bkref_idx; + do + { + Idx dst; + int cpos; + + if (ent->node != node) + continue; + + if (subexp_idx < BITSET_WORD_BITS + && !(ent->eps_reachable_subexps_map + & ((bitset_word_t) 1 << subexp_idx))) + continue; + + /* Recurse trying to reach the OP_OPEN_SUBEXP and + OP_CLOSE_SUBEXP cases below. But, if the + destination node is the same node as the source + node, don't recurse because it would cause an + infinite loop: a regex that exhibits this behavior + is ()\1*\1* */ + dst = dfa->edests[node].elems[0]; + if (dst == from_node) + { + if (boundaries & 1) + return -1; + else /* if (boundaries & 2) */ + return 0; + } + + cpos = + check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, + dst, bkref_idx); + if (cpos == -1 /* && (boundaries & 1) */) + return -1; + if (cpos == 0 && (boundaries & 2)) + return 0; + + if (subexp_idx < BITSET_WORD_BITS) + ent->eps_reachable_subexps_map + &= ~((bitset_word_t) 1 << subexp_idx); + } + while (ent++->more); + } + break; + + case OP_OPEN_SUBEXP: + if ((boundaries & 1) && subexp_idx == dfa->nodes[node].opr.idx) + return -1; + break; + + case OP_CLOSE_SUBEXP: + if ((boundaries & 2) && subexp_idx == dfa->nodes[node].opr.idx) + return 0; + break; + + default: + break; + } + } + + return (boundaries & 2) ? 1 : 0; +} + +static int +internal_function +check_dst_limits_calc_pos (const re_match_context_t *mctx, Idx limit, + Idx subexp_idx, Idx from_node, Idx str_idx, + Idx bkref_idx) +{ + struct re_backref_cache_entry *lim = mctx->bkref_ents + limit; + int boundaries; + + /* If we are outside the range of the subexpression, return -1 or 1. */ + if (str_idx < lim->subexp_from) + return -1; + + if (lim->subexp_to < str_idx) + return 1; + + /* If we are within the subexpression, return 0. */ + boundaries = (str_idx == lim->subexp_from); + boundaries |= (str_idx == lim->subexp_to) << 1; + if (boundaries == 0) + return 0; + + /* Else, examine epsilon closure. */ + return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, + from_node, bkref_idx); +} + +/* Check the limitations of sub expressions LIMITS, and remove the nodes + which are against limitations from DEST_NODES. */ + +static reg_errcode_t +internal_function +check_subexp_limits (const re_dfa_t *dfa, re_node_set *dest_nodes, + const re_node_set *candidates, re_node_set *limits, + struct re_backref_cache_entry *bkref_ents, Idx str_idx) +{ + reg_errcode_t err; + Idx node_idx, lim_idx; + + for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) + { + Idx subexp_idx; + struct re_backref_cache_entry *ent; + ent = bkref_ents + limits->elems[lim_idx]; + + if (str_idx <= ent->subexp_from || ent->str_idx < str_idx) + continue; /* This is unrelated limitation. */ + + subexp_idx = dfa->nodes[ent->node].opr.idx; + if (ent->subexp_to == str_idx) + { + Idx ops_node = REG_MISSING; + Idx cls_node = REG_MISSING; + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + Idx node = dest_nodes->elems[node_idx]; + re_token_type_t type = dfa->nodes[node].type; + if (type == OP_OPEN_SUBEXP + && subexp_idx == dfa->nodes[node].opr.idx) + ops_node = node; + else if (type == OP_CLOSE_SUBEXP + && subexp_idx == dfa->nodes[node].opr.idx) + cls_node = node; + } + + /* Check the limitation of the open subexpression. */ + /* Note that (ent->subexp_to = str_idx != ent->subexp_from). */ + if (REG_VALID_INDEX (ops_node)) + { + err = sub_epsilon_src_nodes (dfa, ops_node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + /* Check the limitation of the close subexpression. */ + if (REG_VALID_INDEX (cls_node)) + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + Idx node = dest_nodes->elems[node_idx]; + if (!re_node_set_contains (dfa->inveclosures + node, + cls_node) + && !re_node_set_contains (dfa->eclosures + node, + cls_node)) + { + /* It is against this limitation. + Remove it form the current sifted state. */ + err = sub_epsilon_src_nodes (dfa, node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + --node_idx; + } + } + } + else /* (ent->subexp_to != str_idx) */ + { + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + Idx node = dest_nodes->elems[node_idx]; + re_token_type_t type = dfa->nodes[node].type; + if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP) + { + if (subexp_idx != dfa->nodes[node].opr.idx) + continue; + /* It is against this limitation. + Remove it form the current sifted state. */ + err = sub_epsilon_src_nodes (dfa, node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + } + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx, + Idx str_idx, const re_node_set *candidates) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + Idx node_idx, node; + re_sift_context_t local_sctx; + Idx first_idx = search_cur_bkref_entry (mctx, str_idx); + + if (first_idx == REG_MISSING) + return REG_NOERROR; + + local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */ + + for (node_idx = 0; node_idx < candidates->nelem; ++node_idx) + { + Idx enabled_idx; + re_token_type_t type; + struct re_backref_cache_entry *entry; + node = candidates->elems[node_idx]; + type = dfa->nodes[node].type; + /* Avoid infinite loop for the REs like "()\1+". */ + if (node == sctx->last_node && str_idx == sctx->last_str_idx) + continue; + if (type != OP_BACK_REF) + continue; + + entry = mctx->bkref_ents + first_idx; + enabled_idx = first_idx; + do + { + Idx subexp_len; + Idx to_idx; + Idx dst_node; + bool ok; + re_dfastate_t *cur_state; + + if (entry->node != node) + continue; + subexp_len = entry->subexp_to - entry->subexp_from; + to_idx = str_idx + subexp_len; + dst_node = (subexp_len ? dfa->nexts[node] + : dfa->edests[node].elems[0]); + + if (to_idx > sctx->last_str_idx + || sctx->sifted_states[to_idx] == NULL + || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx], dst_node) + || check_dst_limits (mctx, &sctx->limits, node, + str_idx, dst_node, to_idx)) + continue; + + if (local_sctx.sifted_states == NULL) + { + local_sctx = *sctx; + err = re_node_set_init_copy (&local_sctx.limits, &sctx->limits); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + local_sctx.last_node = node; + local_sctx.last_str_idx = str_idx; + ok = re_node_set_insert (&local_sctx.limits, enabled_idx); + if (BE (! ok, 0)) + { + err = REG_ESPACE; + goto free_return; + } + cur_state = local_sctx.sifted_states[str_idx]; + err = sift_states_backward (mctx, &local_sctx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + if (sctx->limited_states != NULL) + { + err = merge_state_array (dfa, sctx->limited_states, + local_sctx.sifted_states, + str_idx + 1); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + local_sctx.sifted_states[str_idx] = cur_state; + re_node_set_remove (&local_sctx.limits, enabled_idx); + + /* mctx->bkref_ents may have changed, reload the pointer. */ + entry = mctx->bkref_ents + enabled_idx; + } + while (enabled_idx++, entry++->more); + } + err = REG_NOERROR; + free_return: + if (local_sctx.sifted_states != NULL) + { + re_node_set_free (&local_sctx.limits); + } + + return err; +} + + +#ifdef RE_ENABLE_I18N +static int +internal_function +sift_states_iter_mb (const re_match_context_t *mctx, re_sift_context_t *sctx, + Idx node_idx, Idx str_idx, Idx max_str_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + int naccepted; + /* Check the node can accept `multi byte'. */ + naccepted = check_node_accept_bytes (dfa, node_idx, &mctx->input, str_idx); + if (naccepted > 0 && str_idx + naccepted <= max_str_idx && + !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted], + dfa->nexts[node_idx])) + /* The node can't accept the `multi byte', or the + destination was already thrown away, then the node + could't accept the current input `multi byte'. */ + naccepted = 0; + /* Otherwise, it is sure that the node could accept + `naccepted' bytes input. */ + return naccepted; +} +#endif /* RE_ENABLE_I18N */ + + +/* Functions for state transition. */ + +/* Return the next state to which the current state STATE will transit by + accepting the current input byte, and update STATE_LOG if necessary. + If STATE can accept a multibyte char/collating element/back reference + update the destination of STATE_LOG. */ + +static re_dfastate_t * +internal_function __attribute_warn_unused_result__ +transit_state (reg_errcode_t *err, re_match_context_t *mctx, + re_dfastate_t *state) +{ + re_dfastate_t **trtable; + unsigned char ch; + +#ifdef RE_ENABLE_I18N + /* If the current state can accept multibyte. */ + if (BE (state->accept_mb, 0)) + { + *err = transit_state_mb (mctx, state); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } +#endif /* RE_ENABLE_I18N */ + + /* Then decide the next state with the single byte. */ +#if 0 + if (0) + /* don't use transition table */ + return transit_state_sb (err, mctx, state); +#endif + + /* Use transition table */ + ch = re_string_fetch_byte (&mctx->input); + for (;;) + { + trtable = state->trtable; + if (BE (trtable != NULL, 1)) + return trtable[ch]; + + trtable = state->word_trtable; + if (BE (trtable != NULL, 1)) + { + unsigned int context; + context + = re_string_context_at (&mctx->input, + re_string_cur_idx (&mctx->input) - 1, + mctx->eflags); + if (IS_WORD_CONTEXT (context)) + return trtable[ch + SBC_MAX]; + else + return trtable[ch]; + } + + if (!build_trtable (mctx->dfa, state)) + { + *err = REG_ESPACE; + return NULL; + } + + /* Retry, we now have a transition table. */ + } +} + +/* Update the state_log if we need */ +static re_dfastate_t * +internal_function +merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx, + re_dfastate_t *next_state) +{ + const re_dfa_t *const dfa = mctx->dfa; + Idx cur_idx = re_string_cur_idx (&mctx->input); + + if (cur_idx > mctx->state_log_top) + { + mctx->state_log[cur_idx] = next_state; + mctx->state_log_top = cur_idx; + } + else if (mctx->state_log[cur_idx] == 0) + { + mctx->state_log[cur_idx] = next_state; + } + else + { + re_dfastate_t *pstate; + unsigned int context; + re_node_set next_nodes, *log_nodes, *table_nodes = NULL; + /* If (state_log[cur_idx] != 0), it implies that cur_idx is + the destination of a multibyte char/collating element/ + back reference. Then the next state is the union set of + these destinations and the results of the transition table. */ + pstate = mctx->state_log[cur_idx]; + log_nodes = pstate->entrance_nodes; + if (next_state != NULL) + { + table_nodes = next_state->entrance_nodes; + *err = re_node_set_init_union (&next_nodes, table_nodes, + log_nodes); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } + else + next_nodes = *log_nodes; + /* Note: We already add the nodes of the initial state, + then we don't need to add them here. */ + + context = re_string_context_at (&mctx->input, + re_string_cur_idx (&mctx->input) - 1, + mctx->eflags); + next_state = mctx->state_log[cur_idx] + = re_acquire_state_context (err, dfa, &next_nodes, context); + /* We don't need to check errors here, since the return value of + this function is next_state and ERR is already set. */ + + if (table_nodes != NULL) + re_node_set_free (&next_nodes); + } + + if (BE (dfa->nbackref, 0) && next_state != NULL) + { + /* Check OP_OPEN_SUBEXP in the current state in case that we use them + later. We must check them here, since the back references in the + next state might use them. */ + *err = check_subexp_matching_top (mctx, &next_state->nodes, + cur_idx); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + + /* If the next state has back references. */ + if (next_state->has_backref) + { + *err = transit_state_bkref (mctx, &next_state->nodes); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + next_state = mctx->state_log[cur_idx]; + } + } + + return next_state; +} + +/* Skip bytes in the input that correspond to part of a + multi-byte match, then look in the log for a state + from which to restart matching. */ +static re_dfastate_t * +internal_function +find_recover_state (reg_errcode_t *err, re_match_context_t *mctx) +{ + re_dfastate_t *cur_state; + do + { + Idx max = mctx->state_log_top; + Idx cur_str_idx = re_string_cur_idx (&mctx->input); + + do + { + if (++cur_str_idx > max) + return NULL; + re_string_skip_bytes (&mctx->input, 1); + } + while (mctx->state_log[cur_str_idx] == NULL); + + cur_state = merge_state_with_log (err, mctx, NULL); + } + while (*err == REG_NOERROR && cur_state == NULL); + return cur_state; +} + +/* Helper functions for transit_state. */ + +/* From the node set CUR_NODES, pick up the nodes whose types are + OP_OPEN_SUBEXP and which have corresponding back references in the regular + expression. And register them to use them later for evaluating the + correspoding back references. */ + +static reg_errcode_t +internal_function +check_subexp_matching_top (re_match_context_t *mctx, re_node_set *cur_nodes, + Idx str_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + Idx node_idx; + reg_errcode_t err; + + /* TODO: This isn't efficient. + Because there might be more than one nodes whose types are + OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all + nodes. + E.g. RE: (a){2} */ + for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx) + { + Idx node = cur_nodes->elems[node_idx]; + if (dfa->nodes[node].type == OP_OPEN_SUBEXP + && dfa->nodes[node].opr.idx < BITSET_WORD_BITS + && (dfa->used_bkref_map + & ((bitset_word_t) 1 << dfa->nodes[node].opr.idx))) + { + err = match_ctx_add_subtop (mctx, node, str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + return REG_NOERROR; +} + +#if 0 +/* Return the next state to which the current state STATE will transit by + accepting the current input byte. */ + +static re_dfastate_t * +transit_state_sb (reg_errcode_t *err, re_match_context_t *mctx, + re_dfastate_t *state) +{ + const re_dfa_t *const dfa = mctx->dfa; + re_node_set next_nodes; + re_dfastate_t *next_state; + Idx node_cnt, cur_str_idx = re_string_cur_idx (&mctx->input); + unsigned int context; + + *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt) + { + Idx cur_node = state->nodes.elems[node_cnt]; + if (check_node_accept (mctx, dfa->nodes + cur_node, cur_str_idx)) + { + *err = re_node_set_merge (&next_nodes, + dfa->eclosures + dfa->nexts[cur_node]); + if (BE (*err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return NULL; + } + } + } + context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags); + next_state = re_acquire_state_context (err, dfa, &next_nodes, context); + /* We don't need to check errors here, since the return value of + this function is next_state and ERR is already set. */ + + re_node_set_free (&next_nodes); + re_string_skip_bytes (&mctx->input, 1); + return next_state; +} +#endif + +#ifdef RE_ENABLE_I18N +static reg_errcode_t +internal_function +transit_state_mb (re_match_context_t *mctx, re_dfastate_t *pstate) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + Idx i; + + for (i = 0; i < pstate->nodes.nelem; ++i) + { + re_node_set dest_nodes, *new_nodes; + Idx cur_node_idx = pstate->nodes.elems[i]; + int naccepted; + Idx dest_idx; + unsigned int context; + re_dfastate_t *dest_state; + + if (!dfa->nodes[cur_node_idx].accept_mb) + continue; + + if (dfa->nodes[cur_node_idx].constraint) + { + context = re_string_context_at (&mctx->input, + re_string_cur_idx (&mctx->input), + mctx->eflags); + if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint, + context)) + continue; + } + + /* How many bytes the node can accept? */ + naccepted = check_node_accept_bytes (dfa, cur_node_idx, &mctx->input, + re_string_cur_idx (&mctx->input)); + if (naccepted == 0) + continue; + + /* The node can accepts `naccepted' bytes. */ + dest_idx = re_string_cur_idx (&mctx->input) + naccepted; + mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted + : mctx->max_mb_elem_len); + err = clean_state_log_if_needed (mctx, dest_idx); + if (BE (err != REG_NOERROR, 0)) + return err; +#ifdef DEBUG + assert (dfa->nexts[cur_node_idx] != REG_MISSING); +#endif + new_nodes = dfa->eclosures + dfa->nexts[cur_node_idx]; + + dest_state = mctx->state_log[dest_idx]; + if (dest_state == NULL) + dest_nodes = *new_nodes; + else + { + err = re_node_set_init_union (&dest_nodes, + dest_state->entrance_nodes, new_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + context = re_string_context_at (&mctx->input, dest_idx - 1, + mctx->eflags); + mctx->state_log[dest_idx] + = re_acquire_state_context (&err, dfa, &dest_nodes, context); + if (dest_state != NULL) + re_node_set_free (&dest_nodes); + if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0)) + return err; + } + return REG_NOERROR; +} +#endif /* RE_ENABLE_I18N */ + +static reg_errcode_t +internal_function +transit_state_bkref (re_match_context_t *mctx, const re_node_set *nodes) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + Idx i; + Idx cur_str_idx = re_string_cur_idx (&mctx->input); + + for (i = 0; i < nodes->nelem; ++i) + { + Idx dest_str_idx, prev_nelem, bkc_idx; + Idx node_idx = nodes->elems[i]; + unsigned int context; + const re_token_t *node = dfa->nodes + node_idx; + re_node_set *new_dest_nodes; + + /* Check whether `node' is a backreference or not. */ + if (node->type != OP_BACK_REF) + continue; + + if (node->constraint) + { + context = re_string_context_at (&mctx->input, cur_str_idx, + mctx->eflags); + if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) + continue; + } + + /* `node' is a backreference. + Check the substring which the substring matched. */ + bkc_idx = mctx->nbkref_ents; + err = get_subexp (mctx, node_idx, cur_str_idx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* And add the epsilon closures (which is `new_dest_nodes') of + the backreference to appropriate state_log. */ +#ifdef DEBUG + assert (dfa->nexts[node_idx] != REG_MISSING); +#endif + for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx) + { + Idx subexp_len; + re_dfastate_t *dest_state; + struct re_backref_cache_entry *bkref_ent; + bkref_ent = mctx->bkref_ents + bkc_idx; + if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx) + continue; + subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from; + new_dest_nodes = (subexp_len == 0 + ? dfa->eclosures + dfa->edests[node_idx].elems[0] + : dfa->eclosures + dfa->nexts[node_idx]); + dest_str_idx = (cur_str_idx + bkref_ent->subexp_to + - bkref_ent->subexp_from); + context = re_string_context_at (&mctx->input, dest_str_idx - 1, + mctx->eflags); + dest_state = mctx->state_log[dest_str_idx]; + prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0 + : mctx->state_log[cur_str_idx]->nodes.nelem); + /* Add `new_dest_node' to state_log. */ + if (dest_state == NULL) + { + mctx->state_log[dest_str_idx] + = re_acquire_state_context (&err, dfa, new_dest_nodes, + context); + if (BE (mctx->state_log[dest_str_idx] == NULL + && err != REG_NOERROR, 0)) + goto free_return; + } + else + { + re_node_set dest_nodes; + err = re_node_set_init_union (&dest_nodes, + dest_state->entrance_nodes, + new_dest_nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&dest_nodes); + goto free_return; + } + mctx->state_log[dest_str_idx] + = re_acquire_state_context (&err, dfa, &dest_nodes, context); + re_node_set_free (&dest_nodes); + if (BE (mctx->state_log[dest_str_idx] == NULL + && err != REG_NOERROR, 0)) + goto free_return; + } + /* We need to check recursively if the backreference can epsilon + transit. */ + if (subexp_len == 0 + && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem) + { + err = check_subexp_matching_top (mctx, new_dest_nodes, + cur_str_idx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + err = transit_state_bkref (mctx, new_dest_nodes); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + } + } + err = REG_NOERROR; + free_return: + return err; +} + +/* Enumerate all the candidates which the backreference BKREF_NODE can match + at BKREF_STR_IDX, and register them by match_ctx_add_entry(). + Note that we might collect inappropriate candidates here. + However, the cost of checking them strictly here is too high, then we + delay these checking for prune_impossible_nodes(). */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +get_subexp (re_match_context_t *mctx, Idx bkref_node, Idx bkref_str_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + Idx subexp_num, sub_top_idx; + const char *buf = (const char *) re_string_get_buffer (&mctx->input); + /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX. */ + Idx cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx); + if (cache_idx != REG_MISSING) + { + const struct re_backref_cache_entry *entry + = mctx->bkref_ents + cache_idx; + do + if (entry->node == bkref_node) + return REG_NOERROR; /* We already checked it. */ + while (entry++->more); + } + + subexp_num = dfa->nodes[bkref_node].opr.idx; + + /* For each sub expression */ + for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx) + { + reg_errcode_t err; + re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx]; + re_sub_match_last_t *sub_last; + Idx sub_last_idx, sl_str, bkref_str_off; + + if (dfa->nodes[sub_top->node].opr.idx != subexp_num) + continue; /* It isn't related. */ + + sl_str = sub_top->str_idx; + bkref_str_off = bkref_str_idx; + /* At first, check the last node of sub expressions we already + evaluated. */ + for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx) + { + regoff_t sl_str_diff; + sub_last = sub_top->lasts[sub_last_idx]; + sl_str_diff = sub_last->str_idx - sl_str; + /* The matched string by the sub expression match with the substring + at the back reference? */ + if (sl_str_diff > 0) + { + if (BE (bkref_str_off + sl_str_diff > mctx->input.valid_len, 0)) + { + /* Not enough chars for a successful match. */ + if (bkref_str_off + sl_str_diff > mctx->input.len) + break; + + err = clean_state_log_if_needed (mctx, + bkref_str_off + + sl_str_diff); + if (BE (err != REG_NOERROR, 0)) + return err; + buf = (const char *) re_string_get_buffer (&mctx->input); + } + if (memcmp (buf + bkref_str_off, buf + sl_str, sl_str_diff) != 0) + /* We don't need to search this sub expression any more. */ + break; + } + bkref_str_off += sl_str_diff; + sl_str += sl_str_diff; + err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node, + bkref_str_idx); + + /* Reload buf, since the preceding call might have reallocated + the buffer. */ + buf = (const char *) re_string_get_buffer (&mctx->input); + + if (err == REG_NOMATCH) + continue; + if (BE (err != REG_NOERROR, 0)) + return err; + } + + if (sub_last_idx < sub_top->nlasts) + continue; + if (sub_last_idx > 0) + ++sl_str; + /* Then, search for the other last nodes of the sub expression. */ + for (; sl_str <= bkref_str_idx; ++sl_str) + { + Idx cls_node; + regoff_t sl_str_off; + const re_node_set *nodes; + sl_str_off = sl_str - sub_top->str_idx; + /* The matched string by the sub expression match with the substring + at the back reference? */ + if (sl_str_off > 0) + { + if (BE (bkref_str_off >= mctx->input.valid_len, 0)) + { + /* If we are at the end of the input, we cannot match. */ + if (bkref_str_off >= mctx->input.len) + break; + + err = extend_buffers (mctx); + if (BE (err != REG_NOERROR, 0)) + return err; + + buf = (const char *) re_string_get_buffer (&mctx->input); + } + if (buf [bkref_str_off++] != buf[sl_str - 1]) + break; /* We don't need to search this sub expression + any more. */ + } + if (mctx->state_log[sl_str] == NULL) + continue; + /* Does this state have a ')' of the sub expression? */ + nodes = &mctx->state_log[sl_str]->nodes; + cls_node = find_subexp_node (dfa, nodes, subexp_num, + OP_CLOSE_SUBEXP); + if (cls_node == REG_MISSING) + continue; /* No. */ + if (sub_top->path == NULL) + { + sub_top->path = calloc (sizeof (state_array_t), + sl_str - sub_top->str_idx + 1); + if (sub_top->path == NULL) + return REG_ESPACE; + } + /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node + in the current context? */ + err = check_arrival (mctx, sub_top->path, sub_top->node, + sub_top->str_idx, cls_node, sl_str, + OP_CLOSE_SUBEXP); + if (err == REG_NOMATCH) + continue; + if (BE (err != REG_NOERROR, 0)) + return err; + sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str); + if (BE (sub_last == NULL, 0)) + return REG_ESPACE; + err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node, + bkref_str_idx); + if (err == REG_NOMATCH) + continue; + } + } + return REG_NOERROR; +} + +/* Helper functions for get_subexp(). */ + +/* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR. + If it can arrive, register the sub expression expressed with SUB_TOP + and SUB_LAST. */ + +static reg_errcode_t +internal_function +get_subexp_sub (re_match_context_t *mctx, const re_sub_match_top_t *sub_top, + re_sub_match_last_t *sub_last, Idx bkref_node, Idx bkref_str) +{ + reg_errcode_t err; + Idx to_idx; + /* Can the subexpression arrive the back reference? */ + err = check_arrival (mctx, &sub_last->path, sub_last->node, + sub_last->str_idx, bkref_node, bkref_str, + OP_OPEN_SUBEXP); + if (err != REG_NOERROR) + return err; + err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx, + sub_last->str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx; + return clean_state_log_if_needed (mctx, to_idx); +} + +/* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX. + Search '(' if FL_OPEN, or search ')' otherwise. + TODO: This function isn't efficient... + Because there might be more than one nodes whose types are + OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all + nodes. + E.g. RE: (a){2} */ + +static Idx +internal_function +find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes, + Idx subexp_idx, int type) +{ + Idx cls_idx; + for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx) + { + Idx cls_node = nodes->elems[cls_idx]; + const re_token_t *node = dfa->nodes + cls_node; + if (node->type == type + && node->opr.idx == subexp_idx) + return cls_node; + } + return REG_MISSING; +} + +/* Check whether the node TOP_NODE at TOP_STR can arrive to the node + LAST_NODE at LAST_STR. We record the path onto PATH since it will be + heavily reused. + Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +check_arrival (re_match_context_t *mctx, state_array_t *path, Idx top_node, + Idx top_str, Idx last_node, Idx last_str, int type) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err = REG_NOERROR; + Idx subexp_num, backup_cur_idx, str_idx, null_cnt; + re_dfastate_t *cur_state = NULL; + re_node_set *cur_nodes, next_nodes; + re_dfastate_t **backup_state_log; + unsigned int context; + + subexp_num = dfa->nodes[top_node].opr.idx; + /* Extend the buffer if we need. */ + if (BE (path->alloc < last_str + mctx->max_mb_elem_len + 1, 0)) + { + re_dfastate_t **new_array; + Idx old_alloc = path->alloc; + Idx new_alloc = old_alloc + last_str + mctx->max_mb_elem_len + 1; + if (BE (new_alloc < old_alloc, 0) + || BE (SIZE_MAX / sizeof (re_dfastate_t *) < new_alloc, 0)) + return REG_ESPACE; + new_array = re_realloc (path->array, re_dfastate_t *, new_alloc); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + path->array = new_array; + path->alloc = new_alloc; + memset (new_array + old_alloc, '\0', + sizeof (re_dfastate_t *) * (path->alloc - old_alloc)); + } + + str_idx = path->next_idx ? path->next_idx : top_str; + + /* Temporary modify MCTX. */ + backup_state_log = mctx->state_log; + backup_cur_idx = mctx->input.cur_idx; + mctx->state_log = path->array; + mctx->input.cur_idx = str_idx; + + /* Setup initial node set. */ + context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags); + if (str_idx == top_str) + { + err = re_node_set_init_1 (&next_nodes, top_node); + if (BE (err != REG_NOERROR, 0)) + return err; + err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + else + { + cur_state = mctx->state_log[str_idx]; + if (cur_state && cur_state->has_backref) + { + err = re_node_set_init_copy (&next_nodes, &cur_state->nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + else + re_node_set_init_empty (&next_nodes); + } + if (str_idx == top_str || (cur_state && cur_state->has_backref)) + { + if (next_nodes.nelem) + { + err = expand_bkref_cache (mctx, &next_nodes, str_idx, + subexp_num, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context); + if (BE (cur_state == NULL && err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + mctx->state_log[str_idx] = cur_state; + } + + for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;) + { + re_node_set_empty (&next_nodes); + if (mctx->state_log[str_idx + 1]) + { + err = re_node_set_merge (&next_nodes, + &mctx->state_log[str_idx + 1]->nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + if (cur_state) + { + err = check_arrival_add_next_nodes (mctx, str_idx, + &cur_state->non_eps_nodes, + &next_nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + ++str_idx; + if (next_nodes.nelem) + { + err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + err = expand_bkref_cache (mctx, &next_nodes, str_idx, + subexp_num, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags); + cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context); + if (BE (cur_state == NULL && err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + mctx->state_log[str_idx] = cur_state; + null_cnt = cur_state == NULL ? null_cnt + 1 : 0; + } + re_node_set_free (&next_nodes); + cur_nodes = (mctx->state_log[last_str] == NULL ? NULL + : &mctx->state_log[last_str]->nodes); + path->next_idx = str_idx; + + /* Fix MCTX. */ + mctx->state_log = backup_state_log; + mctx->input.cur_idx = backup_cur_idx; + + /* Then check the current node set has the node LAST_NODE. */ + if (cur_nodes != NULL && re_node_set_contains (cur_nodes, last_node)) + return REG_NOERROR; + + return REG_NOMATCH; +} + +/* Helper functions for check_arrival. */ + +/* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them + to NEXT_NODES. + TODO: This function is similar to the functions transit_state*(), + however this function has many additional works. + Can't we unify them? */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +check_arrival_add_next_nodes (re_match_context_t *mctx, Idx str_idx, + re_node_set *cur_nodes, re_node_set *next_nodes) +{ + const re_dfa_t *const dfa = mctx->dfa; + bool ok; + Idx cur_idx; +#ifdef RE_ENABLE_I18N + reg_errcode_t err = REG_NOERROR; +#endif + re_node_set union_set; + re_node_set_init_empty (&union_set); + for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx) + { + int naccepted = 0; + Idx cur_node = cur_nodes->elems[cur_idx]; +#ifdef DEBUG + re_token_type_t type = dfa->nodes[cur_node].type; + assert (!IS_EPSILON_NODE (type)); +#endif +#ifdef RE_ENABLE_I18N + /* If the node may accept `multi byte'. */ + if (dfa->nodes[cur_node].accept_mb) + { + naccepted = check_node_accept_bytes (dfa, cur_node, &mctx->input, + str_idx); + if (naccepted > 1) + { + re_dfastate_t *dest_state; + Idx next_node = dfa->nexts[cur_node]; + Idx next_idx = str_idx + naccepted; + dest_state = mctx->state_log[next_idx]; + re_node_set_empty (&union_set); + if (dest_state) + { + err = re_node_set_merge (&union_set, &dest_state->nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&union_set); + return err; + } + } + ok = re_node_set_insert (&union_set, next_node); + if (BE (! ok, 0)) + { + re_node_set_free (&union_set); + return REG_ESPACE; + } + mctx->state_log[next_idx] = re_acquire_state (&err, dfa, + &union_set); + if (BE (mctx->state_log[next_idx] == NULL + && err != REG_NOERROR, 0)) + { + re_node_set_free (&union_set); + return err; + } + } + } +#endif /* RE_ENABLE_I18N */ + if (naccepted + || check_node_accept (mctx, dfa->nodes + cur_node, str_idx)) + { + ok = re_node_set_insert (next_nodes, dfa->nexts[cur_node]); + if (BE (! ok, 0)) + { + re_node_set_free (&union_set); + return REG_ESPACE; + } + } + } + re_node_set_free (&union_set); + return REG_NOERROR; +} + +/* For all the nodes in CUR_NODES, add the epsilon closures of them to + CUR_NODES, however exclude the nodes which are: + - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN. + - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN. +*/ + +static reg_errcode_t +internal_function +check_arrival_expand_ecl (const re_dfa_t *dfa, re_node_set *cur_nodes, + Idx ex_subexp, int type) +{ + reg_errcode_t err; + Idx idx, outside_node; + re_node_set new_nodes; +#ifdef DEBUG + assert (cur_nodes->nelem); +#endif + err = re_node_set_alloc (&new_nodes, cur_nodes->nelem); + if (BE (err != REG_NOERROR, 0)) + return err; + /* Create a new node set NEW_NODES with the nodes which are epsilon + closures of the node in CUR_NODES. */ + + for (idx = 0; idx < cur_nodes->nelem; ++idx) + { + Idx cur_node = cur_nodes->elems[idx]; + const re_node_set *eclosure = dfa->eclosures + cur_node; + outside_node = find_subexp_node (dfa, eclosure, ex_subexp, type); + if (outside_node == REG_MISSING) + { + /* There are no problematic nodes, just merge them. */ + err = re_node_set_merge (&new_nodes, eclosure); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&new_nodes); + return err; + } + } + else + { + /* There are problematic nodes, re-calculate incrementally. */ + err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node, + ex_subexp, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&new_nodes); + return err; + } + } + } + re_node_set_free (cur_nodes); + *cur_nodes = new_nodes; + return REG_NOERROR; +} + +/* Helper function for check_arrival_expand_ecl. + Check incrementally the epsilon closure of TARGET, and if it isn't + problematic append it to DST_NODES. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +check_arrival_expand_ecl_sub (const re_dfa_t *dfa, re_node_set *dst_nodes, + Idx target, Idx ex_subexp, int type) +{ + Idx cur_node; + for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);) + { + bool ok; + + if (dfa->nodes[cur_node].type == type + && dfa->nodes[cur_node].opr.idx == ex_subexp) + { + if (type == OP_CLOSE_SUBEXP) + { + ok = re_node_set_insert (dst_nodes, cur_node); + if (BE (! ok, 0)) + return REG_ESPACE; + } + break; + } + ok = re_node_set_insert (dst_nodes, cur_node); + if (BE (! ok, 0)) + return REG_ESPACE; + if (dfa->edests[cur_node].nelem == 0) + break; + if (dfa->edests[cur_node].nelem == 2) + { + reg_errcode_t err; + err = check_arrival_expand_ecl_sub (dfa, dst_nodes, + dfa->edests[cur_node].elems[1], + ex_subexp, type); + if (BE (err != REG_NOERROR, 0)) + return err; + } + cur_node = dfa->edests[cur_node].elems[0]; + } + return REG_NOERROR; +} + + +/* For all the back references in the current state, calculate the + destination of the back references by the appropriate entry + in MCTX->BKREF_ENTS. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +expand_bkref_cache (re_match_context_t *mctx, re_node_set *cur_nodes, + Idx cur_str, Idx subexp_num, int type) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + Idx cache_idx_start = search_cur_bkref_entry (mctx, cur_str); + struct re_backref_cache_entry *ent; + + if (cache_idx_start == REG_MISSING) + return REG_NOERROR; + + restart: + ent = mctx->bkref_ents + cache_idx_start; + do + { + Idx to_idx, next_node; + + /* Is this entry ENT is appropriate? */ + if (!re_node_set_contains (cur_nodes, ent->node)) + continue; /* No. */ + + to_idx = cur_str + ent->subexp_to - ent->subexp_from; + /* Calculate the destination of the back reference, and append it + to MCTX->STATE_LOG. */ + if (to_idx == cur_str) + { + /* The backreference did epsilon transit, we must re-check all the + node in the current state. */ + re_node_set new_dests; + reg_errcode_t err2, err3; + next_node = dfa->edests[ent->node].elems[0]; + if (re_node_set_contains (cur_nodes, next_node)) + continue; + err = re_node_set_init_1 (&new_dests, next_node); + err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num, type); + err3 = re_node_set_merge (cur_nodes, &new_dests); + re_node_set_free (&new_dests); + if (BE (err != REG_NOERROR || err2 != REG_NOERROR + || err3 != REG_NOERROR, 0)) + { + err = (err != REG_NOERROR ? err + : (err2 != REG_NOERROR ? err2 : err3)); + return err; + } + /* TODO: It is still inefficient... */ + goto restart; + } + else + { + re_node_set union_set; + next_node = dfa->nexts[ent->node]; + if (mctx->state_log[to_idx]) + { + bool ok; + if (re_node_set_contains (&mctx->state_log[to_idx]->nodes, + next_node)) + continue; + err = re_node_set_init_copy (&union_set, + &mctx->state_log[to_idx]->nodes); + ok = re_node_set_insert (&union_set, next_node); + if (BE (err != REG_NOERROR || ! ok, 0)) + { + re_node_set_free (&union_set); + err = err != REG_NOERROR ? err : REG_ESPACE; + return err; + } + } + else + { + err = re_node_set_init_1 (&union_set, next_node); + if (BE (err != REG_NOERROR, 0)) + return err; + } + mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set); + re_node_set_free (&union_set); + if (BE (mctx->state_log[to_idx] == NULL + && err != REG_NOERROR, 0)) + return err; + } + } + while (ent++->more); + return REG_NOERROR; +} + +/* Build transition table for the state. + Return true if successful. */ + +static bool +internal_function +build_trtable (const re_dfa_t *dfa, re_dfastate_t *state) +{ + reg_errcode_t err; + Idx i, j; + int ch; + bool need_word_trtable = false; + bitset_word_t elem, mask; + bool dests_node_malloced = false; + bool dest_states_malloced = false; + Idx ndests; /* Number of the destination states from `state'. */ + re_dfastate_t **trtable; + re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl; + re_node_set follows, *dests_node; + bitset_t *dests_ch; + bitset_t acceptable; + + struct dests_alloc + { + re_node_set dests_node[SBC_MAX]; + bitset_t dests_ch[SBC_MAX]; + } *dests_alloc; + + /* We build DFA states which corresponds to the destination nodes + from `state'. `dests_node[i]' represents the nodes which i-th + destination state contains, and `dests_ch[i]' represents the + characters which i-th destination state accepts. */ + if (__libc_use_alloca (sizeof (struct dests_alloc))) + dests_alloc = (struct dests_alloc *) alloca (sizeof (struct dests_alloc)); + else + { + dests_alloc = re_malloc (struct dests_alloc, 1); + if (BE (dests_alloc == NULL, 0)) + return false; + dests_node_malloced = true; + } + dests_node = dests_alloc->dests_node; + dests_ch = dests_alloc->dests_ch; + + /* Initialize transiton table. */ + state->word_trtable = state->trtable = NULL; + + /* At first, group all nodes belonging to `state' into several + destinations. */ + ndests = group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch); + if (BE (! REG_VALID_NONZERO_INDEX (ndests), 0)) + { + if (dests_node_malloced) + free (dests_alloc); + if (ndests == 0) + { + state->trtable = (re_dfastate_t **) + calloc (sizeof (re_dfastate_t *), SBC_MAX); + return true; + } + return false; + } + + err = re_node_set_alloc (&follows, ndests + 1); + if (BE (err != REG_NOERROR, 0)) + goto out_free; + + /* Avoid arithmetic overflow in size calculation. */ + if (BE ((((SIZE_MAX - (sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX) + / (3 * sizeof (re_dfastate_t *))) + < ndests), + 0)) + goto out_free; + + if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX + + ndests * 3 * sizeof (re_dfastate_t *))) + dest_states = (re_dfastate_t **) + alloca (ndests * 3 * sizeof (re_dfastate_t *)); + else + { + dest_states = (re_dfastate_t **) + malloc (ndests * 3 * sizeof (re_dfastate_t *)); + if (BE (dest_states == NULL, 0)) + { +out_free: + if (dest_states_malloced) + free (dest_states); + re_node_set_free (&follows); + for (i = 0; i < ndests; ++i) + re_node_set_free (dests_node + i); + if (dests_node_malloced) + free (dests_alloc); + return false; + } + dest_states_malloced = true; + } + dest_states_word = dest_states + ndests; + dest_states_nl = dest_states_word + ndests; + bitset_empty (acceptable); + + /* Then build the states for all destinations. */ + for (i = 0; i < ndests; ++i) + { + Idx next_node; + re_node_set_empty (&follows); + /* Merge the follows of this destination states. */ + for (j = 0; j < dests_node[i].nelem; ++j) + { + next_node = dfa->nexts[dests_node[i].elems[j]]; + if (next_node != REG_MISSING) + { + err = re_node_set_merge (&follows, dfa->eclosures + next_node); + if (BE (err != REG_NOERROR, 0)) + goto out_free; + } + } + dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0); + if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + /* If the new state has context constraint, + build appropriate states for these contexts. */ + if (dest_states[i]->has_constraint) + { + dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows, + CONTEXT_WORD); + if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + + if (dest_states[i] != dest_states_word[i] && dfa->mb_cur_max > 1) + need_word_trtable = true; + + dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows, + CONTEXT_NEWLINE); + if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + } + else + { + dest_states_word[i] = dest_states[i]; + dest_states_nl[i] = dest_states[i]; + } + bitset_merge (acceptable, dests_ch[i]); + } + + if (!BE (need_word_trtable, 0)) + { + /* We don't care about whether the following character is a word + character, or we are in a single-byte character set so we can + discern by looking at the character code: allocate a + 256-entry transition table. */ + trtable = state->trtable = + (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX); + if (BE (trtable == NULL, 0)) + goto out_free; + + /* For all characters ch...: */ + for (i = 0; i < BITSET_WORDS; ++i) + for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1; + elem; + mask <<= 1, elem >>= 1, ++ch) + if (BE (elem & 1, 0)) + { + /* There must be exactly one destination which accepts + character ch. See group_nodes_into_DFAstates. */ + for (j = 0; (dests_ch[j][i] & mask) == 0; ++j) + ; + + /* j-th destination accepts the word character ch. */ + if (dfa->word_char[i] & mask) + trtable[ch] = dest_states_word[j]; + else + trtable[ch] = dest_states[j]; + } + } + else + { + /* We care about whether the following character is a word + character, and we are in a multi-byte character set: discern + by looking at the character code: build two 256-entry + transition tables, one starting at trtable[0] and one + starting at trtable[SBC_MAX]. */ + trtable = state->word_trtable = + (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX); + if (BE (trtable == NULL, 0)) + goto out_free; + + /* For all characters ch...: */ + for (i = 0; i < BITSET_WORDS; ++i) + for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1; + elem; + mask <<= 1, elem >>= 1, ++ch) + if (BE (elem & 1, 0)) + { + /* There must be exactly one destination which accepts + character ch. See group_nodes_into_DFAstates. */ + for (j = 0; (dests_ch[j][i] & mask) == 0; ++j) + ; + + /* j-th destination accepts the word character ch. */ + trtable[ch] = dest_states[j]; + trtable[ch + SBC_MAX] = dest_states_word[j]; + } + } + + /* new line */ + if (bitset_contain (acceptable, NEWLINE_CHAR)) + { + /* The current state accepts newline character. */ + for (j = 0; j < ndests; ++j) + if (bitset_contain (dests_ch[j], NEWLINE_CHAR)) + { + /* k-th destination accepts newline character. */ + trtable[NEWLINE_CHAR] = dest_states_nl[j]; + if (need_word_trtable) + trtable[NEWLINE_CHAR + SBC_MAX] = dest_states_nl[j]; + /* There must be only one destination which accepts + newline. See group_nodes_into_DFAstates. */ + break; + } + } + + if (dest_states_malloced) + free (dest_states); + + re_node_set_free (&follows); + for (i = 0; i < ndests; ++i) + re_node_set_free (dests_node + i); + + if (dests_node_malloced) + free (dests_alloc); + + return true; +} + +/* Group all nodes belonging to STATE into several destinations. + Then for all destinations, set the nodes belonging to the destination + to DESTS_NODE[i] and set the characters accepted by the destination + to DEST_CH[i]. This function return the number of destinations. */ + +static Idx +internal_function +group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state, + re_node_set *dests_node, bitset_t *dests_ch) +{ + reg_errcode_t err; + bool ok; + Idx i, j, k; + Idx ndests; /* Number of the destinations from `state'. */ + bitset_t accepts; /* Characters a node can accept. */ + const re_node_set *cur_nodes = &state->nodes; + bitset_empty (accepts); + ndests = 0; + + /* For all the nodes belonging to `state', */ + for (i = 0; i < cur_nodes->nelem; ++i) + { + re_token_t *node = &dfa->nodes[cur_nodes->elems[i]]; + re_token_type_t type = node->type; + unsigned int constraint = node->constraint; + + /* Enumerate all single byte character this node can accept. */ + if (type == CHARACTER) + bitset_set (accepts, node->opr.c); + else if (type == SIMPLE_BRACKET) + { + bitset_merge (accepts, node->opr.sbcset); + } + else if (type == OP_PERIOD) + { +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + bitset_merge (accepts, dfa->sb_char); + else +#endif + bitset_set_all (accepts); + if (!(dfa->syntax & RE_DOT_NEWLINE)) + bitset_clear (accepts, '\n'); + if (dfa->syntax & RE_DOT_NOT_NULL) + bitset_clear (accepts, '\0'); + } +#ifdef RE_ENABLE_I18N + else if (type == OP_UTF8_PERIOD) + { + if (ASCII_CHARS % BITSET_WORD_BITS == 0) + memset (accepts, -1, ASCII_CHARS / CHAR_BIT); + else + bitset_merge (accepts, utf8_sb_map); + if (!(dfa->syntax & RE_DOT_NEWLINE)) + bitset_clear (accepts, '\n'); + if (dfa->syntax & RE_DOT_NOT_NULL) + bitset_clear (accepts, '\0'); + } +#endif + else + continue; + + /* Check the `accepts' and sift the characters which are not + match it the context. */ + if (constraint) + { + if (constraint & NEXT_NEWLINE_CONSTRAINT) + { + bool accepts_newline = bitset_contain (accepts, NEWLINE_CHAR); + bitset_empty (accepts); + if (accepts_newline) + bitset_set (accepts, NEWLINE_CHAR); + else + continue; + } + if (constraint & NEXT_ENDBUF_CONSTRAINT) + { + bitset_empty (accepts); + continue; + } + + if (constraint & NEXT_WORD_CONSTRAINT) + { + bitset_word_t any_set = 0; + if (type == CHARACTER && !node->word_char) + { + bitset_empty (accepts); + continue; + } +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + for (j = 0; j < BITSET_WORDS; ++j) + any_set |= (accepts[j] &= (dfa->word_char[j] | ~dfa->sb_char[j])); + else +#endif + for (j = 0; j < BITSET_WORDS; ++j) + any_set |= (accepts[j] &= dfa->word_char[j]); + if (!any_set) + continue; + } + if (constraint & NEXT_NOTWORD_CONSTRAINT) + { + bitset_word_t any_set = 0; + if (type == CHARACTER && node->word_char) + { + bitset_empty (accepts); + continue; + } +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + for (j = 0; j < BITSET_WORDS; ++j) + any_set |= (accepts[j] &= ~(dfa->word_char[j] & dfa->sb_char[j])); + else +#endif + for (j = 0; j < BITSET_WORDS; ++j) + any_set |= (accepts[j] &= ~dfa->word_char[j]); + if (!any_set) + continue; + } + } + + /* Then divide `accepts' into DFA states, or create a new + state. Above, we make sure that accepts is not empty. */ + for (j = 0; j < ndests; ++j) + { + bitset_t intersec; /* Intersection sets, see below. */ + bitset_t remains; + /* Flags, see below. */ + bitset_word_t has_intersec, not_subset, not_consumed; + + /* Optimization, skip if this state doesn't accept the character. */ + if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c)) + continue; + + /* Enumerate the intersection set of this state and `accepts'. */ + has_intersec = 0; + for (k = 0; k < BITSET_WORDS; ++k) + has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k]; + /* And skip if the intersection set is empty. */ + if (!has_intersec) + continue; + + /* Then check if this state is a subset of `accepts'. */ + not_subset = not_consumed = 0; + for (k = 0; k < BITSET_WORDS; ++k) + { + not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k]; + not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k]; + } + + /* If this state isn't a subset of `accepts', create a + new group state, which has the `remains'. */ + if (not_subset) + { + bitset_copy (dests_ch[ndests], remains); + bitset_copy (dests_ch[j], intersec); + err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]); + if (BE (err != REG_NOERROR, 0)) + goto error_return; + ++ndests; + } + + /* Put the position in the current group. */ + ok = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]); + if (BE (! ok, 0)) + goto error_return; + + /* If all characters are consumed, go to next node. */ + if (!not_consumed) + break; + } + /* Some characters remain, create a new group. */ + if (j == ndests) + { + bitset_copy (dests_ch[ndests], accepts); + err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]); + if (BE (err != REG_NOERROR, 0)) + goto error_return; + ++ndests; + bitset_empty (accepts); + } + } + return ndests; + error_return: + for (j = 0; j < ndests; ++j) + re_node_set_free (dests_node + j); + return REG_MISSING; +} + +#ifdef RE_ENABLE_I18N +/* Check how many bytes the node `dfa->nodes[node_idx]' accepts. + Return the number of the bytes the node accepts. + STR_IDX is the current index of the input string. + + This function handles the nodes which can accept one character, or + one collating element like '.', '[a-z]', opposite to the other nodes + can only accept one byte. */ + +static int +internal_function +check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx, + const re_string_t *input, Idx str_idx) +{ + const re_token_t *node = dfa->nodes + node_idx; + int char_len, elem_len; + Idx i; + + if (BE (node->type == OP_UTF8_PERIOD, 0)) + { + unsigned char c = re_string_byte_at (input, str_idx), d; + if (BE (c < 0xc2, 1)) + return 0; + + if (str_idx + 2 > input->len) + return 0; + + d = re_string_byte_at (input, str_idx + 1); + if (c < 0xe0) + return (d < 0x80 || d > 0xbf) ? 0 : 2; + else if (c < 0xf0) + { + char_len = 3; + if (c == 0xe0 && d < 0xa0) + return 0; + } + else if (c < 0xf8) + { + char_len = 4; + if (c == 0xf0 && d < 0x90) + return 0; + } + else if (c < 0xfc) + { + char_len = 5; + if (c == 0xf8 && d < 0x88) + return 0; + } + else if (c < 0xfe) + { + char_len = 6; + if (c == 0xfc && d < 0x84) + return 0; + } + else + return 0; + + if (str_idx + char_len > input->len) + return 0; + + for (i = 1; i < char_len; ++i) + { + d = re_string_byte_at (input, str_idx + i); + if (d < 0x80 || d > 0xbf) + return 0; + } + return char_len; + } + + char_len = re_string_char_size_at (input, str_idx); + if (node->type == OP_PERIOD) + { + if (char_len <= 1) + return 0; + /* FIXME: I don't think this if is needed, as both '\n' + and '\0' are char_len == 1. */ + /* '.' accepts any one character except the following two cases. */ + if ((!(dfa->syntax & RE_DOT_NEWLINE) && + re_string_byte_at (input, str_idx) == '\n') || + ((dfa->syntax & RE_DOT_NOT_NULL) && + re_string_byte_at (input, str_idx) == '\0')) + return 0; + return char_len; + } + + elem_len = re_string_elem_size_at (input, str_idx); + if ((elem_len <= 1 && char_len <= 1) || char_len == 0) + return 0; + + if (node->type == COMPLEX_BRACKET) + { + const re_charset_t *cset = node->opr.mbcset; +# ifdef _LIBC + const unsigned char *pin + = ((const unsigned char *) re_string_get_buffer (input) + str_idx); + Idx j; + uint32_t nrules; +# endif /* _LIBC */ + int match_len = 0; + wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars) + ? re_string_wchar_at (input, str_idx) : 0); + + /* match with multibyte character? */ + for (i = 0; i < cset->nmbchars; ++i) + if (wc == cset->mbchars[i]) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + /* match with character_class? */ + for (i = 0; i < cset->nchar_classes; ++i) + { + wctype_t wt = cset->char_classes[i]; + if (__iswctype (wc, wt)) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + } + +# ifdef _LIBC + nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules != 0) + { + unsigned int in_collseq = 0; + const int32_t *table, *indirect; + const unsigned char *weights, *extra; + const char *collseqwc; + int32_t idx; + /* This #include defines a local function! */ +# include + + /* match with collating_symbol? */ + if (cset->ncoll_syms) + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); + for (i = 0; i < cset->ncoll_syms; ++i) + { + const unsigned char *coll_sym = extra + cset->coll_syms[i]; + /* Compare the length of input collating element and + the length of current collating element. */ + if (*coll_sym != elem_len) + continue; + /* Compare each bytes. */ + for (j = 0; j < *coll_sym; j++) + if (pin[j] != coll_sym[1 + j]) + break; + if (j == *coll_sym) + { + /* Match if every bytes is equal. */ + match_len = j; + goto check_node_accept_bytes_match; + } + } + + if (cset->nranges) + { + if (elem_len <= char_len) + { + collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC); + in_collseq = __collseq_table_lookup (collseqwc, wc); + } + else + in_collseq = find_collation_sequence_value (pin, elem_len); + } + /* match with range expression? */ + for (i = 0; i < cset->nranges; ++i) + if (cset->range_starts[i] <= in_collseq + && in_collseq <= cset->range_ends[i]) + { + match_len = elem_len; + goto check_node_accept_bytes_match; + } + + /* match with equivalence_class? */ + if (cset->nequiv_classes) + { + const unsigned char *cp = pin; + table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + weights = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB); + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB); + int32_t idx = findidx (&cp); + if (idx > 0) + for (i = 0; i < cset->nequiv_classes; ++i) + { + int32_t equiv_class_idx = cset->equiv_classes[i]; + size_t weight_len = weights[idx & 0xffffff]; + if (weight_len == weights[equiv_class_idx & 0xffffff] + && (idx >> 24) == (equiv_class_idx >> 24)) + { + Idx cnt = 0; + + idx &= 0xffffff; + equiv_class_idx &= 0xffffff; + + while (cnt <= weight_len + && (weights[equiv_class_idx + 1 + cnt] + == weights[idx + 1 + cnt])) + ++cnt; + if (cnt > weight_len) + { + match_len = elem_len; + goto check_node_accept_bytes_match; + } + } + } + } + } + else +# endif /* _LIBC */ + { + /* match with range expression? */ +#if __GNUC__ >= 2 && ! (__STDC_VERSION__ < 199901L && __STRICT_ANSI__) + wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'}; +#else + wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'}; + cmp_buf[2] = wc; +#endif + for (i = 0; i < cset->nranges; ++i) + { + cmp_buf[0] = cset->range_starts[i]; + cmp_buf[4] = cset->range_ends[i]; + if (wcscoll (cmp_buf, cmp_buf + 2) <= 0 + && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + } + } + check_node_accept_bytes_match: + if (!cset->non_match) + return match_len; + else + { + if (match_len > 0) + return 0; + else + return (elem_len > char_len) ? elem_len : char_len; + } + } + return 0; +} + +# ifdef _LIBC +static unsigned int +internal_function +find_collation_sequence_value (const unsigned char *mbs, size_t mbs_len) +{ + uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules == 0) + { + if (mbs_len == 1) + { + /* No valid character. Match it as a single byte character. */ + const unsigned char *collseq = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB); + return collseq[mbs[0]]; + } + return UINT_MAX; + } + else + { + int32_t idx; + const unsigned char *extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); + int32_t extrasize = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB + 1) - extra; + + for (idx = 0; idx < extrasize;) + { + int mbs_cnt; + bool found = false; + int32_t elem_mbs_len; + /* Skip the name of collating element name. */ + idx = idx + extra[idx] + 1; + elem_mbs_len = extra[idx++]; + if (mbs_len == elem_mbs_len) + { + for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt) + if (extra[idx + mbs_cnt] != mbs[mbs_cnt]) + break; + if (mbs_cnt == elem_mbs_len) + /* Found the entry. */ + found = true; + } + /* Skip the byte sequence of the collating element. */ + idx += elem_mbs_len; + /* Adjust for the alignment. */ + idx = (idx + 3) & ~3; + /* Skip the collation sequence value. */ + idx += sizeof (uint32_t); + /* Skip the wide char sequence of the collating element. */ + idx = idx + sizeof (uint32_t) * (extra[idx] + 1); + /* If we found the entry, return the sequence value. */ + if (found) + return *(uint32_t *) (extra + idx); + /* Skip the collation sequence value. */ + idx += sizeof (uint32_t); + } + return UINT_MAX; + } +} +# endif /* _LIBC */ +#endif /* RE_ENABLE_I18N */ + +/* Check whether the node accepts the byte which is IDX-th + byte of the INPUT. */ + +static bool +internal_function +check_node_accept (const re_match_context_t *mctx, const re_token_t *node, + Idx idx) +{ + unsigned char ch; + ch = re_string_byte_at (&mctx->input, idx); + switch (node->type) + { + case CHARACTER: + if (node->opr.c != ch) + return false; + break; + + case SIMPLE_BRACKET: + if (!bitset_contain (node->opr.sbcset, ch)) + return false; + break; + +#ifdef RE_ENABLE_I18N + case OP_UTF8_PERIOD: + if (ch >= ASCII_CHARS) + return false; + /* FALLTHROUGH */ +#endif + case OP_PERIOD: + if ((ch == '\n' && !(mctx->dfa->syntax & RE_DOT_NEWLINE)) + || (ch == '\0' && (mctx->dfa->syntax & RE_DOT_NOT_NULL))) + return false; + break; + + default: + return false; + } + + if (node->constraint) + { + /* The node has constraints. Check whether the current context + satisfies the constraints. */ + unsigned int context = re_string_context_at (&mctx->input, idx, + mctx->eflags); + if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) + return false; + } + + return true; +} + +/* Extend the buffers, if the buffers have run out. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +extend_buffers (re_match_context_t *mctx) +{ + reg_errcode_t ret; + re_string_t *pstr = &mctx->input; + + /* Avoid overflow. */ + if (BE (SIZE_MAX / 2 / sizeof (re_dfastate_t *) <= pstr->bufs_len, 0)) + return REG_ESPACE; + + /* Double the lengthes of the buffers. */ + ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); + if (BE (ret != REG_NOERROR, 0)) + return ret; + + if (mctx->state_log != NULL) + { + /* And double the length of state_log. */ + /* XXX We have no indication of the size of this buffer. If this + allocation fail we have no indication that the state_log array + does not have the right size. */ + re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *, + pstr->bufs_len + 1); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + mctx->state_log = new_array; + } + + /* Then reconstruct the buffers. */ + if (pstr->icase) + { +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + ret = build_wcs_upper_buffer (pstr); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + else +#endif /* RE_ENABLE_I18N */ + build_upper_buffer (pstr); + } + else + { +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + build_wcs_buffer (pstr); + else +#endif /* RE_ENABLE_I18N */ + { + if (pstr->trans != NULL) + re_string_translate_buffer (pstr); + } + } + return REG_NOERROR; +} + + +/* Functions for matching context. */ + +/* Initialize MCTX. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +match_ctx_init (re_match_context_t *mctx, int eflags, Idx n) +{ + mctx->eflags = eflags; + mctx->match_last = REG_MISSING; + if (n > 0) + { + /* Avoid overflow. */ + size_t max_object_size = + MAX (sizeof (struct re_backref_cache_entry), + sizeof (re_sub_match_top_t *)); + if (BE (SIZE_MAX / max_object_size < n, 0)) + return REG_ESPACE; + + mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n); + mctx->sub_tops = re_malloc (re_sub_match_top_t *, n); + if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0)) + return REG_ESPACE; + } + /* Already zero-ed by the caller. + else + mctx->bkref_ents = NULL; + mctx->nbkref_ents = 0; + mctx->nsub_tops = 0; */ + mctx->abkref_ents = n; + mctx->max_mb_elem_len = 1; + mctx->asub_tops = n; + return REG_NOERROR; +} + +/* Clean the entries which depend on the current input in MCTX. + This function must be invoked when the matcher changes the start index + of the input, or changes the input string. */ + +static void +internal_function +match_ctx_clean (re_match_context_t *mctx) +{ + Idx st_idx; + for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx) + { + Idx sl_idx; + re_sub_match_top_t *top = mctx->sub_tops[st_idx]; + for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx) + { + re_sub_match_last_t *last = top->lasts[sl_idx]; + re_free (last->path.array); + re_free (last); + } + re_free (top->lasts); + if (top->path) + { + re_free (top->path->array); + re_free (top->path); + } + free (top); + } + + mctx->nsub_tops = 0; + mctx->nbkref_ents = 0; +} + +/* Free all the memory associated with MCTX. */ + +static void +internal_function +match_ctx_free (re_match_context_t *mctx) +{ + /* First, free all the memory associated with MCTX->SUB_TOPS. */ + match_ctx_clean (mctx); + re_free (mctx->sub_tops); + re_free (mctx->bkref_ents); +} + +/* Add a new backreference entry to MCTX. + Note that we assume that caller never call this function with duplicate + entry, and call with STR_IDX which isn't smaller than any existing entry. +*/ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +match_ctx_add_entry (re_match_context_t *mctx, Idx node, Idx str_idx, Idx from, + Idx to) +{ + if (mctx->nbkref_ents >= mctx->abkref_ents) + { + struct re_backref_cache_entry* new_entry; + new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry, + mctx->abkref_ents * 2); + if (BE (new_entry == NULL, 0)) + { + re_free (mctx->bkref_ents); + return REG_ESPACE; + } + mctx->bkref_ents = new_entry; + memset (mctx->bkref_ents + mctx->nbkref_ents, '\0', + sizeof (struct re_backref_cache_entry) * mctx->abkref_ents); + mctx->abkref_ents *= 2; + } + if (mctx->nbkref_ents > 0 + && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx) + mctx->bkref_ents[mctx->nbkref_ents - 1].more = 1; + + mctx->bkref_ents[mctx->nbkref_ents].node = node; + mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx; + mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from; + mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to; + + /* This is a cache that saves negative results of check_dst_limits_calc_pos. + If bit N is clear, means that this entry won't epsilon-transition to + an OP_OPEN_SUBEXP or OP_CLOSE_SUBEXP for the N+1-th subexpression. If + it is set, check_dst_limits_calc_pos_1 will recurse and try to find one + such node. + + A backreference does not epsilon-transition unless it is empty, so set + to all zeros if FROM != TO. */ + mctx->bkref_ents[mctx->nbkref_ents].eps_reachable_subexps_map + = (from == to ? -1 : 0); + + mctx->bkref_ents[mctx->nbkref_ents++].more = 0; + if (mctx->max_mb_elem_len < to - from) + mctx->max_mb_elem_len = to - from; + return REG_NOERROR; +} + +/* Return the first entry with the same str_idx, or REG_MISSING if none is + found. Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX. */ + +static Idx +internal_function +search_cur_bkref_entry (const re_match_context_t *mctx, Idx str_idx) +{ + Idx left, right, mid, last; + last = right = mctx->nbkref_ents; + for (left = 0; left < right;) + { + mid = (left + right) / 2; + if (mctx->bkref_ents[mid].str_idx < str_idx) + left = mid + 1; + else + right = mid; + } + if (left < last && mctx->bkref_ents[left].str_idx == str_idx) + return left; + else + return REG_MISSING; +} + +/* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches + at STR_IDX. */ + +static reg_errcode_t +internal_function __attribute_warn_unused_result__ +match_ctx_add_subtop (re_match_context_t *mctx, Idx node, Idx str_idx) +{ +#ifdef DEBUG + assert (mctx->sub_tops != NULL); + assert (mctx->asub_tops > 0); +#endif + if (BE (mctx->nsub_tops == mctx->asub_tops, 0)) + { + Idx new_asub_tops = mctx->asub_tops * 2; + re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops, + re_sub_match_top_t *, + new_asub_tops); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + mctx->sub_tops = new_array; + mctx->asub_tops = new_asub_tops; + } + mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t)); + if (BE (mctx->sub_tops[mctx->nsub_tops] == NULL, 0)) + return REG_ESPACE; + mctx->sub_tops[mctx->nsub_tops]->node = node; + mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx; + return REG_NOERROR; +} + +/* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches + at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP. */ + +static re_sub_match_last_t * +internal_function +match_ctx_add_sublast (re_sub_match_top_t *subtop, Idx node, Idx str_idx) +{ + re_sub_match_last_t *new_entry; + if (BE (subtop->nlasts == subtop->alasts, 0)) + { + Idx new_alasts = 2 * subtop->alasts + 1; + re_sub_match_last_t **new_array = re_realloc (subtop->lasts, + re_sub_match_last_t *, + new_alasts); + if (BE (new_array == NULL, 0)) + return NULL; + subtop->lasts = new_array; + subtop->alasts = new_alasts; + } + new_entry = calloc (1, sizeof (re_sub_match_last_t)); + if (BE (new_entry != NULL, 1)) + { + subtop->lasts[subtop->nlasts] = new_entry; + new_entry->node = node; + new_entry->str_idx = str_idx; + ++subtop->nlasts; + } + return new_entry; +} + +static void +internal_function +sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, + re_dfastate_t **limited_sts, Idx last_node, Idx last_str_idx) +{ + sctx->sifted_states = sifted_sts; + sctx->limited_states = limited_sts; + sctx->last_node = last_node; + sctx->last_str_idx = last_str_idx; + re_node_set_init_empty (&sctx->limits); +} diff --git a/hook/datehook.c b/hook/datehook.c index 4876e1198..9b5b54bf3 100644 --- a/hook/datehook.c +++ b/hook/datehook.c @@ -84,7 +84,7 @@ grub_read_hook_datetime (struct grub_env_var *var, return buf; } -GRUB_MOD_INIT(datetime) +GRUB_MOD_INIT(datehook) { int i; @@ -93,7 +93,7 @@ GRUB_MOD_INIT(datetime) grub_read_hook_datetime, 0); } -GRUB_MOD_FINI(datetime) +GRUB_MOD_FINI(datehook) { int i; diff --git a/include/grub/aout.h b/include/grub/aout.h index 04e85f8b0..f962a97b3 100644 --- a/include/grub/aout.h +++ b/include/grub/aout.h @@ -102,6 +102,7 @@ union grub_aout_header #define AOUT_MID_I386 134 /* i386 BSD binary */ #define AOUT_MID_SPARC 138 /* sparc */ #define AOUT_MID_HP200 200 /* hp200 (68010) BSD binary */ +#define AOUT_MID_SUN 0x103 #define AOUT_MID_HP300 300 /* hp300 (68020+68881) BSD binary */ #define AOUT_MID_HPUX 0x20C /* hp200/300 HP-UX binary */ #define AOUT_MID_HPUX800 0x20B /* hp800 HP-UX binary */ @@ -114,10 +115,14 @@ union grub_aout_header #define AOUT_GETMID(header) ((header).a_midmag >> 16) & 0x03ff) #define AOUT_GETFLAG(header) ((header).a_midmag >> 26) & 0x3f) +#ifndef GRUB_UTIL + int EXPORT_FUNC(grub_aout_get_type) (union grub_aout_header *header); grub_err_t EXPORT_FUNC(grub_aout_load) (grub_file_t file, int offset, grub_addr_t load_addr, int load_size, grub_addr_t bss_end_addr); +#endif + #endif /* ! GRUB_AOUT_HEADER */ diff --git a/include/grub/bitmap.h b/include/grub/bitmap.h index 42c439d69..6e300391a 100644 --- a/include/grub/bitmap.h +++ b/include/grub/bitmap.h @@ -47,24 +47,24 @@ struct grub_video_bitmap_reader }; typedef struct grub_video_bitmap_reader *grub_video_bitmap_reader_t; -void grub_video_bitmap_reader_register (grub_video_bitmap_reader_t reader); -void grub_video_bitmap_reader_unregister (grub_video_bitmap_reader_t reader); +void EXPORT_FUNC (grub_video_bitmap_reader_register) (grub_video_bitmap_reader_t reader); +void EXPORT_FUNC (grub_video_bitmap_reader_unregister) (grub_video_bitmap_reader_t reader); -grub_err_t grub_video_bitmap_create (struct grub_video_bitmap **bitmap, - unsigned int width, unsigned int height, - enum grub_video_blit_format blit_format); +grub_err_t EXPORT_FUNC (grub_video_bitmap_create) (struct grub_video_bitmap **bitmap, + unsigned int width, unsigned int height, + enum grub_video_blit_format blit_format); -grub_err_t grub_video_bitmap_destroy (struct grub_video_bitmap *bitmap); +grub_err_t EXPORT_FUNC (grub_video_bitmap_destroy) (struct grub_video_bitmap *bitmap); -grub_err_t grub_video_bitmap_load (struct grub_video_bitmap **bitmap, - const char *filename); +grub_err_t EXPORT_FUNC (grub_video_bitmap_load) (struct grub_video_bitmap **bitmap, + const char *filename); -unsigned int grub_video_bitmap_get_width (struct grub_video_bitmap *bitmap); -unsigned int grub_video_bitmap_get_height (struct grub_video_bitmap *bitmap); +unsigned int EXPORT_FUNC (grub_video_bitmap_get_width) (struct grub_video_bitmap *bitmap); +unsigned int EXPORT_FUNC (grub_video_bitmap_get_height) (struct grub_video_bitmap *bitmap); -void grub_video_bitmap_get_mode_info (struct grub_video_bitmap *bitmap, - struct grub_video_mode_info *mode_info); +void EXPORT_FUNC (grub_video_bitmap_get_mode_info) (struct grub_video_bitmap *bitmap, + struct grub_video_mode_info *mode_info); -void *grub_video_bitmap_get_data (struct grub_video_bitmap *bitmap); +void *EXPORT_FUNC (grub_video_bitmap_get_data) (struct grub_video_bitmap *bitmap); #endif /* ! GRUB_BITMAP_HEADER */ diff --git a/include/grub/bitmap_scale.h b/include/grub/bitmap_scale.h new file mode 100644 index 000000000..dce9fbbf2 --- /dev/null +++ b/include/grub/bitmap_scale.h @@ -0,0 +1,49 @@ +/* bitmap_scale.h - Bitmap scaling functions. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_BITMAP_SCALE_HEADER +#define GRUB_BITMAP_SCALE_HEADER 1 + +#include +#include +#include + +enum grub_video_bitmap_scale_method +{ + /* Choose the fastest interpolation algorithm. */ + GRUB_VIDEO_BITMAP_SCALE_METHOD_FASTEST, + /* Choose the highest quality interpolation algorithm. */ + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST, + + /* Specific algorithms: */ + /* Nearest neighbor interpolation. */ + GRUB_VIDEO_BITMAP_SCALE_METHOD_NEAREST, + /* Bilinear interpolation. */ + GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR +}; + +grub_err_t +EXPORT_FUNC (grub_video_bitmap_create_scaled) (struct grub_video_bitmap **dst, + int dst_width, int dst_height, + struct grub_video_bitmap *src, + enum + grub_video_bitmap_scale_method + scale_method); + +#endif /* ! GRUB_BITMAP_SCALE_HEADER */ diff --git a/include/grub/bsdlabel.h b/include/grub/bsdlabel.h new file mode 100644 index 000000000..d88b25353 --- /dev/null +++ b/include/grub/bsdlabel.h @@ -0,0 +1,89 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2004,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_BSDLABEL_PARTITION_HEADER +#define GRUB_BSDLABEL_PARTITION_HEADER 1 + +/* Constants for BSD disk label. */ +#define GRUB_PC_PARTITION_BSD_LABEL_SECTOR 1 +#define GRUB_PC_PARTITION_BSD_LABEL_MAGIC 0x82564557 + +/* BSD partition types. */ +#define GRUB_PC_PARTITION_BSD_TYPE_UNUSED 0 +#define GRUB_PC_PARTITION_BSD_TYPE_SWAP 1 +#define GRUB_PC_PARTITION_BSD_TYPE_V6 2 +#define GRUB_PC_PARTITION_BSD_TYPE_V7 3 +#define GRUB_PC_PARTITION_BSD_TYPE_SYSV 4 +#define GRUB_PC_PARTITION_BSD_TYPE_V71K 5 +#define GRUB_PC_PARTITION_BSD_TYPE_V8 6 +#define GRUB_PC_PARTITION_BSD_TYPE_BSDFFS 7 +#define GRUB_PC_PARTITION_BSD_TYPE_MSDOS 8 +#define GRUB_PC_PARTITION_BSD_TYPE_BSDLFS 9 +#define GRUB_PC_PARTITION_BSD_TYPE_OTHER 10 +#define GRUB_PC_PARTITION_BSD_TYPE_HPFS 11 +#define GRUB_PC_PARTITION_BSD_TYPE_ISO9660 12 +#define GRUB_PC_PARTITION_BSD_TYPE_BOOT 13 + +/* FreeBSD-specific types. */ +#define GRUB_PC_PARTITION_FREEBSD_TYPE_VINUM 14 +#define GRUB_PC_PARTITION_FREEBSD_TYPE_RAID 15 +#define GRUB_PC_PARTITION_FREEBSD_TYPE_JFS2 21 + +/* NetBSD-specific types. */ +#define GRUB_PC_PARTITION_NETBSD_TYPE_ADOS 14 +#define GRUB_PC_PARTITION_NETBSD_TYPE_HFS 15 +#define GRUB_PC_PARTITION_NETBSD_TYPE_FILECORE 16 +#define GRUB_PC_PARTITION_NETBSD_TYPE_EXT2FS 17 +#define GRUB_PC_PARTITION_NETBSD_TYPE_NTFS 18 +#define GRUB_PC_PARTITION_NETBSD_TYPE_RAID 19 +#define GRUB_PC_PARTITION_NETBSD_TYPE_CCD 20 +#define GRUB_PC_PARTITION_NETBSD_TYPE_JFS2 21 +#define GRUB_PC_PARTITION_NETBSD_TYPE_APPLEUFS 22 + +/* OpenBSD-specific types. */ +#define GRUB_PC_PARTITION_OPENBSD_TYPE_ADOS 14 +#define GRUB_PC_PARTITION_OPENBSD_TYPE_HFS 15 +#define GRUB_PC_PARTITION_OPENBSD_TYPE_FILECORE 16 +#define GRUB_PC_PARTITION_OPENBSD_TYPE_EXT2FS 17 +#define GRUB_PC_PARTITION_OPENBSD_TYPE_NTFS 18 +#define GRUB_PC_PARTITION_OPENBSD_TYPE_RAID 19 + +/* The BSD partition entry. */ +struct grub_partition_bsd_entry +{ + grub_uint32_t size; + grub_uint32_t offset; + grub_uint32_t fragment_size; + grub_uint8_t fs_type; + grub_uint8_t fs_fragments; + grub_uint16_t fs_cylinders; +} __attribute__ ((packed)); + +/* The BSD disk label. Only define members useful for GRUB. */ +struct grub_partition_bsd_disk_label +{ + grub_uint32_t magic; + grub_uint8_t padding[128]; + grub_uint32_t magic2; + grub_uint16_t checksum; + grub_uint16_t num_partitions; + grub_uint32_t boot_size; + grub_uint32_t superblock_size; +} __attribute__ ((packed)); + +#endif /* ! GRUB_PC_PARTITION_HEADER */ diff --git a/include/grub/bufio.h b/include/grub/bufio.h index 9a2294c26..acdd0c882 100644 --- a/include/grub/bufio.h +++ b/include/grub/bufio.h @@ -22,7 +22,7 @@ #include -grub_file_t grub_bufio_open (grub_file_t io, int size); -grub_file_t grub_buffile_open (const char *name, int size); +grub_file_t EXPORT_FUNC (grub_bufio_open) (grub_file_t io, int size); +grub_file_t EXPORT_FUNC (grub_buffile_open) (const char *name, int size); #endif /* ! GRUB_BUFIO_H */ diff --git a/include/grub/cache.h b/include/grub/cache.h index 745af43c3..27e44f0a2 100644 --- a/include/grub/cache.h +++ b/include/grub/cache.h @@ -23,6 +23,14 @@ #include #include +#if defined (__i386__) || defined (__x86_64__) +static inline void +grub_arch_sync_caches (void *address __attribute__ ((unused)), + grub_size_t len __attribute__ ((unused))) +{ +} +#else void EXPORT_FUNC(grub_arch_sync_caches) (void *address, grub_size_t len); +#endif #endif /* ! GRUB_CACHE_HEADER */ diff --git a/include/grub/datetime.h b/include/grub/datetime.h index 2dbba55e2..e721e89af 100644 --- a/include/grub/datetime.h +++ b/include/grub/datetime.h @@ -33,10 +33,17 @@ struct grub_datetime }; /* Return date and time. */ +#ifdef GRUB_MACHINE_EMU +grub_err_t EXPORT_FUNC(grub_get_datetime) (struct grub_datetime *datetime); + +/* Set date and time. */ +grub_err_t EXPORT_FUNC(grub_set_datetime) (struct grub_datetime *datetime); +#else grub_err_t grub_get_datetime (struct grub_datetime *datetime); /* Set date and time. */ grub_err_t grub_set_datetime (struct grub_datetime *datetime); +#endif int grub_get_weekday (struct grub_datetime *datetime); char *grub_get_weekday_name (struct grub_datetime *datetime); diff --git a/include/grub/dl.h b/include/grub/dl.h index 9340b6ce4..cfb7c2f99 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -91,7 +91,12 @@ grub_dl_t grub_dl_load_core (void *addr, grub_size_t size); int EXPORT_FUNC(grub_dl_unload) (grub_dl_t mod); void grub_dl_unload_unneeded (void); void grub_dl_unload_all (void); -#ifdef GRUB_UTIL +#if defined (GRUB_UTIL) || defined (GRUB_TARGET_NO_MODULES) +#define GRUB_NO_MODULES 1 +#else +#define GRUB_NO_MODULES 0 +#endif +#if GRUB_NO_MODULES static inline int grub_dl_ref (grub_dl_t mod) { @@ -110,13 +115,13 @@ int EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod); #endif void EXPORT_FUNC(grub_dl_iterate) (int (*hook) (grub_dl_t mod)); grub_dl_t EXPORT_FUNC(grub_dl_get) (const char *name); -grub_err_t EXPORT_FUNC(grub_dl_register_symbol) (const char *name, void *addr, - grub_dl_t mod); +grub_err_t grub_dl_register_symbol (const char *name, void *addr, + grub_dl_t mod); grub_err_t grub_arch_dl_check_header (void *ehdr); grub_err_t grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr); -#if defined (_mips) && ! defined (GRUB_UTIL) +#if defined (_mips) && ! GRUB_NO_MODULES #define GRUB_LINKER_HAVE_INIT 1 void grub_arch_dl_init_linker (void); #endif diff --git a/include/grub/efi/memory.h b/include/grub/efi/memory.h index e5ea58d67..285be8359 100644 --- a/include/grub/efi/memory.h +++ b/include/grub/efi/memory.h @@ -34,9 +34,9 @@ by firmware. */ #define GRUB_MACHINE_MEMORY_HOLE 6 - -grub_err_t EXPORT_FUNC(grub_machine_mmap_iterate) -(int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, grub_uint64_t, grub_uint32_t)); +grub_err_t grub_machine_mmap_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, + grub_uint64_t, + grub_uint32_t)); grub_err_t grub_machine_mmap_register (grub_uint64_t start, grub_uint64_t size, int type, int handle); grub_err_t grub_machine_mmap_unregister (int handle); diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h index 21b56ae27..81a1a5797 100644 --- a/include/grub/efi/pe32.h +++ b/include/grub/efi/pe32.h @@ -99,12 +99,8 @@ struct grub_pe32_optional_header grub_uint32_t entry_addr; grub_uint32_t code_base; -#if GRUB_TARGET_SIZEOF_VOID_P == 4 grub_uint32_t data_base; grub_uint32_t image_base; -#else - grub_uint64_t image_base; -#endif grub_uint32_t section_alignment; grub_uint32_t file_alignment; @@ -121,22 +117,11 @@ struct grub_pe32_optional_header grub_uint16_t subsystem; grub_uint16_t dll_characteristics; -#if GRUB_TARGET_SIZEOF_VOID_P == 4 - grub_uint32_t stack_reserve_size; grub_uint32_t stack_commit_size; grub_uint32_t heap_reserve_size; grub_uint32_t heap_commit_size; -#else - - grub_uint64_t stack_reserve_size; - grub_uint64_t stack_commit_size; - grub_uint64_t heap_reserve_size; - grub_uint64_t heap_commit_size; - -#endif - grub_uint32_t loader_flags; grub_uint32_t num_data_directories; @@ -159,15 +144,63 @@ struct grub_pe32_optional_header struct grub_pe32_data_directory reserved_entry; }; -#if GRUB_TARGET_SIZEOF_VOID_P == 4 +struct grub_pe64_optional_header +{ + grub_uint16_t magic; + grub_uint8_t major_linker_version; + grub_uint8_t minor_linker_version; + grub_uint32_t code_size; + grub_uint32_t data_size; + grub_uint32_t bss_size; + grub_uint32_t entry_addr; + grub_uint32_t code_base; + + grub_uint64_t image_base; + + grub_uint32_t section_alignment; + grub_uint32_t file_alignment; + grub_uint16_t major_os_version; + grub_uint16_t minor_os_version; + grub_uint16_t major_image_version; + grub_uint16_t minor_image_version; + grub_uint16_t major_subsystem_version; + grub_uint16_t minor_subsystem_version; + grub_uint32_t reserved; + grub_uint32_t image_size; + grub_uint32_t header_size; + grub_uint32_t checksum; + grub_uint16_t subsystem; + grub_uint16_t dll_characteristics; + + grub_uint64_t stack_reserve_size; + grub_uint64_t stack_commit_size; + grub_uint64_t heap_reserve_size; + grub_uint64_t heap_commit_size; + + grub_uint32_t loader_flags; + grub_uint32_t num_data_directories; + + /* Data directories. */ + struct grub_pe32_data_directory export_table; + struct grub_pe32_data_directory import_table; + struct grub_pe32_data_directory resource_table; + struct grub_pe32_data_directory exception_table; + struct grub_pe32_data_directory certificate_table; + struct grub_pe32_data_directory base_relocation_table; + struct grub_pe32_data_directory debug; + struct grub_pe32_data_directory architecture; + struct grub_pe32_data_directory global_ptr; + struct grub_pe32_data_directory tls_table; + struct grub_pe32_data_directory load_config_table; + struct grub_pe32_data_directory bound_import; + struct grub_pe32_data_directory iat; + struct grub_pe32_data_directory delay_import_descriptor; + struct grub_pe32_data_directory com_runtime_header; + struct grub_pe32_data_directory reserved_entry; +}; #define GRUB_PE32_PE32_MAGIC 0x10b - -#else - -#define GRUB_PE32_PE32_MAGIC 0x20b - -#endif +#define GRUB_PE32_PE64_MAGIC 0x20b #define GRUB_PE32_SUBSYSTEM_EFI_APPLICATION 10 @@ -205,6 +238,7 @@ struct grub_pe32_section_table #define GRUB_PE32_SCN_ALIGN_SHIFT 20 #define GRUB_PE32_SCN_ALIGN_MASK 7 +#define GRUB_PE32_SIGNATURE_SIZE 4 struct grub_pe32_header { @@ -212,13 +246,18 @@ struct grub_pe32_header grub_uint8_t msdos_stub[GRUB_PE32_MSDOS_STUB_SIZE]; /* This is always PE\0\0. */ - char signature[4]; + char signature[GRUB_PE32_SIGNATURE_SIZE]; /* The COFF file header. */ struct grub_pe32_coff_header coff_header; +#if GRUB_TARGET_SIZEOF_VOID_P == 8 + /* The Optional header. */ + struct grub_pe64_optional_header optional_header; +#else /* The Optional header. */ struct grub_pe32_optional_header optional_header; +#endif }; struct grub_pe32_fixup_block diff --git a/include/grub/elf.h b/include/grub/elf.h index e54989cde..b9401f241 100644 --- a/include/grub/elf.h +++ b/include/grub/elf.h @@ -555,6 +555,7 @@ typedef struct #define PT_NUM 8 /* Number of defined types */ #define PT_LOOS 0x60000000 /* Start of OS-specific */ #define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define PT_GNU_STACK 0x6474e551 /* GCC stack segment */ #define PT_LOSUNW 0x6ffffffa #define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ #define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ diff --git a/include/grub/err.h b/include/grub/err.h index 7a5ce1ae0..e44705389 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -66,7 +66,7 @@ void EXPORT_FUNC(grub_fatal) (const char *fmt, ...) __attribute__ ((noreturn)); void EXPORT_FUNC(grub_error_push) (void); int EXPORT_FUNC(grub_error_pop) (void); void EXPORT_FUNC(grub_print_error) (void); -int EXPORT_FUNC(grub_err_printf) (const char *fmt, ...) -__attribute__ ((format (printf, 1, 2))); +int grub_err_printf (const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); #endif /* ! GRUB_ERR_HEADER */ diff --git a/include/grub/font.h b/include/grub/font.h index 1816e3570..7c5c17403 100644 --- a/include/grub/font.h +++ b/include/grub/font.h @@ -81,36 +81,38 @@ int grub_font_load (const char *filename); "Family Name Bold Italic 14", where Bold and Italic are optional. If no font matches the name specified, the most recently loaded font is returned as a fallback. */ -grub_font_t grub_font_get (const char *font_name); +grub_font_t EXPORT_FUNC (grub_font_get) (const char *font_name); -const char *grub_font_get_name (grub_font_t font); +const char *EXPORT_FUNC (grub_font_get_name) (grub_font_t font); -int grub_font_get_max_char_width (grub_font_t font); +int EXPORT_FUNC (grub_font_get_max_char_width) (grub_font_t font); -int grub_font_get_max_char_height (grub_font_t font); +int EXPORT_FUNC (grub_font_get_max_char_height) (grub_font_t font); -int grub_font_get_ascent (grub_font_t font); +int EXPORT_FUNC (grub_font_get_ascent) (grub_font_t font); -int grub_font_get_descent (grub_font_t font); +int EXPORT_FUNC (grub_font_get_descent) (grub_font_t font); -int grub_font_get_leading (grub_font_t font); +int EXPORT_FUNC (grub_font_get_leading) (grub_font_t font); -int grub_font_get_height (grub_font_t font); +int EXPORT_FUNC (grub_font_get_height) (grub_font_t font); -int grub_font_get_string_width (grub_font_t font, const char *str); +int EXPORT_FUNC (grub_font_get_string_width) (grub_font_t font, + const char *str); -struct grub_font_glyph *grub_font_get_glyph (grub_font_t font, - grub_uint32_t code); +struct grub_font_glyph *EXPORT_FUNC (grub_font_get_glyph) (grub_font_t font, + grub_uint32_t code); -struct grub_font_glyph *grub_font_get_glyph_with_fallback (grub_font_t font, - grub_uint32_t code); +struct grub_font_glyph *EXPORT_FUNC (grub_font_get_glyph_with_fallback) (grub_font_t font, + grub_uint32_t code); -grub_err_t grub_font_draw_glyph (struct grub_font_glyph *glyph, - grub_video_color_t color, - int left_x, int baseline_y); +grub_err_t EXPORT_FUNC (grub_font_draw_glyph) (struct grub_font_glyph *glyph, + grub_video_color_t color, + int left_x, int baseline_y); -grub_err_t grub_font_draw_string (const char *str, grub_font_t font, - grub_video_color_t color, - int left_x, int baseline_y); +grub_err_t EXPORT_FUNC (grub_font_draw_string) (const char *str, + grub_font_t font, + grub_video_color_t color, + int left_x, int baseline_y); #endif /* ! GRUB_FONT_HEADER */ diff --git a/include/grub/fontformat.h b/include/grub/fontformat.h new file mode 100644 index 000000000..b5060588c --- /dev/null +++ b/include/grub/fontformat.h @@ -0,0 +1,38 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_FONT_FORMAT_HEADER +#define GRUB_FONT_FORMAT_HEADER 1 + +/* FONT_FORMAT_PFF2_MAGIC use only 4 relevants bytes and the \0. */ +#define FONT_FORMAT_PFF2_MAGIC "PFF2" +#define FONT_FORMAT_SECTION_NAMES_FILE "FILE" +#define FONT_FORMAT_SECTION_NAMES_FONT_NAME "NAME" +#define FONT_FORMAT_SECTION_NAMES_POINT_SIZE "PTSZ" +#define FONT_FORMAT_SECTION_NAMES_WEIGHT "WEIG" +#define FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH "MAXW" +#define FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT "MAXH" +#define FONT_FORMAT_SECTION_NAMES_ASCENT "ASCE" +#define FONT_FORMAT_SECTION_NAMES_DESCENT "DESC" +#define FONT_FORMAT_SECTION_NAMES_CHAR_INDEX "CHIX" +#define FONT_FORMAT_SECTION_NAMES_DATA "DATA" +#define FONT_FORMAT_SECTION_NAMES_FAMILY "FAMI" +#define FONT_FORMAT_SECTION_NAMES_SLAN "SLAN" + +#endif /* ! GRUB_FONT_FORMAT_HEADER */ + diff --git a/include/grub/gfxmenu_model.h b/include/grub/gfxmenu_model.h new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/include/grub/gfxmenu_model.h @@ -0,0 +1 @@ + diff --git a/include/grub/gfxmenu_view.h b/include/grub/gfxmenu_view.h new file mode 100644 index 000000000..7cbfa89d3 --- /dev/null +++ b/include/grub/gfxmenu_view.h @@ -0,0 +1,107 @@ +/* gfxmenu_view.h - gfxmenu view interface. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_GFXMENU_VIEW_HEADER +#define GRUB_GFXMENU_VIEW_HEADER 1 + +#include +#include +#include +#include +#include + +struct grub_gfxmenu_view; /* Forward declaration of opaque type. */ +typedef struct grub_gfxmenu_view *grub_gfxmenu_view_t; + + +grub_gfxmenu_view_t grub_gfxmenu_view_new (const char *theme_path, + int width, int height); + +void grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view); + +/* Set properties on the view based on settings from the specified + theme file. */ +grub_err_t grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, + const char *theme_path); + +grub_err_t grub_gui_recreate_box (grub_gfxmenu_box_t *boxptr, + const char *pattern, const char *theme_dir); + +void grub_gfxmenu_view_draw (grub_gfxmenu_view_t view); + +void +grub_gfxmenu_redraw_menu (grub_gfxmenu_view_t view); + +void +grub_gfxmenu_redraw_timeout (grub_gfxmenu_view_t view); + +void +grub_gfxmenu_view_redraw (grub_gfxmenu_view_t view, + const grub_video_rect_t *region); + +void +grub_gfxmenu_clear_timeout (void *data); +void +grub_gfxmenu_print_timeout (int timeout, void *data); +void +grub_gfxmenu_set_chosen_entry (int entry, void *data); + +/* Implementation details -- this should not be used outside of the + view itself. */ + +#include +#include +#include +#include +#include + +/* Definition of the private representation of the view. */ +struct grub_gfxmenu_view +{ + grub_video_rect_t screen; + + grub_font_t title_font; + grub_font_t message_font; + char *terminal_font_name; + grub_gui_color_t title_color; + grub_gui_color_t message_color; + grub_gui_color_t message_bg_color; + struct grub_video_bitmap *desktop_image; + grub_gui_color_t desktop_color; + grub_gfxmenu_box_t terminal_box; + char *title_text; + char *progress_message_text; + char *theme_path; + + grub_gui_container_t canvas; + + int double_repaint; + + int selected; + + grub_video_rect_t progress_message_frame; + + grub_menu_t menu; + + int nested; + + int first_timeout; +}; + +#endif /* ! GRUB_GFXMENU_VIEW_HEADER */ diff --git a/include/grub/gfxterm.h b/include/grub/gfxterm.h new file mode 100644 index 000000000..295354baf --- /dev/null +++ b/include/grub/gfxterm.h @@ -0,0 +1,44 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_GFXTERM_HEADER +#define GRUB_GFXTERM_HEADER 1 + +#include +#include +#include +#include + +grub_err_t +EXPORT_FUNC (grub_gfxterm_set_window) (struct grub_video_render_target *target, + int x, int y, int width, int height, + int double_repaint, + const char *font_name, int border_width); + +typedef void (*grub_gfxterm_repaint_callback_t)(int x, int y, + int width, int height); + +void grub_gfxterm_set_repaint_callback (grub_gfxterm_repaint_callback_t func); + +void EXPORT_FUNC (grub_gfxterm_schedule_repaint) (void); + +grub_err_t EXPORT_FUNC (grub_gfxterm_fullscreen) (void); + +extern void (*EXPORT_VAR (grub_gfxterm_decorator_hook)) (void); + +#endif /* ! GRUB_GFXTERM_HEADER */ diff --git a/include/grub/gfxwidgets.h b/include/grub/gfxwidgets.h new file mode 100644 index 000000000..f9678bf9e --- /dev/null +++ b/include/grub/gfxwidgets.h @@ -0,0 +1,49 @@ +/* gfxwidgets.h - Widgets for the graphical menu (gfxmenu). */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_GFXWIDGETS_HEADER +#define GRUB_GFXWIDGETS_HEADER 1 + +#include + +typedef struct grub_gfxmenu_box *grub_gfxmenu_box_t; + +struct grub_gfxmenu_box +{ + /* The size of the content. */ + int content_width; + int content_height; + + struct grub_video_bitmap **raw_pixmaps; + struct grub_video_bitmap **scaled_pixmaps; + + void (*draw) (grub_gfxmenu_box_t self, int x, int y); + void (*set_content_size) (grub_gfxmenu_box_t self, + int width, int height); + int (*get_left_pad) (grub_gfxmenu_box_t self); + int (*get_top_pad) (grub_gfxmenu_box_t self); + int (*get_right_pad) (grub_gfxmenu_box_t self); + int (*get_bottom_pad) (grub_gfxmenu_box_t self); + void (*destroy) (grub_gfxmenu_box_t self); +}; + +grub_gfxmenu_box_t grub_gfxmenu_create_box (const char *pixmaps_prefix, + const char *pixmaps_suffix); + +#endif /* ! GRUB_GFXWIDGETS_HEADER */ diff --git a/include/grub/gui.h b/include/grub/gui.h new file mode 100644 index 000000000..7bd71acd3 --- /dev/null +++ b/include/grub/gui.h @@ -0,0 +1,230 @@ +/* gui.h - GUI components header file. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +#ifndef GRUB_GUI_H +#define GRUB_GUI_H 1 + +/* A representation of a color. Unlike grub_video_color_t, this + representation is independent of any video mode specifics. */ +typedef struct grub_gui_color +{ + grub_uint8_t red; + grub_uint8_t green; + grub_uint8_t blue; + grub_uint8_t alpha; +} grub_gui_color_t; + +typedef struct grub_gui_component *grub_gui_component_t; +typedef struct grub_gui_container *grub_gui_container_t; +typedef struct grub_gui_list *grub_gui_list_t; + +typedef void (*grub_gui_component_callback) (grub_gui_component_t component, + void *userdata); + +/* Component interface. */ + +struct grub_gui_component_ops +{ + void (*destroy) (void *self); + const char * (*get_id) (void *self); + int (*is_instance) (void *self, const char *type); + void (*paint) (void *self, const grub_video_rect_t *bounds); + void (*set_parent) (void *self, grub_gui_container_t parent); + grub_gui_container_t (*get_parent) (void *self); + void (*set_bounds) (void *self, const grub_video_rect_t *bounds); + void (*get_bounds) (void *self, grub_video_rect_t *bounds); + void (*get_minimal_size) (void *self, unsigned *width, unsigned *height); + grub_err_t (*set_property) (void *self, const char *name, const char *value); + void (*repaint) (void *self, int second_pass); +}; + +struct grub_gui_container_ops +{ + void (*add) (void *self, grub_gui_component_t comp); + void (*remove) (void *self, grub_gui_component_t comp); + void (*iterate_children) (void *self, + grub_gui_component_callback cb, void *userdata); +}; + +struct grub_gui_list_ops +{ + void (*set_view_info) (void *self, + grub_gfxmenu_view_t view); +}; + +struct grub_gui_progress_ops +{ + void (*set_state) (void *self, int visible, int start, int current, int end); +}; + +typedef signed grub_fixed_signed_t; +#define GRUB_FIXED_1 0x10000 + +static inline signed +grub_fixed_sfs_divide (signed a, grub_fixed_signed_t b) +{ + return (a * GRUB_FIXED_1) / b; +} + +static inline grub_fixed_signed_t +grub_fixed_fsf_divide (grub_fixed_signed_t a, signed b) +{ + return a / b; +} + +static inline signed +grub_fixed_sfs_multiply (signed a, grub_fixed_signed_t b) +{ + return (a * b) / GRUB_FIXED_1; +} + +static inline signed +grub_fixed_to_signed (grub_fixed_signed_t in) +{ + return in / GRUB_FIXED_1; +} + +static inline grub_fixed_signed_t +grub_signed_to_fixed (signed in) +{ + return in * GRUB_FIXED_1; +} + +struct grub_gui_component +{ + struct grub_gui_component_ops *ops; + signed x; + grub_fixed_signed_t xfrac; + signed y; + grub_fixed_signed_t yfrac; + signed w; + grub_fixed_signed_t wfrac; + signed h; + grub_fixed_signed_t hfrac; +}; + +struct grub_gui_progress +{ + struct grub_gui_component component; + struct grub_gui_progress_ops *ops; +}; + +struct grub_gui_container +{ + struct grub_gui_component component; + struct grub_gui_container_ops *ops; +}; + +struct grub_gui_list +{ + struct grub_gui_component component; + struct grub_gui_list_ops *ops; +}; + + +/* Interfaces to concrete component classes. */ + +grub_gui_container_t grub_gui_canvas_new (void); +grub_gui_container_t grub_gui_vbox_new (void); +grub_gui_container_t grub_gui_hbox_new (void); +grub_gui_component_t grub_gui_label_new (void); +grub_gui_component_t grub_gui_image_new (void); +grub_gui_component_t grub_gui_progress_bar_new (void); +grub_gui_component_t grub_gui_list_new (void); +grub_gui_component_t grub_gui_circular_progress_new (void); + +/* Manipulation functions. */ + +/* Visit all components with the specified ID. */ +void grub_gui_find_by_id (grub_gui_component_t root, + const char *id, + grub_gui_component_callback cb, + void *userdata); + +/* Visit all components. */ +void grub_gui_iterate_recursively (grub_gui_component_t root, + grub_gui_component_callback cb, + void *userdata); + +/* Helper functions. */ + +static __inline void +grub_gui_save_viewport (grub_video_rect_t *r) +{ + grub_video_get_viewport ((unsigned *) &r->x, + (unsigned *) &r->y, + (unsigned *) &r->width, + (unsigned *) &r->height); +} + +static __inline void +grub_gui_restore_viewport (const grub_video_rect_t *r) +{ + grub_video_set_viewport (r->x, r->y, r->width, r->height); +} + +/* Set a new viewport relative the the current one, saving the current + viewport in OLD so it can be later restored. */ +static __inline void +grub_gui_set_viewport (const grub_video_rect_t *r, grub_video_rect_t *old) +{ + grub_gui_save_viewport (old); + grub_video_set_viewport (old->x + r->x, + old->y + r->y, + r->width, + r->height); +} + +static __inline grub_gui_color_t +grub_gui_color_rgb (int r, int g, int b) +{ + grub_gui_color_t c; + c.red = r; + c.green = g; + c.blue = b; + c.alpha = 255; + return c; +} + +static __inline grub_video_color_t +grub_gui_map_color (grub_gui_color_t c) +{ + return grub_video_map_rgba (c.red, c.green, c.blue, c.alpha); +} + +static inline int +grub_video_have_common_points (const grub_video_rect_t *a, + const grub_video_rect_t *b) +{ + if (!((a->x <= b->x && b->x <= a->x + a->width) + || (b->x <= a->x && a->x <= b->x + b->width))) + return 0; + if (!((a->y <= b->y && b->y <= a->y + a->height) + || (b->y <= a->y && a->y <= b->y + b->height))) + return 0; + return 1; +} + +#endif /* ! GRUB_GUI_H */ diff --git a/include/grub/gui_string_util.h b/include/grub/gui_string_util.h new file mode 100644 index 000000000..1baa2eede --- /dev/null +++ b/include/grub/gui_string_util.h @@ -0,0 +1,37 @@ +/* gui_string_util.h - String utilities for the graphical menu interface. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_GUI_STRING_UTIL_HEADER +#define GRUB_GUI_STRING_UTIL_HEADER 1 + +#include +#include + +char *grub_new_substring (const char *buf, + grub_size_t start, grub_size_t end); + +char *grub_resolve_relative_path (const char *base, const char *path); + +char *grub_get_dirname (const char *file_path); + +int grub_gui_get_named_color (const char *name, grub_gui_color_t *color); + +grub_err_t grub_gui_parse_color (const char *s, grub_gui_color_t *color); + +#endif /* GRUB_GUI_STRING_UTIL_HEADER */ diff --git a/include/grub/i18n.h b/include/grub/i18n.h index 4d4a0b7bd..9e7f52d45 100644 --- a/include/grub/i18n.h +++ b/include/grub/i18n.h @@ -22,11 +22,10 @@ #include #include -const char *EXPORT_FUNC(grub_gettext_dummy) (const char *s); extern const char *(*EXPORT_VAR(grub_gettext)) (const char *s); /* NLS can be disabled through the configure --disable-nls option. */ -#if ENABLE_NLS +#if (defined(ENABLE_NLS) && ENABLE_NLS) # ifdef GRUB_UTIL @@ -35,7 +34,7 @@ extern const char *(*EXPORT_VAR(grub_gettext)) (const char *s); # endif /* GRUB_UTIL */ -#else /* ! ENABLE_NLS */ +#else /* ! (defined(ENABLE_NLS) && ENABLE_NLS) */ /* Disabled NLS. The casts to 'const char *' serve the purpose of producing warnings @@ -48,7 +47,7 @@ extern const char *(*EXPORT_VAR(grub_gettext)) (const char *s); # define grub_gettext(str) ((const char *) (str)) # endif /* GRUB_UTIL */ -#endif /* ENABLE_NLS */ +#endif /* (defined(ENABLE_NLS) && ENABLE_NLS) */ #ifdef GRUB_UTIL # define _(str) gettext(str) diff --git a/include/grub/i386/bsd.h b/include/grub/i386/bsd.h index e26c35652..4d55f04fb 100644 --- a/include/grub/i386/bsd.h +++ b/include/grub/i386/bsd.h @@ -44,33 +44,25 @@ enum bsd_kernel_types #define FREEBSD_B_PARTSHIFT OPENBSD_B_PARTSHIFT #define FREEBSD_B_TYPESHIFT OPENBSD_B_TYPESHIFT -#define FREEBSD_BOOTINFO_VERSION 1 -#define FREEBSD_N_BIOS_GEOM 8 - #define FREEBSD_MODTYPE_KERNEL "elf kernel" #define FREEBSD_MODTYPE_KERNEL64 "elf64 kernel" #define FREEBSD_MODTYPE_ELF_MODULE "elf module" #define FREEBSD_MODTYPE_ELF_MODULE_OBJ "elf obj module" #define FREEBSD_MODTYPE_RAW "raw" +#define FREEBSD_BOOTINFO_VERSION 1 + struct grub_freebsd_bootinfo { - grub_uint32_t bi_version; - grub_uint8_t *bi_kernelname; - struct nfs_diskless *bi_nfs_diskless; - grub_uint32_t bi_n_bios_used; - grub_uint32_t bi_bios_geom[FREEBSD_N_BIOS_GEOM]; - grub_uint32_t bi_size; - grub_uint8_t bi_memsizes_valid; - grub_uint8_t bi_bios_dev; - grub_uint8_t bi_pad[2]; - grub_uint32_t bi_basemem; - grub_uint32_t bi_extmem; - grub_uint32_t bi_symtab; - grub_uint32_t bi_esymtab; - grub_uint32_t bi_kernend; - grub_uint32_t bi_envp; - grub_uint32_t bi_modulep; + grub_uint32_t version; + grub_uint8_t unused1[44]; + grub_uint32_t length; + grub_uint8_t unused2; + grub_uint8_t boot_device; + grub_uint8_t unused3[18]; + grub_uint32_t kern_end; + grub_uint32_t environment; + grub_uint32_t tags; } __attribute__ ((packed)); struct grub_openbsd_bios_mmap diff --git a/include/grub/i386/efi/serial.h b/include/grub/i386/efi/serial.h new file mode 100644 index 000000000..2d8563414 --- /dev/null +++ b/include/grub/i386/efi/serial.h @@ -0,0 +1 @@ +#include diff --git a/include/grub/i386/ieee1275/kernel.h b/include/grub/i386/ieee1275/kernel.h deleted file mode 100644 index dccf8cbe1..000000000 --- a/include/grub/i386/ieee1275/kernel.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/grub/i386/multiboot.h b/include/grub/i386/multiboot.h index 584955449..1c711fad4 100644 --- a/include/grub/i386/multiboot.h +++ b/include/grub/i386/multiboot.h @@ -19,17 +19,23 @@ #ifndef GRUB_MULTIBOOT_CPU_HEADER #define GRUB_MULTIBOOT_CPU_HEADER 1 -/* The asm part of the multiboot loader. */ -void grub_multiboot_real_boot (grub_addr_t entry, - struct multiboot_info *mbi) - __attribute__ ((noreturn)); -void grub_multiboot2_real_boot (grub_addr_t entry, - struct multiboot_info *mbi) - __attribute__ ((noreturn)); - extern grub_uint32_t grub_multiboot_payload_eip; extern char *grub_multiboot_payload_orig; extern grub_addr_t grub_multiboot_payload_dest; extern grub_size_t grub_multiboot_payload_size; +#define MULTIBOOT_INITIAL_STATE { .eax = MULTIBOOT_BOOTLOADER_MAGIC, \ + .ecx = 0, \ + .edx = 0, \ + /* Set esp to some random location in low memory to avoid breaking */ \ + /* non-compliant kernels. */ \ + .esp = 0x7ff00 \ + } +#define MULTIBOOT_ENTRY_REGISTER eip +#define MULTIBOOT_MBI_REGISTER ebx +#define MULTIBOOT_ARCHITECTURE_CURRENT MULTIBOOT_ARCHITECTURE_I386 + +#define MULTIBOOT_ELF32_MACHINE EM_386 +#define MULTIBOOT_ELF64_MACHINE EM_X86_64 + #endif /* ! GRUB_MULTIBOOT_CPU_HEADER */ diff --git a/include/grub/i386/pc/boot.h b/include/grub/i386/pc/boot.h index 508b10742..a4d42ff3c 100644 --- a/include/grub/i386/pc/boot.h +++ b/include/grub/i386/pc/boot.h @@ -19,6 +19,8 @@ #ifndef GRUB_BOOT_MACHINE_HEADER #define GRUB_BOOT_MACHINE_HEADER 1 +#include + /* The signature for bootloader. */ #define GRUB_BOOT_MACHINE_SIGNATURE 0xaa55 @@ -57,25 +59,15 @@ floppy. */ #define GRUB_BOOT_MACHINE_BIOS_HD_FLAG 0x80 -/* The segment where the kernel is loaded. */ -#define GRUB_BOOT_MACHINE_KERNEL_SEG 0x800 - /* The address where the kernel is loaded. */ #define GRUB_BOOT_MACHINE_KERNEL_ADDR (GRUB_BOOT_MACHINE_KERNEL_SEG << 4) /* The size of a block list used in the kernel startup code. */ #define GRUB_BOOT_MACHINE_LIST_SIZE 12 -#ifndef ASM_FILE +#define GRUB_BOOT_MACHINE_PXE_DL 0x7f /* This is the blocklist used in the diskboot image. */ -struct grub_boot_blocklist -{ - grub_uint64_t start; - grub_uint16_t len; - grub_uint16_t segment; -} __attribute__ ((packed)); - -#endif /* ! ASM_FILE */ +#define grub_boot_blocklist grub_pc_bios_boot_blocklist #endif /* ! BOOT_MACHINE_HEADER */ diff --git a/include/grub/i386/pc/init.h b/include/grub/i386/pc/init.h index 2be80e773..30130d189 100644 --- a/include/grub/i386/pc/init.h +++ b/include/grub/i386/pc/init.h @@ -33,7 +33,7 @@ grub_uint32_t grub_get_eisa_mmap (void); /* Get a memory map entry. Return next continuation value. Zero means the end. */ -grub_uint32_t EXPORT_FUNC(grub_get_mmap_entry) (struct grub_machine_mmap_entry *entry, +grub_uint32_t grub_get_mmap_entry (struct grub_machine_mmap_entry *entry, grub_uint32_t cont); /* Turn on/off Gate A20. */ diff --git a/include/grub/i386/pc/kernel.h b/include/grub/i386/pc/kernel.h index e830afae2..1de37a5d5 100644 --- a/include/grub/i386/pc/kernel.h +++ b/include/grub/i386/pc/kernel.h @@ -19,29 +19,7 @@ #ifndef KERNEL_MACHINE_HEADER #define KERNEL_MACHINE_HEADER 1 -/* The offset of GRUB_TOTAL_MODULE_SIZE. */ -#define GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE 0x8 - -/* The offset of GRUB_KERNEL_IMAGE_SIZE. */ -#define GRUB_KERNEL_MACHINE_KERNEL_IMAGE_SIZE 0xc - -/* The offset of GRUB_COMPRESSED_SIZE. */ -#define GRUB_KERNEL_MACHINE_COMPRESSED_SIZE 0x10 - -/* The offset of GRUB_INSTALL_DOS_PART. */ -#define GRUB_KERNEL_MACHINE_INSTALL_DOS_PART 0x14 - -/* The offset of GRUB_INSTALL_BSD_PART. */ -#define GRUB_KERNEL_MACHINE_INSTALL_BSD_PART 0x18 - -/* The offset of GRUB_PREFIX. */ -#define GRUB_KERNEL_MACHINE_PREFIX 0x1c - -/* End of the data section. */ -#define GRUB_KERNEL_MACHINE_DATA_END 0x5c - -/* The size of the first region which won't be compressed. */ -#define GRUB_KERNEL_MACHINE_RAW_SIZE (GRUB_KERNEL_MACHINE_DATA_END + 0x5F0) +#include /* Enable LZMA compression */ #define ENABLE_LZMA 1 @@ -63,10 +41,6 @@ extern grub_int32_t grub_install_dos_part; /* The BSD partition number of the installed partition. */ extern grub_int32_t grub_install_bsd_part; -/* The prefix which points to the directory where GRUB modules and its - configuration file are located. */ -extern char grub_prefix[]; - /* The boot BIOS drive number. */ extern grub_uint8_t EXPORT_VAR(grub_boot_drive); diff --git a/include/grub/i386/pc/memory.h b/include/grub/i386/pc/memory.h index 841b06164..68f5e8bc9 100644 --- a/include/grub/i386/pc/memory.h +++ b/include/grub/i386/pc/memory.h @@ -29,6 +29,8 @@ #include +#include + /* The scratch buffer used in real mode code. */ #define GRUB_MEMORY_MACHINE_SCRATCH_ADDR 0x68000 #define GRUB_MEMORY_MACHINE_SCRATCH_SEG (GRUB_MEMORY_MACHINE_SCRATCH_ADDR >> 4) @@ -40,9 +42,6 @@ /* The size of the protect mode stack. */ #define GRUB_MEMORY_MACHINE_PROT_STACK_SIZE 0x8000 -/* The upper memory area (starting at 640 kiB). */ -#define GRUB_MEMORY_MACHINE_UPPER 0xa0000 - /* The protected mode stack. */ #define GRUB_MEMORY_MACHINE_PROT_STACK \ (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + GRUB_MEMORY_MACHINE_SCRATCH_SIZE \ diff --git a/include/grub/i386/pc/vbe.h b/include/grub/i386/pc/vbe.h index 9c4c4dd3d..abf246fa1 100644 --- a/include/grub/i386/pc/vbe.h +++ b/include/grub/i386/pc/vbe.h @@ -19,8 +19,6 @@ #ifndef GRUB_VBE_MACHINE_HEADER #define GRUB_VBE_MACHINE_HEADER 1 -#include - /* Default video mode to be used. */ #define GRUB_VBE_DEFAULT_VIDEO_MODE 0x101 diff --git a/include/grub/i386/pc/vga.h b/include/grub/i386/pc/vga.h index b9822395b..2724f6401 100644 --- a/include/grub/i386/pc/vga.h +++ b/include/grub/i386/pc/vga.h @@ -28,7 +28,4 @@ /* Set the video mode to MODE and return the previous mode. */ unsigned char EXPORT_FUNC(grub_vga_set_mode) (unsigned char mode); -/* Return a pointer to the ROM font table. */ -unsigned char *EXPORT_FUNC(grub_vga_get_font) (void); - #endif /* ! GRUB_VGA_MACHINE_HEADER */ diff --git a/include/grub/i386/qemu/boot.h b/include/grub/i386/qemu/boot.h index 6fbb57750..5f53eabfb 100644 --- a/include/grub/i386/qemu/boot.h +++ b/include/grub/i386/qemu/boot.h @@ -22,7 +22,4 @@ /* The size of boot.img. */ #define GRUB_BOOT_MACHINE_SIZE (0x100000 - GRUB_BOOT_MACHINE_LINK_ADDR) -/* The offset of GRUB_CORE_ENTRY_ADDR. */ -#define GRUB_BOOT_MACHINE_CORE_ENTRY_ADDR 0x4 - #endif diff --git a/include/grub/i386/qemu/kernel.h b/include/grub/i386/qemu/kernel.h index bc0b93d4f..0aa2b3d09 100644 --- a/include/grub/i386/qemu/kernel.h +++ b/include/grub/i386/qemu/kernel.h @@ -19,17 +19,7 @@ #ifndef GRUB_KERNEL_MACHINE_HEADER #define GRUB_KERNEL_MACHINE_HEADER 1 -/* The offset of GRUB_CORE_ENTRY_ADDR. */ -#define GRUB_KERNEL_MACHINE_CORE_ENTRY_ADDR 0x8 - -/* The offset of GRUB_KERNEL_IMAGE_SIZE. */ -#define GRUB_KERNEL_MACHINE_KERNEL_IMAGE_SIZE 0xc - -/* The offset of GRUB_PREFIX. */ -#define GRUB_KERNEL_MACHINE_PREFIX 0x10 - -/* End of the data section. */ -#define GRUB_KERNEL_MACHINE_DATA_END 0x50 +#include #ifndef ASM_FILE @@ -44,10 +34,6 @@ extern grub_int32_t grub_kernel_image_size; /* The total size of module images following the kernel. */ extern grub_int32_t grub_total_module_size; -/* The prefix which points to the directory where GRUB modules and its - configuration file are located. */ -extern char grub_prefix[]; - #endif /* ! ASM_FILE */ #endif /* ! GRUB_KERNEL_MACHINE_HEADER */ diff --git a/include/grub/icon_manager.h b/include/grub/icon_manager.h new file mode 100644 index 000000000..81c488416 --- /dev/null +++ b/include/grub/icon_manager.h @@ -0,0 +1,41 @@ +/* icon_manager.h - gfxmenu icon manager. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_ICON_MANAGER_HEADER +#define GRUB_ICON_MANAGER_HEADER 1 + +#include +#include + +/* Forward declaration of opaque structure handle type. */ +typedef struct grub_gfxmenu_icon_manager *grub_gfxmenu_icon_manager_t; + +grub_gfxmenu_icon_manager_t grub_gfxmenu_icon_manager_new (void); +void grub_gfxmenu_icon_manager_destroy (grub_gfxmenu_icon_manager_t mgr); +void grub_gfxmenu_icon_manager_clear_cache (grub_gfxmenu_icon_manager_t mgr); +void grub_gfxmenu_icon_manager_set_theme_path (grub_gfxmenu_icon_manager_t mgr, + const char *path); +void grub_gfxmenu_icon_manager_set_icon_size (grub_gfxmenu_icon_manager_t mgr, + int width, int height); +struct grub_video_bitmap * +grub_gfxmenu_icon_manager_get_icon (grub_gfxmenu_icon_manager_t mgr, + grub_menu_entry_t entry); + +#endif /* GRUB_ICON_MANAGER_HEADER */ + diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index 8b31e32e2..2b2c36f8f 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -138,7 +138,7 @@ int EXPORT_FUNC(grub_ieee1275_read) (grub_ieee1275_ihandle_t ihandle, void *buffer, grub_size_t len, grub_ssize_t *actualp); int EXPORT_FUNC(grub_ieee1275_seek) (grub_ieee1275_ihandle_t ihandle, - int pos_hi, int pos_lo, + grub_disk_addr_t pos, grub_ssize_t *result); int EXPORT_FUNC(grub_ieee1275_peer) (grub_ieee1275_phandle_t node, grub_ieee1275_phandle_t *result); @@ -173,7 +173,15 @@ grub_err_t EXPORT_FUNC(grub_machine_mmap_iterate) (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, grub_uint64_t, grub_uint32_t)); int EXPORT_FUNC(grub_claimmap) (grub_addr_t addr, grub_size_t size); +int +EXPORT_FUNC(grub_ieee1275_map) (grub_addr_t phys, grub_addr_t virt, + grub_size_t size, grub_uint32_t mode); + char *EXPORT_FUNC(grub_ieee1275_encode_devname) (const char *path); char *EXPORT_FUNC(grub_ieee1275_get_filename) (const char *path); +int EXPORT_FUNC(grub_ieee1275_devices_iterate) (int (*hook) + (struct grub_ieee1275_devalias * + alias)); + #endif /* ! GRUB_IEEE1275_HEADER */ diff --git a/include/grub/kernel.h b/include/grub/kernel.h index 9586a90b9..fed875db1 100644 --- a/include/grub/kernel.h +++ b/include/grub/kernel.h @@ -42,23 +42,39 @@ struct grub_module_header /* "gmim" (GRUB Module Info Magic). */ #define GRUB_MODULE_MAGIC 0x676d696d -struct grub_module_info +struct grub_module_info32 { /* Magic number so we know we have modules present. */ grub_uint32_t magic; -#if GRUB_TARGET_SIZEOF_VOID_P == 8 - grub_uint32_t padding; -#endif /* The offset of the modules. */ - grub_target_off_t offset; + grub_uint32_t offset; /* The size of all modules plus this header. */ - grub_target_size_t size; + grub_uint32_t size; }; +struct grub_module_info64 +{ + /* Magic number so we know we have modules present. */ + grub_uint32_t magic; + grub_uint32_t padding; + /* The offset of the modules. */ + grub_uint64_t offset; + /* The size of all modules plus this header. */ + grub_uint64_t size; +}; + +#if GRUB_TARGET_SIZEOF_VOID_P == 8 +#define grub_module_info grub_module_info64 +#else +#define grub_module_info grub_module_info32 +#endif + extern grub_addr_t grub_arch_modules_addr (void); extern void EXPORT_FUNC(grub_module_iterate) (int (*hook) (struct grub_module_header *)); +grub_addr_t grub_modules_get_end (void); + /* The start point of the C code. */ void grub_main (void); @@ -74,4 +90,8 @@ void grub_machine_set_prefix (void); /* Register all the exported symbols. This is automatically generated. */ void grub_register_exported_symbols (void); +#if ! defined (ASM_FILE) && !defined (GRUB_MACHINE_EMU) +extern char grub_prefix[]; +#endif + #endif /* ! GRUB_KERNEL_HEADER */ diff --git a/include/grub/libgcc.h b/include/grub/libgcc.h new file mode 100644 index 000000000..d0adae8c1 --- /dev/null +++ b/include/grub/libgcc.h @@ -0,0 +1,84 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2004,2007,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include + +/* On x86 these functions aren't really needed. Save some space. */ +#if !defined (__i386__) && !defined (__x86_64__) +# ifdef HAVE___ASHLDI3 +void EXPORT_FUNC (__ashldi3) (void); +# endif +# ifdef HAVE___ASHRDI3 +void EXPORT_FUNC (__ashrdi3) (void); +# endif +# ifdef HAVE___LSHRDI3 +void EXPORT_FUNC (__lshrdi3) (void); +# endif +# ifdef HAVE___UCMPDI2 +void EXPORT_FUNC (__ucmpdi2) (void); +# endif +# ifdef HAVE___BSWAPSI2 +void EXPORT_FUNC (__bswapsi2) (void); +# endif +# ifdef HAVE___BSWAPDI2 +void EXPORT_FUNC (__bswapdi2) (void); +# endif +#endif + +#ifdef HAVE___TRAMPOLINE_SETUP +void EXPORT_FUNC (__trampoline_setup) (void); +#endif + +#ifdef HAVE__RESTGPR_14_X +void EXPORT_FUNC (_restgpr_14_x) (void); +void EXPORT_FUNC (_restgpr_15_x) (void); +void EXPORT_FUNC (_restgpr_16_x) (void); +void EXPORT_FUNC (_restgpr_17_x) (void); +void EXPORT_FUNC (_restgpr_18_x) (void); +void EXPORT_FUNC (_restgpr_19_x) (void); +void EXPORT_FUNC (_restgpr_20_x) (void); +void EXPORT_FUNC (_restgpr_21_x) (void); +void EXPORT_FUNC (_restgpr_22_x) (void); +void EXPORT_FUNC (_restgpr_23_x) (void); +void EXPORT_FUNC (_restgpr_24_x) (void); +void EXPORT_FUNC (_restgpr_25_x) (void); +void EXPORT_FUNC (_restgpr_26_x) (void); +void EXPORT_FUNC (_restgpr_27_x) (void); +void EXPORT_FUNC (_restgpr_28_x) (void); +void EXPORT_FUNC (_restgpr_29_x) (void); +void EXPORT_FUNC (_restgpr_30_x) (void); +void EXPORT_FUNC (_restgpr_31_x) (void); +void EXPORT_FUNC (_savegpr_14) (void); +void EXPORT_FUNC (_savegpr_15) (void); +void EXPORT_FUNC (_savegpr_16) (void); +void EXPORT_FUNC (_savegpr_17) (void); +void EXPORT_FUNC (_savegpr_18) (void); +void EXPORT_FUNC (_savegpr_19) (void); +void EXPORT_FUNC (_savegpr_20) (void); +void EXPORT_FUNC (_savegpr_21) (void); +void EXPORT_FUNC (_savegpr_22) (void); +void EXPORT_FUNC (_savegpr_23) (void); +void EXPORT_FUNC (_savegpr_24) (void); +void EXPORT_FUNC (_savegpr_25) (void); +void EXPORT_FUNC (_savegpr_26) (void); +void EXPORT_FUNC (_savegpr_27) (void); +void EXPORT_FUNC (_savegpr_28) (void); +void EXPORT_FUNC (_savegpr_29) (void); +void EXPORT_FUNC (_savegpr_30) (void); +void EXPORT_FUNC (_savegpr_31) (void); +#endif diff --git a/include/grub/powerpc/libgcc.h b/include/grub/libpciaccess.h similarity index 60% rename from include/grub/powerpc/libgcc.h rename to include/grub/libpciaccess.h index 452ad4366..4d2b3bde5 100644 --- a/include/grub/powerpc/libgcc.h +++ b/include/grub/libpciaccess.h @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2004,2007,2009 Free Software Foundation, Inc. + * Copyright (C) 2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,20 +16,11 @@ * along with GRUB. If not, see . */ -#include - -#ifdef HAVE___ASHLDI3 -void EXPORT_FUNC (__ashldi3) (void); -#endif -#ifdef HAVE___ASHRDI3 -void EXPORT_FUNC (__ashrdi3) (void); -#endif -#ifdef HAVE___LSHRDI3 -void EXPORT_FUNC (__lshrdi3) (void); -#endif -#ifdef HAVE___TRAMPOLINE_SETUP -void EXPORT_FUNC (__trampoline_setup) (void); -#endif -#ifdef HAVE___UCMPDI2 -void EXPORT_FUNC (__ucmpdi2) (void); -#endif +void EXPORT_FUNC (pci_slot_match_iterator_create) (void); +void EXPORT_FUNC (pci_system_cleanup) (void); +void EXPORT_FUNC (pci_device_unmap_range) (void); +void EXPORT_FUNC (pci_iterator_destroy) (void); +void EXPORT_FUNC (pci_device_map_range) (void); +void EXPORT_FUNC (pci_device_cfg_read_u32) (void); +void EXPORT_FUNC (pci_device_next) (void); +void EXPORT_FUNC (pci_system_init) (void); diff --git a/include/grub/i386/efi/kernel.h b/include/grub/libusb.h similarity index 56% rename from include/grub/i386/efi/kernel.h rename to include/grub/libusb.h index c0549f41a..26548bccb 100644 --- a/include/grub/i386/efi/kernel.h +++ b/include/grub/libusb.h @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2002,2003,2007 Free Software Foundation, Inc. + * Copyright (C) 2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,18 +16,14 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_MACHINE_KERNEL_HEADER -#define GRUB_MACHINE_KERNEL_HEADER 1 - -/* The prefix which points to the directory where GRUB modules and its - configuration file are located. */ -extern char grub_prefix[]; - -/* The offset of GRUB_PREFIX. */ -#define GRUB_KERNEL_MACHINE_PREFIX 0x8 - -/* End of the data section. */ -#define GRUB_KERNEL_MACHINE_DATA_END 0x50 - -#endif /* ! GRUB_MACHINE_KERNEL_HEADER */ - +void EXPORT_FUNC (usb_bulk_write) (void); +void EXPORT_FUNC (usb_find_busses) (void); +void EXPORT_FUNC (usb_init) (void); +void EXPORT_FUNC (usb_find_devices) (void); +void EXPORT_FUNC (usb_open) (void); +void EXPORT_FUNC (usb_get_busses) (void); +void EXPORT_FUNC (usb_control_msg) (void); +void EXPORT_FUNC (usb_release_interface) (void); +void EXPORT_FUNC (usb_close) (void); +void EXPORT_FUNC (usb_bulk_read) (void); +void EXPORT_FUNC (usb_claim_interface) (void); diff --git a/include/grub/list.h b/include/grub/list.h index b7703e214..5559158dc 100644 --- a/include/grub/list.h +++ b/include/grub/list.h @@ -52,7 +52,7 @@ grub_bad_type_cast_real (int line, const char *file) return 0; } -#define grub_bad_type_cast() grub_bad_type_cast_real(__LINE__, __FILE__) +#define grub_bad_type_cast() grub_bad_type_cast_real(__LINE__, GRUB_FILE) #define GRUB_FIELD_MATCH(ptr, type, field) \ ((char *) &(ptr)->field == (char *) &((type) (ptr))->field) diff --git a/include/grub/menu.h b/include/grub/menu.h index c7114a93b..78f461b92 100644 --- a/include/grub/menu.h +++ b/include/grub/menu.h @@ -83,7 +83,6 @@ typedef struct grub_menu_execute_callback } *grub_menu_execute_callback_t; - grub_menu_entry_t grub_menu_get_entry (grub_menu_t menu, int no); int grub_menu_get_timeout (void); void grub_menu_set_timeout (int timeout); @@ -93,5 +92,6 @@ void grub_menu_execute_with_fallback (grub_menu_t menu, grub_menu_execute_callback_t callback, void *callback_data); void grub_menu_entry_run (grub_menu_entry_t entry); +int grub_menu_get_default_entry_index (grub_menu_t menu); #endif /* GRUB_MENU_HEADER */ diff --git a/include/grub/mips/kernel.h b/include/grub/mips/kernel.h deleted file mode 100644 index 8b68f7b6b..000000000 --- a/include/grub/mips/kernel.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2006,2007,2008 Free Software Foundation, Inc. - * - * GRUB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GRUB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GRUB. If not, see . - */ - -#ifndef GRUB_KERNEL_CPU_HEADER -#define GRUB_KERNEL_CPU_HEADER 1 - -#define GRUB_MOD_ALIGN 0x1 -/* Non-zero value is only needed for PowerMacs. */ -#define GRUB_MOD_GAP 0x0 - -#define GRUB_KERNEL_MACHINE_LINK_ALIGN 32 - -#define GRUB_KERNEL_CPU_RAW_SIZE 0x200 -#define GRUB_KERNEL_CPU_COMPRESSED_SIZE 0x8 -#define GRUB_KERNEL_CPU_TOTAL_MODULE_SIZE 0xc -#define GRUB_KERNEL_CPU_KERNEL_IMAGE_SIZE 0x10 - -#define GRUB_KERNEL_CPU_PREFIX GRUB_KERNEL_CPU_RAW_SIZE -#define GRUB_KERNEL_CPU_DATA_END GRUB_KERNEL_CPU_RAW_SIZE + 0x48 - -#define GRUB_KERNEL_MACHINE_RAW_SIZE GRUB_KERNEL_CPU_RAW_SIZE - -#define GRUB_KERNEL_MACHINE_PREFIX GRUB_KERNEL_CPU_PREFIX -#define GRUB_KERNEL_MACHINE_DATA_END GRUB_KERNEL_CPU_DATA_END -#define GRUB_KERNEL_MACHINE_KERNEL_IMAGE_SIZE GRUB_KERNEL_CPU_KERNEL_IMAGE_SIZE -#define GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE GRUB_KERNEL_CPU_TOTAL_MODULE_SIZE -#define GRUB_KERNEL_MACHINE_COMPRESSED_SIZE GRUB_KERNEL_CPU_COMPRESSED_SIZE - -#define GRUB_PLATFORM_IMAGE_FORMATS "raw, elf" -#define GRUB_PLATFORM_IMAGE_DEFAULT_FORMAT "raw" - -#define GRUB_PLATFORM_IMAGE_DEFAULT GRUB_PLATFORM_IMAGE_RAW - -#ifndef ASM_FILE - -typedef enum { - GRUB_PLATFORM_IMAGE_RAW, - GRUB_PLATFORM_IMAGE_ELF -} - grub_platform_image_format_t; -#define GRUB_PLATFORM_IMAGE_RAW GRUB_PLATFORM_IMAGE_RAW -#define GRUB_PLATFORM_IMAGE_ELF GRUB_PLATFORM_IMAGE_ELF - -/* The prefix which points to the directory where GRUB modules and its - configuration file are located. */ -extern char grub_prefix[]; - -#endif - -#endif diff --git a/include/grub/mips/libgcc.h b/include/grub/mips/libgcc.h deleted file mode 100644 index f06ea1c1c..000000000 --- a/include/grub/mips/libgcc.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2004,2007,2009 Free Software Foundation, Inc. - * - * GRUB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GRUB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GRUB. If not, see . - */ - -#include - -#ifdef HAVE___ASHLDI3 -void EXPORT_FUNC (__ashldi3) (void); -#endif -#ifdef HAVE___ASHRDI3 -void EXPORT_FUNC (__ashrdi3) (void); -#endif -#ifdef HAVE___LSHRDI3 -void EXPORT_FUNC (__lshrdi3) (void); -#endif -#ifdef HAVE___UCMPDI2 -void EXPORT_FUNC (__ucmpdi2) (void); -#endif -#ifdef HAVE___BSWAPSI2 -void EXPORT_FUNC (__bswapsi2) (void); -#endif -#ifdef HAVE___BSWAPDI2 -void EXPORT_FUNC (__bswapdi2) (void); -#endif diff --git a/include/grub/mips/multiboot.h b/include/grub/mips/multiboot.h new file mode 100644 index 000000000..a27229efe --- /dev/null +++ b/include/grub/mips/multiboot.h @@ -0,0 +1,36 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_MULTIBOOT_CPU_HEADER +#define GRUB_MULTIBOOT_CPU_HEADER 1 + +extern grub_uint32_t grub_multiboot_payload_eip; +extern char *grub_multiboot_payload_orig; +extern grub_addr_t grub_multiboot_payload_dest; +extern grub_size_t grub_multiboot_payload_size; + +#define MULTIBOOT_INITIAL_STATE { .gpr[4] = MULTIBOOT_BOOTLOADER_MAGIC, \ + .jumpreg = 1 } +#define MULTIBOOT_ENTRY_REGISTER gpr[1] +#define MULTIBOOT_MBI_REGISTER gpr[5] +#define MULTIBOOT_ARCHITECTURE_CURRENT MULTIBOOT_ARCHITECTURE_MIPS32 + +#define MULTIBOOT_ELF32_MACHINE EM_MIPS +#define MULTIBOOT_ELF64_MACHINE EM_MIPS + +#endif /* ! GRUB_MULTIBOOT_CPU_HEADER */ diff --git a/include/grub/mips/qemu-mips/kernel.h b/include/grub/mips/qemu-mips/kernel.h index dbf74c1b2..230455dbf 100644 --- a/include/grub/mips/qemu-mips/kernel.h +++ b/include/grub/mips/qemu-mips/kernel.h @@ -27,10 +27,6 @@ void EXPORT_FUNC (grub_reboot) (void); void EXPORT_FUNC (grub_halt) (void); -/* The prefix which points to the directory where GRUB modules and its - configuration file are located. */ -extern char grub_prefix[]; - #endif #endif /* ! GRUB_KERNEL_MACHINE_HEADER */ diff --git a/include/grub/mips/yeeloong/kernel.h b/include/grub/mips/yeeloong/kernel.h index 230455dbf..c08405e83 100644 --- a/include/grub/mips/yeeloong/kernel.h +++ b/include/grub/mips/yeeloong/kernel.h @@ -20,7 +20,6 @@ #define GRUB_KERNEL_MACHINE_HEADER 1 #include -#include #ifndef ASM_FILE diff --git a/include/grub/misc.h b/include/grub/misc.h index 221734a22..9bfc6974e 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -46,7 +46,7 @@ #define ARRAY_SIZE(array) (sizeof (array) / sizeof (array[0])) #define COMPILE_TIME_ASSERT(cond) switch (0) { case 1: case !(cond): ; } -#define grub_dprintf(condition, fmt, args...) grub_real_dprintf(__FILE__, __LINE__, condition, fmt, ## args) +#define grub_dprintf(condition, fmt, args...) grub_real_dprintf(GRUB_FILE, __LINE__, condition, fmt, ## args) /* XXX: If grub_memmove is too slow, we must implement grub_memcpy. */ #define grub_memcpy(d,s,n) grub_memmove ((d), (s), (n)) @@ -110,6 +110,12 @@ char *EXPORT_FUNC(grub_strstr) (const char *haystack, const char *needle); int EXPORT_FUNC(grub_isspace) (int c); int EXPORT_FUNC(grub_isprint) (int c); +static inline int +grub_iscntrl (int c) +{ + return (c >= 0x00 && c <= 0x1F) || c == 0x7F; +} + static inline int grub_isalpha (int c) { @@ -128,6 +134,12 @@ grub_isdigit (int c) return (c >= '0' && c <= '9'); } +static inline int +grub_isalnum (int c) +{ + return grub_isalpha (c) || grub_isdigit (c); +} + static inline int grub_tolower (int c) { @@ -182,6 +194,43 @@ grub_strncasecmp (const char *s1, const char *s2, grub_size_t n) unsigned long EXPORT_FUNC(grub_strtoul) (const char *str, char **end, int base); unsigned long long EXPORT_FUNC(grub_strtoull) (const char *str, char **end, int base); + +static inline long +grub_strtol (const char *str, char **end, int base) +{ + int negative = 0; + unsigned long magnitude; + + while (*str && grub_isspace (*str)) + str++; + + if (*str == '-') + { + negative = 1; + str++; + } + + magnitude = grub_strtoull (str, end, base); + if (negative) + { + if (magnitude > (unsigned long) GRUB_LONG_MAX + 1) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "negative overflow"); + return GRUB_LONG_MIN; + } + return -((long) magnitude); + } + else + { + if (magnitude > GRUB_LONG_MAX) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "positive overflow"); + return GRUB_LONG_MAX; + } + return (long) magnitude; + } +} + char *EXPORT_FUNC(grub_strdup) (const char *s); char *EXPORT_FUNC(grub_strndup) (const char *s, grub_size_t n); void *EXPORT_FUNC(grub_memset) (void *s, int c, grub_size_t n); @@ -212,10 +261,15 @@ grub_size_t EXPORT_FUNC(grub_utf8_to_ucs4) (grub_uint32_t *dest, grub_uint64_t EXPORT_FUNC(grub_divmod64) (grub_uint64_t n, grub_uint32_t d, grub_uint32_t *r); -#ifdef NEED_ENABLE_EXECUTE_STACK +#if defined(NEED_ENABLE_EXECUTE_STACK) && !defined(GRUB_UTIL) void EXPORT_FUNC(__enable_execute_stack) (void *addr); #endif +#if defined (NEED_REGISTER_FRAME_INFO) && !defined(GRUB_UTIL) +void EXPORT_FUNC (__register_frame_info) (void); +void EXPORT_FUNC (__deregister_frame_info) (void); +#endif + /* Inline functions. */ static inline unsigned int diff --git a/include/grub/mm.h b/include/grub/mm.h index 4caf80511..38dd39646 100644 --- a/include/grub/mm.h +++ b/include/grub/mm.h @@ -36,7 +36,7 @@ void *EXPORT_FUNC(grub_realloc) (void *ptr, grub_size_t size); void *EXPORT_FUNC(grub_memalign) (grub_size_t align, grub_size_t size); /* For debugging. */ -#if defined(MM_DEBUG) && !defined(GRUB_UTIL) +#if defined(MM_DEBUG) && !defined(GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) /* Set this variable to 1 when you want to trace all memory function calls. */ extern int EXPORT_VAR(grub_mm_debug); @@ -44,19 +44,19 @@ void grub_mm_dump_free (void); void grub_mm_dump (unsigned lineno); #define grub_malloc(size) \ - grub_debug_malloc (__FILE__, __LINE__, size) + grub_debug_malloc (GRUB_FILE, __LINE__, size) #define grub_zalloc(size) \ - grub_debug_zalloc (__FILE__, __LINE__, size) + grub_debug_zalloc (GRUB_FILE, __LINE__, size) #define grub_realloc(ptr,size) \ - grub_debug_realloc (__FILE__, __LINE__, ptr, size) + grub_debug_realloc (GRUB_FILE, __LINE__, ptr, size) #define grub_memalign(align,size) \ - grub_debug_memalign (__FILE__, __LINE__, align, size) + grub_debug_memalign (GRUB_FILE, __LINE__, align, size) #define grub_free(ptr) \ - grub_debug_free (__FILE__, __LINE__, ptr) + grub_debug_free (GRUB_FILE, __LINE__, ptr) void *EXPORT_FUNC(grub_debug_malloc) (const char *file, int line, grub_size_t size); diff --git a/include/grub/msdos_partition.h b/include/grub/msdos_partition.h index 273d8c95e..650d78493 100644 --- a/include/grub/msdos_partition.h +++ b/include/grub/msdos_partition.h @@ -53,75 +53,6 @@ #define GRUB_PC_PARTITION_TYPE_GPT_DISK 0xee #define GRUB_PC_PARTITION_TYPE_LINUX_RAID 0xfd -/* Constants for BSD disk label. */ -#define GRUB_PC_PARTITION_BSD_LABEL_SECTOR 1 -#define GRUB_PC_PARTITION_BSD_LABEL_MAGIC 0x82564557 -#define GRUB_PC_PARTITION_BSD_MAX_ENTRIES 8 - -/* BSD partition types. */ -#define GRUB_PC_PARTITION_BSD_TYPE_UNUSED 0 -#define GRUB_PC_PARTITION_BSD_TYPE_SWAP 1 -#define GRUB_PC_PARTITION_BSD_TYPE_V6 2 -#define GRUB_PC_PARTITION_BSD_TYPE_V7 3 -#define GRUB_PC_PARTITION_BSD_TYPE_SYSV 4 -#define GRUB_PC_PARTITION_BSD_TYPE_V71K 5 -#define GRUB_PC_PARTITION_BSD_TYPE_V8 6 -#define GRUB_PC_PARTITION_BSD_TYPE_BSDFFS 7 -#define GRUB_PC_PARTITION_BSD_TYPE_MSDOS 8 -#define GRUB_PC_PARTITION_BSD_TYPE_BSDLFS 9 -#define GRUB_PC_PARTITION_BSD_TYPE_OTHER 10 -#define GRUB_PC_PARTITION_BSD_TYPE_HPFS 11 -#define GRUB_PC_PARTITION_BSD_TYPE_ISO9660 12 -#define GRUB_PC_PARTITION_BSD_TYPE_BOOT 13 - -/* FreeBSD-specific types. */ -#define GRUB_PC_PARTITION_FREEBSD_TYPE_VINUM 14 -#define GRUB_PC_PARTITION_FREEBSD_TYPE_RAID 15 -#define GRUB_PC_PARTITION_FREEBSD_TYPE_JFS2 21 - -/* NetBSD-specific types. */ -#define GRUB_PC_PARTITION_NETBSD_TYPE_ADOS 14 -#define GRUB_PC_PARTITION_NETBSD_TYPE_HFS 15 -#define GRUB_PC_PARTITION_NETBSD_TYPE_FILECORE 16 -#define GRUB_PC_PARTITION_NETBSD_TYPE_EXT2FS 17 -#define GRUB_PC_PARTITION_NETBSD_TYPE_NTFS 18 -#define GRUB_PC_PARTITION_NETBSD_TYPE_RAID 19 -#define GRUB_PC_PARTITION_NETBSD_TYPE_CCD 20 -#define GRUB_PC_PARTITION_NETBSD_TYPE_JFS2 21 -#define GRUB_PC_PARTITION_NETBSD_TYPE_APPLEUFS 22 - -/* OpenBSD-specific types. */ -#define GRUB_PC_PARTITION_OPENBSD_TYPE_ADOS 14 -#define GRUB_PC_PARTITION_OPENBSD_TYPE_HFS 15 -#define GRUB_PC_PARTITION_OPENBSD_TYPE_FILECORE 16 -#define GRUB_PC_PARTITION_OPENBSD_TYPE_EXT2FS 17 -#define GRUB_PC_PARTITION_OPENBSD_TYPE_NTFS 18 -#define GRUB_PC_PARTITION_OPENBSD_TYPE_RAID 19 - -/* The BSD partition entry. */ -struct grub_msdos_partition_bsd_entry -{ - grub_uint32_t size; - grub_uint32_t offset; - grub_uint32_t fragment_size; - grub_uint8_t fs_type; - grub_uint8_t fs_fragments; - grub_uint16_t fs_cylinders; -} __attribute__ ((packed)); - -/* The BSD disk label. Only define members useful for GRUB. */ -struct grub_msdos_partition_disk_label -{ - grub_uint32_t magic; - grub_uint8_t padding[128]; - grub_uint32_t magic2; - grub_uint16_t checksum; - grub_uint16_t num_partitions; - grub_uint32_t boot_size; - grub_uint32_t superblock_size; - struct grub_msdos_partition_bsd_entry entries[GRUB_PC_PARTITION_BSD_MAX_ENTRIES]; -} __attribute__ ((packed)); - /* The partition entry. */ struct grub_msdos_partition_entry { @@ -168,23 +99,6 @@ struct grub_msdos_partition_mbr } __attribute__ ((packed)); -struct grub_msdos_partition -{ - /* The DOS partition number. */ - int dos_part; - - /* The BSD partition number (a == 0). */ - int bsd_part; - - /* The DOS partition type. */ - int dos_type; - - /* The BSD partition type. */ - int bsd_type; - - /* The offset of the extended partition. */ - unsigned long ext_offset; -}; static inline int grub_msdos_partition_is_empty (int type) @@ -200,12 +114,4 @@ grub_msdos_partition_is_extended (int type) || type == GRUB_PC_PARTITION_TYPE_LINUX_EXTENDED); } -static inline int -grub_msdos_partition_is_bsd (int type) -{ - return (type == GRUB_PC_PARTITION_TYPE_FREEBSD - || type == GRUB_PC_PARTITION_TYPE_OPENBSD - || type == GRUB_PC_PARTITION_TYPE_NETBSD); -} - #endif /* ! GRUB_PC_PARTITION_HEADER */ diff --git a/include/grub/multiboot.h b/include/grub/multiboot.h index 665292e33..49d71fa09 100644 --- a/include/grub/multiboot.h +++ b/include/grub/multiboot.h @@ -20,6 +20,8 @@ #ifndef GRUB_MULTIBOOT_HEADER #define GRUB_MULTIBOOT_HEADER 1 +#include + #ifdef GRUB_USE_MULTIBOOT2 #include /* Same thing as far as our loader is concerned. */ @@ -35,8 +37,6 @@ void grub_multiboot (int argc, char *argv[]); void grub_module (int argc, char *argv[]); -void grub_multiboot_set_accepts_video (int val); - grub_size_t grub_multiboot_get_mbi_size (void); grub_err_t grub_multiboot_make_mbi (void *orig, grub_uint32_t dest, grub_off_t buf_off, grub_size_t bufsize); @@ -46,5 +46,29 @@ grub_err_t grub_multiboot_add_module (grub_addr_t start, grub_size_t size, int argc, char *argv[]); void grub_multiboot_set_bootdev (void); +grub_uint32_t grub_get_multiboot_mmap_count (void); +grub_err_t grub_multiboot_set_video_mode (void); + +#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU) +#include +#define GRUB_MACHINE_HAS_VGA_TEXT 1 +#else +#define GRUB_MACHINE_HAS_VGA_TEXT 0 +#endif + +#define GRUB_MULTIBOOT_CONSOLE_EGA_TEXT 1 +#define GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER 2 + +grub_err_t +grub_multiboot_set_console (int console_type, int accepted_consoles, + int width, int height, int depth, + int console_required); +grub_err_t +grub_multiboot_load (grub_file_t file); +/* Load ELF32 or ELF64. */ +grub_err_t +grub_multiboot_load_elf (grub_file_t file, void *buffer); +extern grub_size_t grub_multiboot_pure_size; +extern grub_size_t grub_multiboot_alloc_mbi; #endif /* ! GRUB_MULTIBOOT_HEADER */ diff --git a/include/grub/multiboot2.h b/include/grub/multiboot2.h deleted file mode 100644 index af10cdc21..000000000 --- a/include/grub/multiboot2.h +++ /dev/null @@ -1,70 +0,0 @@ -/* multiboot2.h - multiboot2 header file with grub definitions. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2007 Free Software Foundation, Inc. - * - * GRUB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GRUB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GRUB. If not, see . - */ - -#ifndef GRUB_MULTIBOOT2_HEADER -#define GRUB_MULTIBOOT2_HEADER 1 - -#include -#include -#include - -#ifndef GRUB_UTIL -typedef grub_uint32_t uint32_t; -typedef grub_uint64_t uint64_t; -#define __WORDSIZE GRUB_TARGET_WORDSIZE -#endif - -struct multiboot2_tag_header; - -grub_err_t -grub_mb2_tag_alloc (grub_addr_t *addr, int key, grub_size_t len); - -grub_err_t -grub_mb2_tags_arch_create (void); - -void -grub_mb2_arch_boot (grub_addr_t entry, void *tags); - -void -grub_mb2_arch_unload (struct multiboot2_tag_header *tags); - -grub_err_t -grub_mb2_arch_elf32_hook (Elf32_Phdr *phdr, grub_addr_t *addr, int *do_load); - -grub_err_t -grub_mb2_arch_elf64_hook (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load); - -grub_err_t -grub_mb2_arch_module_alloc (grub_size_t size, grub_addr_t *addr); - -grub_err_t -grub_mb2_arch_module_free (grub_addr_t addr, grub_size_t size); - -void -grub_multiboot2 (int argc, char *argv[]); - -void -grub_module2 (int argc, char *argv[]); - -#define for_each_tag(tag, tags) \ - for (tag = tags; \ - tag && tag->key != MULTIBOOT2_TAG_END; \ - tag = (struct multiboot2_tag_header *)((char *)tag + tag->len)) - -#endif /* ! GRUB_MULTIBOOT2_HEADER */ diff --git a/include/grub/normal.h b/include/grub/normal.h index e804fde77..35eedf5ae 100644 --- a/include/grub/normal.h +++ b/include/grub/normal.h @@ -96,51 +96,21 @@ void read_handler_list (void); void free_handler_list (void); /* Defined in `dyncmd.c'. */ -void read_command_list (void); +void read_command_list (const char *prefix); /* Defined in `autofs.c'. */ -void read_fs_list (void); +void read_fs_list (const char *prefix); void grub_context_init (void); void grub_context_fini (void); -void read_crypto_list (void); +void read_crypto_list (const char *prefix); -void read_terminal_list (void); +void read_terminal_list (const char *prefix); void grub_set_more (int onoff); -#ifdef GRUB_UTIL -void grub_normal_init (void); -void grub_normal_fini (void); -void grub_hello_init (void); -void grub_hello_fini (void); -void grub_ls_init (void); -void grub_ls_fini (void); -void grub_cat_init (void); -void grub_cat_fini (void); -void grub_boot_init (void); -void grub_boot_fini (void); -void grub_cmp_init (void); -void grub_cmp_fini (void); -void grub_terminal_init (void); -void grub_terminal_fini (void); -void grub_loop_init (void); -void grub_loop_fini (void); -void grub_help_init (void); -void grub_help_fini (void); -void grub_halt_init (void); -void grub_halt_fini (void); -void grub_reboot_init (void); -void grub_reboot_fini (void); -void grub_configfile_init (void); -void grub_configfile_fini (void); -void grub_search_init (void); -void grub_search_fini (void); -void grub_test_init (void); -void grub_test_fini (void); -void grub_blocklist_init (void); -void grub_blocklist_fini (void); -#endif +int grub_normal_get_line_counter (void); +void grub_install_newline_hook (void); #endif /* ! GRUB_NORMAL_HEADER */ diff --git a/include/grub/offsets.h b/include/grub/offsets.h new file mode 100644 index 000000000..ae0b2557e --- /dev/null +++ b/include/grub/offsets.h @@ -0,0 +1,173 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2007,2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef OFFSETS_HEADER +#define OFFSETS_HEADER 1 + +/* The offset of GRUB_TOTAL_MODULE_SIZE. */ +#define GRUB_KERNEL_I386_PC_TOTAL_MODULE_SIZE 0x8 + +/* The offset of GRUB_KERNEL_IMAGE_SIZE. */ +#define GRUB_KERNEL_I386_PC_KERNEL_IMAGE_SIZE 0xc + +/* The offset of GRUB_COMPRESSED_SIZE. */ +#define GRUB_KERNEL_I386_PC_COMPRESSED_SIZE 0x10 + +/* The offset of GRUB_INSTALL_DOS_PART. */ +#define GRUB_KERNEL_I386_PC_INSTALL_DOS_PART 0x14 + +/* The offset of GRUB_INSTALL_BSD_PART. */ +#define GRUB_KERNEL_I386_PC_INSTALL_BSD_PART 0x18 + +/* The offset of GRUB_PREFIX. */ +#define GRUB_KERNEL_I386_PC_PREFIX 0x1c + +/* End of the data section. */ +#define GRUB_KERNEL_I386_PC_DATA_END 0x5c + +/* The size of the first region which won't be compressed. */ +#define GRUB_KERNEL_I386_PC_RAW_SIZE (GRUB_KERNEL_I386_PC_DATA_END + 0x5F0) + +/* The segment where the kernel is loaded. */ +#define GRUB_BOOT_I386_PC_KERNEL_SEG 0x800 + +#define GRUB_KERNEL_I386_PC_LINK_ADDR 0x8200 + +/* The upper memory area (starting at 640 kiB). */ +#define GRUB_MEMORY_I386_PC_UPPER 0xa0000 +#define GRUB_MEMORY_I386_QEMU_UPPER GRUB_MEMORY_I386_PC_UPPER + +/* The offset of GRUB_CORE_ENTRY_ADDR. */ +#define GRUB_BOOT_I386_QEMU_CORE_ENTRY_ADDR 0x4 + +/* The offset of GRUB_CORE_ENTRY_ADDR. */ +#define GRUB_KERNEL_I386_QEMU_CORE_ENTRY_ADDR 0x8 + +/* The offset of GRUB_KERNEL_IMAGE_SIZE. */ +#define GRUB_KERNEL_I386_QEMU_KERNEL_IMAGE_SIZE 0xc + +/* The offset of GRUB_PREFIX. */ +#define GRUB_KERNEL_I386_QEMU_PREFIX 0x10 + +/* End of the data section. */ +#define GRUB_KERNEL_I386_QEMU_DATA_END 0x50 + +#define GRUB_KERNEL_I386_QEMU_LINK_ADDR 0x8200 + +/* The offset of GRUB_TOTAL_MODULE_SIZE. */ +#define GRUB_KERNEL_SPARC64_IEEE1275_TOTAL_MODULE_SIZE 0x8 + +/* The offset of GRUB_KERNEL_IMAGE_SIZE. */ +#define GRUB_KERNEL_SPARC64_IEEE1275_KERNEL_IMAGE_SIZE 0xc + +/* The offset of GRUB_COMPRESSED_SIZE. */ +#define GRUB_KERNEL_SPARC64_IEEE1275_COMPRESSED_SIZE 0x10 + +/* The offset of GRUB_PREFIX. */ +#define GRUB_KERNEL_SPARC64_IEEE1275_PREFIX 0x14 + +/* End of the data section. */ +#define GRUB_KERNEL_SPARC64_IEEE1275_DATA_END 0x114 + +#define GRUB_BOOT_SPARC64_IEEE1275_LIST_SIZE 12 + +#define GRUB_BOOT_SPARC64_IEEE1275_IMAGE_ADDRESS 0x4400 +#define GRUB_KERNEL_SPARC64_IEEE1275_RAW_SIZE 0 +#define GRUB_KERNEL_SPARC64_IEEE1275_LINK_ADDR 0x4400 + +#define GRUB_KERNEL_POWERPC_IEEE1275_PREFIX 0x4 +#define GRUB_KERNEL_POWERPC_IEEE1275_DATA_END 0x44 +#define GRUB_KERNEL_POWERPC_IEEE1275_LINK_ALIGN 4 +#define GRUB_KERNEL_POWERPC_IEEE1275_LINK_ADDR 0x200000 + +#define GRUB_KERNEL_MIPS_YEELOONG_LINK_ADDR 0x80200000 + +#define GRUB_KERNEL_MIPS_YEELOONG_LINK_ALIGN 32 + +#define GRUB_KERNEL_MIPS_YEELOONG_RAW_SIZE 0x200 +#define GRUB_KERNEL_MIPS_YEELOONG_COMPRESSED_SIZE 0x8 +#define GRUB_KERNEL_MIPS_YEELOONG_TOTAL_MODULE_SIZE 0xc +#define GRUB_KERNEL_MIPS_YEELOONG_KERNEL_IMAGE_SIZE 0x10 + +#define GRUB_KERNEL_MIPS_YEELOONG_PREFIX GRUB_KERNEL_MIPS_YEELOONG_RAW_SIZE +#define GRUB_KERNEL_MIPS_YEELOONG_DATA_END GRUB_KERNEL_MIPS_YEELOONG_RAW_SIZE + 0x48 + +/* The offset of GRUB_PREFIX. */ +#define GRUB_KERNEL_I386_EFI_PREFIX 0x8 + +/* End of the data section. */ +#define GRUB_KERNEL_I386_EFI_DATA_END 0x50 + +/* The offset of GRUB_PREFIX. */ +#define GRUB_KERNEL_X86_64_EFI_PREFIX 0x8 + +/* End of the data section. */ +#define GRUB_KERNEL_X86_64_EFI_DATA_END 0x50 + +#define GRUB_KERNEL_I386_COREBOOT_PREFIX 0x2 +#define GRUB_KERNEL_I386_COREBOOT_DATA_END 0x42 +#define GRUB_KERNEL_I386_COREBOOT_LINK_ADDR 0x8200 + +#define GRUB_KERNEL_I386_IEEE1275_PREFIX 0x2 +#define GRUB_KERNEL_I386_IEEE1275_DATA_END 0x42 +#define GRUB_KERNEL_I386_IEEE1275_LINK_ADDR 0x10000 + +#define GRUB_KERNEL_I386_IEEE1275_MOD_ALIGN 0x1000 +#define GRUB_KERNEL_I386_COREBOOT_MOD_ALIGN 0x1 + +/* Non-zero value is only needed for PowerMacs. */ +#define GRUB_KERNEL_I386_IEEE1275_MOD_GAP 0x0 +#define GRUB_KERNEL_I386_COREBOOT_MOD_GAP 0x0 + +#define GRUB_KERNEL_POWERPC_IEEE1275_MOD_ALIGN 0x1000 + +#define GRUB_KERNEL_MIPS_YEELOONG_MOD_ALIGN 0x1 + +/* Minimal gap between _end and the start of the modules. It's a hack + for PowerMac to prevent "CLAIM failed" error. The real fix is to + rewrite grub-mkimage to generate valid ELF files. */ +#define GRUB_KERNEL_POWERPC_IEEE1275_MOD_GAP 0x8000 + +#ifdef MACHINE +#define GRUB_OFFSETS_CONCAT_(a,b,c) a ## b ## c +#define GRUB_OFFSETS_CONCAT(a,b,c) GRUB_OFFSETS_CONCAT_(a,b,c) +#define GRUB_KERNEL_MACHINE_MOD_ALIGN GRUB_OFFSETS_CONCAT (GRUB_KERNEL_, MACHINE, _MOD_ALIGN) +#define GRUB_KERNEL_MACHINE_MOD_GAP GRUB_OFFSETS_CONCAT (GRUB_KERNEL_, MACHINE, _MOD_GAP) +#define GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE GRUB_OFFSETS_CONCAT (GRUB_KERNEL_, MACHINE, _TOTAL_MODULE_SIZE) +#define GRUB_KERNEL_MACHINE_KERNEL_IMAGE_SIZE GRUB_OFFSETS_CONCAT (GRUB_KERNEL_, MACHINE, _KERNEL_IMAGE_SIZE) +#define GRUB_KERNEL_MACHINE_COMPRESSED_SIZE GRUB_OFFSETS_CONCAT (GRUB_KERNEL_, MACHINE, _COMPRESSED_SIZE) + +#define GRUB_KERNEL_MACHINE_PREFIX GRUB_OFFSETS_CONCAT (GRUB_KERNEL_, MACHINE, _PREFIX) +#define GRUB_KERNEL_MACHINE_DATA_END GRUB_OFFSETS_CONCAT (GRUB_KERNEL_, MACHINE, _DATA_END) +#define GRUB_BOOT_MACHINE_KERNEL_SEG GRUB_OFFSETS_CONCAT (GRUB_BOOT_, MACHINE, _KERNEL_SEG) +#define GRUB_MEMORY_MACHINE_UPPER GRUB_OFFSETS_CONCAT (GRUB_MEMORY_, MACHINE, _UPPER) +#define GRUB_KERNEL_MACHINE_RAW_SIZE GRUB_OFFSETS_CONCAT (GRUB_KERNEL_, MACHINE, _RAW_SIZE) +#define GRUB_KERNEL_MACHINE_INSTALL_BSD_PART GRUB_OFFSETS_CONCAT (GRUB_KERNEL_, MACHINE, _INSTALL_BSD_PART) +#define GRUB_KERNEL_MACHINE_INSTALL_DOS_PART GRUB_OFFSETS_CONCAT (GRUB_KERNEL_, MACHINE, _INSTALL_DOS_PART) +#endif + +#ifndef ASM_FILE +struct grub_pc_bios_boot_blocklist +{ + grub_uint64_t start; + grub_uint16_t len; + grub_uint16_t segment; +} __attribute__ ((packed)); +#endif + +#endif diff --git a/include/grub/partition.h b/include/grub/partition.h index d35658cdd..80a9c15f0 100644 --- a/include/grub/partition.h +++ b/include/grub/partition.h @@ -20,6 +20,7 @@ #define GRUB_PART_HEADER 1 #include +#include struct grub_disk; @@ -28,6 +29,9 @@ typedef struct grub_partition *grub_partition_t; /* Partition map type. */ struct grub_partition_map { + /* The next partition map type. */ + struct grub_partition_map *next; + /* The name of the partition map type. */ const char *name; @@ -35,22 +39,15 @@ struct grub_partition_map grub_err_t (*iterate) (struct grub_disk *disk, int (*hook) (struct grub_disk *disk, const grub_partition_t partition)); - - /* Return the partition named STR on the disk DISK. */ - grub_partition_t (*probe) (struct grub_disk *disk, - const char *str); - - /* Return the name of the partition PARTITION. */ - char *(*get_name) (const grub_partition_t partition); - - /* The next partition map type. */ - struct grub_partition_map *next; }; typedef struct grub_partition_map *grub_partition_map_t; /* Partition description. */ struct grub_partition { + /* The partition number. */ + int number; + /* The start sector. */ grub_disk_addr_t start; @@ -63,8 +60,8 @@ struct grub_partition /* The index of this partition in the partition table. */ int index; - /* Partition map type specific data. */ - void *data; + /* Parent partition map. */ + struct grub_partition *parent; /* The type partition map. */ grub_partition_map_t partmap; @@ -77,31 +74,36 @@ int EXPORT_FUNC(grub_partition_iterate) (struct grub_disk *disk, const grub_partition_t partition)); char *EXPORT_FUNC(grub_partition_get_name) (const grub_partition_t partition); -int EXPORT_FUNC(grub_partition_map_iterate) (int (*hook) (const grub_partition_map_t partmap)); -void EXPORT_FUNC(grub_partition_map_register) (grub_partition_map_t partmap); +extern grub_partition_map_t EXPORT_VAR(grub_partition_map_list); -void EXPORT_FUNC(grub_partition_map_unregister) (grub_partition_map_t partmap); +static inline void +grub_partition_map_register (grub_partition_map_t partmap) +{ + grub_list_push (GRUB_AS_LIST_P (&grub_partition_map_list), + GRUB_AS_LIST (partmap)); +} + +static inline void +grub_partition_map_unregister (grub_partition_map_t partmap) +{ + grub_list_remove (GRUB_AS_LIST_P (&grub_partition_map_list), + GRUB_AS_LIST (partmap)); +} + +#define FOR_PARTITION_MAPS(var) for (var = grub_partition_map_list; var; var = var->next) -#ifdef GRUB_UTIL -void grub_msdos_partition_map_init (void); -void grub_msdos_partition_map_fini (void); -void grub_amiga_partition_map_init (void); -void grub_amiga_partition_map_fini (void); -void grub_apple_partition_map_init (void); -void grub_apple_partition_map_fini (void); -void grub_sun_partition_map_init (void); -void grub_sun_partition_map_fini (void); -void grub_gpt_partition_map_init (void); -void grub_gpt_partition_map_fini (void); -void grub_apple_partition_map_init (void); -void grub_apple_partition_map_fini (void); -#endif static inline grub_disk_addr_t grub_partition_get_start (const grub_partition_t p) { - return p->start; + grub_partition_t part; + grub_uint64_t part_start = 0; + + for (part = p; part; part = part->parent) + part_start += part->start; + + return part_start; } static inline grub_uint64_t diff --git a/include/grub/pci.h b/include/grub/pci.h index 1f3ac7fc7..89bd1034a 100644 --- a/include/grub/pci.h +++ b/include/grub/pci.h @@ -68,7 +68,7 @@ typedef grub_uint32_t grub_pci_id_t; -#ifdef GRUB_UTIL +#ifdef GRUB_MACHINE_EMU #include #else typedef grub_uint32_t grub_pci_address_t; diff --git a/include/grub/powerpc/ieee1275/ieee1275.h b/include/grub/powerpc/ieee1275/ieee1275.h index 7e93055c9..3c7683fad 100644 --- a/include/grub/powerpc/ieee1275/ieee1275.h +++ b/include/grub/powerpc/ieee1275/ieee1275.h @@ -22,6 +22,7 @@ #include +#define GRUB_IEEE1275_CELL_SIZEOF 4 typedef grub_uint32_t grub_ieee1275_cell_t; #endif /* ! GRUB_IEEE1275_MACHINE_HEADER */ diff --git a/include/grub/powerpc/kernel.h b/include/grub/powerpc/kernel.h index b4687337f..3fc0b9e23 100644 --- a/include/grub/powerpc/kernel.h +++ b/include/grub/powerpc/kernel.h @@ -19,14 +19,4 @@ #ifndef GRUB_KERNEL_CPU_HEADER #define GRUB_KERNEL_CPU_HEADER 1 -#define GRUB_MOD_ALIGN 0x1000 - -/* Minimal gap between _end and the start of the modules. It's a hack - for PowerMac to prevent "CLAIM failed" error. The real fix is to - rewrite grub-mkimage to generate valid ELF files. */ -#define GRUB_MOD_GAP 0x8000 - -#define GRUB_KERNEL_CPU_PREFIX 0x4 -#define GRUB_KERNEL_CPU_DATA_END 0x44 - #endif diff --git a/include/grub/script_sh.h b/include/grub/script_sh.h index 0bd14abcd..b55b6a806 100644 --- a/include/grub/script_sh.h +++ b/include/grub/script_sh.h @@ -1,7 +1,7 @@ /* normal_parser.h */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2007,2009 Free Software Foundation, Inc. + * Copyright (C) 2005,2007,2009,2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,8 +45,11 @@ struct grub_script typedef enum { - GRUB_SCRIPT_ARG_TYPE_STR, - GRUB_SCRIPT_ARG_TYPE_VAR + GRUB_SCRIPT_ARG_TYPE_VAR, + GRUB_SCRIPT_ARG_TYPE_TEXT, + GRUB_SCRIPT_ARG_TYPE_DQVAR, + GRUB_SCRIPT_ARG_TYPE_DQSTR, + GRUB_SCRIPT_ARG_TYPE_SQSTR } grub_script_arg_type_t; /* A part of an argument. */ @@ -103,6 +106,36 @@ struct grub_script_cmdif struct grub_script_cmd *exec_on_false; }; +/* A for statement. */ +struct grub_script_cmdfor +{ + struct grub_script_cmd cmd; + + /* The name used as looping variable. */ + struct grub_script_arg *name; + + /* The words loop iterates over. */ + struct grub_script_arglist *words; + + /* The command list executed in each loop. */ + struct grub_script_cmd *list; +}; + +/* A while/until command. */ +struct grub_script_cmdwhile +{ + struct grub_script_cmd cmd; + + /* The command list used as condition. */ + struct grub_script_cmd *cond; + + /* The command list executed in each loop. */ + struct grub_script_cmd *list; + + /* The flag to indicate this as "until" loop. */ + int until; +}; + /* A menu entry generate statement. */ struct grub_script_cmd_menuentry { @@ -112,7 +145,7 @@ struct grub_script_cmd_menuentry struct grub_script_arglist *arglist; /* The sourcecode the entry will be generated from. */ - char *sourcecode; + const char *sourcecode; /* Options. XXX: Not used yet. */ int options; @@ -121,12 +154,6 @@ struct grub_script_cmd_menuentry /* State of the lexer as passed to the lexer. */ struct grub_lexer_param { - /* Set to 0 when the lexer is done. */ - int done; - - /* State of the state machine. */ - grub_parser_state_t state; - /* Function used by the lexer to get a new line when more input is expected, but not available. */ grub_reader_getline_t getline; @@ -137,10 +164,6 @@ struct grub_lexer_param depleted. */ int refs; - /* The character stream that has to be parsed. */ - char *script; - char *newscript; /* XXX */ - /* While walking through the databuffer, `record' the characters to this other buffer. It can be used to edit the menu entry at a later moment. */ @@ -157,13 +180,31 @@ struct grub_lexer_param /* Size of RECORDING. */ int recordlen; - /* The token that is already parsed but not yet returned. */ - int tokenonhold; + /* End of file reached. */ + int eof; - /* Was the last token a newline? */ - int was_newline; + /* Merge multiple word tokens. */ + int merge_start; + int merge_end; + + /* Part of a multi-part token. */ + char *text; + unsigned used; + unsigned size; + + /* Type of text. */ + grub_script_arg_type_t type; + + /* Flex scanner. */ + void *yyscanner; + + /* Flex scanner buffer. */ + void *buffer; }; +#define GRUB_LEXER_INITIAL_TEXT_SIZE 32 +#define GRUB_LEXER_INITIAL_RECORD_SIZE 256 + /* State of the parser as passes to the parser. */ struct grub_parser_param { @@ -202,6 +243,18 @@ grub_script_create_cmdif (struct grub_parser_param *state, struct grub_script_cmd *exec_on_true, struct grub_script_cmd *exec_on_false); +struct grub_script_cmd * +grub_script_create_cmdfor (struct grub_parser_param *state, + struct grub_script_arg *name, + struct grub_script_arglist *words, + struct grub_script_cmd *list); + +struct grub_script_cmd * +grub_script_create_cmdwhile (struct grub_parser_param *state, + struct grub_script_cmd *cond, + struct grub_script_cmd *list, + int is_an_until_loop); + struct grub_script_cmd * grub_script_create_cmdmenu (struct grub_parser_param *state, struct grub_script_arglist *arglist, @@ -223,12 +276,16 @@ void grub_script_free (struct grub_script *script); struct grub_script *grub_script_create (struct grub_script_cmd *cmd, struct grub_script_mem *mem); -struct grub_lexer_param *grub_script_lexer_init (char *s, +struct grub_lexer_param *grub_script_lexer_init (struct grub_parser_param *parser, + char *script, grub_reader_getline_t getline); +void grub_script_lexer_fini (struct grub_lexer_param *); void grub_script_lexer_ref (struct grub_lexer_param *); void grub_script_lexer_deref (struct grub_lexer_param *); -void grub_script_lexer_record_start (struct grub_lexer_param *); -char *grub_script_lexer_record_stop (struct grub_lexer_param *); +void grub_script_lexer_record_start (struct grub_parser_param *); +char *grub_script_lexer_record_stop (struct grub_parser_param *); +int grub_script_lexer_yywrap (struct grub_parser_param *); +void grub_script_lexer_record (struct grub_parser_param *, char *); /* Functions to track allocated memory. */ struct grub_script_mem *grub_script_mem_record (struct grub_parser_param *state); @@ -246,6 +303,8 @@ void grub_script_yyerror (struct grub_parser_param *, char const *); grub_err_t grub_script_execute_cmdline (struct grub_script_cmd *cmd); grub_err_t grub_script_execute_cmdblock (struct grub_script_cmd *cmd); grub_err_t grub_script_execute_cmdif (struct grub_script_cmd *cmd); +grub_err_t grub_script_execute_cmdfor (struct grub_script_cmd *cmd); +grub_err_t grub_script_execute_cmdwhile (struct grub_script_cmd *cmd); grub_err_t grub_script_execute_menuentry (struct grub_script_cmd *cmd); /* Execute any GRUB pre-parsed command or script. */ @@ -284,7 +343,7 @@ int grub_script_function_iterate (int (*iterate) (grub_script_function_t)); int grub_script_function_call (grub_script_function_t func, int argc, char **args); -char * -grub_script_execute_argument_to_string (struct grub_script_arg *arg); +char ** +grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist, int *count); #endif /* ! GRUB_NORMAL_PARSER_HEADER */ diff --git a/include/grub/sdl.h b/include/grub/sdl.h new file mode 100644 index 000000000..e4efdc9b1 --- /dev/null +++ b/include/grub/sdl.h @@ -0,0 +1,24 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +void EXPORT_FUNC (SDL_Quit) (void); +void EXPORT_FUNC (SDL_SetColors) (void); +void EXPORT_FUNC (SDL_Init) (void); +void EXPORT_FUNC (SDL_GetError) (void); +void EXPORT_FUNC (SDL_Flip) (void); +void EXPORT_FUNC (SDL_SetVideoMode) (void); diff --git a/include/grub/sparc64/ieee1275/boot.h b/include/grub/sparc64/ieee1275/boot.h index 95f311ce5..bd0a7bf3c 100644 --- a/include/grub/sparc64/ieee1275/boot.h +++ b/include/grub/sparc64/ieee1275/boot.h @@ -25,7 +25,8 @@ #define BOOTDEV_REG %l6 #define PIC_REG %l7 -#define SCRATCH_PAD 0x10000 +#define SCRATCH_PAD_BOOT 0x5000 +#define SCRATCH_PAD_DISKBOOT 0x4000 #define GET_ABS(symbol, reg) \ add PIC_REG, (symbol - pic_base), reg @@ -44,15 +45,11 @@ #define GRUB_BOOT_MACHINE_BOOT_DEVPATH_END 0x80 -#define GRUB_BOOT_MACHINE_KERNEL_SECTOR 0x88 +#define GRUB_BOOT_MACHINE_KERNEL_BYTE 0x80 #define GRUB_BOOT_MACHINE_CODE_END \ (0x1fc - GRUB_BOOT_AOUT_HEADER_SIZE) -#define GRUB_BOOT_MACHINE_LIST_SIZE 12 - -#define GRUB_BOOT_MACHINE_IMAGE_ADDRESS 0x200000 - #define GRUB_BOOT_MACHINE_KERNEL_ADDR 0x4200 #endif /* ! BOOT_MACHINE_HEADER */ diff --git a/include/grub/sparc64/ieee1275/ieee1275.h b/include/grub/sparc64/ieee1275/ieee1275.h index b25e98a6d..32c77f80f 100644 --- a/include/grub/sparc64/ieee1275/ieee1275.h +++ b/include/grub/sparc64/ieee1275/ieee1275.h @@ -22,6 +22,7 @@ #include +#define GRUB_IEEE1275_CELL_SIZEOF 8 typedef grub_uint64_t grub_ieee1275_cell_t; /* Encoding of 'mode' argument to grub_ieee1275_map_physical() */ @@ -36,14 +37,12 @@ typedef grub_uint64_t grub_ieee1275_cell_t; #define IEEE1275_MAP_DEFAULT (IEEE1275_MAP_WRITE | IEEE1275_MAP_READ | \ IEEE1275_MAP_EXEC | IEEE1275_MAP_CACHED) -extern int EXPORT_FUNC(grub_ieee1275_map_physical) (grub_addr_t paddr, - grub_addr_t vaddr, - grub_size_t size, - grub_uint32_t mode); extern int EXPORT_FUNC(grub_ieee1275_claim_vaddr) (grub_addr_t vaddr, grub_size_t size); extern int EXPORT_FUNC(grub_ieee1275_alloc_physmem) (grub_addr_t *paddr, grub_size_t size, grub_uint32_t align); +extern grub_addr_t EXPORT_VAR (grub_ieee1275_original_stack); + #endif /* ! GRUB_IEEE1275_MACHINE_HEADER */ diff --git a/include/grub/sparc64/ieee1275/kernel.h b/include/grub/sparc64/ieee1275/kernel.h index e63e1f616..5aa50b852 100644 --- a/include/grub/sparc64/ieee1275/kernel.h +++ b/include/grub/sparc64/ieee1275/kernel.h @@ -19,25 +19,7 @@ #ifndef GRUB_KERNEL_MACHINE_HEADER #define GRUB_KERNEL_MACHINE_HEADER 1 -#define GRUB_MOD_ALIGN 0x2000 - -/* Non-zero value is only needed for PowerMacs. */ -#define GRUB_MOD_GAP 0x0 - -/* The offset of GRUB_TOTAL_MODULE_SIZE. */ -#define GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE 0x8 - -/* The offset of GRUB_KERNEL_IMAGE_SIZE. */ -#define GRUB_KERNEL_MACHINE_KERNEL_IMAGE_SIZE 0xc - -/* The offset of GRUB_COMPRESSED_SIZE. */ -#define GRUB_KERNEL_MACHINE_COMPRESSED_SIZE 0x10 - -/* The offset of GRUB_PREFIX. */ -#define GRUB_KERNEL_MACHINE_PREFIX 0x14 - -/* End of the data section. */ -#define GRUB_KERNEL_MACHINE_DATA_END 0x114 +#define GRUB_KERNEL_MACHINE_STACK_SIZE 0x40000 #ifndef ASM_FILE @@ -50,10 +32,6 @@ extern grub_int32_t grub_kernel_image_size; /* The total size of module images following the kernel. */ extern grub_int32_t grub_total_module_size; -/* The prefix which points to the directory where GRUB modules and its - configuration file are located. */ -extern char grub_prefix[]; - #endif /* ! ASM_FILE */ #endif /* ! GRUB_KERNEL_MACHINE_HEADER */ diff --git a/include/grub/test.h b/include/grub/test.h index a7d3a2399..27591cca2 100644 --- a/include/grub/test.h +++ b/include/grub/test.h @@ -54,7 +54,7 @@ void grub_test_nonzero (int cond, const char *file, /* Macro to fill in location details and an optional error message. */ #define grub_test_assert(cond, ...) \ - grub_test_nonzero(cond, __FILE__, __FUNCTION__, __LINE__, \ + grub_test_nonzero(cond, GRUB_FILE, __FUNCTION__, __LINE__, \ ## __VA_ARGS__, \ "assert failed: %s", #cond) diff --git a/include/grub/time.h b/include/grub/time.h index 5aafdc9ed..ae2617edb 100644 --- a/include/grub/time.h +++ b/include/grub/time.h @@ -25,6 +25,8 @@ #if defined (GRUB_MACHINE_EMU) || defined (GRUB_UTIL) #define GRUB_TICKS_PER_SECOND 100000 +/* Return the real time in ticks. */ +grub_uint32_t EXPORT_FUNC (grub_get_rtc) (void); #else #include #endif diff --git a/include/grub/x86_64/efi/kernel.h b/include/grub/trig.h similarity index 55% rename from include/grub/x86_64/efi/kernel.h rename to include/grub/trig.h index c0549f41a..2512a5f07 100644 --- a/include/grub/x86_64/efi/kernel.h +++ b/include/grub/trig.h @@ -1,6 +1,7 @@ +/* trig.h - Trigonometric function support. */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2002,2003,2007 Free Software Foundation, Inc. + * Copyright (C) 2008 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,18 +17,28 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_MACHINE_KERNEL_HEADER -#define GRUB_MACHINE_KERNEL_HEADER 1 +#ifndef GRUB_TRIG_HEADER +#define GRUB_TRIG_HEADER 1 -/* The prefix which points to the directory where GRUB modules and its - configuration file are located. */ -extern char grub_prefix[]; +#define GRUB_TRIG_ANGLE_MAX 256 +#define GRUB_TRIG_ANGLE_MASK 255 +#define GRUB_TRIG_FRACTION_SCALE 16384 -/* The offset of GRUB_PREFIX. */ -#define GRUB_KERNEL_MACHINE_PREFIX 0x8 +extern short grub_trig_sintab[]; +extern short grub_trig_costab[]; -/* End of the data section. */ -#define GRUB_KERNEL_MACHINE_DATA_END 0x50 +static __inline int +grub_sin (int x) +{ + x &= GRUB_TRIG_ANGLE_MASK; + return grub_trig_sintab[x]; +} -#endif /* ! GRUB_MACHINE_KERNEL_HEADER */ +static __inline int +grub_cos (int x) +{ + x &= GRUB_TRIG_ANGLE_MASK; + return grub_trig_costab[x]; +} +#endif /* ! GRUB_TRIG_HEADER */ diff --git a/include/grub/types.h b/include/grub/types.h index 93174b424..6e9461f1d 100644 --- a/include/grub/types.h +++ b/include/grub/types.h @@ -178,21 +178,6 @@ static inline grub_uint64_t grub_swap_bytes64(grub_uint64_t x) # define grub_be_to_cpu16(x) ((grub_uint16_t) (x)) # define grub_be_to_cpu32(x) ((grub_uint32_t) (x)) # define grub_be_to_cpu64(x) ((grub_uint64_t) (x)) -# ifdef GRUB_TARGET_WORDS_BIGENDIAN -# define grub_target_to_host16(x) ((grub_uint16_t) (x)) -# define grub_target_to_host32(x) ((grub_uint32_t) (x)) -# define grub_target_to_host64(x) ((grub_uint64_t) (x)) -# define grub_host_to_target16(x) ((grub_uint16_t) (x)) -# define grub_host_to_target32(x) ((grub_uint32_t) (x)) -# define grub_host_to_target64(x) ((grub_uint64_t) (x)) -# else /* ! GRUB_TARGET_WORDS_BIGENDIAN */ -# define grub_target_to_host16(x) grub_swap_bytes16(x) -# define grub_target_to_host32(x) grub_swap_bytes32(x) -# define grub_target_to_host64(x) grub_swap_bytes64(x) -# define grub_host_to_target16(x) grub_swap_bytes16(x) -# define grub_host_to_target32(x) grub_swap_bytes32(x) -# define grub_host_to_target64(x) grub_swap_bytes64(x) -# endif #else /* ! WORDS_BIGENDIAN */ # define grub_cpu_to_le16(x) ((grub_uint16_t) (x)) # define grub_cpu_to_le32(x) ((grub_uint32_t) (x)) @@ -206,21 +191,6 @@ static inline grub_uint64_t grub_swap_bytes64(grub_uint64_t x) # define grub_be_to_cpu16(x) grub_swap_bytes16(x) # define grub_be_to_cpu32(x) grub_swap_bytes32(x) # define grub_be_to_cpu64(x) grub_swap_bytes64(x) -# ifdef GRUB_TARGET_WORDS_BIGENDIAN -# define grub_target_to_host16(x) grub_swap_bytes16(x) -# define grub_target_to_host32(x) grub_swap_bytes32(x) -# define grub_target_to_host64(x) grub_swap_bytes64(x) -# define grub_host_to_target16(x) grub_swap_bytes16(x) -# define grub_host_to_target32(x) grub_swap_bytes32(x) -# define grub_host_to_target64(x) grub_swap_bytes64(x) -# else /* ! GRUB_TARGET_WORDS_BIGENDIAN */ -# define grub_target_to_host16(x) ((grub_uint16_t) (x)) -# define grub_target_to_host32(x) ((grub_uint32_t) (x)) -# define grub_target_to_host64(x) ((grub_uint64_t) (x)) -# define grub_host_to_target16(x) ((grub_uint16_t) (x)) -# define grub_host_to_target32(x) ((grub_uint32_t) (x)) -# define grub_host_to_target64(x) ((grub_uint64_t) (x)) -# endif #endif /* ! WORDS_BIGENDIAN */ #endif /* ! GRUB_TYPES_HEADER */ diff --git a/include/grub/util/hostdisk.h b/include/grub/util/hostdisk.h index 21efb0d17..246046ee0 100644 --- a/include/grub/util/hostdisk.h +++ b/include/grub/util/hostdisk.h @@ -20,8 +20,11 @@ #ifndef GRUB_BIOSDISK_MACHINE_UTIL_HEADER #define GRUB_BIOSDISK_MACHINE_UTIL_HEADER 1 +#include + void grub_util_biosdisk_init (const char *dev_map); void grub_util_biosdisk_fini (void); char *grub_util_biosdisk_get_grub_dev (const char *os_dev); +const char *grub_util_biosdisk_get_osdev (grub_disk_t disk); #endif /* ! GRUB_BIOSDISK_MACHINE_UTIL_HEADER */ diff --git a/include/grub/util/misc.h b/include/grub/util/misc.h index 373fff8bb..8b78b92ea 100644 --- a/include/grub/util/misc.h +++ b/include/grub/util/misc.h @@ -27,6 +27,7 @@ #include #include +#include #ifdef __NetBSD__ /* NetBSD uses /boot for its boot block. */ @@ -40,9 +41,9 @@ extern char *progname; extern int verbosity; -void grub_util_warn (const char *fmt, ...); -void grub_util_info (const char *fmt, ...); -void grub_util_error (const char *fmt, ...) __attribute__ ((noreturn)); +void EXPORT_FUNC(grub_util_warn) (const char *fmt, ...); +void EXPORT_FUNC(grub_util_info) (const char *fmt, ...); +void EXPORT_FUNC(grub_util_error) (const char *fmt, ...) __attribute__ ((noreturn)); void *xmalloc (size_t size); void *xrealloc (void *ptr, size_t size); @@ -88,6 +89,8 @@ grub_int64_t grub_util_get_disk_size (char *name); char *make_system_path_relative_to_its_root (const char *path); +char *canonicalize_file_name (const char *path); + void grub_util_init_nls (void); #endif /* ! GRUB_UTIL_MISC_HEADER */ diff --git a/include/grub/util/ofpath.h b/include/grub/util/ofpath.h index 78f24d784..b43c523cb 100644 --- a/include/grub/util/ofpath.h +++ b/include/grub/util/ofpath.h @@ -1,6 +1,6 @@ #ifndef GRUB_OFPATH_MACHINE_UTIL_HEADER #define GRUB_OFPATH_MACHINE_UTIL_HEADER 1 -char *grub_util_devname_to_ofpath (char *devname); +char *grub_util_devname_to_ofpath (const char *devname); #endif /* ! GRUB_OFPATH_MACHINE_UTIL_HEADER */ diff --git a/include/grub/video.h b/include/grub/video.h index fb0151878..782a5281b 100644 --- a/include/grub/video.h +++ b/include/grub/video.h @@ -34,6 +34,10 @@ struct grub_video_render_target; struct grub_video_bitmap; /* Defines used to describe video mode or rendering target. */ +/* If following is set render target contains currenly displayed image + after swapping buffers (otherwise it contains previously displayed image). + */ +#define GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP 0x00000080 #define GRUB_VIDEO_MODE_TYPE_PURE_TEXT 0x00000040 #define GRUB_VIDEO_MODE_TYPE_ALPHA 0x00000020 #define GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED 0x00000010 @@ -48,6 +52,8 @@ struct grub_video_bitmap; #define GRUB_VIDEO_MODE_TYPE_DEPTH_MASK 0x0000ff00 #define GRUB_VIDEO_MODE_TYPE_DEPTH_POS 8 +/* The basic render target representing the whole display. This always + renders to the back buffer when double-buffering is in use. */ #define GRUB_VIDEO_RENDER_TARGET_DISPLAY \ ((struct grub_video_render_target *) 0) @@ -151,6 +157,16 @@ struct grub_video_mode_info grub_uint8_t fg_alpha; }; +/* A 2D rectangle type. */ +struct grub_video_rect +{ + unsigned x; + unsigned y; + unsigned width; + unsigned height; +}; +typedef struct grub_video_rect grub_video_rect_t; + struct grub_video_palette_data { grub_uint8_t r; /* Red color value (0-255). */ @@ -164,7 +180,8 @@ typedef enum grub_video_driver_id GRUB_VIDEO_DRIVER_NONE, GRUB_VIDEO_DRIVER_VBE, GRUB_VIDEO_DRIVER_EFI_UGA, - GRUB_VIDEO_DRIVER_EFI_GOP + GRUB_VIDEO_DRIVER_EFI_GOP, + GRUB_VIDEO_DRIVER_SM712 } grub_video_driver_id_t; struct grub_video_adapter @@ -243,20 +260,20 @@ struct grub_video_adapter }; typedef struct grub_video_adapter *grub_video_adapter_t; -void grub_video_register (grub_video_adapter_t adapter); +void EXPORT_FUNC (grub_video_register) (grub_video_adapter_t adapter); void grub_video_unregister (grub_video_adapter_t adapter); void grub_video_iterate (int (*hook) (grub_video_adapter_t adapter)); -grub_err_t grub_video_restore (void); +grub_err_t EXPORT_FUNC (grub_video_restore) (void); -grub_err_t grub_video_get_info (struct grub_video_mode_info *mode_info); +grub_err_t EXPORT_FUNC (grub_video_get_info) (struct grub_video_mode_info *mode_info); /* Framebuffer address may change as a part of normal operation (e.g. double buffering). That's why you need to stop video subsystem to be sure that framebuffer address doesn't change. To ensure this abstraction grub_video_get_info_and_fini is the only function supplying framebuffer address. */ -grub_err_t grub_video_get_info_and_fini (struct grub_video_mode_info *mode_info, +grub_err_t EXPORT_FUNC (grub_video_get_info_and_fini) (struct grub_video_mode_info *mode_info, void **framebuffer); enum grub_video_blit_format grub_video_get_blit_format (struct grub_video_mode_info *mode_info); @@ -264,60 +281,75 @@ enum grub_video_blit_format grub_video_get_blit_format (struct grub_video_mode_i grub_err_t grub_video_set_palette (unsigned int start, unsigned int count, struct grub_video_palette_data *palette_data); -grub_err_t grub_video_get_palette (unsigned int start, unsigned int count, - struct grub_video_palette_data *palette_data); +grub_err_t EXPORT_FUNC (grub_video_get_palette) (unsigned int start, + unsigned int count, + struct grub_video_palette_data *palette_data); -grub_err_t grub_video_set_viewport (unsigned int x, unsigned int y, - unsigned int width, unsigned int height); +grub_err_t EXPORT_FUNC (grub_video_set_viewport) (unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height); -grub_err_t grub_video_get_viewport (unsigned int *x, unsigned int *y, - unsigned int *width, unsigned int *height); +grub_err_t EXPORT_FUNC (grub_video_get_viewport) (unsigned int *x, + unsigned int *y, + unsigned int *width, + unsigned int *height); -grub_video_color_t grub_video_map_color (grub_uint32_t color_name); +grub_video_color_t EXPORT_FUNC (grub_video_map_color) (grub_uint32_t color_name); -grub_video_color_t grub_video_map_rgb (grub_uint8_t red, grub_uint8_t green, - grub_uint8_t blue); +grub_video_color_t EXPORT_FUNC (grub_video_map_rgb) (grub_uint8_t red, + grub_uint8_t green, + grub_uint8_t blue); -grub_video_color_t grub_video_map_rgba (grub_uint8_t red, grub_uint8_t green, - grub_uint8_t blue, grub_uint8_t alpha); +grub_video_color_t EXPORT_FUNC (grub_video_map_rgba) (grub_uint8_t red, + grub_uint8_t green, + grub_uint8_t blue, + grub_uint8_t alpha); -grub_err_t grub_video_unmap_color (grub_video_color_t color, - grub_uint8_t *red, grub_uint8_t *green, - grub_uint8_t *blue, grub_uint8_t *alpha); +grub_err_t EXPORT_FUNC (grub_video_unmap_color) (grub_video_color_t color, + grub_uint8_t *red, + grub_uint8_t *green, + grub_uint8_t *blue, + grub_uint8_t *alpha); -grub_err_t grub_video_fill_rect (grub_video_color_t color, int x, int y, - unsigned int width, unsigned int height); +grub_err_t EXPORT_FUNC (grub_video_fill_rect) (grub_video_color_t color, + int x, int y, + unsigned int width, + unsigned int height); -grub_err_t grub_video_blit_bitmap (struct grub_video_bitmap *bitmap, - enum grub_video_blit_operators oper, - int x, int y, int offset_x, int offset_y, - unsigned int width, unsigned int height); +grub_err_t EXPORT_FUNC (grub_video_blit_bitmap) (struct grub_video_bitmap *bitmap, + enum grub_video_blit_operators oper, + int x, int y, + int offset_x, int offset_y, + unsigned int width, + unsigned int height); -grub_err_t grub_video_blit_render_target (struct grub_video_render_target *source, - enum grub_video_blit_operators oper, - int x, int y, - int offset_x, int offset_y, - unsigned int width, - unsigned int height); +grub_err_t EXPORT_FUNC (grub_video_blit_render_target) (struct grub_video_render_target *source, + enum grub_video_blit_operators oper, + int x, int y, + int offset_x, + int offset_y, + unsigned int width, + unsigned int height); grub_err_t grub_video_scroll (grub_video_color_t color, int dx, int dy); -grub_err_t grub_video_swap_buffers (void); +grub_err_t EXPORT_FUNC (grub_video_swap_buffers) (void); -grub_err_t grub_video_create_render_target (struct grub_video_render_target **result, - unsigned int width, - unsigned int height, - unsigned int mode_type); +grub_err_t EXPORT_FUNC (grub_video_create_render_target) (struct grub_video_render_target **result, + unsigned int width, + unsigned int height, + unsigned int mode_type); -grub_err_t grub_video_delete_render_target (struct grub_video_render_target *target); +grub_err_t EXPORT_FUNC (grub_video_delete_render_target) (struct grub_video_render_target *target); -grub_err_t grub_video_set_active_render_target (struct grub_video_render_target *target); +grub_err_t EXPORT_FUNC (grub_video_set_active_render_target) (struct grub_video_render_target *target); grub_err_t grub_video_get_active_render_target (struct grub_video_render_target **target); -grub_err_t grub_video_set_mode (const char *modestring, - unsigned int modemask, - unsigned int modevalue); +grub_err_t EXPORT_FUNC (grub_video_set_mode) (const char *modestring, + unsigned int modemask, + unsigned int modevalue); static inline int grub_video_check_mode_flag (unsigned int flags, unsigned int mask, @@ -326,7 +358,6 @@ grub_video_check_mode_flag (unsigned int flags, unsigned int mask, return (flag & mask) ? !! (flags & flag) : def; } -grub_video_driver_id_t -grub_video_get_driver_id (void); +grub_video_driver_id_t EXPORT_FUNC (grub_video_get_driver_id) (void); #endif /* ! GRUB_VIDEO_HEADER */ diff --git a/include/grub/video_fb.h b/include/grub/video_fb.h index 17debd69f..3046a597b 100644 --- a/include/grub/video_fb.h +++ b/include/grub/video_fb.h @@ -115,4 +115,15 @@ grub_video_fb_get_active_render_target (struct grub_video_fbrender_target **targ grub_err_t grub_video_fb_set_active_render_target (struct grub_video_fbrender_target *target); +typedef grub_err_t +(*grub_video_fb_doublebuf_update_screen_t) (struct grub_video_fbrender_target *front, + struct grub_video_fbrender_target *back); + +grub_err_t +grub_video_fb_doublebuf_blit_init (struct grub_video_fbrender_target **front, + struct grub_video_fbrender_target **back, + grub_video_fb_doublebuf_update_screen_t *update_screen, + struct grub_video_mode_info mode_info, + void *framebuf); + #endif /* ! GRUB_VIDEO_FB_HEADER */ diff --git a/include/grub/mips/yeeloong/boot.h b/include/grub/x86_64/efi/boot.h similarity index 100% rename from include/grub/mips/yeeloong/boot.h rename to include/grub/x86_64/efi/boot.h diff --git a/include/grub/x86_64/efi/serial.h b/include/grub/x86_64/efi/serial.h new file mode 100644 index 000000000..2d8563414 --- /dev/null +++ b/include/grub/x86_64/efi/serial.h @@ -0,0 +1 @@ +#include diff --git a/include/grub/x86_64/kernel.h b/include/grub/x86_64/kernel.h deleted file mode 100644 index 25ac57e40..000000000 --- a/include/grub/x86_64/kernel.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/grub/xnu.h b/include/grub/xnu.h index 6ce17c25e..6089aad34 100644 --- a/include/grub/xnu.h +++ b/include/grub/xnu.h @@ -108,5 +108,8 @@ extern grub_uint32_t grub_xnu_heap_real_start; extern grub_size_t grub_xnu_heap_size; extern void *grub_xnu_heap_start; extern struct grub_video_bitmap *grub_xnu_bitmap; +typedef enum {GRUB_XNU_BITMAP_CENTER, GRUB_XNU_BITMAP_STRETCH} + grub_xnu_bitmap_mode_t; +extern grub_xnu_bitmap_mode_t grub_xnu_bitmap_mode; extern int grub_xnu_is_64bit; #endif diff --git a/include/multiboot.h b/include/multiboot.h index c529c5c5f..fda863e85 100644 --- a/include/multiboot.h +++ b/include/multiboot.h @@ -24,6 +24,7 @@ /* How many bytes from the start of the file we search for the header. */ #define MULTIBOOT_SEARCH 8192 +#define MULTIBOOT_HEADER_ALIGN 4 /* The magic field should contain this. */ #define MULTIBOOT_HEADER_MAGIC 0x1BADB002 diff --git a/include/multiboot2.h b/include/multiboot2.h index 1b649d514..275debe75 100644 --- a/include/multiboot2.h +++ b/include/multiboot2.h @@ -23,7 +23,8 @@ #define MULTIBOOT_HEADER 1 /* How many bytes from the start of the file we search for the header. */ -#define MULTIBOOT_SEARCH 8192 +#define MULTIBOOT_SEARCH 32768 +#define MULTIBOOT_HEADER_ALIGN 8 /* The magic field should contain this. */ #define MULTIBOOT2_HEADER_MAGIC 0xe85250d6 @@ -35,58 +36,37 @@ #define MULTIBOOT_MOD_ALIGN 0x00001000 /* Alignment of the multiboot info structure. */ -#define MULTIBOOT_INFO_ALIGN 0x00000004 +#define MULTIBOOT_INFO_ALIGN 0x00000008 /* Flags set in the 'flags' member of the multiboot header. */ -/* Align all boot modules on i386 page (4KB) boundaries. */ -#define MULTIBOOT_PAGE_ALIGN 0x00000001 +#define MULTIBOOT_TAG_ALIGN 8 +#define MULTIBOOT_TAG_TYPE_END 0 +#define MULTIBOOT_TAG_TYPE_CMDLINE 1 +#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2 +#define MULTIBOOT_TAG_TYPE_MODULE 3 +#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4 +#define MULTIBOOT_TAG_TYPE_BOOTDEV 5 +#define MULTIBOOT_TAG_TYPE_MMAP 6 +#define MULTIBOOT_TAG_TYPE_VBE 7 +#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8 +#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9 +#define MULTIBOOT_TAG_TYPE_APM 10 -/* Must pass memory information to OS. */ -#define MULTIBOOT_MEMORY_INFO 0x00000002 +#define MULTIBOOT_HEADER_TAG_END 0 +#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1 +#define MULTIBOOT_HEADER_TAG_ADDRESS 2 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3 +#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4 +#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5 +#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6 -/* Must pass video information to OS. */ -#define MULTIBOOT_VIDEO_MODE 0x00000004 +#define MULTIBOOT_ARCHITECTURE_I386 0 +#define MULTIBOOT_ARCHITECTURE_MIPS32 4 +#define MULTIBOOT_HEADER_TAG_OPTIONAL 1 -/* This flag indicates the use of the address fields in the header. */ -#define MULTIBOOT_AOUT_KLUDGE 0x00010000 - -/* Flags to be set in the 'flags' member of the multiboot info structure. */ - -/* is there basic lower/upper memory information? */ -#define MULTIBOOT_INFO_MEMORY 0x00000001 -/* is there a boot device set? */ -#define MULTIBOOT_INFO_BOOTDEV 0x00000002 -/* is the command-line defined? */ -#define MULTIBOOT_INFO_CMDLINE 0x00000004 -/* are there modules to do something with? */ -#define MULTIBOOT_INFO_MODS 0x00000008 - -/* These next two are mutually exclusive */ - -/* is there a symbol table loaded? */ -#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010 -/* is there an ELF section header table? */ -#define MULTIBOOT_INFO_ELF_SHDR 0X00000020 - -/* is there a full memory map? */ -#define MULTIBOOT_INFO_MEM_MAP 0x00000040 - -/* Is there drive info? */ -#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080 - -/* Is there a config table? */ -#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100 - -/* Is there a boot loader name? */ -#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200 - -/* Is there a APM table? */ -#define MULTIBOOT_INFO_APM_TABLE 0x00000400 - -/* Is there video information? */ -#define MULTIBOOT_INFO_VBE_INFO 0x00000800 -#define MULTIBOOT_INFO_FRAMEBUFFER_INFO 0x00001000 +#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1 +#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2 #ifndef ASM_FILE @@ -100,96 +80,175 @@ struct multiboot_header /* Must be MULTIBOOT_MAGIC - see above. */ multiboot_uint32_t magic; - /* Feature flags. */ - multiboot_uint32_t flags; + /* ISA */ + multiboot_uint32_t architecture; + + /* Total header length. */ + multiboot_uint32_t header_length; /* The above fields plus this one must equal 0 mod 2^32. */ multiboot_uint32_t checksum; +}; - /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ +struct multiboot_header_tag +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; +}; + +struct multiboot_header_tag_information_request +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t requests[0]; +}; + +struct multiboot_header_tag_address +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; multiboot_uint32_t header_addr; multiboot_uint32_t load_addr; multiboot_uint32_t load_end_addr; multiboot_uint32_t bss_end_addr; - multiboot_uint32_t entry_addr; +}; - /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ - multiboot_uint32_t mode_type; +struct multiboot_header_tag_entry_address +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t entry_addr; +}; + +struct multiboot_header_tag_console_flags +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t console_flags; +}; + +struct multiboot_header_tag_framebuffer +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; multiboot_uint32_t width; multiboot_uint32_t height; multiboot_uint32_t depth; }; -/* The symbol table for a.out. */ -struct multiboot_aout_symbol_table +struct multiboot_header_tag_module_align { - multiboot_uint32_t tabsize; - multiboot_uint32_t strsize; - multiboot_uint32_t addr; - multiboot_uint32_t reserved; -}; -typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; - -/* The section header table for ELF. */ -struct multiboot_elf_section_header_table -{ - multiboot_uint32_t num; + multiboot_uint16_t type; + multiboot_uint16_t flags; multiboot_uint32_t size; - multiboot_uint32_t addr; - multiboot_uint32_t shndx; + multiboot_uint32_t width; + multiboot_uint32_t height; + multiboot_uint32_t depth; }; -typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; -struct multiboot_info +struct multiboot_color { - /* Multiboot info version number */ - multiboot_uint32_t flags; + multiboot_uint8_t red; + multiboot_uint8_t green; + multiboot_uint8_t blue; +}; - /* Available memory from BIOS */ +struct multiboot_mmap_entry +{ + multiboot_uint64_t addr; + multiboot_uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 +#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 +#define MULTIBOOT_MEMORY_NVS 4 + multiboot_uint32_t type; + multiboot_uint32_t zero; +} __attribute__((packed)); +typedef struct multiboot_mmap_entry multiboot_memory_map_t; + +struct multiboot_tag +{ + multiboot_uint32_t type; + multiboot_uint32_t size; +}; + +struct multiboot_tag_string +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + char string[0]; +}; + +struct multiboot_tag_module +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t mod_start; + multiboot_uint32_t mod_end; + char cmdline[0]; +}; + +struct multiboot_tag_basic_meminfo +{ + multiboot_uint32_t type; + multiboot_uint32_t size; multiboot_uint32_t mem_lower; multiboot_uint32_t mem_upper; +}; - /* "root" partition */ - multiboot_uint32_t boot_device; +struct multiboot_tag_bootdev +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t biosdev; + multiboot_uint32_t slice; + multiboot_uint32_t part; +}; - /* Kernel command line */ - multiboot_uint32_t cmdline; +struct multiboot_tag_mmap +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t entry_size; + multiboot_uint32_t entry_version; + struct multiboot_mmap_entry entries[0]; +}; - /* Boot-Module list */ - multiboot_uint32_t mods_count; - multiboot_uint32_t mods_addr; +struct multiboot_vbe_info_block +{ + multiboot_uint8_t external_specification[512]; +}; - union - { - multiboot_aout_symbol_table_t aout_sym; - multiboot_elf_section_header_table_t elf_sec; - } u; +struct multiboot_vbe_mode_info_block +{ + multiboot_uint8_t external_specification[256]; +}; - /* Memory Mapping buffer */ - multiboot_uint32_t mmap_length; - multiboot_uint32_t mmap_addr; +struct multiboot_tag_vbe +{ + multiboot_uint32_t type; + multiboot_uint32_t size; - /* Drive Info buffer */ - multiboot_uint32_t drives_length; - multiboot_uint32_t drives_addr; - - /* ROM configuration table */ - multiboot_uint32_t config_table; - - /* Boot Loader Name */ - multiboot_uint32_t boot_loader_name; - - /* APM table */ - multiboot_uint32_t apm_table; - - /* Video */ - multiboot_uint32_t vbe_control_info; - multiboot_uint32_t vbe_mode_info; multiboot_uint16_t vbe_mode; multiboot_uint16_t vbe_interface_seg; multiboot_uint16_t vbe_interface_off; multiboot_uint16_t vbe_interface_len; + struct multiboot_vbe_info_block vbe_control_info; + struct multiboot_vbe_mode_info_block vbe_mode_info; +}; + +struct multiboot_tag_framebuffer_common +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint64_t framebuffer_addr; multiboot_uint32_t framebuffer_pitch; multiboot_uint32_t framebuffer_width; @@ -199,12 +258,19 @@ struct multiboot_info #define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 #define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 multiboot_uint8_t framebuffer_type; + multiboot_uint16_t reserved; +}; + +struct multiboot_tag_framebuffer +{ + struct multiboot_tag_framebuffer_common common; + union { struct { - multiboot_uint32_t framebuffer_palette_addr; multiboot_uint16_t framebuffer_palette_num_colors; + struct multiboot_color framebuffer_palette[0]; }; struct { @@ -217,41 +283,31 @@ struct multiboot_info }; }; }; -typedef struct multiboot_info multiboot_info_t; -struct multiboot_color +struct multiboot_tag_elf_sections { - multiboot_uint8_t red; - multiboot_uint8_t green; - multiboot_uint8_t blue; -}; - -struct multiboot_mmap_entry -{ - multiboot_uint32_t size; - multiboot_uint64_t addr; - multiboot_uint64_t len; -#define MULTIBOOT_MEMORY_AVAILABLE 1 -#define MULTIBOOT_MEMORY_RESERVED 2 -#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 -#define MULTIBOOT_MEMORY_NVS 4 multiboot_uint32_t type; -} __attribute__((packed)); -typedef struct multiboot_mmap_entry multiboot_memory_map_t; - -struct multiboot_mod_list -{ - /* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */ - multiboot_uint32_t mod_start; - multiboot_uint32_t mod_end; - - /* Module command line */ - multiboot_uint32_t cmdline; - - /* padding to take it to 16 bytes (must be zero) */ - multiboot_uint32_t pad; + multiboot_uint32_t size; + multiboot_uint32_t num; + multiboot_uint32_t entsize; + multiboot_uint32_t shndx; + char sections[0]; +}; + +struct multiboot_tag_apm +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint16_t version; + multiboot_uint16_t cseg; + multiboot_uint32_t offset; + multiboot_uint16_t cseg_16; + multiboot_uint16_t dseg; + multiboot_uint16_t flags; + multiboot_uint16_t cseg_len; + multiboot_uint16_t cseg_16_len; + multiboot_uint16_t dseg_len; }; -typedef struct multiboot_mod_list multiboot_module_t; #endif /* ! ASM_FILE */ diff --git a/io/gzio.c b/io/gzio.c index 39f6d7b13..9bf609105 100644 --- a/io/gzio.c +++ b/io/gzio.c @@ -1121,14 +1121,13 @@ grub_gzio_open (grub_file_t io, int transparent) if (! file) return 0; - gzio = grub_malloc (sizeof (*gzio)); + gzio = grub_zalloc (sizeof (*gzio)); if (! gzio) { grub_free (file); return 0; } - grub_memset (gzio, 0, sizeof (*gzio)); gzio->file = io; file->device = io->device; diff --git a/kern/device.c b/kern/device.c index 5cfd190f3..4273fedfe 100644 --- a/kern/device.c +++ b/kern/device.c @@ -98,7 +98,10 @@ grub_device_iterate (int (*hook) (const char *name)) dev = grub_device_open (disk_name); if (! dev) - return 0; + { + grub_errno = GRUB_ERR_NONE; + return 0; + } if (dev->disk && dev->disk->has_partitions) { @@ -152,7 +155,7 @@ grub_device_iterate (int (*hook) (const char *name)) grub_free (partition_name); grub_free (p); return 1; - } + } grub_free (partition_name); p->next = ents; diff --git a/kern/disk.c b/kern/disk.c index 544896f2f..ccd5f200f 100644 --- a/kern/disk.c +++ b/kern/disk.c @@ -330,6 +330,7 @@ grub_disk_open (const char *name) void grub_disk_close (grub_disk_t disk) { + grub_partition_t part; grub_dprintf ("disk", "Closing `%s'.\n", disk->name); if (disk->dev && disk->dev->close) @@ -338,7 +339,12 @@ grub_disk_close (grub_disk_t disk) /* Reset the timer. */ grub_last_time = grub_get_time_ms (); - grub_free (disk->partition); + while (disk->partition) + { + part = disk->partition->parent; + grub_free (disk->partition); + disk->partition = part; + } grub_free ((void *) disk->name); grub_free (disk); } @@ -349,18 +355,19 @@ grub_disk_close (grub_disk_t disk) - Verify that the range is inside the partition. */ static grub_err_t grub_disk_adjust_range (grub_disk_t disk, grub_disk_addr_t *sector, - grub_off_t *offset, grub_size_t size) + grub_off_t *offset, grub_size_t size) { + grub_partition_t part; *sector += *offset >> GRUB_DISK_SECTOR_BITS; *offset &= GRUB_DISK_SECTOR_SIZE - 1; - if (disk->partition) + for (part = disk->partition; part; part = part->parent) { grub_disk_addr_t start; grub_uint64_t len; - start = grub_partition_get_start (disk->partition); - len = grub_partition_get_len (disk->partition); + start = part->start; + len = part->len; if (*sector >= len || len - *sector < ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1) @@ -441,7 +448,7 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, grub_errno = GRUB_ERR_NONE; - num = ((size + GRUB_DISK_SECTOR_SIZE - 1) + num = ((size + real_offset + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS); p = grub_realloc (tmp_buf, num << GRUB_DISK_SECTOR_BITS); @@ -464,12 +471,14 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, if (disk->read_hook) while (size) { + grub_size_t to_read = (size > GRUB_DISK_SECTOR_SIZE) ? GRUB_DISK_SECTOR_SIZE : size; (disk->read_hook) (sector, real_offset, - ((size > GRUB_DISK_SECTOR_SIZE) - ? GRUB_DISK_SECTOR_SIZE - : size)); + to_read); + if (grub_errno != GRUB_ERR_NONE) + goto finish; + sector++; - size -= GRUB_DISK_SECTOR_SIZE - real_offset; + size -= to_read - real_offset; real_offset = 0; } diff --git a/kern/dl.c b/kern/dl.c index 4735a004a..19ee13243 100644 --- a/kern/dl.c +++ b/kern/dl.c @@ -348,7 +348,7 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e) sym->st_value = (Elf_Addr) grub_dl_resolve_symbol (name); if (! sym->st_value) return grub_error (GRUB_ERR_BAD_MODULE, - "the symbol `%s' not found", name); + "symbol not found: `%s'", name); } else { @@ -469,7 +469,7 @@ grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e) return GRUB_ERR_NONE; } -#ifndef GRUB_UTIL +#if !GRUB_NO_MODULES int grub_dl_ref (grub_dl_t mod) { diff --git a/kern/efi/efi.c b/kern/efi/efi.c index c6ce04c5e..d8b225535 100644 --- a/kern/efi/efi.c +++ b/kern/efi/efi.c @@ -1,7 +1,7 @@ /* efi.c - generic EFI support */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc. + * Copyright (C) 2006,2007,2008,2009,2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -162,6 +162,8 @@ grub_exit (void) for (;;) ; } +/* On i386, a firmware-independant grub_reboot() is provided by realmode.S. */ +#ifndef __i386__ void grub_reboot (void) { @@ -169,6 +171,7 @@ grub_reboot (void) efi_call_4 (grub_efi_system_table->runtime_services->reset_system, GRUB_EFI_RESET_COLD, GRUB_EFI_SUCCESS, 0, NULL); } +#endif void grub_halt (void) diff --git a/kern/efi/init.c b/kern/efi/init.c index a0b4ff779..afd21055d 100644 --- a/kern/efi/init.c +++ b/kern/efi/init.c @@ -24,7 +24,6 @@ #include #include #include -#include void grub_efi_init (void) diff --git a/kern/i386/coreboot/init.c b/kern/i386/coreboot/init.c index 550a2a60a..08f2cce3a 100644 --- a/kern/i386/coreboot/init.c +++ b/kern/i386/coreboot/init.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -33,8 +32,10 @@ #include #include #include -#include #include +#ifdef GRUB_MACHINE_QEMU +#include +#endif #define GRUB_FLOPPY_REG_DIGITAL_OUTPUT 0x3f2 @@ -67,12 +68,6 @@ grub_exit (void) grub_cpu_idle (); } -void -grub_arch_sync_caches (void *address __attribute__ ((unused)), - grub_size_t len __attribute__ ((unused))) -{ -} - void grub_machine_init (void) { @@ -150,6 +145,6 @@ grub_arch_modules_addr (void) #ifdef GRUB_MACHINE_QEMU return grub_core_entry_addr + grub_kernel_image_size; #else - return ALIGN_UP((grub_addr_t) _end, GRUB_MOD_ALIGN); + return ALIGN_UP((grub_addr_t) _end, GRUB_KERNEL_MACHINE_MOD_ALIGN); #endif } diff --git a/kern/i386/coreboot/startup.S b/kern/i386/coreboot/startup.S index e94950aae..bdefb69bc 100644 --- a/kern/i386/coreboot/startup.S +++ b/kern/i386/coreboot/startup.S @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include @@ -42,7 +42,7 @@ _start: * This is a special data area at a fixed offset from the beginning. */ - . = _start + GRUB_KERNEL_CPU_PREFIX + . = _start + GRUB_KERNEL_MACHINE_PREFIX VARIABLE(grub_prefix) /* to be filled by grub-mkimage */ @@ -51,7 +51,7 @@ VARIABLE(grub_prefix) * Leave some breathing room for the prefix. */ - . = _start + GRUB_KERNEL_CPU_DATA_END + . = _start + GRUB_KERNEL_MACHINE_DATA_END /* * Support for booting GRUB from a Multiboot boot loader (e.g. GRUB itself). diff --git a/kern/i386/efi/init.c b/kern/i386/efi/init.c index e1950d758..f73f828c6 100644 --- a/kern/i386/efi/init.c +++ b/kern/i386/efi/init.c @@ -45,9 +45,3 @@ grub_machine_set_prefix (void) { grub_efi_set_prefix (); } - -void -grub_arch_sync_caches (void *address __attribute__ ((unused)), - grub_size_t len __attribute__ ((unused))) -{ -} diff --git a/kern/i386/efi/startup.S b/kern/i386/efi/startup.S index b88628010..5b464ab83 100644 --- a/kern/i386/efi/startup.S +++ b/kern/i386/efi/startup.S @@ -1,7 +1,7 @@ /* startup.S - bootstrap GRUB itself */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2006,2007 Free Software Foundation, Inc. + * Copyright (C) 2006,2007,2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -62,3 +62,5 @@ codestart: movl %eax, EXT_C(grub_efi_system_table) call EXT_C(grub_main) ret + +#include "../realmode.S" diff --git a/kern/i386/ieee1275/init.c b/kern/i386/ieee1275/init.c index 7658ee1a7..9fb98739b 100644 --- a/kern/i386/ieee1275/init.c +++ b/kern/i386/ieee1275/init.c @@ -26,9 +26,3 @@ void grub_stop_floppy (void) { } - -void -grub_arch_sync_caches (void *address __attribute__ ((unused)), - grub_size_t len __attribute__ ((unused))) -{ -} diff --git a/kern/i386/ieee1275/startup.S b/kern/i386/ieee1275/startup.S index 35258adb6..c0a08a954 100644 --- a/kern/i386/ieee1275/startup.S +++ b/kern/i386/ieee1275/startup.S @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -43,7 +42,7 @@ _start: * This is a special data area at a fixed offset from the beginning. */ - . = _start + GRUB_KERNEL_CPU_PREFIX + . = _start + GRUB_KERNEL_MACHINE_PREFIX VARIABLE(grub_prefix) /* to be filled by grub-mkimage */ @@ -52,7 +51,7 @@ VARIABLE(grub_prefix) * Leave some breathing room for the prefix. */ - . = _start + GRUB_KERNEL_CPU_DATA_END + . = _start + GRUB_KERNEL_MACHINE_DATA_END codestart: movl %eax, EXT_C(grub_ieee1275_entry_fn) diff --git a/kern/i386/pc/init.c b/kern/i386/pc/init.c index e45ad3971..fa646df19 100644 --- a/kern/i386/pc/init.c +++ b/kern/i386/pc/init.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -46,12 +47,6 @@ static int num_regions; grub_addr_t grub_os_area_addr; grub_size_t grub_os_area_size; -void -grub_arch_sync_caches (void *address __attribute__ ((unused)), - grub_size_t len __attribute__ ((unused))) -{ -} - static char * make_install_device (void) { @@ -62,22 +57,28 @@ make_install_device (void) { /* No hardcoded root partition - make it from the boot drive and the partition number encoded at the install time. */ - grub_snprintf (dev, sizeof (dev), - "(%cd%u", (grub_boot_drive & 0x80) ? 'h' : 'f', - grub_boot_drive & 0x7f); - ptr += grub_strlen (ptr); + if (grub_boot_drive == GRUB_BOOT_MACHINE_PXE_DL) + { + grub_strcpy (dev, "(pxe"); + ptr += sizeof ("(pxe") - 1; + } + else + { + grub_snprintf (dev, sizeof (dev), + "(%cd%u", (grub_boot_drive & 0x80) ? 'h' : 'f', + grub_boot_drive & 0x7f); + ptr += grub_strlen (ptr); - if (grub_install_dos_part >= 0) - grub_snprintf (ptr, sizeof (dev) - (ptr - dev), - ",%u", grub_install_dos_part + 1); + if (grub_install_dos_part >= 0) + grub_snprintf (ptr, sizeof (dev) - (ptr - dev), + ",%u", grub_install_dos_part + 1); + ptr += grub_strlen (ptr); - ptr += grub_strlen (ptr); - - if (grub_install_bsd_part >= 0) - grub_snprintf (ptr, sizeof (dev) - (ptr - dev), ",%c", - grub_install_bsd_part + 'a'); - - ptr += grub_strlen (ptr); + if (grub_install_bsd_part >= 0) + grub_snprintf (ptr, sizeof (dev) - (ptr - dev), ",%c", + grub_install_bsd_part + 'a'); + ptr += grub_strlen (ptr); + } grub_snprintf (ptr, sizeof (dev) - (ptr - dev), ")%s", grub_prefix); grub_strcpy (grub_prefix, dev); diff --git a/kern/i386/pc/startup.S b/kern/i386/pc/startup.S index 23f3f398e..374277767 100644 --- a/kern/i386/pc/startup.S +++ b/kern/i386/pc/startup.S @@ -53,7 +53,7 @@ #include #include -#define ABS(x) ((x) - _start + GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200) +#define ABS(x) ((x) - LOCAL (base) + GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200) .file "startup.S" @@ -66,16 +66,15 @@ .globl start, _start start: _start: +LOCAL (base): /* * Guarantee that "main" is loaded at 0x0:0x8200. */ -#ifdef APPLE_CC - codestart_abs = ABS(codestart) - 0x10000 - ljmp $0, $(codestart_abs) +#ifdef __APPLE__ + ljmp $0, $(ABS(LOCAL (codestart)) - 0x10000) #else - ljmp $0, $ABS(codestart) + ljmp $0, $ABS(LOCAL (codestart)) #endif - /* * Compatibility version number * @@ -183,7 +182,7 @@ multiboot_trampoline: .code16 /* the real mode code continues... */ -codestart: +LOCAL (codestart): cli /* we're not safe here! */ /* set up %ds, %ss, and %es */ @@ -1156,7 +1155,7 @@ FUNCTION(grub_console_real_putchar) */ /* this table is used in translate_keycode below */ -translation_table: +LOCAL (translation_table): .word GRUB_CONSOLE_KEY_LEFT, GRUB_TERM_LEFT .word GRUB_CONSOLE_KEY_RIGHT, GRUB_TERM_RIGHT .word GRUB_CONSOLE_KEY_UP, GRUB_TERM_UP @@ -1178,11 +1177,10 @@ translate_keycode: pushw %bx pushw %si -#ifdef APPLE_CC - translation_table_abs = ABS (translation_table) - 0x10000 - movw $(translation_table_abs), %si +#ifdef __APPLE__ + movw $(ABS(LOCAL (translation_table)) - 0x10000), %si #else - movw $ABS(translation_table), %si + movw $ABS(LOCAL (translation_table)), %si #endif 1: lodsw @@ -1446,47 +1444,6 @@ FUNCTION(grub_console_setcursor) popl %ebp ret -/* - * grub_getrtsecs() - * if a seconds value can be read, read it and return it (BCD), - * otherwise return 0xFF - * BIOS call "INT 1AH Function 02H" to check whether a character is pending - * Call with %ah = 0x2 - * Return: - * If RT Clock can give correct values - * %ch = hour (BCD) - * %cl = minutes (BCD) - * %dh = seconds (BCD) - * %dl = daylight savings time (00h std, 01h daylight) - * Carry flag = clear - * else - * Carry flag = set - * (this indicates that the clock is updating, or - * that it isn't running) - */ -FUNCTION(grub_getrtsecs) - pushl %ebp - - call prot_to_real /* enter real mode */ - .code16 - - clc - movb $0x2, %ah - int $0x1a - - DATA32 jnc gottime - movb $0xff, %dh - -gottime: - DATA32 call real_to_prot - .code32 - - movb %dh, %al - - popl %ebp - ret - - /* * grub_get_rtc() * return the real time in ticks, of which there are about @@ -1541,33 +1498,6 @@ FUNCTION(grub_vga_set_mode) popl %ebp ret - -/* - * unsigned char *grub_vga_get_font (void) - */ -FUNCTION(grub_vga_get_font) - pushl %ebp - pushl %ebx - - call prot_to_real - .code16 - movw $0x1130, %ax - movb $0x06, %bh - int $0x10 - movw %es, %bx - movw %bp, %dx - DATA32 call real_to_prot - .code32 - - movzwl %bx, %ecx - shll $4, %ecx - movw %dx, %ax - addl %ecx, %eax - - popl %ebx - popl %ebp - ret - /* * grub_vbe_bios_status_t grub_vbe_get_controller_info (struct grub_vbe_info_block *controller_info) * diff --git a/kern/i386/qemu/startup.S b/kern/i386/qemu/startup.S index 7484650b2..dc40cc4a2 100644 --- a/kern/i386/qemu/startup.S +++ b/kern/i386/qemu/startup.S @@ -27,7 +27,7 @@ _start: jmp codestart - . = _start + GRUB_KERNEL_MACHINE_CORE_ENTRY_ADDR + . = _start + GRUB_KERNEL_I386_QEMU_CORE_ENTRY_ADDR VARIABLE(grub_core_entry_addr) .long 0 VARIABLE(grub_kernel_image_size) diff --git a/kern/i386/realmode.S b/kern/i386/realmode.S index a74eb1217..578c8d2a8 100644 --- a/kern/i386/realmode.S +++ b/kern/i386/realmode.S @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2009 Free Software Foundation, Inc. + * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2009,2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,6 +16,7 @@ * along with GRUB. If not, see . */ +#include /* * Note: These functions defined in this file may be called from C. diff --git a/kern/ieee1275/cmain.c b/kern/ieee1275/cmain.c index c1185f82c..cdc8ca051 100644 --- a/kern/ieee1275/cmain.c +++ b/kern/ieee1275/cmain.c @@ -20,7 +20,6 @@ #include #include #include -#include #include int (*grub_ieee1275_entry_fn) (void *); diff --git a/kern/ieee1275/ieee1275.c b/kern/ieee1275/ieee1275.c index 8a5773c23..9e2919172 100644 --- a/kern/ieee1275/ieee1275.c +++ b/kern/ieee1275/ieee1275.c @@ -284,8 +284,8 @@ grub_ieee1275_read (grub_ieee1275_ihandle_t ihandle, void *buffer, } int -grub_ieee1275_seek (grub_ieee1275_ihandle_t ihandle, int pos_hi, - int pos_lo, grub_ssize_t *result) +grub_ieee1275_seek (grub_ieee1275_ihandle_t ihandle, grub_disk_addr_t pos, + grub_ssize_t *result) { struct write_args { @@ -299,8 +299,15 @@ grub_ieee1275_seek (grub_ieee1275_ihandle_t ihandle, int pos_hi, INIT_IEEE1275_COMMON (&args.common, "seek", 3, 1); args.ihandle = ihandle; - args.pos_hi = (grub_ieee1275_cell_t) pos_hi; - args.pos_lo = (grub_ieee1275_cell_t) pos_lo; + /* To prevent stupid gcc warning. */ +#if GRUB_IEEE1275_CELL_SIZEOF >= 8 + args.pos_hi = 0; + args.pos_lo = pos; +#else + args.pos_hi = (grub_ieee1275_cell_t) (pos >> (8 * GRUB_IEEE1275_CELL_SIZEOF)); + args.pos_lo = (grub_ieee1275_cell_t) + (pos & ((1ULL << (8 * GRUB_IEEE1275_CELL_SIZEOF)) - 1)); +#endif if (IEEE1275_CALL_ENTRY_FN (&args) == -1) return -1; diff --git a/kern/ieee1275/init.c b/kern/ieee1275/init.c index 04e4e2dca..f79b82776 100644 --- a/kern/ieee1275/init.c +++ b/kern/ieee1275/init.c @@ -29,20 +29,19 @@ #include #include #include -#include -#include #include #include +#include /* The minimal heap size we can live with. */ #define HEAP_MIN_SIZE (unsigned long) (2 * 1024 * 1024) /* The maximum heap size we're going to claim */ -#define HEAP_MAX_SIZE (unsigned long) (4 * 1024 * 1024) +#define HEAP_MAX_SIZE (unsigned long) (32 * 1024 * 1024) /* If possible, we will avoid claiming heap above this address, because it seems to cause relocation problems with OSes that link at 4 MiB */ -#define HEAP_MAX_ADDR (unsigned long) (4 * 1024 * 1024) +#define HEAP_MAX_ADDR (unsigned long) (32 * 1024 * 1024) extern char _start[]; extern char _end[]; @@ -75,10 +74,6 @@ grub_machine_set_prefix (void) char *filename; char *prefix; - if (grub_env_get ("prefix")) - /* We already set prefix in grub_machine_init(). */ - return; - if (grub_prefix[0]) { grub_env_set ("prefix", grub_prefix); @@ -288,5 +283,5 @@ grub_get_rtc (void) grub_addr_t grub_arch_modules_addr (void) { - return ALIGN_UP((grub_addr_t) _end + GRUB_MOD_GAP, GRUB_MOD_ALIGN); + return ALIGN_UP((grub_addr_t) _end + GRUB_KERNEL_MACHINE_MOD_GAP, GRUB_KERNEL_MACHINE_MOD_ALIGN); } diff --git a/kern/ieee1275/openfw.c b/kern/ieee1275/openfw.c index 5f0aad119..5693f3be0 100644 --- a/kern/ieee1275/openfw.c +++ b/kern/ieee1275/openfw.c @@ -21,7 +21,6 @@ #include #include #include -#include #include enum grub_ieee1275_parse_type @@ -68,38 +67,33 @@ grub_children_iterate (char *devpath, { struct grub_ieee1275_devalias alias; grub_ssize_t actual; - char *fullname; if (grub_ieee1275_get_property (child, "device_type", childtype, IEEE1275_MAX_PROP_LEN, &actual)) + childtype[0] = 0; + + if (dev == child) continue; if (grub_ieee1275_package_to_path (child, childpath, IEEE1275_MAX_PATH_LEN, &actual)) continue; + if (grub_strcmp (devpath, childpath) == 0) + continue; + if (grub_ieee1275_get_property (child, "name", childname, IEEE1275_MAX_PROP_LEN, &actual)) continue; - fullname = grub_xasprintf ("%s/%s", devpath, childname); - if (!fullname) - { - grub_free (childname); - grub_free (childpath); - grub_free (childtype); - return 0; - } - alias.type = childtype; alias.path = childpath; - alias.name = fullname; + alias.name = childname; ret = hook (&alias); - grub_free (fullname); if (ret) break; } - while (grub_ieee1275_peer (child, &child)); + while (grub_ieee1275_peer (child, &child) != -1); grub_free (childname); grub_free (childpath); @@ -108,6 +102,20 @@ grub_children_iterate (char *devpath, return ret; } +int +grub_ieee1275_devices_iterate (int (*hook) (struct grub_ieee1275_devalias *alias)) +{ + auto int it_through (struct grub_ieee1275_devalias *alias); + int it_through (struct grub_ieee1275_devalias *alias) + { + if (hook (alias)) + return 1; + return grub_children_iterate (alias->path, it_through); + } + + return grub_children_iterate ("/", it_through); +} + /* Iterate through all device aliases. This function can be used to find a device of a specific type. */ int @@ -135,7 +143,7 @@ grub_devalias_iterate (int (*hook) (struct grub_ieee1275_devalias *alias)) /* Find the first property. */ aliasname[0] = '\0'; - while (grub_ieee1275_next_property (aliases, aliasname, aliasname)) + while (grub_ieee1275_next_property (aliases, aliasname, aliasname) > 0) { grub_ieee1275_phandle_t dev; grub_ssize_t pathlen; @@ -199,9 +207,9 @@ nextprop: } /* Call the "map" method of /chosen/mmu. */ -static int -grub_map (grub_addr_t phys, grub_addr_t virt, grub_uint32_t size, - grub_uint8_t mode) +int +grub_ieee1275_map (grub_addr_t phys, grub_addr_t virt, grub_size_t size, + grub_uint32_t mode) { struct map_args { struct grub_ieee1275_common_hdr common; @@ -210,17 +218,30 @@ grub_map (grub_addr_t phys, grub_addr_t virt, grub_uint32_t size, grub_ieee1275_cell_t mode; grub_ieee1275_cell_t size; grub_ieee1275_cell_t virt; - grub_ieee1275_cell_t phys; +#ifdef GRUB_MACHINE_SPARC64 + grub_ieee1275_cell_t phys_high; +#endif + grub_ieee1275_cell_t phys_low; grub_ieee1275_cell_t catch_result; } args; - INIT_IEEE1275_COMMON (&args.common, "call-method", 6, 1); + INIT_IEEE1275_COMMON (&args.common, "call-method", +#ifdef GRUB_MACHINE_SPARC64 + 7, +#else + 6, +#endif + 1); args.method = (grub_ieee1275_cell_t) "map"; args.ihandle = grub_ieee1275_mmu; - args.phys = phys; +#ifdef GRUB_MACHINE_SPARC64 + args.phys_high = 0; +#endif + args.phys_low = phys; args.virt = virt; args.size = size; args.mode = mode; /* Format is WIMG0PP. */ + args.catch_result = (grub_ieee1275_cell_t) -1; if (IEEE1275_CALL_ENTRY_FN (&args) == -1) return -1; @@ -235,7 +256,7 @@ grub_claimmap (grub_addr_t addr, grub_size_t size) return -1; if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_REAL_MODE) - && grub_map (addr, addr, size, 0x00)) + && grub_ieee1275_map (addr, addr, size, 0x00)) { grub_printf ("map failed: address 0x%llx, size 0x%llx\n", (long long) addr, (long long) size); @@ -374,7 +395,7 @@ grub_ieee1275_encode_devname (const char *path) char *partition = grub_ieee1275_parse_args (path, GRUB_PARSE_PARTITION); char *encoding; - if (partition) + if (partition && partition[0]) { unsigned int partno = grub_strtoul (partition, 0, 0); @@ -405,8 +426,9 @@ grub_reboot (void) void grub_halt (void) { - /* Not standardized. We try both known commands. */ + /* Not standardized. We try three known commands. */ grub_ieee1275_interpret ("shut-down", 0); grub_ieee1275_interpret ("power-off", 0); + grub_ieee1275_interpret ("poweroff", 0); } diff --git a/kern/main.c b/kern/main.c index 456105378..2f2c951ab 100644 --- a/kern/main.c +++ b/kern/main.c @@ -53,6 +53,25 @@ grub_module_iterate (int (*hook) (struct grub_module_header *header)) } } +/* This is actualy platform-independant but used only on yeeloong and sparc. */ +#if defined (GRUB_MACHINE_MIPS_YEELOONG) || defined (GRUB_MACHINE_SPARC64) +grub_addr_t +grub_modules_get_end (void) +{ + struct grub_module_info *modinfo; + grub_addr_t modbase; + + modbase = grub_arch_modules_addr (); + modinfo = (struct grub_module_info *) modbase; + + /* Check if there are any modules. */ + if ((modinfo == 0) || modinfo->magic != GRUB_MODULE_MAGIC) + return modbase; + + return modbase + modinfo->size; +} +#endif + /* Load all modules in core. */ static void grub_load_modules (void) @@ -68,6 +87,9 @@ grub_load_modules (void) (header->size - sizeof (struct grub_module_header)))) grub_fatal ("%s", grub_errmsg); + if (grub_errno) + grub_print_error (); + return 0; } diff --git a/kern/mips/init.c b/kern/mips/init.c index 5adcedcbb..f220108d4 100644 --- a/kern/mips/init.c +++ b/kern/mips/init.c @@ -18,7 +18,6 @@ #include #include -#include void grub_machine_set_prefix (void) diff --git a/kern/mips/startup.S b/kern/mips/startup.S index 5e3fb7ad5..1d18131be 100644 --- a/kern/mips/startup.S +++ b/kern/mips/startup.S @@ -18,8 +18,9 @@ */ #include -#include +#include #include +#include #define BASE_ADDR 8 @@ -32,13 +33,13 @@ _start: start: bal codestart base: - . = _start + GRUB_KERNEL_CPU_COMPRESSED_SIZE + . = _start + GRUB_KERNEL_MACHINE_COMPRESSED_SIZE compressed_size: .long 0 - . = _start + GRUB_KERNEL_CPU_TOTAL_MODULE_SIZE + . = _start + GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE total_module_size: .long 0 - . = _start + GRUB_KERNEL_CPU_KERNEL_IMAGE_SIZE + . = _start + GRUB_KERNEL_MACHINE_KERNEL_IMAGE_SIZE kernel_image_size: .long 0 codestart: @@ -105,10 +106,10 @@ argdone: #endif /* Decompress the payload. */ - addiu $a0, $s0, GRUB_KERNEL_CPU_RAW_SIZE - BASE_ADDR + addiu $a0, $s0, GRUB_KERNEL_MACHINE_RAW_SIZE - BASE_ADDR lui $a1, %hi(compressed) addiu $a1, %lo(compressed) - lw $a2, (GRUB_KERNEL_CPU_COMPRESSED_SIZE - BASE_ADDR)($s0) + lw $a2, (GRUB_KERNEL_MACHINE_COMPRESSED_SIZE - BASE_ADDR)($s0) move $s1, $a1 /* $a0 contains source compressed address, $a1 is destination, @@ -134,9 +135,9 @@ reloccont: addiu $t1, %lo(cont) jr $t1 - . = _start + GRUB_KERNEL_CPU_RAW_SIZE + . = _start + GRUB_KERNEL_MACHINE_RAW_SIZE compressed: - . = _start + GRUB_KERNEL_CPU_PREFIX + . = _start + GRUB_KERNEL_MACHINE_PREFIX VARIABLE(grub_prefix) @@ -146,7 +147,7 @@ VARIABLE(grub_prefix) * Leave some breathing room for the prefix. */ - . = _start + GRUB_KERNEL_CPU_DATA_END + . = _start + GRUB_KERNEL_MACHINE_DATA_END #ifdef GRUB_MACHINE_MIPS_YEELOONG VARIABLE (grub_arch_busclock) .long 0 @@ -171,17 +172,17 @@ cont: /* Move the modules out of BSS. */ lui $t1, %hi(_start) addiu $t1, %lo(_start) - lw $t2, (GRUB_KERNEL_CPU_KERNEL_IMAGE_SIZE - BASE_ADDR)($s0) + lw $t2, (GRUB_KERNEL_MACHINE_KERNEL_IMAGE_SIZE - BASE_ADDR)($s0) addu $t2, $t1, $t2 lui $t1, %hi(_end) addiu $t1, %lo(_end) - addiu $t1, (GRUB_MOD_ALIGN-1) - li $t3, (GRUB_MOD_ALIGN-1) + addiu $t1, (GRUB_KERNEL_MACHINE_MOD_ALIGN-1) + li $t3, (GRUB_KERNEL_MACHINE_MOD_ALIGN-1) nor $t3, $t3, $0 and $t1, $t1, $t3 - lw $t3, (GRUB_KERNEL_CPU_TOTAL_MODULE_SIZE - BASE_ADDR)($s0) + lw $t3, (GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE - BASE_ADDR)($s0) /* Backward copy. */ add $t1, $t1, $t3 diff --git a/kern/mips/yeeloong/init.c b/kern/mips/yeeloong/init.c index 14e8a39a2..47aa774a0 100644 --- a/kern/mips/yeeloong/init.c +++ b/kern/mips/yeeloong/init.c @@ -26,13 +26,12 @@ #include #include #include -#include extern void grub_video_sm712_init (void); -extern void grub_video_video_init (void); -extern void grub_video_bitmap_init (void); -extern void grub_font_manager_init (void); -extern void grub_term_gfxterm_init (void); +extern void grub_video_init (void); +extern void grub_bitmap_init (void); +extern void grub_font_init (void); +extern void grub_gfxterm_init (void); extern void grub_at_keyboard_init (void); /* FIXME: use interrupt to count high. */ @@ -63,45 +62,23 @@ grub_machine_mmap_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, return GRUB_ERR_NONE; } - -static void * -get_modules_end (void) -{ - struct grub_module_info *modinfo; - struct grub_module_header *header; - grub_addr_t modbase; - - modbase = grub_arch_modules_addr (); - modinfo = (struct grub_module_info *) modbase; - - /* Check if there are any modules. */ - if ((modinfo == 0) || modinfo->magic != GRUB_MODULE_MAGIC) - return modinfo; - - for (header = (struct grub_module_header *) (modbase + modinfo->offset); - header < (struct grub_module_header *) (modbase + modinfo->size); - header = (struct grub_module_header *) ((char *) header + header->size)); - - return header; -} - void grub_machine_init (void) { - void *modend; - modend = get_modules_end (); - grub_mm_init_region (modend, (grub_arch_memsize << 20) - - (((grub_addr_t) modend) - GRUB_ARCH_LOWMEMVSTART)); + grub_addr_t modend; + modend = grub_modules_get_end (); + grub_mm_init_region ((void *) modend, (grub_arch_memsize << 20) + - (modend - GRUB_ARCH_LOWMEMVSTART)); /* FIXME: use upper memory as well. */ grub_install_get_time_ms (grub_rtc_get_time_ms); /* Initialize output terminal (can't be done earlier, as gfxterm relies on a working heap. */ grub_video_sm712_init (); - grub_video_video_init (); - grub_video_bitmap_init (); - grub_font_manager_init (); - grub_term_gfxterm_init (); + grub_video_init (); + grub_bitmap_init (); + grub_font_init (); + grub_gfxterm_init (); grub_at_keyboard_init (); } diff --git a/kern/misc.c b/kern/misc.c index ba31d24bb..ccc01d43f 100644 --- a/kern/misc.c +++ b/kern/misc.c @@ -35,7 +35,7 @@ grub_iswordseparator (int c) } /* grub_gettext_dummy is not translating anything. */ -const char * +static const char * grub_gettext_dummy (const char *s) { return s; @@ -206,7 +206,6 @@ grub_vprintf (const char *fmt, va_list args) int ret; ret = grub_vsnprintf_real (0, 0, fmt, args); - grub_refresh (); return ret; } @@ -876,9 +875,6 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt, va_list ar if (str) *str = '\0'; - if (count && !str) - grub_refresh (); - return count; } @@ -975,6 +971,10 @@ grub_utf8_to_ucs4 (grub_uint32_t *dest, grub_size_t destsize, { /* invalid */ code = '?'; + /* Character c may be valid, don't eat it. */ + src--; + if (srcsize != (grub_size_t)-1) + srcsize++; count = 0; } else @@ -1058,7 +1058,7 @@ grub_abort (void) void abort (void) __attribute__ ((alias ("grub_abort"))); #endif -#ifdef NEED_ENABLE_EXECUTE_STACK +#if defined(NEED_ENABLE_EXECUTE_STACK) && !defined(GRUB_UTIL) /* Some gcc versions generate a call to this function in trampolines for nested functions. */ void __enable_execute_stack (void *addr __attribute__ ((unused))) @@ -1066,3 +1066,12 @@ void __enable_execute_stack (void *addr __attribute__ ((unused))) } #endif +#if defined (NEED_REGISTER_FRAME_INFO) && !defined(GRUB_UTIL) +void __register_frame_info (void) +{ +} + +void __deregister_frame_info (void) +{ +} +#endif diff --git a/kern/mm.c b/kern/mm.c index ef97b018e..3237b040c 100644 --- a/kern/mm.c +++ b/kern/mm.c @@ -388,7 +388,7 @@ grub_free (void *ptr) do { grub_printf ("%s:%d: q=%p, q->size=0x%x, q->magic=0x%x\n", - __FILE__, __LINE__, q, q->size, q->magic); + GRUB_FILE, __LINE__, q, q->size, q->magic); q = q->next; } while (q != r->first); diff --git a/kern/parser.c b/kern/parser.c index dd4608ba9..80312b9b4 100644 --- a/kern/parser.c +++ b/kern/parser.c @@ -26,32 +26,31 @@ /* All the possible state transitions on the command line. If a transition can not be found, it is assumed that there is no transition and keep_value is assumed to be 1. */ -static struct grub_parser_state_transition state_transitions[] = -{ - { GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_QUOTE, '\'', 0}, - { GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_DQUOTE, '\"', 0}, - { GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_VAR, '$', 0}, - { GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_ESC, '\\', 0}, +static struct grub_parser_state_transition state_transitions[] = { + {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_QUOTE, '\'', 0}, + {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_DQUOTE, '\"', 0}, + {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_VAR, '$', 0}, + {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_ESC, '\\', 0}, - { GRUB_PARSER_STATE_ESC, GRUB_PARSER_STATE_TEXT, 0, 1}, + {GRUB_PARSER_STATE_ESC, GRUB_PARSER_STATE_TEXT, 0, 1}, - { GRUB_PARSER_STATE_QUOTE, GRUB_PARSER_STATE_TEXT, '\'', 0}, + {GRUB_PARSER_STATE_QUOTE, GRUB_PARSER_STATE_TEXT, '\'', 0}, - { GRUB_PARSER_STATE_DQUOTE, GRUB_PARSER_STATE_TEXT, '\"', 0}, - { GRUB_PARSER_STATE_DQUOTE, GRUB_PARSER_STATE_QVAR, '$', 0}, + {GRUB_PARSER_STATE_DQUOTE, GRUB_PARSER_STATE_TEXT, '\"', 0}, + {GRUB_PARSER_STATE_DQUOTE, GRUB_PARSER_STATE_QVAR, '$', 0}, - { GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME2, '{', 0}, - { GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME, 0, 1}, - { GRUB_PARSER_STATE_VARNAME, GRUB_PARSER_STATE_TEXT, ' ', 1}, - { GRUB_PARSER_STATE_VARNAME2, GRUB_PARSER_STATE_TEXT, '}', 0}, + {GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME2, '{', 0}, + {GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME, 0, 1}, + {GRUB_PARSER_STATE_VARNAME, GRUB_PARSER_STATE_TEXT, ' ', 1}, + {GRUB_PARSER_STATE_VARNAME2, GRUB_PARSER_STATE_TEXT, '}', 0}, - { GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME2, '{', 0}, - { GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME, 0, 1}, - { GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_TEXT, '\"', 0}, - { GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_DQUOTE, ' ', 1}, - { GRUB_PARSER_STATE_QVARNAME2, GRUB_PARSER_STATE_DQUOTE, '}', 0}, + {GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME2, '{', 0}, + {GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME, 0, 1}, + {GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_TEXT, '\"', 0}, + {GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_DQUOTE, ' ', 1}, + {GRUB_PARSER_STATE_QVARNAME2, GRUB_PARSER_STATE_DQUOTE, '}', 0}, - { 0, 0, 0, 0} + {0, 0, 0, 0} }; @@ -74,17 +73,17 @@ grub_parser_cmdline_state (grub_parser_state_t state, char c, char *result) if (transition->input == c) break; - if (transition->input == ' ' && ! grub_isalpha (c) - && ! grub_isdigit (c) && c != '_') + if (transition->input == ' ' && !grub_isalpha (c) + && !grub_isdigit (c) && c != '_') break; /* A less perfect match was found, use this one if no exact - match can be found. */ + match can be found. */ if (transition->input == 0) break; } - if (! transition->from_state) + if (!transition->from_state) transition = &default_transition; if (transition->keep_value) @@ -113,43 +112,44 @@ grub_parser_split_cmdline (const char *cmdline, grub_reader_getline_t getline, auto int check_varstate (grub_parser_state_t s); int check_varstate (grub_parser_state_t s) - { - return (s == GRUB_PARSER_STATE_VARNAME - || s == GRUB_PARSER_STATE_VARNAME2 - || s == GRUB_PARSER_STATE_QVARNAME - || s == GRUB_PARSER_STATE_QVARNAME2); - } + { + return (s == GRUB_PARSER_STATE_VARNAME + || s == GRUB_PARSER_STATE_VARNAME2 + || s == GRUB_PARSER_STATE_QVARNAME + || s == GRUB_PARSER_STATE_QVARNAME2); + } auto void add_var (grub_parser_state_t newstate); void add_var (grub_parser_state_t newstate) - { - char *val; + { + char *val; - /* Check if a variable was being read in and the end of the name - was reached. */ - if (! (check_varstate (state) && !check_varstate (newstate))) - return; + /* Check if a variable was being read in and the end of the name + was reached. */ + if (!(check_varstate (state) && !check_varstate (newstate))) + return; - *(vp++) = '\0'; - val = grub_env_get (varname); - vp = varname; - if (! val) - return; + *(vp++) = '\0'; + val = grub_env_get (varname); + vp = varname; + if (!val) + return; - /* Insert the contents of the variable in the buffer. */ - for (; *val; val++) - *(bp++) = *val; - } + /* Insert the contents of the variable in the buffer. */ + for (; *val; val++) + *(bp++) = *val; + } *argc = 0; do { - if (! rd || !*rd) + if (!rd || !*rd) { if (getline) getline (&rd, 1); - else break; + else + break; } if (!rd) @@ -190,7 +190,8 @@ grub_parser_split_cmdline (const char *cmdline, grub_reader_getline_t getline, } state = newstate; } - } while (state != GRUB_PARSER_STATE_TEXT && !check_varstate (state)); + } + while (state != GRUB_PARSER_STATE_TEXT && !check_varstate (state)); /* A special case for when the last character was part of a variable. */ @@ -204,12 +205,12 @@ grub_parser_split_cmdline (const char *cmdline, grub_reader_getline_t getline, /* Reserve memory for the return values. */ args = grub_malloc (bp - buffer); - if (! args) + if (!args) return grub_errno; grub_memcpy (args, buffer, bp - buffer); *argv = grub_malloc (sizeof (char *) * (*argc + 1)); - if (! *argv) + if (!*argv) { grub_free (args); return grub_errno; @@ -229,35 +230,34 @@ grub_parser_split_cmdline (const char *cmdline, grub_reader_getline_t getline, return 0; } -struct grub_handler_class grub_parser_class = - { - .name = "parser" - }; +struct grub_handler_class grub_parser_class = { + .name = "parser" +}; grub_err_t grub_parser_execute (char *source) { auto grub_err_t getline (char **line, int cont); grub_err_t getline (char **line, int cont __attribute__ ((unused))) - { - char *p; + { + char *p; - if (! source) - { - *line = 0; - return 0; - } + if (!source) + { + *line = 0; + return 0; + } - p = grub_strchr (source, '\n'); - if (p) - *p = 0; + p = grub_strchr (source, '\n'); + if (p) + *p = 0; - *line = grub_strdup (source); - if (p) - *p = '\n'; - source = p ? p + 1 : 0; - return 0; - } + *line = grub_strdup (source); + if (p) + *p = '\n'; + source = p ? p + 1 : 0; + return 0; + } while (source) { diff --git a/kern/partition.c b/kern/partition.c index 4d5c63a95..2a33ac329 100644 --- a/kern/partition.c +++ b/kern/partition.c @@ -17,40 +17,44 @@ */ #include +#include #include #include -static grub_partition_map_t grub_partition_map_list; +grub_partition_map_t grub_partition_map_list; -void -grub_partition_map_register (grub_partition_map_t partmap) +static grub_partition_t +grub_partition_map_probe (const grub_partition_map_t partmap, + grub_disk_t disk, int partnum) { - partmap->next = grub_partition_map_list; - grub_partition_map_list = partmap; -} + grub_partition_t p = 0; -void -grub_partition_map_unregister (grub_partition_map_t partmap) -{ - grub_partition_map_t *p, q; + auto int find_func (grub_disk_t d, const grub_partition_t partition); - for (p = &grub_partition_map_list, q = *p; q; p = &(q->next), q = q->next) - if (q == partmap) - { - *p = q->next; - break; - } -} + int find_func (grub_disk_t d __attribute__ ((unused)), + const grub_partition_t partition) + { + if (partnum == partition->number) + { + p = (grub_partition_t) grub_malloc (sizeof (*p)); + if (! p) + return 1; -int -grub_partition_map_iterate (int (*hook) (const grub_partition_map_t partmap)) -{ - grub_partition_map_t p; + grub_memcpy (p, partition, sizeof (*p)); + return 1; + } - for (p = grub_partition_map_list; p; p = p->next) - if (hook (p)) - return 1; + return 0; + } + partmap->iterate (disk, find_func); + if (grub_errno) + goto fail; + + return p; + + fail: + grub_free (p); return 0; } @@ -58,28 +62,66 @@ grub_partition_t grub_partition_probe (struct grub_disk *disk, const char *str) { grub_partition_t part = 0; + grub_partition_t curpart = 0; + grub_partition_t tail; + const char *ptr; - auto int part_map_probe (const grub_partition_map_t partmap); + part = tail = disk->partition; - int part_map_probe (const grub_partition_map_t partmap) + for (ptr = str; *ptr;) { - part = partmap->probe (disk, str); - if (part) - return 1; + grub_partition_map_t partmap; + int num; + const char *partname, *partname_end; - if (grub_errno == GRUB_ERR_BAD_PART_TABLE) + partname = ptr; + while (*ptr && grub_isalpha (*ptr)) + ptr++; + partname_end = ptr; + num = grub_strtoul (ptr, (char **) &ptr, 0) - 1; + + curpart = 0; + /* Use the first partition map type found. */ + FOR_PARTITION_MAPS(partmap) + { + if (partname_end != partname && + (grub_strncmp (partmap->name, partname, partname_end - partname) + != 0 || partmap->name[partname_end - partname] != 0)) + continue; + + disk->partition = part; + curpart = grub_partition_map_probe (partmap, disk, num); + disk->partition = tail; + if (curpart) + break; + + if (grub_errno == GRUB_ERR_BAD_PART_TABLE) + { + /* Continue to next partition map type. */ + grub_errno = GRUB_ERR_NONE; + continue; + } + + break; + } + + if (! curpart) { - /* Continue to next partition map type. */ - grub_errno = GRUB_ERR_NONE; + while (part) + { + curpart = part->parent; + grub_free (part); + part = curpart; + } return 0; } - - return 1; + curpart->parent = part; + part = curpart; + if (! ptr || *ptr != ',') + break; + ptr++; } - /* Use the first partition map type found. */ - grub_partition_map_iterate (part_map_probe); - return part; } @@ -88,40 +130,51 @@ grub_partition_iterate (struct grub_disk *disk, int (*hook) (grub_disk_t disk, const grub_partition_t partition)) { - grub_partition_map_t partmap = 0; int ret = 0; - auto int part_map_iterate (const grub_partition_map_t p); - auto int part_map_iterate_hook (grub_disk_t d, - const grub_partition_t partition); + auto int part_iterate (grub_disk_t dsk, const grub_partition_t p); - int part_map_iterate_hook (grub_disk_t d __attribute__ ((unused)), - const grub_partition_t partition __attribute__ ((unused))) + int part_iterate (grub_disk_t dsk, + const grub_partition_t partition) { - return 1; - } - - int part_map_iterate (const grub_partition_map_t p) - { - grub_dprintf ("partition", "Detecting %s...\n", p->name); - p->iterate (disk, part_map_iterate_hook); - - if (grub_errno != GRUB_ERR_NONE) + struct grub_partition p = *partition; + p.parent = dsk->partition; + dsk->partition = 0; + if (hook (dsk, &p)) { - /* Continue to next partition map type. */ - grub_dprintf ("partition", "%s detection failed.\n", p->name); - grub_errno = GRUB_ERR_NONE; - return 0; + ret = 1; + return 1; } - - grub_dprintf ("partition", "%s detection succeeded.\n", p->name); - partmap = p; - return 1; + if (p.start != 0) + { + const struct grub_partition_map *partmap; + dsk->partition = &p; + FOR_PARTITION_MAPS(partmap) + { + grub_err_t err; + err = partmap->iterate (dsk, part_iterate); + if (err) + grub_errno = GRUB_ERR_NONE; + if (ret) + break; + } + } + dsk->partition = p.parent; + return ret; } - grub_partition_map_iterate (part_map_iterate); - if (partmap) - ret = partmap->iterate (disk, hook); + { + const struct grub_partition_map *partmap; + FOR_PARTITION_MAPS(partmap) + { + grub_err_t err; + err = partmap->iterate (disk, part_iterate); + if (err) + grub_errno = GRUB_ERR_NONE; + if (ret) + break; + } + } return ret; } @@ -129,5 +182,32 @@ grub_partition_iterate (struct grub_disk *disk, char * grub_partition_get_name (const grub_partition_t partition) { - return partition->partmap->get_name (partition); + char *out = 0; + int curlen = 0; + grub_partition_t part; + for (part = partition; part; part = part->parent) + { + /* Even on 64-bit machines this buffer is enough to hold + longest number. */ + char buf[grub_strlen (part->partmap->name) + 25]; + int strl; + grub_snprintf (buf, sizeof (buf), "%s%d", part->partmap->name, + part->number + 1); + strl = grub_strlen (buf); + if (curlen) + { + out = grub_realloc (out, curlen + strl + 2); + grub_memcpy (out + strl + 1, out, curlen); + out[curlen + 1 + strl] = 0; + grub_memcpy (out, buf, strl); + out[strl] = ','; + curlen = curlen + 1 + strl; + } + else + { + curlen = strl; + out = grub_strdup (buf); + } + } + return out; } diff --git a/kern/powerpc/ieee1275/startup.S b/kern/powerpc/ieee1275/startup.S index 75e1ed852..96d153778 100644 --- a/kern/powerpc/ieee1275/startup.S +++ b/kern/powerpc/ieee1275/startup.S @@ -18,7 +18,7 @@ */ #include -#include +#include .extern __bss_start .extern _end @@ -30,7 +30,7 @@ start: _start: b codestart - . = _start + GRUB_KERNEL_CPU_PREFIX + . = _start + GRUB_KERNEL_MACHINE_PREFIX VARIABLE(grub_prefix) /* to be filled by grub-mkelfimage */ @@ -39,7 +39,7 @@ VARIABLE(grub_prefix) * Leave some breathing room for the prefix. */ - . = _start + GRUB_KERNEL_CPU_DATA_END + . = _start + GRUB_KERNEL_MACHINE_DATA_END codestart: li 2, 0 diff --git a/kern/sparc64/ieee1275/crt0.S b/kern/sparc64/ieee1275/crt0.S index 4e67cbc19..f0f47416d 100644 --- a/kern/sparc64/ieee1275/crt0.S +++ b/kern/sparc64/ieee1275/crt0.S @@ -18,13 +18,14 @@ */ #include #include +#include .text .align 4 .globl _start _start: ba codestart - nop + mov %o4, %o0 . = EXT_C(_start) + GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE @@ -53,12 +54,25 @@ codestart: or %o3, %lo(_end), %o3 sethi %hi(grub_total_module_size), %o4 lduw [%o4 + %lo(grub_total_module_size)], %o4 + + add %o2, %o4, %o2 + add %o3, %o4, %o3 + + /* Save ieee1275 stack for future use by booter. */ + mov %o6, %o1 + /* Our future stack. */ + sethi %hi(GRUB_KERNEL_MACHINE_STACK_SIZE - 2047), %o5 + or %o5, %lo(GRUB_KERNEL_MACHINE_STACK_SIZE - 2047), %o5 + add %o3, %o5, %o6 + + sub %o2, 4, %o2 + sub %o3, 4, %o3 1: lduw [%o2], %o5 stw %o5, [%o3] subcc %o4, 4, %o4 - add %o2, 4, %o2 + sub %o2, 4, %o2 bne,pt %icc, 1b - add %o3, 4, %o3 + sub %o3, 4, %o3 /* Now it's safe to clear out the BSS. */ sethi %hi(__bss_start), %o2 @@ -70,8 +84,9 @@ codestart: cmp %o2, %o3 blt,pt %xcc, 1b nop + sethi %hi(grub_ieee1275_original_stack), %o2 + stx %o1, [%o2 + %lo(grub_ieee1275_original_stack)] sethi %hi(grub_ieee1275_entry_fn), %o2 - stx %o0, [%o2 + %lo(grub_ieee1275_entry_fn)] call grub_main - nop + stx %o0, [%o2 + %lo(grub_ieee1275_entry_fn)] 1: ba,a 1b diff --git a/kern/sparc64/ieee1275/ieee1275.c b/kern/sparc64/ieee1275/ieee1275.c index 438a171ca..53be692c3 100644 --- a/kern/sparc64/ieee1275/ieee1275.c +++ b/kern/sparc64/ieee1275/ieee1275.c @@ -21,39 +21,6 @@ /* Sun specific ieee1275 interfaces used by GRUB. */ -int -grub_ieee1275_map_physical (grub_addr_t paddr, grub_addr_t vaddr, - grub_size_t size, grub_uint32_t mode) -{ - struct map_physical_args - { - struct grub_ieee1275_common_hdr common; - grub_ieee1275_cell_t method; - grub_ieee1275_cell_t ihandle; - grub_ieee1275_cell_t mode; - grub_ieee1275_cell_t size; - grub_ieee1275_cell_t virt; - grub_ieee1275_cell_t phys_high; - grub_ieee1275_cell_t phys_low; - grub_ieee1275_cell_t catch_result; - } - args; - - INIT_IEEE1275_COMMON (&args.common, "call-method", 7, 1); - args.method = (grub_ieee1275_cell_t) "map"; - args.ihandle = grub_ieee1275_mmu; - args.mode = mode; - args.size = size; - args.virt = vaddr; - args.phys_high = 0; - args.phys_low = paddr; - args.catch_result = (grub_ieee1275_cell_t) -1; - - if (IEEE1275_CALL_ENTRY_FN (&args) == -1) - return -1; - return args.catch_result; -} - int grub_ieee1275_claim_vaddr (grub_addr_t vaddr, grub_size_t size) { diff --git a/kern/sparc64/ieee1275/init.c b/kern/sparc64/ieee1275/init.c index 115328f40..a995217bc 100644 --- a/kern/sparc64/ieee1275/init.c +++ b/kern/sparc64/ieee1275/init.c @@ -23,12 +23,15 @@ #include #include #include +#include #include #include #include #include #include +grub_addr_t grub_ieee1275_original_stack; + void grub_exit (void) { @@ -104,7 +107,8 @@ grub_machine_set_prefix (void) static void grub_heap_init (void) { - grub_mm_init_region ((void *)(long)0x4000UL, 0x200000 - 0x4000); + grub_mm_init_region ((void *) (grub_modules_get_end () + + GRUB_KERNEL_MACHINE_STACK_SIZE), 0x200000); } static void diff --git a/kern/term.c b/kern/term.c index 50fbbf302..6e3a2b454 100644 --- a/kern/term.c +++ b/kern/term.c @@ -57,16 +57,17 @@ grub_putchar (int c) { static grub_size_t size = 0; static grub_uint8_t buf[6]; + grub_uint8_t *rest; grub_uint32_t code; - grub_size_t ret; buf[size++] = c; - ret = grub_utf8_to_ucs4 (&code, 1, buf, size, 0); - if (ret != 0) + while (grub_utf8_to_ucs4 (&code, 1, buf, size, (const grub_uint8_t **) &rest) + != 0) { struct grub_term_output *term; - size = 0; + size -= rest - buf; + grub_memmove (buf, rest, size); FOR_ACTIVE_TERM_OUTPUTS(term) grub_putcode (code, term); if (code == '\n' && grub_newline_hook) @@ -79,6 +80,8 @@ grub_getkey (void) { grub_term_input_t term; + grub_refresh (); + while (1) { FOR_ACTIVE_TERM_INPUTS(term) diff --git a/lib/ieee1275/datetime.c b/lib/ieee1275/datetime.c new file mode 100644 index 000000000..7e6f8d1f1 --- /dev/null +++ b/lib/ieee1275/datetime.c @@ -0,0 +1,142 @@ +/* kern/cmos_datetime.c - CMOS datetime function. + * + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include + +static char *rtc = 0; + +static void +find_rtc (void) +{ + auto int hook (struct grub_ieee1275_devalias *alias); + int hook (struct grub_ieee1275_devalias *alias) + { + if (grub_strcmp (alias->type, "rtc") == 0) + { + grub_dprintf ("datetime", "Found RTC %s\n", alias->path); + rtc = grub_strdup (alias->path); + return 1; + } + return 0; + } + + grub_ieee1275_devices_iterate (hook); +} + +grub_err_t +grub_get_datetime (struct grub_datetime *datetime) +{ + struct get_time_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t device; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t year; + grub_ieee1275_cell_t month; + grub_ieee1275_cell_t day; + grub_ieee1275_cell_t hour; + grub_ieee1275_cell_t minute; + grub_ieee1275_cell_t second; + } + args; + int status; + grub_ieee1275_ihandle_t ihandle; + + if (!rtc) + find_rtc (); + if (!rtc) + return grub_error (GRUB_ERR_IO, "no RTC found"); + + status = grub_ieee1275_open (rtc, &ihandle); + if (status == -1) + return grub_error (GRUB_ERR_IO, "couldn't open RTC"); + + INIT_IEEE1275_COMMON (&args.common, "call-method", 2, 7); + args.device = (grub_ieee1275_cell_t) ihandle; + args.method = (grub_ieee1275_cell_t) "get-time"; + + status = IEEE1275_CALL_ENTRY_FN (&args); + + grub_ieee1275_close (ihandle); + + if (status == -1) + return grub_error (GRUB_ERR_IO, "get-time failed"); + + datetime->year = args.year; + datetime->month = args.month; + datetime->day = args.day; + datetime->hour = args.hour; + datetime->minute = args.minute; + datetime->second = args.second; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_set_datetime (struct grub_datetime *datetime) +{ + struct set_time_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t device; + grub_ieee1275_cell_t year; + grub_ieee1275_cell_t month; + grub_ieee1275_cell_t day; + grub_ieee1275_cell_t hour; + grub_ieee1275_cell_t minute; + grub_ieee1275_cell_t second; + grub_ieee1275_cell_t catch_result; + } + args; + int status; + grub_ieee1275_ihandle_t ihandle; + + if (!rtc) + find_rtc (); + if (!rtc) + return grub_error (GRUB_ERR_IO, "no RTC found"); + + status = grub_ieee1275_open (rtc, &ihandle); + if (status == -1) + return grub_error (GRUB_ERR_IO, "couldn't open RTC"); + + INIT_IEEE1275_COMMON (&args.common, "call-method", 8, 1); + args.device = (grub_ieee1275_cell_t) ihandle; + args.method = (grub_ieee1275_cell_t) "set-time"; + + args.year = datetime->year; + args.month = datetime->month; + args.day = datetime->day; + args.hour = datetime->hour; + args.minute = datetime->minute; + args.second = datetime->second; + + status = IEEE1275_CALL_ENTRY_FN (&args); + + grub_ieee1275_close (ihandle); + + if (status == -1) + return grub_error (GRUB_ERR_IO, "set-time failed"); + + return GRUB_ERR_NONE; +} diff --git a/lib/libgcrypt_wrap/cipher_wrap.h b/lib/libgcrypt_wrap/cipher_wrap.h index e05f0cda9..b4530c112 100644 --- a/lib/libgcrypt_wrap/cipher_wrap.h +++ b/lib/libgcrypt_wrap/cipher_wrap.h @@ -59,7 +59,7 @@ typedef union { double g; } PROPERLY_ALIGNED_TYPE; -#define gcry_assert(x) grub_assert_real(__FILE__, __LINE__, x) +#define gcry_assert(x) grub_assert_real(GRUB_FILE, __LINE__, x) static inline void grub_assert_real (const char *file, int line, int cond) diff --git a/include/grub/powerpc/ieee1275/kernel.h b/lib/posix_wrap/assert.h similarity index 65% rename from include/grub/powerpc/ieee1275/kernel.h rename to lib/posix_wrap/assert.h index a76c2a4df..94cfdd543 100644 --- a/include/grub/powerpc/ieee1275/kernel.h +++ b/lib/posix_wrap/assert.h @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2006,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2009, 2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,17 +16,18 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_KERNEL_MACHINE_HEADER -#define GRUB_KERNEL_MACHINE_HEADER 1 +#ifndef GRUB_POSIX_ASSERT_H +#define GRUB_POSIX_ASSERT_H 1 -#include +#include -#ifndef ASM_FILE +#define assert(x) assert_real(__FILE__, __LINE__, x) -/* The prefix which points to the directory where GRUB modules and its - configuration file are located. */ -extern char grub_prefix[]; +static inline void +assert_real (const char *file, int line, int cond) +{ + if (!cond) + grub_fatal ("Assertion failed at %s:%d\n", file, line); +} #endif - -#endif /* ! GRUB_KERNEL_MACHINE_HEADER */ diff --git a/lib/posix_wrap/ctype.h b/lib/posix_wrap/ctype.h new file mode 100644 index 000000000..2dc3e53e9 --- /dev/null +++ b/lib/posix_wrap/ctype.h @@ -0,0 +1,103 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009, 2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_POSIX_CTYPE_H +#define GRUB_POSIX_CTYPE_H 1 + +#include + +static inline int +toupper (int c) +{ + return grub_toupper (c); +} + +static inline int +isspace (int c) +{ + return grub_isspace (c); +} + +static inline int +isdigit (int c) +{ + return grub_isdigit (c); +} + +static inline int +islower (int c) +{ + return (c >= 'a' && c <= 'z'); +} + +static inline int +isupper (int c) +{ + return (c >= 'A' && c <= 'Z'); +} + +static inline int +isxdigit (int c) +{ + return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') + || (c >= '0' && c <= '9'); +} + +static inline int +isprint (int c) +{ + return grub_isprint (c); +} + +static inline int +iscntrl (int c) +{ + return !grub_isprint (c); +} + +static inline int +isgraph (int c) +{ + return grub_isprint (c) && !grub_isspace (c); +} + +static inline int +isalnum (int c) +{ + return grub_isalpha (c) || grub_isdigit (c); +} + +static inline int +ispunct (int c) +{ + return grub_isprint (c) && !grub_isspace (c) && !isalnum (c); +} + +static inline int +isalpha (int c) +{ + return grub_isalpha (c); +} + +static inline int +tolower (int c) +{ + return grub_tolower (c); +} + +#endif diff --git a/include/grub/i386/coreboot/kernel.h b/lib/posix_wrap/errno.h similarity index 73% rename from include/grub/i386/coreboot/kernel.h rename to lib/posix_wrap/errno.h index fb60668cc..9031722e2 100644 --- a/include/grub/i386/coreboot/kernel.h +++ b/lib/posix_wrap/errno.h @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2006,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2009, 2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,13 +16,13 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_KERNEL_MACHINE_HEADER -#define GRUB_KERNEL_MACHINE_HEADER 1 +#ifndef GRUB_POSIX_ERRNO_H +#define GRUB_POSIX_ERRNO_H 1 -#include +#include + +#define errno grub_errno +#define EINVAL GRUB_ERR_BAD_NUMBER +#define ENOMEM GRUB_ERR_OUT_OF_MEMORY -#ifndef ASM_FILE -extern char grub_prefix[]; #endif - -#endif /* ! GRUB_KERNEL_MACHINE_HEADER */ diff --git a/lib/posix_wrap/langinfo.h b/lib/posix_wrap/langinfo.h new file mode 100644 index 000000000..14833c0b8 --- /dev/null +++ b/lib/posix_wrap/langinfo.h @@ -0,0 +1,38 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009, 2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_POSIX_LANGINFO_H +#define GRUB_POSIX_LANGINFO_H 1 + +#include + +typedef enum { CODESET } nl_item; + +static inline char * +nl_langinfo (nl_item item) +{ + switch (item) + { + case CODESET: + return locale_charset (); + default: + return ""; + } +} + +#endif diff --git a/lib/posix_wrap/limits.h b/lib/posix_wrap/limits.h new file mode 100644 index 000000000..e69de29bb diff --git a/lib/posix_wrap/localcharset.h b/lib/posix_wrap/localcharset.h new file mode 100644 index 000000000..92eb815ec --- /dev/null +++ b/lib/posix_wrap/localcharset.h @@ -0,0 +1,28 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009, 2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_POSIX_LOCALCHARSET_H +#define GRUB_POSIX_LOCALCHARSET_H 1 + +static inline char * +locale_charset (void) +{ + return "UTF-8"; +} + +#endif diff --git a/lib/posix_wrap/locale.h b/lib/posix_wrap/locale.h new file mode 100644 index 000000000..e69de29bb diff --git a/lib/posix_wrap/stdint.h b/lib/posix_wrap/stdint.h new file mode 100644 index 000000000..a12c43b15 --- /dev/null +++ b/lib/posix_wrap/stdint.h @@ -0,0 +1 @@ +#include diff --git a/include/grub/sparc64/kernel.h b/lib/posix_wrap/stdio.h similarity index 68% rename from include/grub/sparc64/kernel.h rename to lib/posix_wrap/stdio.h index 9f404b05d..701fceaa4 100644 --- a/include/grub/sparc64/kernel.h +++ b/lib/posix_wrap/stdio.h @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2006,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2009, 2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,15 +16,14 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_KERNEL_CPU_HEADER -#define GRUB_KERNEL_CPU_HEADER 1 +#ifndef GRUB_POSIX_STDIO_H +#define GRUB_POSIX_STDIO_H 1 -#define GRUB_MOD_ALIGN 0x2000 +#include +#include -/* Non-zero value is only needed for PowerMacs. */ -#define GRUB_MOD_GAP 0x0 +typedef struct grub_file FILE; -#define GRUB_KERNEL_CPU_PREFIX 0x2 -#define GRUB_KERNEL_CPU_DATA_END 0x42 +#define EOF -1 #endif diff --git a/lib/posix_wrap/stdlib.h b/lib/posix_wrap/stdlib.h new file mode 100644 index 000000000..5ef6159ef --- /dev/null +++ b/lib/posix_wrap/stdlib.h @@ -0,0 +1,56 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009, 2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_POSIX_STDLIB_H +#define GRUB_POSIX_STDLIB_H 1 + +#include + +static inline void +free (void *ptr) +{ + grub_free (ptr); +} + +static inline void * +malloc (grub_size_t size) +{ + return grub_malloc (size); +} + +static inline void * +calloc (grub_size_t size, grub_size_t nelem) +{ + return grub_zalloc (size * nelem); +} + +static inline void * +realloc (void *ptr, grub_size_t size) +{ + return grub_realloc (ptr, size); +} + +static inline void +abort (void) +{ + grub_abort (); +} + +#define MB_CUR_MAX 6 + +#endif diff --git a/include/grub/i386/kernel.h b/lib/posix_wrap/string.h similarity index 63% rename from include/grub/i386/kernel.h rename to lib/posix_wrap/string.h index 5514c8ccf..7bb6f1e6f 100644 --- a/include/grub/i386/kernel.h +++ b/lib/posix_wrap/string.h @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * Copyright (C) 2009, 2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,20 +16,25 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_KERNEL_CPU_HEADER -#define GRUB_KERNEL_CPU_HEADER 1 +#ifndef GRUB_POSIX_STRING_H +#define GRUB_POSIX_STRING_H 1 +static inline grub_size_t +strlen (const char *s) +{ + return grub_strlen (s); +} -#ifdef GRUB_MACHINE_IEEE1275 -#define GRUB_MOD_ALIGN 0x1000 -#else -#define GRUB_MOD_ALIGN 0x1 -#endif +static inline int +strcmp (const char *s1, const char *s2) +{ + return grub_strcmp (s1, s2); +} -/* Non-zero value is only needed for PowerMacs. */ -#define GRUB_MOD_GAP 0x0 - -#define GRUB_KERNEL_CPU_PREFIX 0x2 -#define GRUB_KERNEL_CPU_DATA_END 0x42 +static inline int +strcasecmp (const char *s1, const char *s2) +{ + return grub_strcasecmp (s1, s2); +} #endif diff --git a/include/grub/sparc64/libgcc.h b/lib/posix_wrap/sys/types.h similarity index 68% rename from include/grub/sparc64/libgcc.h rename to lib/posix_wrap/sys/types.h index e73abe29c..ce3794087 100644 --- a/include/grub/sparc64/libgcc.h +++ b/lib/posix_wrap/sys/types.h @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2004,2007,2009 Free Software Foundation, Inc. + * Copyright (C) 2009, 2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,14 +16,17 @@ * along with GRUB. If not, see . */ -#include +#ifndef GRUB_POSIX_SYS_TYPES_H +#define GRUB_POSIX_SYS_TYPES_H 1 -#ifdef HAVE___BSWAPSI2 -typedef int SItype __attribute__ ((mode (SI))); -SItype EXPORT_FUNC (__bswapsi2) (SItype); -#endif +#include + +typedef grub_size_t size_t; +typedef int bool; +static const bool true = 1; +static const bool false = 0; + +#define ULONG_MAX GRUB_ULONG_MAX +#define UCHAR_MAX 0xff -#ifdef HAVE___BSWAPDI2 -typedef int DItype __attribute__ ((mode (DI))); -DItype EXPORT_FUNC (__bswapdi2) (DItype); #endif diff --git a/lib/posix_wrap/unistd.h b/lib/posix_wrap/unistd.h new file mode 100644 index 000000000..e69de29bb diff --git a/lib/posix_wrap/wchar.h b/lib/posix_wrap/wchar.h new file mode 100644 index 000000000..fd56fd332 --- /dev/null +++ b/lib/posix_wrap/wchar.h @@ -0,0 +1,25 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009, 2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_POSIX_WCHAR_H +#define GRUB_POSIX_WCHAR_H 1 + +/* UCS-4. */ +typedef grub_uint32_t wchar_t; + +#endif diff --git a/lib/posix_wrap/wctype.h b/lib/posix_wrap/wctype.h new file mode 100644 index 000000000..e69de29bb diff --git a/loader/i386/bsd.c b/loader/i386/bsd.c index 2598371b7..3c7fe2fee 100644 --- a/loader/i386/bsd.c +++ b/loader/i386/bsd.c @@ -140,7 +140,6 @@ grub_bsd_get_device (grub_uint32_t * biosdev, grub_uint32_t * unit, grub_uint32_t * slice, grub_uint32_t * part) { - char *p; grub_device_t dev; #ifdef GRUB_MACHINE_PCBIOS @@ -154,21 +153,13 @@ grub_bsd_get_device (grub_uint32_t * biosdev, dev = grub_device_open (0); if (dev && dev->disk && dev->disk->partition) { - - p = dev->disk->partition->partmap->get_name (dev->disk->partition); - if (p) + if (dev->disk->partition->parent) { - if ((p[0] >= '0') && (p[0] <= '9')) - { - *slice = grub_strtoul (p, &p, 0); - - if ((p) && (p[0] == ',')) - p++; - } - - if ((p[0] >= 'a') && (p[0] <= 'z')) - *part = p[0] - 'a'; + *part = dev->disk->partition->number; + *slice = dev->disk->partition->parent->number + 1; } + else + *slice = dev->disk->partition->number + 1; } if (dev) grub_device_close (dev); @@ -461,14 +452,14 @@ grub_freebsd_boot (void) } grub_memset (&bi, 0, sizeof (bi)); - bi.bi_version = FREEBSD_BOOTINFO_VERSION; - bi.bi_size = sizeof (bi); + bi.version = FREEBSD_BOOTINFO_VERSION; + bi.length = sizeof (bi); grub_bsd_get_device (&biosdev, &unit, &slice, &part); bootdev = (FREEBSD_B_DEVMAGIC + ((slice + 1) << FREEBSD_B_SLICESHIFT) + (unit << FREEBSD_B_UNITSHIFT) + (part << FREEBSD_B_PARTSHIFT)); - bi.bi_bios_dev = biosdev; + bi.boot_device = biosdev; p = (char *) kern_end; @@ -478,7 +469,7 @@ grub_freebsd_boot (void) { *(p++) = 0; - bi.bi_envp = kern_end; + bi.environment = kern_end; kern_end = ALIGN_PAGE ((grub_uint32_t) p); } @@ -491,23 +482,23 @@ grub_freebsd_boot (void) return grub_errno; grub_memcpy ((char *) kern_end, mod_buf, mod_buf_len); - bi.bi_modulep = kern_end; + bi.tags = kern_end; kern_end = ALIGN_PAGE (kern_end + mod_buf_len); if (is_64bit) kern_end += 4096 * 4; - md_ofs = bi.bi_modulep + kern_end_mdofs; + md_ofs = bi.tags + kern_end_mdofs; ofs = (is_64bit) ? 16 : 12; *((grub_uint32_t *) md_ofs) = kern_end; md_ofs -= ofs; - *((grub_uint32_t *) md_ofs) = bi.bi_envp; + *((grub_uint32_t *) md_ofs) = bi.environment; md_ofs -= ofs; *((grub_uint32_t *) md_ofs) = bootflags; } - bi.bi_kernend = kern_end; + bi.kern_end = kern_end; grub_video_set_mode ("text", 0, 0); @@ -554,12 +545,12 @@ grub_freebsd_boot (void) &grub_bsd64_trampoline_end - &grub_bsd64_trampoline_start); /* Launch trampoline. */ - launch_trampoline (entry, entry_hi, pagetable, bi.bi_modulep, + launch_trampoline (entry, entry_hi, pagetable, bi.tags, kern_end); } else grub_unix_real_boot (entry, bootflags | FREEBSD_RB_BOOTINFO, bootdev, - 0, 0, 0, &bi, bi.bi_modulep, kern_end); + 0, 0, 0, &bi, bi.tags, kern_end); /* Not reached. */ return GRUB_ERR_NONE; diff --git a/loader/i386/bsdXX.c b/loader/i386/bsdXX.c index b4d574821..cd5ba85dc 100644 --- a/loader/i386/bsdXX.c +++ b/loader/i386/bsdXX.c @@ -80,7 +80,7 @@ SUFFIX (grub_freebsd_load_elfmodule_obj) (grub_file_t file, int argc, { Elf_Ehdr e; Elf_Shdr *s; - char *shdr; + char *shdr = 0; grub_addr_t curload, module; grub_err_t err; @@ -148,7 +148,7 @@ SUFFIX (grub_freebsd_load_elfmodule) (grub_file_t file, int argc, char *argv[], { Elf_Ehdr e; Elf_Shdr *s; - char *shdr; + char *shdr = 0; grub_addr_t curload, module; grub_err_t err; @@ -223,7 +223,7 @@ SUFFIX (grub_freebsd_load_elf_meta) (grub_file_t file, grub_addr_t *kern_end) grub_err_t err; Elf_Ehdr e; Elf_Shdr *s; - char *shdr; + char *shdr = 0; unsigned symoff, stroff, symsize, strsize; grub_addr_t curload; grub_freebsd_addr_t symstart, symend, symentsize, dynamic; diff --git a/loader/i386/efi/linux.c b/loader/i386/efi/linux.c index ebaf89743..a6db22e22 100644 --- a/loader/i386/efi/linux.c +++ b/loader/i386/efi/linux.c @@ -576,7 +576,7 @@ grub_linux_setup_video (struct linux_kernel_params *params) params->lfb_line_len = line_len; params->lfb_base = fb_base; - params->lfb_size = (line_len * params->lfb_height + 65535) >> 16; + params->lfb_size = ALIGN_UP (line_len * params->lfb_height, 65536); params->red_mask_size = 8; params->red_field_pos = 16; diff --git a/loader/i386/linux.c b/loader/i386/linux.c index 831d8b25a..d3d935182 100644 --- a/loader/i386/linux.c +++ b/loader/i386/linux.c @@ -394,12 +394,15 @@ grub_linux_setup_video (struct linux_kernel_params *params) { struct grub_video_mode_info mode_info; void *framebuffer; - int ret; + grub_err_t err; - ret = grub_video_get_info_and_fini (&mode_info, &framebuffer); + err = grub_video_get_info_and_fini (&mode_info, &framebuffer); - if (ret) - return 1; + if (err) + { + grub_errno = GRUB_ERR_NONE; + return 1; + } params->lfb_width = mode_info.width; params->lfb_height = mode_info.height; @@ -407,7 +410,7 @@ grub_linux_setup_video (struct linux_kernel_params *params) params->lfb_line_len = mode_info.pitch; params->lfb_base = (grub_size_t) framebuffer; - params->lfb_size = (params->lfb_line_len * params->lfb_height + 65535) >> 16; + params->lfb_size = ALIGN_UP (params->lfb_line_len * params->lfb_height, 65536); params->red_mask_size = mode_info.red_mask_size; params->red_field_pos = mode_info.red_field_pos; @@ -540,6 +543,8 @@ grub_linux_boot (void) /* Use generic framebuffer unless VESA is known to be supported. */ if (params->have_vga != GRUB_VIDEO_LINUX_TYPE_VESA) params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE; + else + params->lfb_size >>= 16; } else { diff --git a/loader/i386/multiboot.c b/loader/i386/multiboot.c deleted file mode 100644 index fc9588269..000000000 --- a/loader/i386/multiboot.c +++ /dev/null @@ -1,334 +0,0 @@ -/* multiboot.c - boot a multiboot OS image. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc. - * - * GRUB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GRUB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GRUB. If not, see . - */ - -/* - * FIXME: The following features from the Multiboot specification still - * need to be implemented: - * - VBE support - * - symbol table - * - drives table - * - ROM configuration table - * - APM table - */ - -/* The bits in the required part of flags field we don't support. */ -#define UNSUPPORTED_FLAGS 0x0000fff8 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef GRUB_MACHINE_EFI -#include -#endif - -extern grub_dl_t my_mod; -static grub_size_t code_size, alloc_mbi; - -char *grub_multiboot_payload_orig; -grub_addr_t grub_multiboot_payload_dest; -grub_size_t grub_multiboot_pure_size; -grub_uint32_t grub_multiboot_payload_eip; - -static grub_err_t -grub_multiboot_boot (void) -{ - grub_size_t mbi_size; - grub_err_t err; - struct grub_relocator32_state state = - { - .eax = MULTIBOOT_BOOTLOADER_MAGIC, - .ecx = 0, - .edx = 0, - .eip = grub_multiboot_payload_eip, - /* Set esp to some random location in low memory to avoid breaking - non-compliant kernels. */ - .esp = 0x7ff00 - }; - - mbi_size = grub_multiboot_get_mbi_size (); - if (alloc_mbi < mbi_size) - { - grub_multiboot_payload_orig - = grub_relocator32_realloc (grub_multiboot_payload_orig, - grub_multiboot_pure_size + mbi_size); - if (!grub_multiboot_payload_orig) - return grub_errno; - alloc_mbi = mbi_size; - } - - state.ebx = grub_multiboot_payload_dest + grub_multiboot_pure_size; - err = grub_multiboot_make_mbi (grub_multiboot_payload_orig, - grub_multiboot_payload_dest, - grub_multiboot_pure_size, mbi_size); - if (err) - return err; - -#ifdef GRUB_MACHINE_EFI - if (! grub_efi_finish_boot_services ()) - grub_fatal ("cannot exit boot services"); -#endif - - grub_relocator32_boot (grub_multiboot_payload_orig, - grub_multiboot_payload_dest, - state); - - /* Not reached. */ - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_multiboot_unload (void) -{ - grub_multiboot_free_mbi (); - - grub_relocator32_free (grub_multiboot_payload_orig); - - alloc_mbi = 0; - - grub_multiboot_payload_orig = NULL; - grub_dl_unref (my_mod); - - return GRUB_ERR_NONE; -} - -#define MULTIBOOT_LOAD_ELF64 -#include "multiboot_elfxx.c" -#undef MULTIBOOT_LOAD_ELF64 - -#define MULTIBOOT_LOAD_ELF32 -#include "multiboot_elfxx.c" -#undef MULTIBOOT_LOAD_ELF32 - -/* Load ELF32 or ELF64. */ -static grub_err_t -grub_multiboot_load_elf (grub_file_t file, void *buffer) -{ - if (grub_multiboot_is_elf32 (buffer)) - return grub_multiboot_load_elf32 (file, buffer); - else if (grub_multiboot_is_elf64 (buffer)) - return grub_multiboot_load_elf64 (file, buffer); - - return grub_error (GRUB_ERR_UNKNOWN_OS, "unknown ELF class"); -} - -void -grub_multiboot (int argc, char *argv[]) -{ - grub_file_t file = 0; - char buffer[MULTIBOOT_SEARCH]; - struct multiboot_header *header; - grub_ssize_t len; - - grub_loader_unset (); - - if (argc == 0) - { - grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified"); - goto fail; - } - - file = grub_gzfile_open (argv[0], 1); - if (! file) - { - grub_error (GRUB_ERR_BAD_ARGUMENT, "couldn't open file"); - goto fail; - } - - len = grub_file_read (file, buffer, MULTIBOOT_SEARCH); - if (len < 32) - { - grub_error (GRUB_ERR_BAD_OS, "file too small"); - goto fail; - } - - /* Look for the multiboot header in the buffer. The header should - be at least 12 bytes and aligned on a 4-byte boundary. */ - for (header = (struct multiboot_header *) buffer; - ((char *) header <= buffer + len - 12) || (header = 0); - header = (struct multiboot_header *) ((char *) header + 4)) - { - if (header->magic == MULTIBOOT_HEADER_MAGIC - && !(header->magic + header->flags + header->checksum)) - break; - } - - if (header == 0) - { - grub_error (GRUB_ERR_BAD_ARGUMENT, "no multiboot header found"); - goto fail; - } - - if (header->flags & UNSUPPORTED_FLAGS) - { - grub_error (GRUB_ERR_UNKNOWN_OS, - "unsupported flag: 0x%x", header->flags); - goto fail; - } - - grub_relocator32_free (grub_multiboot_payload_orig); - grub_multiboot_payload_orig = NULL; - - /* Skip filename. */ - grub_multiboot_init_mbi (argc - 1, argv + 1); - - if (header->flags & MULTIBOOT_AOUT_KLUDGE) - { - int offset = ((char *) header - buffer - - (header->header_addr - header->load_addr)); - int load_size = ((header->load_end_addr == 0) ? file->size - offset : - header->load_end_addr - header->load_addr); - - if (header->bss_end_addr) - code_size = (header->bss_end_addr - header->load_addr); - else - code_size = load_size; - grub_multiboot_payload_dest = header->load_addr; - - grub_multiboot_pure_size += code_size; - - /* Allocate a bit more to avoid relocations in most cases. */ - alloc_mbi = grub_multiboot_get_mbi_size () + 65536; - grub_multiboot_payload_orig - = grub_relocator32_alloc (grub_multiboot_pure_size + alloc_mbi); - - if (! grub_multiboot_payload_orig) - goto fail; - - if ((grub_file_seek (file, offset)) == (grub_off_t) -1) - goto fail; - - grub_file_read (file, (void *) grub_multiboot_payload_orig, load_size); - if (grub_errno) - goto fail; - - if (header->bss_end_addr) - grub_memset ((void *) (grub_multiboot_payload_orig + load_size), 0, - header->bss_end_addr - header->load_addr - load_size); - - grub_multiboot_payload_eip = header->entry_addr; - - } - else if (grub_multiboot_load_elf (file, buffer) != GRUB_ERR_NONE) - goto fail; - - if (header->flags & MULTIBOOT_VIDEO_MODE) - { - switch (header->mode_type) - { - case 1: - grub_env_set ("gfxpayload", "text"); - break; - - case 0: - { - char *buf; - if (header->depth && header->width && header->height) - buf = grub_xasprintf ("%dx%dx%d,%dx%d,auto", header->width, - header->height, header->depth, header->width, - header->height); - else if (header->width && header->height) - buf = grub_xasprintf ("%dx%d,auto", header->width, header->height); - else - buf = grub_strdup ("auto"); - - if (!buf) - goto fail; - grub_env_set ("gfxpayload", buf); - grub_free (buf); - break; - } - } - } - - grub_multiboot_set_accepts_video (!!(header->flags & MULTIBOOT_VIDEO_MODE)); - - grub_multiboot_set_bootdev (); - - grub_loader_set (grub_multiboot_boot, grub_multiboot_unload, 0); - - fail: - if (file) - grub_file_close (file); - - if (grub_errno != GRUB_ERR_NONE) - { - grub_relocator32_free (grub_multiboot_payload_orig); - grub_dl_unref (my_mod); - } -} - -void -grub_module (int argc, char *argv[]) -{ - grub_file_t file = 0; - grub_ssize_t size; - char *module = 0; - grub_err_t err; - - if (argc == 0) - { - grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified"); - goto fail; - } - - if (!grub_multiboot_payload_orig) - { - grub_error (GRUB_ERR_BAD_ARGUMENT, - "you need to load the multiboot kernel first"); - goto fail; - } - - file = grub_gzfile_open (argv[0], 1); - if (! file) - goto fail; - - size = grub_file_size (file); - module = grub_memalign (MULTIBOOT_MOD_ALIGN, size); - if (! module) - goto fail; - - err = grub_multiboot_add_module ((grub_addr_t) module, size, - argc - 1, argv + 1); - if (err) - goto fail; - - if (grub_file_read (file, module, size) != size) - { - grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file"); - goto fail; - } - - fail: - if (file) - grub_file_close (file); -} - diff --git a/loader/i386/multiboot_mbi.c b/loader/i386/multiboot_mbi.c index a154d1b23..3d974f04e 100644 --- a/loader/i386/multiboot_mbi.c +++ b/loader/i386/multiboot_mbi.c @@ -23,6 +23,7 @@ #endif #include #include +#include #include #include #include @@ -30,15 +31,10 @@ #include #include #include +#include -#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU) -#include -#define DEFAULT_VIDEO_MODE "text" -#define HAS_VGA_TEXT 1 -#else -#define DEFAULT_VIDEO_MODE "auto" -#define HAS_VGA_TEXT 0 -#endif +/* The bits in the required part of flags field we don't support. */ +#define UNSUPPORTED_FLAGS 0x0000fff8 struct module { @@ -56,33 +52,136 @@ static unsigned modcnt; static char *cmdline = NULL; static grub_uint32_t bootdev; static int bootdev_set; -static int accepts_video; -void -grub_multiboot_set_accepts_video (int val) +grub_err_t +grub_multiboot_load (grub_file_t file) { - accepts_video = val; -} + char *buffer; + grub_ssize_t len; + struct multiboot_header *header; + grub_err_t err; -/* Return the length of the Multiboot mmap that will be needed to allocate - our platform's map. */ -static grub_uint32_t -grub_get_multiboot_mmap_len (void) -{ - grub_size_t count = 0; + buffer = grub_malloc (MULTIBOOT_SEARCH); + if (!buffer) + return grub_errno; - auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t); - int NESTED_FUNC_ATTR hook (grub_uint64_t addr __attribute__ ((unused)), - grub_uint64_t size __attribute__ ((unused)), - grub_uint32_t type __attribute__ ((unused))) + len = grub_file_read (file, buffer, MULTIBOOT_SEARCH); + if (len < 32) { - count++; - return 0; + grub_free (buffer); + return grub_error (GRUB_ERR_BAD_OS, "file too small"); } - grub_mmap_iterate (hook); + /* Look for the multiboot header in the buffer. The header should + be at least 12 bytes and aligned on a 4-byte boundary. */ + for (header = (struct multiboot_header *) buffer; + ((char *) header <= buffer + len - 12) || (header = 0); + header = (struct multiboot_header *) ((char *) header + MULTIBOOT_HEADER_ALIGN)) + { + if (header->magic == MULTIBOOT_HEADER_MAGIC + && !(header->magic + header->flags + header->checksum)) + break; + } - return count * sizeof (struct multiboot_mmap_entry); + if (header == 0) + { + grub_free (buffer); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no multiboot header found"); + } + + if (header->flags & UNSUPPORTED_FLAGS) + { + grub_free (buffer); + return grub_error (GRUB_ERR_UNKNOWN_OS, + "unsupported flag: 0x%x", header->flags); + } + + if (header->flags & MULTIBOOT_AOUT_KLUDGE) + { + int offset = ((char *) header - buffer - + (header->header_addr - header->load_addr)); + int load_size = ((header->load_end_addr == 0) ? file->size - offset : + header->load_end_addr - header->load_addr); + grub_size_t code_size; + + if (header->bss_end_addr) + code_size = (header->bss_end_addr - header->load_addr); + else + code_size = load_size; + grub_multiboot_payload_dest = header->load_addr; + + grub_multiboot_pure_size += code_size; + + /* Allocate a bit more to avoid relocations in most cases. */ + grub_multiboot_alloc_mbi = grub_multiboot_get_mbi_size () + 65536; + grub_multiboot_payload_orig + = grub_relocator32_alloc (grub_multiboot_pure_size + grub_multiboot_alloc_mbi); + + if (! grub_multiboot_payload_orig) + { + grub_free (buffer); + return grub_errno; + } + + if ((grub_file_seek (file, offset)) == (grub_off_t) -1) + { + grub_free (buffer); + return grub_errno; + } + + grub_file_read (file, (void *) grub_multiboot_payload_orig, load_size); + if (grub_errno) + { + grub_free (buffer); + return grub_errno; + } + + if (header->bss_end_addr) + grub_memset ((void *) (grub_multiboot_payload_orig + load_size), 0, + header->bss_end_addr - header->load_addr - load_size); + + grub_multiboot_payload_eip = header->entry_addr; + + } + else + { + err = grub_multiboot_load_elf (file, buffer); + if (err) + { + grub_free (buffer); + return err; + } + } + + if (header->flags & MULTIBOOT_VIDEO_MODE) + { + switch (header->mode_type) + { + case 1: + err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT, + GRUB_MULTIBOOT_CONSOLE_EGA_TEXT + | GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER, + 0, 0, 0, 0); + break; + case 0: + err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER, + GRUB_MULTIBOOT_CONSOLE_EGA_TEXT + | GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER, + header->width, header->height, + header->depth, 0); + break; + default: + err = grub_error (GRUB_ERR_BAD_OS, + "unsupported graphical mode type %d", + header->mode_type); + break; + } + } + else + err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT, + GRUB_MULTIBOOT_CONSOLE_EGA_TEXT, + 0, 0, 0, 0); + return err; } grub_size_t @@ -90,7 +189,8 @@ grub_multiboot_get_mbi_size (void) { return sizeof (struct multiboot_info) + ALIGN_UP (cmdline_size, 4) + modcnt * sizeof (struct multiboot_mod_list) + total_modcmd - + ALIGN_UP (sizeof(PACKAGE_STRING), 4) + grub_get_multiboot_mmap_len () + + ALIGN_UP (sizeof(PACKAGE_STRING), 4) + + grub_get_multiboot_mmap_count () * sizeof (struct multiboot_mmap_entry) + 256 * sizeof (struct multiboot_color); } @@ -136,33 +236,6 @@ grub_fill_multiboot_mmap (struct multiboot_mmap_entry *first_entry) grub_mmap_iterate (hook); } -static grub_err_t -set_video_mode (void) -{ - grub_err_t err; - const char *modevar; - - if (accepts_video || !HAS_VGA_TEXT) - { - modevar = grub_env_get ("gfxpayload"); - if (! modevar || *modevar == 0) - err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0); - else - { - char *tmp; - tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar); - if (! tmp) - return grub_errno; - err = grub_video_set_mode (tmp, 0, 0); - grub_free (tmp); - } - } - else - err = grub_video_set_mode ("text", 0, 0); - - return err; -} - static grub_err_t retrieve_video_parameters (struct multiboot_info *mbi, grub_uint8_t *ptrorig, grub_uint32_t ptrdest) @@ -173,7 +246,7 @@ retrieve_video_parameters (struct multiboot_info *mbi, grub_video_driver_id_t driv_id; struct grub_video_palette_data palette[256]; - err = set_video_mode (); + err = grub_multiboot_set_video_mode (); if (err) { grub_print_error (); @@ -293,7 +366,8 @@ grub_multiboot_make_mbi (void *orig, grub_uint32_t dest, grub_off_t buf_off, mbi->mods_count = 0; } - mmap_size = grub_get_multiboot_mmap_len (); + mmap_size = grub_get_multiboot_mmap_count () + * sizeof (struct multiboot_mmap_entry); grub_fill_multiboot_mmap ((struct multiboot_mmap_entry *) ptrorig); mbi->mmap_length = mmap_size; mbi->mmap_addr = ptrdest; @@ -435,7 +509,6 @@ grub_multiboot_add_module (grub_addr_t start, grub_size_t size, void grub_multiboot_set_bootdev (void) { - char *p; grub_uint32_t biosdev, slice = ~0, part = ~0; grub_device_t dev; @@ -445,24 +518,19 @@ grub_multiboot_set_bootdev (void) biosdev = 0xffffffff; #endif + if (biosdev == 0xffffffff) + return; + dev = grub_device_open (0); if (dev && dev->disk && dev->disk->partition) { - - p = dev->disk->partition->partmap->get_name (dev->disk->partition); - if (p) - { - if ((p[0] >= '0') && (p[0] <= '9')) - { - slice = grub_strtoul (p, &p, 0) - 1; - - if ((p) && (p[0] == ',')) - p++; - } - - if ((p[0] >= 'a') && (p[0] <= 'z')) - part = p[0] - 'a'; + if (dev->disk->partition->parent) + { + part = dev->disk->partition->number; + slice = dev->disk->partition->parent->number; } + else + slice = dev->disk->partition->number; } if (dev) grub_device_close (dev); diff --git a/loader/i386/pc/chainloader.c b/loader/i386/pc/chainloader.c index fbc356895..502031d0e 100644 --- a/loader/i386/pc/chainloader.c +++ b/loader/i386/pc/chainloader.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -98,10 +99,22 @@ grub_chainloader_cmd (const char *filename, grub_chainloader_flags_t flags) dev = grub_device_open (0); if (dev && dev->disk && dev->disk->partition) { - grub_disk_read (dev->disk, dev->disk->partition->offset, 446, 64, - (void *) GRUB_MEMORY_MACHINE_PART_TABLE_ADDR); - part_addr = (void *) (GRUB_MEMORY_MACHINE_PART_TABLE_ADDR - + (dev->disk->partition->index << 4)); + grub_disk_t disk = dev->disk; + + if (disk) + { + grub_partition_t p = disk->partition; + + if (p && grub_strcmp (p->partmap->name, "msdos") == 0) + { + disk->partition = p->parent; + grub_disk_read (disk, p->offset, 446, 64, + (void *) GRUB_MEMORY_MACHINE_PART_TABLE_ADDR); + part_addr = (void *) (GRUB_MEMORY_MACHINE_PART_TABLE_ADDR + + (p->index << 4)); + disk->partition = p; + } + } } if (dev) diff --git a/loader/i386/pc/xnu.c b/loader/i386/pc/xnu.c index c683dd0f9..39a595d9b 100644 --- a/loader/i386/pc/xnu.c +++ b/loader/i386/pc/xnu.c @@ -22,6 +22,7 @@ #include #include #include +#include #define min(a,b) (((a) < (b)) ? (a) : (b)) #define max(a,b) (((a) > (b)) ? (a) : (b)) @@ -34,9 +35,11 @@ grub_xnu_set_video (struct grub_xnu_boot_params *params) { struct grub_video_mode_info mode_info; int ret; - char *tmp, *modevar; + char *tmp; + const char *modevar; void *framebuffer; grub_err_t err; + struct grub_video_bitmap *bitmap = NULL; modevar = grub_env_get ("gfxpayload"); /* Consider only graphical 32-bit deep modes. */ @@ -49,8 +52,7 @@ grub_xnu_set_video (struct grub_xnu_boot_params *params) { tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar); if (! tmp) - return grub_error (GRUB_ERR_OUT_OF_MEMORY, - "couldn't allocate temporary storag"); + return grub_errno; err = grub_video_set_mode (tmp, GRUB_VIDEO_MODE_TYPE_PURE_TEXT | GRUB_VIDEO_MODE_TYPE_DEPTH_MASK, @@ -61,31 +63,46 @@ grub_xnu_set_video (struct grub_xnu_boot_params *params) if (err) return err; + ret = grub_video_get_info (&mode_info); + if (ret) + return grub_error (GRUB_ERR_IO, "couldn't retrieve video parameters"); + if (grub_xnu_bitmap) + { + if (grub_xnu_bitmap_mode == GRUB_XNU_BITMAP_STRETCH) + err = grub_video_bitmap_create_scaled (&bitmap, + mode_info.width, + mode_info.height, + grub_xnu_bitmap, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + else + bitmap = grub_xnu_bitmap; + } + + if (bitmap) { int x, y; - x = mode_info.width - grub_xnu_bitmap->mode_info.width; + x = mode_info.width - bitmap->mode_info.width; x /= 2; - y = mode_info.height - grub_xnu_bitmap->mode_info.height; + y = mode_info.height - bitmap->mode_info.height; y /= 2; - err = grub_video_blit_bitmap (grub_xnu_bitmap, + err = grub_video_blit_bitmap (bitmap, GRUB_VIDEO_BLIT_REPLACE, x > 0 ? x : 0, y > 0 ? y : 0, x < 0 ? -x : 0, y < 0 ? -y : 0, - min (grub_xnu_bitmap->mode_info.width, + min (bitmap->mode_info.width, mode_info.width), - min (grub_xnu_bitmap->mode_info.height, + min (bitmap->mode_info.height, mode_info.height)); - if (err) - { - grub_print_error (); - grub_errno = GRUB_ERR_NONE; - grub_xnu_bitmap = 0; - } - err = GRUB_ERR_NONE; + } + if (err) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + bitmap = 0; } ret = grub_video_get_info_and_fini (&mode_info, &framebuffer); @@ -98,8 +115,8 @@ grub_xnu_set_video (struct grub_xnu_boot_params *params) params->lfb_line_len = mode_info.pitch; params->lfb_base = PTR_TO_UINT32 (framebuffer); - params->lfb_mode = grub_xnu_bitmap - ? GRUB_XNU_VIDEO_SPLASH : GRUB_XNU_VIDEO_TEXT_IN_VIDEO; + params->lfb_mode = bitmap ? GRUB_XNU_VIDEO_SPLASH + : GRUB_XNU_VIDEO_TEXT_IN_VIDEO; return GRUB_ERR_NONE; } diff --git a/loader/mips/linux.c b/loader/mips/linux.c index 51060c4fb..64497f466 100644 --- a/loader/mips/linux.c +++ b/loader/mips/linux.c @@ -196,7 +196,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), { grub_elf_close (elf); return grub_error (GRUB_ERR_UNKNOWN_OS, - "This ELF file is not of the right type\n"); + "this ELF file is not of the right type\n"); } /* Release the previously used memory. */ @@ -236,7 +236,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (grub_elf_is_elf64 (elf)) err = grub_linux_load64 (elf, &extra, size); else - err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unknown ELF class"); + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "unknown ELF class"); grub_elf_close (elf); @@ -325,13 +325,13 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), grub_size_t overhead; if (argc == 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "No initrd specified"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no initrd specified"); if (!loaded) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "You need to load Linux first."); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load Linux first."); if (initrd_loaded) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "Only one initrd can be loaded."); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "only one initrd can be loaded."); file = grub_file_open (argv[0]); if (! file) @@ -353,7 +353,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), if (grub_file_read (file, playground + linux_size + overhead, size) != size) { - grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file"); + grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file"); grub_file_close (file); return grub_errno; diff --git a/loader/multiboot.c b/loader/multiboot.c new file mode 100644 index 000000000..ce1e75dda --- /dev/null +++ b/loader/multiboot.c @@ -0,0 +1,356 @@ +/* multiboot.c - boot a multiboot OS image. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +/* + * FIXME: The following features from the Multiboot specification still + * need to be implemented: + * - VBE support + * - symbol table + * - drives table + * - ROM configuration table + * - APM table + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GRUB_MACHINE_EFI +#include +#endif + +#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU) +#define DEFAULT_VIDEO_MODE "text" +#else +#define DEFAULT_VIDEO_MODE "auto" +#endif + +grub_size_t grub_multiboot_alloc_mbi; + +char *grub_multiboot_payload_orig; +grub_addr_t grub_multiboot_payload_dest; +grub_size_t grub_multiboot_pure_size; +grub_uint32_t grub_multiboot_payload_eip; +static int accepts_video; +static int accepts_ega_text; +static int console_required; +static grub_dl_t my_mod; + + +/* Return the length of the Multiboot mmap that will be needed to allocate + our platform's map. */ +grub_uint32_t +grub_get_multiboot_mmap_count (void) +{ + grub_size_t count = 0; + + auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t); + int NESTED_FUNC_ATTR hook (grub_uint64_t addr __attribute__ ((unused)), + grub_uint64_t size __attribute__ ((unused)), + grub_uint32_t type __attribute__ ((unused))) + { + count++; + return 0; + } + + grub_mmap_iterate (hook); + + return count; +} + +grub_err_t +grub_multiboot_set_video_mode (void) +{ + grub_err_t err; + const char *modevar; + + if (accepts_video || !GRUB_MACHINE_HAS_VGA_TEXT) + { + modevar = grub_env_get ("gfxpayload"); + if (! modevar || *modevar == 0) + err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0); + else + { + char *tmp; + tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar); + if (! tmp) + return grub_errno; + err = grub_video_set_mode (tmp, 0, 0); + grub_free (tmp); + } + } + else + err = grub_video_set_mode ("text", 0, 0); + + return err; +} + +static grub_err_t +grub_multiboot_boot (void) +{ + grub_size_t mbi_size; + grub_err_t err; + struct grub_relocator32_state state = MULTIBOOT_INITIAL_STATE; + + state.MULTIBOOT_ENTRY_REGISTER = grub_multiboot_payload_eip; + + mbi_size = grub_multiboot_get_mbi_size (); + if (grub_multiboot_alloc_mbi < mbi_size) + { + grub_multiboot_payload_orig + = grub_relocator32_realloc (grub_multiboot_payload_orig, + grub_multiboot_pure_size + mbi_size); + if (!grub_multiboot_payload_orig) + return grub_errno; + grub_multiboot_alloc_mbi = mbi_size; + } + + state.MULTIBOOT_MBI_REGISTER = grub_multiboot_payload_dest + + grub_multiboot_pure_size; + err = grub_multiboot_make_mbi (grub_multiboot_payload_orig, + grub_multiboot_payload_dest, + grub_multiboot_pure_size, mbi_size); + if (err) + return err; + +#ifdef GRUB_MACHINE_EFI + if (! grub_efi_finish_boot_services ()) + grub_fatal ("cannot exit boot services"); +#endif + + grub_relocator32_boot (grub_multiboot_payload_orig, + grub_multiboot_payload_dest, + state); + + /* Not reached. */ + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_multiboot_unload (void) +{ + grub_multiboot_free_mbi (); + + grub_relocator32_free (grub_multiboot_payload_orig); + + grub_multiboot_alloc_mbi = 0; + + grub_multiboot_payload_orig = NULL; + grub_dl_unref (my_mod); + + return GRUB_ERR_NONE; +} + +#define MULTIBOOT_LOAD_ELF64 +#include "multiboot_elfxx.c" +#undef MULTIBOOT_LOAD_ELF64 + +#define MULTIBOOT_LOAD_ELF32 +#include "multiboot_elfxx.c" +#undef MULTIBOOT_LOAD_ELF32 + +/* Load ELF32 or ELF64. */ +grub_err_t +grub_multiboot_load_elf (grub_file_t file, void *buffer) +{ + if (grub_multiboot_is_elf32 (buffer)) + return grub_multiboot_load_elf32 (file, buffer); + else if (grub_multiboot_is_elf64 (buffer)) + return grub_multiboot_load_elf64 (file, buffer); + + return grub_error (GRUB_ERR_UNKNOWN_OS, "unknown ELF class"); +} + +grub_err_t +grub_multiboot_set_console (int console_type, int accepted_consoles, + int width, int height, int depth, + int console_req) +{ + console_required = console_req; + if (!(accepted_consoles + & (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER + | (GRUB_MACHINE_HAS_VGA_TEXT ? GRUB_MULTIBOOT_CONSOLE_EGA_TEXT : 0)))) + { + if (console_required) + return grub_error (GRUB_ERR_BAD_OS, + "OS requires a console but none is available"); + grub_printf ("WARNING: no console will be available to OS"); + accepts_video = 0; + accepts_ega_text = 0; + return GRUB_ERR_NONE; + } + + if (console_type == GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER) + { + char *buf; + if (depth && width && height) + buf = grub_xasprintf ("%dx%dx%d,%dx%d,auto", width, + height, depth, width, height); + else if (width && height) + buf = grub_xasprintf ("%dx%d,auto", width, height); + else + buf = grub_strdup ("auto"); + + if (!buf) + return grub_errno; + grub_env_set ("gfxpayload", buf); + grub_free (buf); + } + else + grub_env_set ("gfxpayload", "text"); + + accepts_video = !!(accepted_consoles & GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER); + accepts_ega_text = !!(accepted_consoles & GRUB_MULTIBOOT_CONSOLE_EGA_TEXT); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_multiboot (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + grub_err_t err; + + grub_loader_unset (); + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified"); + + file = grub_gzfile_open (argv[0], 1); + if (! file) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "couldn't open file"); + + grub_dl_ref (my_mod); + + /* Skip filename. */ + grub_multiboot_init_mbi (argc - 1, argv + 1); + + grub_relocator32_free (grub_multiboot_payload_orig); + grub_multiboot_payload_orig = NULL; + + err = grub_multiboot_load (file); + if (err) + goto fail; + + grub_multiboot_set_bootdev (); + + grub_loader_set (grub_multiboot_boot, grub_multiboot_unload, 0); + + fail: + if (file) + grub_file_close (file); + + if (grub_errno != GRUB_ERR_NONE) + { + grub_relocator32_free (grub_multiboot_payload_orig); + grub_multiboot_free_mbi (); + grub_dl_unref (my_mod); + } + + return grub_errno; +} + +static grub_err_t +grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + grub_ssize_t size; + char *module = 0; + grub_err_t err; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified"); + + if (!grub_multiboot_payload_orig) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "you need to load the multiboot kernel first"); + + file = grub_gzfile_open (argv[0], 1); + if (! file) + return grub_errno; + + size = grub_file_size (file); + module = grub_memalign (MULTIBOOT_MOD_ALIGN, size); + if (! module) + { + grub_file_close (file); + return grub_errno; + } + + err = grub_multiboot_add_module ((grub_addr_t) module, size, + argc - 1, argv + 1); + if (err) + { + grub_file_close (file); + return err; + } + + if (grub_file_read (file, module, size) != size) + { + grub_file_close (file); + return grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file"); + } + + grub_file_close (file); + return GRUB_ERR_NONE;; +} + +static grub_command_t cmd_multiboot, cmd_module; + +GRUB_MOD_INIT(multiboot) +{ + cmd_multiboot = +#ifdef GRUB_USE_MULTIBOOT2 + grub_register_command ("multiboot2", grub_cmd_multiboot, + 0, N_("Load a multiboot 2 kernel.")); + cmd_module = + grub_register_command ("module2", grub_cmd_module, + 0, N_("Load a multiboot 2 module.")); +#else + grub_register_command ("multiboot", grub_cmd_multiboot, + 0, N_("Load a multiboot kernel.")); + cmd_module = + grub_register_command ("module", grub_cmd_module, + 0, N_("Load a multiboot module.")); +#endif + + my_mod = mod; +} + +GRUB_MOD_FINI(multiboot) +{ + grub_unregister_command (cmd_multiboot); + grub_unregister_command (cmd_module); +} diff --git a/loader/i386/multiboot_elfxx.c b/loader/multiboot_elfxx.c similarity index 95% rename from loader/i386/multiboot_elfxx.c rename to loader/multiboot_elfxx.c index 2e35183a4..92a52d3a8 100644 --- a/loader/i386/multiboot_elfxx.c +++ b/loader/multiboot_elfxx.c @@ -18,13 +18,13 @@ #if defined(MULTIBOOT_LOAD_ELF32) # define XX 32 -# define E_MACHINE EM_386 +# define E_MACHINE MULTIBOOT_ELF32_MACHINE # define ELFCLASSXX ELFCLASS32 # define Elf_Ehdr Elf32_Ehdr # define Elf_Phdr Elf32_Phdr #elif defined(MULTIBOOT_LOAD_ELF64) # define XX 64 -# define E_MACHINE EM_X86_64 +# define E_MACHINE MULTIBOOT_ELF64_MACHINE # define ELFCLASSXX ELFCLASS64 # define Elf_Ehdr Elf64_Ehdr # define Elf_Phdr Elf64_Phdr @@ -53,6 +53,7 @@ CONCAT(grub_multiboot_load_elf, XX) (grub_file_t file, void *buffer) char *phdr_base; int lowest_segment = -1, highest_segment = -1; int i; + grub_size_t code_size; if (ehdr->e_ident[EI_CLASS] != ELFCLASSXX) return grub_error (GRUB_ERR_UNKNOWN_OS, "invalid ELF class"); @@ -102,9 +103,9 @@ CONCAT(grub_multiboot_load_elf, XX) (grub_file_t file, void *buffer) grub_multiboot_pure_size += code_size; - alloc_mbi = grub_multiboot_get_mbi_size (); + grub_multiboot_alloc_mbi = grub_multiboot_get_mbi_size () + 65536; grub_multiboot_payload_orig - = grub_relocator32_alloc (grub_multiboot_pure_size + alloc_mbi + 65536); + = grub_relocator32_alloc (grub_multiboot_pure_size + grub_multiboot_alloc_mbi); if (!grub_multiboot_payload_orig) return grub_errno; diff --git a/loader/multiboot_loader.c b/loader/multiboot_loader.c deleted file mode 100644 index 6d042fa81..000000000 --- a/loader/multiboot_loader.c +++ /dev/null @@ -1,150 +0,0 @@ -/* multiboot_loader.c - boot multiboot kernel image */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2007,2008,2009,2010 Free Software Foundation, Inc. - * - * GRUB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GRUB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GRUB. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -grub_dl_t my_mod; - -static int -find_multi_boot1_header (grub_file_t file) -{ - struct multiboot_header *header; - char buffer[MULTIBOOT_SEARCH]; - int found_status = 0; - grub_ssize_t len; - - len = grub_file_read (file, buffer, MULTIBOOT_SEARCH); - if (len < 32) - return found_status; - - /* Look for the multiboot header in the buffer. The header should - be at least 12 bytes and aligned on a 4-byte boundary. */ - for (header = (struct multiboot_header *) buffer; - ((char *) header <= buffer + len - 12) || (header = 0); - header = (struct multiboot_header *) ((char *) header + 4)) - { - if (header->magic == MULTIBOOT_HEADER_MAGIC - && !(header->magic + header->flags + header->checksum)) - { - found_status = 1; - break; - } - } - - return found_status; -} - -static grub_err_t -grub_cmd_multiboot_loader (grub_command_t cmd __attribute__ ((unused)), - int argc, char *argv[]) -{ - grub_file_t file = 0; - int header_multi_ver_found = 0; - - grub_dl_ref (my_mod); - - if (argc == 0) - { - grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified"); - goto fail; - } - - file = grub_gzfile_open (argv[0], 1); - if (! file) - { - grub_error (GRUB_ERR_BAD_ARGUMENT, "couldn't open file"); - goto fail; - } - - /* find which header is in the file */ - if (find_multi_boot1_header (file)) - header_multi_ver_found = 1; - else - { - grub_error (GRUB_ERR_BAD_OS, "multiboot header not found"); - goto fail; - } - - /* close file before calling functions */ - if (file) - grub_file_close (file); - - /* Launch multi boot with header */ - - grub_dprintf ("multiboot_loader", - "Launching multiboot 1 grub_multiboot() function\n"); - grub_multiboot (argc, argv); - - return grub_errno; - -fail: - if (file) - grub_file_close (file); - - grub_dl_unref (my_mod); - - return grub_errno; -} - -static grub_err_t -grub_cmd_module_loader (grub_command_t cmd __attribute__ ((unused)), - int argc, char *argv[]) -{ - - grub_dprintf("multiboot_loader", - "Launching multiboot 1 grub_module() function\n"); - grub_module (argc, argv); - - return grub_errno; -} - -static grub_command_t cmd_multiboot, cmd_module; - -GRUB_MOD_INIT(multiboot) -{ - cmd_multiboot = -#ifdef GRUB_USE_MULTIBOOT2 - grub_register_command ("multiboot2", grub_cmd_multiboot_loader, - 0, N_("Load a multiboot 2 kernel.")); -#else - grub_register_command ("multiboot", grub_cmd_multiboot_loader, - 0, N_("Load a multiboot kernel.")); -#endif - - cmd_module = - grub_register_command ("module", grub_cmd_module_loader, - 0, N_("Load a multiboot module.")); - - my_mod = mod; -} - -GRUB_MOD_FINI(multiboot) -{ - grub_unregister_command (cmd_multiboot); - grub_unregister_command (cmd_module); -} diff --git a/loader/multiboot_mbi2.c b/loader/multiboot_mbi2.c new file mode 100644 index 000000000..8b67f9383 --- /dev/null +++ b/loader/multiboot_mbi2.c @@ -0,0 +1,701 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#ifdef GRUB_MACHINE_PCBIOS +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU) +#include +#define HAS_VGA_TEXT 1 +#else +#define HAS_VGA_TEXT 0 +#endif + +struct module +{ + struct module *next; + grub_addr_t start; + grub_size_t size; + char *cmdline; + int cmdline_size; +}; + +struct module *modules, *modules_last; +static grub_size_t cmdline_size; +static grub_size_t total_modcmd; +static unsigned modcnt; +static char *cmdline = NULL; +static int bootdev_set; +static grub_uint32_t biosdev, slice, part; + +grub_err_t +grub_multiboot_load (grub_file_t file) +{ + char *buffer; + grub_ssize_t len; + struct multiboot_header *header; + grub_err_t err; + struct multiboot_header_tag *tag; + struct multiboot_header_tag_address *addr_tag = NULL; + int entry_specified = 0; + grub_addr_t entry = 0; + grub_uint32_t console_required = 0; + struct multiboot_header_tag_framebuffer *fbtag = NULL; + int accepted_consoles = GRUB_MULTIBOOT_CONSOLE_EGA_TEXT; + + buffer = grub_malloc (MULTIBOOT_SEARCH); + if (!buffer) + return grub_errno; + + len = grub_file_read (file, buffer, MULTIBOOT_SEARCH); + if (len < 32) + { + grub_free (buffer); + return grub_error (GRUB_ERR_BAD_OS, "file too small"); + } + + /* Look for the multiboot header in the buffer. The header should + be at least 12 bytes and aligned on a 4-byte boundary. */ + for (header = (struct multiboot_header *) buffer; + ((char *) header <= buffer + len - 12) || (header = 0); + header = (struct multiboot_header *) ((char *) header + MULTIBOOT_HEADER_ALIGN)) + { + if (header->magic == MULTIBOOT_HEADER_MAGIC + && !(header->magic + header->architecture + + header->header_length + header->checksum) + && header->architecture == MULTIBOOT_ARCHITECTURE_CURRENT) + break; + } + + if (header == 0) + { + grub_free (buffer); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no multiboot header found"); + } + + for (tag = (struct multiboot_header_tag *) (header + 1); + tag->type != MULTIBOOT_TAG_TYPE_END; + tag = (struct multiboot_header_tag *) ((char *) tag + tag->size)) + switch (tag->type) + { + case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST: + { + unsigned i; + struct multiboot_header_tag_information_request *request_tag + = (struct multiboot_header_tag_information_request *) tag; + if (request_tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL) + break; + for (i = 0; i < (request_tag->size - sizeof (request_tag)) + / sizeof (request_tag->requests[0]); i++) + switch (request_tag->requests[i]) + { + case MULTIBOOT_TAG_TYPE_END: + case MULTIBOOT_TAG_TYPE_CMDLINE: + case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME: + case MULTIBOOT_TAG_TYPE_MODULE: + case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: + case MULTIBOOT_TAG_TYPE_BOOTDEV: + case MULTIBOOT_TAG_TYPE_MMAP: + case MULTIBOOT_TAG_TYPE_FRAMEBUFFER: + break; + + case MULTIBOOT_TAG_TYPE_VBE: + case MULTIBOOT_TAG_TYPE_ELF_SECTIONS: + case MULTIBOOT_TAG_TYPE_APM: + default: + grub_free (buffer); + return grub_error (GRUB_ERR_UNKNOWN_OS, + "unsupported information tag: 0x%x", + request_tag->requests[i]); + } + break; + } + + case MULTIBOOT_HEADER_TAG_ADDRESS: + addr_tag = (struct multiboot_header_tag_address *) tag; + break; + + case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS: + entry_specified = 1; + entry = ((struct multiboot_header_tag_entry_address *) tag)->entry_addr; + break; + + case MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS: + if (!(((struct multiboot_header_tag_console_flags *) tag)->console_flags + & MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED)) + accepted_consoles &= ~GRUB_MULTIBOOT_CONSOLE_EGA_TEXT; + if (((struct multiboot_header_tag_console_flags *) tag)->console_flags + & MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED) + console_required = 1; + break; + + case MULTIBOOT_HEADER_TAG_FRAMEBUFFER: + fbtag = (struct multiboot_header_tag_framebuffer *) tag; + accepted_consoles |= GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER; + break; + + /* GRUB always page-aligns modules. */ + case MULTIBOOT_HEADER_TAG_MODULE_ALIGN: + break; + + default: + if (! (tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL)) + { + grub_free (buffer); + return grub_error (GRUB_ERR_UNKNOWN_OS, + "unsupported tag: 0x%x", tag->type); + } + break; + } + + if (addr_tag && !entry_specified) + { + grub_free (buffer); + return grub_error (GRUB_ERR_UNKNOWN_OS, + "load address tag without entry address tag"); + } + + if (addr_tag) + { + int offset = ((char *) header - buffer - + (addr_tag->header_addr - addr_tag->load_addr)); + int load_size = ((addr_tag->load_end_addr == 0) ? file->size - offset : + addr_tag->load_end_addr - addr_tag->load_addr); + grub_size_t code_size; + + if (addr_tag->bss_end_addr) + code_size = (addr_tag->bss_end_addr - addr_tag->load_addr); + else + code_size = load_size; + grub_multiboot_payload_dest = addr_tag->load_addr; + + grub_multiboot_pure_size += code_size; + + /* Allocate a bit more to avoid relocations in most cases. */ + grub_multiboot_alloc_mbi = grub_multiboot_get_mbi_size () + 65536; + grub_multiboot_payload_orig + = grub_relocator32_alloc (grub_multiboot_pure_size + grub_multiboot_alloc_mbi); + + if (! grub_multiboot_payload_orig) + { + grub_free (buffer); + return grub_errno; + } + + if ((grub_file_seek (file, offset)) == (grub_off_t) -1) + { + grub_free (buffer); + return grub_errno; + } + + grub_file_read (file, (void *) grub_multiboot_payload_orig, load_size); + if (grub_errno) + { + grub_free (buffer); + return grub_errno; + } + + if (addr_tag->bss_end_addr) + grub_memset ((void *) (grub_multiboot_payload_orig + load_size), 0, + addr_tag->bss_end_addr - addr_tag->load_addr - load_size); + } + else + { + err = grub_multiboot_load_elf (file, buffer); + if (err) + { + grub_free (buffer); + return err; + } + } + + if (entry_specified) + grub_multiboot_payload_eip = entry; + + if (fbtag) + err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER, + accepted_consoles, + fbtag->width, fbtag->height, + fbtag->depth, console_required); + else + err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT, + accepted_consoles, + 0, 0, 0, console_required); + return err; +} + +grub_size_t +grub_multiboot_get_mbi_size (void) +{ + return 2 * sizeof (grub_uint32_t) + sizeof (struct multiboot_tag) + + (sizeof (struct multiboot_tag_string) + + ALIGN_UP (cmdline_size, MULTIBOOT_TAG_ALIGN)) + + (sizeof (struct multiboot_tag_string) + + ALIGN_UP (sizeof (PACKAGE_STRING), MULTIBOOT_TAG_ALIGN)) + + (modcnt * sizeof (struct multiboot_tag_module) + total_modcmd) + + sizeof (struct multiboot_tag_basic_meminfo) + + ALIGN_UP (sizeof (struct multiboot_tag_bootdev), MULTIBOOT_TAG_ALIGN) + + (sizeof (struct multiboot_tag_mmap) + grub_get_multiboot_mmap_count () + * sizeof (struct multiboot_mmap_entry)) + + sizeof (struct multiboot_tag_vbe) + MULTIBOOT_TAG_ALIGN - 1; +} + +/* Fill previously allocated Multiboot mmap. */ +static void +grub_fill_multiboot_mmap (struct multiboot_tag_mmap *tag) +{ + struct multiboot_mmap_entry *mmap_entry = tag->entries; + + auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t); + int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, grub_uint32_t type) + { + mmap_entry->addr = addr; + mmap_entry->len = size; + switch (type) + { + case GRUB_MACHINE_MEMORY_AVAILABLE: + mmap_entry->type = MULTIBOOT_MEMORY_AVAILABLE; + break; + +#ifdef GRUB_MACHINE_MEMORY_ACPI_RECLAIMABLE + case GRUB_MACHINE_MEMORY_ACPI_RECLAIMABLE: + mmap_entry->type = MULTIBOOT_MEMORY_ACPI_RECLAIMABLE; + break; +#endif + +#ifdef GRUB_MACHINE_MEMORY_NVS + case GRUB_MACHINE_MEMORY_NVS: + mmap_entry->type = MULTIBOOT_MEMORY_NVS; + break; +#endif + + default: + mmap_entry->type = MULTIBOOT_MEMORY_RESERVED; + break; + } + mmap_entry++; + + return 0; + } + + tag->type = MULTIBOOT_TAG_TYPE_MMAP; + tag->size = sizeof (struct multiboot_tag_mmap) + + sizeof (struct multiboot_mmap_entry) * grub_get_multiboot_mmap_count (); + tag->entry_size = sizeof (struct multiboot_mmap_entry); + tag->entry_version = 0; + + grub_mmap_iterate (hook); +} + +static grub_err_t +retrieve_video_parameters (grub_uint8_t **ptrorig) +{ + grub_err_t err; + struct grub_video_mode_info mode_info; + void *framebuffer; + grub_video_driver_id_t driv_id; + struct grub_video_palette_data palette[256]; + struct multiboot_tag_framebuffer *tag + = (struct multiboot_tag_framebuffer *) *ptrorig; + + err = grub_multiboot_set_video_mode (); + if (err) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + } + + grub_video_get_palette (0, ARRAY_SIZE (palette), palette); + + driv_id = grub_video_get_driver_id (); +#if HAS_VGA_TEXT + if (driv_id == GRUB_VIDEO_DRIVER_NONE) + { + struct grub_vbe_mode_info_block vbe_mode_info; + grub_uint32_t vbe_mode; + +#if defined (GRUB_MACHINE_PCBIOS) + { + grub_vbe_status_t status; + void *scratch = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; + status = grub_vbe_bios_get_mode (scratch); + vbe_mode = *(grub_uint32_t *) scratch; + if (status != GRUB_VBE_STATUS_OK) + return GRUB_ERR_NONE; + } +#else + vbe_mode = 3; +#endif + + /* get_mode_info isn't available for mode 3. */ + if (vbe_mode == 3) + { + grub_memset (&vbe_mode_info, 0, + sizeof (struct grub_vbe_mode_info_block)); + vbe_mode_info.memory_model = GRUB_VBE_MEMORY_MODEL_TEXT; + vbe_mode_info.x_resolution = 80; + vbe_mode_info.y_resolution = 25; + } +#if defined (GRUB_MACHINE_PCBIOS) + else + { + grub_vbe_status_t status; + void *scratch = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; + status = grub_vbe_bios_get_mode_info (vbe_mode, scratch); + if (status != GRUB_VBE_STATUS_OK) + return GRUB_ERR_NONE; + grub_memcpy (&vbe_mode_info, scratch, + sizeof (struct grub_vbe_mode_info_block)); + } +#endif + + if (vbe_mode_info.memory_model == GRUB_VBE_MEMORY_MODEL_TEXT) + { + tag = (struct multiboot_tag_framebuffer *) *ptrorig; + tag->common.type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER; + tag->common.size = 0; + + tag->common.framebuffer_addr = 0xb8000; + + tag->common.framebuffer_pitch = 2 * vbe_mode_info.x_resolution; + tag->common.framebuffer_width = vbe_mode_info.x_resolution; + tag->common.framebuffer_height = vbe_mode_info.y_resolution; + + tag->common.framebuffer_bpp = 16; + + tag->common.framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT; + tag->common.size = sizeof (tag->common); + tag->common.reserved = 0; + *ptrorig += ALIGN_UP (tag->common.size, MULTIBOOT_TAG_ALIGN); + } + return GRUB_ERR_NONE; + } +#else + if (driv_id == GRUB_VIDEO_DRIVER_NONE) + return GRUB_ERR_NONE; +#endif + + err = grub_video_get_info_and_fini (&mode_info, &framebuffer); + if (err) + return err; + + tag = (struct multiboot_tag_framebuffer *) *ptrorig; + tag->common.type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER; + tag->common.size = 0; + + tag->common.framebuffer_addr = (grub_addr_t) framebuffer; + tag->common.framebuffer_pitch = mode_info.pitch; + + tag->common.framebuffer_width = mode_info.width; + tag->common.framebuffer_height = mode_info.height; + + tag->common.framebuffer_bpp = mode_info.bpp; + + tag->common.reserved = 0; + + if (mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) + { + unsigned i; + tag->common.framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED; + tag->framebuffer_palette_num_colors = mode_info.number_of_colors; + if (tag->framebuffer_palette_num_colors > ARRAY_SIZE (palette)) + tag->framebuffer_palette_num_colors = ARRAY_SIZE (palette); + tag->common.size = sizeof (struct multiboot_tag_framebuffer_common) + + sizeof (multiboot_uint16_t) + tag->framebuffer_palette_num_colors + * sizeof (struct multiboot_color); + for (i = 0; i < tag->framebuffer_palette_num_colors; i++) + { + tag->framebuffer_palette[i].red = palette[i].r; + tag->framebuffer_palette[i].green = palette[i].g; + tag->framebuffer_palette[i].blue = palette[i].b; + } + } + else + { + tag->common.framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_RGB; + tag->common.framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_RGB; + tag->framebuffer_red_field_position = mode_info.green_field_pos; + tag->framebuffer_red_mask_size = mode_info.green_mask_size; + tag->framebuffer_green_field_position = mode_info.green_field_pos; + tag->framebuffer_green_mask_size = mode_info.green_mask_size; + tag->framebuffer_blue_field_position = mode_info.blue_field_pos; + tag->framebuffer_blue_mask_size = mode_info.blue_mask_size; + + tag->common.size = sizeof (struct multiboot_tag_framebuffer_common) + 6; + } + *ptrorig += ALIGN_UP (tag->common.size, MULTIBOOT_TAG_ALIGN); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_multiboot_make_mbi (void *orig, grub_uint32_t dest, grub_off_t buf_off, + grub_size_t bufsize) +{ + grub_uint8_t *ptrorig; + grub_uint8_t *mbistart = (grub_uint8_t *) orig + buf_off + + (ALIGN_UP (dest + buf_off, MULTIBOOT_TAG_ALIGN) - (dest + buf_off)); + grub_err_t err; + + if (bufsize < grub_multiboot_get_mbi_size ()) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "mbi buffer is too small"); + + ptrorig = mbistart + 2 * sizeof (grub_uint32_t); + + { + struct multiboot_tag_string *tag = (struct multiboot_tag_string *) ptrorig; + tag->type = MULTIBOOT_TAG_TYPE_CMDLINE; + tag->size = sizeof (struct multiboot_tag_string) + cmdline_size; + grub_memcpy (tag->string, cmdline, cmdline_size); + ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN); + } + + { + struct multiboot_tag_string *tag = (struct multiboot_tag_string *) ptrorig; + tag->type = MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME; + tag->size = sizeof (struct multiboot_tag_string) + sizeof (PACKAGE_STRING); + grub_memcpy (tag->string, PACKAGE_STRING, sizeof (PACKAGE_STRING)); + ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN); + } + + { + unsigned i; + struct module *cur; + + for (i = 0, cur = modules; i < modcnt; i++, cur = cur->next) + { + struct multiboot_tag_module *tag + = (struct multiboot_tag_module *) ptrorig; + tag->type = MULTIBOOT_TAG_TYPE_MODULE; + tag->size = sizeof (struct multiboot_tag_module) + cur->cmdline_size; + tag->mod_start = dest + cur->start; + tag->mod_end = tag->mod_start + cur->size; + grub_memcpy (tag->cmdline, cur->cmdline, cur->cmdline_size); + ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN); + } + } + + { + struct multiboot_tag_mmap *tag = (struct multiboot_tag_mmap *) ptrorig; + grub_fill_multiboot_mmap (tag); + ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN); + } + + { + struct multiboot_tag_basic_meminfo *tag + = (struct multiboot_tag_basic_meminfo *) ptrorig; + tag->type = MULTIBOOT_TAG_TYPE_BASIC_MEMINFO; + tag->size = sizeof (struct multiboot_tag_basic_meminfo); + + /* Convert from bytes to kilobytes. */ + tag->mem_lower = grub_mmap_get_lower () / 1024; + tag->mem_upper = grub_mmap_get_upper () / 1024; + ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN); + } + + if (bootdev_set) + { + struct multiboot_tag_bootdev *tag + = (struct multiboot_tag_bootdev *) ptrorig; + tag->type = MULTIBOOT_TAG_TYPE_BOOTDEV; + tag->size = sizeof (struct multiboot_tag_bootdev); + + tag->biosdev = biosdev; + tag->slice = slice; + tag->part = part; + ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN); + } + + { + err = retrieve_video_parameters (&ptrorig); + if (err) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + } + } + + { + struct multiboot_tag *tag = (struct multiboot_tag *) ptrorig; + tag->type = MULTIBOOT_TAG_TYPE_END; + tag->size = sizeof (struct multiboot_tag); + ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN); + } + + ((grub_uint32_t *) mbistart)[0] = ptrorig - mbistart; + ((grub_uint32_t *) mbistart)[1] = 0; + + return GRUB_ERR_NONE; +} + +void +grub_multiboot_free_mbi (void) +{ + struct module *cur, *next; + + cmdline_size = 0; + total_modcmd = 0; + modcnt = 0; + grub_free (cmdline); + cmdline = NULL; + bootdev_set = 0; + + for (cur = modules; cur; cur = next) + { + next = cur->next; + grub_free (cur->cmdline); + grub_free (cur); + } + modules = NULL; + modules_last = NULL; +} + +grub_err_t +grub_multiboot_init_mbi (int argc, char *argv[]) +{ + grub_ssize_t len = 0; + char *p; + int i; + + grub_multiboot_free_mbi (); + + for (i = 0; i < argc; i++) + len += grub_strlen (argv[i]) + 1; + if (len == 0) + len = 1; + + cmdline = p = grub_malloc (len); + if (! cmdline) + return grub_errno; + cmdline_size = len; + + for (i = 0; i < argc; i++) + { + p = grub_stpcpy (p, argv[i]); + *(p++) = ' '; + } + + /* Remove the space after the last word. */ + if (p != cmdline) + p--; + *p = '\0'; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_multiboot_add_module (grub_addr_t start, grub_size_t size, + int argc, char *argv[]) +{ + struct module *newmod; + char *p; + grub_ssize_t len = 0; + int i; + + newmod = grub_malloc (sizeof (*newmod)); + if (!newmod) + return grub_errno; + newmod->start = start; + newmod->size = size; + + for (i = 0; i < argc; i++) + len += grub_strlen (argv[i]) + 1; + + if (len == 0) + len = 1; + + newmod->cmdline = p = grub_malloc (len); + if (! newmod->cmdline) + { + grub_free (newmod); + return grub_errno; + } + newmod->cmdline_size = len; + total_modcmd += ALIGN_UP (len, MULTIBOOT_TAG_ALIGN); + + for (i = 0; i < argc; i++) + { + p = grub_stpcpy (p, argv[i]); + *(p++) = ' '; + } + + /* Remove the space after the last word. */ + if (p != newmod->cmdline) + p--; + *p = '\0'; + + if (modules_last) + modules_last->next = newmod; + else + { + modules = newmod; + modules_last->next = NULL; + } + modules_last = newmod; + + modcnt++; + + return GRUB_ERR_NONE; +} + +void +grub_multiboot_set_bootdev (void) +{ + grub_device_t dev; + + slice = ~0; + part = ~0; + +#ifdef GRUB_MACHINE_PCBIOS + biosdev = grub_get_root_biosnumber (); +#else + biosdev = 0xffffffff; +#endif + + if (biosdev == 0xffffffff) + return; + + dev = grub_device_open (0); + if (dev && dev->disk && dev->disk->partition) + { + if (dev->disk->partition->parent) + { + part = dev->disk->partition->number; + slice = dev->disk->partition->parent->number; + } + else + slice = dev->disk->partition->number; + } + if (dev) + grub_device_close (dev); + + bootdev_set = 1; +} diff --git a/loader/sparc64/ieee1275/linux.c b/loader/sparc64/ieee1275/linux.c index 42bae6bb8..f55b4fa2a 100644 --- a/loader/sparc64/ieee1275/linux.c +++ b/loader/sparc64/ieee1275/linux.c @@ -58,9 +58,6 @@ static grub_size_t linux_size; static char *linux_args; -typedef void (*kernel_entry_t) (unsigned long, unsigned long, - unsigned long, unsigned long, int (void *)); - struct linux_bootstr_info { int len, valid; char buf[]; @@ -92,7 +89,6 @@ static grub_err_t grub_linux_boot (void) { struct linux_bootstr_info *bp; - kernel_entry_t linuxmain; struct linux_hdrs *hp; grub_addr_t addr; @@ -141,8 +137,17 @@ grub_linux_boot (void) grub_dprintf ("loader", "Jumping to Linux...\n"); /* Boot the kernel. */ - linuxmain = (kernel_entry_t) linux_addr; - linuxmain (0, 0, 0, 0, grub_ieee1275_entry_fn); + asm volatile ("sethi %hi(grub_ieee1275_entry_fn), %o1\n" + "ldx [%o1 + %lo(grub_ieee1275_entry_fn)], %o4\n" + "sethi %hi(grub_ieee1275_original_stack), %o1\n" + "ldx [%o1 + %lo(grub_ieee1275_original_stack)], %o6\n" + "sethi %hi(linux_addr), %o1\n" + "ldx [%o1 + %lo(linux_addr)], %o5\n" + "mov %g0, %o0\n" + "mov %g0, %o2\n" + "mov %g0, %o3\n" + "jmp %o5\n" + "mov %g0, %o1\n"); return GRUB_ERR_NONE; } @@ -173,12 +178,6 @@ grub_linux_unload (void) #define FOUR_MB (4 * 1024 * 1024) -static grub_addr_t -align_addr(grub_addr_t val, grub_addr_t align) -{ - return (val + (align - 1)) & ~(align - 1); -} - static grub_addr_t alloc_phys (grub_addr_t size) { @@ -192,39 +191,39 @@ alloc_phys (grub_addr_t size) if (type != 1) return 0; - addr = align_addr (addr, FOUR_MB); - if (addr >= end) + addr = ALIGN_UP (addr, FOUR_MB); + if (addr + size >= end) return 0; if (addr >= grub_phys_start && addr < grub_phys_end) { - addr = align_addr (grub_phys_end, FOUR_MB); - if (addr >= end) + addr = ALIGN_UP (grub_phys_end, FOUR_MB); + if (addr + size >= end) return 0; } if ((addr + size) >= grub_phys_start && (addr + size) < grub_phys_end) { - addr = align_addr (grub_phys_end, FOUR_MB); - if (addr >= end) + addr = ALIGN_UP (grub_phys_end, FOUR_MB); + if (addr + size >= end) return 0; } if (loaded) { - grub_addr_t linux_end = align_addr (linux_paddr + linux_size, FOUR_MB); + grub_addr_t linux_end = ALIGN_UP (linux_paddr + linux_size, FOUR_MB); if (addr >= linux_paddr && addr < linux_end) { addr = linux_end; - if (addr >= end) + if (addr + size >= end) return 0; } if ((addr + size) >= linux_paddr && (addr + size) < linux_end) { addr = linux_end; - if (addr >= end) + if (addr + size >= end) return 0; } } @@ -258,8 +257,8 @@ grub_linux_load64 (grub_elf_t elf) if (paddr == (grub_addr_t) -1) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate physical memory"); - ret = grub_ieee1275_map_physical (paddr, linux_addr - off, - linux_size + off, IEEE1275_MAP_DEFAULT); + ret = grub_ieee1275_map (paddr, linux_addr - off, + linux_size + off, IEEE1275_MAP_DEFAULT); if (ret) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't map physical memory"); @@ -409,7 +408,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), "couldn't allocate physical memory"); goto fail; } - ret = grub_ieee1275_map_physical (paddr, addr, size, IEEE1275_MAP_DEFAULT); + ret = grub_ieee1275_map (paddr, addr, size, IEEE1275_MAP_DEFAULT); if (ret) { grub_error (GRUB_ERR_OUT_OF_MEMORY, diff --git a/loader/xnu.c b/loader/xnu.c index e0a2dfe8b..8f1d0c641 100644 --- a/loader/xnu.c +++ b/loader/xnu.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -1354,23 +1355,42 @@ grub_xnu_fill_devicetree (void) } struct grub_video_bitmap *grub_xnu_bitmap = 0; +grub_xnu_bitmap_mode_t grub_xnu_bitmap_mode; + +/* Option array indices. */ +#define XNU_SPLASH_CMD_ARGINDEX_MODE 0 + +static const struct grub_arg_option xnu_splash_cmd_options[] = + { + {"mode", 'm', 0, "Background image mode.", "stretch|normal", + ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} + }; static grub_err_t -grub_cmd_xnu_splash (grub_command_t cmd __attribute__ ((unused)), +grub_cmd_xnu_splash (grub_extcmd_t cmd, int argc, char *args[]) { grub_err_t err; if (argc != 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required"); + if (cmd->state[XNU_SPLASH_CMD_ARGINDEX_MODE].set && + grub_strcmp (cmd->state[XNU_SPLASH_CMD_ARGINDEX_MODE].arg, + "stretch") == 0) + grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_STRETCH; + else + grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_CENTER; + err = grub_video_bitmap_load (&grub_xnu_bitmap, args[0]); if (err) grub_xnu_bitmap = 0; + return err; } -#ifndef GRUB_UTIL +#ifndef GRUB_MACHINE_EMU static grub_err_t grub_cmd_xnu_resume (grub_command_t cmd __attribute__ ((unused)), int argc, char *args[]) @@ -1399,7 +1419,8 @@ grub_xnu_unlock () } static grub_command_t cmd_kernel64, cmd_kernel, cmd_mkext, cmd_kext; -static grub_command_t cmd_kextdir, cmd_ramdisk, cmd_resume, cmd_splash; +static grub_command_t cmd_kextdir, cmd_ramdisk, cmd_resume; +static grub_extcmd_t cmd_splash; GRUB_MOD_INIT(xnu) { @@ -1415,12 +1436,15 @@ GRUB_MOD_INIT(xnu) N_("DIRECTORY [OSBundleRequired]"), N_("Load XNU extension directory.")); cmd_ramdisk = grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk, 0, - N_("Load XNU ramdisk. " - "It will be seen as md0.")); - cmd_splash = grub_register_command ("xnu_splash", grub_cmd_xnu_splash, 0, - N_("Load a splash image for XNU.")); + "Load XNU ramdisk. " + "It will be seen as md0."); + cmd_splash = grub_register_extcmd ("xnu_splash", + grub_cmd_xnu_splash, + GRUB_COMMAND_FLAG_BOTH, 0, + N_("Load a splash image for XNU."), + xnu_splash_cmd_options); -#ifndef GRUB_UTIL +#ifndef GRUB_MACHINE_EMU cmd_resume = grub_register_command ("xnu_resume", grub_cmd_xnu_resume, 0, N_("Load XNU hibernate image.")); #endif @@ -1432,7 +1456,7 @@ GRUB_MOD_INIT(xnu) GRUB_MOD_FINI(xnu) { -#ifndef GRUB_UTIL +#ifndef GRUB_MACHINE_EMU grub_unregister_command (cmd_resume); #endif grub_unregister_command (cmd_mkext); @@ -1440,8 +1464,8 @@ GRUB_MOD_FINI(xnu) grub_unregister_command (cmd_kextdir); grub_unregister_command (cmd_ramdisk); grub_unregister_command (cmd_kernel); + grub_unregister_extcmd (cmd_splash); grub_unregister_command (cmd_kernel64); - grub_unregister_command (cmd_splash); grub_cpu_xnu_fini (); } diff --git a/mmap/i386/pc/mmap_helper.S b/mmap/i386/pc/mmap_helper.S index c6d12fd6c..743954574 100644 --- a/mmap/i386/pc/mmap_helper.S +++ b/mmap/i386/pc/mmap_helper.S @@ -19,32 +19,27 @@ #include -#define DS(x) ((x) - segstart) +#define DS(x) ((x) - LOCAL (segstart)) -segstart: +LOCAL (segstart): VARIABLE(grub_machine_mmaphook_start) .code16 VARIABLE(grub_machine_mmaphook_int12) push %ds push %cs pop %ds -#ifdef APPLE_CC - grub_machine_mmaphook_kblow_rel = DS (EXT_C (grub_machine_mmaphook_kblow)) - movw (grub_machine_mmaphook_kblow_rel), %ax -#else - movw DS (EXT_C (grub_machine_mmaphook_kblow)), %ax -#endif + movw DS (LOCAL (kblow)), %ax pop %ds iret VARIABLE(grub_machine_mmaphook_int15) pushf cmpw $0xe801, %ax - jz e801 + jz LOCAL (e801) cmpw $0xe820, %ax - jz e820 + jz LOCAL (e820) cmpb $0x88, %ah - jz h88 + jz LOCAL (h88) popf /* ljmp */ .byte 0xea @@ -53,67 +48,44 @@ VARIABLE (grub_machine_mmaphook_int15offset) VARIABLE (grub_machine_mmaphook_int15segment) .word 0 -e801: +LOCAL (e801): popf push %ds push %cs pop %ds -#ifdef APPLE_CC - grub_machine_mmaphook_kbin16mb_rel = DS (EXT_C (grub_machine_mmaphook_kbin16mb)) - grub_machine_mmaphook_64kbin4gb_rel = DS (EXT_C (grub_machine_mmaphook_64kbin4gb)) - movw (grub_machine_mmaphook_kbin16mb_rel), %ax - movw (grub_machine_mmaphook_64kbin4gb_rel), %bx -#else - movw DS (EXT_C (grub_machine_mmaphook_kbin16mb)), %ax - movw DS (EXT_C (grub_machine_mmaphook_64kbin4gb)), %bx -#endif + movw DS (LOCAL (kbin16mb)), %ax + movw DS (LOCAL (m64kbin4gb)), %bx movw %ax, %cx movw %bx, %dx pop %ds clc iret -h88: +LOCAL (h88): popf push %ds push %cs pop %ds -#ifdef APPLE_CC - movw (grub_machine_mmaphook_kbin16mb_rel), %ax -#else - movw DS (EXT_C (grub_machine_mmaphook_kbin16mb)), %ax -#endif + movw DS (LOCAL (kbin16mb)), %ax pop %ds clc iret -e820: -#ifdef APPLE_CC - mmaphook_mmap_rel = DS(mmaphook_mmap) - mmaphook_mmap_num_rel = DS(EXT_C(grub_machine_mmaphook_mmap_num)) -#endif +LOCAL (e820): popf push %ds push %cs pop %ds cmpw $20, %cx - jb errexit -#ifdef APPLE_CC - cmpw (mmaphook_mmap_num_rel), %bx -#else - cmpw DS (EXT_C (grub_machine_mmaphook_mmap_num)), %bx -#endif - jae errexit + jb LOCAL (errexit) + cmpw DS (LOCAL (mmap_num)), %bx + jae LOCAL (errexit) cmp $0x534d4150, %edx - jne errexit + jne LOCAL (errexit) push %si push %di movw $20, %cx -#ifdef APPLE_CC - movl $(mmaphook_mmap_rel), %esi -#else - movw $(DS(mmaphook_mmap)), %si -#endif + movw $(DS(LOCAL (mmaphook_mmap))), %si mov %bx, %ax imul $20, %ax add %ax, %si @@ -122,19 +94,15 @@ e820: pop %si movl $20, %ecx inc %bx -#ifdef APPLE_CC - cmpw (mmaphook_mmap_num_rel), %bx -#else - cmpw DS(EXT_C(grub_machine_mmaphook_mmap_num)), %bx -#endif - jb noclean + cmpw DS(LOCAL (mmap_num)), %bx + jb LOCAL (noclean) xor %bx, %bx -noclean: +LOCAL (noclean): mov $0x534d4150, %eax pop %ds clc iret -errexit: +LOCAL (errexit): mov $0x534d4150, %eax pop %ds stc @@ -142,13 +110,18 @@ errexit: iret VARIABLE(grub_machine_mmaphook_mmap_num) +LOCAL (mmap_num): .word 0 VARIABLE(grub_machine_mmaphook_kblow) +LOCAL (kblow): .word 0 VARIABLE (grub_machine_mmaphook_kbin16mb) +LOCAL (kbin16mb): .word 0 VARIABLE (grub_machine_mmaphook_64kbin4gb) +LOCAL (m64kbin4gb): .word 0 -mmaphook_mmap: +LOCAL (mmaphook_mmap): /* Memory map is placed just after the interrupt handlers. */ VARIABLE(grub_machine_mmaphook_end) + .byte 0 diff --git a/normal/autofs.c b/normal/autofs.c index 57e43fdf4..e1d4c017c 100644 --- a/normal/autofs.c +++ b/normal/autofs.c @@ -38,6 +38,9 @@ autoload_fs_module (void) if (! grub_dl_get (p->name) && grub_dl_load (p->name)) return 1; + if (grub_errno) + grub_print_error (); + fs_module_list = p->next; grub_free (p->name); grub_free (p); @@ -48,11 +51,8 @@ autoload_fs_module (void) /* Read the file fs.lst for auto-loading. */ void -read_fs_list (void) +read_fs_list (const char *prefix) { - const char *prefix; - - prefix = grub_env_get ("prefix"); if (prefix) { char *filename; diff --git a/normal/cmdline.c b/normal/cmdline.c index bcffffeab..05d665411 100644 --- a/normal/cmdline.c +++ b/normal/cmdline.c @@ -303,8 +303,9 @@ grub_cmdline_get (const char *prompt) grub_memmove (buf + lpos, str, len * sizeof (grub_uint32_t)); llen += len; + cl_set_pos_all (); + cl_print_all (lpos, 0); lpos += len; - cl_print_all (lpos - len, 0); cl_set_pos_all (); } } @@ -384,6 +385,8 @@ grub_cmdline_get (const char *prompt) if (hist_used == 0) grub_history_add (buf, llen); + grub_refresh (); + while ((key = GRUB_TERM_ASCII_CHAR (grub_getkey ())) != '\n' && key != '\r') { switch (key) @@ -419,10 +422,13 @@ grub_cmdline_get (const char *prompt) int restore; char *insertu8; char *bufu8; + grub_uint32_t c; + c = buf[lpos]; buf[lpos] = '\0'; bufu8 = grub_ucs4_to_utf8_alloc (buf, lpos); + buf[lpos] = c; if (!bufu8) { grub_print_error (); @@ -462,13 +468,24 @@ grub_cmdline_get (const char *prompt) insertlen, 0); if (t > 0) { - insert[t] = 0; - cl_insert (insert); + if (insert[t-1] == ' ' && buf[lpos] == ' ') + { + insert[t-1] = 0; + if (t != 1) + cl_insert (insert); + lpos++; + } + else + { + insert[t] = 0; + cl_insert (insert); + } } grub_free (insertu8); grub_free (insert); } + cl_set_pos_all (); } break; @@ -593,6 +610,7 @@ grub_cmdline_get (const char *prompt) } break; } + grub_refresh (); } diff --git a/normal/crypto.c b/normal/crypto.c index 932f26f97..465c9f81d 100644 --- a/normal/crypto.c +++ b/normal/crypto.c @@ -66,14 +66,12 @@ grub_crypto_spec_free (void) /* Read the file crypto.lst for auto-loading. */ void -read_crypto_list (void) +read_crypto_list (const char *prefix) { - const char *prefix; char *filename; grub_file_t file; char *buf = NULL; - prefix = grub_env_get ("prefix"); if (!prefix) { grub_errno = GRUB_ERR_NONE; @@ -88,13 +86,14 @@ read_crypto_list (void) } file = grub_file_open (filename); + grub_free (filename); if (!file) { grub_errno = GRUB_ERR_NONE; return; } - /* Override previous commands.lst. */ + /* Override previous crypto.lst. */ grub_crypto_spec_free (); for (;; grub_free (buf)) diff --git a/normal/dyncmd.c b/normal/dyncmd.c index ca9a82289..a3cafa514 100644 --- a/normal/dyncmd.c +++ b/normal/dyncmd.c @@ -60,11 +60,8 @@ grub_dyncmd_dispatcher (struct grub_command *cmd, /* Read the file command.lst for auto-loading. */ void -read_command_list (void) +read_command_list (const char *prefix) { - const char *prefix; - - prefix = grub_env_get ("prefix"); if (prefix) { char *filename; diff --git a/normal/main.c b/normal/main.c index 9f8c12773..df92e622b 100644 --- a/normal/main.c +++ b/normal/main.c @@ -418,6 +418,7 @@ grub_normal_init_page (struct grub_term_output *term) msg_len = grub_utf8_to_ucs4_alloc (msg_formatted, &unicode_msg, &last_position); + grub_free (msg_formatted); if (msg_len < 0) { @@ -433,14 +434,20 @@ grub_normal_init_page (struct grub_term_output *term) grub_free (unicode_msg); } -static char * -read_lists (struct grub_env_var *var __attribute__ ((unused)), - const char *val) +static void +read_lists (const char *val) { - read_command_list (); - read_fs_list (); - read_crypto_list (); - read_terminal_list (); + read_command_list (val); + read_fs_list (val); + read_crypto_list (val); + read_terminal_list (val); +} + +static char * +read_lists_hook (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + read_lists (val); return val ? grub_strdup (val) : NULL; } @@ -450,10 +457,11 @@ void grub_normal_execute (const char *config, int nested, int batch) { grub_menu_t menu = 0; + const char *prefix = grub_env_get ("prefix"); - read_lists (NULL, NULL); + read_lists (prefix); read_handler_list (); - grub_register_variable_hook ("prefix", NULL, read_lists); + grub_register_variable_hook ("prefix", NULL, read_lists_hook); grub_command_execute ("parser.grub", 0, 0); if (config) @@ -582,10 +590,13 @@ grub_normal_read_line_real (char **line, int cont, int nested) if (cont || nested) { grub_free (*line); + grub_free (prompt); *line = 0; return grub_errno; } } + + grub_free (prompt); return 0; } @@ -650,6 +661,7 @@ GRUB_MOD_INIT(normal) grub_set_history (GRUB_DEFAULT_HISTORY_SIZE); + grub_install_newline_hook (); grub_register_variable_hook ("pager", 0, grub_env_write_pager); /* Register a command "normal" for the rescue mode. */ diff --git a/normal/menu.c b/normal/menu.c index 585c10304..07951dacc 100644 --- a/normal/menu.c +++ b/normal/menu.c @@ -362,6 +362,8 @@ run_menu (grub_menu_t menu, int nested, int *auto_boot) if (timeout > 0) menu_print_timeout (timeout); + else + clear_timeout (); while (1) { @@ -557,14 +559,14 @@ show_menu (grub_menu_t menu, int nested) } else { + int lines_before = grub_normal_get_line_counter (); grub_errno = GRUB_ERR_NONE; grub_menu_execute_entry (e); - if (grub_errno != GRUB_ERR_NONE) - { - grub_print_error (); - grub_errno = GRUB_ERR_NONE; - grub_wait_after_message (); - } + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + + if (lines_before != grub_normal_get_line_counter ()) + grub_wait_after_message (); } } diff --git a/normal/menu_text.c b/normal/menu_text.c index b39f57512..bb0587502 100644 --- a/normal/menu_text.c +++ b/normal/menu_text.c @@ -176,10 +176,17 @@ print_message (int nested, int edit, struct grub_term_output *term) if (edit) { grub_putcode ('\n', term); +#ifdef GRUB_MACHINE_EFI + grub_print_message_indented (_("Minimum Emacs-like screen editing is \ +supported. TAB lists completions. Press F1 to boot, F2=Ctrl-a, F3=Ctrl-e, \ +F4 for a command-line or ESC to discard edits and return to the GRUB menu."), + STANDARD_MARGIN, STANDARD_MARGIN, term); +#else grub_print_message_indented (_("Minimum Emacs-like screen editing is \ supported. TAB lists completions. Press Ctrl-x to boot, Ctrl-c for a \ -command-line or ESC to return menu."), STANDARD_MARGIN, STANDARD_MARGIN, - term); +command-line or ESC to discard edits and return to the GRUB menu."), + STANDARD_MARGIN, STANDARD_MARGIN, term); +#endif } else { diff --git a/normal/term.c b/normal/term.c index 42201fbb3..688141dab 100644 --- a/normal/term.c +++ b/normal/term.c @@ -30,6 +30,14 @@ static unsigned grub_more_lines; /* If the more pager is active. */ static int grub_more; +static int grub_normal_line_counter = 0; + +int +grub_normal_get_line_counter (void) +{ + return grub_normal_line_counter; +} + static void process_newline (void) { @@ -41,6 +49,8 @@ process_newline (void) height = grub_term_height (cur); grub_more_lines++; + grub_normal_line_counter++; + if (grub_more && grub_more_lines >= height - 1) { char key; @@ -76,6 +86,11 @@ grub_set_more (int onoff) grub_more--; grub_more_lines = 0; +} + +void +grub_install_newline_hook (void) +{ grub_newline_hook = process_newline; } @@ -150,17 +165,14 @@ grub_terminal_autoload_free (void) grub_term_output_autoload = NULL; } - /* Read the file terminal.lst for auto-loading. */ void -read_terminal_list (void) +read_terminal_list (const char *prefix) { - const char *prefix; char *filename; grub_file_t file; char *buf = NULL; - prefix = grub_env_get ("prefix"); if (!prefix) { grub_errno = GRUB_ERR_NONE; @@ -175,6 +187,7 @@ read_terminal_list (void) } file = grub_file_open (filename); + grub_free (filename); if (!file) { grub_errno = GRUB_ERR_NONE; diff --git a/partmap/acorn.c b/partmap/acorn.c index 081b6ee94..677ec61d5 100644 --- a/partmap/acorn.c +++ b/partmap/acorn.c @@ -96,17 +96,12 @@ acorn_partition_map_iterate (grub_disk_t disk, const grub_partition_t partition)) { struct grub_partition part; - struct grub_disk raw; struct linux_part map[LINUX_MAP_ENTRIES]; int i; - grub_disk_addr_t sector; + grub_disk_addr_t sector = 0; grub_err_t err; - /* Enforce raw disk access. */ - raw = *disk; - raw.partition = 0; - - err = acorn_partition_map_find (&raw, map, §or); + err = acorn_partition_map_find (disk, map, §or); if (err) return err; @@ -121,7 +116,7 @@ acorn_partition_map_iterate (grub_disk_t disk, part.start = sector + map[i].start; part.len = map[i].size; part.offset = 6; - part.index = i; + part.number = part.index = i; if (hook (disk, &part)) return grub_errno; @@ -130,70 +125,21 @@ acorn_partition_map_iterate (grub_disk_t disk, return GRUB_ERR_NONE; } - -static grub_partition_t -acorn_partition_map_probe (grub_disk_t disk, const char *str) -{ - struct linux_part map[LINUX_MAP_ENTRIES]; - struct grub_disk raw = *disk; - unsigned long partnum = grub_strtoul (str, 0, 10) - 1; - grub_disk_addr_t sector; - grub_err_t err; - grub_partition_t p; - - /* Enforce raw disk access. */ - raw.partition = 0; - - /* Get the partition number. */ - if (partnum > LINUX_MAP_ENTRIES) - goto fail; - - err = acorn_partition_map_find (&raw, map, §or); - if (err) - return 0; - - if (map[partnum].magic != LINUX_NATIVE_MAGIC - && map[partnum].magic != LINUX_SWAP_MAGIC) - goto fail; - - p = grub_malloc (sizeof (struct grub_partition)); - if (! p) - return 0; - - p->start = sector + map[partnum].start; - p->len = map[partnum].size; - p->offset = 6; - p->index = partnum; - return p; - -fail: - grub_error (GRUB_ERR_BAD_FILENAME, "invalid partition"); - return 0; -} - - -static char * -acorn_partition_map_get_name (const grub_partition_t p) -{ - return grub_xasprintf ("%d", p->index + 1); -} /* Partition map type. */ static struct grub_partition_map grub_acorn_partition_map = { - .name = "part_acorn", + .name = "acorn", .iterate = acorn_partition_map_iterate, - .probe = acorn_partition_map_probe, - .get_name = acorn_partition_map_get_name }; -GRUB_MOD_INIT(acorn_partition_map) +GRUB_MOD_INIT(part_acorn) { grub_partition_map_register (&grub_acorn_partition_map); } -GRUB_MOD_FINI(acorn_partition_map) +GRUB_MOD_FINI(part_acorn) { grub_partition_map_unregister (&grub_acorn_partition_map); } diff --git a/partmap/amiga.c b/partmap/amiga.c index f832db354..f21c5b243 100644 --- a/partmap/amiga.c +++ b/partmap/amiga.c @@ -76,20 +76,15 @@ amiga_partition_map_iterate (grub_disk_t disk, { struct grub_partition part; struct grub_amiga_rdsk rdsk; - struct grub_disk raw; int partno = 0; int next = -1; unsigned pos; - /* Enforce raw disk access. */ - raw = *disk; - raw.partition = 0; - /* The RDSK block is one of the first 15 blocks. */ for (pos = 0; pos < 15; pos++) { /* Read the RDSK block which is a descriptor for the entire disk. */ - if (grub_disk_read (&raw, pos, 0, sizeof (rdsk), &rdsk)) + if (grub_disk_read (disk, pos, 0, sizeof (rdsk), &rdsk)) return grub_errno; if (grub_strcmp ((char *) rdsk.magic, "RDSK") == 0) @@ -110,7 +105,7 @@ amiga_partition_map_iterate (grub_disk_t disk, struct grub_amiga_partition apart; /* Read the RDSK block which is a descriptor for the entire disk. */ - if (grub_disk_read (&raw, next, 0, sizeof (apart), &apart)) + if (grub_disk_read (disk, next, 0, sizeof (apart), &apart)) return grub_errno; /* Calculate the first block and the size of the partition. */ @@ -123,7 +118,8 @@ amiga_partition_map_iterate (grub_disk_t disk, * grub_be_to_cpu32 (apart.block_per_track)); part.offset = (grub_off_t) next * 512; - part.index = partno; + part.number = partno; + part.index = 0; part.partmap = &grub_amiga_partition_map; if (hook (disk, &part)) @@ -136,73 +132,20 @@ amiga_partition_map_iterate (grub_disk_t disk, return 0; } - -static grub_partition_t -amiga_partition_map_probe (grub_disk_t disk, const char *str) -{ - grub_partition_t p = 0; - int partnum = 0; - char *s = (char *) str; - - auto int find_func (grub_disk_t d, const grub_partition_t partition); - - int find_func (grub_disk_t d __attribute__ ((unused)), - const grub_partition_t partition) - { - if (partnum == partition->index) - { - p = (grub_partition_t) grub_malloc (sizeof (*p)); - if (! p) - return 1; - - grub_memcpy (p, partition, sizeof (*p)); - return 1; - } - - return 0; - } - - /* Get the partition number. */ - partnum = grub_strtoul (s, 0, 10) - 1; - if (grub_errno) - { - grub_error (GRUB_ERR_BAD_FILENAME, "invalid partition"); - return 0; - } - - if (amiga_partition_map_iterate (disk, find_func)) - goto fail; - - return p; - - fail: - grub_free (p); - return 0; -} - - -static char * -amiga_partition_map_get_name (const grub_partition_t p) -{ - return grub_xasprintf ("%d", p->index + 1); -} - /* Partition map type. */ static struct grub_partition_map grub_amiga_partition_map = { - .name = "part_amiga", + .name = "amiga", .iterate = amiga_partition_map_iterate, - .probe = amiga_partition_map_probe, - .get_name = amiga_partition_map_get_name }; -GRUB_MOD_INIT(amiga_partition_map) +GRUB_MOD_INIT(part_amiga) { grub_partition_map_register (&grub_amiga_partition_map); } -GRUB_MOD_FINI(amiga_partition_map) +GRUB_MOD_FINI(part_amiga) { grub_partition_map_unregister (&grub_amiga_partition_map); } diff --git a/partmap/apple.c b/partmap/apple.c index a1a645acf..e162d18d7 100644 --- a/partmap/apple.c +++ b/partmap/apple.c @@ -105,17 +105,12 @@ apple_partition_map_iterate (grub_disk_t disk, struct grub_partition part; struct grub_apple_header aheader; struct grub_apple_part apart; - struct grub_disk raw; int partno = 0, partnum = 0; unsigned pos; - /* Enforce raw disk access. */ - raw = *disk; - raw.partition = 0; - part.partmap = &grub_apple_partition_map; - if (grub_disk_read (&raw, 0, 0, sizeof (aheader), &aheader)) + if (grub_disk_read (disk, 0, 0, sizeof (aheader), &aheader)) return grub_errno; if (grub_be_to_cpu16 (aheader.magic) != GRUB_APPLE_HEADER_MAGIC) @@ -131,8 +126,10 @@ apple_partition_map_iterate (grub_disk_t disk, do { - if (grub_disk_read (&raw, pos / GRUB_DISK_SECTOR_SIZE, - pos % GRUB_DISK_SECTOR_SIZE, + part.offset = pos / GRUB_DISK_SECTOR_SIZE; + part.index = pos % GRUB_DISK_SECTOR_SIZE; + + if (grub_disk_read (disk, part.offset, part.index, sizeof (struct grub_apple_part), &apart)) return grub_errno; @@ -156,6 +153,7 @@ apple_partition_map_iterate (grub_disk_t disk, / GRUB_DISK_SECTOR_SIZE; part.offset = pos; part.index = partno; + part.number = partno; grub_dprintf ("partition", "partition %d: name %s, type %s, start 0x%x, len 0x%x\n", @@ -179,73 +177,20 @@ apple_partition_map_iterate (grub_disk_t disk, "Apple partition map not found"); } - -static grub_partition_t -apple_partition_map_probe (grub_disk_t disk, const char *str) -{ - grub_partition_t p = 0; - int partnum = 0; - char *s = (char *) str; - - auto int find_func (grub_disk_t d, const grub_partition_t partition); - - int find_func (grub_disk_t d __attribute__ ((unused)), - const grub_partition_t partition) - { - if (partnum == partition->index) - { - p = (grub_partition_t) grub_malloc (sizeof (*p)); - if (! p) - return 1; - - grub_memcpy (p, partition, sizeof (*p)); - return 1; - } - - return 0; - } - - /* Get the partition number. */ - partnum = grub_strtoul (s, 0, 10) - 1; - if (grub_errno) - { - grub_error (GRUB_ERR_BAD_FILENAME, "invalid partition"); - return 0; - } - - if (apple_partition_map_iterate (disk, find_func)) - goto fail; - - return p; - - fail: - grub_free (p); - return 0; -} - - -static char * -apple_partition_map_get_name (const grub_partition_t p) -{ - return grub_xasprintf ("%d", p->index + 1); -} - /* Partition map type. */ static struct grub_partition_map grub_apple_partition_map = { - .name = "part_apple", + .name = "apple", .iterate = apple_partition_map_iterate, - .probe = apple_partition_map_probe, - .get_name = apple_partition_map_get_name }; -GRUB_MOD_INIT(apple_partition_map) +GRUB_MOD_INIT(part_apple) { grub_partition_map_register (&grub_apple_partition_map); } -GRUB_MOD_FINI(apple_partition_map) +GRUB_MOD_FINI(part_apple) { grub_partition_map_unregister (&grub_apple_partition_map); } diff --git a/partmap/bsdlabel.c b/partmap/bsdlabel.c new file mode 100644 index 000000000..d28f36d07 --- /dev/null +++ b/partmap/bsdlabel.c @@ -0,0 +1,97 @@ +/* bsdlabel.c - Read BSD style partition tables. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +static struct grub_partition_map grub_bsdlabel_partition_map; + + +static grub_err_t +bsdlabel_partition_map_iterate (grub_disk_t disk, + int (*hook) (grub_disk_t disk, + const grub_partition_t partition)) +{ + struct grub_partition_bsd_disk_label label; + struct grub_partition p; + grub_disk_addr_t delta = 0; + unsigned pos; + + /* BSDLabel offsets are absolute even when it's embed inside partition. */ + delta = grub_partition_get_start (disk->partition); + + /* Read the BSD label. */ + if (grub_disk_read (disk, GRUB_PC_PARTITION_BSD_LABEL_SECTOR, + 0, sizeof (label), &label)) + return grub_errno; + + /* Check if it is valid. */ + if (label.magic != grub_cpu_to_le32 (GRUB_PC_PARTITION_BSD_LABEL_MAGIC)) + return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature"); + + pos = sizeof (label) + GRUB_PC_PARTITION_BSD_LABEL_SECTOR + * GRUB_DISK_SECTOR_SIZE; + + for (p.number = 0; + p.number < grub_cpu_to_le16 (label.num_partitions); + p.number++) + { + struct grub_partition_bsd_entry be; + + p.offset = pos / GRUB_DISK_SECTOR_SIZE; + p.index = pos % GRUB_DISK_SECTOR_SIZE; + + if (grub_disk_read (disk, p.offset, p.index, sizeof (be), &be)) + return grub_errno; + + p.start = grub_le_to_cpu32 (be.offset) - delta; + p.len = grub_le_to_cpu32 (be.size); + p.partmap = &grub_bsdlabel_partition_map; + + if (be.fs_type != GRUB_PC_PARTITION_BSD_TYPE_UNUSED) + if (hook (disk, &p)) + return grub_errno; + + pos += sizeof (struct grub_partition_bsd_entry); + } + + return GRUB_ERR_NONE; +} + + +/* Partition map type. */ +static struct grub_partition_map grub_bsdlabel_partition_map = + { + .name = "bsd", + .iterate = bsdlabel_partition_map_iterate, + }; + +GRUB_MOD_INIT(part_bsd) +{ + grub_partition_map_register (&grub_bsdlabel_partition_map); +} + +GRUB_MOD_FINI(part_bsd) +{ + grub_partition_map_unregister (&grub_bsdlabel_partition_map); +} diff --git a/partmap/gpt.c b/partmap/gpt.c index cb1229bee..9dd88bec1 100644 --- a/partmap/gpt.c +++ b/partmap/gpt.c @@ -44,18 +44,13 @@ gpt_partition_map_iterate (grub_disk_t disk, struct grub_partition part; struct grub_gpt_header gpt; struct grub_gpt_partentry entry; - struct grub_disk raw; struct grub_msdos_partition_mbr mbr; grub_uint64_t entries; unsigned int i; int last_offset = 0; - /* Enforce raw disk access. */ - raw = *disk; - raw.partition = 0; - /* Read the protective MBR. */ - if (grub_disk_read (&raw, 0, 0, sizeof (mbr), &mbr)) + if (grub_disk_read (disk, 0, 0, sizeof (mbr), &mbr)) return grub_errno; /* Check if it is valid. */ @@ -67,7 +62,7 @@ gpt_partition_map_iterate (grub_disk_t disk, return grub_error (GRUB_ERR_BAD_PART_TABLE, "no GPT partition map found"); /* Read the GPT header. */ - if (grub_disk_read (&raw, 1, 0, sizeof (gpt), &gpt)) + if (grub_disk_read (disk, 1, 0, sizeof (gpt), &gpt)) return grub_errno; if (grub_memcmp (gpt.magic, grub_gpt_magic, sizeof (grub_gpt_magic))) @@ -78,7 +73,7 @@ gpt_partition_map_iterate (grub_disk_t disk, entries = grub_le_to_cpu64 (gpt.partitions); for (i = 0; i < grub_le_to_cpu32 (gpt.maxpart); i++) { - if (grub_disk_read (&raw, entries, last_offset, + if (grub_disk_read (disk, entries, last_offset, sizeof (entry), &entry)) return grub_errno; @@ -90,16 +85,16 @@ gpt_partition_map_iterate (grub_disk_t disk, part.len = (grub_le_to_cpu64 (entry.end) - grub_le_to_cpu64 (entry.start) + 1); part.offset = entries; - part.index = i; + part.number = i; + part.index = last_offset; part.partmap = &grub_gpt_partition_map; - part.data = &entry; grub_dprintf ("gpt", "GPT entry %d: start=%lld, length=%lld\n", i, (unsigned long long) part.start, (unsigned long long) part.len); if (hook (disk, &part)) - return 1; + return grub_errno; } last_offset += grub_le_to_cpu32 (gpt.partentry_size); @@ -110,77 +105,23 @@ gpt_partition_map_iterate (grub_disk_t disk, } } - return 0; -} - - -static grub_partition_t -gpt_partition_map_probe (grub_disk_t disk, const char *str) -{ - grub_partition_t p = 0; - int partnum = 0; - char *s = (char *) str; - - auto int find_func (grub_disk_t d, const grub_partition_t partition); - - int find_func (grub_disk_t d __attribute__ ((unused)), - const grub_partition_t partition) - { - if (partnum == partition->index) - { - p = (grub_partition_t) grub_malloc (sizeof (*p)); - if (! p) - return 1; - - grub_memcpy (p, partition, sizeof (*p)); - return 1; - } - - return 0; - } - - /* Get the partition number. */ - partnum = grub_strtoul (s, 0, 10) - 1; - if (grub_errno) - { - grub_error (GRUB_ERR_BAD_FILENAME, "invalid partition"); - return 0; - } - - gpt_partition_map_iterate (disk, find_func); - if (grub_errno) - goto fail; - - return p; - - fail: - grub_free (p); - return 0; -} - - -static char * -gpt_partition_map_get_name (const grub_partition_t p) -{ - return grub_xasprintf ("%d", p->index + 1); + return GRUB_ERR_NONE; } /* Partition map type. */ static struct grub_partition_map grub_gpt_partition_map = { - .name = "part_gpt", + .name = "gpt", .iterate = gpt_partition_map_iterate, - .probe = gpt_partition_map_probe, - .get_name = gpt_partition_map_get_name }; -GRUB_MOD_INIT(gpt_partition_map) +GRUB_MOD_INIT(part_gpt) { grub_partition_map_register (&grub_gpt_partition_map); } -GRUB_MOD_FINI(gpt_partition_map) +GRUB_MOD_FINI(part_gpt) { grub_partition_map_unregister (&grub_gpt_partition_map); } diff --git a/partmap/msdos.c b/partmap/msdos.c index 1c3861cc7..3898d09fa 100644 --- a/partmap/msdos.c +++ b/partmap/msdos.c @@ -27,87 +27,20 @@ static struct grub_partition_map grub_msdos_partition_map; -/* Parse the partition representation in STR and return a partition. */ -static grub_partition_t -grub_partition_parse (const char *str) -{ - grub_partition_t p; - struct grub_msdos_partition *pcdata; - - char *s = (char *) str; - - p = (grub_partition_t) grub_malloc (sizeof (*p)); - if (! p) - return 0; - - pcdata = (struct grub_msdos_partition *) grub_malloc (sizeof (*pcdata)); - if (! pcdata) - goto fail; - - p->data = pcdata; - p->partmap = &grub_msdos_partition_map; - - /* Initialize some of the fields with invalid values. */ - pcdata->bsd_part = pcdata->dos_type = pcdata->bsd_type = p->index = -1; - - /* Get the DOS partition number. The number is counted from one for - the user interface, and from zero internally. */ - pcdata->dos_part = grub_strtoul (s, &s, 0) - 1; - - if (grub_errno) - { - /* Not found. Maybe only a BSD label is specified. */ - pcdata->dos_part = -1; - grub_errno = GRUB_ERR_NONE; - } - else if (*s == ',') - s++; - - if (*s) - { - if (*s >= 'a' && *s <= 'h') - { - pcdata->bsd_part = *s - 'a'; - s++; - } - - if (*s) - goto fail; - } - - if (pcdata->dos_part == -1 && pcdata->bsd_part == -1) - goto fail; - - return p; - - fail: - grub_free (p); - grub_free (pcdata); - grub_error (GRUB_ERR_BAD_FILENAME, "invalid partition"); - return 0; -} - static grub_err_t pc_partition_map_iterate (grub_disk_t disk, int (*hook) (grub_disk_t disk, const grub_partition_t partition)) { struct grub_partition p; - struct grub_msdos_partition pcdata; struct grub_msdos_partition_mbr mbr; - struct grub_msdos_partition_disk_label label; - struct grub_disk raw; int labeln = 0; grub_disk_addr_t lastaddr; - - /* Enforce raw disk access. */ - raw = *disk; - raw.partition = 0; + grub_disk_addr_t ext_offset; p.offset = 0; - pcdata.ext_offset = 0; - pcdata.dos_part = -1; - p.data = &pcdata; + ext_offset = 0; + p.number = -1; p.partmap = &grub_msdos_partition_map; /* Any value different than `p.offset' will satisfy the check during @@ -120,7 +53,7 @@ pc_partition_map_iterate (grub_disk_t disk, struct grub_msdos_partition_entry *e; /* Read the MBR. */ - if (grub_disk_read (&raw, p.offset, 0, sizeof (mbr), &mbr)) + if (grub_disk_read (disk, p.offset, 0, sizeof (mbr), &mbr)) goto finish; /* This is our loop-detection algorithm. It works the following way: @@ -150,13 +83,10 @@ pc_partition_map_iterate (grub_disk_t disk, p.start = p.offset + grub_le_to_cpu32 (e->start); p.len = grub_le_to_cpu32 (e->length); - pcdata.bsd_part = -1; - pcdata.dos_type = e->type; - pcdata.bsd_type = -1; grub_dprintf ("partition", "partition %d: flag 0x%x, type 0x%x, start 0x%llx, len 0x%llx\n", - p.index, e->flag, pcdata.dos_type, + p.index, e->flag, e->type, (unsigned long long) p.start, (unsigned long long) p.len); @@ -168,59 +98,15 @@ pc_partition_map_iterate (grub_disk_t disk, if (! grub_msdos_partition_is_empty (e->type) && ! grub_msdos_partition_is_extended (e->type)) { - pcdata.dos_part++; + p.number++; if (hook (disk, &p)) - return 1; - - /* Check if this is a BSD partition. */ - if (grub_msdos_partition_is_bsd (e->type)) - { - /* Check if the BSD label is within the DOS partition. */ - if (p.len <= GRUB_PC_PARTITION_BSD_LABEL_SECTOR) - { - grub_dprintf ("partition", "no space for disk label\n"); - continue; - } - /* Read the BSD label. */ - if (grub_disk_read (&raw, - (p.start - + GRUB_PC_PARTITION_BSD_LABEL_SECTOR), - 0, - sizeof (label), - &label)) - goto finish; - - /* Check if it is valid. */ - if (label.magic - != grub_cpu_to_le32 (GRUB_PC_PARTITION_BSD_LABEL_MAGIC)) - { - grub_dprintf ("partition", - "invalid disk label magic 0x%x on partition %d\n", - label.magic, p.index); - continue; - } - for (pcdata.bsd_part = 0; - pcdata.bsd_part < grub_cpu_to_le16 (label.num_partitions); - pcdata.bsd_part++) - { - struct grub_msdos_partition_bsd_entry *be - = label.entries + pcdata.bsd_part; - - p.start = grub_le_to_cpu32 (be->offset); - p.len = grub_le_to_cpu32 (be->size); - pcdata.bsd_type = be->fs_type; - - if (be->fs_type != GRUB_PC_PARTITION_BSD_TYPE_UNUSED) - if (hook (disk, &p)) - return 1; - } - } + return grub_errno; } - else if (pcdata.dos_part < 4) + else if (p.number < 4) /* If this partition is a logical one, shouldn't increase the partition number. */ - pcdata.dos_part++; + p.number++; } /* Find an extended partition. */ @@ -230,9 +116,9 @@ pc_partition_map_iterate (grub_disk_t disk, if (grub_msdos_partition_is_extended (e->type)) { - p.offset = pcdata.ext_offset + grub_le_to_cpu32 (e->start); - if (! pcdata.ext_offset) - pcdata.ext_offset = p.offset; + p.offset = ext_offset + grub_le_to_cpu32 (e->start); + if (! ext_offset) + ext_offset = p.offset; break; } @@ -247,86 +133,20 @@ pc_partition_map_iterate (grub_disk_t disk, return grub_errno; } - -static grub_partition_t -pc_partition_map_probe (grub_disk_t disk, const char *str) -{ - grub_partition_t p; - struct grub_msdos_partition *pcdata; - - auto int find_func (grub_disk_t d, const grub_partition_t partition); - - int find_func (grub_disk_t d __attribute__ ((unused)), - const grub_partition_t partition) - { - struct grub_msdos_partition *partdata = partition->data; - - if ((pcdata->dos_part == partdata->dos_part || pcdata->dos_part == -1) - && pcdata->bsd_part == partdata->bsd_part) - { - grub_memcpy (p, partition, sizeof (*p)); - p->data = pcdata; - grub_memcpy (pcdata, partdata, sizeof (*pcdata)); - return 1; - } - - return 0; - } - - p = grub_partition_parse (str); - if (! p) - return 0; - - pcdata = p->data; - pc_partition_map_iterate (disk, find_func); - if (grub_errno) - goto fail; - - if (p->index < 0) - { - grub_error (GRUB_ERR_BAD_DEVICE, "no such partition"); - goto fail; - } - - return p; - - fail: - grub_free (p); - grub_free (pcdata); - return 0; -} - - -static char * -pc_partition_map_get_name (const grub_partition_t p) -{ - struct grub_msdos_partition *pcdata = p->data; - - if (pcdata->bsd_part < 0) - return grub_xasprintf ("%d", pcdata->dos_part + 1); - else if (pcdata->dos_part < 0) - return grub_xasprintf ("%c", pcdata->bsd_part + 'a'); - else - return grub_xasprintf ("%d,%c", pcdata->dos_part + 1, - pcdata->bsd_part + 'a'); -} - /* Partition map type. */ static struct grub_partition_map grub_msdos_partition_map = { - .name = "part_msdos", + .name = "msdos", .iterate = pc_partition_map_iterate, - .probe = pc_partition_map_probe, - .get_name = pc_partition_map_get_name }; -GRUB_MOD_INIT(pc_partition_map) +GRUB_MOD_INIT(part_msdos) { grub_partition_map_register (&grub_msdos_partition_map); } -GRUB_MOD_FINI(pc_partition_map) +GRUB_MOD_FINI(part_msdos) { grub_partition_map_unregister (&grub_msdos_partition_map); } diff --git a/partmap/sun.c b/partmap/sun.c index 42cf0d598..7a7eaef27 100644 --- a/partmap/sun.c +++ b/partmap/sun.c @@ -88,48 +88,55 @@ sun_partition_map_iterate (grub_disk_t disk, const grub_partition_t partition)) { grub_partition_t p; - struct grub_disk raw; struct grub_sun_block block; int partnum; - - raw = *disk; - raw.partition = 0; + grub_err_t err; p = (grub_partition_t) grub_zalloc (sizeof (struct grub_partition)); if (! p) return grub_errno; p->partmap = &grub_sun_partition_map; - if (grub_disk_read (&raw, 0, 0, sizeof (struct grub_sun_block), - &block) == GRUB_ERR_NONE) + err = grub_disk_read (disk, 0, 0, sizeof (struct grub_sun_block), + &block); + if (err) { - if (GRUB_PARTMAP_SUN_MAGIC != grub_be_to_cpu16 (block.magic)) - grub_error (GRUB_ERR_BAD_PART_TABLE, "not a sun partition table"); + grub_free (p); + return err; + } - if (! grub_sun_is_valid (&block)) - grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid checksum"); + if (GRUB_PARTMAP_SUN_MAGIC != grub_be_to_cpu16 (block.magic)) + { + grub_free (p); + return grub_error (GRUB_ERR_BAD_PART_TABLE, "not a sun partition table"); + } - /* Maybe another error value would be better, because partition - table _is_ recognized but invalid. */ - for (partnum = 0; partnum < GRUB_PARTMAP_SUN_MAX_PARTS; partnum++) + if (! grub_sun_is_valid (&block)) + { + grub_free (p); + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid checksum"); + } + + /* Maybe another error value would be better, because partition + table _is_ recognized but invalid. */ + for (partnum = 0; partnum < GRUB_PARTMAP_SUN_MAX_PARTS; partnum++) + { + struct grub_sun_partition_descriptor *desc; + + if (block.infos[partnum].id == 0 + || block.infos[partnum].id == GRUB_PARTMAP_SUN_WHOLE_DISK_ID) + continue; + + desc = &block.partitions[partnum]; + p->start = ((grub_uint64_t) grub_be_to_cpu32 (desc->start_cylinder) + * grub_be_to_cpu16 (block.ntrks) + * grub_be_to_cpu16 (block.nsect)); + p->len = grub_be_to_cpu32 (desc->num_sectors); + p->number = p->index = partnum; + if (p->len) { - struct grub_sun_partition_descriptor *desc; - - if (block.infos[partnum].id == 0 - || block.infos[partnum].id == GRUB_PARTMAP_SUN_WHOLE_DISK_ID) - continue; - - desc = &block.partitions[partnum]; - p->start = ((grub_uint64_t) grub_be_to_cpu32 (desc->start_cylinder) - * grub_be_to_cpu16 (block.ntrks) - * grub_be_to_cpu16 (block.nsect)); - p->len = grub_be_to_cpu32 (desc->num_sectors); - p->index = partnum; - if (p->len) - { - if (hook (disk, p)) - partnum = GRUB_PARTMAP_SUN_MAX_PARTS; - } + if (hook (disk, p)) + partnum = GRUB_PARTMAP_SUN_MAX_PARTS; } } @@ -138,70 +145,19 @@ sun_partition_map_iterate (grub_disk_t disk, return grub_errno; } -static grub_partition_t -sun_partition_map_probe (grub_disk_t disk, const char *str) -{ - grub_partition_t p = 0; - int partnum = 0; - char *s = (char *) str; - - auto int find_func (grub_disk_t d, const grub_partition_t partition); - - int find_func (grub_disk_t d __attribute__ ((unused)), - const grub_partition_t partition) - { - if (partnum == partition->index) - { - p = (grub_partition_t) grub_malloc (sizeof (*p)); - if (p) - grub_memcpy (p, partition, sizeof (*p)); - - return 1; - } - - return 0; - } - - grub_errno = GRUB_ERR_NONE; - partnum = grub_strtoul (s, 0, 10) - 1; - if (grub_errno == GRUB_ERR_NONE) - { - if (sun_partition_map_iterate (disk, find_func)) - { - grub_free (p); - p = 0; - } - } - else - { - grub_error (GRUB_ERR_BAD_FILENAME, "invalid partition"); - p = 0; - } - - return p; -} - -static char * -sun_partition_map_get_name (const grub_partition_t p) -{ - return grub_xasprintf ("%d", p->index + 1); -} - /* Partition map type. */ static struct grub_partition_map grub_sun_partition_map = { - .name = "part_sun", + .name = "sun", .iterate = sun_partition_map_iterate, - .probe = sun_partition_map_probe, - .get_name = sun_partition_map_get_name }; -GRUB_MOD_INIT(sun_partition_map) +GRUB_MOD_INIT(part_sun) { grub_partition_map_register (&grub_sun_partition_map); } -GRUB_MOD_FINI(sun_partition_map) +GRUB_MOD_FINI(part_sun) { grub_partition_map_unregister (&grub_sun_partition_map); } diff --git a/partmap/sunpc.c b/partmap/sunpc.c new file mode 100644 index 000000000..ea69c28b9 --- /dev/null +++ b/partmap/sunpc.c @@ -0,0 +1,144 @@ +/* sunpc.c - Read SUN PC style partition tables. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2005,2006,2007,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define GRUB_PARTMAP_SUN_PC_MAGIC 0xDABE +#define GRUB_PARTMAP_SUN_PC_MAX_PARTS 16 +#define GRUB_PARTMAP_SUN_PC_WHOLE_DISK_ID 0x05 + +struct grub_sun_pc_partition_descriptor +{ + grub_uint16_t id; + grub_uint16_t unused; + grub_uint32_t start_sector; + grub_uint32_t num_sectors; +} __attribute__ ((packed)); + +struct grub_sun_pc_block +{ + grub_uint8_t unused[72]; + struct grub_sun_pc_partition_descriptor partitions[GRUB_PARTMAP_SUN_PC_MAX_PARTS]; + grub_uint8_t unused2[244]; + grub_uint16_t magic; /* Magic number. */ + grub_uint16_t csum; /* Label xor'd checksum. */ +} __attribute__ ((packed)); + +static struct grub_partition_map grub_sun_pc_partition_map; + +/* Verify checksum (true=ok). */ +static int +grub_sun_is_valid (struct grub_sun_pc_block *label) +{ + grub_uint16_t *pos; + grub_uint16_t sum = 0; + + for (pos = (grub_uint16_t *) label; + pos < (grub_uint16_t *) (label + 1); + pos++) + sum ^= *pos; + + return ! sum; +} + +static grub_err_t +sun_pc_partition_map_iterate (grub_disk_t disk, + int (*hook) (grub_disk_t disk, + const grub_partition_t partition)) +{ + grub_partition_t p; + struct grub_sun_pc_block block; + int partnum; + grub_err_t err; + + p = (grub_partition_t) grub_zalloc (sizeof (struct grub_partition)); + if (! p) + return grub_errno; + + p->partmap = &grub_sun_pc_partition_map; + err = grub_disk_read (disk, 1, 0, sizeof (struct grub_sun_pc_block), &block); + if (err) + { + grub_free (p); + return err; + } + + if (GRUB_PARTMAP_SUN_PC_MAGIC != grub_le_to_cpu16 (block.magic)) + { + grub_free (p); + return grub_error (GRUB_ERR_BAD_PART_TABLE, + "not a sun_pc partition table"); + } + + if (! grub_sun_is_valid (&block)) + { + grub_free (p); + return grub_error (GRUB_ERR_BAD_PART_TABLE, "invalid checksum"); + } + + /* Maybe another error value would be better, because partition + table _is_ recognized but invalid. */ + for (partnum = 0; partnum < GRUB_PARTMAP_SUN_PC_MAX_PARTS; partnum++) + { + struct grub_sun_pc_partition_descriptor *desc; + + if (block.partitions[partnum].id == 0 + || block.partitions[partnum].id == GRUB_PARTMAP_SUN_PC_WHOLE_DISK_ID) + continue; + + desc = &block.partitions[partnum]; + p->start = grub_le_to_cpu32 (desc->start_sector); + p->len = grub_le_to_cpu32 (desc->num_sectors); + p->number = partnum; + if (p->len) + { + if (hook (disk, p)) + partnum = GRUB_PARTMAP_SUN_PC_MAX_PARTS; + } + } + + grub_free (p); + + return grub_errno; +} + +/* Partition map type. */ +static struct grub_partition_map grub_sun_pc_partition_map = + { + .name = "sunpc", + .iterate = sun_pc_partition_map_iterate, + }; + +GRUB_MOD_INIT(part_sunpc) +{ + grub_partition_map_register (&grub_sun_pc_partition_map); +} + +GRUB_MOD_FINI(part_sunpc) +{ + grub_partition_map_unregister (&grub_sun_pc_partition_map); +} + diff --git a/parttool/msdospart.c b/parttool/msdospart.c index dbb25bc52..006a87def 100644 --- a/parttool/msdospart.c +++ b/parttool/msdospart.c @@ -49,7 +49,7 @@ static grub_err_t grub_pcpart_boot (const grub_device_t dev, index = dev->disk->partition->index; part = dev->disk->partition; - dev->disk->partition = 0; + dev->disk->partition = part->parent; /* Read the MBR. */ if (grub_disk_read (dev->disk, 0, 0, sizeof (mbr), &mbr)) @@ -96,7 +96,7 @@ static grub_err_t grub_pcpart_type (const grub_device_t dev, index = dev->disk->partition->index; part = dev->disk->partition; - dev->disk->partition = 0; + dev->disk->partition = part->parent; /* Read the parttable. */ if (grub_disk_read (dev->disk, part->offset, 0, @@ -138,17 +138,17 @@ static grub_err_t grub_pcpart_type (const grub_device_t dev, return GRUB_ERR_NONE; } -GRUB_MOD_INIT (pcpart) +GRUB_MOD_INIT (msdospart) { - activate_table_handle = grub_parttool_register ("part_msdos", + activate_table_handle = grub_parttool_register ("msdos", grub_pcpart_boot, grub_pcpart_bootargs); - type_table_handle = grub_parttool_register ("part_msdos", + type_table_handle = grub_parttool_register ("msdos", grub_pcpart_type, grub_pcpart_typeargs); } -GRUB_MOD_FINI(pcpart) +GRUB_MOD_FINI(msdospart) { grub_parttool_unregister (activate_table_handle); grub_parttool_unregister (type_table_handle); diff --git a/po/POTFILES b/po/POTFILES index 04a3516fa..0dfc3c041 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -62,7 +62,7 @@ loader/i386/linux.c loader/i386/pc/chainloader.c loader/i386/pc/linux.c loader/i386/xnu.c -loader/multiboot_loader.c +loader/multiboot.c loader/powerpc/ieee1275/linux.c loader/sparc64/ieee1275/linux.c loader/xnu.c @@ -79,12 +79,3 @@ term/serial.c util/grub-mkrawimage.c util/i386/pc/grub-setup.c - -util/mkisofs/eltorito.c -util/mkisofs/joliet.c -util/mkisofs/mkisofs.c -util/mkisofs/mkisofs.h -util/mkisofs/multi.c -util/mkisofs/rock.c -util/mkisofs/tree.c -util/mkisofs/write.c diff --git a/po/POTFILES-shell b/po/POTFILES-shell index cb28d331b..90d2b978c 100644 --- a/po/POTFILES-shell +++ b/po/POTFILES-shell @@ -2,3 +2,4 @@ # Shell language are included here. util/grub.d/10_kfreebsd.in util/grub.d/10_linux.in +util/grub.d/10_netbsd.in diff --git a/script/execute.c b/script/execute.c index ee7e099bc..40f161267 100644 --- a/script/execute.c +++ b/script/execute.c @@ -1,7 +1,7 @@ /* execute.c -- Execute a GRUB script. */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2007,2008,2009 Free Software Foundation, Inc. + * Copyright (C) 2005,2007,2008,2009,2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,58 +26,169 @@ #include #include +/* Max digits for a char is 3 (0xFF is 255), similarly for an int it + is sizeof (int) * 3, and one extra for a possible -ve sign. */ +#define ERRNO_DIGITS_MAX (sizeof (int) * 3 + 1) + static grub_err_t grub_script_execute_cmd (struct grub_script_cmd *cmd) { + int ret; + char errnobuf[ERRNO_DIGITS_MAX + 1]; + if (cmd == 0) return 0; - return cmd->exec (cmd); + ret = cmd->exec (cmd); + + grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret); + grub_env_set ("?", errnobuf); + return ret; } -/* Parse ARG and return the textual representation. Add strings are - concatenated and all values of the variables are filled in. */ -char * -grub_script_execute_argument_to_string (struct grub_script_arg *arg) +#define ARG_ALLOCATION_UNIT (32 * sizeof (char)) +#define ARGV_ALLOCATION_UNIT (8 * sizeof (void*)) + +/* Expand arguments in ARGLIST into multiple arguments. */ +char ** +grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist, int *count) { - int size = 0; - char *val; - char *chararg; - struct grub_script_arg *argi; + int i; + int oom; + int argc; + int empty; + char *ptr; + char **argv; + char *value; + struct grub_script_arg *arg; - /* First determine the size of the argument. */ - for (argi = arg; argi; argi = argi->next) + auto void push (char *str); + void push (char *str) + { + char **p; + + if (oom) + return; + + p = grub_realloc (argv, ALIGN_UP (sizeof(char*) * (argc + 1), ARGV_ALLOCATION_UNIT)); + if (!p) + oom = 1; + else + { + p[argc++] = str; + argv = p; + } + } + + auto char* append (const char *str, grub_size_t nchar); + char* append (const char *str, grub_size_t nchar) + { + int len; + int old; + char *p; + + if (oom || !str) + return 0; + + len = nchar ?: grub_strlen (str); + old = argv[argc - 1] ? grub_strlen (argv[argc - 1]) : 0; + p = grub_realloc (argv[argc - 1], ALIGN_UP(old + len + 1, ARG_ALLOCATION_UNIT)); + + if (p) + { + grub_strncpy (p + old, str, len); + p[old + len] = '\0'; + } + else + { + oom = 1; + grub_free (argv[argc - 1]); + } + argv[argc - 1] = p; + return argv[argc - 1]; + } + + /* Move *STR to the begining of next word, but return current word. */ + auto char* move_to_next (char **str); + char* move_to_next (char **str) + { + char *end; + char *start; + + if (oom || !str || !*str) + return 0; + + start = *str; + while (*start && grub_isspace (*start)) start++; + if (*start == '\0') + return 0; + + end = start + 1; + while (*end && !grub_isspace (*end)) end++; + + *str = end; + return start; + } + + oom = 0; + argv = 0; + argc = 0; + push (0); + for (; arglist; arglist = arglist->next) { - if (argi->type == 1) + empty = 1; + arg = arglist->arg; + while (arg) { - val = grub_env_get (argi->str); - if (val) - size += grub_strlen (val); + switch (arg->type) + { + case GRUB_SCRIPT_ARG_TYPE_VAR: + value = grub_env_get (arg->str); + while (value && *value && (ptr = move_to_next(&value))) + { + empty = 0; + append (ptr, value - ptr); + if (*value) push(0); + } + break; + + case GRUB_SCRIPT_ARG_TYPE_TEXT: + if (grub_strlen (arg->str) > 0) + { + empty = 0; + append (arg->str, 0); + } + break; + + case GRUB_SCRIPT_ARG_TYPE_DQSTR: + case GRUB_SCRIPT_ARG_TYPE_SQSTR: + empty = 0; + append (arg->str, 0); + break; + + case GRUB_SCRIPT_ARG_TYPE_DQVAR: + empty = 0; + append (grub_env_get (arg->str), 0); + break; + } + arg = arg->next; } - else - size += grub_strlen (argi->str); + if (!empty) + push (0); } - /* Create the argument. */ - chararg = grub_malloc (size + 1); - if (! chararg) - return 0; - - *chararg = '\0'; - /* First determine the size of the argument. */ - for (argi = arg; argi; argi = argi->next) + if (oom) { - if (argi->type == 1) - { - val = grub_env_get (argi->str); - if (val) - grub_strcat (chararg, val); - } - else - grub_strcat (chararg, argi->str); + for (i = 0; i < argc; i++) + grub_free (argv[i]); + grub_free (argv); + argv = 0; } - return chararg; + if (argv) + *count = argc - 1; + + return argv; } /* Execute a single command line. */ @@ -85,7 +196,6 @@ grub_err_t grub_script_execute_cmdline (struct grub_script_cmd *cmd) { struct grub_script_cmdline *cmdline = (struct grub_script_cmdline *) cmd; - struct grub_script_arglist *arglist; char **args = 0; int i = 0; grub_command_t grubcmd; @@ -96,11 +206,14 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd) char *cmdname; /* Lookup the command. */ - cmdname = grub_script_execute_argument_to_string (cmdline->arglist->arg); + args = grub_script_execute_arglist_to_argv (cmdline->arglist, &argcount); + if (!args) + return grub_errno; + + cmdname = args[0]; grubcmd = grub_command_find (cmdname); if (! grubcmd) { - /* Ignore errors. */ grub_errno = GRUB_ERR_NONE; /* It's not a GRUB command, try all functions. */ @@ -126,36 +239,28 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd) grub_snprintf (errnobuf, sizeof (errnobuf), "%d", grub_errno); grub_env_set ("?", errnobuf); + grub_print_error (); + return 0; } } - grub_free (cmdname); - - if (cmdline->arglist->next) - { - argcount = cmdline->arglist->argcount - 1; - - /* Create argv from the arguments. */ - args = grub_malloc (sizeof (char *) * argcount); - for (arglist = cmdline->arglist->next; arglist; arglist = arglist->next) - { - char *str; - str = grub_script_execute_argument_to_string (arglist->arg); - args[i++] = str; - } - } /* Execute the GRUB command or function. */ if (grubcmd) - ret = (grubcmd->func) (grubcmd, argcount, args); + ret = (grubcmd->func) (grubcmd, argcount - 1, args + 1); else - ret = grub_script_function_call (func, argcount, args); + ret = grub_script_function_call (func, argcount - 1, args + 1); /* Free arguments. */ for (i = 0; i < argcount; i++) grub_free (args[i]); grub_free (args); + if (grub_errno == GRUB_ERR_TEST_FAILURE) + grub_errno = GRUB_ERR_NONE; + + grub_print_error (); + grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret); grub_env_set ("?", errnobuf); @@ -166,13 +271,14 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd) grub_err_t grub_script_execute_cmdblock (struct grub_script_cmd *cmd) { + int ret = 0; struct grub_script_cmdblock *cmdblock = (struct grub_script_cmdblock *) cmd; /* Loop over every command and execute it. */ for (cmd = cmdblock->cmdlist; cmd; cmd = cmd->next) - grub_script_execute_cmd (cmd); + ret = grub_script_execute_cmd (cmd); - return 0; + return ret; } /* Execute an if statement. */ @@ -197,12 +303,57 @@ grub_script_execute_cmdif (struct grub_script_cmd *cmd) return grub_script_execute_cmd (cmdif->exec_on_false); } +/* Execute a for statement. */ +grub_err_t +grub_script_execute_cmdfor (struct grub_script_cmd *cmd) +{ + int i; + int result; + char **args; + int argcount; + struct grub_script_cmdfor *cmdfor = (struct grub_script_cmdfor *) cmd; + + args = grub_script_execute_arglist_to_argv (cmdfor->words, &argcount); + if (!args) + return grub_errno; + + result = 0; + for (i = 0; i < argcount; i++) + { + grub_env_set (cmdfor->name->str, args[i]); + result = grub_script_execute_cmd (cmdfor->list); + grub_free (args[i]); + } + + grub_free (args); + return result; +} + +/* Execute a "while" or "until" command. */ +grub_err_t +grub_script_execute_cmdwhile (struct grub_script_cmd *cmd) +{ + int cond; + int result; + struct grub_script_cmdwhile *cmdwhile = (struct grub_script_cmdwhile *) cmd; + + result = 0; + do { + cond = grub_script_execute_cmd (cmdwhile->cond); + if (cmdwhile->until ? !cond : cond) + break; + + result = grub_script_execute_cmd (cmdwhile->list); + } while (1); /* XXX Put a check for ^C here */ + + return result; +} + /* Execute the menu entry generate statement. */ grub_err_t grub_script_execute_menuentry (struct grub_script_cmd *cmd) { struct grub_script_cmd_menuentry *cmd_menuentry; - struct grub_script_arglist *arglist; char **args = 0; int argcount = 0; int i = 0; @@ -211,22 +362,9 @@ grub_script_execute_menuentry (struct grub_script_cmd *cmd) if (cmd_menuentry->arglist) { - argcount = cmd_menuentry->arglist->argcount; - - /* Create argv from the arguments. */ - args = grub_malloc (sizeof (char *) * argcount); - - if (! args) - { - return grub_errno; - } - - for (arglist = cmd_menuentry->arglist; arglist; arglist = arglist->next) - { - char *str; - str = grub_script_execute_argument_to_string (arglist->arg); - args[i++] = str; - } + args = grub_script_execute_arglist_to_argv (cmd_menuentry->arglist, &argcount); + if (!args) + return grub_errno; } grub_normal_add_menu_entry (argcount, (const char **) args, diff --git a/script/function.c b/script/function.c index a3950a8a0..ded470c4e 100644 --- a/script/function.c +++ b/script/function.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2007,2009 Free Software Foundation, Inc. + * Copyright (C) 2005,2007,2009,2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,7 +34,7 @@ grub_script_function_create (struct grub_script_arg *functionname_arg, if (! func) return 0; - func->name = grub_script_execute_argument_to_string (functionname_arg); + func->name = grub_strdup (functionname_arg->str); if (! func->name) { grub_free (func); diff --git a/script/lexer.c b/script/lexer.c index 5bcdf628b..42a570348 100644 --- a/script/lexer.c +++ b/script/lexer.c @@ -1,7 +1,7 @@ /* lexer.c - The scripting lexer. */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,42 +23,7 @@ #include #include "grub_script.tab.h" - -static int -check_varstate (grub_parser_state_t state) -{ - return (state == GRUB_PARSER_STATE_VARNAME - || state == GRUB_PARSER_STATE_VAR - || state == GRUB_PARSER_STATE_QVAR - || state == GRUB_PARSER_STATE_VARNAME2 - || state == GRUB_PARSER_STATE_QVARNAME - || state == GRUB_PARSER_STATE_QVARNAME2); -} - -static int -check_textstate (grub_parser_state_t state) -{ - return (state == GRUB_PARSER_STATE_TEXT - || state == GRUB_PARSER_STATE_ESC - || state == GRUB_PARSER_STATE_QUOTE - || state == GRUB_PARSER_STATE_DQUOTE); -} - -struct grub_lexer_param * -grub_script_lexer_init (char *script, grub_reader_getline_t getline) -{ - struct grub_lexer_param *param; - - param = grub_zalloc (sizeof (*param)); - if (! param) - return 0; - - param->state = GRUB_PARSER_STATE_TEXT; - param->getline = getline; - param->script = script; - - return param; -} +#include "grub_script.yy.h" void grub_script_lexer_ref (struct grub_lexer_param *state) @@ -74,360 +39,308 @@ grub_script_lexer_deref (struct grub_lexer_param *state) /* Start recording all characters passing through the lexer. */ void -grub_script_lexer_record_start (struct grub_lexer_param *state) +grub_script_lexer_record_start (struct grub_parser_param *parser) { - state->record = 1; - state->recordlen = 100; - state->recording = grub_malloc (state->recordlen); - state->recordpos = 0; + struct grub_lexer_param *lexer = parser->lexerstate; + + lexer->record = 1; + lexer->recordpos = 0; + if (lexer->recording) /* reuse last record */ + return; + + lexer->recordlen = GRUB_LEXER_INITIAL_RECORD_SIZE; + lexer->recording = grub_malloc (lexer->recordlen); + if (!lexer->recording) + { + grub_script_yyerror (parser, 0); + lexer->record = 0; + lexer->recordlen = 0; + } } char * -grub_script_lexer_record_stop (struct grub_lexer_param *state) +grub_script_lexer_record_stop (struct grub_parser_param *parser) { - state->record = 0; + char *ptr; + char *result; + struct grub_lexer_param *lexer = parser->lexerstate; - /* Delete the last character, it is a `}'. */ - if (state->recordpos > 0) - { - if (state->recording[--state->recordpos] != '}') - { - grub_printf ("Internal error while parsing menu entry"); - for (;;); /* XXX */ - } - state->recording[state->recordpos] = '\0'; - } + auto char *compact (char *start, char *end); + char *compact (char *start, char *end) + { + /* Delete '{' and '}' characters and whitespaces. */ + while (*start && grub_isspace (*start)) start++; + if (*start == '{') start++; + while (*start && grub_isspace (*start)) start++; - return state->recording; + while (*end && grub_isspace (*end)) end--; + if (*end == '}') end--; + while (*end && grub_isspace (*end)) end--; + end[1] = '\0'; + + return start; + } + + if (!lexer->record || !lexer->recording) + return 0; + + /* XXX This is not necessary in BASH. */ + + ptr = compact (lexer->recording, lexer->recording + lexer->recordpos - 1); + lexer->record = 0; + lexer->recordpos = 0; + + /* This memory would be freed by, grub_script_free. */ + result = grub_script_malloc (parser, grub_strlen (ptr) + 1); + if (result) + grub_strcpy (result, ptr); + + return result; } -/* When recording is enabled, record the character C as the next item - in the character stream. */ -static void -recordchar (struct grub_lexer_param *state, char c) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) + +/* Record STR if input recording is enabled. */ +void +grub_script_lexer_record (struct grub_parser_param *parser, char *str) { - if (state->recordpos == state->recordlen) + int len; + char *old; + struct grub_lexer_param *lexer = parser->lexerstate; + + if (!lexer->record) + return; + + len = grub_strlen (str); + if (lexer->recordpos + len + 1 > lexer->recordlen) { - char *old = state->recording; - state->recordlen += 100; - state->recording = grub_realloc (state->recording, state->recordlen); - if (! state->recording) + old = lexer->recording; + lexer->recordlen = MAX (len, lexer->recordlen) * 2; + lexer->recording = grub_realloc (lexer->recording, lexer->recordlen); + if (!lexer->recording) { grub_free (old); - state->record = 0; + lexer->record = 0; + lexer->recordpos = 0; + lexer->recordlen /= 2; + grub_script_yyerror (parser, 0); + return; } } - state->recording[state->recordpos++] = c; + grub_strcpy (lexer->recording + lexer->recordpos, str); + lexer->recordpos += len; } -/* Fetch the next character for the lexer. */ -static void -nextchar (struct grub_lexer_param *state) +/* Append '\n' to SRC, before '\0' */ +static char * +append_newline (const char *src) { - if (state->record) - recordchar (state, *state->script); - state->script++; + char *line; + grub_size_t len; + + len = grub_strlen (src); + line = grub_malloc (len + 2); + if (!line) + return 0; + + grub_strcpy (line, src); + + line[len] = '\n'; + line[len + 1] = '\0'; + return line; } +/* Read next line of input if necessary, and set yyscanner buffers. */ int -grub_script_yylex (union YYSTYPE *yylval, struct grub_parser_param *parsestate) +grub_script_lexer_yywrap (struct grub_parser_param *parserstate) { - grub_parser_state_t newstate; - char use; - struct grub_lexer_param *state = parsestate->lexerstate; - int firstrun = 1; + int len; + char *line; + char *line2; + YY_BUFFER_STATE buffer; + struct grub_lexer_param *lexerstate = parserstate->lexerstate; - yylval->arg = 0; + if (!lexerstate->refs) + return 0; - if (state->tokenonhold) + if (!lexerstate->getline) { - int token = state->tokenonhold; - state->tokenonhold = 0; - return token; + grub_script_yyerror (parserstate, "unexpected end of file"); + return 0; } - for (;! state->done; firstrun = 0) + line = 0; + buffer = 0; + lexerstate->getline (&line, 1); + if (!line) { - if (! state->script || ! *state->script) + grub_script_yyerror (parserstate, 0); /* XXX this could be for ^C case? */ + return 0; + } + + len = grub_strlen (line); + if (line[len - 1] == '\n') + { + buffer = yy_scan_string (line, lexerstate->yyscanner); + } + else + { + line2 = append_newline (line); + if (line2) { - /* Check if more tokens are requested by the parser. */ - if (((state->refs && ! parsestate->err) - || state->state == GRUB_PARSER_STATE_ESC - || state->state == GRUB_PARSER_STATE_QUOTE - || state->state == GRUB_PARSER_STATE_DQUOTE) - && state->getline) - { - int doexit = 0; - if (state->state != GRUB_PARSER_STATE_ESC - && state->state != GRUB_PARSER_STATE_QUOTE - && state->state != GRUB_PARSER_STATE_DQUOTE - && ! state->was_newline) - { - state->was_newline = 1; - state->tokenonhold = '\n'; - break; - } - while (! state->script || ! *state->script) - { - grub_free (state->newscript); - state->newscript = 0; - state->getline (&state->newscript, 1); - state->script = state->newscript; - if (! state->script) - { - doexit = 1; - break; - } - } - if (doexit) - break; - grub_dprintf ("scripting", "token=`\\n'\n"); - recordchar (state, '\n'); - if (state->state == GRUB_PARSER_STATE_VARNAME) - state->state = GRUB_PARSER_STATE_TEXT; - if (state->state == GRUB_PARSER_STATE_QVARNAME) - state->state = GRUB_PARSER_STATE_DQUOTE; - if (state->state == GRUB_PARSER_STATE_DQUOTE - || state->state == GRUB_PARSER_STATE_QUOTE) - yylval->arg = grub_script_arg_add (parsestate, yylval->arg, - GRUB_SCRIPT_ARG_TYPE_STR, - "\n"); - } - else - { - grub_free (state->newscript); - state->newscript = 0; - state->done = 1; - grub_dprintf ("scripting", "token=`\\n'\n"); - state->tokenonhold = '\n'; - break; - } - } - state->was_newline = 0; - - newstate = grub_parser_cmdline_state (state->state, *state->script, &use); - - /* Check if it is a text. */ - if (check_textstate (newstate)) - { - char *buffer = NULL; - int bufpos = 0; - /* Buffer is initially large enough to hold most commands - but extends automatically when needed. */ - int bufsize = 128; - - buffer = grub_malloc (bufsize); - - /* In case the string is not quoted, this can be a one char - length symbol. */ - if (newstate == GRUB_PARSER_STATE_TEXT) - { - int doexit = 0; - switch (*state->script) - { - case ' ': - while (*state->script) - { - newstate = grub_parser_cmdline_state (state->state, - *state->script, &use); - if (! (state->state == GRUB_PARSER_STATE_TEXT - && *state->script == ' ')) - { - grub_dprintf ("scripting", "token=` '\n"); - if (! firstrun) - doexit = 1; - break; - } - state->state = newstate; - nextchar (state); - } - grub_dprintf ("scripting", "token=` '\n"); - if (! firstrun) - doexit = 1; - break; - case '{': - case '}': - case ';': - case '\n': - { - char c; - grub_dprintf ("scripting", "token=`%c'\n", *state->script); - c = *state->script; - nextchar (state); - state->tokenonhold = c; - doexit = 1; - break; - } - } - if (doexit) - { - grub_free (buffer); - break; - } - } - - /* Read one token, possible quoted. */ - while (*state->script) - { - newstate = grub_parser_cmdline_state (state->state, - *state->script, &use); - - /* Check if a variable name starts. */ - if (check_varstate (newstate)) - break; - - /* If the string is not quoted or escaped, stop processing - when a special token was found. It will be recognized - next time when this function is called. */ - if (newstate == GRUB_PARSER_STATE_TEXT - && state->state != GRUB_PARSER_STATE_ESC - && state->state != GRUB_PARSER_STATE_QUOTE - && state->state != GRUB_PARSER_STATE_DQUOTE) - { - int breakout = 0; - - switch (use) - { - case ' ': - case '{': - case '}': - case ';': - case '\n': - breakout = 1; - } - if (breakout) - break; - } - - if (use) - { - if (bufsize <= bufpos + 1) - { - bufsize <<= 1; - buffer = grub_realloc (buffer, bufsize); - } - buffer[bufpos++] = use; - } - - state->state = newstate; - nextchar (state); - } - - /* A string of text was read in. */ - if (bufsize <= bufpos + 1) - { - bufsize <<= 1; - buffer = grub_realloc (buffer, bufsize); - } - - buffer[bufpos++] = 0; - - grub_dprintf ("scripting", "token=`%s'\n", buffer); - yylval->arg = grub_script_arg_add (parsestate, yylval->arg, - GRUB_SCRIPT_ARG_TYPE_STR, buffer); - - grub_free (buffer); - } - else if (newstate == GRUB_PARSER_STATE_VAR - || newstate == GRUB_PARSER_STATE_QVAR) - { - char *buffer = NULL; - int bufpos = 0; - /* Buffer is initially large enough to hold most commands - but extends automatically when needed. */ - int bufsize = 128; - - buffer = grub_malloc (bufsize); - - /* This is a variable, read the variable name. */ - while (*state->script) - { - newstate = grub_parser_cmdline_state (state->state, - *state->script, &use); - - /* Check if this character is not part of the variable name - anymore. */ - if (! (check_varstate (newstate))) - { - if (state->state == GRUB_PARSER_STATE_VARNAME2 - || state->state == GRUB_PARSER_STATE_QVARNAME2) - nextchar (state); - state->state = newstate; - break; - } - - if (use) - { - if (bufsize <= bufpos + 1) - { - bufsize <<= 1; - buffer = grub_realloc (buffer, bufsize); - } - buffer[bufpos++] = use; - } - - nextchar (state); - state->state = newstate; - } - - if (bufsize <= bufpos + 1) - { - bufsize <<= 1; - buffer = grub_realloc (buffer, bufsize); - } - - buffer[bufpos++] = 0; - - state->state = newstate; - yylval->arg = grub_script_arg_add (parsestate, yylval->arg, - GRUB_SCRIPT_ARG_TYPE_VAR, buffer); - grub_dprintf ("scripting", "vartoken=`%s'\n", buffer); - - grub_free (buffer); - } - else - { - /* There is either text or a variable name. In the case you - arrive here there is a serious problem with the lexer. */ - grub_error (GRUB_ERR_BAD_ARGUMENT, "internal error"); - return 0; + buffer = yy_scan_string (line2, lexerstate->yyscanner); + grub_free (line2); } } - if (yylval->arg == 0) + grub_free (line); + if (!buffer) { - int token = state->tokenonhold; - state->tokenonhold = 0; - return token; + grub_script_yyerror (parserstate, 0); + return 0; } - if (yylval->arg->next == 0 && yylval->arg->type == GRUB_SCRIPT_ARG_TYPE_STR) + return 1; +} + +struct grub_lexer_param * +grub_script_lexer_init (struct grub_parser_param *parser, char *script, + grub_reader_getline_t getline) +{ + int len; + char *script2; + YY_BUFFER_STATE buffer; + struct grub_lexer_param *lexerstate; + + lexerstate = grub_zalloc (sizeof (*lexerstate)); + if (!lexerstate) + return 0; + + lexerstate->size = GRUB_LEXER_INITIAL_TEXT_SIZE; + lexerstate->text = grub_malloc (lexerstate->size); + if (!lexerstate->text) { - /* Detect some special tokens. */ - if (! grub_strcmp (yylval->arg->str, "while")) - return GRUB_PARSER_TOKEN_WHILE; - else if (! grub_strcmp (yylval->arg->str, "if")) - return GRUB_PARSER_TOKEN_IF; - else if (! grub_strcmp (yylval->arg->str, "function")) - return GRUB_PARSER_TOKEN_FUNCTION; - else if (! grub_strcmp (yylval->arg->str, "menuentry")) - return GRUB_PARSER_TOKEN_MENUENTRY; - else if (! grub_strcmp (yylval->arg->str, "@")) - return GRUB_PARSER_TOKEN_MENUENTRY; - else if (! grub_strcmp (yylval->arg->str, "else")) - return GRUB_PARSER_TOKEN_ELSE; - else if (! grub_strcmp (yylval->arg->str, "then")) - return GRUB_PARSER_TOKEN_THEN; - else if (! grub_strcmp (yylval->arg->str, "fi")) - return GRUB_PARSER_TOKEN_FI; + grub_free (lexerstate); + return 0; } - return GRUB_PARSER_TOKEN_ARG; + lexerstate->getline = getline; /* rest are all zeros already */ + if (yylex_init (&lexerstate->yyscanner)) + { + grub_free (lexerstate->text); + grub_free (lexerstate); + return 0; + } + + buffer = 0; + script = script ? : "\n"; + len = grub_strlen (script); + + if (script[len - 1] == '\n') + { + buffer = yy_scan_string (script, lexerstate->yyscanner); + } + else + { + script2 = append_newline (script); + if (script2) + { + buffer = yy_scan_string (script2, lexerstate->yyscanner); + grub_free (script2); + } + } + + if (!buffer) + { + yylex_destroy (lexerstate->yyscanner); + grub_free (lexerstate->yyscanner); + + grub_free (lexerstate->text); + grub_free (lexerstate); + return 0; + } + yyset_extra (parser, lexerstate->yyscanner); + + return lexerstate; } void -grub_script_yyerror (struct grub_parser_param *lex __attribute__ ((unused)), - char const *err) +grub_script_lexer_fini (struct grub_lexer_param *lexerstate) { - grub_printf ("%s\n", err); + if (!lexerstate) + return; + + yylex_destroy (lexerstate->yyscanner); + + grub_free (lexerstate->recording); + grub_free (lexerstate->text); + grub_free (lexerstate); +} + +int +grub_script_yylex (union YYSTYPE *value, + struct grub_parser_param *parserstate) +{ + char *str; + int token; + grub_script_arg_type_t type; + struct grub_lexer_param *lexerstate = parserstate->lexerstate; + + value->arg = 0; + if (parserstate->err) + return GRUB_PARSER_TOKEN_BAD; + + if (lexerstate->eof) + return GRUB_PARSER_TOKEN_EOF; + + /* + * Words with environment variables, like foo${bar}baz needs + * multiple tokens to be merged into a single grub_script_arg. We + * use two variables to achieve this: lexerstate->merge_start and + * lexerstate->merge_end + */ + + lexerstate->merge_start = 0; + lexerstate->merge_end = 0; + do + { + /* Empty lexerstate->text. */ + lexerstate->used = 1; + lexerstate->text[0] = '\0'; + + token = yylex (value, lexerstate->yyscanner); + if (token == GRUB_PARSER_TOKEN_BAD) + break; + + /* Merging feature uses lexerstate->text instead of yytext. */ + if (lexerstate->merge_start) + { + str = lexerstate->text; + type = lexerstate->type; + } + else + { + str = yyget_text (lexerstate->yyscanner); + type = GRUB_SCRIPT_ARG_TYPE_TEXT; + } + grub_dprintf("lexer", "token %u text [%s]\n", token, str); + + value->arg = grub_script_arg_add (parserstate, value->arg, type, str); + } + while (lexerstate->merge_start && !lexerstate->merge_end); + + if (!value->arg || parserstate->err) + return GRUB_PARSER_TOKEN_BAD; + + return token; +} + +void +grub_script_yyerror (struct grub_parser_param *state, char const *err) +{ + if (err) + grub_error (GRUB_ERR_INVALID_COMMAND, err); + + grub_print_error (); + state->err++; } diff --git a/script/parser.y b/script/parser.y index 094a8856e..b5815ea8d 100644 --- a/script/parser.y +++ b/script/parser.y @@ -1,7 +1,7 @@ /* parser.y - The scripting parser. */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,10 +21,10 @@ #include #include -#define YYFREE grub_free -#define YYMALLOC grub_malloc +#define YYFREE grub_free +#define YYMALLOC grub_malloc #define YYLTYPE_IS_TRIVIAL 0 -#define YYENABLE_NLS 0 +#define YYENABLE_NLS 0 %} @@ -35,163 +35,252 @@ char *string; } -%token GRUB_PARSER_TOKEN_IF "if" -%token GRUB_PARSER_TOKEN_WHILE "while" -%token GRUB_PARSER_TOKEN_FUNCTION "function" -%token GRUB_PARSER_TOKEN_MENUENTRY "menuentry" -%token GRUB_PARSER_TOKEN_ELSE "else" -%token GRUB_PARSER_TOKEN_THEN "then" -%token GRUB_PARSER_TOKEN_FI "fi" -%token GRUB_PARSER_TOKEN_ARG -%type script_init script grubcmd command commands commandblock menuentry if -%type arguments; -%type GRUB_PARSER_TOKEN_ARG; +%token GRUB_PARSER_TOKEN_BAD +%token GRUB_PARSER_TOKEN_EOF 0 "end-of-input" + +%token GRUB_PARSER_TOKEN_NEWLINE "\n" +%token GRUB_PARSER_TOKEN_AND "&&" +%token GRUB_PARSER_TOKEN_OR "||" +%token GRUB_PARSER_TOKEN_SEMI2 ";;" +%token GRUB_PARSER_TOKEN_PIPE "|" +%token GRUB_PARSER_TOKEN_AMP "&" +%token GRUB_PARSER_TOKEN_SEMI ";" +%token GRUB_PARSER_TOKEN_LBR "{" +%token GRUB_PARSER_TOKEN_RBR "}" +%token GRUB_PARSER_TOKEN_NOT "!" +%token GRUB_PARSER_TOKEN_LSQBR2 "[" +%token GRUB_PARSER_TOKEN_RSQBR2 "]" +%token GRUB_PARSER_TOKEN_LT "<" +%token GRUB_PARSER_TOKEN_GT ">" + +%token GRUB_PARSER_TOKEN_CASE "case" +%token GRUB_PARSER_TOKEN_DO "do" +%token GRUB_PARSER_TOKEN_DONE "done" +%token GRUB_PARSER_TOKEN_ELIF "elif" +%token GRUB_PARSER_TOKEN_ELSE "else" +%token GRUB_PARSER_TOKEN_ESAC "esac" +%token GRUB_PARSER_TOKEN_FI "fi" +%token GRUB_PARSER_TOKEN_FOR "for" +%token GRUB_PARSER_TOKEN_IF "if" +%token GRUB_PARSER_TOKEN_IN "in" +%token GRUB_PARSER_TOKEN_SELECT "select" +%token GRUB_PARSER_TOKEN_THEN "then" +%token GRUB_PARSER_TOKEN_UNTIL "until" +%token GRUB_PARSER_TOKEN_WHILE "while" +%token GRUB_PARSER_TOKEN_TIME "time" +%token GRUB_PARSER_TOKEN_FUNCTION "function" +%token GRUB_PARSER_TOKEN_MENUENTRY "menuentry" +%token GRUB_PARSER_TOKEN_NAME "name" +%token GRUB_PARSER_TOKEN_WORD "word" + +%type word argument arguments0 arguments1 + +%type script_init script +%type grubcmd ifclause ifcmd forcmd whilecmd untilcmd +%type command commands1 menuentry statement %pure-parser -%lex-param { struct grub_parser_param *state }; +%lex-param { struct grub_parser_param *state }; %parse-param { struct grub_parser_param *state }; +%start script_init + %% /* It should be possible to do this in a clean way... */ -script_init: { state->err = 0; } script - { - state->parsed = $2; - } +script_init: { state->err = 0; } script { state->parsed = $2; state->err = 0; } ; -script: { $$ = 0; } - | '\n' { $$ = 0; } - | commands { $$ = $1; } - | function '\n' { $$ = 0; } - | menuentry '\n' { $$ = $1; } - | error - { - $$ = 0; - yyerror (state, "Incorrect command"); - state->err = 1; - yyerrok; - } +script: newlines0 + { + $$ = 0; + } + | script statement delimiter newlines0 + { + struct grub_script_cmdblock *cmdblock; + cmdblock = (struct grub_script_cmdblock *) $1; + $$ = grub_script_add_cmd (state, cmdblock, $2); + } + | error + { + $$ = 0; + yyerror (state, "Incorrect command"); + yyerrok; + } ; -delimiter: '\n' - | ';' - | delimiter '\n' +newlines0: /* Empty */ | newlines1 ; +newlines1: newlines0 "\n" ; + +delimiter: ";" + | "\n" +; +delimiters0: /* Empty */ | delimiters1 ; +delimiters1: delimiter + | delimiters1 "\n" ; -newlines: /* Empty */ - | newlines '\n' +word: GRUB_PARSER_TOKEN_NAME { $$ = grub_script_add_arglist (state, 0, $1); } + | GRUB_PARSER_TOKEN_WORD { $$ = grub_script_add_arglist (state, 0, $1); } ; +statement: command { $$ = $1; } + | function { $$ = 0; } + | menuentry { $$ = $1; } - -arguments: GRUB_PARSER_TOKEN_ARG - { - $$ = grub_script_add_arglist (state, 0, $1); - } - | arguments GRUB_PARSER_TOKEN_ARG - { - $$ = grub_script_add_arglist (state, $1, $2); - } +argument : "case" { $$ = grub_script_add_arglist (state, 0, $1); } + | "do" { $$ = grub_script_add_arglist (state, 0, $1); } + | "done" { $$ = grub_script_add_arglist (state, 0, $1); } + | "elif" { $$ = grub_script_add_arglist (state, 0, $1); } + | "else" { $$ = grub_script_add_arglist (state, 0, $1); } + | "esac" { $$ = grub_script_add_arglist (state, 0, $1); } + | "fi" { $$ = grub_script_add_arglist (state, 0, $1); } + | "for" { $$ = grub_script_add_arglist (state, 0, $1); } + | "if" { $$ = grub_script_add_arglist (state, 0, $1); } + | "in" { $$ = grub_script_add_arglist (state, 0, $1); } + | "select" { $$ = grub_script_add_arglist (state, 0, $1); } + | "then" { $$ = grub_script_add_arglist (state, 0, $1); } + | "until" { $$ = grub_script_add_arglist (state, 0, $1); } + | "while" { $$ = grub_script_add_arglist (state, 0, $1); } + | "function" { $$ = grub_script_add_arglist (state, 0, $1); } + | "menuentry" { $$ = grub_script_add_arglist (state, 0, $1); } + | word { $$ = $1; } ; -grubcmd: arguments - { - $$ = grub_script_create_cmdline (state, $1); - } +arguments0: /* Empty */ { $$ = 0; } + | arguments1 { $$ = $1; } +; +arguments1: argument arguments0 + { + if ($1 && $2) + { + $1->next = $2; + $1->argcount += $2->argcount; + $2->argcount = 0; + } + $$ = $1; + } +; + +grubcmd: word arguments0 + { + if ($1 && $2) { + $1->next = $2; + $1->argcount += $2->argcount; + $2->argcount = 0; + } + $$ = grub_script_create_cmdline (state, $1); + } ; /* A single command. */ -command: grubcmd delimiter { $$ = $1; } - | if delimiter { $$ = $1; } - | commandblock delimiter { $$ = $1; } +command: grubcmd { $$ = $1; } + | ifcmd { $$ = $1; } + | forcmd { $$ = $1; } + | whilecmd { $$ = $1; } + | untilcmd { $$ = $1; } ; -/* A block of commands. */ -commands: command - { - $$ = grub_script_add_cmd (state, 0, $1); - } - | command commands - { - struct grub_script_cmdblock *cmd; - cmd = (struct grub_script_cmdblock *) $2; - $$ = grub_script_add_cmd (state, cmd, $1); - } +/* A list of commands. */ +commands1: newlines0 command + { + $$ = grub_script_add_cmd (state, 0, $2); + } + | commands1 delimiters1 command + { + struct grub_script_cmdblock *cmdblock; + cmdblock = (struct grub_script_cmdblock *) $1; + $$ = grub_script_add_cmd (state, cmdblock, $3); + } ; -/* A function. Carefully save the memory that is allocated. Don't - change any stuff because it might seem like a fun thing to do! - Special care was take to make sure the mid-rule actions are - executed on the right moment. So the `commands' rule should be - recognized after executing the `grub_script_mem_record; and before - `grub_script_mem_record_stop'. */ -function: "function" GRUB_PARSER_TOKEN_ARG - { - grub_script_lexer_ref (state->lexerstate); - } newlines '{' - { - /* The first part of the function was recognized. - Now start recording the memory usage to store - this function. */ - state->func_mem = grub_script_mem_record (state); - } newlines commands '}' - { - struct grub_script *script; +function: "function" "name" + { + grub_script_lexer_ref (state->lexerstate); + state->func_mem = grub_script_mem_record (state); + } + delimiters0 "{" commands1 delimiters1 "}" + { + struct grub_script *script; + state->func_mem = grub_script_mem_record_stop (state, + state->func_mem); + script = grub_script_create ($6, state->func_mem); + if (script) + grub_script_function_create ($2, script); - /* All the memory usage for parsing this function - was recorded. */ - state->func_mem = grub_script_mem_record_stop (state, - state->func_mem); - script = grub_script_create ($8, state->func_mem); - if (script) - grub_script_function_create ($2, script); - grub_script_lexer_deref (state->lexerstate); - } + grub_script_lexer_deref (state->lexerstate); + } ; -/* Carefully designed, together with `menuentry' so everything happens - just in the expected order. */ -commandblock: '{' - { - grub_script_lexer_ref (state->lexerstate); - } - newlines commands '}' - { - grub_script_lexer_deref (state->lexerstate); - $$ = $4; - } +menuentry: "menuentry" + { + grub_script_lexer_ref (state->lexerstate); + } + arguments1 + { + grub_script_lexer_record_start (state); + } + delimiters0 "{" commands1 delimiters1 "}" + { + char *menu_entry; + menu_entry = grub_script_lexer_record_stop (state); + grub_script_lexer_deref (state->lexerstate); + $$ = grub_script_create_cmdmenu (state, $3, menu_entry, 0); + } ; -/* A menu entry. Carefully save the memory that is allocated. */ -menuentry: "menuentry" - { - grub_script_lexer_ref (state->lexerstate); - } arguments newlines '{' - { - grub_script_lexer_record_start (state->lexerstate); - } newlines commands '}' - { - char *menu_entry; - menu_entry = grub_script_lexer_record_stop (state->lexerstate); - grub_script_lexer_deref (state->lexerstate); - $$ = grub_script_create_cmdmenu (state, $3, menu_entry, 0); - } +ifcmd: "if" + { + grub_script_lexer_ref (state->lexerstate); + } + ifclause "fi" + { + $$ = $3; + grub_script_lexer_deref (state->lexerstate); + } +; +ifclause: commands1 delimiters1 "then" commands1 delimiters1 + { + $$ = grub_script_create_cmdif (state, $1, $4, 0); + } + | commands1 delimiters1 "then" commands1 delimiters1 "else" commands1 delimiters1 + { + $$ = grub_script_create_cmdif (state, $1, $4, $7); + } + | commands1 delimiters1 "then" commands1 delimiters1 "elif" ifclause + { + $$ = grub_script_create_cmdif (state, $1, $4, $7); + } ; -/* The first part of the if statement. It's used to switch the lexer - to a state in which it demands more tokens. */ -if_statement: "if" { grub_script_lexer_ref (state->lexerstate); } +forcmd: "for" "name" + { + grub_script_lexer_ref (state->lexerstate); + } + "in" arguments0 delimiters1 "do" commands1 delimiters1 "done" + { + $$ = grub_script_create_cmdfor (state, $2, $5, $8); + grub_script_lexer_deref (state->lexerstate); + } ; -/* The if statement. */ -if: if_statement commands "then" newlines commands "fi" - { - $$ = grub_script_create_cmdif (state, $2, $5, 0); - grub_script_lexer_deref (state->lexerstate); - } - | if_statement commands "then" newlines commands "else" newlines commands "fi" - { - $$ = grub_script_create_cmdif (state, $2, $5, $8); - grub_script_lexer_deref (state->lexerstate); - } +whilecmd: "while" + { + grub_script_lexer_ref (state->lexerstate); + } + commands1 delimiters1 "do" commands1 delimiters1 "done" + { + $$ = grub_script_create_cmdwhile (state, $3, $6, 0); + grub_script_lexer_deref (state->lexerstate); + } +; + +untilcmd: "until" + { + grub_script_lexer_ref (state->lexerstate); + } + commands1 delimiters1 "do" commands1 delimiters1 "done" + { + $$ = grub_script_create_cmdwhile (state, $3, $6, 1); + grub_script_lexer_deref (state->lexerstate); + } ; diff --git a/script/script.c b/script/script.c index c04a44966..4c87d9491 100644 --- a/script/script.c +++ b/script/script.c @@ -1,7 +1,7 @@ /* script.c -- Functions to create an in memory description of the script. */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2006,2007,2009 Free Software Foundation, Inc. + * Copyright (C) 2005,2006,2007,2009,2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,12 +24,10 @@ /* It is not possible to deallocate the memory when a syntax error was found. Because of that it is required to keep track of all memory - allocations. The memory is freed in case of an error, or - assigned to the parsed script when parsing was successful. */ + allocations. The memory is freed in case of an error, or assigned + to the parsed script when parsing was successful. -/* XXX */ - -/* In case of the normal malloc, some additional bytes are allocated + In case of the normal malloc, some additional bytes are allocated for this datastructure. All reserved memory is stored in a linked list so it can be easily freed. The original memory can be found from &mem. */ @@ -46,6 +44,8 @@ grub_script_malloc (struct grub_parser_param *state, grub_size_t size) struct grub_script_mem *mem; mem = (struct grub_script_mem *) grub_malloc (size + sizeof (*mem) - sizeof (char)); + if (!mem) + return 0; grub_dprintf ("scripting", "malloc %p\n", mem); mem->next = state->memused; @@ -94,32 +94,40 @@ grub_script_mem_record_stop (struct grub_parser_param *state, void grub_script_free (struct grub_script *script) { - if (! script) + if (!script) return; grub_script_mem_free (script->mem); grub_free (script); } - + /* Extend the argument arg with a variable or string of text. If ARG is zero a new list is created. */ struct grub_script_arg * -grub_script_arg_add (struct grub_parser_param *state, struct grub_script_arg *arg, - grub_script_arg_type_t type, char *str) +grub_script_arg_add (struct grub_parser_param *state, + struct grub_script_arg *arg, grub_script_arg_type_t type, + char *str) { struct grub_script_arg *argpart; struct grub_script_arg *ll; int len; - argpart = (struct grub_script_arg *) grub_script_malloc (state, sizeof (*arg)); + argpart = + (struct grub_script_arg *) grub_script_malloc (state, sizeof (*arg)); + if (!argpart) + return arg; + argpart->type = type; len = grub_strlen (str) + 1; argpart->str = grub_script_malloc (state, len); + if (!argpart->str) + return arg; /* argpart is freed later, during grub_script_free. */ + grub_memcpy (argpart->str, str, len); argpart->next = 0; - if (! arg) + if (!arg) return argpart; for (ll = arg; ll->next; ll = ll->next); @@ -132,19 +140,24 @@ grub_script_arg_add (struct grub_parser_param *state, struct grub_script_arg *ar is zero, a new list will be created. */ struct grub_script_arglist * grub_script_add_arglist (struct grub_parser_param *state, - struct grub_script_arglist *list, struct grub_script_arg *arg) + struct grub_script_arglist *list, + struct grub_script_arg *arg) { struct grub_script_arglist *link; struct grub_script_arglist *ll; grub_dprintf ("scripting", "arglist\n"); - link = (struct grub_script_arglist *) grub_script_malloc (state, sizeof (*link)); + link = + (struct grub_script_arglist *) grub_script_malloc (state, sizeof (*link)); + if (!link) + return list; + link->next = 0; link->arg = arg; link->argcount = 0; - if (! list) + if (!list) { link->argcount++; return link; @@ -171,6 +184,9 @@ grub_script_create_cmdline (struct grub_parser_param *state, grub_dprintf ("scripting", "cmdline\n"); cmd = grub_script_malloc (state, sizeof (*cmd)); + if (!cmd) + return 0; + cmd->cmd.exec = grub_script_execute_cmdline; cmd->cmd.next = 0; cmd->arglist = arglist; @@ -193,6 +209,9 @@ grub_script_create_cmdif (struct grub_parser_param *state, grub_dprintf ("scripting", "cmdif\n"); cmd = grub_script_malloc (state, sizeof (*cmd)); + if (!cmd) + return 0; + cmd->cmd.exec = grub_script_execute_cmdif; cmd->cmd.next = 0; cmd->exec_to_evaluate = exec_to_evaluate; @@ -202,6 +221,52 @@ grub_script_create_cmdif (struct grub_parser_param *state, return (struct grub_script_cmd *) cmd; } +/* Create a command that functions as a for statement. */ +struct grub_script_cmd * +grub_script_create_cmdfor (struct grub_parser_param *state, + struct grub_script_arg *name, + struct grub_script_arglist *words, + struct grub_script_cmd *list) +{ + struct grub_script_cmdfor *cmd; + + grub_dprintf ("scripting", "cmdfor\n"); + + cmd = grub_script_malloc (state, sizeof (*cmd)); + if (! cmd) + return 0; + + cmd->cmd.exec = grub_script_execute_cmdfor; + cmd->cmd.next = 0; + cmd->name = name; + cmd->words = words; + cmd->list = list; + + return (struct grub_script_cmd *) cmd; +} + +/* Create a "while" or "until" command. */ +struct grub_script_cmd * +grub_script_create_cmdwhile (struct grub_parser_param *state, + struct grub_script_cmd *cond, + struct grub_script_cmd *list, + int is_an_until_loop) +{ + struct grub_script_cmdwhile *cmd; + + cmd = grub_script_malloc (state, sizeof (*cmd)); + if (! cmd) + return 0; + + cmd->cmd.exec = grub_script_execute_cmdwhile; + cmd->cmd.next = 0; + cmd->cond = cond; + cmd->list = list; + cmd->until = is_an_until_loop; + + return (struct grub_script_cmd *) cmd; +} + /* Create a command that adds a menu entry to the menu. Title is an argument that is parsed to generate a string that can be used as the title. The sourcecode for this entry is passed in SOURCECODE. @@ -209,30 +274,16 @@ grub_script_create_cmdif (struct grub_parser_param *state, struct grub_script_cmd * grub_script_create_cmdmenu (struct grub_parser_param *state, struct grub_script_arglist *arglist, - char *sourcecode, - int options) + char *sourcecode, int options) { struct grub_script_cmd_menuentry *cmd; - int i; - - /* Skip leading newlines to make the sourcecode better readable when - using the editor. */ - while (*sourcecode == '\n') - sourcecode++; - - /* Having trailing returns can some some annoying conflicts, remove - them. XXX: Can the parser be improved to handle this? */ - for (i = grub_strlen (sourcecode) - 1; i > 0; i--) - { - if (sourcecode[i] != '\n') - break; - sourcecode[i] = '\0'; - } cmd = grub_script_malloc (state, sizeof (*cmd)); + if (!cmd) + return 0; + cmd->cmd.exec = grub_script_execute_menuentry; cmd->cmd.next = 0; - /* XXX: Check if this memory is properly freed. */ cmd->sourcecode = sourcecode; cmd->arglist = arglist; cmd->options = options; @@ -248,15 +299,19 @@ grub_script_add_cmd (struct grub_parser_param *state, struct grub_script_cmdblock *cmdblock, struct grub_script_cmd *cmd) { + struct grub_script_cmd *ptr; + grub_dprintf ("scripting", "cmdblock\n"); - if (! cmd) + if (!cmd) return (struct grub_script_cmd *) cmdblock; - if (! cmdblock) + if (!cmdblock) { - cmdblock = (struct grub_script_cmdblock *) grub_script_malloc (state, - sizeof (*cmdblock)); + cmdblock = grub_script_malloc (state, sizeof (*cmdblock)); + if (!cmdblock) + return 0; + cmdblock->cmd.exec = grub_script_execute_cmdblock; cmdblock->cmd.next = 0; cmdblock->cmdlist = cmd; @@ -264,22 +319,29 @@ grub_script_add_cmd (struct grub_parser_param *state, } else { - cmd->next = cmdblock->cmdlist; - cmdblock->cmdlist = cmd; + if (!cmdblock->cmdlist) + cmdblock->cmdlist = cmd; + else + { + ptr = cmdblock->cmdlist; + while (ptr->next) + ptr = ptr->next; + ptr->next = cmd; + } } return (struct grub_script_cmd *) cmdblock; } - + struct grub_script * grub_script_create (struct grub_script_cmd *cmd, struct grub_script_mem *mem) { struct grub_script *parsed; parsed = grub_malloc (sizeof (*parsed)); - if (! parsed) + if (!parsed) { grub_script_mem_free (mem); grub_free (cmd); @@ -304,16 +366,16 @@ grub_script_parse (char *script, grub_reader_getline_t getline) struct grub_parser_param *parsestate; parsed = grub_malloc (sizeof (*parsed)); - if (! parsed) + if (!parsed) return 0; parsestate = grub_zalloc (sizeof (*parsestate)); - if (! parsestate) + if (!parsestate) return 0; /* Initialize the lexer. */ - lexstate = grub_script_lexer_init (script, getline); - if (! lexstate) + lexstate = grub_script_lexer_init (parsestate, script, getline); + if (!lexstate) { grub_free (parsed); grub_free (parsestate); @@ -330,7 +392,7 @@ grub_script_parse (char *script, grub_reader_getline_t getline) struct grub_script_mem *memfree; memfree = grub_script_mem_record_stop (parsestate, membackup); grub_script_mem_free (memfree); - grub_free (lexstate); + grub_script_lexer_fini (lexstate); grub_free (parsestate); return 0; } @@ -338,7 +400,7 @@ grub_script_parse (char *script, grub_reader_getline_t getline) parsed->mem = grub_script_mem_record_stop (parsestate, membackup); parsed->cmd = parsestate->parsed; - grub_free (lexstate); + grub_script_lexer_fini (lexstate); grub_free (parsestate); return parsed; diff --git a/script/yylex.l b/script/yylex.l new file mode 100644 index 000000000..29aa5c2e3 --- /dev/null +++ b/script/yylex.l @@ -0,0 +1,327 @@ +%{ +/* yylex.l The scripting lexer. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include "grub_script.tab.h" + +#define yyfree grub_lexer_yyfree +#define yyalloc grub_lexer_yyalloc +#define yyrealloc grub_lexer_yyrealloc + +/* + * As we don't have access to yyscanner, we cannot do much except to + * print the fatal error. + */ +#define YY_FATAL_ERROR(msg) \ + do { \ + grub_printf ("fatal error: %s\n", msg); \ + } while (0) + +#define COPY(str, hint) \ + do { \ + copy_string (yyextra, str, hint); \ + } while (0) + + +#define RECORD \ + do { \ + grub_script_lexer_record (yyextra, yytext); \ + } while (0) + +#define ARG(t) \ + do { \ + yyextra->lexerstate->type = t; \ + return GRUB_PARSER_TOKEN_WORD; \ + } while (0) + +/* We don't need YY_INPUT, as we rely on yy_scan_strings */ +#define YY_INPUT(buf,res,max) do { res = 0; } while (0) + +/* forward declarations */ +static void grub_lexer_yyfree (void *, yyscan_t yyscanner); +static void* grub_lexer_yyalloc (yy_size_t, yyscan_t yyscanner); +static void* grub_lexer_yyrealloc (void*, yy_size_t, yyscan_t yyscanner); +static void copy_string (struct grub_parser_param *, const char *, + unsigned hint); + +%} + +%top{ + +#include + +typedef size_t yy_size_t; +#define YY_TYPEDEF_YY_SIZE_T 1 + +/* + * Some flex hacks for -nostdinc; XXX We need to fix these when libc + * support becomes availble in GRUB. + */ + +#ifndef GRUB_UTIL +#define stdin 0 +#define stdout 0 + +#define fprintf(...) 0 +#define exit(...) +#endif + +} + +%option ecs +%option meta-ecs + +%option warn +%option array +%option stack +%option reentrant +%option bison-bridge +%option never-interactive + +%option noyyfree noyyalloc noyyrealloc +%option nounistd nostdinit nodefault noyylineno noyywrap + +/* Reduce lexer size, by not defining these. */ +%option noyy_top_state +%option noinput nounput +%option noyyget_in noyyset_in +%option noyyget_out noyyset_out +%option noyyget_debug noyyset_debug +%option noyyget_lineno noyyset_lineno + +%option extra-type="struct grub_parser_param*" + +BLANK [ \t] +COMMENT ^[ \t]*#.*$ + +CHAR [^|&$;<> \t\n\'\"\\] +DIGITS [[:digit:]]+ +NAME [[:alpha:]_][[:alnum:][:digit:]_]* + +ESC \\. +VARIABLE ${NAME}|$\{{NAME}\}|${DIGITS}|$\{{DIGITS}\}|$\?|$\{\?\} +DQSTR \"([^\\\"]|{ESC})*\" +SQSTR \'[^\']*\' +WORD ({CHAR}|{DQSTR}|{SQSTR}|{ESC}|{VARIABLE})+ + +%x SPLIT +%x DQUOTE +%x SQUOTE +%x VAR + +%% + + /* White spaces */ +{BLANK}+ { RECORD; } +{COMMENT} { RECORD; } + + /* Special symbols */ +"\n" { RECORD; return GRUB_PARSER_TOKEN_NEWLINE; } +"||" { RECORD; return GRUB_PARSER_TOKEN_OR; } +"&&" { RECORD; return GRUB_PARSER_TOKEN_AND; } +";;" { RECORD; return GRUB_PARSER_TOKEN_SEMI2; } +"|" { RECORD; return GRUB_PARSER_TOKEN_PIPE; } +"&" { RECORD; return GRUB_PARSER_TOKEN_AMP; } +";" { RECORD; return GRUB_PARSER_TOKEN_SEMI; } +"<" { RECORD; return GRUB_PARSER_TOKEN_LT; } +">" { RECORD; return GRUB_PARSER_TOKEN_GT; } + + /* Reserved words */ +"!" { RECORD; return GRUB_PARSER_TOKEN_NOT; } +"{" { RECORD; return GRUB_PARSER_TOKEN_LBR; } +"}" { RECORD; return GRUB_PARSER_TOKEN_RBR; } +"[[" { RECORD; return GRUB_PARSER_TOKEN_RSQBR2; } +"]]" { RECORD; return GRUB_PARSER_TOKEN_LSQBR2; } +"time" { RECORD; return GRUB_PARSER_TOKEN_TIME; } +"case" { RECORD; return GRUB_PARSER_TOKEN_CASE; } +"do" { RECORD; return GRUB_PARSER_TOKEN_DO; } +"done" { RECORD; return GRUB_PARSER_TOKEN_DONE; } +"elif" { RECORD; return GRUB_PARSER_TOKEN_ELIF; } +"else" { RECORD; return GRUB_PARSER_TOKEN_ELSE; } +"esac" { RECORD; return GRUB_PARSER_TOKEN_ESAC; } +"fi" { RECORD; return GRUB_PARSER_TOKEN_FI; } +"for" { RECORD; return GRUB_PARSER_TOKEN_FOR; } +"if" { RECORD; return GRUB_PARSER_TOKEN_IF; } +"in" { RECORD; return GRUB_PARSER_TOKEN_IN; } +"select" { RECORD; return GRUB_PARSER_TOKEN_SELECT; } +"then" { RECORD; return GRUB_PARSER_TOKEN_THEN; } +"until" { RECORD; return GRUB_PARSER_TOKEN_UNTIL; } +"while" { RECORD; return GRUB_PARSER_TOKEN_WHILE; } +"function" { RECORD; return GRUB_PARSER_TOKEN_FUNCTION; } +"menuentry" { RECORD; return GRUB_PARSER_TOKEN_MENUENTRY; } + +{NAME} { RECORD; return GRUB_PARSER_TOKEN_NAME; } +{WORD} { + RECORD; + /* resplit yytext */ + grub_dprintf ("lexer", "word: [%s]\n", yytext); + yypush_buffer_state (YY_CURRENT_BUFFER, yyscanner); + if (yy_scan_string (yytext, yyscanner)) + { + yyextra->lexerstate->merge_start = 1; + yy_push_state (SPLIT, yyscanner); + } + else + { + grub_script_yyerror (yyextra, 0); + yypop_buffer_state (yyscanner); + return GRUB_PARSER_TOKEN_WORD; + } + } + +.|\n { + grub_script_yyerror (yyextra, "unrecognized token"); + return GRUB_PARSER_TOKEN_BAD; + } + + /* Split word into multiple args */ + +{ + \\. { COPY (yytext + 1, yyleng - 1); } + \" { + yy_push_state (DQUOTE, yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); + } + \' { + yy_push_state (SQUOTE, yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); + } + \$ { + yy_push_state (VAR, yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); + } + \\ | + [^\"\'\$\\]+ { COPY (yytext, yyleng); } + <> { + yy_pop_state (yyscanner); + yypop_buffer_state (yyscanner); + yyextra->lexerstate->merge_end = 1; + ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); + } +} + +{ + \? | + {DIGITS} | + {NAME} { + COPY (yytext, yyleng); + yy_pop_state (yyscanner); + if (YY_START == SPLIT) + ARG (GRUB_SCRIPT_ARG_TYPE_VAR); + else + ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR); + } + \{\?\} | + \{{DIGITS}\} | + \{{NAME}\} { + yytext[yyleng - 1] = '\0'; + COPY (yytext + 1, yyleng - 2); + yy_pop_state (yyscanner); + if (YY_START == SPLIT) + ARG (GRUB_SCRIPT_ARG_TYPE_VAR); + else + ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR); + } + .|\n { return GRUB_PARSER_TOKEN_BAD; } +} + +{ + \' { + yy_pop_state (yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_SQSTR); + } + [^\']+ { COPY (yytext, yyleng); } +} + +{ + \\\$ { COPY ("$", 1); } + \\\\ { COPY ("\\", 1); } + \\\" { COPY ("\"", 1); } + \\\n { /* ignore */ } + [^\"\$\\\n]+ { COPY (yytext, yyleng); } + \" { + yy_pop_state (yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR); + } + \$ { + yy_push_state (VAR, yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR); + } + (.|\n) { COPY (yytext, yyleng); } +} + +<> { + yypop_buffer_state (yyscanner); + if (! grub_script_lexer_yywrap (yyextra)) + { + yyextra->lexerstate->eof = 1; + return GRUB_PARSER_TOKEN_EOF; + } + } + +%% + +static void +grub_lexer_yyfree (void *ptr, yyscan_t yyscanner __attribute__ ((unused))) +{ + grub_free(ptr); +} + +static void* +grub_lexer_yyalloc (yy_size_t size, yyscan_t yyscanner __attribute__ ((unused))) +{ + return grub_malloc (size); +} + +static void* +grub_lexer_yyrealloc (void *ptr, yy_size_t size, + yyscan_t yyscanner __attribute__ ((unused))) +{ + return grub_realloc (ptr, size); +} + +#define MAX(a,b) ((a) < (b) ? (b) : (a)) + +static void copy_string (struct grub_parser_param *parser, const char *str, unsigned hint) +{ + int size; + char *ptr; + unsigned len; + + len = hint ? hint : grub_strlen (str); + if (parser->lexerstate->used + len >= parser->lexerstate->size) + { + size = MAX (len, parser->lexerstate->size) * 2; + ptr = grub_realloc (parser->lexerstate->text, size); + if (!ptr) + { + grub_script_yyerror (parser, 0); + return; + } + + parser->lexerstate->text = ptr; + parser->lexerstate->size = size; + } + grub_strcpy (parser->lexerstate->text + parser->lexerstate->used - 1, str); + parser->lexerstate->used += len; +} diff --git a/term/efi/console.c b/term/efi/console.c index 264770cae..664861398 100644 --- a/term/efi/console.c +++ b/term/efi/console.c @@ -159,27 +159,27 @@ grub_console_checkkey (void) read_key = key.unicode_char; break; case 0x01: - read_key = 16; + read_key = GRUB_TERM_UP; break; case 0x02: - read_key = 14; + read_key = GRUB_TERM_DOWN; break; case 0x03: - read_key = 6; + read_key = GRUB_TERM_RIGHT; break; case 0x04: - read_key = 2; + read_key = GRUB_TERM_LEFT; break; case 0x05: - read_key = 1; + read_key = GRUB_TERM_HOME; break; case 0x06: - read_key = 5; + read_key = GRUB_TERM_END; break; case 0x07: break; case 0x08: - read_key = 4; + read_key = GRUB_TERM_DC; break; case 0x09: break; @@ -194,6 +194,9 @@ grub_console_checkkey (void) case 0x0d: read_key = 5; break; + case 0x0e: + read_key = 3; + break; case 0x17: read_key = '\e'; break; diff --git a/term/gfxterm.c b/term/gfxterm.c index a8aca7820..a1409980b 100644 --- a/term/gfxterm.c +++ b/term/gfxterm.c @@ -24,8 +24,11 @@ #include #include #include +#include #include #include +#include +#include #define DEFAULT_VIDEO_MODE "auto" #define DEFAULT_BORDER_WIDTH 10 @@ -100,11 +103,28 @@ struct grub_virtual_screen /* Text buffer for virtual screen. Contains (columns * rows) number of entries. */ struct grub_colored_char *text_buffer; + + int total_scroll; }; -static struct grub_virtual_screen virtual_screen; +struct grub_gfxterm_window +{ + unsigned x; + unsigned y; + unsigned width; + unsigned height; + int double_repaint; +}; -static struct grub_video_mode_info mode_info; +static struct grub_video_render_target *render_target; +void (*grub_gfxterm_decorator_hook) (void) = NULL; +static struct grub_gfxterm_window window; +static struct grub_virtual_screen virtual_screen; +static grub_gfxterm_repaint_callback_t repaint_callback; +static int repaint_schedulded = 0; +static int repaint_was_schedulded = 0; + +static void destroy_window (void); static struct grub_video_render_target *text_layer; @@ -125,6 +145,8 @@ static unsigned int calculate_normal_character_width (grub_font_t font); static unsigned char calculate_character_width (struct grub_font_glyph *glyph); +static void grub_gfxterm_refresh (void); + static void set_term_color (grub_uint8_t term_color) { @@ -202,6 +224,7 @@ grub_virtual_screen_setup (unsigned int x, unsigned int y, virtual_screen.cursor_x = 0; virtual_screen.cursor_y = 0; virtual_screen.cursor_state = 1; + virtual_screen.total_scroll = 0; /* Calculate size of text buffer. */ virtual_screen.columns = virtual_screen.width / virtual_screen.normal_char_width; @@ -236,7 +259,7 @@ grub_virtual_screen_setup (unsigned int x, unsigned int y, set_term_color (virtual_screen.term_color); - grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + grub_video_set_active_render_target (render_target); virtual_screen.bg_color_display = grub_video_map_rgba(0, 0, 0, 0); @@ -247,21 +270,95 @@ grub_virtual_screen_setup (unsigned int x, unsigned int y, return grub_errno; } -static grub_err_t -grub_gfxterm_init (void) +void +grub_gfxterm_schedule_repaint (void) { - char *font_name; - char *modevar; - char *tmp; - grub_video_color_t color; - int width; - int height; - grub_err_t err; + repaint_schedulded = 1; +} - /* Select the font to use. */ +grub_err_t +grub_gfxterm_set_window (struct grub_video_render_target *target, + int x, int y, int width, int height, + int double_repaint, + const char *font_name, int border_width) +{ + /* Clean up any prior instance. */ + destroy_window (); + + /* Set the render target. */ + render_target = target; + + /* Create virtual screen. */ + if (grub_virtual_screen_setup (border_width, border_width, + width - 2 * border_width, + height - 2 * border_width, + font_name) + != GRUB_ERR_NONE) + { + return grub_errno; + } + + /* Set window bounds. */ + window.x = x; + window.y = y; + window.width = width; + window.height = height; + window.double_repaint = double_repaint; + + dirty_region_reset (); + grub_gfxterm_schedule_repaint (); + + return grub_errno; +} + +grub_err_t +grub_gfxterm_fullscreen (void) +{ + const char *font_name; + struct grub_video_mode_info mode_info; + grub_video_color_t color; + grub_err_t err; + int double_redraw; + + err = grub_video_get_info (&mode_info); + /* Figure out what mode we ended up. */ + if (err) + return err; + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + double_redraw = mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + && !(mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + + /* Make sure screen is black. */ + color = grub_video_map_rgb (0, 0, 0); + grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height); + if (double_redraw) + { + grub_video_swap_buffers (); + grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height); + } + bitmap = 0; + + /* Select the font to use. */ font_name = grub_env_get ("gfxterm_font"); if (! font_name) - font_name = ""; /* Allow fallback to any font. */ + font_name = ""; /* Allow fallback to any font. */ + + grub_gfxterm_decorator_hook = NULL; + + return grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY, + 0, 0, mode_info.width, mode_info.height, + double_redraw, + font_name, DEFAULT_BORDER_WIDTH); +} + +static grub_err_t +grub_gfxterm_term_init (void) +{ + char *tmp; + grub_err_t err; + const char *modevar; /* Parse gfxmode environment variable if set. */ modevar = grub_env_get ("gfxmode"); @@ -280,37 +377,15 @@ grub_gfxterm_init (void) if (err) return err; - err = grub_video_get_info (&mode_info); - /* Figure out what mode we ended up. */ + err = grub_gfxterm_fullscreen (); if (err) - return err; + grub_video_restore (); - /* Make sure screen is black. */ - color = grub_video_map_rgb (0, 0, 0); - grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height); - bitmap = 0; - - /* Leave borders for virtual screen. */ - width = mode_info.width - (2 * DEFAULT_BORDER_WIDTH); - height = mode_info.height - (2 * DEFAULT_BORDER_WIDTH); - - /* Create virtual screen. */ - if (grub_virtual_screen_setup (DEFAULT_BORDER_WIDTH, DEFAULT_BORDER_WIDTH, - width, height, font_name) != GRUB_ERR_NONE) - { - grub_video_restore (); - return grub_errno; - } - - /* Mark whole screen as dirty. */ - dirty_region_reset (); - dirty_region_add (0, 0, mode_info.width, mode_info.height); - - return (grub_errno = GRUB_ERR_NONE); + return err; } -static grub_err_t -grub_gfxterm_fini (void) +static void +destroy_window (void) { if (bitmap) { @@ -318,10 +393,18 @@ grub_gfxterm_fini (void) bitmap = 0; } + repaint_callback = 0; grub_virtual_screen_free (); +} +static grub_err_t +grub_gfxterm_term_fini (void) +{ + destroy_window (); grub_video_restore (); + /* Clear error state. */ + grub_errno = GRUB_ERR_NONE; return GRUB_ERR_NONE; } @@ -330,9 +413,15 @@ redraw_screen_rect (unsigned int x, unsigned int y, unsigned int width, unsigned int height) { grub_video_color_t color; + grub_video_rect_t saved_view; - grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); - + grub_video_set_active_render_target (render_target); + /* Save viewport and set it to our window. */ + grub_video_get_viewport ((unsigned *) &saved_view.x, + (unsigned *) &saved_view.y, + (unsigned *) &saved_view.width, + (unsigned *) &saved_view.height); + grub_video_set_viewport (window.x, window.y, window.width, window.height); if (bitmap) { @@ -399,6 +488,14 @@ redraw_screen_rect (unsigned int x, unsigned int y, y - virtual_screen.offset_y, width, height); } + + /* Restore saved viewport. */ + grub_video_set_viewport (saved_view.x, saved_view.y, + saved_view.width, saved_view.height); + grub_video_set_active_render_target (render_target); + + if (repaint_callback) + repaint_callback (x, y, width, height); } static void @@ -408,6 +505,7 @@ dirty_region_reset (void) dirty_region.top_left_y = -1; dirty_region.bottom_right_x = -1; dirty_region.bottom_right_y = -1; + repaint_was_schedulded = 0; } static int @@ -427,6 +525,16 @@ dirty_region_add (int x, int y, unsigned int width, unsigned int height) if ((width == 0) || (height == 0)) return; + if (repaint_schedulded) + { + x = virtual_screen.offset_x; + y = virtual_screen.offset_y; + width = virtual_screen.width; + height = virtual_screen.height; + repaint_schedulded = 0; + repaint_was_schedulded = 1; + } + if (dirty_region_is_empty ()) { dirty_region.top_left_x = x; @@ -473,13 +581,14 @@ dirty_region_redraw (void) width = dirty_region.bottom_right_x - x + 1; height = dirty_region.bottom_right_y - y + 1; - redraw_screen_rect (x, y, width, height); + if (repaint_was_schedulded && grub_gfxterm_decorator_hook) + grub_gfxterm_decorator_hook (); - dirty_region_reset (); + redraw_screen_rect (x, y, width, height); } -static void -write_char (void) +static inline void +paint_char (unsigned cx, unsigned cy) { struct grub_colored_char *p; struct grub_font_glyph *glyph; @@ -491,10 +600,12 @@ write_char (void) unsigned int height; unsigned int width; + if (cy + virtual_screen.total_scroll >= virtual_screen.rows) + return; + /* Find out active character. */ p = (virtual_screen.text_buffer - + virtual_screen.cursor_x - + (virtual_screen.cursor_y * virtual_screen.columns)); + + cx + (cy * virtual_screen.columns)); p -= p->index; @@ -508,68 +619,163 @@ write_char (void) color = p->fg_color; bgcolor = p->bg_color; - x = virtual_screen.cursor_x * virtual_screen.normal_char_width; - y = virtual_screen.cursor_y * virtual_screen.normal_char_height; + x = cx * virtual_screen.normal_char_width; + y = (cy + virtual_screen.total_scroll) * virtual_screen.normal_char_height; /* Render glyph to text layer. */ grub_video_set_active_render_target (text_layer); grub_video_fill_rect (bgcolor, x, y, width, height); grub_font_draw_glyph (glyph, color, x, y + ascent); - grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + grub_video_set_active_render_target (render_target); /* Mark character to be drawn. */ dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y, width, height); } -static void +static inline void +write_char (void) +{ + paint_char (virtual_screen.cursor_x, virtual_screen.cursor_y); +} + +static inline void draw_cursor (int show) { + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int height; + grub_video_color_t color; + write_char (); - if (show) + if (!show) + return; + + if (virtual_screen.cursor_y + virtual_screen.total_scroll + >= virtual_screen.rows) + return; + + /* Determine cursor properties and position on text layer. */ + x = virtual_screen.cursor_x * virtual_screen.normal_char_width; + width = virtual_screen.normal_char_width; + color = virtual_screen.fg_color; + y = ((virtual_screen.cursor_y + virtual_screen.total_scroll) + * virtual_screen.normal_char_height + + grub_font_get_ascent (virtual_screen.font)); + height = 2; + + /* Render cursor to text layer. */ + grub_video_set_active_render_target (text_layer); + grub_video_fill_rect (color, x, y, width, height); + grub_video_set_active_render_target (render_target); + + /* Mark cursor to be redrawn. */ + dirty_region_add (virtual_screen.offset_x + x, + virtual_screen.offset_y + y, + width, height); +} + +static void +real_scroll (void) +{ + unsigned int i, j, was_scroll; + grub_video_color_t color; + + if (!virtual_screen.total_scroll) + return; + + /* If we have bitmap, re-draw screen, otherwise scroll physical screen too. */ + if (bitmap) { - unsigned int x; - unsigned int y; - unsigned int width; - unsigned int height; - grub_video_color_t color; - - /* Determine cursor properties and position on text layer. */ - x = virtual_screen.cursor_x * virtual_screen.normal_char_width; - width = virtual_screen.normal_char_width; - color = virtual_screen.fg_color; - y = (virtual_screen.cursor_y * virtual_screen.normal_char_height - + grub_font_get_ascent (virtual_screen.font)); - height = 2; - - /* Render cursor to text layer. */ + /* Scroll physical screen. */ grub_video_set_active_render_target (text_layer); - grub_video_fill_rect (color, x, y, width, height); - grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + color = virtual_screen.bg_color; + grub_video_scroll (color, 0, -virtual_screen.normal_char_height + * virtual_screen.total_scroll); - /* Mark cursor to be redrawn. */ - dirty_region_add (virtual_screen.offset_x + x, - virtual_screen.offset_y + y, - width, height); + /* Mark virtual screen to be redrawn. */ + dirty_region_add_virtualscreen (); } + else + { + grub_video_rect_t saved_view; + + /* Remove cursor. */ + draw_cursor (0); + + grub_video_set_active_render_target (render_target); + /* Save viewport and set it to our window. */ + grub_video_get_viewport ((unsigned *) &saved_view.x, + (unsigned *) &saved_view.y, + (unsigned *) &saved_view.width, + (unsigned *) &saved_view.height); + grub_video_set_viewport (window.x, window.y, window.width, window.height); + + i = window.double_repaint ? 2 : 1; + + color = virtual_screen.bg_color; + + while (i--) + { + /* Clear new border area. */ + grub_video_fill_rect (color, + virtual_screen.offset_x, + virtual_screen.offset_y, + virtual_screen.width, + virtual_screen.normal_char_height + * virtual_screen.total_scroll); + + grub_video_set_active_render_target (render_target); + dirty_region_redraw (); + + /* Scroll physical screen. */ + grub_video_scroll (color, 0, -virtual_screen.normal_char_height + * virtual_screen.total_scroll); + + if (i) + grub_video_swap_buffers (); + } + dirty_region_reset (); + + /* Scroll physical screen. */ + grub_video_set_active_render_target (text_layer); + color = virtual_screen.bg_color; + grub_video_scroll (color, 0, -virtual_screen.normal_char_height + * virtual_screen.total_scroll); + + /* Restore saved viewport. */ + grub_video_set_viewport (saved_view.x, saved_view.y, + saved_view.width, saved_view.height); + grub_video_set_active_render_target (render_target); + + } + + was_scroll = virtual_screen.total_scroll; + virtual_screen.total_scroll = 0; + + if (was_scroll > virtual_screen.rows) + was_scroll = virtual_screen.rows; + + /* Draw shadow part. */ + for (i = virtual_screen.rows - was_scroll; + i < virtual_screen.rows; i++) + for (j = 0; j < virtual_screen.columns; j++) + paint_char (j, i); + + /* Draw cursor if visible. */ + if (virtual_screen.cursor_state) + draw_cursor (1); + + if (repaint_callback) + repaint_callback (window.x, window.y, window.width, window.height); } static void scroll_up (void) { unsigned int i; - grub_video_color_t color; - - /* If we don't have background bitmap, remove cursor. */ - if (!bitmap) - { - /* Remove cursor. */ - draw_cursor (0); - - /* Redraw only changed regions. */ - dirty_region_redraw (); - } /* Scroll text buffer with one line to up. */ grub_memmove (virtual_screen.text_buffer, @@ -584,32 +790,7 @@ scroll_up (void) i++) clear_char (&(virtual_screen.text_buffer[i])); - /* Scroll physical screen. */ - grub_video_set_active_render_target (text_layer); - color = virtual_screen.bg_color; - grub_video_scroll (color, 0, -virtual_screen.normal_char_height); - grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); - - /* If we have bitmap, re-draw screen, otherwise scroll physical screen too. */ - if (bitmap) - { - /* Mark virtual screen to be redrawn. */ - dirty_region_add_virtualscreen (); - } - else - { - /* Clear new border area. */ - grub_video_fill_rect (color, - virtual_screen.offset_x, virtual_screen.offset_y, - virtual_screen.width, virtual_screen.normal_char_height); - - /* Scroll physical screen. */ - grub_video_scroll (color, 0, -virtual_screen.normal_char_height); - - /* Draw cursor if visible. */ - if (virtual_screen.cursor_state) - draw_cursor (1); - } + virtual_screen.total_scroll++; } static void @@ -812,12 +993,14 @@ grub_gfxterm_cls (void) /* Clear text layer. */ grub_video_set_active_render_target (text_layer); color = virtual_screen.bg_color; - grub_video_fill_rect (color, 0, 0, virtual_screen.width, - virtual_screen.height); - grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + grub_video_fill_rect (color, 0, 0, + virtual_screen.width, virtual_screen.height); + grub_video_set_active_render_target (render_target); /* Mark virtual screen to be redrawn. */ dirty_region_add_virtualscreen (); + + grub_gfxterm_refresh (); } static void @@ -878,15 +1061,41 @@ grub_gfxterm_setcursor (int on) static void grub_gfxterm_refresh (void) { + real_scroll (); + /* Redraw only changed regions. */ dirty_region_redraw (); + + grub_video_swap_buffers (); + + if (window.double_repaint) + dirty_region_redraw (); + dirty_region_reset (); } +void +grub_gfxterm_set_repaint_callback (grub_gfxterm_repaint_callback_t func) +{ + repaint_callback = func; +} + +/* Option array indices. */ +#define BACKGROUND_CMD_ARGINDEX_MODE 0 + +static const struct grub_arg_option background_image_cmd_options[] = + { + {"mode", 'm', 0, "Background image mode.", "stretch|normal", + ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} + }; + static grub_err_t -grub_gfxterm_background_image_cmd (grub_command_t cmd __attribute__ ((unused)), +grub_gfxterm_background_image_cmd (grub_extcmd_t cmd __attribute__ ((unused)), int argc, char **args) { + struct grub_arg_list *state = cmd->state; + /* Check that we have video adapter active. */ if (grub_video_get_info(NULL) != GRUB_ERR_NONE) return grub_errno; @@ -898,8 +1107,7 @@ grub_gfxterm_background_image_cmd (grub_command_t cmd __attribute__ ((unused)), bitmap = 0; /* Mark whole screen as dirty. */ - dirty_region_reset (); - dirty_region_add (0, 0, mode_info.width, mode_info.height); + dirty_region_add (0, 0, window.width, window.height); } /* If filename was provided, try to load that. */ @@ -910,16 +1118,38 @@ grub_gfxterm_background_image_cmd (grub_command_t cmd __attribute__ ((unused)), if (grub_errno != GRUB_ERR_NONE) return grub_errno; + /* Determine if the bitmap should be scaled to fit the screen. */ + if (!state[BACKGROUND_CMD_ARGINDEX_MODE].set + || grub_strcmp (state[BACKGROUND_CMD_ARGINDEX_MODE].arg, + "stretch") == 0) + { + if (window.width != grub_video_bitmap_get_width (bitmap) + || window.height != grub_video_bitmap_get_height (bitmap)) + { + struct grub_video_bitmap *scaled_bitmap; + grub_video_bitmap_create_scaled (&scaled_bitmap, + window.width, + window.height, + bitmap, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + if (grub_errno == GRUB_ERR_NONE) + { + /* Replace the original bitmap with the scaled one. */ + grub_video_bitmap_destroy (bitmap); + bitmap = scaled_bitmap; + } + } + } + /* If bitmap was loaded correctly, display it. */ if (bitmap) { /* Determine bitmap dimensions. */ bitmap_width = grub_video_bitmap_get_width (bitmap); - bitmap_height = grub_video_bitmap_get_width (bitmap); + bitmap_height = grub_video_bitmap_get_height (bitmap); /* Mark whole screen as dirty. */ - dirty_region_reset (); - dirty_region_add (0, 0, mode_info.width, mode_info.height); + dirty_region_add (0, 0, window.width, window.height); } } @@ -931,8 +1161,8 @@ grub_gfxterm_background_image_cmd (grub_command_t cmd __attribute__ ((unused)), static struct grub_term_output grub_video_term = { .name = "gfxterm", - .init = grub_gfxterm_init, - .fini = grub_gfxterm_fini, + .init = grub_gfxterm_term_init, + .fini = grub_gfxterm_term_fini, .putchar = grub_gfxterm_putchar, .getcharwidth = grub_gfxterm_getcharwidth, .getwh = grub_virtual_screen_getwh, @@ -948,18 +1178,22 @@ static struct grub_term_output grub_video_term = .next = 0 }; -static grub_command_t cmd; +static grub_extcmd_t background_image_cmd_handle; -GRUB_MOD_INIT(term_gfxterm) +GRUB_MOD_INIT(gfxterm) { grub_term_register_output ("gfxterm", &grub_video_term); - cmd = grub_register_command ("background_image", - grub_gfxterm_background_image_cmd, - 0, "Load background image for active terminal."); + background_image_cmd_handle = + grub_register_extcmd ("background_image", + grub_gfxterm_background_image_cmd, + GRUB_COMMAND_FLAG_BOTH, + "[-m (stretch|normal)] FILE", + "Load background image for active terminal.", + background_image_cmd_options); } -GRUB_MOD_FINI(term_gfxterm) +GRUB_MOD_FINI(gfxterm) { - grub_unregister_command (cmd); + grub_unregister_extcmd (background_image_cmd_handle); grub_term_unregister_output (&grub_video_term); } diff --git a/term/i386/pc/vesafb.c b/term/i386/pc/vesafb.c deleted file mode 100644 index 52694ed10..000000000 --- a/term/i386/pc/vesafb.c +++ /dev/null @@ -1,606 +0,0 @@ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2007,2009 Free Software Foundation, Inc. - * - * GRUB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GRUB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GRUB. If not, see . - */ - -// TODO: Deprecated and broken. Scheduled for removal as there is VBE driver in Video subsystem. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEFAULT_CHAR_WIDTH 8 -#define DEFAULT_CHAR_HEIGHT 16 - -#define DEFAULT_FG_COLOR 0xa -#define DEFAULT_BG_COLOR 0x0 - -struct grub_colored_char -{ - /* An Unicode codepoint. */ - grub_uint32_t code; - - /* Color indexes. */ - unsigned char fg_color; - unsigned char bg_color; - - /* The width of this character minus one. */ - unsigned char width; - - /* The column index of this character. */ - unsigned char index; -}; - -struct grub_virtual_screen -{ - /* Dimensions of the virtual screen. */ - grub_uint32_t width; - grub_uint32_t height; - - /* Offset in the display. */ - grub_uint32_t offset_x; - grub_uint32_t offset_y; - - /* TTY Character sizes. */ - grub_uint32_t char_width; - grub_uint32_t char_height; - - /* Virtual screen TTY size. */ - grub_uint32_t columns; - grub_uint32_t rows; - - /* Current cursor details. */ - grub_uint32_t cursor_x; - grub_uint32_t cursor_y; - grub_uint8_t cursor_state; - grub_uint8_t fg_color; - grub_uint8_t bg_color; - - /* Text buffer for virtual screen. Contains (columns * rows) number - of entries. */ - struct grub_colored_char *text_buffer; -}; - -/* Make sure text buffer is not marked as allocated. */ -static struct grub_virtual_screen virtual_screen = - { - .text_buffer = 0 - }; - -static unsigned char *vga_font = 0; -static grub_uint32_t old_mode = 0; - -static struct grub_vbe_mode_info_block mode_info; -static grub_uint8_t *framebuffer = 0; -static grub_uint32_t bytes_per_scan_line = 0; - -static void -grub_virtual_screen_free (void) -{ - /* If virtual screen has been allocated, free it. */ - if (virtual_screen.text_buffer != 0) - grub_free (virtual_screen.text_buffer); - - /* Reset virtual screen data. */ - grub_memset (&virtual_screen, 0, sizeof (virtual_screen)); -} - -static grub_err_t -grub_virtual_screen_setup (grub_uint32_t width, - grub_uint32_t height) -{ - /* Free old virtual screen. */ - grub_virtual_screen_free (); - - /* Initialize with default data. */ - virtual_screen.width = width; - virtual_screen.height = height; - virtual_screen.offset_x = 0; - virtual_screen.offset_y = 0; - virtual_screen.char_width = DEFAULT_CHAR_WIDTH; - virtual_screen.char_height = DEFAULT_CHAR_HEIGHT; - virtual_screen.cursor_x = 0; - virtual_screen.cursor_y = 0; - virtual_screen.cursor_state = 1; - virtual_screen.fg_color = DEFAULT_FG_COLOR; - virtual_screen.bg_color = DEFAULT_BG_COLOR; - - /* Calculate size of text buffer. */ - virtual_screen.columns = virtual_screen.width / virtual_screen.char_width; - virtual_screen.rows = virtual_screen.height / virtual_screen.char_height; - - /* Allocate memory for text buffer. */ - virtual_screen.text_buffer = - (struct grub_colored_char *) grub_malloc (virtual_screen.columns - * virtual_screen.rows - * sizeof (*virtual_screen.text_buffer)); - - return grub_errno; -} - -static grub_err_t -grub_vesafb_mod_init (void) -{ - grub_uint32_t use_mode = GRUB_VBE_DEFAULT_VIDEO_MODE; - struct grub_vbe_info_block controller_info; - char *modevar; - - /* Use fonts from VGA bios. */ - vga_font = grub_vga_get_font (); - - /* Check if we have VESA BIOS installed. */ - if (grub_vbe_probe (&controller_info) != GRUB_ERR_NONE) - return grub_errno; - - /* Check existence of vbe_mode environment variable. */ - modevar = grub_env_get ("vbe_mode"); - - if (modevar != 0) - { - unsigned long value; - - value = grub_strtoul (modevar, 0, 0); - if (grub_errno == GRUB_ERR_NONE) - use_mode = value; - } - - /* Store initial video mode. */ - if (grub_vbe_get_video_mode (&old_mode) != GRUB_ERR_NONE) - return grub_errno; - - /* Setup desired graphics mode. */ - if (grub_vbe_set_video_mode (use_mode, &mode_info) != GRUB_ERR_NONE) - return grub_errno; - - /* Determine framebuffer and bytes per scan line. */ - framebuffer = (grub_uint8_t *) mode_info.phys_base_addr; - - if (controller_info.version >= 0x300) - bytes_per_scan_line = mode_info.lin_bytes_per_scan_line; - else - bytes_per_scan_line = mode_info.bytes_per_scan_line; - - /* Create virtual screen. */ - if (grub_virtual_screen_setup (mode_info.x_resolution, - mode_info.y_resolution) != GRUB_ERR_NONE) - { - grub_vbe_set_video_mode (old_mode, 0); - return grub_errno; - } - - /* Make sure frame buffer is black. */ - grub_memset (framebuffer, - 0, - bytes_per_scan_line * mode_info.y_resolution); - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_vesafb_mod_fini (void) -{ - grub_virtual_screen_free (); - - grub_vbe_set_video_mode (old_mode, 0); - - return GRUB_ERR_NONE; -} - -static int -grub_virtual_screen_get_glyph (grub_uint32_t code, - unsigned char bitmap[32], - unsigned *width) -{ - if (code > 0x7f) - { - /* Map some unicode characters to the VGA font, if possible. */ - switch (code) - { - case 0x2190: /* left arrow */ - code = 0x1b; - break; - case 0x2191: /* up arrow */ - code = 0x18; - break; - case 0x2192: /* right arrow */ - code = 0x1a; - break; - case 0x2193: /* down arrow */ - code = 0x19; - break; - case 0x2501: /* horizontal line */ - code = 0xc4; - break; - case 0x2503: /* vertical line */ - code = 0xb3; - break; - case 0x250F: /* upper-left corner */ - code = 0xda; - break; - case 0x2513: /* upper-right corner */ - code = 0xbf; - break; - case 0x2517: /* lower-left corner */ - code = 0xc0; - break; - case 0x251B: /* lower-right corner */ - code = 0xd9; - break; - - default: - return grub_font_get_glyph_any (code, bitmap, width); - } - } - - /* TODO This is wrong for the new font module. Should it be fixed? */ - if (bitmap) - grub_memcpy (bitmap, - vga_font + code * virtual_screen.char_height, - virtual_screen.char_height); - *width = 1; - return 1; -} - -static void -grub_virtual_screen_invalidate_char (struct grub_colored_char *p) -{ - p->code = 0xFFFF; - - if (p->width) - { - struct grub_colored_char *q; - - for (q = p + 1; q <= p + p->width; q++) - { - q->code = 0xFFFF; - q->width = 0; - q->index = 0; - } - } - - p->width = 0; -} - -static void -write_char (void) -{ - struct grub_colored_char *p; - unsigned char bitmap[32]; - unsigned width; - unsigned y; - unsigned offset; - - p = (virtual_screen.text_buffer - + virtual_screen.cursor_x - + (virtual_screen.cursor_y * virtual_screen.columns)); - - p -= p->index; - - if (! grub_virtual_screen_get_glyph (p->code, bitmap, &width)) - { - grub_virtual_screen_invalidate_char (p); - width = 0; - } - - for (y = 0, offset = 0; - y < virtual_screen.char_height; - y++, offset++) - { - unsigned i; - - for (i = 0; - (i < width * virtual_screen.char_width) && (offset < 32); - i++) - { - unsigned char color; - - if (bitmap[offset] & (1 << (8-i))) - { - color = p->fg_color; - } - else - { - color = p->bg_color; - } - - grub_vbe_set_pixel_index(i + (virtual_screen.cursor_x - * virtual_screen.char_width), - y + (virtual_screen.cursor_y - * virtual_screen.char_height), - color); - } - } -} - -static void -write_cursor (void) -{ - grub_uint32_t x; - grub_uint32_t y; - - for (y = ((virtual_screen.cursor_y + 1) * virtual_screen.char_height) - 3; - y < ((virtual_screen.cursor_y + 1) * virtual_screen.char_height) - 1; - y++) - { - for (x = virtual_screen.cursor_x * virtual_screen.char_width; - x < (virtual_screen.cursor_x + 1) * virtual_screen.char_width; - x++) - { - grub_vbe_set_pixel_index(x, y, 10); - } - } -} - -static void -scroll_up (void) -{ - grub_uint32_t i; - - /* Scroll text buffer with one line to up. */ - grub_memmove (virtual_screen.text_buffer, - virtual_screen.text_buffer + virtual_screen.columns, - sizeof (*virtual_screen.text_buffer) - * virtual_screen.columns - * (virtual_screen.rows - 1)); - - /* Clear last line in text buffer. */ - for (i = virtual_screen.columns * (virtual_screen.rows - 1); - i < virtual_screen.columns * virtual_screen.rows; - i++) - { - virtual_screen.text_buffer[i].code = ' '; - virtual_screen.text_buffer[i].fg_color = 0; - virtual_screen.text_buffer[i].bg_color = 0; - virtual_screen.text_buffer[i].width = 0; - virtual_screen.text_buffer[i].index = 0; - } - - /* Scroll framebuffer with one line to up. */ - grub_memmove (framebuffer, - framebuffer - + bytes_per_scan_line * virtual_screen.char_height, - bytes_per_scan_line - * (mode_info.y_resolution - virtual_screen.char_height)); - - /* Clear last line in framebuffer. */ - grub_memset (framebuffer - + (bytes_per_scan_line - * (mode_info.y_resolution - virtual_screen.char_height)), - 0, - bytes_per_scan_line * virtual_screen.char_height); -} - -static void -grub_vesafb_putchar (grub_uint32_t c) -{ - if (c == '\a') - /* FIXME */ - return; - - if (c == '\b' || c == '\n' || c == '\r') - { - /* Erase current cursor, if any. */ - if (virtual_screen.cursor_state) - write_char (); - - switch (c) - { - case '\b': - if (virtual_screen.cursor_x > 0) - virtual_screen.cursor_x--; - break; - - case '\n': - if (virtual_screen.cursor_y >= virtual_screen.rows - 1) - scroll_up (); - else - virtual_screen.cursor_y++; - break; - - case '\r': - virtual_screen.cursor_x = 0; - break; - } - - if (virtual_screen.cursor_state) - write_cursor (); - } - else - { - unsigned width; - struct grub_colored_char *p; - - grub_virtual_screen_get_glyph (c, 0, &width); - - if (virtual_screen.cursor_x + width > virtual_screen.columns) - grub_putchar ('\n'); - - p = (virtual_screen.text_buffer + - virtual_screen.cursor_x + - virtual_screen.cursor_y * virtual_screen.columns); - p->code = c; - p->fg_color = virtual_screen.fg_color; - p->bg_color = virtual_screen.bg_color; - p->width = width - 1; - p->index = 0; - - if (width > 1) - { - unsigned i; - - for (i = 1; i < width; i++) - { - p[i].code = ' '; - p[i].width = width - 1; - p[i].index = i; - } - } - - write_char (); - - virtual_screen.cursor_x += width; - if (virtual_screen.cursor_x >= virtual_screen.columns) - { - virtual_screen.cursor_x = 0; - - if (virtual_screen.cursor_y >= virtual_screen.rows - 1) - scroll_up (); - else - virtual_screen.cursor_y++; - } - - if (virtual_screen.cursor_state) - write_cursor (); - } -} - -static grub_ssize_t -grub_vesafb_getcharwidth (grub_uint32_t c) -{ - unsigned width; - - if (! grub_virtual_screen_get_glyph (c, 0, &width)) - return 0; - - return width; -} - -static grub_uint16_t -grub_virtual_screen_getwh (void) -{ - return (virtual_screen.columns << 8) | virtual_screen.rows; -} - -static grub_uint16_t -grub_virtual_screen_getxy (void) -{ - return ((virtual_screen.cursor_x << 8) | virtual_screen.cursor_y); -} - -static void -grub_vesafb_gotoxy (grub_uint8_t x, grub_uint8_t y) -{ - if (x >= virtual_screen.columns || y >= virtual_screen.rows) - { - grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid point (%u,%u)", - (unsigned) x, (unsigned) y); - return; - } - - if (virtual_screen.cursor_state) - write_char (); - - virtual_screen.cursor_x = x; - virtual_screen.cursor_y = y; - - if (virtual_screen.cursor_state) - write_cursor (); -} - -static void -grub_virtual_screen_cls (void) -{ - grub_uint32_t i; - - for (i = 0; i < virtual_screen.columns * virtual_screen.rows; i++) - { - virtual_screen.text_buffer[i].code = ' '; - virtual_screen.text_buffer[i].fg_color = 0; - virtual_screen.text_buffer[i].bg_color = 0; - virtual_screen.text_buffer[i].width = 0; - virtual_screen.text_buffer[i].index = 0; - } - - virtual_screen.cursor_x = virtual_screen.cursor_y = 0; -} - -static void -grub_vesafb_cls (void) -{ - grub_virtual_screen_cls (); - - grub_memset (framebuffer, - 0, - mode_info.y_resolution * bytes_per_scan_line); -} - -static void -grub_virtual_screen_setcolorstate (grub_term_color_state state) -{ - switch (state) - { - case GRUB_TERM_COLOR_STANDARD: - case GRUB_TERM_COLOR_NORMAL: - virtual_screen.fg_color = DEFAULT_FG_COLOR; - virtual_screen.bg_color = DEFAULT_BG_COLOR; - break; - case GRUB_TERM_COLOR_HIGHLIGHT: - virtual_screen.fg_color = DEFAULT_BG_COLOR; - virtual_screen.bg_color = DEFAULT_FG_COLOR; - break; - default: - break; - } -} - -static void -grub_vesafb_setcursor (int on) -{ - if (virtual_screen.cursor_state != on) - { - if (virtual_screen.cursor_state) - write_char (); - else - write_cursor (); - - virtual_screen.cursor_state = on; - } -} - -static struct grub_term_output grub_vesafb_term = - { - .name = "vesafb", - .init = grub_vesafb_mod_init, - .fini = grub_vesafb_mod_fini, - .putchar = grub_vesafb_putchar, - .getcharwidth = grub_vesafb_getcharwidth, - .getwh = grub_virtual_screen_getwh, - .getxy = grub_virtual_screen_getxy, - .gotoxy = grub_vesafb_gotoxy, - .cls = grub_vesafb_cls, - .setcolorstate = grub_virtual_screen_setcolorstate, - .setcursor = grub_vesafb_setcursor, - .flags = 0, - }; - -GRUB_MOD_INIT(vesafb) -{ - grub_term_register_output ("vesafb", &grub_vesafb_term); -} - -GRUB_MOD_FINI(vesafb) -{ - grub_term_unregister_output (&grub_vesafb_term); -} diff --git a/term/ieee1275/ofconsole.c b/term/ieee1275/ofconsole.c index dd4270eff..c0f895a15 100644 --- a/term/ieee1275/ofconsole.c +++ b/term/ieee1275/ofconsole.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -77,7 +78,52 @@ grub_ofconsole_writeesc (const char *str) static void grub_ofconsole_putchar (grub_uint32_t c) { - char chr = c; + char chr; + + if (c > 0x7F) + { + /* Better than nothing. */ + switch (c) + { + case GRUB_TERM_DISP_LEFT: + c = '<'; + break; + + case GRUB_TERM_DISP_UP: + c = '^'; + break; + + case GRUB_TERM_DISP_RIGHT: + c = '>'; + break; + + case GRUB_TERM_DISP_DOWN: + c = 'v'; + break; + + case GRUB_TERM_DISP_HLINE: + c = '-'; + break; + + case GRUB_TERM_DISP_VLINE: + c = '|'; + break; + + case GRUB_TERM_DISP_UL: + case GRUB_TERM_DISP_UR: + case GRUB_TERM_DISP_LL: + case GRUB_TERM_DISP_LR: + c = '+'; + break; + + default: + c = '?'; + break; + } + } + + chr = c; + if (c == '\n') { grub_curr_y++; @@ -109,7 +155,7 @@ grub_ofconsole_getcharwidth (grub_uint32_t c __attribute__((unused))) static void grub_ofconsole_setcolorstate (grub_term_color_state state) { - char *setcol; + char setcol[256]; int fg; int bg; @@ -128,10 +174,8 @@ grub_ofconsole_setcolorstate (grub_term_color_state state) return; } - setcol = grub_xasprintf ("\e[3%dm\e[4%dm", fg, bg); - if (setcol) - grub_ofconsole_writeesc (setcol); - grub_free (setcol); + grub_snprintf (setcol, sizeof (setcol), "\e[3%dm\e[4%dm", fg, bg); + grub_ofconsole_writeesc (setcol); } static void @@ -158,42 +202,81 @@ grub_ofconsole_readkey (int *key) grub_ieee1275_read (stdin_ihandle, &c, 1, &actual); - if (actual > 0 && c == '\e') - { - grub_ieee1275_read (stdin_ihandle, &c, 1, &actual); - if (actual <= 0) + if (actual > 0) + switch(c) + { + case 0x7f: + /* Backspace: Ctrl-h. */ + c = '\b'; + break; + case '\e': { - *key = '\e'; - return 1; + grub_uint64_t start; + grub_ieee1275_read (stdin_ihandle, &c, 1, &actual); + + /* On 9600 we have to wait up to 12 milliseconds. */ + start = grub_get_time_ms (); + while (actual <= 0 && grub_get_time_ms () - start < 12) + grub_ieee1275_read (stdin_ihandle, &c, 1, &actual); + + if (actual <= 0) + { + *key = '\e'; + return 1; + } + + if (c != '[') + return 0; + + grub_ieee1275_read (stdin_ihandle, &c, 1, &actual); + + /* On 9600 we have to wait up to 12 milliseconds. */ + start = grub_get_time_ms (); + while (actual <= 0 && grub_get_time_ms () - start < 12) + grub_ieee1275_read (stdin_ihandle, &c, 1, &actual); + if (actual <= 0) + return 0; + + switch (c) + { + case 'A': + /* Up: Ctrl-p. */ + c = GRUB_TERM_UP; + break; + case 'B': + /* Down: Ctrl-n. */ + c = GRUB_TERM_DOWN; + break; + case 'C': + /* Right: Ctrl-f. */ + c = GRUB_TERM_RIGHT; + break; + case 'D': + /* Left: Ctrl-b. */ + c = GRUB_TERM_LEFT; + break; + case '3': + { + grub_ieee1275_read (stdin_ihandle, &c, 1, &actual); + /* On 9600 we have to wait up to 12 milliseconds. */ + start = grub_get_time_ms (); + while (actual <= 0 && grub_get_time_ms () - start < 12) + grub_ieee1275_read (stdin_ihandle, &c, 1, &actual); + + if (actual <= 0) + return 0; + + /* Delete: Ctrl-d. */ + if (c == '~') + c = GRUB_TERM_DC; + else + return 0; + break; + } + break; + } } - - if (c != 91) - return 0; - - grub_ieee1275_read (stdin_ihandle, &c, 1, &actual); - if (actual <= 0) - return 0; - - switch (c) - { - case 65: - /* Up: Ctrl-p. */ - c = 16; - break; - case 66: - /* Down: Ctrl-n. */ - c = 14; - break; - case 67: - /* Right: Ctrl-f. */ - c = 6; - break; - case 68: - /* Left: Ctrl-b. */ - c = 2; - break; - } - } + } *key = c; return actual > 0; @@ -245,37 +328,28 @@ static void grub_ofconsole_dimensions (void) { grub_ieee1275_ihandle_t options; - char *val; grub_ssize_t lval; if (! grub_ieee1275_finddevice ("/options", &options) && options != (grub_ieee1275_ihandle_t) -1) { if (! grub_ieee1275_get_property_length (options, "screen-#columns", - &lval) && lval != -1) + &lval) + && lval >= 0 && lval < 1024) { - val = grub_malloc (lval); - if (val) - { - if (! grub_ieee1275_get_property (options, "screen-#columns", - val, lval, 0)) - grub_ofconsole_width = (grub_uint8_t) grub_strtoul (val, 0, 10); + char val[lval]; - grub_free (val); - } + if (! grub_ieee1275_get_property (options, "screen-#columns", + val, lval, 0)) + grub_ofconsole_width = (grub_uint8_t) grub_strtoul (val, 0, 10); } - if (! grub_ieee1275_get_property_length (options, "screen-#rows", - &lval) && lval != -1) + if (! grub_ieee1275_get_property_length (options, "screen-#rows", &lval) + && lval >= 0 && lval < 1024) { - val = grub_malloc (lval); - if (val) - { - if (! grub_ieee1275_get_property (options, "screen-#rows", - val, lval, 0)) - grub_ofconsole_height = (grub_uint8_t) grub_strtoul (val, 0, 10); - - grub_free (val); - } + char val[lval]; + if (! grub_ieee1275_get_property (options, "screen-#rows", + val, lval, 0)) + grub_ofconsole_height = (grub_uint8_t) grub_strtoul (val, 0, 10); } } @@ -297,14 +371,12 @@ grub_ofconsole_gotoxy (grub_uint8_t x, grub_uint8_t y) { if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_ANSI)) { - char *s; + char s[256]; grub_curr_x = x; grub_curr_y = y; - s = grub_xasprintf ("\e[%d;%dH", y + 1, x + 1); - if (s) - grub_ofconsole_writeesc (s); - grub_free (s); + grub_snprintf (s, sizeof (s), "\e[%d;%dH", y + 1, x + 1); + grub_ofconsole_writeesc (s); } else { diff --git a/term/serial.c b/term/serial.c index 62cd11fee..05497ce40 100644 --- a/term/serial.c +++ b/term/serial.c @@ -232,7 +232,12 @@ serial_get_divisor (unsigned int speed) /* Set the baud rate. */ for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++) if (divisor_tab[i].speed == speed) + /* UART in Yeeloong runs twice the usual rate. */ +#ifdef GRUB_MACHINE_MIPS_YEELOONG + return 2 * divisor_tab[i].div; +#else return divisor_tab[i].div; +#endif return 0; } @@ -292,11 +297,14 @@ serial_hw_init (void) | serial_settings.stop_bits); grub_outb (status, serial_settings.port + UART_LCR); + /* In Yeeloong serial port has only 3 wires. */ +#ifndef GRUB_MACHINE_MIPS_YEELOONG /* Enable the FIFO. */ grub_outb (UART_ENABLE_FIFO, serial_settings.port + UART_FCR); /* Turn on DTR, RTS, and OUT2. */ grub_outb (UART_ENABLE_MODEM, serial_settings.port + UART_MCR); +#endif /* Drain the input buffer. */ while (grub_serial_checkkey () != -1) @@ -613,7 +621,11 @@ GRUB_MOD_INIT(serial) /* Set default settings. */ serial_settings.port = serial_hw_get_port (0); +#ifdef GRUB_MACHINE_MIPS_YEELOONG + serial_settings.divisor = serial_get_divisor (115200); +#else serial_settings.divisor = serial_get_divisor (9600); +#endif serial_settings.word_len = UART_8BITS_WORD; serial_settings.parity = UART_NO_PARITY; serial_settings.stop_bits = UART_1_STOP_BIT; diff --git a/tests/example_functional_test.c b/tests/example_functional_test.c index f43c0f1ce..6802d2d53 100644 --- a/tests/example_functional_test.c +++ b/tests/example_functional_test.c @@ -28,7 +28,7 @@ example_test (void) /* Check if 1st argument is true and report with custom error message. */ grub_test_assert (2 == 2, "2 equal 2 expected"); - grub_test_assert (2 == 3, "2 is not equal to %d", 3); + grub_test_assert (2 != 3, "2 matches %d", 3); } /* Register example_test method as a functional test. */ diff --git a/tests/example_unit_test.c b/tests/example_unit_test.c index 4999f1412..d721a9d0a 100644 --- a/tests/example_unit_test.c +++ b/tests/example_unit_test.c @@ -31,7 +31,7 @@ example_test (void) /* Check if 1st argument is true and report with custom error message. */ grub_test_assert (2 == 2, "2 equal 2 expected"); - grub_test_assert (2 == 3, "2 is not equal to %d", 3); + grub_test_assert (2 != 3, "2 matches %d", 3); } /* Register example_test method as a unit test. */ diff --git a/tests/grub_script_blanklines.in b/tests/grub_script_blanklines.in new file mode 100644 index 000000000..71b869bd3 --- /dev/null +++ b/tests/grub_script_blanklines.in @@ -0,0 +1,14 @@ +#! /bin/sh -e + +@builddir@/grub-script-check <. + +foo=bar +echo $foo ${foo} +echo "$foo" "${foo}" +echo '$foo' '${foo}' +echo a$foob a${foo}b +echo ab"cd"ef$foo'gh'ij${foo}kl\ mn\"op\'qr\$st\(uv\yz\) + +foo=c +bar=h +echo e"$foo"${bar}o +e"$foo"${bar}o hello world + +foo=echo +$foo 1234 diff --git a/tests/grub_script_echo_keywords.in b/tests/grub_script_echo_keywords.in new file mode 100644 index 000000000..a6383f0e2 --- /dev/null +++ b/tests/grub_script_echo_keywords.in @@ -0,0 +1,3 @@ +#! @builddir@/grub-shell-tester + +echo if then else fi for do done diff --git a/tests/grub_script_final_semicolon.in b/tests/grub_script_final_semicolon.in new file mode 100644 index 000000000..99e55e545 --- /dev/null +++ b/tests/grub_script_final_semicolon.in @@ -0,0 +1,10 @@ +#! /bin/sh -e + +@builddir@/grub-script-check <. + +var=foo +echo $var +echo "$var" +echo ${var} +echo "${var}" + +echo $1 $2 $? + +foo=foo +echo "" $foo + +echo $bar $foo + +bar="" +echo $bar $foo + diff --git a/tests/grub_script_while1.in b/tests/grub_script_while1.in new file mode 100644 index 000000000..554247f76 --- /dev/null +++ b/tests/grub_script_while1.in @@ -0,0 +1,32 @@ +#! @builddir@/grub-shell-tester + +echo one +foo="" +while test "$foo" != "1111"; do foo="${foo}1"; echo "$foo"; done + +echo two +foo="" +while test "$foo" != "aaaa" +do + foo="${foo}a" + echo $foo +done + +foo="" +until test "$foo" = "1111"; do foo="${foo}1"; echo $foo; done +foo="" +until test "$foo" = "aaaa" +do + foo="${foo}a" + echo $foo +done + +# check "$?" in condition gets its value from while body commands +foo="" +false +while test "$?" != "0" +do + echo $foo + foo="${foo}1" + test "$foo" = "111111" +done diff --git a/tests/util/grub-shell-tester.in b/tests/util/grub-shell-tester.in index 6ed4ebcac..e9507c8f5 100644 --- a/tests/util/grub-shell-tester.in +++ b/tests/util/grub-shell-tester.in @@ -1,7 +1,7 @@ #! /bin/bash -e # Compares GRUB script output with BASH output. -# Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. +# Copyright (C) 2009,2010 Free Software Foundation, Inc. # # GRUB is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index ee0cded55..5726ec0d8 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -1,7 +1,7 @@ #! /bin/bash -e # Run GRUB script in a Qemu instance -# Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. +# Copyright (C) 2009,2010 Free Software Foundation, Inc. # # GRUB is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -55,7 +55,7 @@ Report bugs to . EOF } -boot=bios-hd +boot=hd qemu=qemu-system-i386 # Check the arguments. @@ -80,9 +80,9 @@ for option in "$@"; do qemuopts="$qemuopts $qs" ;; --boot=*) dev=`echo "$option" | sed -e 's/--boot=//'` - if [ "$dev" = "bios-fd" ] ; then boot=bios-fd; - elif [ "$dev" = "bios-hd" ] ; then boot=bios-hd; - elif [ "$dev" = "bios-cd" ] ; then boot=bios-cd; + if [ "$dev" = "fd" ] ; then boot=fd; + elif [ "$dev" = "hd" ] ; then boot=hd; + elif [ "$dev" = "cd" ] ; then boot=cd; else echo "Unrecognized boot method \`$dev'" 1>&2 usage @@ -129,26 +129,24 @@ source /boot/grub/testcase.cfg halt EOF -if [ x$boot = xbios-hd ] || [ x$boot = xbios-fd ] || [ x$boot = xbios-cd ]; then - isofile=`mktemp` - grub-mkrescue --output=${isofile} --override-directory=${builddir} \ - /boot/grub/grub.cfg=${cfgfile} /boot/grub/testcase.cfg=${source} \ - ${files} >/dev/null 2>&1 - if [ x$boot = xbios-hd ]; then - device=hda - bootdev=c - fi - if [ x$boot = xbios-cd ]; then - device=cdrom - bootdev=d - fi - if [ x$boot = xbios-fd ]; then - device=fda - bootdev=a - fi - ${qemu} ${qemuopts} -nographic -serial file:/dev/stdout -monitor file:/dev/null -${device} ${isofile} -boot ${bootdev} | tr -d "\r" - rm -f ${isofile} +isofile=`mktemp` +grub-mkrescue --output=${isofile} --override-directory=${builddir} \ + /boot/grub/grub.cfg=${cfgfile} /boot/grub/testcase.cfg=${source} \ + ${files} >/dev/null 2>&1 +if [ x$boot = xhd ]; then + device=hda + bootdev=c fi +if [ x$boot = xcd ]; then + device=cdrom + bootdev=d +fi +if [ x$boot = xfd ]; then + device=fda + bootdev=a +fi +${qemu} ${qemuopts} -nographic -serial file:/dev/stdout -monitor file:/dev/null -${device} ${isofile} -boot ${bootdev} | tr -d "\r" +rm -f ${isofile} rm -f ${tmpfile} ${cfgfile} exit 0 diff --git a/util/bin2h.c b/util/bin2h.c index 5ce47f086..e81ede8c6 100644 --- a/util/bin2h.c +++ b/util/bin2h.c @@ -40,6 +40,8 @@ usage (int status) else printf ("\ Usage: %s [OPTIONS] SYMBOL-NAME\n\ +\n\ +Convert a binary file to a C header.\n\ \n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ diff --git a/util/elf/grub-mkimage.c b/util/elf/grub-mkimage.c deleted file mode 100644 index 04a19bc4e..000000000 --- a/util/elf/grub-mkimage.c +++ /dev/null @@ -1,462 +0,0 @@ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. - * - * GRUB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GRUB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GRUB. If not, see . - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "progname.h" - -#define GRUB_IEEE1275_NOTE_NAME "PowerPC" -#define GRUB_IEEE1275_NOTE_TYPE 0x1275 - -/* These structures are defined according to the CHRP binding to IEEE1275, - "Client Program Format" section. */ - -struct grub_ieee1275_note_hdr -{ - grub_uint32_t namesz; - grub_uint32_t descsz; - grub_uint32_t type; - char name[sizeof (GRUB_IEEE1275_NOTE_NAME)]; -}; - -struct grub_ieee1275_note_desc -{ - grub_uint32_t real_mode; - grub_uint32_t real_base; - grub_uint32_t real_size; - grub_uint32_t virt_base; - grub_uint32_t virt_size; - grub_uint32_t load_base; -}; - -struct grub_ieee1275_note -{ - struct grub_ieee1275_note_hdr header; - struct grub_ieee1275_note_desc descriptor; -}; - -void -load_note (Elf32_Phdr *phdr, FILE *out) -{ - struct grub_ieee1275_note note; - int note_size = sizeof (struct grub_ieee1275_note); - - grub_util_info ("adding CHRP NOTE segment"); - - note.header.namesz = grub_host_to_target32 (sizeof (GRUB_IEEE1275_NOTE_NAME)); - note.header.descsz = grub_host_to_target32 (note_size); - note.header.type = grub_host_to_target32 (GRUB_IEEE1275_NOTE_TYPE); - strcpy (note.header.name, GRUB_IEEE1275_NOTE_NAME); - note.descriptor.real_mode = grub_host_to_target32 (0xffffffff); - note.descriptor.real_base = grub_host_to_target32 (0x00c00000); - note.descriptor.real_size = grub_host_to_target32 (0xffffffff); - note.descriptor.virt_base = grub_host_to_target32 (0xffffffff); - note.descriptor.virt_size = grub_host_to_target32 (0xffffffff); - note.descriptor.load_base = grub_host_to_target32 (0x00004000); - - /* Write the note data to the new segment. */ - grub_util_write_image_at (¬e, note_size, - grub_target_to_host32 (phdr->p_offset), out); - - /* Fill in the rest of the segment header. */ - phdr->p_type = grub_host_to_target32 (PT_NOTE); - phdr->p_flags = grub_host_to_target32 (PF_R); - phdr->p_align = grub_host_to_target32 (GRUB_TARGET_SIZEOF_LONG); - phdr->p_vaddr = 0; - phdr->p_paddr = 0; - phdr->p_filesz = grub_host_to_target32 (note_size); - phdr->p_memsz = 0; -} - -void -load_modules (grub_addr_t modbase, Elf32_Phdr *phdr, const char *dir, - char *mods[], FILE *out, char *memdisk_path, char *config_path) -{ - char *module_img; - struct grub_util_path_list *path_list; - struct grub_util_path_list *p; - struct grub_module_info *modinfo; - size_t offset; - size_t total_module_size; - size_t memdisk_size = 0; - size_t config_size = 0; - - path_list = grub_util_resolve_dependencies (dir, "moddep.lst", mods); - - offset = sizeof (struct grub_module_info); - total_module_size = sizeof (struct grub_module_info); - - if (memdisk_path) - { - memdisk_size = ALIGN_UP(grub_util_get_image_size (memdisk_path), 512); - grub_util_info ("the size of memory disk is 0x%x", memdisk_size); - total_module_size += memdisk_size + sizeof (struct grub_module_header); - } - - if (config_path) - { - config_size = ALIGN_UP(grub_util_get_image_size (config_path), 512); - grub_util_info ("the size of memory disk is 0x%x", config_size); - total_module_size += config_size + sizeof (struct grub_module_header); - } - - for (p = path_list; p; p = p->next) - { - total_module_size += (grub_util_get_image_size (p->name) - + sizeof (struct grub_module_header)); - } - - grub_util_info ("the total module size is 0x%x", total_module_size); - - module_img = xmalloc (total_module_size); - modinfo = (struct grub_module_info *) module_img; - modinfo->magic = grub_host_to_target32 (GRUB_MODULE_MAGIC); - modinfo->offset = grub_host_to_target32 (sizeof (struct grub_module_info)); - modinfo->size = grub_host_to_target32 (total_module_size); - - /* Load all the modules, with headers, into module_img. */ - for (p = path_list; p; p = p->next) - { - struct grub_module_header *header; - size_t mod_size; - - grub_util_info ("adding module %s", p->name); - - mod_size = grub_util_get_image_size (p->name); - - header = (struct grub_module_header *) (module_img + offset); - header->type = OBJ_TYPE_ELF; - header->size = grub_host_to_target32 (mod_size + sizeof (*header)); - - grub_util_load_image (p->name, module_img + offset + sizeof (*header)); - - offset += sizeof (*header) + mod_size; - } - - if (memdisk_path) - { - struct grub_module_header *header; - - header = (struct grub_module_header *) (module_img + offset); - header->type = OBJ_TYPE_MEMDISK; - header->size = grub_host_to_target32 (memdisk_size + sizeof (*header)); - offset += sizeof (*header); - - grub_util_load_image (memdisk_path, module_img + offset); - offset += memdisk_size; - } - - if (config_path) - { - struct grub_module_header *header; - - header = (struct grub_module_header *) (module_img + offset); - header->type = OBJ_TYPE_CONFIG; - header->size = grub_host_to_target32 (config_size + sizeof (*header)); - offset += sizeof (*header); - - grub_util_load_image (config_path, module_img + offset); - offset += config_size; - } - - - /* Write the module data to the new segment. */ - grub_util_write_image_at (module_img, total_module_size, - grub_host_to_target32 (phdr->p_offset), out); - - /* Fill in the rest of the segment header. */ - phdr->p_type = grub_host_to_target32 (PT_LOAD); - phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X); - phdr->p_align = grub_host_to_target32 (GRUB_TARGET_SIZEOF_LONG); - phdr->p_vaddr = grub_host_to_target32 (modbase); - phdr->p_paddr = grub_host_to_target32 (modbase); - phdr->p_filesz = grub_host_to_target32 (total_module_size); - phdr->p_memsz = grub_host_to_target32 (total_module_size); -} - -void -add_segments (char *dir, char *prefix, FILE *out, int chrp, char *mods[], char *memdisk_path, char *config_path) -{ - Elf32_Ehdr ehdr; - Elf32_Phdr *phdrs = NULL; - Elf32_Phdr *phdr; - FILE *in; - char *kernel_path; - grub_addr_t grub_end = 0; - off_t offset, first_segment; - int i, phdr_size; - - /* Read ELF header. */ - kernel_path = grub_util_get_path (dir, "kernel.img"); - in = fopen (kernel_path, "rb"); - if (! in) - grub_util_error ("cannot open %s", kernel_path); - - grub_util_read_at (&ehdr, sizeof (ehdr), 0, in); - - offset = ALIGN_UP (sizeof (ehdr), GRUB_TARGET_SIZEOF_LONG); - ehdr.e_phoff = grub_host_to_target32 (offset); - - phdr_size = (grub_target_to_host16 (ehdr.e_phentsize) * - grub_target_to_host16 (ehdr.e_phnum)); - - if (mods[0] != NULL) - phdr_size += grub_target_to_host16 (ehdr.e_phentsize); - - if (chrp) - phdr_size += grub_target_to_host16 (ehdr.e_phentsize); - - phdrs = xmalloc (phdr_size); - offset += ALIGN_UP (phdr_size, GRUB_TARGET_SIZEOF_LONG); - - first_segment = offset; - - /* Copy all existing segments. */ - for (i = 0; i < grub_target_to_host16 (ehdr.e_phnum); i++) - { - char *segment_img; - grub_size_t segment_end; - - phdr = phdrs + i; - - /* Read segment header. */ - grub_util_read_at (phdr, sizeof (Elf32_Phdr), - (grub_target_to_host32 (ehdr.e_phoff) - + (i * grub_target_to_host16 (ehdr.e_phentsize))), - in); - grub_util_info ("copying segment %d, type %d", i, - grub_target_to_host32 (phdr->p_type)); - - /* Locate _end. */ - segment_end = grub_target_to_host32 (phdr->p_paddr) - + grub_target_to_host32 (phdr->p_memsz); - grub_util_info ("segment %u end 0x%lx", i, segment_end); - if (segment_end > grub_end) - grub_end = segment_end; - - /* Read segment data and write it to new file. */ - segment_img = xmalloc (grub_target_to_host32 (phdr->p_filesz)); - - grub_util_read_at (segment_img, grub_target_to_host32 (phdr->p_filesz), - grub_target_to_host32 (phdr->p_offset), in); - - phdr->p_offset = grub_host_to_target32 (offset); - grub_util_write_image_at (segment_img, grub_target_to_host32 (phdr->p_filesz), - offset, out); - offset += ALIGN_UP (grub_target_to_host32 (phdr->p_filesz), - GRUB_TARGET_SIZEOF_LONG); - - free (segment_img); - } - - if (mods[0] != NULL) - { - grub_addr_t modbase; - - /* Place modules just after grub segment. */ - modbase = ALIGN_UP(grub_end + GRUB_MOD_GAP, GRUB_MOD_ALIGN); - - /* Construct new segment header for modules. */ - phdr = phdrs + grub_target_to_host16 (ehdr.e_phnum); - ehdr.e_phnum = grub_host_to_target16 (grub_target_to_host16 (ehdr.e_phnum) + 1); - - /* Fill in p_offset so the callees know where to write. */ - phdr->p_offset = grub_host_to_target32 (ALIGN_UP (grub_util_get_fp_size (out), - GRUB_TARGET_SIZEOF_LONG)); - - load_modules (modbase, phdr, dir, mods, out, memdisk_path, config_path); - } - - if (chrp) - { - /* Construct new segment header for the CHRP note. */ - phdr = phdrs + grub_target_to_host16 (ehdr.e_phnum); - ehdr.e_phnum = grub_host_to_target16 (grub_target_to_host16 (ehdr.e_phnum) + 1); - - /* Fill in p_offset so the callees know where to write. */ - phdr->p_offset = grub_host_to_target32 (ALIGN_UP (grub_util_get_fp_size (out), - GRUB_TARGET_SIZEOF_LONG)); - - load_note (phdr, out); - } - - /* Don't bother preserving the section headers. */ - ehdr.e_shoff = 0; - ehdr.e_shnum = 0; - ehdr.e_shstrndx = 0; - - /* Write entire segment table to the file. */ - grub_util_write_image_at (phdrs, phdr_size, grub_target_to_host32 (ehdr.e_phoff), out); - - /* Write ELF header. */ - grub_util_write_image_at (&ehdr, sizeof (ehdr), 0, out); - - if (prefix) - { - if (GRUB_KERNEL_CPU_PREFIX + strlen (prefix) + 1 > GRUB_KERNEL_CPU_DATA_END) - grub_util_error ("prefix too long"); - grub_util_write_image_at (prefix, strlen (prefix) + 1, first_segment + GRUB_KERNEL_CPU_PREFIX, out); - } - - free (phdrs); - free (kernel_path); -} - -static struct option options[] = - { - {"directory", required_argument, 0, 'd'}, - {"prefix", required_argument, 0, 'p'}, - {"memdisk", required_argument, 0, 'm'}, - {"config", required_argument, 0, 'c'}, - {"output", required_argument, 0, 'o'}, - {"help", no_argument, 0, 'h'}, - {"note", no_argument, 0, 'n'}, - {"version", no_argument, 0, 'V'}, - {"verbose", no_argument, 0, 'v'}, - { 0, 0, 0, 0 }, - }; - -static void -usage (int status) -{ - if (status) - fprintf (stderr, "Try `%s --help' for more information.\n", program_name); - else - printf ("\ -Usage: %s -o FILE [OPTION]... [MODULES]\n\ -\n\ -Make a bootable image of GRUB.\n\ -\n\ - -d, --directory=DIR use images and modules under DIR [default=%s]\n\ - -p, --prefix=DIR set grub_prefix directory\n\ - -m, --memdisk=FILE embed FILE as a memdisk image\n\ - -c, --config=FILE embed FILE as boot config\n\ - -o, --output=FILE output a generated image to FILE\n\ - -h, --help display this message and exit\n\ - -n, --note add NOTE segment for CHRP Open Firmware\n\ - -V, --version print version information and exit\n\ - -v, --verbose print verbose messages\n\ -\n\ -Report bugs to <%s>.\n\ -", program_name, GRUB_LIBDIR, PACKAGE_BUGREPORT); - - exit (status); -} - -int -main (int argc, char *argv[]) -{ - FILE *fp; - char *output = NULL; - char *dir = NULL; - char *prefix = NULL; - char *memdisk = NULL; - char *config = NULL; - int chrp = 0; - - set_program_name (argv[0]); - - grub_util_init_nls (); - - while (1) - { - int c = getopt_long (argc, argv, "d:p:m:c:o:hVvn", options, 0); - if (c == -1) - break; - - switch (c) - { - case 'd': - if (dir) - free (dir); - dir = xstrdup (optarg); - break; - case 'p': - if (prefix) - free (prefix); - prefix = xstrdup (optarg); - break; - case 'm': - if (memdisk) - free (memdisk); - memdisk = xstrdup (optarg); - - if (prefix) - free (prefix); - prefix = xstrdup ("(memdisk)/boot/grub"); - - break; - case 'c': - if (config) - free (config); - config = xstrdup (optarg); - - break; - - case 'h': - usage (0); - break; - case 'n': - chrp = 1; - break; - case 'o': - if (output) - free (output); - output = xstrdup (optarg); - break; - case 'V': - printf ("grub-mkimage (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); - return 0; - case 'v': - verbosity++; - break; - default: - usage (1); - break; - } - } - - if (!output) - usage (1); - - fp = fopen (output, "wb"); - if (! fp) - grub_util_error ("cannot open %s", output); - - add_segments (dir ? : GRUB_LIBDIR, prefix, fp, chrp, argv + optind, memdisk, - config); - - fclose (fp); - - return 0; -} diff --git a/util/getroot.c b/util/getroot.c index 6357c8a26..94eadc5e1 100644 --- a/util/getroot.c +++ b/util/getroot.c @@ -264,10 +264,17 @@ find_root_device (const char *dir, dev_t dev) /* Found! */ char *res; char *cwd; +#if defined(__NetBSD__) + /* Convert this block device to its character (raw) device. */ + const char *template = "%s/r%s"; +#else + /* Keep the device name as it is. */ + const char *template = "%s/%s"; +#endif cwd = xgetcwd (); - res = xmalloc (strlen (cwd) + strlen (ent->d_name) + 2); - sprintf (res, "%s/%s", cwd, ent->d_name); + res = xmalloc (strlen (cwd) + strlen (ent->d_name) + 3); + sprintf (res, template, cwd, ent->d_name); strip_extra_slashes (res); free (cwd); @@ -460,7 +467,8 @@ grub_guess_root_device (const char *dir) return os_dev; } -int + +static int grub_util_is_dmraid (const char *os_dev) { if (! strncmp (os_dev, "/dev/mapper/nvidia_", 19)) diff --git a/util/grub-emu.c b/util/grub-emu.c index 21fc6fa09..8660f0aa0 100644 --- a/util/grub-emu.c +++ b/util/grub-emu.c @@ -38,8 +38,7 @@ #include #include -#include - +#define ENABLE_RELOCATABLE 0 #include "progname.h" /* Used for going back to the main function. */ @@ -51,9 +50,10 @@ static char *prefix = NULL; grub_addr_t grub_arch_modules_addr (void) { - return NULL; + return 0; } +#if GRUB_NO_MODULES grub_err_t grub_arch_dl_check_header (void *ehdr) { @@ -70,6 +70,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) return GRUB_ERR_BAD_MODULE; } +#endif void grub_reboot (void) @@ -106,10 +107,6 @@ grub_machine_fini (void) grub_console_fini (); } -void -read_command_list (void) -{ -} static struct option options[] = @@ -149,6 +146,15 @@ usage (int status) } +void grub_hostfs_init (void); +void grub_hostfs_fini (void); +void grub_host_init (void); +void grub_host_fini (void); +#if GRUB_NO_MODULES +void grub_init_all (void); +void grub_fini_all (void); +#endif + int main (int argc, char *argv[]) { @@ -160,8 +166,6 @@ main (int argc, char *argv[]) set_program_name (argv[0]); - grub_util_init_nls (); - while ((opt = getopt_long (argc, argv, "r:d:m:vH:hV", options, 0)) != -1) switch (opt) { @@ -209,11 +213,15 @@ main (int argc, char *argv[]) signal (SIGINT, SIG_IGN); grub_console_init (); + grub_host_init (); + grub_hostfs_init (); /* XXX: This is a bit unportable. */ grub_util_biosdisk_init (dev_map); +#if GRUB_NO_MODULES grub_init_all (); +#endif /* Make sure that there is a root device. */ if (! root_dev) @@ -231,7 +239,10 @@ main (int argc, char *argv[]) } } - dir = grub_get_prefix (dir); + if (strcmp (root_dev, "host") == 0) + dir = xstrdup (dir); + else + dir = grub_get_prefix (dir); prefix = xmalloc (strlen (root_dev) + 2 + strlen (dir) + 1); sprintf (prefix, "(%s)%s", root_dev, dir); free (dir); @@ -240,7 +251,11 @@ main (int argc, char *argv[]) if (setjmp (main_env) == 0) grub_main (); +#if GRUB_NO_MODULES grub_fini_all (); +#endif + grub_hostfs_fini (); + grub_host_fini (); grub_machine_fini (); diff --git a/util/grub-fstest.c b/util/grub-fstest.c index bf30286a4..c03c43451 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -280,27 +280,29 @@ fstest (char **images, int num_disks, int cmd, int n, char **args) { char *host_file; char *loop_name; - char *argv[3] = { "-p" }; + char *argv[3]; int i; + argv[0] = "-p"; + for (i = 0; i < num_disks; i++) { loop_name = grub_xasprintf ("loop%d", i); - host_file = grub_xasprintf ("(host)%s", images[i]); + if (!loop_name) + grub_util_error (grub_errmsg); - if (!loop_name || !host_file) - { - grub_free (loop_name); - grub_free (host_file); - grub_util_error (grub_errmsg); - return; - } + host_file = grub_xasprintf ("(host)%s", images[i]); + if (!host_file) + grub_util_error (grub_errmsg); argv[1] = loop_name; argv[2] = host_file; if (execute_command ("loopback", 3, argv)) grub_util_error ("loopback command fails"); + + grub_free (loop_name); + grub_free (host_file); } grub_lvm_fini (); @@ -336,19 +338,16 @@ fstest (char **images, int num_disks, int cmd, int n, char **args) for (i = 0; i < num_disks; i++) { - grub_free (loop_name); loop_name = grub_xasprintf ("loop%d", i); if (!loop_name) - { - grub_free (host_file); - grub_util_error (grub_errmsg); - return; - } - execute_command ("loopback", 2, argv); - } + grub_util_error (grub_errmsg); - grub_free (loop_name); - grub_free (host_file); + argv[1] = loop_name; + + execute_command ("loopback", 2, argv); + + grub_free (loop_name); + } } static struct option options[] = { diff --git a/util/grub-install.in b/util/grub-install.in index bb323d706..8a93ace8c 100644 --- a/util/grub-install.in +++ b/util/grub-install.in @@ -32,13 +32,10 @@ platform=@platform@ host_os=@host_os@ font=@datadir@/@PACKAGE_TARNAME@/ascii.pf2 pkglibdir=${libdir}/`echo ${PACKAGE_TARNAME}/${target_cpu}-${platform} | sed ${transform}` +localedir=@datadir@/locale grub_setup=${sbindir}/`echo grub-setup | sed ${transform}` -if [ "${target_cpu}-${platform}" = "i386-pc" ] || [ "${target_cpu}-${platform}" = "sparc64-ieee1275" ] || [ "${target_cpu}-${platform}" = "mips-yeeloong" ] ; then - grub_mkimage=${bindir}/`echo grub-mkimage | sed ${transform}` -else - grub_mkimage=${bindir}/`echo grub-mkelfimage | sed ${transform}` -fi +grub_mkimage=${bindir}/`echo grub-mkimage | sed ${transform}` grub_mkdevicemap=${sbindir}/`echo grub-mkdevicemap | sed ${transform}` grub_probe=${sbindir}/`echo grub-probe | sed ${transform}` grub_editenv=${bindir}/`echo grub-editenv | sed ${transform}` @@ -218,8 +215,7 @@ else fi # Create the GRUB directory if it is not present. -test -d "$bootdir" || mkdir "$bootdir" || exit 1 -test -d "$grubdir" || mkdir "$grubdir" || exit 1 +mkdir -p "$grubdir" || exit 1 # If --recheck is specified, remove the device map, if present. if test $recheck = yes; then @@ -263,14 +259,14 @@ fi # Copy gettext files mkdir -p ${grubdir}/locale/ -for file in ${grubdir}/locale/*.mo ${pkglibdir}/locale/*.mo; do - if test -f "$file"; then - cp -f "$file" ${grubdir}/locale/ +for dir in ${localedir}/*; do + if test -f "$dir/LC_MESSAGES/grub.mo"; then + cp -f "$dir/LC_MESSAGES/grub.mo" "${grubdir}/locale/${dir##*/}.mo" fi done # Write device to a variable so we don't have to traverse /dev every time. -grub_device=`$grub_probe --target=device ${grubdir}` +grub_device=`$grub_probe --target=device ${grubdir}` || exit 1 if ! test -f ${grubdir}/grubenv; then $grub_editenv ${grubdir}/grubenv create @@ -287,7 +283,10 @@ fi # Then the partition map module. In order to support partition-less media, # this command is allowed to fail (--target=fs already grants us that the # filesystem will be accessible). -partmap_module=`$grub_probe --target=partmap --device ${grub_device} 2> /dev/null` +partmap_module= +for x in `$grub_probe --target=partmap --device ${grub_device} 2> /dev/null`; do + partmap_module="$partmap_module part_$x"; +done # Device abstraction module, if any (lvm, raid). devabstraction_module=`$grub_probe --target=abstraction --device ${grub_device}` @@ -309,11 +308,11 @@ if [ "x${devabstraction_module}" = "x" ] ; then if echo "${install_device}" | grep -qx "(.*)" ; then install_drive="${install_device}" else - install_drive="`$grub_probe --target=drive --device ${install_device}`" + install_drive="`$grub_probe --target=drive --device ${install_device}`" || exit 1 fi install_drive="`echo ${install_drive} | sed -e s/,[0-9]*[a-z]*//g`" fi - grub_drive="`$grub_probe --target=drive --device ${grub_device}`" + grub_drive="`$grub_probe --target=drive --device ${grub_device}`" || exit 1 # Strip partition number grub_drive="`echo ${grub_drive} | sed -e s/,[0-9]*[a-z]*//g`" @@ -340,19 +339,26 @@ if [ "x${devabstraction_module}" = "x" ] ; then modules="$modules search_fs_uuid" fi else - prefix_drive=`$grub_probe --target=drive --device ${grub_device}` + prefix_drive=`$grub_probe --target=drive --device ${grub_device}` || exit 1 fi +case "${target_cpu}-${platform}" + i386-pc) mkimage_target=i386-pc ;; + sparc64-ieee1275) mkimage_target=sparc64-ieee1275-raw ;; + mips-yeeloong) mkimage_target=mipsel-yeeloong-elf ;; + *) mkimage_target=i386-coreboot; +esac + if [ "${target_cpu}-${platform}" = "i386-pc" ] || [ "${target_cpu}-${platform}" = "sparc64-ieee1275" ] ; then - $grub_mkimage ${config_opt} --output=${grubdir}/core.img --prefix=${prefix_drive}${relative_grubdir} $modules || exit 1 + $grub_mkimage ${config_opt} -O ${mkimage_target} --output=${grubdir}/core.img --prefix=${prefix_drive}${relative_grubdir} $modules || exit 1 # Now perform the installation. $grub_setup ${setup_verbose} ${setup_force} --directory=${grubdir} --device-map=${device_map} \ ${install_device} || exit 1 elif [ "${target_cpu}-${platform}" = "mips-yeeloong" ] ; then - $grub_mkimage ${config_opt} -f ${font} -d ${pkglibdir} -O elf --output=/boot/grub.elf --prefix=${prefix_drive}${relative_grubdir} $modules || exit 1 + $grub_mkimage ${config_opt} -f ${font} -d ${pkglibdir} -O ${mkimage_target} --output=/boot/grub.elf --prefix=${prefix_drive}${relative_grubdir} $modules || exit 1 else - $grub_mkimage ${config_opt} -d ${pkglibdir} --output=/boot/multiboot.img --prefix=${prefix_drive}${relative_grubdir} $modules || exit 1 + $grub_mkimage -O ${mkimage_target} ${config_opt} -d ${pkglibdir} --output=/boot/multiboot.img --prefix=${prefix_drive}${relative_grubdir} $modules || exit 1 fi echo "Installation finished. No error reported." diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index 49e52b313..85212f8fc 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -24,10 +24,10 @@ sbindir=@sbindir@ libdir=@libdir@ sysconfdir=@sysconfdir@ package_version=@PACKAGE_VERSION@ +host_os=@host_os@ datarootdir=@datarootdir@ datadir=@datadir@ pkgdatadir=${datadir}/`echo @PACKAGE_TARNAME@ | sed "${transform}"` -grub_prefix=`echo /boot/grub | sed ${transform}` grub_cfg="" grub_mkconfig_dir=${sysconfdir}/grub.d @@ -75,6 +75,18 @@ done . ${libdir}/grub/grub-mkconfig_lib +case "$host_os" in +netbsd* | openbsd*) + # Because /boot is used for the boot block in NetBSD and OpenBSD, use /grub + # instead of /boot/grub. + grub_prefix=`echo /grub | sed ${transform}` + ;; +*) + # Use /boot/grub by default. + grub_prefix=`echo /boot/grub | sed ${transform}` + ;; +esac + if [ "x$EUID" = "x" ] ; then EUID=`id -u` fi @@ -140,60 +152,73 @@ if [ "x${GRUB_TERMINAL}" != "x" ] ; then GRUB_TERMINAL_OUTPUT="${GRUB_TERMINAL}" fi -case x${GRUB_TERMINAL_OUTPUT} in - x | xgfxterm) - # If this platform supports gfxterm, try to use it. - if test -e ${grub_prefix}/gfxterm.mod ; then - # FIXME: this should do something smarter than just loading first - # video backend. - GRUB_VIDEO_BACKEND=$(head -n 1 ${grub_prefix}/video.lst || true) - if [ -n "${GRUB_VIDEO_BACKEND}" ] ; then - GRUB_TERMINAL_OUTPUT=gfxterm - elif [ "${GRUB_TERMINAL_OUTPUT}" = "gfxterm" ] ; then - echo "No suitable backend could be found for gfxterm." >&2 ; exit 1 - fi - fi - ;; - xconsole | xserial | xofconsole) ;; - *) echo "Invalid output terminal \"${GRUB_TERMINAL_OUTPUT}\"" >&2 ; exit 1 ;; -esac +termoutdefault=0 +if [ "x${GRUB_TERMINAL_OUTPUT}" = "x" ]; then + GRUB_TERMINAL_OUTPUT=gfxterm; + termoutdefault=1; +fi -# check for terminals that require fonts -case ${GRUB_TERMINAL_OUTPUT} in - gfxterm) - if [ -n "$GRUB_FONT" ] ; then - if is_path_readable_by_grub ${GRUB_FONT} > /dev/null ; then - GRUB_FONT_PATH=${GRUB_FONT} - else - echo "No such font or not readable by grub: ${GRUB_FONT}" >&2 - exit 1 +for x in ${GRUB_TERMINAL_OUTPUT}; do + if [ "x${x}" = "xgfxterm" ]; then + # If this platform supports gfxterm, try to use it. + if ! test -e ${grub_prefix}/gfxterm.mod ; then + if [ "x$termoutdefault" != "x1" ]; then + echo "gfxterm isn't available on your platform" >&2 ; exit 1 + fi + GRUB_TERMINAL_OUTPUT= + break; + fi + # FIXME: this should do something smarter than just loading first + # video backend. + GRUB_VIDEO_BACKEND=$(head -n 1 ${grub_prefix}/video.lst || true) + if [ -z "${GRUB_VIDEO_BACKEND}" ] ; then + if [ "x$termoutdefault" != "x1" ]; then + echo "No suitable backend could be found for gfxterm." >&2 ; exit 1 + fi + GRUB_TERMINAL_OUTPUT= + fi + if [ -n "$GRUB_FONT" ] ; then + if is_path_readable_by_grub ${GRUB_FONT} > /dev/null ; then + GRUB_FONT_PATH=${GRUB_FONT} + else + echo "No such font or not readable by grub: ${GRUB_FONT}" >&2 + exit 1 + fi + else + for dir in ${pkgdatadir} /boot/grub /usr/share/grub ; do + for basename in unicode unifont ascii; do + path="${dir}/${basename}.pf2" + if is_path_readable_by_grub ${path} > /dev/null ; then + GRUB_FONT_PATH=${path} + else + continue + fi + if [ "${basename}" = "ascii" ] ; then + # make sure all our children behave in conformance with ascii.. + export LANG=C + fi + break 2 + done + done + fi + if [ -z "${GRUB_FONT_PATH}" ] ; then + if [ "x$termoutdefault" != "x1" ]; then + echo "No font for gfxterm found." >&2 ; exit 1 + fi + GRUB_TERMINAL_OUTPUT= fi - else - for dir in ${pkgdatadir} /boot/grub /usr/share/grub ; do - for basename in unicode unifont ascii; do - path="${dir}/${basename}.pf2" - if is_path_readable_by_grub ${path} > /dev/null ; then - GRUB_FONT_PATH=${path} - else - continue - fi - if [ "${basename}" = "ascii" ] ; then - # make sure all our children behave in conformance with ascii.. - export LANG=C - fi - break 2 - done - done fi - if [ -z "${GRUB_FONT_PATH}" ] ; then - # fallback to the native terminal for this platform - unset GRUB_TERMINAL_OUTPUT - fi - ;; - *) - # make sure all our children behave in conformance with ascii.. - export LANG=C -esac +done + +for x in ${GRUB_TERMINAL_OUTPUT}; do + case "x${x}" in + xgfxterm) ;; + xconsole | xserial | xofconsole) + # make sure all our children behave in conformance with ascii.. + export LANG=C;; + *) echo "Invalid output terminal \"${GRUB_TERMINAL_OUTPUT}\"" >&2 ; exit 1 ;; + esac +done # These are defined in this script, export them here so that user can # override them. @@ -214,13 +239,21 @@ export GRUB_DEFAULT \ GRUB_DISTRIBUTOR \ GRUB_CMDLINE_LINUX \ GRUB_CMDLINE_LINUX_DEFAULT \ + GRUB_CMDLINE_NETBSD \ + GRUB_CMDLINE_NETBSD_DEFAULT \ GRUB_TERMINAL_INPUT \ GRUB_TERMINAL_OUTPUT \ GRUB_SERIAL_COMMAND \ GRUB_DISABLE_LINUX_UUID \ GRUB_DISABLE_LINUX_RECOVERY \ + GRUB_DISABLE_NETBSD_RECOVERY \ GRUB_GFXMODE \ - GRUB_DISABLE_OS_PROBER + GRUB_BACKGROUND \ + GRUB_THEME \ + GRUB_GFXPAYLOAD_LINUX \ + GRUB_DISABLE_OS_PROBER \ + GRUB_INIT_TUNE \ + GRUB_SAVEDEFAULT if test "x${grub_cfg}" != "x"; then rm -f ${grub_cfg}.new diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index fc24c7b70..831bef846 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -31,6 +31,12 @@ if test "x$grub_mkrelpath" = x; then grub_mkrelpath=${bindir}/`echo grub-mkrelpath | sed ${transform}` fi +if $(which gettext >/dev/null 2>/dev/null) ; then + gettext="gettext" +else + gettext="echo" +fi + grub_warn () { echo "Warning: $@" >&2 @@ -96,7 +102,7 @@ convert_system_path_to_grub_path () save_default_entry () { - if [ "x${GRUB_DEFAULT}" = "xsaved" ] ; then + if [ "x${GRUB_SAVEDEFAULT}" = "xtrue" ] ; then cat << EOF savedefault EOF @@ -120,7 +126,7 @@ prepare_grub_to_access_device () # If there's a filesystem UUID that GRUB is capable of identifying, use it; # otherwise set root as per value in device.map. - echo "set root=`${grub_probe} --device ${device} --target=drive`" + echo "set root='`${grub_probe} --device ${device} --target=drive`'" if fs_uuid="`${grub_probe} --device ${device} --target=fs_uuid 2> /dev/null`" ; then echo "search --no-floppy --fs-uuid --set ${fs_uuid}" fi @@ -188,3 +194,7 @@ version_find_latest () done echo "$a" } + +gettext_quoted () { + $gettext "$@" | sed "s/'/'\\\\''/g" +} diff --git a/util/grub-mkfont.c b/util/grub-mkfont.c index 38906a70e..51e2e494c 100644 --- a/util/grub-mkfont.c +++ b/util/grub-mkfont.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -392,9 +393,10 @@ write_font_pf2 (struct grub_font_info *font_info, char *output_file) offset = 0; leng = grub_cpu_to_be32 (4); - grub_util_write_image ("FILE", 4, file); + grub_util_write_image (FONT_FORMAT_SECTION_NAMES_FILE, + sizeof(FONT_FORMAT_SECTION_NAMES_FILE) - 1, file); grub_util_write_image ((char *) &leng, 4, file); - grub_util_write_image ("PFF2", 4, file); + grub_util_write_image (FONT_FORMAT_PFF2_MAGIC, 4, file); offset += 12; if (! font_info->name) @@ -416,20 +418,25 @@ write_font_pf2 (struct grub_font_info *font_info, char *output_file) font_name = xasprintf ("%s %s %d", font_info->name, &style_name[1], font_info->size); - write_string_section ("NAME", font_name, &offset, file); - write_string_section ("FAMI", font_info->name, &offset, file); - write_string_section ("WEIG", + write_string_section (FONT_FORMAT_SECTION_NAMES_FONT_NAME, + font_name, &offset, file); + write_string_section (FONT_FORMAT_SECTION_NAMES_FAMILY, + font_info->name, &offset, file); + write_string_section (FONT_FORMAT_SECTION_NAMES_WEIGHT, (font_info->style & FT_STYLE_FLAG_BOLD) ? "bold" : "normal", &offset, file); - write_string_section ("SLAN", + write_string_section (FONT_FORMAT_SECTION_NAMES_SLAN, (font_info->style & FT_STYLE_FLAG_ITALIC) ? "italic" : "normal", &offset, file); - write_be16_section ("PTSZ", font_info->size, &offset, file); - write_be16_section ("MAXW", font_info->max_width, &offset, file); - write_be16_section ("MAXH", font_info->max_height, &offset, file); + write_be16_section (FONT_FORMAT_SECTION_NAMES_POINT_SIZE, + font_info->size, &offset, file); + write_be16_section (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH, + font_info->max_width, &offset, file); + write_be16_section (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT, + font_info->max_height, &offset, file); if (! font_info->desc) { @@ -447,8 +454,10 @@ write_font_pf2 (struct grub_font_info *font_info, char *output_file) font_info->asce = font_info->max_y; } - write_be16_section ("ASCE", font_info->asce, &offset, file); - write_be16_section ("DESC", font_info->desc, &offset, file); + write_be16_section (FONT_FORMAT_SECTION_NAMES_ASCENT, + font_info->asce, &offset, file); + write_be16_section (FONT_FORMAT_SECTION_NAMES_DESCENT, + font_info->desc, &offset, file); if (font_verbosity > 0) { @@ -479,7 +488,9 @@ write_font_pf2 (struct grub_font_info *font_info, char *output_file) printf ("Number of glyph: %d\n", num); leng = grub_cpu_to_be32 (num * 9); - grub_util_write_image ("CHIX", 4, file); + grub_util_write_image (FONT_FORMAT_SECTION_NAMES_CHAR_INDEX, + sizeof(FONT_FORMAT_SECTION_NAMES_CHAR_INDEX) - 1, + file); grub_util_write_image ((char *) &leng, 4, file); offset += 8 + num * 9 + 8; @@ -495,7 +506,8 @@ write_font_pf2 (struct grub_font_info *font_info, char *output_file) } leng = 0xffffffff; - grub_util_write_image ("DATA", 4, file); + grub_util_write_image (FONT_FORMAT_SECTION_NAMES_DATA, + sizeof(FONT_FORMAT_SECTION_NAMES_DATA) - 1, file); grub_util_write_image ((char *) &leng, 4, file); for (cur = font_info->glyph; cur; cur = cur->next) diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c new file mode 100644 index 000000000..cc4a225d4 --- /dev/null +++ b/util/grub-mkimage.c @@ -0,0 +1,1340 @@ +/* grub-mkimage.c - make a bootable image */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define _GNU_SOURCE 1 +#include + +#include "progname.h" + +#define ALIGN_ADDR(x) (ALIGN_UP((x), image_target->voidp_sizeof)) + +#define TARGET_NO_FIELD 0xffffffff +struct image_target_desc +{ + const char *name; + grub_size_t voidp_sizeof; + int bigendian; + enum { + IMAGE_I386_PC, IMAGE_EFI, IMAGE_COREBOOT, + IMAGE_SPARC64_AOUT, IMAGE_SPARC64_RAW, IMAGE_I386_IEEE1275, + IMAGE_YEELOONG_ELF, IMAGE_QEMU, IMAGE_PPC + } id; + enum + { + PLATFORM_FLAGS_NONE = 0, + PLATFORM_FLAGS_LZMA = 1 + } flags; + unsigned prefix; + unsigned data_end; + unsigned raw_size; + unsigned total_module_size; + unsigned kernel_image_size; + unsigned compressed_size; + unsigned link_align; + grub_uint16_t elf_target; + unsigned section_align; + signed vaddr_offset; + unsigned install_dos_part, install_bsd_part; + grub_uint64_t link_addr; + unsigned mod_gap, mod_align; +}; + +struct image_target_desc image_targets[] = + { + { + .name = "i386-coreboot", + .voidp_sizeof = 4, + .bigendian = 0, + .id = IMAGE_COREBOOT, + .flags = PLATFORM_FLAGS_NONE, + .prefix = GRUB_KERNEL_I386_COREBOOT_PREFIX, + .data_end = GRUB_KERNEL_I386_COREBOOT_DATA_END, + .raw_size = 0, + .total_module_size = TARGET_NO_FIELD, + .kernel_image_size = TARGET_NO_FIELD, + .compressed_size = TARGET_NO_FIELD, + .section_align = 1, + .vaddr_offset = 0, + .install_dos_part = TARGET_NO_FIELD, + .install_bsd_part = TARGET_NO_FIELD, + .link_addr = GRUB_KERNEL_I386_COREBOOT_LINK_ADDR, + .elf_target = EM_386, + .link_align = 4, + .mod_gap = GRUB_KERNEL_I386_COREBOOT_MOD_GAP, + .mod_align = GRUB_KERNEL_I386_COREBOOT_MOD_ALIGN + }, + { + .name = "i386-pc", + .voidp_sizeof = 4, + .bigendian = 0, + .id = IMAGE_I386_PC, + .flags = PLATFORM_FLAGS_LZMA, + .prefix = GRUB_KERNEL_I386_PC_PREFIX, + .data_end = GRUB_KERNEL_I386_PC_DATA_END, + .raw_size = GRUB_KERNEL_I386_PC_RAW_SIZE, + .total_module_size = GRUB_KERNEL_I386_PC_TOTAL_MODULE_SIZE, + .kernel_image_size = GRUB_KERNEL_I386_PC_KERNEL_IMAGE_SIZE, + .compressed_size = GRUB_KERNEL_I386_PC_COMPRESSED_SIZE, + .section_align = 1, + .vaddr_offset = 0, + .install_dos_part = GRUB_KERNEL_I386_PC_INSTALL_DOS_PART, + .install_bsd_part = GRUB_KERNEL_I386_PC_INSTALL_BSD_PART, + .link_addr = GRUB_KERNEL_I386_PC_LINK_ADDR + }, + { + .name = "i386-efi", + .voidp_sizeof = 4, + .bigendian = 0, + .id = IMAGE_EFI, + .flags = PLATFORM_FLAGS_NONE, + .prefix = GRUB_KERNEL_I386_EFI_PREFIX, + .data_end = GRUB_KERNEL_I386_EFI_DATA_END, + .raw_size = 0, + .total_module_size = TARGET_NO_FIELD, + .kernel_image_size = TARGET_NO_FIELD, + .compressed_size = TARGET_NO_FIELD, + .section_align = GRUB_PE32_SECTION_ALIGNMENT, + .vaddr_offset = ALIGN_UP (GRUB_PE32_MSDOS_STUB_SIZE + + GRUB_PE32_SIGNATURE_SIZE + + sizeof (struct grub_pe32_coff_header) + + sizeof (struct grub_pe32_optional_header) + + 4 * sizeof (struct grub_pe32_section_table), + GRUB_PE32_SECTION_ALIGNMENT), + .install_dos_part = TARGET_NO_FIELD, + .install_bsd_part = TARGET_NO_FIELD, + }, + { + .name = "i386-ieee1275", + .voidp_sizeof = 4, + .bigendian = 0, + .id = IMAGE_I386_IEEE1275, + .flags = PLATFORM_FLAGS_NONE, + .prefix = GRUB_KERNEL_I386_IEEE1275_PREFIX, + .data_end = GRUB_KERNEL_I386_IEEE1275_DATA_END, + .raw_size = 0, + .total_module_size = TARGET_NO_FIELD, + .kernel_image_size = TARGET_NO_FIELD, + .compressed_size = TARGET_NO_FIELD, + .section_align = 1, + .vaddr_offset = 0, + .install_dos_part = TARGET_NO_FIELD, + .install_bsd_part = TARGET_NO_FIELD, + .link_addr = GRUB_KERNEL_I386_IEEE1275_LINK_ADDR, + .elf_target = EM_386, + .mod_gap = GRUB_KERNEL_I386_IEEE1275_MOD_GAP, + .mod_align = GRUB_KERNEL_I386_IEEE1275_MOD_ALIGN, + .link_align = 4, + }, + { + .name = "i386-qemu", + .voidp_sizeof = 4, + .bigendian = 0, + .id = IMAGE_QEMU, + .flags = PLATFORM_FLAGS_NONE, + .prefix = GRUB_KERNEL_I386_QEMU_PREFIX, + .data_end = GRUB_KERNEL_I386_QEMU_DATA_END, + .raw_size = 0, + .total_module_size = TARGET_NO_FIELD, + .compressed_size = TARGET_NO_FIELD, + .kernel_image_size = GRUB_KERNEL_I386_QEMU_KERNEL_IMAGE_SIZE, + .section_align = 1, + .vaddr_offset = 0, + .install_dos_part = TARGET_NO_FIELD, + .install_bsd_part = TARGET_NO_FIELD, + .link_addr = GRUB_KERNEL_I386_QEMU_LINK_ADDR + }, + { + .name = "x86_64-efi", + .voidp_sizeof = 8, + .bigendian = 0, + .id = IMAGE_EFI, + .flags = PLATFORM_FLAGS_NONE, + .prefix = GRUB_KERNEL_X86_64_EFI_PREFIX, + .data_end = GRUB_KERNEL_X86_64_EFI_DATA_END, + .raw_size = 0, + .total_module_size = TARGET_NO_FIELD, + .kernel_image_size = TARGET_NO_FIELD, + .compressed_size = TARGET_NO_FIELD, + .section_align = GRUB_PE32_SECTION_ALIGNMENT, + .vaddr_offset = ALIGN_UP (GRUB_PE32_MSDOS_STUB_SIZE + + GRUB_PE32_SIGNATURE_SIZE + + sizeof (struct grub_pe32_coff_header) + + sizeof (struct grub_pe64_optional_header) + + 4 * sizeof (struct grub_pe32_section_table), + GRUB_PE32_SECTION_ALIGNMENT), + .install_dos_part = TARGET_NO_FIELD, + .install_bsd_part = TARGET_NO_FIELD, + }, + { + .name = "mipsel-yeeloong-elf", + .voidp_sizeof = 4, + .bigendian = 0, + .id = IMAGE_YEELOONG_ELF, + .flags = PLATFORM_FLAGS_NONE, + .prefix = GRUB_KERNEL_MIPS_YEELOONG_PREFIX, + .data_end = GRUB_KERNEL_MIPS_YEELOONG_DATA_END, + .raw_size = GRUB_KERNEL_MIPS_YEELOONG_RAW_SIZE, + .total_module_size = GRUB_KERNEL_MIPS_YEELOONG_TOTAL_MODULE_SIZE, + .compressed_size = GRUB_KERNEL_MIPS_YEELOONG_COMPRESSED_SIZE, + .kernel_image_size = GRUB_KERNEL_MIPS_YEELOONG_KERNEL_IMAGE_SIZE, + .section_align = 1, + .vaddr_offset = 0, + .install_dos_part = TARGET_NO_FIELD, + .install_bsd_part = TARGET_NO_FIELD, + .link_addr = GRUB_KERNEL_MIPS_YEELOONG_LINK_ADDR, + .elf_target = EM_MIPS, + .link_align = GRUB_KERNEL_MIPS_YEELOONG_LINK_ALIGN + }, + { + .name = "powerpc-ieee1275", + .voidp_sizeof = 4, + .bigendian = 1, + .id = IMAGE_PPC, + .flags = PLATFORM_FLAGS_NONE, + .prefix = GRUB_KERNEL_POWERPC_IEEE1275_PREFIX, + .data_end = GRUB_KERNEL_POWERPC_IEEE1275_DATA_END, + .raw_size = 0, + .total_module_size = TARGET_NO_FIELD, + .kernel_image_size = TARGET_NO_FIELD, + .compressed_size = TARGET_NO_FIELD, + .section_align = 1, + .vaddr_offset = 0, + .install_dos_part = TARGET_NO_FIELD, + .install_bsd_part = TARGET_NO_FIELD, + .link_addr = GRUB_KERNEL_POWERPC_IEEE1275_LINK_ADDR, + .elf_target = EM_PPC, + .mod_gap = GRUB_KERNEL_POWERPC_IEEE1275_MOD_GAP, + .mod_align = GRUB_KERNEL_POWERPC_IEEE1275_MOD_ALIGN, + .link_align = 4 + }, + { + .name = "sparc64-ieee1275-raw", + .voidp_sizeof = 8, + .bigendian = 1, + .id = IMAGE_SPARC64_RAW, + .flags = PLATFORM_FLAGS_NONE, + .prefix = GRUB_KERNEL_SPARC64_IEEE1275_PREFIX, + .data_end = GRUB_KERNEL_SPARC64_IEEE1275_DATA_END, + .raw_size = GRUB_KERNEL_SPARC64_IEEE1275_RAW_SIZE, + .total_module_size = GRUB_KERNEL_SPARC64_IEEE1275_TOTAL_MODULE_SIZE, + .kernel_image_size = GRUB_KERNEL_SPARC64_IEEE1275_KERNEL_IMAGE_SIZE, + .compressed_size = GRUB_KERNEL_SPARC64_IEEE1275_COMPRESSED_SIZE, + .section_align = 1, + .vaddr_offset = 0, + .install_dos_part = TARGET_NO_FIELD, + .install_bsd_part = TARGET_NO_FIELD, + .link_addr = GRUB_KERNEL_SPARC64_IEEE1275_LINK_ADDR + }, + { + .name = "sparc64-ieee1275-aout", + .voidp_sizeof = 8, + .bigendian = 1, + .id = IMAGE_SPARC64_AOUT, + .flags = PLATFORM_FLAGS_NONE, + .prefix = GRUB_KERNEL_SPARC64_IEEE1275_PREFIX, + .data_end = GRUB_KERNEL_SPARC64_IEEE1275_DATA_END, + .raw_size = GRUB_KERNEL_SPARC64_IEEE1275_RAW_SIZE, + .total_module_size = GRUB_KERNEL_SPARC64_IEEE1275_TOTAL_MODULE_SIZE, + .kernel_image_size = GRUB_KERNEL_SPARC64_IEEE1275_KERNEL_IMAGE_SIZE, + .compressed_size = GRUB_KERNEL_SPARC64_IEEE1275_COMPRESSED_SIZE, + .section_align = 1, + .vaddr_offset = 0, + .install_dos_part = TARGET_NO_FIELD, + .install_bsd_part = TARGET_NO_FIELD, + .link_addr = GRUB_KERNEL_SPARC64_IEEE1275_LINK_ADDR + }, + }; + +#define grub_target_to_host32(x) (grub_target_to_host32_real (image_target, (x))) +#define grub_host_to_target32(x) (grub_host_to_target32_real (image_target, (x))) +#define grub_target_to_host64(x) (grub_target_to_host64_real (image_target, (x))) +#define grub_host_to_target64(x) (grub_host_to_target64_real (image_target, (x))) +#define grub_host_to_target_addr(x) (grub_host_to_target_addr_real (image_target, (x))) +#define grub_target_to_host16(x) (grub_target_to_host16_real (image_target, (x))) +#define grub_host_to_target16(x) (grub_host_to_target16_real (image_target, (x))) + +static inline grub_uint32_t +grub_target_to_host32_real (struct image_target_desc *image_target, grub_uint32_t in) +{ + if (image_target->bigendian) + return grub_be_to_cpu32 (in); + else + return grub_le_to_cpu32 (in); +} + +static inline grub_uint64_t +grub_target_to_host64_real (struct image_target_desc *image_target, grub_uint64_t in) +{ + if (image_target->bigendian) + return grub_be_to_cpu64 (in); + else + return grub_le_to_cpu64 (in); +} + +static inline grub_uint64_t +grub_host_to_target64_real (struct image_target_desc *image_target, grub_uint64_t in) +{ + if (image_target->bigendian) + return grub_cpu_to_be64 (in); + else + return grub_cpu_to_le64 (in); +} + +static inline grub_uint32_t +grub_host_to_target32_real (struct image_target_desc *image_target, grub_uint32_t in) +{ + if (image_target->bigendian) + return grub_cpu_to_be32 (in); + else + return grub_cpu_to_le32 (in); +} + +static inline grub_uint16_t +grub_target_to_host16_real (struct image_target_desc *image_target, grub_uint16_t in) +{ + if (image_target->bigendian) + return grub_be_to_cpu16 (in); + else + return grub_le_to_cpu16 (in); +} + +static inline grub_uint16_t +grub_host_to_target16_real (struct image_target_desc *image_target, grub_uint16_t in) +{ + if (image_target->bigendian) + return grub_cpu_to_be16 (in); + else + return grub_cpu_to_le16 (in); +} + +static inline grub_uint64_t +grub_host_to_target_addr_real (struct image_target_desc *image_target, grub_uint64_t in) +{ + if (image_target->voidp_sizeof == 8) + return grub_host_to_target64_real (image_target, in); + else + return grub_host_to_target32_real (image_target, in); +} + +static inline grub_uint64_t +grub_target_to_host_real (struct image_target_desc *image_target, grub_uint64_t in) +{ + if (image_target->voidp_sizeof == 8) + return grub_target_to_host64_real (image_target, in); + else + return grub_target_to_host32_real (image_target, in); +} + +#define GRUB_IEEE1275_NOTE_NAME "PowerPC" +#define GRUB_IEEE1275_NOTE_TYPE 0x1275 + +/* These structures are defined according to the CHRP binding to IEEE1275, + "Client Program Format" section. */ + +struct grub_ieee1275_note_hdr +{ + grub_uint32_t namesz; + grub_uint32_t descsz; + grub_uint32_t type; + char name[sizeof (GRUB_IEEE1275_NOTE_NAME)]; +}; + +struct grub_ieee1275_note_desc +{ + grub_uint32_t real_mode; + grub_uint32_t real_base; + grub_uint32_t real_size; + grub_uint32_t virt_base; + grub_uint32_t virt_size; + grub_uint32_t load_base; +}; + +struct grub_ieee1275_note +{ + struct grub_ieee1275_note_hdr header; + struct grub_ieee1275_note_desc descriptor; +}; + +#define grub_target_to_host(val) grub_target_to_host_real(image_target, (val)) + +#include + +static void *SzAlloc(void *p, size_t size) { p = p; return xmalloc(size); } +static void SzFree(void *p, void *address) { p = p; free(address); } +static ISzAlloc g_Alloc = { SzAlloc, SzFree }; + +static void +compress_kernel_lzma (char *kernel_img, size_t kernel_size, + char **core_img, size_t *core_size, size_t raw_size) +{ + CLzmaEncProps props; + unsigned char out_props[5]; + size_t out_props_size = 5; + + LzmaEncProps_Init(&props); + props.dictSize = 1 << 16; + props.lc = 3; + props.lp = 0; + props.pb = 2; + props.numThreads = 1; + + if (kernel_size < raw_size) + grub_util_error (_("the core image is too small")); + + *core_img = xmalloc (kernel_size); + memcpy (*core_img, kernel_img, raw_size); + + *core_size = kernel_size - raw_size; + if (LzmaEncode ((unsigned char *) *core_img + raw_size, core_size, + (unsigned char *) kernel_img + raw_size, + kernel_size - raw_size, + &props, out_props, &out_props_size, + 0, NULL, &g_Alloc, &g_Alloc) != SZ_OK) + grub_util_error (_("cannot compress the kernel image")); + + *core_size += raw_size; +} + +static void +compress_kernel (struct image_target_desc *image_target, char *kernel_img, + size_t kernel_size, char **core_img, size_t *core_size) +{ + if (image_target->flags & PLATFORM_FLAGS_LZMA) + { + compress_kernel_lzma (kernel_img, kernel_size, core_img, + core_size, image_target->raw_size); + return; + } + + *core_img = xmalloc (kernel_size); + memcpy (*core_img, kernel_img, kernel_size); + *core_size = kernel_size; +} + +struct fixup_block_list +{ + struct fixup_block_list *next; + int state; + struct grub_pe32_fixup_block b; +}; + +#define MKIMAGE_ELF32 1 +#include "grub-mkimagexx.c" +#undef MKIMAGE_ELF32 + +#define MKIMAGE_ELF64 1 +#include "grub-mkimagexx.c" +#undef MKIMAGE_ELF64 + +static void +generate_image (const char *dir, char *prefix, FILE *out, char *mods[], + char *memdisk_path, char *font_path, char *config_path, + struct image_target_desc *image_target, int note) +{ + char *kernel_img, *core_img; + size_t kernel_size, total_module_size, core_size, exec_size; + size_t memdisk_size = 0, font_size = 0, config_size = 0, config_size_pure = 0; + char *kernel_path; + size_t offset; + struct grub_util_path_list *path_list, *p, *next; + grub_size_t bss_size; + grub_uint64_t start_address; + void *rel_section; + grub_size_t reloc_size, align; + path_list = grub_util_resolve_dependencies (dir, "moddep.lst", mods); + + kernel_path = grub_util_get_path (dir, "kernel.img"); + + if (image_target->voidp_sizeof == 8) + total_module_size = sizeof (struct grub_module_info64); + else + total_module_size = sizeof (struct grub_module_info32); + + if (memdisk_path) + { + memdisk_size = ALIGN_UP(grub_util_get_image_size (memdisk_path), 512); + grub_util_info ("the size of memory disk is 0x%x", memdisk_size); + total_module_size += memdisk_size + sizeof (struct grub_module_header); + } + + if (font_path) + { + font_size = ALIGN_ADDR (grub_util_get_image_size (font_path)); + total_module_size += font_size + sizeof (struct grub_module_header); + } + + if (config_path) + { + config_size_pure = grub_util_get_image_size (config_path) + 1; + config_size = ALIGN_ADDR (config_size_pure); + grub_util_info ("the size of config file is 0x%x", config_size); + total_module_size += config_size + sizeof (struct grub_module_header); + } + + for (p = path_list; p; p = p->next) + total_module_size += (ALIGN_ADDR (grub_util_get_image_size (p->name)) + + sizeof (struct grub_module_header)); + + grub_util_info ("the total module size is 0x%x", total_module_size); + + if (image_target->voidp_sizeof == 4) + kernel_img = load_image32 (kernel_path, &exec_size, &kernel_size, &bss_size, + total_module_size, &start_address, &rel_section, + &reloc_size, &align, image_target); + else + kernel_img = load_image64 (kernel_path, &exec_size, &kernel_size, &bss_size, + total_module_size, &start_address, &rel_section, + &reloc_size, &align, image_target); + + if (image_target->prefix + strlen (prefix) + 1 > image_target->data_end) + grub_util_error (_("prefix is too long")); + strcpy (kernel_img + image_target->prefix, prefix); + + if (image_target->voidp_sizeof == 8) + { + /* Fill in the grub_module_info structure. */ + struct grub_module_info64 *modinfo; + modinfo = (struct grub_module_info64 *) (kernel_img + kernel_size); + memset (modinfo, 0, sizeof (struct grub_module_info64)); + modinfo->magic = grub_host_to_target32 (GRUB_MODULE_MAGIC); + modinfo->offset = grub_host_to_target_addr (sizeof (struct grub_module_info64)); + modinfo->size = grub_host_to_target_addr (total_module_size); + offset = kernel_size + sizeof (struct grub_module_info64); + } + else + { + /* Fill in the grub_module_info structure. */ + struct grub_module_info32 *modinfo; + modinfo = (struct grub_module_info32 *) (kernel_img + kernel_size); + memset (modinfo, 0, sizeof (struct grub_module_info32)); + modinfo->magic = grub_host_to_target32 (GRUB_MODULE_MAGIC); + modinfo->offset = grub_host_to_target_addr (sizeof (struct grub_module_info32)); + modinfo->size = grub_host_to_target_addr (total_module_size); + offset = kernel_size + sizeof (struct grub_module_info32); + } + + for (p = path_list; p; p = p->next) + { + struct grub_module_header *header; + size_t mod_size, orig_size; + + orig_size = grub_util_get_image_size (p->name); + mod_size = ALIGN_ADDR (orig_size); + + header = (struct grub_module_header *) (kernel_img + offset); + memset (header, 0, sizeof (struct grub_module_header)); + header->type = grub_host_to_target32 (OBJ_TYPE_ELF); + header->size = grub_host_to_target32 (mod_size + sizeof (*header)); + offset += sizeof (*header); + memset (kernel_img + offset + orig_size, 0, mod_size - orig_size); + + grub_util_load_image (p->name, kernel_img + offset); + offset += mod_size; + } + + if (memdisk_path) + { + struct grub_module_header *header; + + header = (struct grub_module_header *) (kernel_img + offset); + memset (header, 0, sizeof (struct grub_module_header)); + header->type = grub_host_to_target32 (OBJ_TYPE_MEMDISK); + header->size = grub_host_to_target32 (memdisk_size + sizeof (*header)); + offset += sizeof (*header); + + grub_util_load_image (memdisk_path, kernel_img + offset); + offset += memdisk_size; + } + + if (font_path) + { + struct grub_module_header *header; + + header = (struct grub_module_header *) (kernel_img + offset); + memset (header, 0, sizeof (struct grub_module_header)); + header->type = grub_host_to_target32 (OBJ_TYPE_FONT); + header->size = grub_host_to_target32 (font_size + sizeof (*header)); + offset += sizeof (*header); + + grub_util_load_image (font_path, kernel_img + offset); + offset += font_size; + } + + if (config_path) + { + struct grub_module_header *header; + + header = (struct grub_module_header *) (kernel_img + offset); + memset (header, 0, sizeof (struct grub_module_header)); + header->type = grub_host_to_target32 (OBJ_TYPE_CONFIG); + header->size = grub_host_to_target32 (config_size + sizeof (*header)); + offset += sizeof (*header); + + grub_util_load_image (config_path, kernel_img + offset); + *(kernel_img + offset + config_size_pure - 1) = 0; + offset += config_size; + } + + grub_util_info ("kernel_img=%p, kernel_size=0x%x", kernel_img, kernel_size); + compress_kernel (image_target, kernel_img, kernel_size + total_module_size, + &core_img, &core_size); + + grub_util_info ("the core size is 0x%x", core_size); + + if (image_target->total_module_size != TARGET_NO_FIELD) + *((grub_uint32_t *) (core_img + image_target->total_module_size)) + = grub_host_to_target32 (total_module_size); + if (image_target->kernel_image_size != TARGET_NO_FIELD) + *((grub_uint32_t *) (core_img + image_target->kernel_image_size)) + = grub_host_to_target32 (kernel_size); + if (image_target->compressed_size != TARGET_NO_FIELD) + *((grub_uint32_t *) (core_img + image_target->compressed_size)) + = grub_host_to_target32 (core_size - image_target->raw_size); + + /* If we included a drive in our prefix, let GRUB know it doesn't have to + prepend the drive told by BIOS. */ + if (image_target->install_dos_part != TARGET_NO_FIELD + && image_target->install_bsd_part != TARGET_NO_FIELD && prefix[0] == '(') + { + *((grub_int32_t *) (core_img + image_target->install_dos_part)) + = grub_host_to_target32 (-2); + *((grub_int32_t *) (core_img + image_target->install_bsd_part)) + = grub_host_to_target32 (-2); + } + + switch (image_target->id) + { + case IMAGE_I386_PC: + { + unsigned num; + char *boot_path, *boot_img; + size_t boot_size; + + if (GRUB_KERNEL_I386_PC_LINK_ADDR + core_size > GRUB_MEMORY_I386_PC_UPPER) + grub_util_error (_("core image is too big (%p > %p)"), + GRUB_KERNEL_I386_PC_LINK_ADDR + core_size, + GRUB_MEMORY_I386_PC_UPPER); + + num = ((core_size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS); + if (num > 0xffff) + grub_util_error (_("the core image is too big")); + + boot_path = grub_util_get_path (dir, "diskboot.img"); + boot_size = grub_util_get_image_size (boot_path); + if (boot_size != GRUB_DISK_SECTOR_SIZE) + grub_util_error (_("diskboot.img size must be %u bytes"), + GRUB_DISK_SECTOR_SIZE); + + boot_img = grub_util_read_image (boot_path); + + { + struct grub_pc_bios_boot_blocklist *block; + block = (struct grub_pc_bios_boot_blocklist *) (boot_img + + GRUB_DISK_SECTOR_SIZE + - sizeof (*block)); + block->len = grub_host_to_target16 (num); + + /* This is filled elsewhere. Verify it just in case. */ + assert (block->segment + == grub_host_to_target16 (GRUB_BOOT_I386_PC_KERNEL_SEG + + (GRUB_DISK_SECTOR_SIZE >> 4))); + } + + grub_util_write_image (boot_img, boot_size, out); + free (boot_img); + free (boot_path); + } + break; + case IMAGE_EFI: + { + void *pe_img; + grub_uint8_t *header; + void *sections; + size_t pe_size; + struct grub_pe32_coff_header *c; + struct grub_pe32_section_table *text_section, *data_section; + struct grub_pe32_section_table *mods_section, *reloc_section; + static const grub_uint8_t stub[] = GRUB_PE32_MSDOS_STUB; + int header_size; + int reloc_addr; + + if (image_target->voidp_sizeof == 4) + header_size = ALIGN_UP (GRUB_PE32_MSDOS_STUB_SIZE + + GRUB_PE32_SIGNATURE_SIZE + + sizeof (struct grub_pe32_coff_header) + + sizeof (struct grub_pe32_optional_header) + + 4 * sizeof (struct grub_pe32_section_table), + GRUB_PE32_SECTION_ALIGNMENT); + else + header_size = ALIGN_UP (GRUB_PE32_MSDOS_STUB_SIZE + + GRUB_PE32_SIGNATURE_SIZE + + sizeof (struct grub_pe32_coff_header) + + sizeof (struct grub_pe64_optional_header) + + 4 * sizeof (struct grub_pe32_section_table), + GRUB_PE32_SECTION_ALIGNMENT); + + reloc_addr = ALIGN_UP (header_size + core_size, + image_target->section_align); + + pe_size = ALIGN_UP (reloc_addr + reloc_size, + image_target->section_align); + pe_img = xmalloc (reloc_addr + reloc_size); + memset (pe_img, 0, header_size); + memcpy (pe_img + header_size, core_img, core_size); + memcpy (pe_img + reloc_addr, rel_section, reloc_size); + header = pe_img; + + /* The magic. */ + memcpy (header, stub, GRUB_PE32_MSDOS_STUB_SIZE); + memcpy (header + GRUB_PE32_MSDOS_STUB_SIZE, "PE\0\0", + GRUB_PE32_SIGNATURE_SIZE); + + /* The COFF file header. */ + c = (struct grub_pe32_coff_header *) (header + GRUB_PE32_MSDOS_STUB_SIZE + + GRUB_PE32_SIGNATURE_SIZE); + if (image_target->voidp_sizeof == 4) + c->machine = grub_host_to_target16 (GRUB_PE32_MACHINE_I386); + else + c->machine = grub_host_to_target16 (GRUB_PE32_MACHINE_X86_64); + + c->num_sections = grub_host_to_target16 (4); + c->time = grub_host_to_target32 (time (0)); + c->characteristics = grub_host_to_target16 (GRUB_PE32_EXECUTABLE_IMAGE + | GRUB_PE32_LINE_NUMS_STRIPPED + | ((image_target->voidp_sizeof == 4) + ? GRUB_PE32_32BIT_MACHINE + : 0) + | GRUB_PE32_LOCAL_SYMS_STRIPPED + | GRUB_PE32_DEBUG_STRIPPED); + + /* The PE Optional header. */ + if (image_target->voidp_sizeof == 4) + { + struct grub_pe32_optional_header *o; + + c->optional_header_size = grub_host_to_target16 (sizeof (struct grub_pe32_optional_header)); + + o = (struct grub_pe32_optional_header *) + (header + GRUB_PE32_MSDOS_STUB_SIZE + GRUB_PE32_SIGNATURE_SIZE + + sizeof (struct grub_pe32_coff_header)); + o->magic = grub_host_to_target16 (GRUB_PE32_PE32_MAGIC); + o->code_size = grub_host_to_target32 (exec_size); + o->data_size = grub_cpu_to_le32 (reloc_addr - exec_size + - header_size); + o->bss_size = grub_cpu_to_le32 (bss_size); + o->entry_addr = grub_cpu_to_le32 (start_address); + o->code_base = grub_cpu_to_le32 (header_size); + + o->data_base = grub_host_to_target32 (header_size + exec_size); + + o->image_base = 0; + o->section_alignment = grub_host_to_target32 (image_target->section_align); + o->file_alignment = grub_host_to_target32 (image_target->section_align); + o->image_size = grub_host_to_target32 (pe_size); + o->header_size = grub_host_to_target32 (header_size); + o->subsystem = grub_host_to_target16 (GRUB_PE32_SUBSYSTEM_EFI_APPLICATION); + + /* Do these really matter? */ + o->stack_reserve_size = grub_host_to_target32 (0x10000); + o->stack_commit_size = grub_host_to_target32 (0x10000); + o->heap_reserve_size = grub_host_to_target32 (0x10000); + o->heap_commit_size = grub_host_to_target32 (0x10000); + + o->num_data_directories = grub_host_to_target32 (GRUB_PE32_NUM_DATA_DIRECTORIES); + + o->base_relocation_table.rva = grub_host_to_target32 (reloc_addr); + o->base_relocation_table.size = grub_host_to_target32 (reloc_size); + sections = o + 1; + } + else + { + struct grub_pe64_optional_header *o; + + c->optional_header_size = grub_host_to_target16 (sizeof (struct grub_pe64_optional_header)); + + o = (struct grub_pe64_optional_header *) + (header + GRUB_PE32_MSDOS_STUB_SIZE + GRUB_PE32_SIGNATURE_SIZE + + sizeof (struct grub_pe32_coff_header)); + o->magic = grub_host_to_target16 (GRUB_PE32_PE64_MAGIC); + o->code_size = grub_host_to_target32 (exec_size); + o->data_size = grub_cpu_to_le32 (reloc_addr - exec_size + - header_size); + o->bss_size = grub_cpu_to_le32 (bss_size); + o->entry_addr = grub_cpu_to_le32 (start_address); + o->code_base = grub_cpu_to_le32 (header_size); + o->image_base = 0; + o->section_alignment = grub_host_to_target32 (image_target->section_align); + o->file_alignment = grub_host_to_target32 (image_target->section_align); + o->image_size = grub_host_to_target32 (pe_size); + o->header_size = grub_host_to_target32 (header_size); + o->subsystem = grub_host_to_target16 (GRUB_PE32_SUBSYSTEM_EFI_APPLICATION); + + /* Do these really matter? */ + o->stack_reserve_size = grub_host_to_target32 (0x10000); + o->stack_commit_size = grub_host_to_target32 (0x10000); + o->heap_reserve_size = grub_host_to_target32 (0x10000); + o->heap_commit_size = grub_host_to_target32 (0x10000); + + o->num_data_directories + = grub_host_to_target32 (GRUB_PE32_NUM_DATA_DIRECTORIES); + + o->base_relocation_table.rva = grub_host_to_target32 (reloc_addr); + o->base_relocation_table.size = grub_host_to_target32 (reloc_size); + sections = o + 1; + } + /* The sections. */ + text_section = sections; + strcpy (text_section->name, ".text"); + text_section->virtual_size = grub_cpu_to_le32 (exec_size); + text_section->virtual_address = grub_cpu_to_le32 (header_size); + text_section->raw_data_size = grub_cpu_to_le32 (exec_size); + text_section->raw_data_offset = grub_cpu_to_le32 (header_size); + text_section->characteristics = grub_cpu_to_le32 (GRUB_PE32_SCN_CNT_CODE + | GRUB_PE32_SCN_MEM_EXECUTE + | GRUB_PE32_SCN_MEM_READ); + + data_section = text_section + 1; + strcpy (data_section->name, ".data"); + data_section->virtual_size = grub_cpu_to_le32 (kernel_size - exec_size); + data_section->virtual_address = grub_cpu_to_le32 (header_size + exec_size); + data_section->raw_data_size = grub_cpu_to_le32 (kernel_size - exec_size); + data_section->raw_data_offset = grub_cpu_to_le32 (header_size + exec_size); + data_section->characteristics + = grub_cpu_to_le32 (GRUB_PE32_SCN_CNT_INITIALIZED_DATA + | GRUB_PE32_SCN_MEM_READ + | GRUB_PE32_SCN_MEM_WRITE); + +#if 0 + bss_section = data_section + 1; + strcpy (bss_section->name, ".bss"); + bss_section->virtual_size = grub_cpu_to_le32 (bss_size); + bss_section->virtual_address = grub_cpu_to_le32 (header_size + kernel_size); + bss_section->raw_data_size = 0; + bss_section->raw_data_offset = 0; + bss_section->characteristics + = grub_cpu_to_le32 (GRUB_PE32_SCN_MEM_READ + | GRUB_PE32_SCN_MEM_WRITE + | GRUB_PE32_SCN_ALIGN_64BYTES + | GRUB_PE32_SCN_CNT_INITIALIZED_DATA + | 0x80); +#endif + + mods_section = data_section + 1; + strcpy (mods_section->name, "mods"); + mods_section->virtual_size = grub_cpu_to_le32 (reloc_addr - kernel_size - header_size); + mods_section->virtual_address = grub_cpu_to_le32 (header_size + kernel_size + bss_size); + mods_section->raw_data_size = grub_cpu_to_le32 (reloc_addr - kernel_size - header_size); + mods_section->raw_data_offset = grub_cpu_to_le32 (header_size + kernel_size); + mods_section->characteristics + = grub_cpu_to_le32 (GRUB_PE32_SCN_CNT_INITIALIZED_DATA + | GRUB_PE32_SCN_MEM_READ + | GRUB_PE32_SCN_MEM_WRITE); + + reloc_section = mods_section + 1; + strcpy (reloc_section->name, ".reloc"); + reloc_section->virtual_size = grub_cpu_to_le32 (reloc_size); + reloc_section->virtual_address = grub_cpu_to_le32 (reloc_addr + bss_size); + reloc_section->raw_data_size = grub_cpu_to_le32 (reloc_size); + reloc_section->raw_data_offset = grub_cpu_to_le32 (reloc_addr); + reloc_section->characteristics + = grub_cpu_to_le32 (GRUB_PE32_SCN_CNT_INITIALIZED_DATA + | GRUB_PE32_SCN_MEM_DISCARDABLE + | GRUB_PE32_SCN_MEM_READ); + free (core_img); + core_img = pe_img; + core_size = pe_size; + } + break; + case IMAGE_QEMU: + { + char *rom_img; + size_t rom_size; + char *boot_path, *boot_img; + size_t boot_size; + + boot_path = grub_util_get_path (dir, "boot.img"); + boot_size = grub_util_get_image_size (boot_path); + boot_img = grub_util_read_image (boot_path); + + /* Rom sizes must be 64k-aligned. */ + rom_size = ALIGN_UP (core_size + boot_size, 64 * 1024); + + rom_img = xmalloc (rom_size); + memset (rom_img, 0, rom_size); + + *((grub_int32_t *) (core_img + GRUB_KERNEL_I386_QEMU_CORE_ENTRY_ADDR)) + = grub_host_to_target32 ((grub_uint32_t) -rom_size); + + memcpy (rom_img, core_img, core_size); + + *((grub_int32_t *) (boot_img + GRUB_BOOT_I386_QEMU_CORE_ENTRY_ADDR)) + = grub_host_to_target32 ((grub_uint32_t) -rom_size); + + memcpy (rom_img + rom_size - boot_size, boot_img, boot_size); + + free (core_img); + core_img = rom_img; + core_size = rom_size; + + free (boot_img); + free (boot_path); + } + break; + case IMAGE_SPARC64_AOUT: + { + void *aout_img; + size_t aout_size; + struct grub_aout32_header *aout_head; + + aout_size = core_size + sizeof (*aout_head); + aout_img = xmalloc (aout_size); + aout_head = aout_img; + aout_head->a_midmag = grub_host_to_target32 ((AOUT_MID_SUN << 16) + | AOUT32_OMAGIC); + aout_head->a_text = grub_host_to_target32 (core_size); + aout_head->a_entry + = grub_host_to_target32 (GRUB_BOOT_SPARC64_IEEE1275_IMAGE_ADDRESS); + memcpy (aout_img + sizeof (*aout_head), core_img, core_size); + + free (core_img); + core_img = aout_img; + core_size = aout_size; + } + break; + case IMAGE_SPARC64_RAW: + { + unsigned int num; + char *boot_path, *boot_img; + size_t boot_size; + + num = ((core_size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS); + num <<= GRUB_DISK_SECTOR_BITS; + + boot_path = grub_util_get_path (dir, "diskboot.img"); + boot_size = grub_util_get_image_size (boot_path); + if (boot_size != GRUB_DISK_SECTOR_SIZE) + grub_util_error ("diskboot.img is not one sector size"); + + boot_img = grub_util_read_image (boot_path); + + *((grub_uint32_t *) (boot_img + GRUB_DISK_SECTOR_SIZE + - GRUB_BOOT_SPARC64_IEEE1275_LIST_SIZE + 8)) + = grub_host_to_target32 (num); + + grub_util_write_image (boot_img, boot_size, out); + free (boot_img); + free (boot_path); + } + break; + case IMAGE_YEELOONG_ELF: + case IMAGE_PPC: + case IMAGE_COREBOOT: + case IMAGE_I386_IEEE1275: + { + char *elf_img; + size_t program_size; + Elf32_Ehdr *ehdr; + Elf32_Phdr *phdr; + grub_uint32_t target_addr; + int header_size, footer_size = 0; + int phnum = 1; + + if (image_target->id != IMAGE_YEELOONG_ELF) + phnum += 2; + + if (note) + { + phnum++; + footer_size += sizeof (struct grub_ieee1275_note); + } + header_size = ALIGN_ADDR (sizeof (*ehdr) + phnum * sizeof (*phdr)); + + program_size = ALIGN_ADDR (core_size); + + elf_img = xmalloc (program_size + header_size + footer_size); + memset (elf_img, 0, program_size + header_size); + memcpy (elf_img + header_size, core_img, core_size); + ehdr = (void *) elf_img; + phdr = (void *) (elf_img + sizeof (*ehdr)); + memcpy (ehdr->e_ident, ELFMAG, SELFMAG); + ehdr->e_ident[EI_CLASS] = ELFCLASS32; + if (!image_target->bigendian) + ehdr->e_ident[EI_DATA] = ELFDATA2LSB; + else + ehdr->e_ident[EI_DATA] = ELFDATA2MSB; + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE; + ehdr->e_type = grub_host_to_target16 (ET_EXEC); + ehdr->e_machine = grub_host_to_target16 (image_target->elf_target); + ehdr->e_version = grub_host_to_target32 (EV_CURRENT); + + ehdr->e_phoff = grub_host_to_target32 ((char *) phdr - (char *) ehdr); + ehdr->e_phentsize = grub_host_to_target16 (sizeof (*phdr)); + ehdr->e_phnum = grub_host_to_target16 (phnum); + + /* No section headers. */ + ehdr->e_shoff = grub_host_to_target32 (0); + if (image_target->id == IMAGE_YEELOONG_ELF) + ehdr->e_shentsize = grub_host_to_target16 (0); + else + ehdr->e_shentsize = grub_host_to_target16 (sizeof (Elf32_Shdr)); + ehdr->e_shnum = grub_host_to_target16 (0); + ehdr->e_shstrndx = grub_host_to_target16 (0); + + ehdr->e_ehsize = grub_host_to_target16 (sizeof (*ehdr)); + + phdr->p_type = grub_host_to_target32 (PT_LOAD); + phdr->p_offset = grub_host_to_target32 (header_size); + phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X); + + if (image_target->id == IMAGE_YEELOONG_ELF) + target_addr = ALIGN_UP (image_target->link_addr + + kernel_size + total_module_size, 32); + else + target_addr = image_target->link_addr; + ehdr->e_entry = grub_host_to_target32 (target_addr); + phdr->p_vaddr = grub_host_to_target32 (target_addr); + phdr->p_paddr = grub_host_to_target32 (target_addr); + phdr->p_align = grub_host_to_target32 (align > image_target->link_align ? align : image_target->link_align); + if (image_target->id == IMAGE_YEELOONG_ELF) + ehdr->e_flags = grub_host_to_target32 (0x1000 | EF_MIPS_NOREORDER + | EF_MIPS_PIC | EF_MIPS_CPIC); + else + ehdr->e_flags = 0; + if (image_target->id == IMAGE_YEELOONG_ELF) + { + phdr->p_filesz = grub_host_to_target32 (core_size); + phdr->p_memsz = grub_host_to_target32 (core_size); + } + else + { + grub_uint32_t target_addr_mods; + phdr->p_filesz = grub_host_to_target32 (kernel_size); + phdr->p_memsz = grub_host_to_target32 (kernel_size + bss_size); + + phdr++; + phdr->p_type = grub_host_to_target32 (PT_GNU_STACK); + phdr->p_offset = grub_host_to_target32 (header_size + kernel_size); + phdr->p_paddr = phdr->p_vaddr = phdr->p_filesz = phdr->p_memsz = 0; + phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X); + phdr->p_align = grub_host_to_target32 (image_target->link_align); + + phdr++; + phdr->p_type = grub_host_to_target32 (PT_LOAD); + phdr->p_offset = grub_host_to_target32 (header_size + kernel_size); + phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X); + phdr->p_filesz = phdr->p_memsz + = grub_host_to_target32 (core_size - kernel_size); + + target_addr_mods = ALIGN_UP (target_addr + kernel_size + bss_size + + image_target->mod_gap, + image_target->mod_align); + phdr->p_vaddr = grub_host_to_target32 (target_addr_mods); + phdr->p_paddr = grub_host_to_target32 (target_addr_mods); + phdr->p_align = grub_host_to_target32 (image_target->link_align); + } + + if (note) + { + int note_size = sizeof (struct grub_ieee1275_note); + struct grub_ieee1275_note *note = (struct grub_ieee1275_note *) + (elf_img + program_size + header_size); + + grub_util_info ("adding CHRP NOTE segment"); + + note->header.namesz = grub_host_to_target32 (sizeof (GRUB_IEEE1275_NOTE_NAME)); + note->header.descsz = grub_host_to_target32 (note_size); + note->header.type = grub_host_to_target32 (GRUB_IEEE1275_NOTE_TYPE); + strcpy (note->header.name, GRUB_IEEE1275_NOTE_NAME); + note->descriptor.real_mode = grub_host_to_target32 (0xffffffff); + note->descriptor.real_base = grub_host_to_target32 (0x00c00000); + note->descriptor.real_size = grub_host_to_target32 (0xffffffff); + note->descriptor.virt_base = grub_host_to_target32 (0xffffffff); + note->descriptor.virt_size = grub_host_to_target32 (0xffffffff); + note->descriptor.load_base = grub_host_to_target32 (0x00004000); + + phdr++; + phdr->p_type = grub_host_to_target32 (PT_NOTE); + phdr->p_flags = grub_host_to_target32 (PF_R); + phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof); + phdr->p_vaddr = 0; + phdr->p_paddr = 0; + phdr->p_filesz = grub_host_to_target32 (note_size); + phdr->p_memsz = 0; + phdr->p_offset = grub_host_to_target32 (header_size + program_size); + } + + free (core_img); + core_img = elf_img; + core_size = program_size + header_size + footer_size; + } + break; + } + + grub_util_write_image (core_img, core_size, out); + free (kernel_img); + free (core_img); + free (kernel_path); + + while (path_list) + { + next = path_list->next; + free ((void *) path_list->name); + free (path_list); + path_list = next; + } +} + + + +static struct option options[] = + { + {"directory", required_argument, 0, 'd'}, + {"prefix", required_argument, 0, 'p'}, + {"memdisk", required_argument, 0, 'm'}, + {"font", required_argument, 0, 'f'}, + {"config", required_argument, 0, 'c'}, + {"output", required_argument, 0, 'o'}, + {"note", no_argument, 0, 'n'}, + {"format", required_argument, 0, 'O'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {"verbose", no_argument, 0, 'v'}, + {0, 0, 0, 0} + }; + +static void +usage (int status) +{ + if (status) + fprintf (stderr, _("Try `%s --help' for more information.\n"), program_name); + else + { + int format_len = 0; + char *formats; + char *ptr; + unsigned i; + for (i = 0; i < ARRAY_SIZE (image_targets); i++) + format_len += strlen (image_targets[i].name) + 2; + ptr = formats = xmalloc (format_len); + for (i = 0; i < ARRAY_SIZE (image_targets); i++) + { + strcpy (ptr, image_targets[i].name); + ptr += strlen (image_targets[i].name); + *ptr++ = ','; + *ptr++ = ' '; + } + ptr[-2] = 0; + + printf (_("\ +Usage: %s [OPTION]... [MODULES]\n\ +\n\ +Make a bootable image of GRUB.\n\ +\n\ + -d, --directory=DIR use images and modules under DIR [default=%s/@platform@]\n\ + -p, --prefix=DIR set grub_prefix directory [default=%s]\n\ + -m, --memdisk=FILE embed FILE as a memdisk image\n\ + -f, --font=FILE embed FILE as a boot font\n\ + -c, --config=FILE embed FILE as boot config\n\ + -n, --note add NOTE segment for CHRP Open Firmware\n\ + -o, --output=FILE output a generated image to FILE [default=stdout]\n\ + -O, --format=FORMAT generate an image in format\n\ + available formats: %s\n\ + -h, --help display this message and exit\n\ + -V, --version print version information and exit\n\ + -v, --verbose print verbose messages\n\ +\n\ +Report bugs to <%s>.\n\ +"), + program_name, GRUB_LIBDIR, DEFAULT_DIRECTORY, + formats, + PACKAGE_BUGREPORT); + free (formats); + } + exit (status); +} + +int +main (int argc, char *argv[]) +{ + char *output = NULL; + char *dir = NULL; + char *prefix = NULL; + char *memdisk = NULL; + char *font = NULL; + char *config = NULL; + FILE *fp = stdout; + int note = 0; + struct image_target_desc *image_target = NULL; + + set_program_name (argv[0]); + + grub_util_init_nls (); + + while (1) + { + int c = getopt_long (argc, argv, "d:p:m:c:o:O:f:hVvn", options, 0); + + if (c == -1) + break; + else + switch (c) + { + case 'o': + if (output) + free (output); + + output = xstrdup (optarg); + break; + + case 'O': + { + unsigned i; + for (i = 0; i < ARRAY_SIZE (image_targets); i++) + if (strcmp (optarg, image_targets[i].name) == 0) + image_target = &image_targets[i]; + if (!image_target) + { + printf ("unknown target %s\n", optarg); + usage (1); + } + break; + } + case 'd': + if (dir) + free (dir); + + dir = xstrdup (optarg); + break; + + case 'n': + note = 1; + break; + + case 'm': + if (memdisk) + free (memdisk); + + memdisk = xstrdup (optarg); + + if (prefix) + free (prefix); + + prefix = xstrdup ("(memdisk)/boot/grub"); + break; + + case 'f': + if (font) + free (font); + + font = xstrdup (optarg); + break; + + case 'c': + if (config) + free (config); + + config = xstrdup (optarg); + break; + + case 'h': + usage (0); + break; + + case 'p': + if (prefix) + free (prefix); + + prefix = xstrdup (optarg); + break; + + case 'V': + printf ("grub-mkimage (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); + return 0; + + case 'v': + verbosity++; + break; + + default: + usage (1); + break; + } + } + + if (!image_target) + { + printf ("Target not specified.\n"); + usage (1); + } + + if (output) + { + fp = fopen (output, "wb"); + if (! fp) + grub_util_error (_("cannot open %s"), output); + free (output); + } + + if (!dir) + { + const char *last; + last = strchr (image_target->name, '-'); + if (last) + last = strchr (last + 1, '-'); + if (!last) + last = image_target->name + strlen (image_target->name); + dir = xmalloc (sizeof (GRUB_LIBDIR) + (last - image_target->name)); + memcpy (dir, GRUB_LIBDIR, sizeof (GRUB_LIBDIR) - 1); + memcpy (dir + sizeof (GRUB_LIBDIR) - 1, image_target->name, + last - image_target->name); + *(dir + sizeof (GRUB_LIBDIR) - 1 + (last - image_target->name)) = 0; + } + + generate_image (dir ? : GRUB_LIBDIR, prefix ? : DEFAULT_DIRECTORY, fp, + argv + optind, memdisk, font, config, + image_target, note); + + fclose (fp); + + if (dir) + free (dir); + + return 0; +} diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c new file mode 100644 index 000000000..4a257e329 --- /dev/null +++ b/util/grub-mkimagexx.c @@ -0,0 +1,756 @@ +/* grub-mkimage.c - make a bootable image */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#undef ELF_R_SYM +#undef ELF_R_TYPE + +#if defined(MKIMAGE_ELF32) +# define SUFFIX(x) x ## 32 +# define ELFCLASSXX ELFCLASS32 +# define Elf_Ehdr Elf32_Ehdr +# define Elf_Phdr Elf32_Phdr +# define Elf_Addr Elf32_Addr +# define Elf_Sym Elf32_Sym +# define Elf_Off Elf32_Off +# define Elf_Shdr Elf32_Shdr +# define Elf_Rela Elf32_Rela +# define Elf_Rel Elf32_Rel +# define ELF_R_SYM(val) ELF32_R_SYM(val) +# define ELF_R_TYPE(val) ELF32_R_TYPE(val) +#elif defined(MKIMAGE_ELF64) +# define SUFFIX(x) x ## 64 +# define ELFCLASSXX ELFCLASS64 +# define Elf_Ehdr Elf64_Ehdr +# define Elf_Phdr Elf64_Phdr +# define Elf_Addr Elf64_Addr +# define Elf_Sym Elf64_Sym +# define Elf_Off Elf64_Off +# define Elf_Shdr Elf64_Shdr +# define Elf_Rela Elf64_Rela +# define Elf_Rel Elf64_Rel +# define ELF_R_SYM(val) ELF64_R_SYM(val) +# define ELF_R_TYPE(val) ELF64_R_TYPE(val) +#else +#error "I'm confused" +#endif + +/* Relocate symbols; note that this function overwrites the symbol table. + Return the address of a start symbol. */ +static Elf_Addr +SUFFIX (relocate_symbols) (Elf_Ehdr *e, Elf_Shdr *sections, + Elf_Shdr *symtab_section, Elf_Addr *section_addresses, + Elf_Half section_entsize, Elf_Half num_sections, + struct image_target_desc *image_target) +{ + Elf_Word symtab_size, sym_size, num_syms; + Elf_Off symtab_offset; + Elf_Addr start_address = 0; + Elf_Sym *sym; + Elf_Word i; + Elf_Shdr *strtab_section; + const char *strtab; + + strtab_section + = (Elf_Shdr *) ((char *) sections + + (grub_target_to_host32 (symtab_section->sh_link) + * section_entsize)); + strtab = (char *) e + grub_target_to_host (strtab_section->sh_offset); + + symtab_size = grub_target_to_host (symtab_section->sh_size); + sym_size = grub_target_to_host (symtab_section->sh_entsize); + symtab_offset = grub_target_to_host (symtab_section->sh_offset); + num_syms = symtab_size / sym_size; + + for (i = 0, sym = (Elf_Sym *) ((char *) e + symtab_offset); + i < num_syms; + i++, sym = (Elf_Sym *) ((char *) sym + sym_size)) + { + Elf_Section index; + const char *name; + + name = strtab + grub_target_to_host32 (sym->st_name); + + index = grub_target_to_host16 (sym->st_shndx); + if (index == STN_ABS) + { + continue; + } + else if ((index == STN_UNDEF)) + { + if (sym->st_name) + grub_util_error ("undefined symbol %s", name); + else + continue; + } + else if (index >= num_sections) + grub_util_error ("section %d does not exist", index); + + sym->st_value = (grub_target_to_host32 (sym->st_value) + + section_addresses[index]); + grub_util_info ("locating %s at 0x%x", name, sym->st_value); + + if (! start_address) + if (strcmp (name, "_start") == 0 || strcmp (name, "start") == 0) + start_address = sym->st_value; + } + + return start_address; +} + +/* Return the address of a symbol at the index I in the section S. */ +static Elf_Addr +SUFFIX (get_symbol_address) (Elf_Ehdr *e, Elf_Shdr *s, Elf_Word i, + struct image_target_desc *image_target) +{ + Elf_Sym *sym; + + sym = (Elf_Sym *) ((char *) e + + grub_target_to_host32 (s->sh_offset) + + i * grub_target_to_host32 (s->sh_entsize)); + return sym->st_value; +} + +/* Return the address of a modified value. */ +static Elf_Addr * +SUFFIX (get_target_address) (Elf_Ehdr *e, Elf_Shdr *s, Elf_Addr offset, + struct image_target_desc *image_target) +{ + return (Elf_Addr *) ((char *) e + grub_target_to_host32 (s->sh_offset) + offset); +} + +/* Deal with relocation information. This function relocates addresses + within the virtual address space starting from 0. So only relative + addresses can be fully resolved. Absolute addresses must be relocated + again by a PE32 relocator when loaded. */ +static void +SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections, + Elf_Addr *section_addresses, + Elf_Half section_entsize, Elf_Half num_sections, + const char *strtab, struct image_target_desc *image_target) +{ + Elf_Half i; + Elf_Shdr *s; + + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if ((s->sh_type == grub_host_to_target32 (SHT_REL)) || + (s->sh_type == grub_host_to_target32 (SHT_RELA))) + { + Elf_Rela *r; + Elf_Word rtab_size, r_size, num_rs; + Elf_Off rtab_offset; + Elf_Shdr *symtab_section; + Elf_Word target_section_index; + Elf_Addr target_section_addr; + Elf_Shdr *target_section; + Elf_Word j; + + symtab_section = (Elf_Shdr *) ((char *) sections + + (grub_target_to_host32 (s->sh_link) + * section_entsize)); + target_section_index = grub_target_to_host32 (s->sh_info); + target_section_addr = section_addresses[target_section_index]; + target_section = (Elf_Shdr *) ((char *) sections + + (target_section_index + * section_entsize)); + + grub_util_info ("dealing with the relocation section %s for %s", + strtab + grub_target_to_host32 (s->sh_name), + strtab + grub_target_to_host32 (target_section->sh_name)); + + rtab_size = grub_target_to_host32 (s->sh_size); + r_size = grub_target_to_host32 (s->sh_entsize); + rtab_offset = grub_target_to_host32 (s->sh_offset); + num_rs = rtab_size / r_size; + + for (j = 0, r = (Elf_Rela *) ((char *) e + rtab_offset); + j < num_rs; + j++, r = (Elf_Rela *) ((char *) r + r_size)) + { + Elf_Addr info; + Elf_Addr offset; + Elf_Addr sym_addr; + Elf_Addr *target; + Elf_Addr addend; + + offset = grub_target_to_host (r->r_offset); + target = SUFFIX (get_target_address) (e, target_section, + offset, image_target); + info = grub_target_to_host (r->r_info); + sym_addr = SUFFIX (get_symbol_address) (e, symtab_section, + ELF_R_SYM (info), image_target); + + addend = (s->sh_type == grub_target_to_host32 (SHT_RELA)) ? + r->r_addend : 0; + + if (image_target->voidp_sizeof == 4) + switch (ELF_R_TYPE (info)) + { + case R_386_NONE: + break; + + case R_386_32: + /* This is absolute. */ + *target = grub_host_to_target32 (grub_target_to_host32 (*target) + + addend + sym_addr); + grub_util_info ("relocating an R_386_32 entry to 0x%x at the offset 0x%x", + *target, offset); + break; + + case R_386_PC32: + /* This is relative. */ + *target = grub_host_to_target32 (grub_target_to_host32 (*target) + + addend + sym_addr + - target_section_addr - offset + - image_target->vaddr_offset); + grub_util_info ("relocating an R_386_PC32 entry to 0x%x at the offset 0x%x", + *target, offset); + break; + default: + grub_util_error ("unknown relocation type %d", + ELF_R_TYPE (info)); + break; + } + else + switch (ELF_R_TYPE (info)) + { + + case R_X86_64_NONE: + break; + + case R_X86_64_64: + *target = grub_host_to_target64 (grub_target_to_host64 (*target) + + addend + sym_addr); + grub_util_info ("relocating an R_X86_64_64 entry to 0x%llx at the offset 0x%llx", + *target, offset); + break; + + case R_X86_64_PC32: + { + grub_uint32_t *t32 = (grub_uint32_t *) target; + *t32 = grub_host_to_target64 (grub_target_to_host32 (*t32) + + addend + sym_addr + - target_section_addr - offset + - image_target->vaddr_offset); + grub_util_info ("relocating an R_X86_64_PC32 entry to 0x%x at the offset 0x%llx", + *t32, offset); + break; + } + + case R_X86_64_32: + case R_X86_64_32S: + { + grub_uint32_t *t32 = (grub_uint32_t *) target; + *t32 = grub_host_to_target64 (grub_target_to_host32 (*t32) + + addend + sym_addr); + grub_util_info ("relocating an R_X86_64_32(S) entry to 0x%x at the offset 0x%llx", + *t32, offset); + break; + } + + default: + grub_util_error ("unknown relocation type %d", + ELF_R_TYPE (info)); + break; + } + } + } +} + +/* Add a PE32's fixup entry for a relocation. Return the resulting address + after having written to the file OUT. */ +static Elf_Addr +SUFFIX (add_fixup_entry) (struct fixup_block_list **cblock, grub_uint16_t type, + Elf_Addr addr, int flush, Elf_Addr current_address, + struct image_target_desc *image_target) +{ + struct grub_pe32_fixup_block *b; + + b = &((*cblock)->b); + + /* First, check if it is necessary to write out the current block. */ + if ((*cblock)->state) + { + if (flush || addr < b->page_rva || b->page_rva + 0x1000 <= addr) + { + grub_uint32_t size; + + if (flush) + { + /* Add as much padding as necessary to align the address + with a section boundary. */ + Elf_Addr next_address; + unsigned padding_size; + size_t index; + + next_address = current_address + b->block_size; + padding_size = ((ALIGN_UP (next_address, image_target->section_align) + - next_address) + >> 1); + index = ((b->block_size - sizeof (*b)) >> 1); + grub_util_info ("adding %d padding fixup entries", padding_size); + while (padding_size--) + { + b->entries[index++] = 0; + b->block_size += 2; + } + } + else if (b->block_size & (8 - 1)) + { + /* If not aligned with a 32-bit boundary, add + a padding entry. */ + size_t index; + + grub_util_info ("adding a padding fixup entry"); + index = ((b->block_size - sizeof (*b)) >> 1); + b->entries[index] = 0; + b->block_size += 2; + } + + /* Flush it. */ + grub_util_info ("writing %d bytes of a fixup block starting at 0x%x", + b->block_size, b->page_rva); + size = b->block_size; + current_address += size; + b->page_rva = grub_host_to_target32 (b->page_rva); + b->block_size = grub_host_to_target32 (b->block_size); + (*cblock)->next = xmalloc (sizeof (**cblock) + 2 * 0x1000); + memset ((*cblock)->next, 0, sizeof (**cblock) + 2 * 0x1000); + *cblock = (*cblock)->next; + } + } + + b = &((*cblock)->b); + + if (! flush) + { + grub_uint16_t entry; + size_t index; + + /* If not allocated yet, allocate a block with enough entries. */ + if (! (*cblock)->state) + { + (*cblock)->state = 1; + + /* The spec does not mention the requirement of a Page RVA. + Here, align the address with a 4K boundary for safety. */ + b->page_rva = (addr & ~(0x1000 - 1)); + b->block_size = sizeof (*b); + } + + /* Sanity check. */ + if (b->block_size >= sizeof (*b) + 2 * 0x1000) + grub_util_error ("too many fixup entries"); + + /* Add a new entry. */ + index = ((b->block_size - sizeof (*b)) >> 1); + entry = GRUB_PE32_FIXUP_ENTRY (type, addr - b->page_rva); + b->entries[index] = grub_host_to_target16 (entry); + b->block_size += 2; + } + + return current_address; +} + +/* Make a .reloc section. */ +static Elf_Addr +SUFFIX (make_reloc_section) (Elf_Ehdr *e, void **out, + Elf_Addr *section_addresses, Elf_Shdr *sections, + Elf_Half section_entsize, Elf_Half num_sections, + const char *strtab, struct image_target_desc *image_target) +{ + Elf_Half i; + Elf_Shdr *s; + struct fixup_block_list *lst, *lst0; + Elf_Addr current_address = 0; + + lst = lst0 = xmalloc (sizeof (*lst) + 2 * 0x1000); + memset (lst, 0, sizeof (*lst) + 2 * 0x1000); + + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if ((s->sh_type == grub_cpu_to_le32 (SHT_REL)) || + (s->sh_type == grub_cpu_to_le32 (SHT_RELA))) + { + Elf_Rel *r; + Elf_Word rtab_size, r_size, num_rs; + Elf_Off rtab_offset; + Elf_Addr section_address; + Elf_Word j; + + grub_util_info ("translating the relocation section %s", + strtab + grub_le_to_cpu32 (s->sh_name)); + + rtab_size = grub_le_to_cpu32 (s->sh_size); + r_size = grub_le_to_cpu32 (s->sh_entsize); + rtab_offset = grub_le_to_cpu32 (s->sh_offset); + num_rs = rtab_size / r_size; + + section_address = section_addresses[grub_le_to_cpu32 (s->sh_info)]; + + for (j = 0, r = (Elf_Rel *) ((char *) e + rtab_offset); + j < num_rs; + j++, r = (Elf_Rel *) ((char *) r + r_size)) + { + Elf_Addr info; + Elf_Addr offset; + + offset = grub_le_to_cpu32 (r->r_offset); + info = grub_le_to_cpu32 (r->r_info); + + /* Necessary to relocate only absolute addresses. */ + if (image_target->voidp_sizeof == 4) + { + if (ELF_R_TYPE (info) == R_386_32) + { + Elf_Addr addr; + + addr = section_address + offset; + grub_util_info ("adding a relocation entry for 0x%x", addr); + current_address + = SUFFIX (add_fixup_entry) (&lst, + GRUB_PE32_REL_BASED_HIGHLOW, + addr, 0, current_address, + image_target); + } + } + else + { + if ((ELF_R_TYPE (info) == R_X86_64_32) || + (ELF_R_TYPE (info) == R_X86_64_32S)) + { + grub_util_error ("can\'t add fixup entry for R_X86_64_32(S)"); + } + else if (ELF_R_TYPE (info) == R_X86_64_64) + { + Elf_Addr addr; + + addr = section_address + offset; + grub_util_info ("adding a relocation entry for 0x%llx", addr); + current_address + = SUFFIX (add_fixup_entry) (&lst, + GRUB_PE32_REL_BASED_DIR64, + addr, + 0, current_address, + image_target); + } + } + } + } + + current_address = SUFFIX (add_fixup_entry) (&lst, 0, 0, 1, current_address, image_target); + + { + grub_uint8_t *ptr; + ptr = *out = xmalloc (current_address); + for (lst = lst0; lst; lst = lst->next) + if (lst->state) + { + memcpy (ptr, &lst->b, grub_target_to_host32 (lst->b.block_size)); + ptr += grub_target_to_host32 (lst->b.block_size); + } + if (current_address + *out != ptr) + { + grub_util_error ("Bug detected %d != %d\n", ptr - (grub_uint8_t *) *out, + current_address); + } + } + + return current_address; +} + +/* Determine if this section is a text section. Return false if this + section is not allocated. */ +static int +SUFFIX (is_text_section) (Elf_Shdr *s, struct image_target_desc *image_target) +{ + if (image_target->id != IMAGE_EFI + && grub_target_to_host32 (s->sh_type) != SHT_PROGBITS) + return 0; + return ((grub_target_to_host (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC)) + == (SHF_EXECINSTR | SHF_ALLOC)); +} + +/* Determine if this section is a data section. This assumes that + BSS is also a data section, since the converter initializes BSS + when producing PE32 to avoid a bug in EFI implementations. */ +static int +SUFFIX (is_data_section) (Elf_Shdr *s, struct image_target_desc *image_target) +{ + if (image_target->id != IMAGE_EFI + && grub_target_to_host32 (s->sh_type) != SHT_PROGBITS) + return 0; + return ((grub_target_to_host (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC)) + == SHF_ALLOC); +} + +/* Return if the ELF header is valid. */ +static int +SUFFIX (check_elf_header) (Elf_Ehdr *e, size_t size, struct image_target_desc *image_target) +{ + if (size < sizeof (*e) + || e->e_ident[EI_MAG0] != ELFMAG0 + || e->e_ident[EI_MAG1] != ELFMAG1 + || e->e_ident[EI_MAG2] != ELFMAG2 + || e->e_ident[EI_MAG3] != ELFMAG3 + || e->e_ident[EI_VERSION] != EV_CURRENT + || e->e_ident[EI_CLASS] != ELFCLASSXX + || e->e_version != grub_host_to_target32 (EV_CURRENT)) + return 0; + + return 1; +} + +/* Locate section addresses by merging code sections and data sections + into .text and .data, respectively. Return the array of section + addresses. */ +static Elf_Addr * +SUFFIX (locate_sections) (Elf_Shdr *sections, Elf_Half section_entsize, + Elf_Half num_sections, const char *strtab, + grub_size_t *exec_size, grub_size_t *kernel_sz, + grub_size_t *all_align, + struct image_target_desc *image_target) +{ + int i; + Elf_Addr current_address; + Elf_Addr *section_addresses; + Elf_Shdr *s; + + *all_align = 1; + + section_addresses = xmalloc (sizeof (*section_addresses) * num_sections); + memset (section_addresses, 0, sizeof (*section_addresses) * num_sections); + + current_address = 0; + + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if ((grub_target_to_host (s->sh_flags) & SHF_ALLOC) + && grub_host_to_target32 (s->sh_addralign) > *all_align) + *all_align = grub_host_to_target32 (s->sh_addralign); + + + /* .text */ + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if (SUFFIX (is_text_section) (s, image_target)) + { + Elf_Word align = grub_host_to_target32 (s->sh_addralign); + const char *name = strtab + grub_host_to_target32 (s->sh_name); + if (align) + current_address = ALIGN_UP (current_address + image_target->vaddr_offset, + align) - image_target->vaddr_offset; + grub_util_info ("locating the section %s at 0x%x", + name, current_address); + section_addresses[i] = current_address; + current_address += grub_host_to_target_addr (s->sh_size); + } + + current_address = ALIGN_UP (current_address + image_target->vaddr_offset, + image_target->section_align) + - image_target->vaddr_offset; + *exec_size = current_address; + + /* .data */ + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if (SUFFIX (is_data_section) (s, image_target)) + { + Elf_Word align = grub_host_to_target32 (s->sh_addralign); + const char *name = strtab + grub_host_to_target32 (s->sh_name); + + if (align) + current_address = ALIGN_UP (current_address + image_target->vaddr_offset, + align) + - image_target->vaddr_offset; + + grub_util_info ("locating the section %s at 0x%x", + name, current_address); + section_addresses[i] = current_address; + current_address += grub_host_to_target_addr (s->sh_size); + } + + current_address = ALIGN_UP (current_address + image_target->vaddr_offset, + image_target->section_align) - image_target->vaddr_offset; + *kernel_sz = current_address; + return section_addresses; +} + +static char * +SUFFIX (load_image) (const char *kernel_path, grub_size_t *exec_size, + grub_size_t *kernel_sz, grub_size_t *bss_size, + grub_size_t total_module_size, grub_uint64_t *start, + void **reloc_section, grub_size_t *reloc_size, + grub_size_t *align, + struct image_target_desc *image_target) +{ + char *kernel_img, *out_img; + const char *strtab; + Elf_Ehdr *e; + Elf_Shdr *sections; + Elf_Addr *section_addresses; + Elf_Addr *section_vaddresses; + int i; + Elf_Shdr *s; + Elf_Half num_sections; + Elf_Off section_offset; + Elf_Half section_entsize; + grub_size_t kernel_size; + Elf_Shdr *symtab_section; + + *start = 0; + + kernel_size = grub_util_get_image_size (kernel_path); + kernel_img = xmalloc (kernel_size); + grub_util_load_image (kernel_path, kernel_img); + + e = (Elf_Ehdr *) kernel_img; + if (! SUFFIX (check_elf_header) (e, kernel_size, image_target)) + grub_util_error ("invalid ELF header"); + + section_offset = grub_target_to_host (e->e_shoff); + section_entsize = grub_target_to_host16 (e->e_shentsize); + num_sections = grub_target_to_host16 (e->e_shnum); + + if (kernel_size < section_offset + section_entsize * num_sections) + grub_util_error ("invalid ELF format"); + + sections = (Elf_Shdr *) (kernel_img + section_offset); + + /* Relocate sections then symbols in the virtual address space. */ + s = (Elf_Shdr *) ((char *) sections + + grub_host_to_target16 (e->e_shstrndx) * section_entsize); + strtab = (char *) e + grub_host_to_target32 (s->sh_offset); + + section_addresses = SUFFIX (locate_sections) (sections, section_entsize, + num_sections, strtab, + exec_size, kernel_sz, align, + image_target); + + section_vaddresses = xmalloc (sizeof (*section_addresses) * num_sections); + + for (i = 0; i < num_sections; i++) + section_vaddresses[i] = section_addresses[i] + image_target->vaddr_offset; + + if (image_target->id != IMAGE_EFI) + { + Elf_Addr current_address = *kernel_sz; + + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if (grub_target_to_host32 (s->sh_type) == SHT_NOBITS) + { + Elf_Word align = grub_host_to_target32 (s->sh_addralign); + const char *name = strtab + grub_host_to_target32 (s->sh_name); + + if (align) + current_address = ALIGN_UP (current_address + + image_target->vaddr_offset, align) + - image_target->vaddr_offset; + + grub_util_info ("locating the section %s at 0x%x", + name, current_address); + section_vaddresses[i] = current_address + + image_target->vaddr_offset; + current_address += grub_host_to_target_addr (s->sh_size); + } + current_address = ALIGN_UP (current_address + image_target->vaddr_offset, + image_target->section_align) + - image_target->vaddr_offset; + *bss_size = current_address - *kernel_sz; + } + else + *bss_size = 0; + + if (image_target->id == IMAGE_EFI) + { + symtab_section = NULL; + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if (s->sh_type == grub_host_to_target32 (SHT_SYMTAB)) + { + symtab_section = s; + break; + } + + if (! symtab_section) + grub_util_error ("no symbol table"); + + *start = SUFFIX (relocate_symbols) (e, sections, symtab_section, + section_vaddresses, section_entsize, + num_sections, image_target); + if (*start == 0) + grub_util_error ("start symbol is not defined"); + + /* Resolve addresses in the virtual address space. */ + SUFFIX (relocate_addresses) (e, sections, section_addresses, section_entsize, + num_sections, strtab, image_target); + + *reloc_size = SUFFIX (make_reloc_section) (e, reloc_section, + section_vaddresses, sections, + section_entsize, num_sections, + strtab, image_target); + } + else + { + *reloc_size = 0; + *reloc_section = NULL; + } + + out_img = xmalloc (*kernel_sz + total_module_size); + + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if (SUFFIX (is_data_section) (s, image_target) + || SUFFIX (is_text_section) (s, image_target)) + { + if (grub_target_to_host32 (s->sh_type) == SHT_NOBITS) + memset (out_img + section_addresses[i], 0, + grub_host_to_target_addr (s->sh_size)); + else + memcpy (out_img + section_addresses[i], + kernel_img + grub_host_to_target_addr (s->sh_offset), + grub_host_to_target_addr (s->sh_size)); + } + free (kernel_img); + + return out_img; +} + + +#undef SUFFIX +#undef ELFCLASSXX +#undef Elf_Ehdr +#undef Elf_Phdr +#undef Elf_Shdr +#undef Elf_Addr +#undef Elf_Sym +#undef Elf_Off +#undef Elf_Rela +#undef Elf_Rel +#undef ELF_R_TYPE +#undef ELF_R_SYM diff --git a/util/grub-mkrawimage.c b/util/grub-mkrawimage.c deleted file mode 100644 index 2316479e9..000000000 --- a/util/grub-mkrawimage.c +++ /dev/null @@ -1,592 +0,0 @@ -/* grub-mkimage.c - make a bootable image */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. - * - * GRUB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GRUB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GRUB. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define _GNU_SOURCE 1 -#include - -#include "progname.h" - -#ifdef ENABLE_LZMA -#include - -static void *SzAlloc(void *p, size_t size) { p = p; return xmalloc(size); } -static void SzFree(void *p, void *address) { p = p; free(address); } -static ISzAlloc g_Alloc = { SzAlloc, SzFree }; - -static void -compress_kernel (char *kernel_img, size_t kernel_size, - char **core_img, size_t *core_size) -{ - CLzmaEncProps props; - unsigned char out_props[5]; - size_t out_props_size = 5; - - LzmaEncProps_Init(&props); - props.dictSize = 1 << 16; - props.lc = 3; - props.lp = 0; - props.pb = 2; - props.numThreads = 1; - - if (kernel_size < GRUB_KERNEL_MACHINE_RAW_SIZE) - grub_util_error (_("the core image is too small")); - - *core_img = xmalloc (kernel_size); - memcpy (*core_img, kernel_img, GRUB_KERNEL_MACHINE_RAW_SIZE); - - *core_size = kernel_size - GRUB_KERNEL_MACHINE_RAW_SIZE; - if (LzmaEncode((unsigned char *) *core_img + GRUB_KERNEL_MACHINE_RAW_SIZE, - core_size, - (unsigned char *) kernel_img + GRUB_KERNEL_MACHINE_RAW_SIZE, - kernel_size - GRUB_KERNEL_MACHINE_RAW_SIZE, - &props, out_props, &out_props_size, - 0, NULL, &g_Alloc, &g_Alloc) != SZ_OK) - grub_util_error (_("cannot compress the kernel image")); - - *core_size += GRUB_KERNEL_MACHINE_RAW_SIZE; -} - -#else /* No lzma compression */ - -static void -compress_kernel (char *kernel_img, size_t kernel_size, - char **core_img, size_t *core_size) -{ - *core_img = xmalloc (kernel_size); - memcpy (*core_img, kernel_img, kernel_size); - *core_size = kernel_size; -} - -#endif /* No lzma compression */ - -static void -generate_image (const char *dir, char *prefix, FILE *out, char *mods[], - char *memdisk_path, char *font_path, char *config_path, -#ifdef GRUB_PLATFORM_IMAGE_DEFAULT - grub_platform_image_format_t format -#else - int dummy __attribute__ ((unused)) -#endif - -) -{ - char *kernel_img, *core_img; - size_t kernel_size, total_module_size, core_size; - size_t memdisk_size = 0, font_size = 0, config_size = 0, config_size_pure = 0; - char *kernel_path; - size_t offset; - struct grub_util_path_list *path_list, *p, *next; - struct grub_module_info *modinfo; - - path_list = grub_util_resolve_dependencies (dir, "moddep.lst", mods); - - kernel_path = grub_util_get_path (dir, "kernel.img"); - kernel_size = grub_util_get_image_size (kernel_path); - - total_module_size = sizeof (struct grub_module_info); - - if (memdisk_path) - { - memdisk_size = ALIGN_UP(grub_util_get_image_size (memdisk_path), 512); - grub_util_info ("the size of memory disk is 0x%x", memdisk_size); - total_module_size += memdisk_size + sizeof (struct grub_module_header); - } - - if (font_path) - { - font_size = ALIGN_UP(grub_util_get_image_size (font_path), 4); - total_module_size += font_size + sizeof (struct grub_module_header); - } - - if (config_path) - { - config_size_pure = grub_util_get_image_size (config_path) + 1; - config_size = ALIGN_UP(config_size_pure, 4); - grub_util_info ("the size of config file is 0x%x", config_size); - total_module_size += config_size + sizeof (struct grub_module_header); - } - - for (p = path_list; p; p = p->next) - total_module_size += (grub_util_get_image_size (p->name) - + sizeof (struct grub_module_header)); - - grub_util_info ("the total module size is 0x%x", total_module_size); - - kernel_img = xmalloc (kernel_size + total_module_size); - grub_util_load_image (kernel_path, kernel_img); - - if (GRUB_KERNEL_MACHINE_PREFIX + strlen (prefix) + 1 > GRUB_KERNEL_MACHINE_DATA_END) - grub_util_error (_("prefix is too long")); - strcpy (kernel_img + GRUB_KERNEL_MACHINE_PREFIX, prefix); - - /* Fill in the grub_module_info structure. */ - modinfo = (struct grub_module_info *) (kernel_img + kernel_size); - memset (modinfo, 0, sizeof (struct grub_module_info)); - modinfo->magic = GRUB_MODULE_MAGIC; - modinfo->offset = sizeof (struct grub_module_info); - modinfo->size = total_module_size; - - offset = kernel_size + sizeof (struct grub_module_info); - for (p = path_list; p; p = p->next) - { - struct grub_module_header *header; - size_t mod_size, orig_size; - - orig_size = grub_util_get_image_size (p->name); - mod_size = ALIGN_UP(orig_size, 4); - - header = (struct grub_module_header *) (kernel_img + offset); - memset (header, 0, sizeof (struct grub_module_header)); - header->type = OBJ_TYPE_ELF; - header->size = grub_host_to_target32 (mod_size + sizeof (*header)); - offset += sizeof (*header); - memset (kernel_img + offset + orig_size, 0, mod_size - orig_size); - - grub_util_load_image (p->name, kernel_img + offset); - offset += mod_size; - } - - if (memdisk_path) - { - struct grub_module_header *header; - - header = (struct grub_module_header *) (kernel_img + offset); - memset (header, 0, sizeof (struct grub_module_header)); - header->type = OBJ_TYPE_MEMDISK; - header->size = grub_host_to_target32 (memdisk_size + sizeof (*header)); - offset += sizeof (*header); - - grub_util_load_image (memdisk_path, kernel_img + offset); - offset += memdisk_size; - } - - if (font_path) - { - struct grub_module_header *header; - - header = (struct grub_module_header *) (kernel_img + offset); - memset (header, 0, sizeof (struct grub_module_header)); - header->type = OBJ_TYPE_FONT; - header->size = grub_host_to_target32 (font_size + sizeof (*header)); - offset += sizeof (*header); - - grub_util_load_image (font_path, kernel_img + offset); - offset += font_size; - } - - if (config_path) - { - struct grub_module_header *header; - - header = (struct grub_module_header *) (kernel_img + offset); - memset (header, 0, sizeof (struct grub_module_header)); - header->type = OBJ_TYPE_CONFIG; - header->size = grub_host_to_target32 (config_size + sizeof (*header)); - offset += sizeof (*header); - - grub_util_load_image (config_path, kernel_img + offset); - *(kernel_img + offset + config_size_pure - 1) = 0; - offset += config_size; - } - - grub_util_info ("kernel_img=%p, kernel_size=0x%x", kernel_img, kernel_size); - compress_kernel (kernel_img, kernel_size + total_module_size, - &core_img, &core_size); - - grub_util_info ("the core size is 0x%x", core_size); - -#if defined(GRUB_MACHINE_PCBIOS) - { - unsigned num; - char *boot_path, *boot_img; - size_t boot_size; - num = ((core_size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS); - if (num > 0xffff) - grub_util_error (_("the core image is too big")); - - boot_path = grub_util_get_path (dir, "diskboot.img"); - boot_size = grub_util_get_image_size (boot_path); - if (boot_size != GRUB_DISK_SECTOR_SIZE) - grub_util_error (_("diskboot.img size must be %u bytes"), - GRUB_DISK_SECTOR_SIZE); - - boot_img = grub_util_read_image (boot_path); - - { - struct grub_boot_blocklist *block; - block = (struct grub_boot_blocklist *) (boot_img - + GRUB_DISK_SECTOR_SIZE - - sizeof (*block)); - block->len = grub_host_to_target16 (num); - - /* This is filled elsewhere. Verify it just in case. */ - assert (block->segment - == grub_host_to_target16 (GRUB_BOOT_MACHINE_KERNEL_SEG - + (GRUB_DISK_SECTOR_SIZE >> 4))); - } - - grub_util_write_image (boot_img, boot_size, out); - free (boot_img); - free (boot_path); - } -#elif defined(GRUB_MACHINE_QEMU) - { - char *rom_img; - size_t rom_size; - char *boot_path, *boot_img; - size_t boot_size; - - boot_path = grub_util_get_path (dir, "boot.img"); - boot_size = grub_util_get_image_size (boot_path); - boot_img = grub_util_read_image (boot_path); - - /* Rom sizes must be 64k-aligned. */ - rom_size = ALIGN_UP (core_size + boot_size, 64 * 1024); - - rom_img = xmalloc (rom_size); - memset (rom_img, 0, rom_size); - - *((grub_int32_t *) (core_img + GRUB_KERNEL_MACHINE_CORE_ENTRY_ADDR)) - = grub_host_to_target32 ((grub_uint32_t) -rom_size); - - memcpy (rom_img, core_img, core_size); - - *((grub_int32_t *) (boot_img + GRUB_BOOT_MACHINE_CORE_ENTRY_ADDR)) - = grub_host_to_target32 ((grub_uint32_t) -rom_size); - - memcpy (rom_img + rom_size - boot_size, boot_img, boot_size); - - free (core_img); - core_img = rom_img; - core_size = rom_size; - - free (boot_img); - free (boot_path); - } -#endif - -#ifdef GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE - *((grub_uint32_t *) (core_img + GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE)) - = grub_host_to_target32 (total_module_size); -#endif - *((grub_uint32_t *) (core_img + GRUB_KERNEL_MACHINE_KERNEL_IMAGE_SIZE)) - = grub_host_to_target32 (kernel_size); -#ifdef GRUB_KERNEL_MACHINE_COMPRESSED_SIZE - *((grub_uint32_t *) (core_img + GRUB_KERNEL_MACHINE_COMPRESSED_SIZE)) - = grub_host_to_target32 (core_size - GRUB_KERNEL_MACHINE_RAW_SIZE); -#endif - -#if defined(GRUB_KERNEL_MACHINE_INSTALL_DOS_PART) && defined(GRUB_KERNEL_MACHINE_INSTALL_BSD_PART) - /* If we included a drive in our prefix, let GRUB know it doesn't have to - prepend the drive told by BIOS. */ - if (prefix[0] == '(') - { - *((grub_int32_t *) (core_img + GRUB_KERNEL_MACHINE_INSTALL_DOS_PART)) - = grub_host_to_target32 (-2); - *((grub_int32_t *) (core_img + GRUB_KERNEL_MACHINE_INSTALL_BSD_PART)) - = grub_host_to_target32 (-2); - } -#endif - -#ifdef GRUB_MACHINE_PCBIOS - if (GRUB_KERNEL_MACHINE_LINK_ADDR + core_size > GRUB_MEMORY_MACHINE_UPPER) - grub_util_error (_("core image is too big (%p > %p)"), - GRUB_KERNEL_MACHINE_LINK_ADDR + core_size, - GRUB_MEMORY_MACHINE_UPPER); -#endif - -#if defined(GRUB_MACHINE_MIPS) - if (format == GRUB_PLATFORM_IMAGE_ELF) - { - char *elf_img; - size_t program_size; - Elf32_Ehdr *ehdr; - Elf32_Phdr *phdr; - grub_uint32_t target_addr; - - program_size = ALIGN_UP (core_size, 4); - - elf_img = xmalloc (program_size + sizeof (*ehdr) + sizeof (*phdr)); - memset (elf_img, 0, program_size + sizeof (*ehdr) + sizeof (*phdr)); - memcpy (elf_img + sizeof (*ehdr) + sizeof (*phdr), core_img, core_size); - ehdr = (void *) elf_img; - phdr = (void *) (elf_img + sizeof (*ehdr)); - memcpy (ehdr->e_ident, ELFMAG, SELFMAG); - ehdr->e_ident[EI_CLASS] = ELFCLASS32; -#ifdef GRUB_CPU_MIPSEL - ehdr->e_ident[EI_DATA] = ELFDATA2LSB; -#else - ehdr->e_ident[EI_DATA] = ELFDATA2MSB; -#endif - ehdr->e_ident[EI_VERSION] = EV_CURRENT; - ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE; - ehdr->e_type = grub_host_to_target16 (ET_EXEC); - ehdr->e_machine = grub_host_to_target16 (EM_MIPS); - ehdr->e_version = grub_host_to_target32 (EV_CURRENT); - - ehdr->e_phoff = grub_host_to_target32 ((char *) phdr - (char *) ehdr); - ehdr->e_phentsize = grub_host_to_target16 (sizeof (*phdr)); - ehdr->e_phnum = grub_host_to_target16 (1); - - /* No section headers. */ - ehdr->e_shoff = grub_host_to_target32 (0); - ehdr->e_shentsize = grub_host_to_target16 (0); - ehdr->e_shnum = grub_host_to_target16 (0); - ehdr->e_shstrndx = grub_host_to_target16 (0); - - ehdr->e_ehsize = grub_host_to_target16 (sizeof (*ehdr)); - - phdr->p_type = grub_host_to_target32 (PT_LOAD); - phdr->p_offset = grub_host_to_target32 (sizeof (*ehdr) + sizeof (*phdr)); - phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X); - - target_addr = ALIGN_UP (GRUB_KERNEL_MACHINE_LINK_ADDR - + kernel_size + total_module_size, 32); - ehdr->e_entry = grub_host_to_target32 (target_addr); - phdr->p_vaddr = grub_host_to_target32 (target_addr); - phdr->p_paddr = grub_host_to_target32 (target_addr); - phdr->p_align = grub_host_to_target32 (GRUB_KERNEL_MACHINE_LINK_ALIGN); - ehdr->e_flags = grub_host_to_target32 (0x1000 | EF_MIPS_NOREORDER - | EF_MIPS_PIC | EF_MIPS_CPIC); - phdr->p_filesz = grub_host_to_target32 (core_size); - phdr->p_memsz = grub_host_to_target32 (core_size); - - free (core_img); - core_img = elf_img; - core_size = program_size + sizeof (*ehdr) + sizeof (*phdr); - } -#endif - - grub_util_write_image (core_img, core_size, out); - free (kernel_img); - free (core_img); - free (kernel_path); - - while (path_list) - { - next = path_list->next; - free ((void *) path_list->name); - free (path_list); - path_list = next; - } -} - - - -static struct option options[] = - { - {"directory", required_argument, 0, 'd'}, - {"prefix", required_argument, 0, 'p'}, - {"memdisk", required_argument, 0, 'm'}, - {"font", required_argument, 0, 'f'}, - {"config", required_argument, 0, 'c'}, - {"output", required_argument, 0, 'o'}, -#ifdef GRUB_PLATFORM_IMAGE_DEFAULT - {"format", required_argument, 0, 'O'}, -#endif - {"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'V'}, - {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0} - }; - -static void -usage (int status) -{ - if (status) - fprintf (stderr, _("Try `%s --help' for more information.\n"), program_name); - else - printf (_("\ -Usage: %s [OPTION]... [MODULES]\n\ -\n\ -Make a bootable image of GRUB.\n\ -\n\ - -d, --directory=DIR use images and modules under DIR [default=%s]\n\ - -p, --prefix=DIR set grub_prefix directory [default=%s]\n\ - -m, --memdisk=FILE embed FILE as a memdisk image\n\ - -f, --font=FILE embed FILE as a boot font\n\ - -c, --config=FILE embed FILE as boot config\n\ - -o, --output=FILE output a generated image to FILE [default=stdout]\n" -#ifdef GRUB_PLATFORM_IMAGE_DEFAULT - "\ - -O, --format=FORMAT generate an image in format [default=" - GRUB_PLATFORM_IMAGE_DEFAULT_FORMAT "]\n \ - available formats: " - GRUB_PLATFORM_IMAGE_FORMATS "\n" -#endif - "\ - -h, --help display this message and exit\n\ - -V, --version print version information and exit\n\ - -v, --verbose print verbose messages\n\ -\n\ -Report bugs to <%s>.\n\ -"), program_name, GRUB_LIBDIR, DEFAULT_DIRECTORY, PACKAGE_BUGREPORT); - - exit (status); -} - -int -main (int argc, char *argv[]) -{ - char *output = NULL; - char *dir = NULL; - char *prefix = NULL; - char *memdisk = NULL; - char *font = NULL; - char *config = NULL; - FILE *fp = stdout; -#ifdef GRUB_PLATFORM_IMAGE_DEFAULT - grub_platform_image_format_t format = GRUB_PLATFORM_IMAGE_DEFAULT; -#endif - - grub_util_init_nls (); - - while (1) - { - int c = getopt_long (argc, argv, "d:p:m:c:o:O:f:hVv", options, 0); - - if (c == -1) - break; - else - switch (c) - { - case 'o': - if (output) - free (output); - - output = xstrdup (optarg); - break; - -#ifdef GRUB_PLATFORM_IMAGE_DEFAULT - case 'O': -#ifdef GRUB_PLATFORM_IMAGE_RAW - if (strcmp (optarg, "raw") == 0) - format = GRUB_PLATFORM_IMAGE_RAW; - else -#endif -#ifdef GRUB_PLATFORM_IMAGE_ELF - if (strcmp (optarg, "elf") == 0) - format = GRUB_PLATFORM_IMAGE_ELF; - else -#endif - usage (1); - break; -#endif - - case 'd': - if (dir) - free (dir); - - dir = xstrdup (optarg); - break; - - case 'm': - if (memdisk) - free (memdisk); - - memdisk = xstrdup (optarg); - - if (prefix) - free (prefix); - - prefix = xstrdup ("(memdisk)/boot/grub"); - break; - - case 'f': - if (font) - free (font); - - font = xstrdup (optarg); - break; - - case 'c': - if (config) - free (config); - - config = xstrdup (optarg); - break; - - case 'h': - usage (0); - break; - - case 'p': - if (prefix) - free (prefix); - - prefix = xstrdup (optarg); - break; - - case 'V': - printf ("grub-mkimage (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); - return 0; - - case 'v': - verbosity++; - break; - - default: - usage (1); - break; - } - } - - if (output) - { - fp = fopen (output, "wb"); - if (! fp) - grub_util_error (_("cannot open %s"), output); - free (output); - } - - generate_image (dir ? : GRUB_LIBDIR, prefix ? : DEFAULT_DIRECTORY, fp, - argv + optind, memdisk, font, config, -#ifdef GRUB_PLATFORM_IMAGE_DEFAULT - format -#else - 0 -#endif - ); - - fclose (fp); - - if (dir) - free (dir); - - return 0; -} diff --git a/util/grub-mkrescue.in b/util/grub-mkrescue.in index 6b3c9ecb8..a5430534a 100644 --- a/util/grub-mkrescue.in +++ b/util/grub-mkrescue.in @@ -86,7 +86,15 @@ if [ "x${output_image}" = x ] ; then exit 1 fi -iso9660_dir=`mktemp -d` +if test "x$TMP" != x; then + MKTEMP_TEMPLATE="$TMP/grub-mkrescue.XXXXXXXXXX" +elif test "x$TEMP" != x; then + MKTEMP_TEMPLATE="$TEMP/grub-mkrescue.XXXXXXXXXX" +else + MKTEMP_TEMPLATE="/tmp/grub-mkrescue.XXXXXXXXXX" +fi + +iso9660_dir=`mktemp -d "$MKTEMP_TEMPLATE"` mkdir -p ${iso9660_dir}/boot/grub process_input_dir () @@ -133,11 +141,11 @@ fi # build coreboot core.img if test -e "${coreboot_dir}" ; then echo "Enabling coreboot support ..." - memdisk_img=`mktemp` - memdisk_dir=`mktemp -d` + memdisk_img=`mktemp "$MKTEMP_TEMPLATE"` + memdisk_dir=`mktemp -d "$MKTEMP_TEMPLATE"` mkdir -p ${memdisk_dir}/boot/grub # obtain date-based UUID - iso_uuid=$(date +%Y-%m-%d-%H-%M-%S-00) + iso_uuid=$(date -u +%Y-%m-%d-%H-%M-%S-00) modules="$(cat ${coreboot_dir}/partmap.lst) ${modules}" cat << EOF > ${memdisk_dir}/boot/grub/grub.cfg @@ -148,11 +156,11 @@ EOF echo "insmod $i" done ; \ echo "source /boot/grub/grub.cfg") \ - > ${iso9660_dir}/boot/grub/i386-pc/grub.cfg + > ${iso9660_dir}/boot/grub/i386-coreboot/grub.cfg tar -C ${memdisk_dir} -cf ${memdisk_img} boot rm -rf ${memdisk_dir} - grub-mkelfimage -d ${coreboot_dir}/ -m ${memdisk_img} -o ${iso9660_dir}/boot/multiboot.img \ + grub-mkimage -O i386-coreboot -d ${coreboot_dir}/ -m ${memdisk_img} -o ${iso9660_dir}/boot/multiboot.img \ memdisk tar search iso9660 configfile sh \ ata at_keyboard rm -f ${memdisk_img} @@ -162,12 +170,12 @@ fi # build BIOS core.img if test -e "${pc_dir}" ; then echo "Enabling BIOS support ..." - core_img=`mktemp` - grub-mkimage -d ${pc_dir}/ -o ${core_img} --prefix=/boot/grub/i386-pc \ + core_img=`mktemp "$MKTEMP_TEMPLATE"` + grub-mkimage -O i386-pc -d ${pc_dir}/ -O i386-pc -o ${core_img} --prefix=/boot/grub/i386-pc \ iso9660 biosdisk cat ${pc_dir}/cdboot.img ${core_img} > ${iso9660_dir}/boot/grub/i386-pc/eltorito.img - embed_img=`mktemp` + embed_img=`mktemp "$MKTEMP_TEMPLATE"` cat ${pc_dir}/boot.img ${core_img} > ${embed_img} rm -f ${core_img} @@ -179,7 +187,7 @@ if test -e "${pc_dir}" ; then echo "source /boot/grub/grub.cfg") \ > ${iso9660_dir}/boot/grub/i386-pc/grub.cfg - grub_mkisofs_arguments="${grub_mkisofs_arguments} -b boot/grub/i386-pc/eltorito.img -boot-info-table \ + grub_mkisofs_arguments="${grub_mkisofs_arguments} -b boot/grub/i386-pc/eltorito.img -no-emul-boot -boot-info-table \ --embedded-boot ${embed_img}" fi diff --git a/util/grub-pe2elf.c b/util/grub-pe2elf.c index 488ad9b23..f370bbfa8 100644 --- a/util/grub-pe2elf.c +++ b/util/grub-pe2elf.c @@ -29,6 +29,8 @@ #include #include +#include "progname.h" + static struct option options[] = { {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, diff --git a/util/grub-probe.c b/util/grub-probe.c index ba2fe4c35..bb41480e2 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -82,13 +82,16 @@ grub_refresh (void) static void probe_partmap (grub_disk_t disk) { + grub_partition_t part; + if (disk->partition == NULL) { grub_util_info ("no partition map found for %s", disk->name); return; } - printf ("%s\n", disk->partition->partmap->name); + for (part = disk->partition; part; part = part->parent) + printf ("%s\n", part->partmap->name); } static int @@ -116,7 +119,7 @@ probe (const char *path, char *device_name) if (path == NULL) { -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) if (! grub_util_check_char_device (device_name)) grub_util_error ("%s is not a character device", device_name); #else diff --git a/util/grub-script-check.c b/util/grub-script-check.c index 3bfd6a425..dc732aa01 100644 --- a/util/grub-script-check.c +++ b/util/grub-script-check.c @@ -1,7 +1,7 @@ /* grub-script-check.c - check grub script file for syntax errors */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. + * Copyright (C) 2009,2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -82,16 +82,20 @@ grub_script_execute_cmdif (struct grub_script_cmd *cmd __attribute__ ((unused))) } grub_err_t -grub_script_execute_menuentry (struct grub_script_cmd *cmd) +grub_script_execute_cmdfor (struct grub_script_cmd *cmd __attribute__ ((unused))) { - struct grub_script_cmd_menuentry *menu; - menu = (struct grub_script_cmd_menuentry *)cmd; + return 0; +} - if (menu->sourcecode) - { - grub_free (menu->sourcecode); - menu->sourcecode = 0; - } +grub_err_t +grub_script_execute_cmdwhile (struct grub_script_cmd *cmd __attribute__ ((unused))) +{ + return 0; +} + +grub_err_t +grub_script_execute_menuentry (struct grub_script_cmd *cmd __attribute__ ((unused))) +{ return 0; } @@ -126,7 +130,7 @@ Checks GRUB script configuration file for syntax errors.\n\ \n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ - -v, --verbose print script being processed\n\ + -v, --verbose print the script as it is being processed\n\ \n\ Report bugs to <%s>.\n\ ", program_name, @@ -146,6 +150,7 @@ main (int argc, char *argv[]) auto grub_err_t get_config_line (char **line, int cont); grub_err_t get_config_line (char **line, int cont __attribute__ ((unused))) { + int i; char *cmdline = 0; size_t len = 0; ssize_t read; @@ -164,6 +169,17 @@ main (int argc, char *argv[]) if (verbose) grub_printf("%s", cmdline); + for (i = 0; cmdline[i] != '\0'; i++) + { + /* Replace tabs and carriage returns with spaces. */ + if (cmdline[i] == '\t' || cmdline[i] == '\r') + cmdline[i] = ' '; + + /* Replace '\n' with '\0'. */ + if (cmdline[i] == '\n') + cmdline[i] = '\0'; + } + *line = grub_strdup (cmdline); free (cmdline); diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in index 23e0cb569..d181fdffc 100644 --- a/util/grub.d/00_header.in +++ b/util/grub.d/00_header.in @@ -59,19 +59,92 @@ function savedefault { } EOF -case ${GRUB_TERMINAL_INPUT}:${GRUB_TERMINAL_OUTPUT} in - serial:* | *:serial) +serial=0; +gfxterm=0; +for x in ${GRUB_TERMINAL_INPUT} ${GRUB_TERMINAL_OUTPUT}; do + if [ xserial = "x$x" ]; then + serial=1; + fi + if [ xgfxterm = "x$x" ]; then + gfxterm=1; + fi +done + +if [ "x$serial" = x1 ]; then if ! test -e ${grub_prefix}/serial.mod ; then - echo "Serial terminal not available on this platform." >&2 ; exit 1 + echo "Serial terminal not available on this platform." >&2 ; exit 1 fi if [ "x${GRUB_SERIAL_COMMAND}" = "x" ] ; then - grub_warn "Requested serial terminal but GRUB_SERIAL_COMMAND is unspecified. Default parameters will be used." - GRUB_SERIAL_COMMAND=serial + grub_warn "Requested serial terminal but GRUB_SERIAL_COMMAND is unspecified. Default parameters will be used." + GRUB_SERIAL_COMMAND=serial fi echo "${GRUB_SERIAL_COMMAND}" - ;; -esac +fi + +if [ "x$gfxterm" = x1 ]; then + # Make the font accessible + prepare_grub_to_access_device `${grub_probe} --target=device "${GRUB_FONT_PATH}"` + + cat << EOF +if loadfont `make_system_path_relative_to_its_root "${GRUB_FONT_PATH}"` ; then + set gfxmode=${GRUB_GFXMODE} + insmod gfxterm + insmod ${GRUB_VIDEO_BACKEND} +EOF + if [ "x$GRUB_THEME" != x ] && [ -f "$GRUB_THEME" ] \ + && is_path_readable_by_grub "$GRUB_THEME"; then + echo "Found theme: $GRUB_THEME" >&2 + prepare_grub_to_access_device `${grub_probe} --target=device "$GRUB_THEME"` | sed -e "s/^/ /" + cat << EOF + insmod gfxmenu +EOF + themedir="`dirname "$GRUB_THEME"`" + for x in "$themedir"/*.pf2 "$themedir"/f/*.pf2; do + if [ -f "$x" ]; then + cat << EOF + loadfont (\$root)`make_system_path_relative_to_its_root $x` +EOF + fi + done + if [ x"`echo "$themedir"/*.jpg`" != x"$themedir/*.jpg" ] || [ x"`echo "$themedir"/*.jpeg`" != x"$themedir/*.jpeg" ]; then + cat << EOF + insmod jpeg +EOF + fi + if [ x"`echo "$themedir"/*.png`" != x"$themedir/*.png" ]; then + cat << EOF + insmod png +EOF + fi + if [ x"`echo "$themedir"/*.tga`" != x"$themedir/*.tga" ]; then + cat << EOF + insmod tga +EOF + fi + + cat << EOF + set theme=(\$root)`make_system_path_relative_to_its_root $GRUB_THEME` +EOF + elif [ "x$GRUB_BACKGROUND" != x ] && [ -f "$GRUB_BACKGROUND" ] \ + && is_path_readable_by_grub "$GRUB_BACKGROUND"; then + echo "Found background: $GRUB_BACKGROUND" >&2 + case "$GRUB_BACKGROUND" in + *.png) reader=png ;; + *.tga) reader=tga ;; + *.jpg|*.jpeg) reader=jpeg ;; + *) echo "Unsupported image format" >&2; exit 1 ;; + esac + prepare_grub_to_access_device `${grub_probe} --target=device "$GRUB_BACKGROUND"` | sed -e "s/^/ /" + cat << EOF + insmod $reader + background_image -m stretch `make_system_path_relative_to_its_root "$GRUB_BACKGROUND"` +EOF + fi + cat << EOF +fi +EOF +fi case x${GRUB_TERMINAL_INPUT} in x) @@ -89,23 +162,6 @@ EOF esac case x${GRUB_TERMINAL_OUTPUT} in - xgfxterm) - # Make the font accessible - prepare_grub_to_access_device `${grub_probe} --target=device ${GRUB_FONT_PATH}` - - cat << EOF -if loadfont `make_system_path_relative_to_its_root ${GRUB_FONT_PATH}` ; then - set gfxmode=${GRUB_GFXMODE} - insmod gfxterm - insmod ${GRUB_VIDEO_BACKEND} - if terminal_output gfxterm ; then true ; else - # For backward compatibility with versions of terminal.mod that don't - # understand terminal_output - terminal gfxterm - fi -fi -EOF - ;; x) # Just use the native terminal ;; @@ -146,3 +202,11 @@ else set timeout=${GRUB_TIMEOUT} EOF fi + +# Play an initial tune +if [ "x${GRUB_INIT_TUNE}" != "x" ] ; then + cat << EOF +insmod play +play ${GRUB_INIT_TUNE} +EOF +fi diff --git a/util/grub.d/10_hurd.in b/util/grub.d/10_hurd.in index 65a9a70b1..8e16e7d9f 100644 --- a/util/grub.d/10_hurd.in +++ b/util/grub.d/10_hurd.in @@ -21,10 +21,13 @@ exec_prefix=@exec_prefix@ libdir=@libdir@ . ${libdir}/grub/grub-mkconfig_lib +CLASS="--class gnu --class os" + if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then OS=GNU else OS="${GRUB_DISTRIBUTOR} GNU/Hurd" + CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr '[A-Z]' '[a-z]' | cut -d' ' -f1) ${CLASS}" fi at_least_one=false @@ -69,17 +72,17 @@ if ${all_of_them} && test -e /lib/ld.so.1 ; then : ; else fi cat << EOF -menuentry "${OS}" { +menuentry "${OS}" ${CLASS} { EOF prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | sed -e "s/^/\t/" cat << EOF - echo $(gettext "Loading GNU Mach ...") + echo '$(gettext_quoted "Loading GNU Mach ...")' multiboot ${kernel} root=device:${GRUB_DEVICE#/dev/} EOF save_default_entry | sed -e "s/^/\t/" prepare_grub_to_access_device ${GRUB_DEVICE} | sed -e "s/^/\t/" cat << EOF - echo $(gettext "Loading the Hurd ...") + echo '$(gettext_quoted "Loading the Hurd ...")' module /hurd/${hurd_fs}.static ${hurd_fs} --readonly \\ --multiboot-command-line='\${kernel-command-line}' \\ --host-priv-port='\${host-port}' \\ @@ -89,3 +92,25 @@ cat << EOF module /lib/ld.so.1 exec /hurd/exec '\$(exec-task=task-create)' } EOF + +cat << EOF +menuentry "${OS} (recovery mode)" { +EOF +prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | sed -e "s/^/\t/" +cat << EOF + echo '$(gettext_quoted "Loading GNU Mach ...")' + multiboot ${kernel} root=device:${GRUB_DEVICE#/dev/} -s +EOF +save_default_entry | sed -e "s/^/\t/" +prepare_grub_to_access_device ${GRUB_DEVICE} | sed -e "s/^/\t/" +cat << EOF + echo '$(gettext_quoted "Loading the Hurd ...")' + module /hurd/${hurd_fs}.static ${hurd_fs} \\ + --multiboot-command-line='\${kernel-command-line}' \\ + --host-priv-port='\${host-port}' \\ + --device-master-port='\${device-port}' \\ + --exec-server-task='\${exec-task}' -T typed '\${root}' \\ + '\$(task-create)' '\$(task-resume)' + module /lib/ld.so.1 exec /hurd/exec '\$(exec-task=task-create)' +} +EOF diff --git a/util/grub.d/10_kfreebsd.in b/util/grub.d/10_kfreebsd.in index b84b90a33..f63421617 100644 --- a/util/grub.d/10_kfreebsd.in +++ b/util/grub.d/10_kfreebsd.in @@ -22,13 +22,20 @@ bindir=@bindir@ libdir=@libdir@ . ${libdir}/grub/grub-mkconfig_lib -. ${bindir}/gettext.sh export TEXTDOMAIN=@PACKAGE@ export TEXTDOMAINDIR=@localedir@ +CLASS="--class os" + case "${GRUB_DISTRIBUTOR}" in - Debian) OS="${GRUB_DISTRIBUTOR} GNU/kFreeBSD" ;; - *) OS="FreeBSD" ;; + Debian) + OS="${GRUB_DISTRIBUTOR} GNU/kFreeBSD" + CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr '[A-Z]' '[a-z]' | cut -d' ' -f1) --class gnu-kfreebsd --class gnu ${CLASS}" + ;; + *) + OS="FreeBSD" + CLASS="--class freebsd --class bsd ${CLASS}" + ;; esac kfreebsd_entry () @@ -37,15 +44,15 @@ kfreebsd_entry () version="$2" recovery="$3" # not used yet args="$4" # not used yet - title="$(gettext "%s, with kFreeBSD %s")" - printf "menuentry \"${title}\" {\n" "${os}" "${version}" + title="$(gettext_quoted "%s, with kFreeBSD %s")" + printf "menuentry '${title}' ${CLASS} {\n" "${os}" "${version}" save_default_entry | sed -e "s/^/\t/" if [ -z "${prepare_boot_cache}" ]; then prepare_boot_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | sed -e "s/^/\t/")" fi printf '%s\n' "${prepare_boot_cache}" cat << EOF - echo $(printf "$(gettext "Loading kernel of FreeBSD %s ...")" ${version}) + echo '$(printf "$(gettext_quoted "Loading kernel of FreeBSD %s ...")" ${version})' kfreebsd ${rel_dirname}/${basename} EOF diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 90a6e83e7..802d59f51 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -22,14 +22,16 @@ bindir=@bindir@ libdir=@libdir@ . ${libdir}/grub/grub-mkconfig_lib -. ${bindir}/gettext.sh export TEXTDOMAIN=@PACKAGE@ export TEXTDOMAINDIR=@localedir@ +CLASS="--class gnu-linux --class gnu --class os" + if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then OS=GNU/Linux else OS="${GRUB_DISTRIBUTOR} GNU/Linux" + CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr '[A-Z]' '[a-z]' | cut -d' ' -f1) ${CLASS}" fi # loop-AES arranges things so that /dev/loop/X can be our root device, but @@ -54,18 +56,25 @@ linux_entry () recovery="$3" args="$4" if ${recovery} ; then - title="$(gettext "%s, with Linux %s (recovery mode)")" + title="$(gettext_quoted "%s, with Linux %s (recovery mode)")" else - title="$(gettext "%s, with Linux %s")" + title="$(gettext_quoted "%s, with Linux %s")" fi - printf "menuentry \"${title}\" {\n" "${os}" "${version}" + printf "menuentry '${title}' ${CLASS} {\n" "${os}" "${version}" save_default_entry | sed -e "s/^/\t/" # Use ELILO's generic "efifb" when it's known to be available. # FIXME: We need an interface to select vesafb in case efifb can't be used. - if grep -qx "CONFIG_FB_EFI=y" /boot/config-${version} 2> /dev/null ; then - cat << EOF + if [ "x$GRUB_GFXPAYLOAD_LINUX" = x ]; then + if grep -qx "CONFIG_FB_EFI=y" /boot/config-${version} 2> /dev/null \ + && grep -qx "CONFIG_VT_HW_CONSOLE_BINDING=y" /boot/config-${version} 2> /dev/null; then + cat << EOF set gfxpayload=keep +EOF + fi + else + cat << EOF + set gfxpayload=$GRUB_GFXPAYLOAD_LINUX EOF fi @@ -74,12 +83,12 @@ EOF fi printf '%s\n' "${prepare_boot_cache}" cat << EOF - echo $(printf "$(gettext "Loading Linux %s ...")" ${version}) + echo '$(printf "$(gettext_quoted "Loading Linux %s ...")" ${version})' linux ${rel_dirname}/${basename} root=${linux_root_device_thisversion} ro ${args} EOF if test -n "${initrd}" ; then cat << EOF - echo $(gettext "Loading initial ramdisk ...") + echo '$(gettext_quoted "Loading initial ramdisk ...")' initrd ${rel_dirname}/${initrd} EOF fi diff --git a/util/grub.d/10_netbsd.in b/util/grub.d/10_netbsd.in new file mode 100644 index 000000000..7e5fb34ad --- /dev/null +++ b/util/grub.d/10_netbsd.in @@ -0,0 +1,86 @@ +#! /bin/sh -e + +# grub-mkconfig helper script. +# Copyright (C) 2006,2007,2008,2009,2010 Free Software Foundation, Inc. +# +# GRUB is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# GRUB is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GRUB. If not, see . + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +bindir=@bindir@ +libdir=@libdir@ +. ${libdir}/grub/grub-mkconfig_lib + +export TEXTDOMAIN=@PACKAGE@ +export TEXTDOMAINDIR=@localedir@ + +if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then + OS=NetBSD +else + OS="${GRUB_DISTRIBUTOR} NetBSD" +fi + +netbsd_entry () +{ + loader="$1" # "knetbsd" or "multiboot" + kernel="$2" # absolute path to the kernel file + recovery="$3" # is this is a recovery entry? + args="$4" # extra arguments appended to loader command + + kroot_device="$(echo ${GRUB_DEVICE} | sed -e 's,^/dev/r,,')" + if ${recovery} ; then + title="$(gettext_quoted "%s, with kernel %s (via %s, recovery mode)")" + else + title="$(gettext_quoted "%s, with kernel %s (via %s)")" + fi + + printf "menuentry \"${title}\" {\n" \ + "${OS}" "$(echo ${kernel} | sed -e 's,^.*/,,')" "${loader}" + printf "%s\n" "${prepare_boot_cache}" + case "${loader}" in + knetbsd) + printf "\tknetbsd %s -r %s %s\n" \ + "${kernel}" "${kroot_device}" "${GRUB_CMDLINE_NETBSD} ${args}" + ;; + multiboot) + printf "\tmultiboot %s %s root=%s %s\n" \ + "${kernel}" "${kernel}" "${kroot_device}" "${GRUB_CMDLINE_NETBSD} ${args}" + ;; + esac + printf "}\n" +} + +prepare_boot_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE} | sed -e 's,^, ,')" + +# We look for NetBSD kernels in / but not in subdirectories. We simply +# pick all statically linked ELF executable files (or links) in / with a +# name that starts with `netbsd'. +pattern="^ELF[^,]*executable.*statically linked" +for k in $(ls -t /netbsd*) ; do + if ! grub_file_is_not_garbage "$k" ; then + continue + fi + if ! ((file -bL "$k" | grep -q "${pattern}") || + (zcat "$k" | file -bL - | grep -q "${pattern}")) 2>/dev/null ; then + continue + fi + + echo "Found NetBSD kernel: $k" >&2 + netbsd_entry "knetbsd" "$k" false "${GRUB_CMDLINE_NETBSD_DEFAULT}" + netbsd_entry "multiboot" "$k" false "${GRUB_CMDLINE_NETBSD_DEFAULT}" + if [ "x${GRUB_DISABLE_NETBSD_RECOVERY}" != "xtrue" ]; then + netbsd_entry "knetbsd" "$k" true "-s" + netbsd_entry "multiboot" "$k" true "-s" + fi +done diff --git a/util/hostdisk.c b/util/hostdisk.c index 603445801..8be487461 100644 --- a/util/hostdisk.c +++ b/util/hostdisk.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -97,12 +98,31 @@ struct hd_geometry # include #endif +#if defined(__NetBSD__) +# include +# include /* struct disklabel */ +# ifdef HAVE_GETRAWPARTITION +# include /* getrawpartition */ +# endif /* HAVE_GETRAWPARTITION */ +# include +# ifndef RAW_FLOPPY_MAJOR +# define RAW_FLOPPY_MAJOR 9 +# endif /* ! RAW_FLOPPY_MAJOR */ +#endif /* defined(__NetBSD__) */ + struct { char *drive; char *device; } map[256]; +struct grub_util_biosdisk_data +{ + char *dev; + int access_mode; + int fd; +}; + #ifdef __linux__ /* Check if we have devfs support. */ static int @@ -121,6 +141,31 @@ have_devfs (void) } #endif /* __linux__ */ +#if defined(__NetBSD__) +/* Adjust device driver parameters. This function should be called just + after successfully opening the device. For now, it simply prevents the + floppy driver from retrying operations on failure, as otherwise the + driver takes a while to abort when there is no floppy in the drive. */ +static void +configure_device_driver (int fd) +{ + struct stat st; + + if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode)) + return; + if (major(st.st_rdev) == RAW_FLOPPY_MAJOR) + { + int floppy_opts; + + if (ioctl (fd, FDIOCGETOPTS, &floppy_opts) == -1) + return; + floppy_opts |= FDOPT_NORETRY; + if (ioctl (fd, FDIOCSETOPTS, &floppy_opts) == -1) + return; + } +} +#endif /* defined(__NetBSD__) */ + static int find_grub_drive (const char *name) { @@ -137,7 +182,7 @@ find_grub_drive (const char *name) } static int -find_free_slot () +find_free_slot (void) { unsigned int i; @@ -165,14 +210,19 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk) { int drive; struct stat st; + struct grub_util_biosdisk_data *data; drive = find_grub_drive (name); if (drive < 0) - return grub_error (GRUB_ERR_BAD_DEVICE, + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no mapping exists for `%s'", name); disk->has_partitions = 1; disk->id = drive; + disk->data = data = xmalloc (sizeof (struct grub_util_biosdisk_data)); + data->dev = NULL; + data->access_mode = 0; + data->fd = -1; /* Get the size. */ #if defined(__MINGW32__) @@ -191,16 +241,20 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk) return GRUB_ERR_NONE; } #elif defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || defined(__APPLE__) + defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) { +# if defined(__NetBSD__) + struct disklabel label; +# else unsigned long long nr; +# endif int fd; fd = open (map[drive].device, O_RDONLY); if (fd == -1) - return grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' while attempting to get disk size", map[drive].device); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot open `%s' while attempting to get disk size", map[drive].device); -# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) +# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode)) # else if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode)) @@ -214,6 +268,9 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk) if (ioctl (fd, DIOCGMEDIASIZE, &nr)) # elif defined(__APPLE__) if (ioctl (fd, DKIOCGETBLOCKCOUNT, &nr)) +# elif defined(__NetBSD__) + configure_device_driver (fd); + if (ioctl (fd, DIOCGDINFO, &label) == -1) # else if (ioctl (fd, BLKGETSIZE64, &nr)) # endif @@ -224,14 +281,16 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk) close (fd); -#if defined (__APPLE__) +# if defined (__APPLE__) disk->total_sectors = nr; -#else +# elif defined(__NetBSD__) + disk->total_sectors = label.d_secperunit; +# else disk->total_sectors = nr / 512; if (nr % 512) grub_util_error ("unaligned device size"); -#endif +# endif grub_util_info ("the size of %s is %llu", name, disk->total_sectors); @@ -244,7 +303,7 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk) # warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal." #endif if (stat (map[drive].device, &st) < 0) - return grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", map[drive].device); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot stat `%s'", map[drive].device); disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS; @@ -254,6 +313,17 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk) } #ifdef __linux__ +/* Cache of partition start sectors for each disk. */ +struct linux_partition_cache +{ + struct linux_partition_cache *next; + char *dev; + unsigned long start; + int partno; +}; + +struct linux_partition_cache *linux_partition_cache_list; + static int linux_find_partition (char *dev, unsigned long sector) { @@ -262,6 +332,7 @@ linux_find_partition (char *dev, unsigned long sector) char *p; int i; char real_dev[PATH_MAX]; + struct linux_partition_cache *cache; strcpy(real_dev, dev); @@ -281,6 +352,16 @@ linux_find_partition (char *dev, unsigned long sector) format = "%d"; } + for (cache = linux_partition_cache_list; cache; cache = cache->next) + { + if (strcmp (cache->dev, dev) == 0 && cache->start == sector) + { + sprintf (p, format, cache->partno); + strcpy (dev, real_dev); + return 1; + } + } + for (i = 1; i < 10000; i++) { int fd; @@ -301,6 +382,15 @@ linux_find_partition (char *dev, unsigned long sector) if (hdg.start == sector) { + struct linux_partition_cache *new_cache_item; + + new_cache_item = xmalloc (sizeof *new_cache_item); + new_cache_item->dev = xstrdup (dev); + new_cache_item->start = hdg.start; + new_cache_item->partno = i; + grub_list_push (GRUB_AS_LIST_P (&linux_partition_cache_list), + GRUB_AS_LIST (new_cache_item)); + strcpy (dev, real_dev); return 1; } @@ -314,6 +404,7 @@ static int open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags) { int fd; + struct grub_util_biosdisk_data *data = disk->data; #ifdef O_LARGEFILE flags |= O_LARGEFILE; @@ -334,26 +425,47 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags) { int is_partition = 0; char dev[PATH_MAX]; + grub_disk_addr_t part_start = 0; + + part_start = grub_partition_get_start (disk->partition); strcpy (dev, map[disk->id].device); - if (disk->partition && strncmp (map[disk->id].device, "/dev/", 5) == 0) - is_partition = linux_find_partition (dev, disk->partition->start); + if (disk->partition && sector >= part_start + && strncmp (map[disk->id].device, "/dev/", 5) == 0) + is_partition = linux_find_partition (dev, part_start); - /* Open the partition. */ - grub_dprintf ("hostdisk", "opening the device `%s' in open_device()", dev); - fd = open (dev, flags); - if (fd < 0) + if (data->dev && strcmp (data->dev, dev) == 0 && + data->access_mode == (flags & O_ACCMODE)) { - grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", dev); - return -1; + grub_dprintf ("hostdisk", "reusing open device `%s'\n", dev); + fd = data->fd; + } + else + { + free (data->dev); + if (data->fd != -1) + close (data->fd); + + /* Open the partition. */ + grub_dprintf ("hostdisk", "opening the device `%s' in open_device()\n", dev); + fd = open (dev, flags); + if (fd < 0) + { + grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", dev); + return -1; + } + + /* Flush the buffer cache to the physical disk. + XXX: This also empties the buffer cache. */ + ioctl (fd, BLKFLSBUF, 0); + + data->dev = xstrdup (dev); + data->access_mode = (flags & O_ACCMODE); + data->fd = fd; } - /* Flush the buffer cache to the physical disk. - XXX: This also empties the buffer cache. */ - ioctl (fd, BLKFLSBUF, 0); - if (is_partition) - sector -= disk->partition->start; + sector -= part_start; } #else /* ! __linux__ */ #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) @@ -374,7 +486,26 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags) } #endif - fd = open (map[disk->id].device, flags); + if (data->dev && strcmp (data->dev, map[disk->id].device) == 0 && + data->access_mode == (flags & O_ACCMODE)) + { + grub_dprintf ("hostdisk", "reusing open device `%s'\n", data->dev); + fd = data->fd; + } + else + { + free (data->dev); + if (data->fd != -1) + close (data->fd); + + fd = open (map[disk->id].device, flags); + if (fd >= 0) + { + data->dev = xstrdup (map[disk->id].device); + data->access_mode = (flags & O_ACCMODE); + data->fd = fd; + } + } #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) if (! (sysctl_oldflags & 0x10) @@ -398,6 +529,10 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags) } #endif /* ! __linux__ */ +#if defined(__NetBSD__) + configure_device_driver (fd); +#endif /* defined(__NetBSD__) */ + #if defined(__linux__) && (!defined(__GLIBC__) || \ ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))) /* Maybe libc doesn't have large file support. */ @@ -490,6 +625,23 @@ grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, { int fd; + /* Split pre-partition and partition reads. */ + if (disk->partition && sector < disk->partition->start + && sector + size > disk->partition->start) + { + grub_err_t err; + err = grub_util_biosdisk_read (disk, sector, + disk->partition->start - sector, + buf); + if (err) + return err; + + return grub_util_biosdisk_read (disk, disk->partition->start, + size - (disk->partition->start - sector), + buf + ((disk->partition->start - sector) + << GRUB_DISK_SECTOR_BITS)); + } + fd = open_device (disk, sector, O_RDONLY); if (fd < 0) return grub_errno; @@ -517,7 +669,6 @@ grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, != (ssize_t) (size << GRUB_DISK_SECTOR_BITS)) grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id].device); - close (fd); return grub_errno; } @@ -527,6 +678,23 @@ grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector, { int fd; + /* Split pre-partition and partition writes. */ + if (disk->partition && sector < disk->partition->start + && sector + size > disk->partition->start) + { + grub_err_t err; + err = grub_util_biosdisk_write (disk, sector, + disk->partition->start - sector, + buf); + if (err) + return err; + + return grub_util_biosdisk_write (disk, disk->partition->start, + size - (disk->partition->start - sector), + buf + ((disk->partition->start - sector) + << GRUB_DISK_SECTOR_BITS)); + } + fd = open_device (disk, sector, O_WRONLY); if (fd < 0) return grub_errno; @@ -535,17 +703,27 @@ grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector, != (ssize_t) (size << GRUB_DISK_SECTOR_BITS)) grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id].device); - close (fd); return grub_errno; } +static void +grub_util_biosdisk_close (struct grub_disk *disk) +{ + struct grub_util_biosdisk_data *data = disk->data; + + free (data->dev); + if (data->fd != -1) + close (data->fd); + free (data); +} + static struct grub_disk_dev grub_util_biosdisk_dev = { .name = "biosdisk", .id = GRUB_DISK_DEVICE_BIOSDISK_ID, .iterate = grub_util_biosdisk_iterate, .open = grub_util_biosdisk_open, - .close = 0, + .close = grub_util_biosdisk_close, .read = grub_util_biosdisk_read, .write = grub_util_biosdisk_write, .next = 0 @@ -683,7 +861,7 @@ make_device_name (int drive, int dos_part, int bsd_part) dos_part_str = xasprintf (",%d", dos_part + 1); if (bsd_part >= 0) - bsd_part_str = xasprintf (",%c", dos_part + 'a'); + bsd_part_str = xasprintf (",%d", bsd_part + 1); ret = xasprintf ("%s%s%s", map[drive].drive, dos_part_str ? : "", @@ -853,6 +1031,27 @@ convert_system_partition_to_system_disk (const char *os_dev) } return path; +#elif defined(__NetBSD__) + /* NetBSD uses "/dev/r[wsc]d[0-9]+[a-z]". */ + char *path = xstrdup (os_dev); + if (strncmp ("/dev/rwd", path, 8) == 0 || + strncmp ("/dev/rsd", path, 8) == 0 || + strncmp ("/dev/rcd", path, 8) == 0) + { + char *q; + q = path + strlen(path) - 1; /* last character */ + if (grub_isalpha(*q) && grub_isdigit(*(q-1))) + { + int rawpart = -1; +# ifdef HAVE_GETRAWPARTITION + rawpart = getrawpartition(); +# endif /* HAVE_GETRAWPARTITION */ + if (rawpart >= 0) + *q = 'a' + rawpart; + } + } + return path; + #else # warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly." return xstrdup (os_dev); @@ -871,6 +1070,26 @@ device_is_wholedisk (const char *os_dev) } #endif +#if defined(__NetBSD__) +/* Try to determine whether a given device name corresponds to a whole disk. + This function should give in most cases a definite answer, but it may + actually give an approximate one in the following sense: if the return + value is 0 then the device name does not correspond to a whole disk. */ +static int +device_is_wholedisk (const char *os_dev) +{ + int len = strlen (os_dev); + int rawpart = -1; + +# ifdef HAVE_GETRAWPARTITION + rawpart = getrawpartition(); +# endif /* HAVE_GETRAWPARTITION */ + if (rawpart < 0) + return 1; + return (os_dev[len - 1] == ('a' + rawpart)); +} +#endif /* defined(__NetBSD__) */ + static int find_system_device (const char *os_dev) { @@ -914,7 +1133,7 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev) drive = find_system_device (os_dev); if (drive < 0) { - grub_error (GRUB_ERR_BAD_DEVICE, + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no mapping exists for `%s'", os_dev); return 0; } @@ -923,14 +1142,14 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev) == 0) return make_device_name (drive, -1, -1); -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) if (! S_ISCHR (st.st_mode)) #else if (! S_ISBLK (st.st_mode)) #endif return make_device_name (drive, -1, -1); -#if defined(__linux__) || defined(__CYGWIN__) +#if defined(__linux__) || defined(__CYGWIN__) || defined(__NetBSD__) /* Linux counts partitions uniformly, whether a BSD partition or a DOS partition, so mapping them to GRUB devices is not trivial. Here, get the start sector of a partition by HDIO_GETGEO, and @@ -938,53 +1157,49 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev) Cygwin /dev/sdXN emulation uses Windows partition mapping. It does not count the extended partition and missing primary - partitions. Use same method as on Linux here. */ + partitions. Use same method as on Linux here. + + For NetBSD, proceed as for Linux, except that the start sector is + obtained from the disk label. */ { char *name; grub_disk_t disk; int fd; +# if !defined(__NetBSD__) struct hd_geometry hdg; + typeof (hdg.start) p_offset; +# else /* defined(__NetBSD__) */ + struct disklabel label; + int index; + u_int32_t p_offset; +# endif /* !defined(__NetBSD__) */ int dos_part = -1; int bsd_part = -1; - auto int find_partition (grub_disk_t disk, + auto int find_partition (grub_disk_t dsk, const grub_partition_t partition); - int find_partition (grub_disk_t disk __attribute__ ((unused)), + int find_partition (grub_disk_t dsk __attribute__ ((unused)), const grub_partition_t partition) { - struct grub_msdos_partition *pcdata = NULL; + grub_disk_addr_t part_start = 0; + grub_util_info ("Partition %d starts from %lu", + partition->number, partition->start); - if (strcmp (partition->partmap->name, "part_msdos") == 0) - pcdata = partition->data; + part_start = grub_partition_get_start (partition); - if (pcdata) + if (p_offset == part_start) { - if (pcdata->bsd_part < 0) - grub_util_info ("DOS partition %d starts from %lu", - pcdata->dos_part, partition->start); - else - grub_util_info ("BSD partition %d,%c starts from %lu", - pcdata->dos_part, pcdata->bsd_part + 'a', - partition->start); - } - else - { - grub_util_info ("Partition %d starts from %lu", - partition->index, partition->start); - } - - if (hdg.start == partition->start) - { - if (pcdata) + if (partition->parent) { - dos_part = pcdata->dos_part; - bsd_part = pcdata->bsd_part; + dos_part = partition->parent->number; + bsd_part = partition->number; } else { - dos_part = partition->index; + dos_part = partition->number; bsd_part = -1; } + return 1; } @@ -993,8 +1208,15 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev) name = make_device_name (drive, -1, -1); +# if !defined(__NetBSD__) if (MAJOR (st.st_rdev) == FLOPPY_MAJOR) return name; +# else /* defined(__NetBSD__) */ + /* Since os_dev and convert_system_partition_to_system_disk (os_dev) are + * different, we know that os_dev is of the form /dev/r[wsc]d[0-9]+[a-z] + * and in particular it cannot be a floppy device. */ + index = os_dev[strlen(os_dev) - 1] - 'a'; +# endif /* !defined(__NetBSD__) */ fd = open (os_dev, O_RDONLY); if (fd == -1) @@ -1004,10 +1226,15 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev) return 0; } +# if !defined(__NetBSD__) if (ioctl (fd, HDIO_GETGEO, &hdg)) +# else /* defined(__NetBSD__) */ + configure_device_driver (fd); + if (ioctl (fd, DIOCGDINFO, &label) == -1) +# endif /* !defined(__NetBSD__) */ { grub_error (GRUB_ERR_BAD_DEVICE, - "cannot get geometry of `%s'", os_dev); + "cannot get disk geometry of `%s'", os_dev); close (fd); free (name); return 0; @@ -1015,9 +1242,22 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev) close (fd); - grub_util_info ("%s starts from %lu", os_dev, hdg.start); +# if !defined(__NetBSD__) + p_offset = hdg.start; +# else /* defined(__NetBSD__) */ + if (index >= label.d_npartitions) + { + grub_error (GRUB_ERR_BAD_DEVICE, + "no disk label entry for `%s'", os_dev); + free (name); + return 0; + } + p_offset = label.d_partitions[index].p_offset; +# endif /* !defined(__NetBSD__) */ - if (hdg.start == 0 && device_is_wholedisk (os_dev)) + grub_util_info ("%s starts from %lu", os_dev, p_offset); + + if (p_offset == 0 && device_is_wholedisk (os_dev)) return name; grub_util_info ("opening the device %s", name); @@ -1112,3 +1352,9 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev) return make_device_name (drive, -1, -1); #endif } + +const char * +grub_util_biosdisk_get_osdev (grub_disk_t disk) +{ + return map[disk->id].device; +} diff --git a/util/hostfs.c b/util/hostfs.c index df930b42e..501ad4664 100644 --- a/util/hostfs.c +++ b/util/hostfs.c @@ -1,7 +1,7 @@ /* hostfs.c - Dummy filesystem to provide access to the hosts filesystem */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2007,2008,2009 Free Software Foundation, Inc. + * Copyright (C) 2007,2008,2009,2010 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ #include #include +#include /* dirent.d_type is a BSD extension, not part of POSIX */ @@ -118,10 +119,17 @@ grub_hostfs_read (grub_file_t file, char *buf, grub_size_t len) FILE *f; f = (FILE *) file->data; - fseeko (f, file->offset, SEEK_SET); - int s = fread (buf, 1, len, f); + if (fseeko (f, file->offset, SEEK_SET) != 0) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "fseeko: %s", strerror (errno)); + return -1; + } - return s; + unsigned int s = fread (buf, 1, len, f); + if (s != len) + grub_error (GRUB_ERR_FILE_READ_ERROR, "fread: %s", strerror (errno)); + + return (signed) s; } static grub_err_t diff --git a/util/i386/efi/grub-dumpdevtree b/util/i386/efi/grub-dumpdevtree index 25aa35e23..51004cc85 100644 --- a/util/i386/efi/grub-dumpdevtree +++ b/util/i386/efi/grub-dumpdevtree @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with GRUB. If not, see . -if [ x$1 == x ]; then +if [ "x$1" = "x" ]; then echo "Filename required". fi diff --git a/util/i386/efi/grub-install.in b/util/i386/efi/grub-install.in index caa7be7e4..9b4270ff6 100644 --- a/util/i386/efi/grub-install.in +++ b/util/i386/efi/grub-install.in @@ -31,6 +31,7 @@ target_cpu=@target_cpu@ platform=@platform@ host_os=@host_os@ pkglibdir=${libdir}/`echo ${PACKAGE_TARNAME}/${target_cpu}-${platform} | sed ${transform}` +localedir=@datadir@/locale grub_mkimage=${bindir}/`echo grub-mkimage | sed ${transform}` grub_mkdevicemap=${sbindir}/`echo grub-mkdevicemap | sed ${transform}` @@ -144,8 +145,7 @@ else fi # Create the GRUB directory if it is not present. -test -d "$bootdir" || mkdir "$bootdir" || exit 1 -test -d "$grubdir" || mkdir "$grubdir" || exit 1 +mkdir -p "$grubdir" || exit 1 # If --recheck is specified, remove the device map, if present. if test $recheck = yes; then @@ -180,6 +180,14 @@ for file in ${pkglibdir}/*.mod ${pkglibdir}/*.lst; do cp -f $file ${grubdir} || exit 1 done +# Copy gettext files +mkdir -p ${grubdir}/locale/ +for dir in ${localedir}/*; do + if test -f "$dir/LC_MESSAGES/grub.mo"; then + cp -f "$dir/LC_MESSAGES/grub.mo" "${grubdir}/locale/${dir##*/}.mo" + fi +done + if ! test -f ${grubdir}/grubenv; then $grub_editenv ${grubdir}/grubenv create fi @@ -194,7 +202,10 @@ fi # Then the partition map module. In order to support partition-less media, # this command is allowed to fail (--target=fs already grants us that the # filesystem will be accessible). -partmap_module=`$grub_probe --target=partmap --device-map=${device_map} ${grubdir} 2> /dev/null` +partmap_module= +for x in `$grub_probe --target=partmap --device ${grub_device} 2> /dev/null`; do + partmap_module="$partmap_module part_$x"; +done # Device abstraction module, if any (lvm, raid). devabstraction_module=`$grub_probe --target=abstraction --device-map=${device_map} ${grubdir}` @@ -202,7 +213,7 @@ devabstraction_module=`$grub_probe --target=abstraction --device-map=${device_ma # The order in this list is critical. Be careful when modifying it. modules="$modules $fs_module $partmap_module $devabstraction_module" -$grub_mkimage --output=${grubdir}/grub.efi $modules || exit 1 +$grub_mkimage -O ${target_cpu}-efi --output=${grubdir}/grub.efi $modules || exit 1 # Prompt the user to check if the device map is correct. echo "Installation finished. No error reported." diff --git a/util/i386/efi/grub-mkimage.c b/util/i386/efi/grub-mkimage.c deleted file mode 100644 index f8c0f152e..000000000 --- a/util/i386/efi/grub-mkimage.c +++ /dev/null @@ -1,1112 +0,0 @@ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. - * - * GRUB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GRUB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GRUB. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "progname.h" - -#if GRUB_TARGET_WORDSIZE == 32 -# define grub_le_to_cpu(val) grub_le_to_cpu32(val) -#elif GRUB_TARGET_WORDSIZE == 64 -# define grub_le_to_cpu(val) grub_le_to_cpu64(val) -#endif - -static const grub_uint8_t stub[] = GRUB_PE32_MSDOS_STUB; - -static inline Elf_Addr -align_address (Elf_Addr addr, unsigned alignment) -{ - return (addr + alignment - 1) & ~(alignment - 1); -} - -static inline Elf_Addr -align_pe32_section (Elf_Addr addr) -{ - return align_address (addr, GRUB_PE32_SECTION_ALIGNMENT); -} - -/* Read the whole kernel image. Return the pointer to a read image, - and store the size in bytes in *SIZE. */ -static char * -read_kernel_image (const char *dir, size_t *size) -{ - char *kernel_image; - char *kernel_path; - - kernel_path = grub_util_get_path (dir, "kernel.img"); - *size = grub_util_get_image_size (kernel_path); - kernel_image = grub_util_read_image (kernel_path); - free (kernel_path); - - return kernel_image; -} - -/* Return if the ELF header is valid. */ -static int -check_elf_header (Elf_Ehdr *e, size_t size) -{ - if (size < sizeof (*e) - || e->e_ident[EI_MAG0] != ELFMAG0 - || e->e_ident[EI_MAG1] != ELFMAG1 - || e->e_ident[EI_MAG2] != ELFMAG2 - || e->e_ident[EI_MAG3] != ELFMAG3 - || e->e_ident[EI_VERSION] != EV_CURRENT - || e->e_version != grub_cpu_to_le32 (EV_CURRENT) - || ((e->e_ident[EI_CLASS] != ELFCLASS32) && - (e->e_ident[EI_CLASS] != ELFCLASS64)) - || e->e_ident[EI_DATA] != ELFDATA2LSB - || ((e->e_machine != grub_cpu_to_le16 (EM_386)) && - (e->e_machine != grub_cpu_to_le16 (EM_X86_64)))) - return 0; - - return 1; -} - -/* Return the starting address right after the header, - aligned by the section alignment. Allocate 4 section tables for - .text, .data, .reloc, and mods. */ -static Elf_Addr -get_starting_section_address (void) -{ - return align_pe32_section (sizeof (struct grub_pe32_header) - + 4 * sizeof (struct grub_pe32_section_table)); -} - -/* Determine if this section is a text section. Return false if this - section is not allocated. */ -static int -is_text_section (Elf_Shdr *s) -{ - return ((s->sh_flags & grub_cpu_to_le32 (SHF_EXECINSTR | SHF_ALLOC)) - == grub_cpu_to_le32 (SHF_EXECINSTR | SHF_ALLOC)); -} - -/* Determine if this section is a data section. This assumes that - BSS is also a data section, since the converter initializes BSS - when producing PE32 to avoid a bug in EFI implementations. */ -static int -is_data_section (Elf_Shdr *s) -{ - return (s->sh_flags & grub_cpu_to_le32 (SHF_ALLOC) - && ! (s->sh_flags & grub_cpu_to_le32 (SHF_EXECINSTR))); -} - -/* Locate section addresses by merging code sections and data sections - into .text and .data, respectively. Return the array of section - addresses. */ -static Elf_Addr * -locate_sections (Elf_Shdr *sections, Elf_Half section_entsize, - Elf_Half num_sections, const char *strtab) -{ - int i; - Elf_Addr current_address; - Elf_Addr *section_addresses; - Elf_Shdr *s; - - section_addresses = xmalloc (sizeof (*section_addresses) * num_sections); - memset (section_addresses, 0, sizeof (*section_addresses) * num_sections); - - current_address = get_starting_section_address (); - - /* .text */ - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if (is_text_section (s)) - { - Elf_Word align = grub_le_to_cpu32 (s->sh_addralign); - const char *name = strtab + grub_le_to_cpu32 (s->sh_name); - - if (align) - current_address = align_address (current_address, align); - - grub_util_info ("locating the section %s at 0x%x", - name, current_address); - section_addresses[i] = current_address; - current_address += grub_le_to_cpu32 (s->sh_size); - } - - current_address = align_pe32_section (current_address); - - /* .data */ - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if (is_data_section (s)) - { - Elf_Word align = grub_le_to_cpu32 (s->sh_addralign); - const char *name = strtab + grub_le_to_cpu32 (s->sh_name); - - if (align) - current_address = align_address (current_address, align); - - grub_util_info ("locating the section %s at 0x%x", - name, current_address); - section_addresses[i] = current_address; - current_address += grub_le_to_cpu32 (s->sh_size); - } - - return section_addresses; -} - -/* Return the symbol table section, if any. */ -static Elf_Shdr * -find_symtab_section (Elf_Shdr *sections, - Elf_Half section_entsize, Elf_Half num_sections) -{ - int i; - Elf_Shdr *s; - - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if (s->sh_type == grub_cpu_to_le32 (SHT_SYMTAB)) - return s; - - return 0; -} - -/* Return the address of the string table. */ -static const char * -find_strtab (Elf_Ehdr *e, Elf_Shdr *sections, Elf_Half section_entsize) -{ - Elf_Shdr *s; - char *strtab; - - s = (Elf_Shdr *) ((char *) sections - + grub_le_to_cpu16 (e->e_shstrndx) * section_entsize); - strtab = (char *) e + grub_le_to_cpu32 (s->sh_offset); - return strtab; -} - -/* Relocate symbols; note that this function overwrites the symbol table. - Return the address of a start symbol. */ -static Elf_Addr -relocate_symbols (Elf_Ehdr *e, Elf_Shdr *sections, - Elf_Shdr *symtab_section, Elf_Addr *section_addresses, - Elf_Half section_entsize, Elf_Half num_sections) -{ - Elf_Word symtab_size, sym_size, num_syms; - Elf_Off symtab_offset; - Elf_Addr start_address = 0; - Elf_Sym *sym; - Elf_Word i; - Elf_Shdr *strtab_section; - const char *strtab; - - strtab_section - = (Elf_Shdr *) ((char *) sections - + (grub_le_to_cpu32 (symtab_section->sh_link) - * section_entsize)); - strtab = (char *) e + grub_le_to_cpu32 (strtab_section->sh_offset); - - symtab_size = grub_le_to_cpu32 (symtab_section->sh_size); - sym_size = grub_le_to_cpu32 (symtab_section->sh_entsize); - symtab_offset = grub_le_to_cpu32 (symtab_section->sh_offset); - num_syms = symtab_size / sym_size; - - for (i = 0, sym = (Elf_Sym *) ((char *) e + symtab_offset); - i < num_syms; - i++, sym = (Elf_Sym *) ((char *) sym + sym_size)) - { - Elf_Section index; - const char *name; - - name = strtab + grub_le_to_cpu32 (sym->st_name); - - index = grub_le_to_cpu16 (sym->st_shndx); - if (index == STN_ABS) - { - continue; - } - else if ((index == STN_UNDEF)) - { - if (sym->st_name) - grub_util_error ("undefined symbol %s", name); - else - continue; - } - else if (index >= num_sections) - grub_util_error ("section %d does not exist", index); - - sym->st_value = (grub_le_to_cpu32 (sym->st_value) - + section_addresses[index]); - grub_util_info ("locating %s at 0x%x", name, sym->st_value); - - if (! start_address) - if (strcmp (name, "_start") == 0 || strcmp (name, "start") == 0) - start_address = sym->st_value; - } - - return start_address; -} - -/* Return the address of a symbol at the index I in the section S. */ -static Elf_Addr -get_symbol_address (Elf_Ehdr *e, Elf_Shdr *s, Elf_Word i) -{ - Elf_Sym *sym; - - sym = (Elf_Sym *) ((char *) e - + grub_le_to_cpu32 (s->sh_offset) - + i * grub_le_to_cpu32 (s->sh_entsize)); - return sym->st_value; -} - -/* Return the address of a modified value. */ -static Elf_Addr * -get_target_address (Elf_Ehdr *e, Elf_Shdr *s, Elf_Addr offset) -{ - return (Elf_Addr *) ((char *) e + grub_le_to_cpu32 (s->sh_offset) + offset); -} - -/* Deal with relocation information. This function relocates addresses - within the virtual address space starting from 0. So only relative - addresses can be fully resolved. Absolute addresses must be relocated - again by a PE32 relocator when loaded. */ -static void -relocate_addresses (Elf_Ehdr *e, Elf_Shdr *sections, - Elf_Addr *section_addresses, - Elf_Half section_entsize, Elf_Half num_sections, - const char *strtab) -{ - Elf_Half i; - Elf_Shdr *s; - - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if ((s->sh_type == grub_cpu_to_le32 (SHT_REL)) || - (s->sh_type == grub_cpu_to_le32 (SHT_RELA))) - { - Elf_Rela *r; - Elf_Word rtab_size, r_size, num_rs; - Elf_Off rtab_offset; - Elf_Shdr *symtab_section; - Elf_Word target_section_index; - Elf_Addr target_section_addr; - Elf_Shdr *target_section; - Elf_Word j; - - symtab_section = (Elf_Shdr *) ((char *) sections - + (grub_le_to_cpu32 (s->sh_link) - * section_entsize)); - target_section_index = grub_le_to_cpu32 (s->sh_info); - target_section_addr = section_addresses[target_section_index]; - target_section = (Elf_Shdr *) ((char *) sections - + (target_section_index - * section_entsize)); - - grub_util_info ("dealing with the relocation section %s for %s", - strtab + grub_le_to_cpu32 (s->sh_name), - strtab + grub_le_to_cpu32 (target_section->sh_name)); - - rtab_size = grub_le_to_cpu32 (s->sh_size); - r_size = grub_le_to_cpu32 (s->sh_entsize); - rtab_offset = grub_le_to_cpu32 (s->sh_offset); - num_rs = rtab_size / r_size; - - for (j = 0, r = (Elf_Rela *) ((char *) e + rtab_offset); - j < num_rs; - j++, r = (Elf_Rela *) ((char *) r + r_size)) - { - Elf_Addr info; - Elf_Addr offset; - Elf_Addr sym_addr; - Elf_Addr *target; - Elf_Addr addend; - - offset = grub_le_to_cpu (r->r_offset); - target = get_target_address (e, target_section, offset); - info = grub_le_to_cpu (r->r_info); - sym_addr = get_symbol_address (e, symtab_section, - ELF_R_SYM (info)); - - addend = (s->sh_type == grub_cpu_to_le32 (SHT_RELA)) ? - r->r_addend : 0; - - switch (ELF_R_TYPE (info)) - { -#if GRUB_TARGET_SIZEOF_VOID_P == 4 - case R_386_NONE: - break; - - case R_386_32: - /* This is absolute. */ - *target = grub_cpu_to_le32 (grub_le_to_cpu32 (*target) - + addend + sym_addr); - grub_util_info ("relocating an R_386_32 entry to 0x%x at the offset 0x%x", - *target, offset); - break; - - case R_386_PC32: - /* This is relative. */ - *target = grub_cpu_to_le32 (grub_le_to_cpu32 (*target) - + addend + sym_addr - - target_section_addr - offset); - grub_util_info ("relocating an R_386_PC32 entry to 0x%x at the offset 0x%x", - *target, offset); - break; - -#else - - case R_X86_64_NONE: - break; - - case R_X86_64_64: - *target = grub_cpu_to_le64 (grub_le_to_cpu64 (*target) - + addend + sym_addr); - grub_util_info ("relocating an R_X86_64_64 entry to 0x%llx at the offset 0x%llx", - *target, offset); - break; - - case R_X86_64_PC32: - { - grub_uint32_t *t32 = (grub_uint32_t *) target; - *t32 = grub_cpu_to_le64 (grub_le_to_cpu32 (*t32) - + addend + sym_addr - - target_section_addr - offset); - grub_util_info ("relocating an R_X86_64_PC32 entry to 0x%x at the offset 0x%llx", - *t32, offset); - break; - } - - case R_X86_64_32: - case R_X86_64_32S: - { - grub_uint32_t *t32 = (grub_uint32_t *) target; - *t32 = grub_cpu_to_le64 (grub_le_to_cpu32 (*t32) - + addend + sym_addr); - grub_util_info ("relocating an R_X86_64_32(S) entry to 0x%x at the offset 0x%llx", - *t32, offset); - break; - } - -#endif - default: - grub_util_error ("unknown relocation type %d", - ELF_R_TYPE (info)); - break; - } - } - } -} - -void -write_padding (FILE *out, size_t size) -{ - size_t i; - - for (i = 0; i < size; i++) - if (fputc (0, out) == EOF) - grub_util_error ("padding failed"); -} - -/* Add a PE32's fixup entry for a relocation. Return the resulting address - after having written to the file OUT. */ -Elf_Addr -add_fixup_entry (struct grub_pe32_fixup_block **block, grub_uint16_t type, - Elf_Addr addr, int flush, Elf_Addr current_address, - FILE *out) -{ - struct grub_pe32_fixup_block *b = *block; - - /* First, check if it is necessary to write out the current block. */ - if (b) - { - if (flush || addr < b->page_rva || b->page_rva + 0x1000 <= addr) - { - grub_uint32_t size; - - if (flush) - { - /* Add as much padding as necessary to align the address - with a section boundary. */ - Elf_Addr next_address; - unsigned padding_size; - size_t index; - - next_address = current_address + b->block_size; - padding_size = ((align_pe32_section (next_address) - - next_address) - >> 1); - index = ((b->block_size - sizeof (*b)) >> 1); - grub_util_info ("adding %d padding fixup entries", padding_size); - while (padding_size--) - { - b->entries[index++] = 0; - b->block_size += 2; - } - } - else if (b->block_size & (8 - 1)) - { - /* If not aligned with a 32-bit boundary, add - a padding entry. */ - size_t index; - - grub_util_info ("adding a padding fixup entry"); - index = ((b->block_size - sizeof (*b)) >> 1); - b->entries[index] = 0; - b->block_size += 2; - } - - /* Flush it. */ - grub_util_info ("writing %d bytes of a fixup block starting at 0x%x", - b->block_size, b->page_rva); - size = b->block_size; - current_address += size; - b->page_rva = grub_cpu_to_le32 (b->page_rva); - b->block_size = grub_cpu_to_le32 (b->block_size); - if (fwrite (b, size, 1, out) != 1) - grub_util_error ("write failed"); - free (b); - *block = b = 0; - } - } - - if (! flush) - { - grub_uint16_t entry; - size_t index; - - /* If not allocated yet, allocate a block with enough entries. */ - if (! b) - { - *block = b = xmalloc (sizeof (*b) + 2 * 0x1000); - - /* The spec does not mention the requirement of a Page RVA. - Here, align the address with a 4K boundary for safety. */ - b->page_rva = (addr & ~(0x1000 - 1)); - b->block_size = sizeof (*b); - } - - /* Sanity check. */ - if (b->block_size >= sizeof (*b) + 2 * 0x1000) - grub_util_error ("too many fixup entries"); - - /* Add a new entry. */ - index = ((b->block_size - sizeof (*b)) >> 1); - entry = GRUB_PE32_FIXUP_ENTRY (type, addr - b->page_rva); - b->entries[index] = grub_cpu_to_le16 (entry); - b->block_size += 2; - } - - return current_address; -} - -/* Write out zeros to make space for the header. */ -static Elf_Addr -make_header_space (FILE *out) -{ - Elf_Addr addr; - - addr = get_starting_section_address (); - write_padding (out, addr); - - return addr; -} - -/* Write text sections. */ -static Elf_Addr -write_text_sections (FILE *out, Elf_Addr current_address, - Elf_Ehdr *e, Elf_Shdr *sections, - Elf_Half section_entsize, Elf_Half num_sections, - const char *strtab) -{ - Elf_Half i; - Elf_Shdr *s; - Elf_Addr addr; - - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if (is_text_section (s)) - { - Elf_Word align = grub_le_to_cpu32 (s->sh_addralign); - Elf_Off offset = grub_le_to_cpu32 (s->sh_offset); - Elf_Word size = grub_le_to_cpu32 (s->sh_size); - const char *name = strtab + grub_le_to_cpu32 (s->sh_name); - - if (align) - { - addr = align_address (current_address, align); - if (current_address != addr) - { - grub_util_info ("padding %d bytes for the ELF section alignment", - addr - current_address); - write_padding (out, addr - current_address); - current_address = addr; - } - } - - grub_util_info ("writing the text section %s at 0x%x", - name, current_address); - - if (fwrite ((char *) e + offset, size, 1, out) != 1) - grub_util_error ("write failed"); - - current_address += size; - } - - addr = align_pe32_section (current_address); - if (addr != current_address) - { - grub_util_info ("padding %d bytes for the PE32 section alignment", - addr - current_address); - write_padding (out, addr - current_address); - } - - return addr; -} - -/* Write data sections. */ -static Elf_Addr -write_data_sections (FILE *out, Elf_Addr current_address, - Elf_Ehdr *e, Elf_Shdr *sections, - Elf_Half section_entsize, Elf_Half num_sections, - const char *strtab) -{ - Elf_Half i; - Elf_Shdr *s; - Elf_Addr addr; - - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if (is_data_section (s)) - { - Elf_Word align = grub_le_to_cpu32 (s->sh_addralign); - Elf_Off offset = grub_le_to_cpu32 (s->sh_offset); - Elf_Word size = grub_le_to_cpu32 (s->sh_size); - const char *name = strtab + grub_le_to_cpu32 (s->sh_name); - - if (align) - { - addr = align_address (current_address, align); - if (current_address != addr) - { - grub_util_info ("padding %d bytes for the ELF section alignment", - addr - current_address); - write_padding (out, addr - current_address); - current_address = addr; - } - } - - grub_util_info ("writing the data section %s at 0x%x", - name, current_address); - - if (s->sh_type == grub_cpu_to_le32 (SHT_NOBITS)) - write_padding (out, size); - else - if (fwrite ((char *) e + offset, size, 1, out) != 1) - grub_util_error ("write failed"); - - current_address += size; - } - - addr = align_pe32_section (current_address); - if (addr != current_address) - { - grub_util_info ("padding %d bytes for the PE32 section alignment", - addr - current_address); - write_padding (out, addr - current_address); - } - - return addr; -} - -/* Write modules. */ -static Elf_Addr -make_mods_section (FILE *out, Elf_Addr current_address, - const char *dir, char *mods[]) -{ - struct grub_util_path_list *path_list; - grub_size_t total_module_size; - struct grub_util_path_list *p; - struct grub_module_info modinfo; - Elf_Addr addr; - - memset (&modinfo, 0, sizeof (modinfo)); - - path_list = grub_util_resolve_dependencies (dir, "moddep.lst", mods); - - total_module_size = sizeof (struct grub_module_info); - for (p = path_list; p; p = p->next) - { - total_module_size += (grub_util_get_image_size (p->name) - + sizeof (struct grub_module_header)); - } - - grub_util_info ("the total module size is 0x%x", total_module_size); - - modinfo.magic = grub_cpu_to_le32 (GRUB_MODULE_MAGIC); - modinfo.offset = grub_cpu_to_le32 (sizeof (modinfo)); - modinfo.size = grub_cpu_to_le32 (total_module_size); - - if (fwrite (&modinfo, sizeof (modinfo), 1, out) != 1) - grub_util_error ("write failed"); - - for (p = path_list; p; p = p->next) - { - struct grub_module_header header; - size_t mod_size; - char *mod_image; - - memset (&header, 0, sizeof (header)); - - grub_util_info ("adding module %s", p->name); - - mod_size = grub_util_get_image_size (p->name); - header.type = OBJ_TYPE_ELF; - header.size = grub_host_to_target32 (mod_size + sizeof (header)); - - mod_image = grub_util_read_image (p->name); - - if (fwrite (&header, sizeof (header), 1, out) != 1 - || fwrite (mod_image, mod_size, 1, out) != 1) - grub_util_error ("write failed"); - - free (mod_image); - } - - for (p = path_list; p; ) - { - struct grub_util_path_list *q; - - q = p->next; - free (p); - p = q; - } - - current_address += total_module_size; - - addr = align_pe32_section (current_address); - if (addr != current_address) - { - grub_util_info ("padding %d bytes for the PE32 section alignment", - addr - current_address); - write_padding (out, addr - current_address); - } - - return addr; -} - -/* Make a .reloc section. */ -static Elf_Addr -make_reloc_section (FILE *out, Elf_Addr current_address, Elf_Ehdr *e, - Elf_Addr *section_addresses, Elf_Shdr *sections, - Elf_Half section_entsize, Elf_Half num_sections, - const char *strtab) -{ - Elf_Half i; - Elf_Shdr *s; - struct grub_pe32_fixup_block *fixup_block = 0; - - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if ((s->sh_type == grub_cpu_to_le32 (SHT_REL)) || - (s->sh_type == grub_cpu_to_le32 (SHT_RELA))) - { - Elf_Rel *r; - Elf_Word rtab_size, r_size, num_rs; - Elf_Off rtab_offset; - Elf_Addr section_address; - Elf_Word j; - - grub_util_info ("translating the relocation section %s", - strtab + grub_le_to_cpu32 (s->sh_name)); - - rtab_size = grub_le_to_cpu32 (s->sh_size); - r_size = grub_le_to_cpu32 (s->sh_entsize); - rtab_offset = grub_le_to_cpu32 (s->sh_offset); - num_rs = rtab_size / r_size; - - section_address = section_addresses[grub_le_to_cpu32 (s->sh_info)]; - - for (j = 0, r = (Elf_Rel *) ((char *) e + rtab_offset); - j < num_rs; - j++, r = (Elf_Rel *) ((char *) r + r_size)) - { - Elf_Addr info; - Elf_Addr offset; - - offset = grub_le_to_cpu32 (r->r_offset); - info = grub_le_to_cpu32 (r->r_info); - - /* Necessary to relocate only absolute addresses. */ -#if GRUB_TARGET_SIZEOF_VOID_P == 4 - if (ELF_R_TYPE (info) == R_386_32) - { - Elf_Addr addr; - - addr = section_address + offset; - grub_util_info ("adding a relocation entry for 0x%x", addr); - current_address = add_fixup_entry (&fixup_block, - GRUB_PE32_REL_BASED_HIGHLOW, - addr, 0, current_address, - out); - } -#else - if ((ELF_R_TYPE (info) == R_X86_64_32) || - (ELF_R_TYPE (info) == R_X86_64_32S)) - { - grub_util_error ("can\'t add fixup entry for R_X86_64_32(S)"); - } - else if (ELF_R_TYPE (info) == R_X86_64_64) - { - Elf_Addr addr; - - addr = section_address + offset; - grub_util_info ("adding a relocation entry for 0x%llx", addr); - current_address = add_fixup_entry (&fixup_block, - GRUB_PE32_REL_BASED_DIR64, - addr, - 0, current_address, - out); - } -#endif - } - } - - current_address = add_fixup_entry (&fixup_block, 0, 0, 1, - current_address, out); - - return current_address; -} - -/* Create the header. */ -static void -make_header (FILE *out, Elf_Addr text_address, Elf_Addr data_address, - Elf_Addr mods_address, Elf_Addr reloc_address, - Elf_Addr end_address, Elf_Addr start_address) -{ - struct grub_pe32_header header; - struct grub_pe32_coff_header *c; - struct grub_pe32_optional_header *o; - struct grub_pe32_section_table text_section, data_section; - struct grub_pe32_section_table mods_section, reloc_section; - - /* The magic. */ - memset (&header, 0, sizeof (header)); - memcpy (header.msdos_stub, stub, sizeof (header.msdos_stub)); - memcpy (header.signature, "PE\0\0", sizeof (header.signature)); - - /* The COFF file header. */ - c = &header.coff_header; -#if GRUB_TARGET_SIZEOF_VOID_P == 4 - c->machine = grub_cpu_to_le16 (GRUB_PE32_MACHINE_I386); -#else - c->machine = grub_cpu_to_le16 (GRUB_PE32_MACHINE_X86_64); -#endif - - c->num_sections = grub_cpu_to_le16 (4); - c->time = grub_cpu_to_le32 (time (0)); - c->optional_header_size = grub_cpu_to_le16 (sizeof (header.optional_header)); - c->characteristics = grub_cpu_to_le16 (GRUB_PE32_EXECUTABLE_IMAGE - | GRUB_PE32_LINE_NUMS_STRIPPED -#if GRUB_TARGET_SIZEOF_VOID_P == 4 - | GRUB_PE32_32BIT_MACHINE -#endif - | GRUB_PE32_LOCAL_SYMS_STRIPPED - | GRUB_PE32_DEBUG_STRIPPED); - - /* The PE Optional header. */ - o = &header.optional_header; - o->magic = grub_cpu_to_le16 (GRUB_PE32_PE32_MAGIC); - o->code_size = grub_cpu_to_le32 (data_address - text_address); - o->data_size = grub_cpu_to_le32 (reloc_address - data_address); - o->bss_size = 0; - o->entry_addr = grub_cpu_to_le32 (start_address); - o->code_base = grub_cpu_to_le32 (text_address); -#if GRUB_TARGET_SIZEOF_VOID_P == 4 - o->data_base = grub_cpu_to_le32 (data_address); -#endif - o->image_base = 0; - o->section_alignment = grub_cpu_to_le32 (GRUB_PE32_SECTION_ALIGNMENT); - o->file_alignment = grub_cpu_to_le32 (GRUB_PE32_FILE_ALIGNMENT); - o->image_size = grub_cpu_to_le32 (end_address); - o->header_size = grub_cpu_to_le32 (text_address); - o->subsystem = grub_cpu_to_le16 (GRUB_PE32_SUBSYSTEM_EFI_APPLICATION); - - /* Do these really matter? */ - o->stack_reserve_size = grub_cpu_to_le32 (0x10000); - o->stack_commit_size = grub_cpu_to_le32 (0x10000); - o->heap_reserve_size = grub_cpu_to_le32 (0x10000); - o->heap_commit_size = grub_cpu_to_le32 (0x10000); - - o->num_data_directories = grub_cpu_to_le32 (GRUB_PE32_NUM_DATA_DIRECTORIES); - - o->base_relocation_table.rva = grub_cpu_to_le32 (reloc_address); - o->base_relocation_table.size = grub_cpu_to_le32 (end_address - - reloc_address); - - /* The sections. */ - memset (&text_section, 0, sizeof (text_section)); - strcpy (text_section.name, ".text"); - text_section.virtual_size = grub_cpu_to_le32 (data_address - text_address); - text_section.virtual_address = grub_cpu_to_le32 (text_address); - text_section.raw_data_size = grub_cpu_to_le32 (data_address - text_address); - text_section.raw_data_offset = grub_cpu_to_le32 (text_address); - text_section.characteristics = grub_cpu_to_le32 (GRUB_PE32_SCN_CNT_CODE - | GRUB_PE32_SCN_MEM_EXECUTE - | GRUB_PE32_SCN_MEM_READ); - - memset (&data_section, 0, sizeof (data_section)); - strcpy (data_section.name, ".data"); - data_section.virtual_size = grub_cpu_to_le32 (mods_address - data_address); - data_section.virtual_address = grub_cpu_to_le32 (data_address); - data_section.raw_data_size = grub_cpu_to_le32 (mods_address - data_address); - data_section.raw_data_offset = grub_cpu_to_le32 (data_address); - data_section.characteristics - = grub_cpu_to_le32 (GRUB_PE32_SCN_CNT_INITIALIZED_DATA - | GRUB_PE32_SCN_MEM_READ - | GRUB_PE32_SCN_MEM_WRITE); - - memset (&mods_section, 0, sizeof (mods_section)); - strcpy (mods_section.name, "mods"); - mods_section.virtual_size = grub_cpu_to_le32 (reloc_address - mods_address); - mods_section.virtual_address = grub_cpu_to_le32 (mods_address); - mods_section.raw_data_size = grub_cpu_to_le32 (reloc_address - mods_address); - mods_section.raw_data_offset = grub_cpu_to_le32 (mods_address); - mods_section.characteristics - = grub_cpu_to_le32 (GRUB_PE32_SCN_CNT_INITIALIZED_DATA - | GRUB_PE32_SCN_MEM_READ - | GRUB_PE32_SCN_MEM_WRITE); - - memset (&reloc_section, 0, sizeof (reloc_section)); - strcpy (reloc_section.name, ".reloc"); - reloc_section.virtual_size = grub_cpu_to_le32 (end_address - reloc_address); - reloc_section.virtual_address = grub_cpu_to_le32 (reloc_address); - reloc_section.raw_data_size = grub_cpu_to_le32 (end_address - reloc_address); - reloc_section.raw_data_offset = grub_cpu_to_le32 (reloc_address); - reloc_section.characteristics - = grub_cpu_to_le32 (GRUB_PE32_SCN_CNT_INITIALIZED_DATA - | GRUB_PE32_SCN_MEM_DISCARDABLE - | GRUB_PE32_SCN_MEM_READ); - - /* Write them out. */ - if (fseeko (out, 0, SEEK_SET) < 0) - grub_util_error ("seek failed"); - - if (fwrite (&header, sizeof (header), 1, out) != 1 - || fwrite (&text_section, sizeof (text_section), 1, out) != 1 - || fwrite (&data_section, sizeof (data_section), 1, out) != 1 - || fwrite (&mods_section, sizeof (mods_section), 1, out) != 1 - || fwrite (&reloc_section, sizeof (reloc_section), 1, out) != 1) - grub_util_error ("write failed"); -} - -/* Convert an ELF relocatable object into an EFI Application (PE32). */ -void -convert_elf (const char *dir, char *prefix, FILE *out, char *mods[]) -{ - char *kernel_image; - size_t kernel_size; - const char *strtab; - Elf_Ehdr *e; - Elf_Shdr *sections; - Elf_Off section_offset; - Elf_Half section_entsize; - Elf_Half num_sections; - Elf_Addr *section_addresses; - Elf_Shdr *symtab_section; - Elf_Addr start_address; - Elf_Addr text_address, data_address, reloc_address, mods_address; - Elf_Addr end_address; - Elf_Shdr *s; - int i; - - /* Get the kernel image and check the format. */ - kernel_image = read_kernel_image (dir, &kernel_size); - e = (Elf_Ehdr *) kernel_image; - if (! check_elf_header (e, kernel_size)) - grub_util_error ("invalid ELF header"); - - section_offset = grub_cpu_to_le32 (e->e_shoff); - section_entsize = grub_cpu_to_le16 (e->e_shentsize); - num_sections = grub_cpu_to_le16 (e->e_shnum); - - if (kernel_size < section_offset + section_entsize * num_sections) - grub_util_error ("invalid ELF format"); - - sections = (Elf_Shdr *) (kernel_image + section_offset); - strtab = find_strtab (e, sections, section_entsize); - - for (i = 0, s = sections; - i < num_sections; - i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) - if (is_text_section (s)) - { - Elf_Off offset = grub_le_to_cpu32 (s->sh_offset); - - if (GRUB_KERNEL_MACHINE_PREFIX + strlen (prefix) + 1 > GRUB_KERNEL_MACHINE_DATA_END) - grub_util_error ("prefix too long"); - - strcpy (kernel_image + offset + GRUB_KERNEL_MACHINE_PREFIX, prefix); - break; - } - - /* Relocate sections then symbols in the virtual address space. */ - section_addresses = locate_sections (sections, section_entsize, - num_sections, strtab); - - symtab_section = find_symtab_section (sections, - section_entsize, num_sections); - if (! symtab_section) - grub_util_error ("no symbol table"); - - start_address = relocate_symbols (e, sections, symtab_section, - section_addresses, section_entsize, - num_sections); - if (start_address == 0) - grub_util_error ("start symbol is not defined"); - - /* Resolve addresses in the virtual address space. */ - relocate_addresses (e, sections, section_addresses, section_entsize, - num_sections, strtab); - - /* Generate a PE32 image file. The strategy is to dump binary data first, - then fill up the header. */ - text_address = make_header_space (out); - data_address = write_text_sections (out, text_address, e, sections, - section_entsize, num_sections, - strtab); - mods_address = write_data_sections (out, data_address, e, sections, - section_entsize, num_sections, - strtab); - reloc_address = make_mods_section (out, mods_address, dir, mods); - end_address = make_reloc_section (out, reloc_address, e, section_addresses, - sections, section_entsize, num_sections, - strtab); - make_header (out, text_address, data_address, mods_address, - reloc_address, end_address, start_address); - - /* Clean up. */ - free (section_addresses); - free (kernel_image); -} - -static struct option options[] = - { - {"directory", required_argument, 0, 'd'}, - {"prefix", required_argument, 0, 'p'}, - {"output", required_argument, 0, 'o'}, - {"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'V'}, - {"verbose", no_argument, 0, 'v'}, - { 0, 0, 0, 0 } - }; - -static void -usage (int status) -{ - if (status) - fprintf (stderr, "Try `%s --help' for more information.\n", program_name); - else - printf ("\ -Usage: %s -o FILE [OPTION]... [MODULES]\n\ -\n\ -Make a bootable image of GRUB.\n\ -\n\ - -d, --directory=DIR use images and modules under DIR [default=%s]\n\ - -p, --prefix=DIR set grub_prefix directory [default=%s]\n\ - -o, --output=FILE output a generated image to FILE\n\ - -h, --help display this message and exit\n\ - -V, --version print version information and exit\n\ - -v, --verbose print verbose messages\n\ -\n\ -Report bugs to <%s>.\n\ -", program_name, GRUB_LIBDIR, DEFAULT_DIRECTORY, PACKAGE_BUGREPORT); - - exit (status); -} - -int -main (int argc, char *argv[]) -{ - FILE *fp; - char *output = NULL; - char *dir = NULL; - char *prefix = NULL; - - program_name = "grub-mkimage"; - - while (1) - { - int c = getopt_long (argc, argv, "d:p:o:hVv", options, 0); - if (c == -1) - break; - - switch (c) - { - case 'd': - if (dir) - free (dir); - dir = xstrdup (optarg); - break; - case 'h': - usage (0); - break; - case 'o': - if (output) - free (output); - output = xstrdup (optarg); - break; - case 'p': - if (prefix) - free (prefix); - prefix = xstrdup (optarg); - break; - case 'V': - printf ("grub-mkimage (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); - return 0; - case 'v': - verbosity++; - break; - default: - usage (1); - break; - } - } - - if (! output) - usage (1); - - fp = fopen (output, "wb"); - if (! fp) - grub_util_error ("cannot open %s", output); - - convert_elf (dir ? : GRUB_LIBDIR, prefix ? : DEFAULT_DIRECTORY, fp, argv + optind); - - fclose (fp); - - return 0; -} diff --git a/util/i386/pc/grub-setup.c b/util/i386/pc/grub-setup.c index 4e2517ef2..f479d5090 100644 --- a/util/i386/pc/grub-setup.c +++ b/util/i386/pc/grub-setup.c @@ -57,6 +57,13 @@ static const grub_gpt_part_type_t grub_gpt_partition_type_bios_boot = GRUB_GPT_P #define DEFAULT_BOOT_FILE "boot.img" #define DEFAULT_CORE_FILE "core.img" +#define grub_target_to_host16(x) grub_le_to_cpu16(x) +#define grub_target_to_host32(x) grub_le_to_cpu32(x) +#define grub_target_to_host64(x) grub_le_to_cpu64(x) +#define grub_host_to_target16(x) grub_cpu_to_le16(x) +#define grub_host_to_target32(x) grub_cpu_to_le32(x) +#define grub_host_to_target64(x) grub_cpu_to_le64(x) + void grub_putchar (int c) { @@ -116,15 +123,10 @@ setup (const char *dir, int NESTED_FUNC_ATTR find_usable_region_msdos (grub_disk_t disk __attribute__ ((unused)), const grub_partition_t p) { - struct grub_msdos_partition *pcdata = p->data; - /* There's always an embed region, and it starts right after the MBR. */ embed_region.start = 1; - /* For its end offset, include as many dummy partitions as we can. */ - if (! grub_msdos_partition_is_empty (pcdata->dos_type) - && ! grub_msdos_partition_is_bsd (pcdata->dos_type) - && embed_region.end > p->start) + if (embed_region.end > p->start) embed_region.end = p->start; return 0; @@ -135,17 +137,21 @@ setup (const char *dir, int NESTED_FUNC_ATTR find_usable_region_gpt (grub_disk_t disk __attribute__ ((unused)), const grub_partition_t p) { - struct grub_gpt_partentry *gptdata = p->data; + struct grub_gpt_partentry gptdata; + + disk->partition = p->parent; + if (grub_disk_read (disk, p->offset, p->index, + sizeof (gptdata), &gptdata)) + return 0; /* If there's an embed region, it is in a dedicated partition. */ - if (! memcmp (&gptdata->type, &grub_gpt_partition_type_bios_boot, 16)) + if (! memcmp (&gptdata.type, &grub_gpt_partition_type_bios_boot, 16)) { embed_region.start = p->start; embed_region.end = p->start + p->len; return 1; } - return 0; } @@ -289,22 +295,19 @@ setup (const char *dir, /* Embed information about the installed location. */ if (root_dev->disk->partition) { - if (strcmp (root_dev->disk->partition->partmap->name, - "part_msdos") == 0) - { - struct grub_msdos_partition *pcdata = - root_dev->disk->partition->data; - dos_part = pcdata->dos_part; - bsd_part = pcdata->bsd_part; - } - else if (strcmp (root_dev->disk->partition->partmap->name, - "part_gpt") == 0) - { - dos_part = root_dev->disk->partition->index; - bsd_part = -1; - } + if (root_dev->disk->partition->parent) + { + if (root_dev->disk->partition->parent->parent) + grub_util_error ("Installing on doubly nested partitions is " + "not supported"); + dos_part = root_dev->disk->partition->parent->number; + bsd_part = root_dev->disk->partition->number; + } else - grub_util_error (_("no DOS-style partitions found")); + { + dos_part = root_dev->disk->partition->number; + bsd_part = -1; + } } else dos_part = bsd_part = -1; @@ -337,6 +340,8 @@ setup (const char *dir, int NESTED_FUNC_ATTR identify_partmap (grub_disk_t disk __attribute__ ((unused)), const grub_partition_t p) { + if (p->parent) + return 0; dest_partmap = p->partmap->name; return 1; } @@ -349,16 +354,16 @@ setup (const char *dir, goto unable_to_embed; } - if (strcmp (dest_partmap, "part_msdos") == 0) + if (strcmp (dest_partmap, "msdos") == 0) grub_partition_iterate (dest_dev->disk, find_usable_region_msdos); - else if (strcmp (dest_partmap, "part_gpt") == 0) + else if (strcmp (dest_partmap, "gpt") == 0) grub_partition_iterate (dest_dev->disk, find_usable_region_gpt); else grub_util_error (_("No DOS-style partitions found")); if (embed_region.end == embed_region.start) { - if (! strcmp (dest_partmap, "part_msdos")) + if (! strcmp (dest_partmap, "msdos")) grub_util_warn (_("This msdos-style partition label has no post-MBR gap; embedding won't be possible!")); else grub_util_warn (_("This GPT partition label has no BIOS Boot Partition; embedding won't be possible!")); @@ -418,7 +423,7 @@ unable_to_embed: grub_util_warn (_("Embedding is not possible. GRUB can only be installed in this " "setup by using blocklists. However, blocklists are UNRELIABLE and " - "its use is discouraged.")); + "their use is discouraged.")); if (! force) grub_util_error (_("if you really want blocklists, use --force")); diff --git a/util/ieee1275/grub-install.in b/util/ieee1275/grub-install.in index 9a26b0dca..4d00cc217 100644 --- a/util/ieee1275/grub-install.in +++ b/util/ieee1275/grub-install.in @@ -34,7 +34,7 @@ target_cpu=@target_cpu@ platform=@platform@ pkglibdir=${libdir}/`echo ${PACKAGE_TARNAME}/${target_cpu}-${platform} | sed ${transform}` -grub_mkimage=${bindir}/`echo grub-mkelfimage | sed ${transform}` +grub_mkimage=${bindir}/`echo grub-mkimage | sed ${transform}` grub_mkdevicemap=${sbindir}/`echo grub-mkdevicemap | sed ${transform}` grub_probe=${sbindir}/`echo grub-probe | sed ${transform}` grub_editenv=${bindir}/`echo grub-editenv | sed ${transform}` @@ -46,8 +46,8 @@ install_device= debug=no update_nvram=yes -ofpathname=/usr/sbin/ofpathname -nvsetenv=/sbin/nvsetenv +ofpathname=`which ofpathname` +nvsetenv=`which nvsetenv` # Usage: usage # Print the usage. @@ -141,8 +141,7 @@ fi # XXX warn on firmware-unreadable filesystems? # Create the GRUB directory if it is not present. -test -d "$bootdir" || mkdir "$bootdir" || exit 1 -test -d "$grubdir" || mkdir "$grubdir" || exit 1 +mkdir -p "$grubdir" || exit 1 # Create the device map file if it is not present. if test -f "$device_map"; then @@ -179,7 +178,10 @@ fi # Then the partition map module. In order to support partition-less media, # this command is allowed to fail (--target=fs already grants us that the # filesystem will be accessible). -partmap_module=`$grub_probe --target=partmap --device-map=${device_map} ${grubdir} 2> /dev/null` +partmap_module= +for x in `$grub_probe --target=partmap --device ${grub_device} 2> /dev/null`; do + partmap_module="$partmap_module part_$x"; +done # Device abstraction module, if any (lvm, raid). devabstraction_module=`$grub_probe --target=abstraction --device-map=${device_map} ${grubdir}` @@ -187,7 +189,7 @@ devabstraction_module=`$grub_probe --target=abstraction --device-map=${device_ma modules="$modules $fs_module $partmap_module $devabstraction_module" # Now perform the installation. -"$grub_mkimage" --directory=${pkglibdir} --output=${grubdir}/grub $modules || exit 1 +"$grub_mkimage" -O ${target_cpu}-ieee1275 --directory=${pkglibdir} --output=${grubdir}/grub $modules || exit 1 if test $update_nvram = yes; then set $ofpathname dummy @@ -216,11 +218,11 @@ if test $update_nvram = yes; then } # Point boot-device at the new grub install - boot_device="boot-device $ofpath:$partno,\\grub" - "$nvsetenv" "$boot_device" || { + boot_device="$ofpath:$partno,\\grub" + "$nvsetenv" boot-device "$boot_device" || { echo "$nvsetenv failed." echo "You will have to set boot-device manually. At the Open Firmware prompt, type:" - echo " setenv $boot_device" + echo " setenv boot-device $boot_device" exit 1 } fi diff --git a/util/sparc64/ieee1275/grub-ofpathname.c b/util/ieee1275/grub-ofpathname.c similarity index 100% rename from util/sparc64/ieee1275/grub-ofpathname.c rename to util/ieee1275/grub-ofpathname.c diff --git a/util/ieee1275/ofpath.c b/util/ieee1275/ofpath.c index 79a0e8be5..fa0d48cf9 100644 --- a/util/ieee1275/ofpath.c +++ b/util/ieee1275/ofpath.c @@ -368,7 +368,7 @@ strip_trailing_digits (const char *p) } char * -grub_util_devname_to_ofpath (char *devname) +grub_util_devname_to_ofpath (const char *devname) { char *name_buf, *device, *devnode, *devicenode, *ofpath; diff --git a/util/import_gcry.py b/util/import_gcry.py index d71924d53..b9c3edcde 100644 --- a/util/import_gcry.py +++ b/util/import_gcry.py @@ -62,6 +62,7 @@ mdblocksizes = {"_gcry_digest_spec_crc32" : 64, "_gcry_digest_spec_whirlpool" : 64} cryptolist = open (os.path.join (cipher_dir_out, "crypto.lst"), "w") +conf.write ("MAINTAINER_CLEANFILES += $(srcdir)/conf/gcry.rmk $(srcdir)/lib/libgcrypt-grub/cipher/ChangeLog $(srcdir)/lib/libgcrypt-grub/cipher/cipher.h $(srcdir)/lib/libgcrypt-grub/cipher/crypto.lst $(srcdir)/lib/libgcrypt-grub/cipher/g10lib.h $(srcdir)/lib/libgcrypt-grub/cipher/memory.h $(srcdir)/lib/libgcrypt-grub/cipher/types.h\n"); # rijndael is the only cipher using aliases. So no need for mangling, just # hardcode it @@ -87,6 +88,7 @@ for cipher_file in cipher_files: continue nch = False if re.match (".*\.[ch]$", cipher_file): + conf.write ("MAINTAINER_CLEANFILES += $(srcdir)/lib/libgcrypt-grub/cipher/" + cipher_file + "\n"); isc = re.match (".*\.c$", cipher_file) f = open (infile, "r") fw = open (outfile, "w") diff --git a/util/lvm.c b/util/lvm.c index 8a8ed1e4c..0a0916344 100644 --- a/util/lvm.c +++ b/util/lvm.c @@ -26,6 +26,8 @@ #include #include +#define LVM_DEV_MAPPER_STRING "/dev/mapper/" + int grub_util_lvm_isvolume (char *name) { @@ -33,10 +35,10 @@ grub_util_lvm_isvolume (char *name) struct stat st; int err; - devname = xmalloc (strlen (name) + 13); + devname = xmalloc (strlen (name) + sizeof (LVM_DEV_MAPPER_STRING)); - strcpy (devname, "/dev/mapper/"); - strcpy (devname+12, name); + strcpy (devname, LVM_DEV_MAPPER_STRING); + strcpy (devname + sizeof(LVM_DEV_MAPPER_STRING) - 1, name); err = stat (devname, &st); free (devname); diff --git a/util/misc.c b/util/misc.c index c5ab62e42..17fa6d5e4 100644 --- a/util/misc.c +++ b/util/misc.c @@ -30,8 +30,12 @@ #include #include #include +#ifdef HAVE_LIMITS_H +#include +#endif #include +#include #include #include #include @@ -40,6 +44,7 @@ #include #include +#define ENABLE_RELOCATABLE 0 #include "progname.h" /* Include malloc.h, only if memalign is available. It is known that @@ -100,6 +105,7 @@ grub_util_error (const char *fmt, ...) exit (1); } +#ifdef GRUB_UTIL int grub_err_printf (const char *fmt, ...) { @@ -112,6 +118,7 @@ grub_err_printf (const char *fmt, ...) return ret; } +#endif void * xmalloc (size_t size) @@ -139,13 +146,13 @@ char * xstrdup (const char *str) { size_t len; - char *dup; + char *newstr; len = strlen (str); - dup = (char *) xmalloc (len + 1); - memcpy (dup, str, len + 1); + newstr = (char *) xmalloc (len + 1); + memcpy (newstr, str, len + 1); - return dup; + return newstr; } char * @@ -256,56 +263,6 @@ grub_util_write_image (const char *img, size_t size, FILE *out) grub_util_error ("write failed"); } -void * -grub_malloc (grub_size_t size) -{ - return xmalloc (size); -} - -void * -grub_zalloc (grub_size_t size) -{ - void *ret; - - ret = xmalloc (size); - memset (ret, 0, size); - return ret; -} - -void -grub_free (void *ptr) -{ - free (ptr); -} - -void * -grub_realloc (void *ptr, grub_size_t size) -{ - return xrealloc (ptr, size); -} - -void * -grub_memalign (grub_size_t align, grub_size_t size) -{ - void *p; - -#if defined(HAVE_POSIX_MEMALIGN) - if (posix_memalign (&p, align, size) != 0) - p = 0; -#elif defined(HAVE_MEMALIGN) - p = memalign (align, size); -#else - (void) align; - (void) size; - grub_util_error ("grub_memalign is not supported"); -#endif - - if (! p) - grub_util_error ("out of memory"); - - return p; -} - /* Some functions that we don't use. */ void grub_mm_init_region (void *addr __attribute__ ((unused)), @@ -313,10 +270,12 @@ grub_mm_init_region (void *addr __attribute__ ((unused)), { } +#if GRUB_NO_MODULES void grub_register_exported_symbols (void) { } +#endif void grub_exit (void) @@ -368,11 +327,13 @@ grub_millisleep (grub_uint32_t ms) #endif +#if !(defined (__i386__) || defined (__x86_64__)) && GRUB_NO_MODULES void grub_arch_sync_caches (void *address __attribute__ ((unused)), grub_size_t len __attribute__ ((unused))) { } +#endif #ifndef HAVE_VASPRINTF @@ -513,13 +474,13 @@ make_system_path_relative_to_its_root (const char *path) grub_util_error ("failed to get canonical path of %s", path); len = strlen (p) + 1; - buf = strdup (p); + buf = xstrdup (p); free (p); if (stat (buf, &st) < 0) grub_util_error ("cannot stat %s: %s", buf, strerror (errno)); - buf2 = strdup (buf); + buf2 = xstrdup (buf); num = st.st_dev; /* This loop sets offset to the number of chars of the root @@ -541,12 +502,16 @@ make_system_path_relative_to_its_root (const char *path) /* buf is another filesystem; we found it. */ if (st.st_dev != num) { - /* offset == 0 means path given is the mount point. */ + /* offset == 0 means path given is the mount point. + This works around special-casing of "/" in Un*x. This function never + prints trailing slashes (so that its output can be appended a slash + unconditionally). Each slash in is considered a preceding slash, and + therefore the root directory is an empty string. */ if (offset == 0) { free (buf); free (buf2); - return strdup ("/"); + return xstrdup (""); } else break; @@ -563,11 +528,19 @@ make_system_path_relative_to_its_root (const char *path) buf2[len - 1] = '\0'; len--; } - return buf2; + if (len > 1) + return buf2; + else + { + /* This means path given is just a backslash. As above + we have to return an empty string. */ + free (buf2); + return xstrdup (""); + } } } free (buf); - buf3 = strdup (buf2 + offset); + buf3 = xstrdup (buf2 + offset); free (buf2); len = strlen (buf3); @@ -577,22 +550,17 @@ make_system_path_relative_to_its_root (const char *path) len--; } - /* This works around special-casing of "/" in Un*x. This function never - prints trailing slashes (so that its output can be appended a slash - unconditionally). Each slash in is considered a preceding slash, and - therefore the root directory is an empty string. */ - if (!strcmp (buf3, "/")) - buf3[0] = '\0'; - return buf3; } +#ifdef GRUB_UTIL void grub_util_init_nls (void) { -#if ENABLE_NLS +#if (defined(ENABLE_NLS) && ENABLE_NLS) setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); -#endif /* ENABLE_NLS */ +#endif /* (defined(ENABLE_NLS) && ENABLE_NLS) */ } +#endif diff --git a/util/mkisofs/mkisofs.c b/util/mkisofs/mkisofs.c index 69b4b3d0d..16e2f0c7d 100644 --- a/util/mkisofs/mkisofs.c +++ b/util/mkisofs/mkisofs.c @@ -640,11 +640,11 @@ int FDECL2(main, int, argc, char **, argv){ char *log_file = 0; set_program_name (argv[0]); -#if ENABLE_NLS +#if (defined(ENABLE_NLS) && ENABLE_NLS) setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); -#endif /* ENABLE_NLS */ +#endif /* (defined(ENABLE_NLS) && ENABLE_NLS) */ if (argc < 2) usage(); diff --git a/util/mkisofs/mkisofs.h b/util/mkisofs/mkisofs.h index 482db6ceb..b699516e9 100644 --- a/util/mkisofs/mkisofs.h +++ b/util/mkisofs/mkisofs.h @@ -30,12 +30,12 @@ #include #include -#if ENABLE_NLS +#if (defined(ENABLE_NLS) && ENABLE_NLS) # include # include -#else /* ! ENABLE_NLS */ +#else /* ! (defined(ENABLE_NLS) && ENABLE_NLS) */ /* Disabled NLS. The casts to 'const char *' serve the purpose of producing warnings @@ -43,7 +43,7 @@ On pre-ANSI systems without 'const', the config.h file is supposed to contain "#define const". */ # define gettext(Msgid) ((const char *) (Msgid)) -#endif /* ENABLE_NLS */ +#endif /* (defined(ENABLE_NLS) && ENABLE_NLS) */ #define _(str) gettext(str) #define N_(str) str diff --git a/util/mm.c b/util/mm.c new file mode 100644 index 000000000..0e9e9f3a8 --- /dev/null +++ b/util/mm.c @@ -0,0 +1,85 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +void * +grub_malloc (grub_size_t size) +{ + void *ret; + ret = malloc (size); + if (!ret) + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + return ret; +} + +void * +grub_zalloc (grub_size_t size) +{ + void *ret; + + ret = grub_malloc (size); + if (!ret) + return NULL; + memset (ret, 0, size); + return ret; +} + +void +grub_free (void *ptr) +{ + free (ptr); +} + +void * +grub_realloc (void *ptr, grub_size_t size) +{ + void *ret; + ret = realloc (ptr, size); + if (!ret) + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + return ret; +} + +void * +grub_memalign (grub_size_t align, grub_size_t size) +{ + void *p; + +#if defined(HAVE_POSIX_MEMALIGN) + if (align < sizeof (void *)) + align = sizeof (void *); + if (posix_memalign (&p, align, size) != 0) + p = 0; +#elif defined(HAVE_MEMALIGN) + p = memalign (align, size); +#else + (void) align; + (void) size; + grub_util_error ("grub_memalign is not supported"); +#endif + + if (!p) + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + + return p; +} diff --git a/util/powerpc/ieee1275/grub-mkrescue.in b/util/powerpc/ieee1275/grub-mkrescue.in index 0110e799c..16658505b 100644 --- a/util/powerpc/ieee1275/grub-mkrescue.in +++ b/util/powerpc/ieee1275/grub-mkrescue.in @@ -30,7 +30,7 @@ target_cpu=@target_cpu@ platform=@platform@ pkglibdir=${libdir}/`echo ${PACKAGE_TARNAME}/${target_cpu}-${platform} | sed ${transform}` -grub_mkimage=${bindir}/`echo grub-mkelfimage | sed ${transform}` +grub_mkimage=${bindir}/`echo grub-mkimage | sed ${transform}` # Usage: usage # Print the usage. @@ -104,7 +104,7 @@ boot_dir=${iso_dir}/boot/grub mkdir ${iso_dir}/boot mkdir ${boot_dir} core_img=${boot_dir}/grub.img -${grub_mkimage} -n -d ${input_dir}/ -o ${core_img} ${modules} +${grub_mkimage} -O powerpc-ieee1275 -n -d ${input_dir}/ -o ${core_img} ${modules} genisoimage -hfs -part -no-desktop -r -J -o ${output_image} \ -map ${map_file} -hfs-bless ${boot_dir} -chrp-boot -sysid PPC \ ${iso_dir} diff --git a/util/sdl.c b/util/sdl.c new file mode 100644 index 000000000..d261db6b0 --- /dev/null +++ b/util/sdl.c @@ -0,0 +1,237 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#define grub_video_render_target grub_video_fbrender_target + +#include +#include +#include +#include +#include +#include +#include +#include + +static SDL_Surface *window = 0; +static struct grub_video_render_target *sdl_render_target; +static struct grub_video_mode_info mode_info; + +static grub_err_t +grub_video_sdl_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data); + +static grub_err_t +grub_video_sdl_init (void) +{ + window = 0; + + if (SDL_Init (SDL_INIT_VIDEO) < 0) + return grub_error (GRUB_ERR_BAD_DEVICE, "Couldn't init SDL: %s", + SDL_GetError ()); + + grub_memset (&mode_info, 0, sizeof (mode_info)); + + return grub_video_fb_init (); +} + +static grub_err_t +grub_video_sdl_fini (void) +{ + SDL_Quit (); + window = 0; + + grub_memset (&mode_info, 0, sizeof (mode_info)); + + return grub_video_fb_fini (); +} + +static inline unsigned int +get_mask_size (grub_uint32_t mask) +{ + unsigned i; + for (i = 0; mask > 1U << i; i++); + return i; +} + +static grub_err_t +grub_video_sdl_setup (unsigned int width, unsigned int height, + unsigned int mode_type, unsigned int mode_mask) +{ + int depth; + int flags = 0; + grub_err_t err; + + /* Decode depth from mode_type. If it is zero, then autodetect. */ + depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) + >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS; + + if (depth == 0) + depth = 32; + + if (width == 0 && height == 0) + { + width = 800; + height = 600; + } + + if ((mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED) + || !(mode_mask & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED)) + flags |= SDL_DOUBLEBUF; + + window = SDL_SetVideoMode (width, height, depth, flags | SDL_HWSURFACE); + if (! window) + window = SDL_SetVideoMode (width, height, depth, flags | SDL_SWSURFACE); + if (! window) + return grub_error (GRUB_ERR_BAD_DEVICE, "Couldn't open window: %s", + SDL_GetError ()); + + grub_memset (&sdl_render_target, 0, sizeof (sdl_render_target)); + + mode_info.width = window->w; + mode_info.height = window->h; + mode_info.mode_type = 0; + if (window->flags & SDL_DOUBLEBUF) + mode_info.mode_type + |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + if (window->format->palette) + mode_info.mode_type |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; + else + mode_info.mode_type |= GRUB_VIDEO_MODE_TYPE_RGB; + + mode_info.bpp = window->format->BitsPerPixel; + mode_info.bytes_per_pixel = window->format->BytesPerPixel; + mode_info.pitch = window->pitch; + + /* In index color mode, number of colors. In RGB mode this is 256. */ + if (window->format->palette) + mode_info.number_of_colors + = 1 << window->format->BitsPerPixel; + else + mode_info.number_of_colors = 256; + + if (! window->format->palette) + { + mode_info.red_mask_size + = get_mask_size (window->format->Rmask >> window->format->Rshift); + mode_info.red_field_pos = window->format->Rshift; + mode_info.green_mask_size + = get_mask_size (window->format->Gmask >> window->format->Gshift); + mode_info.green_field_pos = window->format->Gshift; + mode_info.blue_mask_size + = get_mask_size (window->format->Bmask >> window->format->Bshift); + mode_info.blue_field_pos = window->format->Bshift; + mode_info.reserved_mask_size + = get_mask_size (window->format->Amask >> window->format->Ashift); + mode_info.reserved_field_pos = window->format->Ashift; + mode_info.blit_format + = grub_video_get_blit_format (&mode_info); + } + + err = grub_video_fb_create_render_target_from_pointer (&sdl_render_target, + &mode_info, + window->pixels); + if (err) + return err; + + /* Copy default palette to initialize emulated palette. */ + grub_video_sdl_set_palette (0, (sizeof (grub_video_fbstd_colors) + / sizeof (grub_video_fbstd_colors[0])), + grub_video_fbstd_colors); + + /* Reset render target to SDL one. */ + return grub_video_fb_set_active_render_target (sdl_render_target); +} + +static grub_err_t +grub_video_sdl_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data) +{ + unsigned i; + if (window->format->palette) + { + SDL_Color *tmp = grub_malloc (count * sizeof (tmp[0])); + for (i = 0; i < count; i++) + { + tmp[i].r = palette_data[i].r; + tmp[i].g = palette_data[i].g; + tmp[i].b = palette_data[i].b; + tmp[i].unused = palette_data[i].a; + } + SDL_SetColors (window, tmp, start, count); + grub_free (tmp); + } + + return grub_video_fb_set_palette (start, count, palette_data); +} + +static grub_err_t +grub_video_sdl_swap_buffers (void) +{ + if (SDL_Flip (window) < 0) + return grub_error (GRUB_ERR_BAD_DEVICE, "couldn't swap buffers: %s", + SDL_GetError ()); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_sdl_set_active_render_target (struct grub_video_render_target *target) +{ + if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) + return grub_video_fb_set_active_render_target (sdl_render_target); + + return grub_video_fb_set_active_render_target (target); +} + +static struct grub_video_adapter grub_video_sdl_adapter = + { + .name = "SDL Video Driver", + + .init = grub_video_sdl_init, + .fini = grub_video_sdl_fini, + .setup = grub_video_sdl_setup, + .get_info = grub_video_fb_get_info, + .set_palette = grub_video_sdl_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_sdl_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_sdl_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + + .next = 0 + }; + +GRUB_MOD_INIT(sdl) +{ + grub_video_register (&grub_video_sdl_adapter); +} + +GRUB_MOD_FINI(sdl) +{ + grub_video_unregister (&grub_video_sdl_adapter); +} diff --git a/util/sparc64/ieee1275/grub-mkimage.c b/util/sparc64/ieee1275/grub-mkimage.c deleted file mode 100644 index 6907b8d8a..000000000 --- a/util/sparc64/ieee1275/grub-mkimage.c +++ /dev/null @@ -1,300 +0,0 @@ -/* grub-mkimage.c - make a bootable image */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2008,2009,2010 Free Software Foundation, Inc. - * - * GRUB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GRUB is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GRUB. If not, see . - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define _GNU_SOURCE 1 -#include - -#include "progname.h" - -static void -compress_kernel (char *kernel_img, size_t kernel_size, - char **core_img, size_t *core_size) -{ - /* No compression support yet. */ - grub_util_info ("kernel_img=%p, kernel_size=0x%x", kernel_img, kernel_size); - *core_img = xmalloc (kernel_size); - memcpy (*core_img, kernel_img, kernel_size); - *core_size = kernel_size; -} - -static void -generate_image (const char *dir, const char *prefix, FILE *out, char *mods[], char *memdisk_path) -{ - size_t kernel_size, total_module_size, memdisk_size, core_size, boot_size, offset; - char *kernel_path, *kernel_img, *core_img, *boot_path, *boot_img; - struct grub_util_path_list *path_list, *p; - struct grub_module_info *modinfo; - grub_addr_t module_addr; - unsigned int num; - - path_list = grub_util_resolve_dependencies (dir, "moddep.lst", mods); - - kernel_path = grub_util_get_path (dir, "kernel.img"); - kernel_size = grub_util_get_image_size (kernel_path); - - total_module_size = sizeof (struct grub_module_info); - for (p = path_list; p; p = p->next) - total_module_size += (grub_util_get_image_size (p->name) - + sizeof (struct grub_module_header)); - - memdisk_size = 0; - if (memdisk_path) - { - memdisk_size = ALIGN_UP(grub_util_get_image_size (memdisk_path), 512); - grub_util_info ("the size of memory disk is 0x%x", memdisk_size); - total_module_size += memdisk_size + sizeof (struct grub_module_header); - } - - grub_util_info ("the total module size is 0x%x", total_module_size); - - kernel_img = xmalloc (kernel_size + total_module_size); - grub_util_load_image (kernel_path, kernel_img); - - if ((GRUB_KERNEL_MACHINE_PREFIX + strlen (prefix) + 1) - > GRUB_KERNEL_MACHINE_DATA_END) - grub_util_error ("prefix too long"); - strcpy (kernel_img + GRUB_KERNEL_MACHINE_PREFIX, prefix); - - /* Fill in the grub_module_info structure. */ - modinfo = (struct grub_module_info *) (kernel_img + kernel_size); - modinfo->magic = GRUB_MODULE_MAGIC; - modinfo->offset = sizeof (struct grub_module_info); - modinfo->size = total_module_size; - - offset = kernel_size + sizeof (struct grub_module_info); - for (p = path_list; p; p = p->next) - { - struct grub_module_header *header; - size_t mod_size; - - mod_size = grub_util_get_image_size (p->name); - - header = (struct grub_module_header *) (kernel_img + offset); - header->type = OBJ_TYPE_ELF; - header->size = grub_host_to_target32 (mod_size + sizeof (*header)); - offset += sizeof (*header); - - grub_util_load_image (p->name, kernel_img + offset); - offset += mod_size; - } - - if (memdisk_path) - { - struct grub_module_header *header; - - header = (struct grub_module_header *) (kernel_img + offset); - header->type = OBJ_TYPE_MEMDISK; - header->size = grub_host_to_target32 (memdisk_size + sizeof (*header)); - offset += sizeof (*header); - - grub_util_load_image (memdisk_path, kernel_img + offset); - offset += memdisk_size; - } - - compress_kernel (kernel_img, kernel_size + total_module_size, - &core_img, &core_size); - - grub_util_info ("the core size is 0x%x", core_size); - - num = ((core_size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS); - num <<= GRUB_DISK_SECTOR_BITS; - - boot_path = grub_util_get_path (dir, "diskboot.img"); - boot_size = grub_util_get_image_size (boot_path); - if (boot_size != GRUB_DISK_SECTOR_SIZE) - grub_util_error ("diskboot.img is not one sector size"); - - boot_img = grub_util_read_image (boot_path); - - /* sparc is a big endian architecture. */ - *((grub_uint32_t *) (boot_img + GRUB_DISK_SECTOR_SIZE - - GRUB_BOOT_MACHINE_LIST_SIZE + 8)) - = grub_cpu_to_be32 (num); - - grub_util_write_image (boot_img, boot_size, out); - free (boot_img); - free (boot_path); - - module_addr = (path_list - ? (GRUB_BOOT_MACHINE_IMAGE_ADDRESS + kernel_size) - : 0); - - grub_util_info ("the first module address is 0x%x", module_addr); - - *((grub_uint32_t *) (core_img + GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE)) - = grub_cpu_to_be32 (total_module_size); - *((grub_uint32_t *) (core_img + GRUB_KERNEL_MACHINE_KERNEL_IMAGE_SIZE)) - = grub_cpu_to_be32 (kernel_size); - - /* No compression support yet. */ - *((grub_uint32_t *) (core_img + GRUB_KERNEL_MACHINE_COMPRESSED_SIZE)) - = grub_cpu_to_be32 (0); - - grub_util_write_image (core_img, core_size, out); - free (kernel_img); - free (core_img); - free (kernel_path); - - while (path_list) - { - struct grub_util_path_list *next = path_list->next; - free ((void *) path_list->name); - free (path_list); - path_list = next; - } -} - -static struct option options[] = - { - {"directory", required_argument, 0, 'd'}, - {"prefix", required_argument, 0, 'p'}, - {"memdisk", required_argument, 0, 'm'}, - {"output", required_argument, 0, 'o'}, - {"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'V'}, - {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0} - }; - -static void -usage (int status) -{ - if (status) - fprintf (stderr, "Try `%s --help' for more information.\n", program_name); - else - printf ("\ -Usage: %s [OPTION]... [MODULES]\n\ -\n\ -Make a bootable image of GRUB.\n\ -\n\ - -d, --directory=DIR use images and modules under DIR [default=%s]\n\ - -p, --prefix=DIR set grub_prefix directory [default=%s]\n\ - -m, --memdisk=FILE embed FILE as a memdisk image\n\ - -o, --output=FILE output a generated image to FILE [default=stdout]\n\ - -h, --help display this message and exit\n\ - -V, --version print version information and exit\n\ - -v, --verbose print verbose messages\n\ -\n\ -Report bugs to <%s>.\n\ -", program_name, GRUB_LIBDIR, DEFAULT_DIRECTORY, PACKAGE_BUGREPORT); - - exit (status); -} - -int -main (int argc, char *argv[]) -{ - char *output = NULL; - char *dir = NULL; - char *prefix = NULL; - char *memdisk = NULL; - FILE *fp = stdout; - - set_program_name (argv[0]); - - grub_util_init_nls (); - - while (1) - { - int c = getopt_long (argc, argv, "d:p:m:o:hVv", options, 0); - - if (c == -1) - break; - else - switch (c) - { - case 'o': - if (output) - free (output); - output = xstrdup (optarg); - break; - - case 'd': - if (dir) - free (dir); - dir = xstrdup (optarg); - break; - - case 'm': - if (memdisk) - free (memdisk); - memdisk = xstrdup (optarg); - - if (prefix) - free (prefix); - prefix = xstrdup ("(memdisk)/boot/grub"); - break; - - case 'h': - usage (0); - break; - - case 'p': - if (prefix) - free (prefix); - prefix = xstrdup (optarg); - break; - - case 'V': - printf ("grub-mkimage (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); - return 0; - - case 'v': - verbosity++; - break; - - default: - usage (1); - break; - } - } - - if (output) - { - fp = fopen (output, "wb"); - if (! fp) - grub_util_error ("cannot open %s", output); - } - - generate_image (dir ? : GRUB_LIBDIR, - prefix ? : DEFAULT_DIRECTORY, fp, - argv + optind, memdisk); - - fclose (fp); - - if (dir) - free (dir); - - return 0; -} diff --git a/util/sparc64/ieee1275/grub-setup.c b/util/sparc64/ieee1275/grub-setup.c index c39ea853f..fbf6ba036 100644 --- a/util/sparc64/ieee1275/grub-setup.c +++ b/util/sparc64/ieee1275/grub-setup.c @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -75,6 +76,13 @@ #define DEFAULT_BOOT_FILE "boot.img" #define DEFAULT_CORE_FILE "core.img" +#define grub_target_to_host16(x) grub_be_to_cpu16(x) +#define grub_target_to_host32(x) grub_be_to_cpu32(x) +#define grub_target_to_host64(x) grub_be_to_cpu64(x) +#define grub_host_to_target16(x) grub_cpu_to_be16(x) +#define grub_host_to_target32(x) grub_cpu_to_be32(x) +#define grub_host_to_target64(x) grub_cpu_to_be64(x) + /* This is the blocklist used in the diskboot image. */ struct boot_blocklist { @@ -103,28 +111,6 @@ grub_refresh (void) fflush (stdout); } -static char *compute_dest_ofpath (const char *dest) -{ - int len = strlen (dest); - char *res, *p, c; - - res = xmalloc (len); - p = res; - while ((c = *dest++) != '\0') - { - if (c == '\\' && *dest == ',') - { - *p++ = ','; - dest++; - } - else - *p++ = c; - } - *p++ = '\0'; - - return res; -} - static void setup (const char *prefix, const char *dir, const char *boot_file, const char *core_file, @@ -135,8 +121,8 @@ setup (const char *prefix, const char *dir, size_t boot_size, core_size; grub_uint16_t core_sectors; grub_device_t root_dev, dest_dev; - char *boot_devpath, *dest_ofpath; - grub_disk_addr_t *kernel_sector; + char *boot_devpath; + grub_disk_addr_t *kernel_byte; struct boot_blocklist *first_block, *block; char *tmp_img; int i; @@ -195,8 +181,6 @@ setup (const char *prefix, const char *dir, last_length = length; } - dest_ofpath = compute_dest_ofpath (dest); - /* Read the boot image by the OS service. */ boot_path = grub_util_get_path (dir, boot_file); boot_size = grub_util_get_image_size (boot_path); @@ -210,9 +194,9 @@ setup (const char *prefix, const char *dir, boot_devpath = (char *) (boot_img + GRUB_BOOT_AOUT_HEADER_SIZE + GRUB_BOOT_MACHINE_BOOT_DEVPATH); - kernel_sector = (grub_disk_addr_t *) (boot_img - + GRUB_BOOT_AOUT_HEADER_SIZE - + GRUB_BOOT_MACHINE_KERNEL_SECTOR); + kernel_byte = (grub_disk_addr_t *) (boot_img + + GRUB_BOOT_AOUT_HEADER_SIZE + + GRUB_BOOT_MACHINE_KERNEL_BYTE); core_path = grub_util_get_path (dir, core_file); core_size = grub_util_get_image_size (core_path); @@ -229,8 +213,7 @@ setup (const char *prefix, const char *dir, + GRUB_DISK_SECTOR_SIZE - sizeof (*block)); - grub_util_info ("root is `%s', dest is `%s', and dest_ofpath is `%s'", - root, dest, dest_ofpath); + grub_util_info ("root is `%s', dest is `%s'", root, dest); /* Open the root device and the destination device. */ grub_util_info ("Opening root"); @@ -351,14 +334,30 @@ setup (const char *prefix, const char *dir, != (grub_ssize_t) core_size - GRUB_DISK_SECTOR_SIZE) grub_util_error ("failed to read the rest sectors of the core image"); + if (file->device->disk->id != dest_dev->disk->id) + { + const char *dest_ofpath; + dest_ofpath + = grub_util_devname_to_ofpath (grub_util_biosdisk_get_osdev (file->device->disk)); + grub_util_info ("dest_ofpath is `%s'", dest_ofpath); + strncpy (boot_devpath, dest_ofpath, GRUB_BOOT_MACHINE_BOOT_DEVPATH_END + - GRUB_BOOT_MACHINE_BOOT_DEVPATH - 1); + boot_devpath[GRUB_BOOT_MACHINE_BOOT_DEVPATH_END + - GRUB_BOOT_MACHINE_BOOT_DEVPATH - 1] = 0; + } + else + { + grub_util_info ("non cross-disk install"); + memset (boot_devpath, 0, GRUB_BOOT_MACHINE_BOOT_DEVPATH_END + - GRUB_BOOT_MACHINE_BOOT_DEVPATH); + } + grub_file_close (file); free (core_path); free (tmp_img); - *kernel_sector = grub_cpu_to_be64 (first_sector); - - strcpy(boot_devpath, dest_ofpath); + *kernel_byte = grub_cpu_to_be64 (first_sector << GRUB_DISK_SECTOR_BITS); grub_util_info ("boot device path %s, prefix is %s, dest is %s", boot_devpath, prefix, dest); diff --git a/util/time.c b/util/time.c new file mode 100644 index 000000000..5da8092a9 --- /dev/null +++ b/util/time.c @@ -0,0 +1,46 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include + +grub_err_t +grub_get_datetime (struct grub_datetime *datetime) +{ + struct tm *mytm; + time_t mytime; + + mytime = time (&mytime); + mytm = gmtime (&mytime); + + datetime->year = mytm->tm_year + 1900; + datetime->month = mytm->tm_mon + 1; + datetime->day = mytm->tm_mday; + datetime->hour = mytm->tm_hour; + datetime->minute = mytm->tm_min; + datetime->second = mytm->tm_sec; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_set_datetime (struct grub_datetime *datetime __attribute__ ((unused))) +{ + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "no clock setting routine available"); +} diff --git a/video/bitmap.c b/video/bitmap.c index 2ea640728..e06a5b696 100644 --- a/video/bitmap.c +++ b/video/bitmap.c @@ -243,11 +243,11 @@ void *grub_video_bitmap_get_data (struct grub_video_bitmap *bitmap) } /* Initialize bitmap module. */ -GRUB_MOD_INIT(video_bitmap) +GRUB_MOD_INIT(bitmap) { } /* Finalize bitmap module. */ -GRUB_MOD_FINI(video_bitmap) +GRUB_MOD_FINI(bitmap) { } diff --git a/video/bitmap_scale.c b/video/bitmap_scale.c new file mode 100644 index 000000000..6f8ff247e --- /dev/null +++ b/video/bitmap_scale.c @@ -0,0 +1,308 @@ +/* bitmap_scale.c - Bitmap scaling. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +/* Prototypes for module-local functions. */ +static grub_err_t scale_nn (struct grub_video_bitmap *dst, + struct grub_video_bitmap *src); +static grub_err_t scale_bilinear (struct grub_video_bitmap *dst, + struct grub_video_bitmap *src); + +/* This function creates a new scaled version of the bitmap SRC. The new + bitmap has dimensions DST_WIDTH by DST_HEIGHT. The scaling algorithm + is given by SCALE_METHOD. If an error is encountered, the return code is + not equal to GRUB_ERR_NONE, and the bitmap DST is either not created, or + it is destroyed before this function returns. + + Supports only direct color modes which have components separated + into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color). + But because of this simplifying assumption, the implementation is + greatly simplified. */ +grub_err_t +grub_video_bitmap_create_scaled (struct grub_video_bitmap **dst, + int dst_width, int dst_height, + struct grub_video_bitmap *src, + enum grub_video_bitmap_scale_method + scale_method) +{ + *dst = 0; + + /* Verify the simplifying assumptions. */ + if (src == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "null src bitmap in grub_video_bitmap_create_scaled"); + if (src->mode_info.red_field_pos % 8 != 0 + || src->mode_info.green_field_pos % 8 != 0 + || src->mode_info.blue_field_pos % 8 != 0 + || src->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "src format not supported for scale"); + if (src->mode_info.width == 0 || src->mode_info.height == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "source bitmap has a zero dimension"); + if (dst_width <= 0 || dst_height <= 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "requested to scale to a size w/ a zero dimension"); + if (src->mode_info.bytes_per_pixel * 8 != src->mode_info.bpp) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "bitmap to scale has inconsistent Bpp and bpp"); + + /* Create the new bitmap. */ + grub_err_t ret; + ret = grub_video_bitmap_create (dst, dst_width, dst_height, + src->mode_info.blit_format); + if (ret != GRUB_ERR_NONE) + return ret; /* Error. */ + + switch (scale_method) + { + case GRUB_VIDEO_BITMAP_SCALE_METHOD_FASTEST: + case GRUB_VIDEO_BITMAP_SCALE_METHOD_NEAREST: + ret = scale_nn (*dst, src); + break; + case GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST: + case GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR: + ret = scale_bilinear (*dst, src); + break; + default: + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid scale_method value"); + break; + } + + if (ret == GRUB_ERR_NONE) + { + /* Success: *dst is now a pointer to the scaled bitmap. */ + return GRUB_ERR_NONE; + } + else + { + /* Destroy the bitmap and return the error code. */ + grub_video_bitmap_destroy (*dst); + *dst = 0; + return ret; + } +} + +/* Nearest neighbor bitmap scaling algorithm. + + Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the + dimensions of DST. This function uses the nearest neighbor algorithm to + interpolate the pixels. + + Supports only direct color modes which have components separated + into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color). + But because of this simplifying assumption, the implementation is + greatly simplified. */ +static grub_err_t +scale_nn (struct grub_video_bitmap *dst, struct grub_video_bitmap *src) +{ + /* Verify the simplifying assumptions. */ + if (dst == 0 || src == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "null bitmap in scale_nn"); + if (dst->mode_info.red_field_pos % 8 != 0 + || dst->mode_info.green_field_pos % 8 != 0 + || dst->mode_info.blue_field_pos % 8 != 0 + || dst->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst format not supported"); + if (src->mode_info.red_field_pos % 8 != 0 + || src->mode_info.green_field_pos % 8 != 0 + || src->mode_info.blue_field_pos % 8 != 0 + || src->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "src format not supported"); + if (dst->mode_info.red_field_pos != src->mode_info.red_field_pos + || dst->mode_info.red_mask_size != src->mode_info.red_mask_size + || dst->mode_info.green_field_pos != src->mode_info.green_field_pos + || dst->mode_info.green_mask_size != src->mode_info.green_mask_size + || dst->mode_info.blue_field_pos != src->mode_info.blue_field_pos + || dst->mode_info.blue_mask_size != src->mode_info.blue_mask_size + || dst->mode_info.reserved_field_pos != + src->mode_info.reserved_field_pos + || dst->mode_info.reserved_mask_size != + src->mode_info.reserved_mask_size) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible"); + if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible"); + if (dst->mode_info.width == 0 || dst->mode_info.height == 0 + || src->mode_info.width == 0 || src->mode_info.height == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bitmap has a zero dimension"); + + grub_uint8_t *ddata = dst->data; + grub_uint8_t *sdata = src->data; + int dw = dst->mode_info.width; + int dh = dst->mode_info.height; + int sw = src->mode_info.width; + int sh = src->mode_info.height; + int dstride = dst->mode_info.pitch; + int sstride = src->mode_info.pitch; + /* bytes_per_pixel is the same for both src and dst. */ + int bytes_per_pixel = dst->mode_info.bytes_per_pixel; + + int dy; + for (dy = 0; dy < dh; dy++) + { + int dx; + for (dx = 0; dx < dw; dx++) + { + grub_uint8_t *dptr; + grub_uint8_t *sptr; + int sx; + int sy; + int comp; + + /* Compute the source coordinate that the destination coordinate + maps to. Note: sx/sw = dx/dw => sx = sw*dx/dw. */ + sx = sw * dx / dw; + sy = sh * dy / dh; + + /* Get the address of the pixels in src and dst. */ + dptr = ddata + dy * dstride + dx * bytes_per_pixel; + sptr = sdata + sy * sstride + sx * bytes_per_pixel; + + /* Copy the pixel color value. */ + for (comp = 0; comp < bytes_per_pixel; comp++) + dptr[comp] = sptr[comp]; + } + } + return GRUB_ERR_NONE; +} + +/* Bilinear interpolation image scaling algorithm. + + Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the + dimensions of DST. This function uses the bilinear interpolation algorithm + to interpolate the pixels. + + Supports only direct color modes which have components separated + into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color). + But because of this simplifying assumption, the implementation is + greatly simplified. */ +static grub_err_t +scale_bilinear (struct grub_video_bitmap *dst, struct grub_video_bitmap *src) +{ + /* Verify the simplifying assumptions. */ + if (dst == 0 || src == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "null bitmap in scale func"); + if (dst->mode_info.red_field_pos % 8 != 0 + || dst->mode_info.green_field_pos % 8 != 0 + || dst->mode_info.blue_field_pos % 8 != 0 + || dst->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst format not supported"); + if (src->mode_info.red_field_pos % 8 != 0 + || src->mode_info.green_field_pos % 8 != 0 + || src->mode_info.blue_field_pos % 8 != 0 + || src->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "src format not supported"); + if (dst->mode_info.red_field_pos != src->mode_info.red_field_pos + || dst->mode_info.red_mask_size != src->mode_info.red_mask_size + || dst->mode_info.green_field_pos != src->mode_info.green_field_pos + || dst->mode_info.green_mask_size != src->mode_info.green_mask_size + || dst->mode_info.blue_field_pos != src->mode_info.blue_field_pos + || dst->mode_info.blue_mask_size != src->mode_info.blue_mask_size + || dst->mode_info.reserved_field_pos != + src->mode_info.reserved_field_pos + || dst->mode_info.reserved_mask_size != + src->mode_info.reserved_mask_size) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible"); + if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible"); + if (dst->mode_info.width == 0 || dst->mode_info.height == 0 + || src->mode_info.width == 0 || src->mode_info.height == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bitmap has a zero dimension"); + + grub_uint8_t *ddata = dst->data; + grub_uint8_t *sdata = src->data; + int dw = dst->mode_info.width; + int dh = dst->mode_info.height; + int sw = src->mode_info.width; + int sh = src->mode_info.height; + int dstride = dst->mode_info.pitch; + int sstride = src->mode_info.pitch; + /* bytes_per_pixel is the same for both src and dst. */ + int bytes_per_pixel = dst->mode_info.bytes_per_pixel; + + int dy; + for (dy = 0; dy < dh; dy++) + { + int dx; + for (dx = 0; dx < dw; dx++) + { + grub_uint8_t *dptr; + grub_uint8_t *sptr; + int sx; + int sy; + int comp; + + /* Compute the source coordinate that the destination coordinate + maps to. Note: sx/sw = dx/dw => sx = sw*dx/dw. */ + sx = sw * dx / dw; + sy = sh * dy / dh; + + /* Get the address of the pixels in src and dst. */ + dptr = ddata + dy * dstride + dx * bytes_per_pixel; + sptr = sdata + sy * sstride + sx * bytes_per_pixel; + + /* If we have enough space to do so, use bilinear interpolation. + Otherwise, fall back to nearest neighbor for this pixel. */ + if (sx < sw - 1 && sy < sh - 1) + { + /* Do bilinear interpolation. */ + + /* Fixed-point .8 numbers representing the fraction of the + distance in the x (u) and y (v) direction within the + box of 4 pixels in the source. */ + int u = (256 * sw * dx / dw) - (sx * 256); + int v = (256 * sh * dy / dh) - (sy * 256); + + for (comp = 0; comp < bytes_per_pixel; comp++) + { + /* Get the component's values for the + four source corner pixels. */ + grub_uint8_t f00 = sptr[comp]; + grub_uint8_t f10 = sptr[comp + bytes_per_pixel]; + grub_uint8_t f01 = sptr[comp + sstride]; + grub_uint8_t f11 = sptr[comp + sstride + bytes_per_pixel]; + + /* Do linear interpolations along the top and bottom + rows of the box. */ + grub_uint8_t f0y = (256 - v) * f00 / 256 + v * f01 / 256; + grub_uint8_t f1y = (256 - v) * f10 / 256 + v * f11 / 256; + + /* Interpolate vertically. */ + grub_uint8_t fxy = (256 - u) * f0y / 256 + u * f1y / 256; + + dptr[comp] = fxy; + } + } + else + { + /* Fall back to nearest neighbor interpolation. */ + /* Copy the pixel color value. */ + for (comp = 0; comp < bytes_per_pixel; comp++) + dptr[comp] = sptr[comp]; + } + } + } + return GRUB_ERR_NONE; +} diff --git a/video/fb/fbblit.c b/video/fb/fbblit.c index a0f44d268..15797be97 100644 --- a/video/fb/fbblit.c +++ b/video/fb/fbblit.c @@ -1170,10 +1170,15 @@ grub_video_fbblit_blend_XXXA8888_1bit (struct grub_video_fbblit_info *dst, grub_uint8_t a; if (*srcptr & srcmask) - color = fgcolor; + { + color = fgcolor; + a = src->mode_info->fg_alpha; + } else - color = bgcolor; - a = (color >> 24) & 0xff; + { + color = bgcolor; + a = src->mode_info->bg_alpha; + } if (a == 255) *(grub_uint32_t *) dstptr = color; diff --git a/video/fb/video_fb.c b/video/fb/video_fb.c index d03a1cd7e..9c5577bb9 100644 --- a/video/fb/video_fb.c +++ b/video/fb/video_fb.c @@ -66,6 +66,8 @@ grub_video_fb_init (void) grub_err_t grub_video_fb_fini (void) { + /* TODO: destroy render targets. */ + grub_free (palette); render_target = 0; palette = 0; @@ -1004,11 +1006,13 @@ grub_video_fb_scroll (grub_video_color_t color, int dx, int dy) { \ /* 3b. Move data downwards. */ \ dst = (void *) grub_video_fb_get_video_ptr (&target, \ - dst_x + width - 1, \ + dst_x + width, \ dst_y + height - 1); \ src = (void *) grub_video_fb_get_video_ptr (&target, \ - src_x + width - 1, \ + src_x + width, \ src_y + height - 1); \ + dst--; \ + src--; \ for (j = 0; j < height; j++) \ { \ for (i = 0; i < linelen; i++) \ @@ -1233,3 +1237,53 @@ grub_video_fb_get_active_render_target (struct grub_video_fbrender_target **targ return GRUB_ERR_NONE; } + +static grub_err_t +doublebuf_blit_update_screen (struct grub_video_fbrender_target *front, + struct grub_video_fbrender_target *back) +{ + grub_memcpy (front->data, back->data, + front->mode_info.pitch * front->mode_info.height); + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_fb_doublebuf_blit_init (struct grub_video_fbrender_target **front, + struct grub_video_fbrender_target **back, + grub_video_fb_doublebuf_update_screen_t *update_screen, + struct grub_video_mode_info mode_info, + void *framebuf) +{ + grub_err_t err; + int page_size = mode_info.pitch * mode_info.height; + void *offscreen_buffer; + + err = grub_video_fb_create_render_target_from_pointer (front, &mode_info, + framebuf); + if (err) + return err; + + offscreen_buffer = grub_malloc (page_size); + if (! offscreen_buffer) + { + grub_video_fb_delete_render_target (*front); + *front = 0; + return grub_errno; + } + + err = grub_video_fb_create_render_target_from_pointer (back, &mode_info, + offscreen_buffer); + + if (err) + { + grub_video_fb_delete_render_target (*front); + grub_free (offscreen_buffer); + *front = 0; + return grub_errno; + } + (*back)->is_allocated = 1; + + *update_screen = doublebuf_blit_update_screen; + + return GRUB_ERR_NONE; +} diff --git a/video/i386/pc/vbe.c b/video/i386/pc/vbe.c index 34745b474..72b8f1831 100644 --- a/video/i386/pc/vbe.c +++ b/video/i386/pc/vbe.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -39,13 +40,25 @@ static grub_uint32_t last_set_mode = 3; static struct { struct grub_video_mode_info mode_info; - struct grub_video_render_target *render_target; + struct grub_video_render_target *front_target; + struct grub_video_render_target *back_target; unsigned int bytes_per_scan_line; unsigned int bytes_per_pixel; grub_uint32_t active_vbe_mode; grub_uint8_t *ptr; int index_color_mode; + + char *offscreen_buffer; + + grub_size_t page_size; /* The size of a page in bytes. */ + + /* For page flipping strategy. */ + int displayed_page; /* The page # that is the front buffer. */ + int render_page; /* The page # that is the back buffer. */ + + /* Virtual functions. */ + grub_video_fb_doublebuf_update_screen_t update_screen; } framebuffer; static grub_uint32_t initial_vbe_mode; @@ -350,6 +363,7 @@ static grub_err_t grub_video_vbe_fini (void) { grub_vbe_status_t status; + grub_err_t err; /* Restore old video mode. */ status = grub_vbe_bios_set_mode (initial_vbe_mode, 0); @@ -362,11 +376,190 @@ grub_video_vbe_fini (void) grub_free (vbe_mode_list); vbe_mode_list = NULL; - /* TODO: destroy render targets. */ - - return grub_video_fb_fini (); + err = grub_video_fb_fini (); + grub_free (framebuffer.offscreen_buffer); + return err; } +/* + Set framebuffer render target page and display the proper page, based on + `doublebuf_state.render_page' and `doublebuf_state.displayed_page', + respectively. +*/ +static grub_err_t +doublebuf_pageflipping_commit (void) +{ + /* Tell the video adapter to display the new front page. */ + int display_start_line + = framebuffer.mode_info.height * framebuffer.displayed_page; + + grub_vbe_status_t vbe_err = + grub_vbe_bios_set_display_start (0, display_start_line); + + if (vbe_err != GRUB_VBE_STATUS_OK) + return grub_error (GRUB_ERR_IO, "couldn't commit pageflip"); + + return 0; +} + +static grub_err_t +doublebuf_pageflipping_update_screen (struct grub_video_fbrender_target *front + __attribute__ ((unused)), + struct grub_video_fbrender_target *back + __attribute__ ((unused))) +{ + int new_displayed_page; + struct grub_video_fbrender_target *target; + grub_err_t err; + + /* Swap the page numbers in the framebuffer struct. */ + new_displayed_page = framebuffer.render_page; + framebuffer.render_page = framebuffer.displayed_page; + framebuffer.displayed_page = new_displayed_page; + + err = doublebuf_pageflipping_commit (); + if (err) + { + /* Restore previous state. */ + framebuffer.render_page = framebuffer.displayed_page; + framebuffer.displayed_page = new_displayed_page; + return err; + } + + if (framebuffer.mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP) + grub_memcpy (framebuffer.ptr + framebuffer.render_page + * framebuffer.page_size, framebuffer.ptr + + framebuffer.displayed_page * framebuffer.page_size, + framebuffer.page_size); + + target = framebuffer.back_target; + framebuffer.back_target = framebuffer.front_target; + framebuffer.front_target = target; + + err = grub_video_fb_get_active_render_target (&target); + if (err) + return err; + + if (target == framebuffer.back_target) + err = grub_video_fb_set_active_render_target (framebuffer.front_target); + else if (target == framebuffer.front_target) + err = grub_video_fb_set_active_render_target (framebuffer.back_target); + + return err; +} + +static grub_err_t +doublebuf_pageflipping_init (void) +{ + /* Get video RAM size in bytes. */ + grub_size_t vram_size = controller_info.total_memory << 16; + grub_err_t err; + + framebuffer.page_size = + framebuffer.mode_info.pitch * framebuffer.mode_info.height; + + if (2 * framebuffer.page_size > vram_size) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Not enough video memory for double buffering."); + + framebuffer.displayed_page = 0; + framebuffer.render_page = 1; + + framebuffer.update_screen = doublebuf_pageflipping_update_screen; + + err = grub_video_fb_create_render_target_from_pointer (&framebuffer.front_target, &framebuffer.mode_info, framebuffer.ptr); + if (err) + return err; + + err = grub_video_fb_create_render_target_from_pointer (&framebuffer.back_target, &framebuffer.mode_info, framebuffer.ptr + framebuffer.page_size); + if (err) + { + grub_video_fb_delete_render_target (framebuffer.front_target); + return err; + } + + /* Set the framebuffer memory data pointer and display the right page. */ + err = doublebuf_pageflipping_commit (); + if (err) + { + grub_video_fb_delete_render_target (framebuffer.front_target); + grub_video_fb_delete_render_target (framebuffer.back_target); + return err; + } + + return GRUB_ERR_NONE; +} + +/* Select the best double buffering mode available. */ +static grub_err_t +double_buffering_init (unsigned int mode_type, unsigned int mode_mask) +{ + grub_err_t err; + int updating_swap_needed; + + updating_swap_needed + = grub_video_check_mode_flag (mode_type, mode_mask, + GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP, 0); + + /* Do double buffering only if it's either requested or efficient. */ + if (grub_video_check_mode_flag (mode_type, mode_mask, + GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED, + !updating_swap_needed)) + { + framebuffer.mode_info.mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + if (updating_swap_needed) + framebuffer.mode_info.mode_type |= GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP; + err = doublebuf_pageflipping_init (); + if (!err) + return GRUB_ERR_NONE; + + framebuffer.mode_info.mode_type + &= ~(GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + + grub_errno = GRUB_ERR_NONE; + } + + if (grub_video_check_mode_flag (mode_type, mode_mask, + GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED, + 0)) + { + framebuffer.mode_info.mode_type + |= (GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + + err = grub_video_fb_doublebuf_blit_init (&framebuffer.front_target, + &framebuffer.back_target, + &framebuffer.update_screen, + framebuffer.mode_info, + framebuffer.ptr); + + if (!err) + return GRUB_ERR_NONE; + + framebuffer.mode_info.mode_type + &= ~(GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED + | GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP); + + grub_errno = GRUB_ERR_NONE; + } + + /* Fall back to no double buffering. */ + err = grub_video_fb_create_render_target_from_pointer (&framebuffer.front_target, &framebuffer.mode_info, framebuffer.ptr); + + if (err) + return err; + + framebuffer.back_target = framebuffer.front_target; + framebuffer.update_screen = 0; + + framebuffer.mode_info.mode_type &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + + return GRUB_ERR_NONE; +} + + + static grub_err_t grub_video_vbe_setup (unsigned int width, unsigned int height, unsigned int mode_type, unsigned int mode_mask) @@ -491,12 +684,12 @@ grub_video_vbe_setup (unsigned int width, unsigned int height, framebuffer.mode_info.blit_format = grub_video_get_blit_format (&framebuffer.mode_info); - err = grub_video_fb_create_render_target_from_pointer (&framebuffer.render_target, &framebuffer.mode_info, framebuffer.ptr); - + /* Set up double buffering and targets. */ + err = double_buffering_init (mode_type, mode_mask); if (err) return err; - err = grub_video_fb_set_active_render_target (framebuffer.render_target); + err = grub_video_fb_set_active_render_target (framebuffer.back_target); if (err) return err; @@ -533,7 +726,15 @@ grub_video_vbe_set_palette (unsigned int start, unsigned int count, static grub_err_t grub_video_vbe_swap_buffers (void) { - /* TODO: Implement buffer swapping. */ + grub_err_t err; + if (!framebuffer.update_screen) + return GRUB_ERR_NONE; + + err = framebuffer.update_screen (framebuffer.front_target, + framebuffer.back_target); + if (err) + return err; + return GRUB_ERR_NONE; } @@ -541,27 +742,42 @@ static grub_err_t grub_video_vbe_set_active_render_target (struct grub_video_render_target *target) { if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) - target = framebuffer.render_target; + target = framebuffer.back_target; return grub_video_fb_set_active_render_target (target); } +static grub_err_t +grub_video_vbe_get_active_render_target (struct grub_video_render_target **target) +{ + grub_err_t err; + err = grub_video_fb_get_active_render_target (target); + if (err) + return err; + + if (*target == framebuffer.back_target) + *target = GRUB_VIDEO_RENDER_TARGET_DISPLAY; + + return GRUB_ERR_NONE; +} + static grub_err_t grub_video_vbe_get_info_and_fini (struct grub_video_mode_info *mode_info, void **framebuf) { grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info)); - *framebuf = (char *) framebuffer.ptr; + *framebuf = (char *) framebuffer.ptr + + framebuffer.displayed_page * framebuffer.page_size; grub_free (vbe_mode_list); vbe_mode_list = NULL; grub_video_fb_fini (); + grub_free (framebuffer.offscreen_buffer); return GRUB_ERR_NONE; } - static struct grub_video_adapter grub_video_vbe_adapter = { .name = "VESA BIOS Extension Video Driver", @@ -588,7 +804,7 @@ static struct grub_video_adapter grub_video_vbe_adapter = .create_render_target = grub_video_fb_create_render_target, .delete_render_target = grub_video_fb_delete_render_target, .set_active_render_target = grub_video_vbe_set_active_render_target, - .get_active_render_target = grub_video_fb_get_active_render_target, + .get_active_render_target = grub_video_vbe_get_active_render_target, .next = 0 }; diff --git a/video/ieee1275.c b/video/ieee1275.c new file mode 100644 index 000000000..5c6bc1594 --- /dev/null +++ b/video/ieee1275.c @@ -0,0 +1,300 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#define grub_video_render_target grub_video_fbrender_target + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Only 8-bit indexed color is supported for now. */ + +static unsigned old_width, old_height; +static int restore_needed; +static char *display; + +static struct +{ + struct grub_video_mode_info mode_info; + struct grub_video_render_target *render_target; + grub_uint8_t *ptr; +} framebuffer; + +static grub_err_t +grub_video_ieee1275_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data); + +static void +set_video_mode (unsigned width __attribute__ ((unused)), + unsigned height __attribute__ ((unused))) +{ + /* TODO */ +} + +static void +find_display (void) +{ + auto int hook (struct grub_ieee1275_devalias *alias); + int hook (struct grub_ieee1275_devalias *alias) + { + if (grub_strcmp (alias->type, "display") == 0) + { + grub_dprintf ("video", "Found display %s\n", alias->path); + display = grub_strdup (alias->path); + return 1; + } + return 0; + } + + grub_ieee1275_devices_iterate (hook); +} + +static grub_err_t +grub_video_ieee1275_init (void) +{ + grub_memset (&framebuffer, 0, sizeof(framebuffer)); + return grub_video_fb_init (); +} + +static grub_err_t +grub_video_ieee1275_fini (void) +{ + if (restore_needed) + { + set_video_mode (old_width, old_height); + restore_needed = 0; + } + return grub_video_fb_fini (); +} + +static grub_err_t +grub_video_ieee1275_fill_mode_info (grub_ieee1275_phandle_t dev, + struct grub_video_mode_info *out) +{ + grub_uint32_t tmp; + + grub_memset (out, 0, sizeof (*out)); + + if (grub_ieee1275_get_integer_property (dev, "width", &tmp, + sizeof (tmp), 0)) + return grub_error (GRUB_ERR_IO, "Couldn't retrieve display width."); + out->width = tmp; + + if (grub_ieee1275_get_integer_property (dev, "height", &tmp, + sizeof (tmp), 0)) + return grub_error (GRUB_ERR_IO, "Couldn't retrieve display height."); + out->height = tmp; + + if (grub_ieee1275_get_integer_property (dev, "linebytes", &tmp, + sizeof (tmp), 0)) + return grub_error (GRUB_ERR_IO, "Couldn't retrieve display pitch."); + out->pitch = tmp; + + out->mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; + out->bpp = 8; + out->bytes_per_pixel = 1; + out->number_of_colors = 256; + + out->blit_format = grub_video_get_blit_format (out); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_ieee1275_setup (unsigned int width, unsigned int height, + unsigned int mode_type __attribute__ ((unused)), + unsigned int mode_mask __attribute__ ((unused))) +{ + grub_uint32_t current_width, current_height, address; + grub_err_t err; + grub_ieee1275_phandle_t dev; + + if (!display) + return grub_error (GRUB_ERR_IO, "Couldn't find display device."); + + if (grub_ieee1275_finddevice (display, &dev)) + return grub_error (GRUB_ERR_IO, "Couldn't open display device."); + + if (grub_ieee1275_get_integer_property (dev, "width", ¤t_width, + sizeof (current_width), 0)) + return grub_error (GRUB_ERR_IO, "Couldn't retrieve display width."); + + if (grub_ieee1275_get_integer_property (dev, "height", ¤t_height, + sizeof (current_width), 0)) + return grub_error (GRUB_ERR_IO, "Couldn't retrieve display height."); + + if ((width == current_width && height == current_height) + || (width == 0 && height == 0)) + { + grub_dprintf ("video", "IEEE1275: keeping current mode %dx%d\n", + current_width, current_height); + } + else + { + grub_dprintf ("video", "IEEE1275: Setting mode %dx%d\n", width, height); + /* TODO. */ + return grub_error (GRUB_ERR_IO, "can't set mode %dx%d", width, height); + } + + err = grub_video_ieee1275_fill_mode_info (dev, &framebuffer.mode_info); + if (err) + { + grub_dprintf ("video", "IEEE1275: couldn't fill mode info\n"); + return err; + } + + if (grub_ieee1275_get_integer_property (dev, "address", (void *) &address, + sizeof (address), 0)) + return grub_error (GRUB_ERR_IO, "Couldn't retrieve display address."); + + /* For some reason sparc64 uses 32-bit pointer too. */ + framebuffer.ptr = (void *) (grub_addr_t) address; + + grub_video_ieee1275_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); + + grub_dprintf ("video", "IEEE1275: initialising FB @ %p %dx%dx%d\n", + framebuffer.ptr, framebuffer.mode_info.width, + framebuffer.mode_info.height, framebuffer.mode_info.bpp); + + err = grub_video_fb_create_render_target_from_pointer + (&framebuffer.render_target, &framebuffer.mode_info, framebuffer.ptr); + + if (err) + { + grub_dprintf ("video", "IEEE1275: Couldn't create FB target\n"); + return err; + } + + err = grub_video_fb_set_active_render_target (framebuffer.render_target); + + if (err) + { + grub_dprintf ("video", "IEEE1275: Couldn't set FB target\n"); + return err; + } + + err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS, + grub_video_fbstd_colors); + + if (err) + grub_dprintf ("video", "IEEE1275: Couldn't set palette\n"); + else + grub_dprintf ("video", "IEEE1275: Success\n"); + + return err; +} + +static grub_err_t +grub_video_ieee1275_swap_buffers (void) +{ + /* TODO: Implement buffer swapping. */ + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_ieee1275_set_active_render_target (struct grub_video_render_target *target) +{ + if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) + target = framebuffer.render_target; + + return grub_video_fb_set_active_render_target (target); +} + +static grub_err_t +grub_video_ieee1275_get_info_and_fini (struct grub_video_mode_info *mode_info, + void **framebuf) +{ + grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info)); + *framebuf = (char *) framebuffer.ptr; + + grub_video_fb_fini (); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_video_ieee1275_set_palette (unsigned int start, unsigned int count, + struct grub_video_palette_data *palette_data) +{ + grub_err_t err; + struct grub_video_palette_data fb_palette_data[256]; + + err = grub_video_fb_set_palette (start, count, palette_data); + if (err) + return err; + + grub_video_fb_get_palette (0, 256, fb_palette_data); + + /* TODO. */ + + return GRUB_ERR_NONE; +} + +static struct grub_video_adapter grub_video_ieee1275_adapter = + { + .name = "IEEE1275 video driver", + + .init = grub_video_ieee1275_init, + .fini = grub_video_ieee1275_fini, + .setup = grub_video_ieee1275_setup, + .get_info = grub_video_fb_get_info, + .get_info_and_fini = grub_video_ieee1275_get_info_and_fini, + .set_palette = grub_video_ieee1275_set_palette, + .get_palette = grub_video_fb_get_palette, + .set_viewport = grub_video_fb_set_viewport, + .get_viewport = grub_video_fb_get_viewport, + .map_color = grub_video_fb_map_color, + .map_rgb = grub_video_fb_map_rgb, + .map_rgba = grub_video_fb_map_rgba, + .unmap_color = grub_video_fb_unmap_color, + .fill_rect = grub_video_fb_fill_rect, + .blit_bitmap = grub_video_fb_blit_bitmap, + .blit_render_target = grub_video_fb_blit_render_target, + .scroll = grub_video_fb_scroll, + .swap_buffers = grub_video_ieee1275_swap_buffers, + .create_render_target = grub_video_fb_create_render_target, + .delete_render_target = grub_video_fb_delete_render_target, + .set_active_render_target = grub_video_ieee1275_set_active_render_target, + .get_active_render_target = grub_video_fb_get_active_render_target, + + .next = 0 + }; + +GRUB_MOD_INIT(ieee1275_fb) +{ + find_display (); + if (display) + grub_video_register (&grub_video_ieee1275_adapter); +} + +GRUB_MOD_FINI(ieee1275_fb) +{ + if (restore_needed) + { + set_video_mode (old_width, old_height); + restore_needed = 0; + } + if (display) + grub_video_unregister (&grub_video_ieee1275_adapter); + grub_free (display); +} diff --git a/video/readers/jpeg.c b/video/readers/jpeg.c index 3c3ac33bb..5e749b8fd 100644 --- a/video/readers/jpeg.c +++ b/video/readers/jpeg.c @@ -729,7 +729,7 @@ static struct grub_video_bitmap_reader jpeg_reader = { .next = 0 }; -GRUB_MOD_INIT (video_reader_jpeg) +GRUB_MOD_INIT (jpeg) { grub_video_bitmap_reader_register (&jpg_reader); grub_video_bitmap_reader_register (&jpeg_reader); @@ -740,7 +740,7 @@ GRUB_MOD_INIT (video_reader_jpeg) #endif } -GRUB_MOD_FINI (video_reader_jpeg) +GRUB_MOD_FINI (jpeg) { #if defined(JPEG_DEBUG) grub_unregister_command (cmd); diff --git a/video/readers/png.c b/video/readers/png.c index 8eec421dd..2cec49e2f 100644 --- a/video/readers/png.c +++ b/video/readers/png.c @@ -894,7 +894,7 @@ static struct grub_video_bitmap_reader png_reader = { .next = 0 }; -GRUB_MOD_INIT (video_reader_png) +GRUB_MOD_INIT (png) { grub_video_bitmap_reader_register (&png_reader); #if defined(PNG_DEBUG) @@ -904,7 +904,7 @@ GRUB_MOD_INIT (video_reader_png) #endif } -GRUB_MOD_FINI (video_reader_png) +GRUB_MOD_FINI (png) { #if defined(PNG_DEBUG) grub_unregister_command (cmd); diff --git a/video/readers/tga.c b/video/readers/tga.c index d720141e1..6c9e9d691 100644 --- a/video/readers/tga.c +++ b/video/readers/tga.c @@ -477,7 +477,7 @@ static struct grub_video_bitmap_reader tga_reader = { .next = 0 }; -GRUB_MOD_INIT(video_reader_tga) +GRUB_MOD_INIT(tga) { grub_video_bitmap_reader_register (&tga_reader); #if defined(TGA_DEBUG) @@ -486,7 +486,7 @@ GRUB_MOD_INIT(video_reader_tga) #endif } -GRUB_MOD_FINI(video_reader_tga) +GRUB_MOD_FINI(tga) { #if defined(TGA_DEBUG) grub_unregister_command (cmd); diff --git a/video/sm712.c b/video/sm712.c index a86470b7d..33861beef 100644 --- a/video/sm712.c +++ b/video/sm712.c @@ -191,6 +191,7 @@ grub_video_sm712_get_info_and_fini (struct grub_video_mode_info *mode_info, static struct grub_video_adapter grub_video_sm712_adapter = { .name = "SM712 Video Driver", + .id = GRUB_VIDEO_DRIVER_SM712, .init = grub_video_sm712_video_init, .fini = grub_video_sm712_video_fini, diff --git a/video/video.c b/video/video.c index f6b1aad9e..42418f980 100644 --- a/video/video.c +++ b/video/video.c @@ -696,11 +696,11 @@ grub_video_set_mode (const char *modestring, } /* Initialize Video API module. */ -GRUB_MOD_INIT(video_video) +GRUB_MOD_INIT(video) { } /* Finalize Video API module. */ -GRUB_MOD_FINI(video_video) +GRUB_MOD_FINI(video) { }