diff --git a/ChangeLog b/ChangeLog index 843be6dc5..6d7c7fe42 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,241 @@ +2009-11-13 Robert Millan + + Disable Multiboot2 in i386-ieee1275. It didn't actually work, and on + this platform we should support Multiboot1 first. + + * conf/i386-ieee1275.rmk (pkglib_MODULES): Remove `multiboot.mod'. + (multiboot_mod_SOURCES, multiboot_mod_CFLAGS) + (multiboot_mod_LDFLAGS, multiboot_mod_ASFLAGS): Remove. + +2009-11-12 Robert Millan + + * util/mkisofs/eltorito.c (init_boot_catalog): Handle return code + of write calls (converting them to fwrite() if they aren't already). + (get_torito_desc): Likewise. + * util/mkisofs/rock.c (generate_rock_ridge_attributes): Likewise. + +2009-11-12 Robert Millan + + * util/i386/pc/grub-install.in: Move from here ... + * util/grub-install.in: ... to here. Update all users. + +2009-11-11 Colin Watson + + * util/powerpc/ieee1275/grub-mkrescue.in: Fix --version output. + +2009-11-11 Robert Millan + + Support for El Torito without floppy emulation. + + * util/mkisofs/eltorito.c: Include `'. + (init_boot_catalog): Improve error handling. + (get_torito_desc): Don't use floppy emulation unless requested by + user. Patch boot information table when requested via + `-boot-info-table'. + * util/mkisofs/iso9660.h (struct eltorito_boot_info): New struct. + * util/mkisofs/mkisofs.c (use_eltorito_emul_floppy) + (use_boot_info_table): New variables. + (OPTION_BOOT_INFO_TABLE, OPTION_NO_EMUL_BOOT) + (OPTION_ELTORITO_EMUL_FLOPPY): New macros. + (ld_options): Handle `-boot-info-table', `-no-emul-boot' and + `--eltorito-emul-floppy'. + (main): Handle `OPTION_BOOT_INFO_TABLE', `OPTION_NO_EMUL_BOOT' + and `OPTION_ELTORITO_EMUL_FLOPPY'. + * util/mkisofs/mkisofs.h (use_eltorito_emul_floppy) + (use_boot_info_table, get_731): New prototypes. + * util/mkisofs/write.c (get_731): New function. + +2009-11-11 Felix Zielcke + + Fix the generation of the man page. + + * util/pc/i386/grub-install.in: Source + `${libdir}/grub/grub-mkconfig_lib' after options have been parsed. + +2009-11-11 Robert Millan + + Large file support for grub-mkisofs. + + * conf/common.rmk (grub_mkisofs_CFLAGS): Add `-D_FILE_OFFSET_BITS=64'. + * util/mkisofs/mkisofs.c (next_extent, last_extent) + (session_start): Upgrade type to `uint64_t'. Update all users. + * util/mkisofs/mkisofs.h: Include `'. + (struct directory_entry): Upgrade type of `starting_block' and + `size' to `uint64_t'. Update all users. + (struct deferred): Remove unused structure. + (xfwrite): Upgrade type of `count' and `size' to `uint64_t'. + Update all users. + * util/mkisofs/tree.c (stat_filter, lstat_filter): Return -1 when + file is larger than `UINT32_MAX'. + * util/mkisofs/write.c (xfwrite): Upgrade type of `count' and + `size' to `uint64_t'. Update all users. Fix handling of fwrite() + return value. + (struct deferred_write): Upgrade type of `extent' and `size' to + `uint64_t'. Update all users. + (last_extent_written): Upgrade type to `uint64_t'. Update all + users. + (write_one_file): Upgrade type of `count' and `size' to `uint64_t'. + Update all users. Upgrade type of `remain' to `int64_t' and + `use' to `size_t'. Use error() to handle fread() errors. + (write_files): Rely on write_one_file() rather than calling + xfwrite() directly. + +2009-11-09 Felix Zielcke + + * util/mkisofs/mkisofs.c (ld_options): Fix a spelling mistake. + +2009-11-09 Robert Millan + + * util/mkisofs/fnmatch.c: Remove. + * util/mkisofs/getopt1.c: Likewise. + * util/mkisofs/getopt.c: Likewise. + * conf/common.rmk (grub_mkisofs_SOURCES): Replace + `util/mkisofs/fnmatch.c', `util/mkisofs/getopt1.c' and + `util/mkisofs/getopt.c' with `gnulib/fnmatch.c', + `gnulib/getopt1.c' and `gnulib/getopt.c'. + (grub_mkisofs_CFLAGS): Add `-I$(srcdir)/gnulib'. + + * configure.ac: Detect `mingw32msvc' host_os. + Check for lstat(), getuid() and getgid(). + + * util/mkisofs/joliet.c: Include `'. Replace all + instances of `u_char' with `uint8_t'. + + * util/mkisofs/mkisofs.h: Include `'. + [!HAVE_GETUID] (getuid): New function (stub). + [!HAVE_GETGID] (getgid): Likewise. + [!HAVE_LSTAT] (lstat): Likewise. + [!S_IROTH] (S_IROTH): New macro (dummy). + [!S_IRGRP] (S_IRGRP): Likewise. + +2009-11-09 Robert Millan + + * gnulib/fnmatch_loop.c (EXT): Fix warning (signed and unsigned type in + conditional expression). + +2009-11-09 Robert Millan + + Import from Gnulib. + + * gnulib/fnmatch.c: New file. + * gnulib/fnmatch.h: Likewise. + * gnulib/fnmatch_loop.c: Likewise. + * gnulib/getopt.c: Likewise. + * gnulib/getopt.h: Likewise. + * gnulib/getopt1.c: Likewise. + * gnulib/getopt_int.h: Likewise. + * gnulib/gettext.h: Likewise. + +2009-11-09 Robert Millan + + * normal/dyncmd.c (read_command_list): Replace `0' with `NULL'. + * normal/handler.c (read_handler_list): Likewise. + +2009-11-09 Robert Millan + + Misc cleanup. + + * kern/command.c (grub_register_command_prio): Use + grub_zalloc() instead of explicitly zeroing data. + * kern/list.c: Include `'. + (grub_named_list_find): Replace `0' with `NULL'. + * normal/autofs.c (struct grub_fs_module_list): Remove ad-hoc type. + (fs_module_list): Change type to `grub_named_list_t'. Update all + users. + * normal/dyncmd.c (read_command_list): Add space between function + call and parenthesis. + * normal/handler.c (read_handler_list): Likewise. + +2009-11-09 Robert Millan + + * normal/auth.c (punishment_delay): Moved from here ... + (grub_auth_strcmp): ... to here (inside function). + +2009-11-09 Robert Millan + + * include/grub/list.h (struct grub_named_list): Remove `const' + qualifier from `name'. + (struct grub_prio_list): Likewise. + +2009-11-09 Robert Millan + + * normal/auth.c: Include `'. + (grub_auth_strcmp): Replace `strcmp' with `grub_strcmp'. + +2009-11-09 Robert Millan + + * normal/auth.c (punishment_delay): New variable. + (grub_auth_strcmp): Rewrite using grub_get_time_ms (). + (grub_auth_check_authentication): Punish failed login attempts with + an incremental (2^N) delay. + +2009-11-09 Robert Millan + + * conf/common.rmk (grub_mkisofs_CFLAGS): Prefix include + path with $(srcdir). + +2009-11-09 Vladimir Serbinenko + + * normal/auth.c (grub_auth_strcmp): Fixed incorrect variable usage. + +2009-11-09 Robert Millan + + * util/i386/coreboot/grub-mkrescue.in: New file. + * conf/i386-coreboot.rmk (bin_SCRIPTS, grub_mkrescue_SOURCES): New + variables. + + * conf/common.rmk (bin_UTILITIES): Add `grub-mkisofs'. + (grub_mkisofs_SOURCES, grub_mkisofs_CFLAGS): New variables. + * configure.ac: Add header and function checks to satisfy grub-mkisofs + requirements. + * util/mkisofs/defaults.h: New file. + * util/mkisofs/eltorito.c: Likewise. + * util/mkisofs/exclude.h: Likewise. + * util/mkisofs/fnmatch.c: Likewise. + * util/mkisofs/getopt.c: Likewise. + * util/mkisofs/getopt1.c: Likewise. + * util/mkisofs/hash.c: Likewise. + * util/mkisofs/include/fctldefs.h: Likewise. + * util/mkisofs/include/mconfig.h: Likewise. + * util/mkisofs/include/prototyp.h: Likewise. + * util/mkisofs/include/statdefs.h: Likewise. + * util/mkisofs/iso9660.h: Likewise. + * util/mkisofs/joliet.c: Likewise. + * util/mkisofs/match.c: Likewise. + * util/mkisofs/match.h: Likewise. + * util/mkisofs/mkisofs.c: Likewise. + * util/mkisofs/mkisofs.h: Likewise. + * util/mkisofs/multi.c: Likewise. + * util/mkisofs/name.c: Likewise. + * util/mkisofs/rock.c: Likewise. + * util/mkisofs/tree.c: Likewise. + * util/mkisofs/write.c: Likewise. + +2009-11-09 Vladimir Serbinenko + + * normal/auth.c (grub_auth_strcmp): Fix bug which resulted in function + being insecure. + +2009-11-08 Robert Millan + + * util/i386/pc/grub-mkrescue.in: Fix miss-identification as + `grub-mkimage' (and use $0 when possible). + +2009-11-08 Robert Millan + + * kern/i386/multiboot_mmap.c (grub_machine_mmap_init): Improve + error message for excessively large memory map. + +2009-11-08 Robert Millan + + * autogen.sh: Use `sh gendistlist.sh' to avoid reliing on + executable bit. + +2009-11-08 Robert Millan + + * kern/i386/multiboot_mmap.c (grub_machine_mmap_init): Improve error + message for coreboot users. + 2009-11-07 Robert Millan Fix build with GNU gold. diff --git a/autogen.sh b/autogen.sh index 4fb6bf7db..6751c8adc 100755 --- a/autogen.sh +++ b/autogen.sh @@ -10,6 +10,6 @@ for rmk in conf/*.rmk ${GRUB_CONTRIB}/*/conf/*.rmk; do ruby genmk.rb < $rmk > `echo $rmk | sed 's/\.rmk$/.mk/'` fi done -./gendistlist.sh > DISTLIST +sh gendistlist.sh > DISTLIST exit 0 diff --git a/conf/common.rmk b/conf/common.rmk index c1f0bbdcf..a66bd97fd 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -28,6 +28,19 @@ ifeq ($(enable_grub_fstest), yes) bin_UTILITIES += grub-fstest endif +bin_UTILITIES += grub-mkisofs +grub_mkisofs_SOURCES = util/mkisofs/eltorito.c \ + util/mkisofs/hash.c util/mkisofs/joliet.c \ + util/mkisofs/match.c util/mkisofs/mkisofs.c \ + util/mkisofs/multi.c util/mkisofs/name.c \ + util/mkisofs/rock.c util/mkisofs/tree.c \ + util/mkisofs/write.c \ + \ + gnulib/fnmatch.c gnulib/getopt1.c gnulib/getopt.c +grub_mkisofs_CFLAGS = -D_FILE_OFFSET_BITS=64 \ + -I$(srcdir)/util/mkisofs/include -I$(srcdir)/gnulib \ + -Wno-all -Werror + # For grub-fstest. util/grub-fstest.c_DEPENDENCIES = grub_fstest_init.h grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ diff --git a/conf/i386-coreboot.rmk b/conf/i386-coreboot.rmk index 09ec7787c..b65833776 100644 --- a/conf/i386-coreboot.rmk +++ b/conf/i386-coreboot.rmk @@ -150,7 +150,10 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ grub_emu_LDFLAGS = $(LIBCURSES) sbin_SCRIPTS += grub-install -grub_install_SOURCES = util/i386/pc/grub-install.in +grub_install_SOURCES = util/grub-install.in + +bin_SCRIPTS += grub-mkrescue +grub_mkrescue_SOURCES = util/i386/coreboot/grub-mkrescue.in # Modules. pkglib_MODULES = linux.mod multiboot.mod \ diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk index 4b640de49..1eeba3af5 100644 --- a/conf/i386-ieee1275.rmk +++ b/conf/i386-ieee1275.rmk @@ -110,7 +110,7 @@ grub_install_SOURCES = util/ieee1275/grub-install.in # Modules. pkglib_MODULES = halt.mod reboot.mod suspend.mod \ - multiboot.mod aout.mod serial.mod linux.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 @@ -126,15 +126,6 @@ mmap_mod_CFLAGS = $(COMMON_CFLAGS) mmap_mod_LDFLAGS = $(COMMON_LDFLAGS) mmap_mod_ASFLAGS = $(COMMON_ASFLAGS) -# For multiboot.mod. -multiboot_mod_SOURCES = loader/ieee1275/multiboot2.c \ - loader/i386/multiboot_helper.S \ - loader/multiboot2.c \ - loader/multiboot_loader.c -multiboot_mod_CFLAGS = $(COMMON_CFLAGS) -multiboot_mod_LDFLAGS = $(COMMON_LDFLAGS) -multiboot_mod_ASFLAGS = $(COMMON_ASFLAGS) - # For aout.mod. aout_mod_SOURCES = loader/aout.c aout_mod_CFLAGS = $(COMMON_CFLAGS) diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 373b942cc..74b88f0df 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -173,7 +173,7 @@ sbin_SCRIPTS = grub-install bin_SCRIPTS = grub-mkrescue # For grub-install. -grub_install_SOURCES = util/i386/pc/grub-install.in +grub_install_SOURCES = util/grub-install.in # For grub-mkrescue. grub_mkrescue_SOURCES = util/i386/pc/grub-mkrescue.in diff --git a/configure.ac b/configure.ac index 0953e0410..6287c4f59 100644 --- a/configure.ac +++ b/configure.ac @@ -98,7 +98,7 @@ case "$target_cpu" in esac case "$host_os" in - mingw32) host_os=cygwin ;; + mingw32*) host_os=cygwin ;; esac # This normalizes the names, and creates a new variable ("host_kernel") @@ -181,6 +181,13 @@ fi # Check for functions. AC_CHECK_FUNCS(posix_memalign memalign asprintf) +# For grub-mkisofs +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) + # # Check for target programs. # diff --git a/gnulib/alloca.h b/gnulib/alloca.h new file mode 100644 index 000000000..5d16e08b7 --- /dev/null +++ b/gnulib/alloca.h @@ -0,0 +1,56 @@ +/* Memory allocation on the stack. + + Copyright (C) 1995, 1999, 2001-2004, 2006-2008 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 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. */ + +/* Avoid using the symbol _ALLOCA_H here, as Bison assumes _ALLOCA_H + means there is a real alloca function. */ +#ifndef _GL_ALLOCA_H +#define _GL_ALLOCA_H + +/* alloca (N) returns a pointer to N bytes of memory + allocated on the stack, which will last until the function returns. + Use of alloca should be avoided: + - inside arguments of function calls - undefined behaviour, + - in inline functions - the allocation may actually last until the + calling function returns, + - for huge N (say, N >= 65536) - you never know how large (or small) + the stack is, and when the stack cannot fulfill the memory allocation + request, the program just crashes. + */ + +#ifndef alloca +# ifdef __GNUC__ +# define alloca __builtin_alloca +# elif defined _AIX +# define alloca __alloca +# elif defined _MSC_VER +# include +# define alloca _alloca +# elif defined __DECC && defined __VMS +# define alloca __ALLOCA +# else +# include +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +# endif +#endif + +#endif /* _GL_ALLOCA_H */ diff --git a/gnulib/fnmatch.c b/gnulib/fnmatch.c new file mode 100644 index 000000000..48bc8b5d2 --- /dev/null +++ b/gnulib/fnmatch.c @@ -0,0 +1,354 @@ +/* Copyright (C) 1991,1992,1993,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007 + 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 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 _LIBC +# include +#endif + +/* Enable GNU extensions in fnmatch.h. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#if ! defined __builtin_expect && __GNUC__ < 3 +# define __builtin_expect(expr, expected) (expr) +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define WIDE_CHAR_SUPPORT \ + (HAVE_WCTYPE_H && HAVE_BTOWC && HAVE_ISWCTYPE \ + && HAVE_WMEMCHR && (HAVE_WMEMCPY || HAVE_WMEMPCPY)) + +/* For platform which support the ISO C amendement 1 functionality we + support user defined character classes. */ +#if defined _LIBC || WIDE_CHAR_SUPPORT +# include +# include +#endif + +/* We need some of the locale data (the collation sequence information) + but there is no interface to get this information in general. Therefore + we support a correct implementation only in glibc. */ +#ifdef _LIBC +# include "../locale/localeinfo.h" +# include "../locale/elem-hash.h" +# include "../locale/coll-lookup.h" +# include + +# define CONCAT(a,b) __CONCAT(a,b) +# define mbsrtowcs __mbsrtowcs +# define fnmatch __fnmatch +extern int fnmatch (const char *pattern, const char *string, int flags); +#endif + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif + +/* We often have to test for FNM_FILE_NAME and FNM_PERIOD being both set. */ +#define NO_LEADING_PERIOD(flags) \ + ((flags & (FNM_FILE_NAME | FNM_PERIOD)) == (FNM_FILE_NAME | FNM_PERIOD)) + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself, and have not detected a bug + in the library. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined _LIBC || !defined __GNU_LIBRARY__ || !HAVE_FNMATCH_GNU + + +# if ! (defined isblank || (HAVE_ISBLANK && HAVE_DECL_ISBLANK)) +# define isblank(c) ((c) == ' ' || (c) == '\t') +# endif + +# 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 + and the functions from ISO C amendement 1. */ +# ifdef CHARCLASS_NAME_MAX +# define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX +# else +/* This shouldn't happen but some implementation might still have this + problem. Use a reasonable default value. */ +# define CHAR_CLASS_MAX_LENGTH 256 +# endif + +# ifdef _LIBC +# define IS_CHAR_CLASS(string) __wctype (string) +# else +# define IS_CHAR_CLASS(string) wctype (string) +# endif + +# ifdef _LIBC +# define ISWCTYPE(WC, WT) __iswctype (WC, WT) +# else +# 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 +# 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") \ + || STREQ (string, "cntrl") || STREQ (string, "blank")) +# endif + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +/* Global variable. */ +static int posixly_correct; + +# ifndef internal_function +/* Inside GNU libc we mark some function in a special way. In other + environments simply ignore the marking. */ +# define internal_function +# endif + +/* 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 +# ifdef _LIBC +# define BTOWC(C) __btowc (C) +# else +# define BTOWC(C) btowc (C) +# endif +# define STRLEN(S) strlen (S) +# define STRCAT(D, S) strcat (D, S) +# ifdef _LIBC +# define MEMPCPY(D, S, N) __mempcpy (D, S, N) +# else +# if HAVE_MEMPCPY +# define MEMPCPY(D, S, N) mempcpy (D, S, N) +# else +# define MEMPCPY(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N))) +# endif +# endif +# define MEMCHR(S, C, N) memchr (S, C, N) +# define STRCOLL(S1, S2) strcoll (S1, S2) +# include "fnmatch_loop.c" + + +# 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) +# ifdef _LIBC +# define STRLEN(S) __wcslen (S) +# define STRCAT(D, S) __wcscat (D, S) +# define MEMPCPY(D, S, N) __wmempcpy (D, S, N) +# else +# define STRLEN(S) wcslen (S) +# define STRCAT(D, S) wcscat (D, S) +# if HAVE_WMEMPCPY +# define MEMPCPY(D, S, N) wmempcpy (D, S, N) +# else +# define MEMPCPY(D, S, N) (wmemcpy (D, S, N) + (N)) +# endif +# endif +# define MEMCHR(S, C, N) wmemchr (S, C, N) +# define STRCOLL(S1, S2) wcscoll (S1, S2) +# define WIDE_CHAR_VERSION 1 + +# undef IS_CHAR_CLASS +/* We have to convert the wide character string in a multibyte string. But + we know that the character class names consist of alphanumeric characters + from the portable character set, and since the wide character encoding + for a member of the portable character set is the same code point as + its single-byte encoding, we can use a simplified method to convert the + string to a multibyte character string. */ +static wctype_t +is_char_class (const wchar_t *wcs) +{ + char s[CHAR_CLASS_MAX_LENGTH + 1]; + char *cp = s; + + do + { + /* 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; +# 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; + } +# endif + + /* Avoid overrunning the buffer. */ + if (cp == s + CHAR_CLASS_MAX_LENGTH) + return (wctype_t) 0; + + *cp++ = (char) *wcs++; + } + while (*wcs != L'\0'); + + *cp = '\0'; + +# ifdef _LIBC + return __wctype (s); +# else + return wctype (s); +# endif +} +# define IS_CHAR_CLASS(string) is_char_class (string) + +# include "fnmatch_loop.c" +# endif + + +int +fnmatch (const char *pattern, const char *string, int flags) +{ +# if HANDLE_MULTIBYTE +# define ALLOCA_LIMIT 2000 + if (__builtin_expect (MB_CUR_MAX, 1) != 1) + { + mbstate_t ps; + size_t patsize; + size_t strsize; + size_t totsize; + wchar_t *wpattern; + wchar_t *wstring; + int res; + + /* Calculate the size needed to convert the strings to + 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; + } + + /* 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); + + res = internal_fnwmatch (wpattern, wstring, wstring + strsize - 1, + flags & FNM_PERIOD, flags); + + 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); +} + +# ifdef _LIBC +# undef fnmatch +versioned_symbol (libc, __fnmatch, fnmatch, GLIBC_2_2_3); +# if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_2_3) +strong_alias (__fnmatch, __fnmatch_old) +compat_symbol (libc, __fnmatch_old, fnmatch, GLIBC_2_0); +# endif +libc_hidden_ver (__fnmatch, fnmatch) +# endif + +#endif /* _LIBC or not __GNU_LIBRARY__. */ diff --git a/gnulib/fnmatch.h b/gnulib/fnmatch.h new file mode 100644 index 000000000..b086b45aa --- /dev/null +++ b/gnulib/fnmatch.h @@ -0,0 +1,65 @@ +/* Copyright (C) 1991, 1992, 1993, 1996, 1997, 1998, 1999, 2001, 2002, 2003, + 2005, 2007 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 _FNMATCH_H +#define _FNMATCH_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* We #undef these before defining them because some losing systems + (HP-UX A.08.07 for example) define these in . */ +#undef FNM_PATHNAME +#undef FNM_NOESCAPE +#undef FNM_PERIOD + +/* Bits set in the FLAGS argument to `fnmatch'. */ +#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */ +#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */ +#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */ + +#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _GNU_SOURCE +# define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */ +# define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */ +# define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */ +# define FNM_EXTMATCH (1 << 5) /* Use ksh-like extended matching. */ +#endif + +/* Value returned by `fnmatch' if STRING does not match PATTERN. */ +#define FNM_NOMATCH 1 + +/* This value is returned if the implementation does not support + `fnmatch'. Since this is not the case here it will never be + returned but the conformance test suites still require the symbol + to be defined. */ +#ifdef _XOPEN_SOURCE +# define FNM_NOSYS (-1) +#endif + +/* Match NAME against the file name pattern PATTERN, + returning zero if it matches, FNM_NOMATCH if not. */ +extern int fnmatch (const char *__pattern, const char *__name, + int __flags); + +#ifdef __cplusplus +} +#endif + +#endif /* fnmatch.h */ diff --git a/gnulib/fnmatch_loop.c b/gnulib/fnmatch_loop.c new file mode 100644 index 000000000..c2182ffb0 --- /dev/null +++ b/gnulib/fnmatch_loop.c @@ -0,0 +1,1211 @@ +/* Copyright (C) 1991,1992,1993,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006 + 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. */ + +/* 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) + internal_function; +static const CHAR *END (const CHAR *patternp) internal_function; + +static int +internal_function +FCT (const CHAR *pattern, const CHAR *string, const CHAR *string_end, + bool no_leading_period, int flags) +{ + register const CHAR *p = pattern, *n = string; + register UCHAR c; +#ifdef _LIBC +# if WIDE_CHAR_VERSION + const char *collseq = (const char *) + _NL_CURRENT(LC_COLLATE, _NL_COLLATE_COLLSEQWC); +# else + const UCHAR *collseq = (const UCHAR *) + _NL_CURRENT(LC_COLLATE, _NL_COLLATE_COLLSEQMB); +# endif +#endif + + while ((c = *p++) != L_('\0')) + { + bool new_no_leading_period = false; + c = FOLD (c); + + switch (c) + { + 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; + } + + 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 (__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; + } + + 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; + } + } + + 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 (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; + + 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; + + 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 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; + + if (posixly_correct == 0) + posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1; + + if (n == string_end) + 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; + + not = (*p == L_('!') || (posixly_correct < 0 && *p == L_('^'))); + if (not) + ++p; + + 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; + + 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; +#endif + 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; + + 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; + +# 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; +# else + 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; +#endif + 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; + + 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; + + 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; +# else + const unsigned char *weights; + const unsigned char *extra; +# endif + const int32_t *indirect; + int32_t idx; + const UCHAR *cp = (const UCHAR *) str; + + /* This #include defines a local function! */ +# if WIDE_CHAR_VERSION +# include +# else +# include +# 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); +# 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); +# 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; + + idx2 = findidx (&np); + if (idx2 != 0 && len == weights[idx2]) + { + int cnt = 0; + + while (cnt < len + && (weights[idx + 1 + cnt] + == weights[idx2 + 1 + cnt])) + ++cnt; + + if (cnt == len) + goto matched; + } + } + } + + c = *p++; + } +#endif + else if (c == L_('\0')) + /* [ (unterminated) loses. */ + return FNM_NOMATCH; + else + { + bool is_range = false; + +#ifdef _LIBC + 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; + + 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'); + + 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; + + 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; +# else +# define str (startp + 1) +# endif + 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]; +# 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); + + /* 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; + + 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); + } + + 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; + + idx += 1 + extra[idx]; + /* Adjust for the alignment. */ + idx = (idx + 3) & ~3; + + wextra = (int32_t *) &extra[idx + 4]; +# endif + + if (! is_range) + { +# ifdef WIDE_CHAR_VERSION + for (c1 = 0; + (int32_t) c1 < wextra[idx]; + ++c1) + if (n[c1] != wextra[1 + c1]) + break; + + if ((int32_t) c1 == wextra[idx]) + goto matched; +# else + for (c1 = 0; c1 < extra[idx]; ++c1) + if (n[c1] != extra[1 + c1]) + break; + + if (c1 == extra[idx]) + goto matched; +# endif + } + + /* Get the collation sequence value. */ + is_seqval = true; +# ifdef WIDE_CHAR_VERSION + cold = wextra[1 + wextra[idx]]; +# else + /* 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; + + cold = str[0]; + c = *p++; + } + else + return FNM_NOMATCH; + } + } + else +# undef str +#endif + { + 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_(']')); + + 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; +#endif + + cold = c; + c = *p++; + } + + 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++; + +# 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; + + if (is_seqval) + lcollseq = cold; + else + lcollseq = __collseq_table_lookup (collseq, cold); +# else + 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; + + 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; + + cend = startp[1]; + } + else + { + int32_t table_size; + const int32_t *symb_table; +# ifdef WIDE_CHAR_VERSION + 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; + +# 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]; +# 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); + + /* 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; + + 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); + } + + 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; + + idx += 1 + extra[idx]; + /* Adjust for the alignment. */ + idx = (idx + 3) & ~4; + + wextra = (int32_t *) &extra[idx + 4]; +# endif + /* Get the collation sequence value. */ + is_seqval = true; +# ifdef WIDE_CHAR_VERSION + cend = wextra[1 + wextra[idx]]; +# else + /* 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; + } +# undef str + } + 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 ( +# ifdef WIDE_CHAR_VERSION + lcollseq == 0xffffffff || +# endif + lcollseq <= fcollseq) + { + /* We have to look at the upper bound. */ + uint32_t hcollseq; + + 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; + + goto matched; + } +# else + hcollseq = collseq[cend]; +# endif + } + + if (lcollseq <= hcollseq && fcollseq <= hcollseq) + goto matched; + } +# ifdef WIDE_CHAR_VERSION + 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++; + + 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; +#endif + + c = *p++; + } + } + + if (c == L_(']')) + break; + } + + if (!not) + return FNM_NOMATCH; + break; + + matched: + /* Skip the rest of the [...] that already matched. */ + do + { + ignore_next: + c = *p++; + + 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; + + while (1) + { + c = *++p; + if (++c1 == CHAR_CLASS_MAX_LENGTH) + return FNM_NOMATCH; + + 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 (*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; + + 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; + + 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; + } + + if (n == string_end) + return 0; + + if ((flags & FNM_LEADING_DIR) && n != string_end && *n == L_('/')) + /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */ + return 0; + + return FNM_NOMATCH; +} + + +static const CHAR * +internal_function +END (const CHAR *pattern) +{ + const CHAR *p = pattern; + + while (1) + if (*++p == L_('\0')) + /* This is an invalid pattern. */ + return pattern; + else if (*p == L_('[')) + { + /* 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; + } + else if ((*p == L_('?') || *p == L_('*') || *p == L_('+') || *p == L_('@') + || *p == L_('!')) && p[1] == L_('(')) + p = END (p + 1); + else if (*p == L_(')')) + break; + + return p + 1; +} + + +static int +internal_function +EXT (INT opt, const CHAR *pattern, const CHAR *string, const CHAR *string_end, + bool no_leading_period, int flags) +{ + const CHAR *startp; + size_t level; + struct patternlist + { + struct patternlist *next; + CHAR str[1]; + } *list = NULL; + struct patternlist **lastp = &list; + size_t pattern_len = STRLEN (pattern); + const CHAR *p; + const CHAR *rs; + enum { ALLOCA_LIMIT = 8000 }; + + /* Parse the pattern. Store the individual parts in the list. */ + level = 0; + for (startp = p = pattern + 1; ; ++p) + if (*p == L_('\0')) + /* This is an invalid pattern. */ + return -1; + else if (*p == L_('[')) + { + /* 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; + } + else if ((*p == L_('?') || *p == L_('*') || *p == L_('+') || *p == 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. */ +#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; + } + } + else if (*p == L_('|')) + { + if (level == 0) + { + NEW_PATTERN; + startp = p + 1; + } + } + assert (list != NULL); + assert (p[-1] == L_(')')); +#undef NEW_PATTERN + + switch (opt) + { + case L_('*'): + if (FCT (p, string, string_end, no_leading_period, flags) == 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; + } + while ((list = list->next) != NULL); + + /* None of the patterns lead to a match. */ + return FNM_NOMATCH; + + case L_('?'): + if (FCT (p, string, string_end, no_leading_period, flags) == 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; + while ((list = list->next) != NULL); + + /* None of the patterns lead to a match. */ + return FNM_NOMATCH; + + case L_('!'): + for (rs = string; rs <= string_end; ++rs) + { + 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; + + /* 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. */ + return FNM_NOMATCH; + + default: + assert (! "Invalid extended matching operator"); + break; + } + + return -1; +} + + +#undef FOLD +#undef CHAR +#undef UCHAR +#undef INT +#undef FCT +#undef EXT +#undef END +#undef MEMPCPY +#undef MEMCHR +#undef STRCOLL +#undef STRLEN +#undef STRCAT +#undef L_ +#undef BTOWC diff --git a/gnulib/getopt.c b/gnulib/getopt.c new file mode 100644 index 000000000..f1e6d1f7c --- /dev/null +++ b/gnulib/getopt.c @@ -0,0 +1,1186 @@ +/* Getopt for GNU. + NOTE: getopt is now 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. + 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 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 _LIBC +# include +#endif + +#include "getopt.h" + +#include +#include +#include +#include + +#ifdef _LIBC +# include +#else +# include "gettext.h" +# define _(msgid) gettext (msgid) +#endif + +#if defined _LIBC && defined USE_IN_LIBIO +# 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. + + As `getopt_long' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Using `getopt' or setting the environment variable POSIXLY_CORRECT + disables permutation. + Then the application's 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. */ + +#include "getopt_int.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Keep a global copy of all internal members of getopt_data. */ + +static struct _getopt_data getopt_data; + + +#if defined HAVE_DECL_GETENV && !HAVE_DECL_GETENV +extern char *getenv (); +#endif + +#ifdef _LIBC +/* Stored original parameters. + XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ +extern int __libc_argc; +extern char **__libc_argv; + +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +# ifdef USE_NONOPTION_FLAGS +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; +# endif + +# 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; \ + } +# else +# define SWAP_FLAGS(ch1, ch2) +# endif +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (char **argv, struct _getopt_data *d) +{ + int bottom = d->__first_nonopt; + int middle = d->__last_nonopt; + int top = d->optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + 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. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + 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; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* 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; + } + else + { + /* 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; + } + } + + /* Update records for the slots the non-options now occupy. */ + + d->__first_nonopt += (d->optind - d->__last_nonopt); + d->__last_nonopt = d->optind; +} + +/* 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) +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + d->__first_nonopt = d->__last_nonopt = d->optind; + + d->__nextchar = NULL; + + d->__posixly_correct = posixly_correct || !!getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + d->__ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + d->__ordering = REQUIRE_ORDER; + ++optstring; + } + else if (d->__posixly_correct) + d->__ordering = REQUIRE_ORDER; + else + d->__ordering = PERMUTE; + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + if (!d->__posixly_correct + && 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); + } + } + d->__nonoption_flags_len = d->__nonoption_flags_max_len; + } + else + d->__nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + 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. */ + +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) +{ + int print_errors = d->opterr; + if (optstring[0] == ':') + print_errors = 0; + + if (argc < 1) + return -1; + + d->optarg = NULL; + + 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->__initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + 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')) +#else +# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0') +#endif + + if (d->__nextchar == NULL || *d->__nextchar == '\0') + { + /* 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). */ + if (d->__last_nonopt > d->optind) + d->__last_nonopt = d->optind; + if (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 (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. */ + + 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. */ + + if (d->optind != argc && !strcmp (argv[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; + + 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. */ + + 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; + } + + /* 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. */ + + if (NONOPTION_P) + { + 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. */ + + d->__nextchar = (argv[d->optind] + 1 + + (longopts != NULL && argv[d->optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[d->optind][1] == '-' + || (long_only && (argv[d->optind][2] + || !strchr (optstring, argv[d->optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = d->__nextchar; *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) + == (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 defined _LIBC && defined USE_IN_LIBIO + char *buf; + + 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; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + 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 '?'; + } + + 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) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + int n; +#endif + + 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); +#else + fprintf (stderr, _("\ +%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); +#endif + } + 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); +#else + 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); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 + |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#endif + } + + 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) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + 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; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + 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; + } + + /* 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. */ + if (!long_only || argv[d->optind][1] == '-' + || strchr (optstring, *d->__nextchar) == NULL) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + int n; +#endif + + 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); +#else + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], d->__nextchar); +#endif + } + 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); +#else + 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); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#endif + } + d->__nextchar = (char *) ""; + d->optind++; + d->optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *d->__nextchar++; + char *temp = strchr (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*d->__nextchar == '\0') + ++d->optind; + + if (temp == NULL || c == ':') + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + 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); +#else + fprintf (stderr, _("%s: illegal 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); + + int old_flags2 = ((_IO_FILE *) stderr)->_flags2; + ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#endif + } + 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; + + /* 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 defined _LIBC && defined USE_IN_LIBIO + char *buf; + + 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; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + 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++]; + + /* 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. */ ; + + /* 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; + + 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; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + 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) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + 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; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + 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) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + 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; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + 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. */ + } + 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 defined _LIBC && defined USE_IN_LIBIO + char *buf; + + 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; + + __fxprintf (NULL, "%s", buf); + + ((_IO_FILE *) stderr)->_flags2 = old_flags2; + _IO_funlockfile (stderr); + + free (buf); + } +#else + 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; + } + } + return c; + } +} + +int +_getopt_internal (int argc, char **argv, const char *optstring, + 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); + + optind = getopt_data.optind; + optarg = getopt_data.optarg; + optopt = getopt_data.optopt; + + return result; +} + +/* glibc gets a LSB-compliant getopt. + Standalone applications get a POSIX-compliant getopt. */ +#if _LIBC +enum { POSIXLY_CORRECT = 0 }; +#else +enum { POSIXLY_CORRECT = 1 }; +#endif + +int +getopt (int argc, char *const *argv, const char *optstring) +{ + return _getopt_internal (argc, (char **) argv, optstring, NULL, NULL, 0, + POSIXLY_CORRECT); +} + + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (int argc, char **argv) +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + 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 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + 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 ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/gnulib/getopt.h b/gnulib/getopt.h new file mode 100644 index 000000000..d2d3e6e63 --- /dev/null +++ b/gnulib/getopt.h @@ -0,0 +1,225 @@ +/* Declarations for getopt. + Copyright (C) 1989-1994,1996-1999,2001,2003,2004,2005,2006,2007 + 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 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 _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +/* Standalone applications should #define __GETOPT_PREFIX to an + identifier that prefixes the external functions and variables + defined in this header. When this happens, include the + headers that might declare getopt so that they will not cause + confusion if included after this file. Then systematically rename + identifiers so that they do not collide with the system functions + and variables. Renaming avoids problems with some compilers and + linkers. */ +#if defined __GETOPT_PREFIX && !defined __need_getopt +# include +# include +# include +# undef __need_getopt +# undef getopt +# undef getopt_long +# undef getopt_long_only +# undef optarg +# undef opterr +# undef optind +# undef optopt +# define __GETOPT_CONCAT(x, y) x ## y +# define __GETOPT_XCONCAT(x, y) __GETOPT_CONCAT (x, y) +# define __GETOPT_ID(y) __GETOPT_XCONCAT (__GETOPT_PREFIX, y) +# define getopt __GETOPT_ID (getopt) +# define getopt_long __GETOPT_ID (getopt_long) +# define getopt_long_only __GETOPT_ID (getopt_long_only) +# define optarg __GETOPT_ID (optarg) +# define opterr __GETOPT_ID (opterr) +# define optind __GETOPT_ID (optind) +# define optopt __GETOPT_ID (optopt) +#endif + +/* Standalone applications get correct prototypes for getopt_long and + getopt_long_only; they declare "char **argv". libc uses prototypes + with "char *const *argv" that are incorrect because getopt_long and + getopt_long_only can permute argv; this is required for backward + compatibility (e.g., for LSB 2.0.1). + + This used to be `#if defined __GETOPT_PREFIX && !defined __need_getopt', + but it caused redefinition warnings if both unistd.h and getopt.h were + included, since unistd.h includes getopt.h having previously defined + __need_getopt. + + The only place where __getopt_argv_const is used is in definitions + of getopt_long and getopt_long_only below, but these are visible + only if __need_getopt is not defined, so it is quite safe to rewrite + the conditional as follows: +*/ +#if !defined __need_getopt +# if defined __GETOPT_PREFIX +# define __getopt_argv_const /* empty */ +# else +# define __getopt_argv_const const +# endif +#endif + +/* If __GNU_LIBRARY__ is not already defined, either we are being used + standalone, or this is the first header included in the source file. + If we are being used with glibc, we need to include , but + that does not exist if we are standalone. So: if __GNU_LIBRARY__ is + not defined, include , which will pull in for us + if it's from glibc. (Why ctype.h? It's guaranteed to exist and it + doesn't flood the namespace with stuff the way some other headers do.) */ +#if !defined __GNU_LIBRARY__ +# include +#endif + +#ifndef __THROW +# ifndef __GNUC_PREREQ +# define __GNUC_PREREQ(maj, min) (0) +# endif +# if defined __cplusplus && __GNUC_PREREQ (2,8) +# define __THROW throw () +# else +# define __THROW +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +#ifndef __need_getopt +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ + const char *name; + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `-', then non-option arguments are treated as + arguments to the option '\1'. This behavior is specific to the GNU + `getopt'. If OPTS begins with `+', or POSIXLY_CORRECT is set in + the environment, then do not permute arguments. */ + +extern int getopt (int ___argc, char *const *___argv, const char *__shortopts) + __THROW; + +#ifndef __need_getopt +extern int getopt_long (int ___argc, char *__getopt_argv_const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind) + __THROW; +extern int getopt_long_only (int ___argc, char *__getopt_argv_const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind) + __THROW; + +#endif + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +#undef __need_getopt + +#endif /* getopt.h */ diff --git a/gnulib/getopt1.c b/gnulib/getopt1.c new file mode 100644 index 000000000..d6a3ecf4e --- /dev/null +++ b/gnulib/getopt1.c @@ -0,0 +1,170 @@ +/* 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. + 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 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 _LIBC +# include +#else +# include +# include "getopt.h" +#endif +#include "getopt_int.h" + +#include + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#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) +{ + return _getopt_internal (argc, (char **) argv, options, long_options, + 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) +{ + return _getopt_internal_r (argc, argv, options, long_options, opt_index, + 0, 0, d); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (int argc, char *__getopt_argv_const *argv, + const char *options, + const struct option *long_options, int *opt_index) +{ + return _getopt_internal (argc, (char **) argv, options, long_options, + 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) +{ + return _getopt_internal_r (argc, argv, options, long_options, opt_index, + 1, 0, d); +} + + +#ifdef TEST + +#include + +int +main (int argc, char **argv) +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static 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} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + 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': + 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 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + 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 ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/gnulib/getopt_int.h b/gnulib/getopt_int.h new file mode 100644 index 000000000..3c6628bb9 --- /dev/null +++ b/gnulib/getopt_int.h @@ -0,0 +1,130 @@ +/* Internal declarations for getopt. + Copyright (C) 1989-1994,1996-1999,2001,2003,2004 + 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 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 _GETOPT_INT_H +#define _GETOPT_INT_H 1 + +extern int _getopt_internal (int ___argc, char **___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only, int __posixly_correct); + + +/* Reentrant versions which can handle parsing multiple argument + vectors at the same time. */ + +/* Data type for reentrant functions. */ +struct _getopt_data +{ + /* These have exactly the same meaning as the corresponding global + variables, except that they are used for the reentrant + versions of getopt. */ + int optind; + int opterr; + int optopt; + char *optarg; + + /* Internal members. */ + + /* True if the internal members have been initialized. */ + int __initialized; + + /* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + char *__nextchar; + + /* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters, or by calling getopt. + + PERMUTE is the default. We permute the contents of ARGV as we + scan, so that eventually all the non-options are at the end. + This allows options to be given in any order, even with programs + that were not written to expect this. + + RETURN_IN_ORDER is an option available to programs that were + written to expect options and other ARGV-elements in any order + and that care about the ordering of the two. We describe each + non-option ARGV-element as if it were the argument of an option + with character code 1. Using `-' as the first character of the + list of option characters selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + + enum + { + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER + } __ordering; + + /* If the POSIXLY_CORRECT environment variable is set + or getopt was called. */ + int __posixly_correct; + + + /* Handle permutation of arguments. */ + + /* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first + of them; `last_nonopt' is the index after the last of them. */ + + int __first_nonopt; + int __last_nonopt; + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + int __nonoption_flags_max_len; + int __nonoption_flags_len; +# endif +}; + +/* 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 } + +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); + +extern int _getopt_long_r (int ___argc, char **___argv, + 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); + +#endif /* getopt_int.h */ diff --git a/gnulib/gettext.h b/gnulib/gettext.h new file mode 100644 index 000000000..9d76ec9af --- /dev/null +++ b/gnulib/gettext.h @@ -0,0 +1,270 @@ +/* Convenience header for conditional use of GNU . + Copyright (C) 1995-1998, 2000-2002, 2004-2006 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 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 _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include + +/* You can set the DEFAULT_TEXT_DOMAIN macro to specify the domain used by + the gettext() and ngettext() macros. This is an alternative to calling + textdomain(), and is useful for libraries. */ +# ifdef DEFAULT_TEXT_DOMAIN +# undef gettext +# define gettext(Msgid) \ + dgettext (DEFAULT_TEXT_DOMAIN, Msgid) +# undef ngettext +# define ngettext(Msgid1, Msgid2, N) \ + dngettext (DEFAULT_TEXT_DOMAIN, Msgid1, Msgid2, N) +# endif + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of a NOP. We don't include + as well because people using "gettext.h" will not include , + and also including would fail on SunOS 4, whereas + is OK. */ +#if defined(__sun) +# include +#endif + +/* Many header files from the libstdc++ coming with g++ 3.3 or newer include + , which chokes if dcgettext is defined as a macro. So include + it now, to make later inclusions of a NOP. */ +#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3) +# include +# if (__GLIBC__ >= 2) || _GLIBCXX_HAVE_LIBINTL_H +# include +# endif +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + 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". */ +# define gettext(Msgid) ((const char *) (Msgid)) +# define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid)) +# define dcgettext(Domainname, Msgid, Category) \ + ((void) (Category), dgettext (Domainname, Msgid)) +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 \ + ? ((void) (Msgid2), (const char *) (Msgid1)) \ + : ((void) (Msgid1), (const char *) (Msgid2))) +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((void) (Domainname), ngettext (Msgid1, Msgid2, N)) +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((void) (Category), dngettext(Domainname, Msgid1, Msgid2, N)) +# define textdomain(Domainname) ((const char *) (Domainname)) +# define bindtextdomain(Domainname, Dirname) \ + ((void) (Domainname), (const char *) (Dirname)) +# define bind_textdomain_codeset(Domainname, Codeset) \ + ((void) (Domainname), (const char *) (Codeset)) + +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +/* The separator between msgctxt and msgid in a .mo file. */ +#define GETTEXT_CONTEXT_GLUE "\004" + +/* Pseudo function calls, taking a MSGCTXT and a MSGID instead of just a + MSGID. MSGCTXT and MSGID must be string literals. MSGCTXT should be + short and rarely need to change. + The letter 'p' stands for 'particular' or 'special'. */ +#ifdef DEFAULT_TEXT_DOMAIN +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#else +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#endif +#define dpgettext(Domainname, Msgctxt, Msgid) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#define dcpgettext(Domainname, Msgctxt, Msgid, Category) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, Category) +#ifdef DEFAULT_TEXT_DOMAIN +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#else +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#endif +#define dnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dcnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, Category) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +pgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + int category) +{ + const char *translation = dcgettext (domain, msg_ctxt_id, category); + if (translation == msg_ctxt_id) + return msgid; + else + return translation; +} + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#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 *translation = + dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); + if (translation == msg_ctxt_id || translation == msgid_plural) + return (n == 1 ? msgid : msgid_plural); + else + return translation; +} + +/* The same thing extended for non-constant arguments. Here MSGCTXT and MSGID + can be arbitrary expressions. But for string literals these macros are + less efficient than those above. */ + +#include + +#define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS \ + (((__GNUC__ >= 3 || __GNUG__ >= 2) && !__STRICT_ANSI__) \ + /* || __STDC_VERSION__ >= 199901L */ ) + +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS +#include +#endif + +#define pgettext_expr(Msgctxt, Msgid) \ + dcpgettext_expr (NULL, Msgctxt, Msgid, LC_MESSAGES) +#define dpgettext_expr(Domainname, Msgctxt, Msgid) \ + dcpgettext_expr (Domainname, Msgctxt, Msgid, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcgettext (domain, msg_ctxt_id, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (translation != msg_ctxt_id) + return translation; + } + return msgid; +} + +#define npgettext_expr(Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (NULL, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dnpgettext_expr(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (Domainname, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#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) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + 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); +#endif + if (!(translation == msg_ctxt_id || translation == msgid_plural)) + return translation; + } + return (n == 1 ? msgid : msgid_plural); +} + +#endif /* _LIBGETTEXT_H */ diff --git a/include/grub/i386/coreboot/boot.h b/include/grub/i386/coreboot/boot.h new file mode 100644 index 000000000..6cd23aa83 --- /dev/null +++ b/include/grub/i386/coreboot/boot.h @@ -0,0 +1 @@ +#include diff --git a/include/grub/i386/coreboot/init.h b/include/grub/i386/coreboot/init.h new file mode 100644 index 000000000..e67007414 --- /dev/null +++ b/include/grub/i386/coreboot/init.h @@ -0,0 +1,28 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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_INIT_I386_LINUXBIOS_HEADER +#define GRUB_INIT_I386_LINUXBIOS_HEADER 1 + +#include +#include + +void EXPORT_FUNC(grub_stop) (void) __attribute__ ((noreturn)); +void EXPORT_FUNC(grub_stop_floppy) (void); + +#endif diff --git a/include/grub/i386/coreboot/loader.h b/include/grub/i386/coreboot/loader.h new file mode 100644 index 000000000..d3f36bba5 --- /dev/null +++ b/include/grub/i386/coreboot/loader.h @@ -0,0 +1 @@ +#include diff --git a/include/grub/i386/coreboot/serial.h b/include/grub/i386/coreboot/serial.h new file mode 100644 index 000000000..2c527f626 --- /dev/null +++ b/include/grub/i386/coreboot/serial.h @@ -0,0 +1 @@ +#include diff --git a/include/grub/i386/coreboot/time.h b/include/grub/i386/coreboot/time.h new file mode 100644 index 000000000..2298ee8f4 --- /dev/null +++ b/include/grub/i386/coreboot/time.h @@ -0,0 +1 @@ +#include diff --git a/include/grub/list.h b/include/grub/list.h index 6e034928a..2dffdc08d 100644 --- a/include/grub/list.h +++ b/include/grub/list.h @@ -68,7 +68,7 @@ extern void* grub_assert_fail (void); struct grub_named_list { struct grub_named_list *next; - const char *name; + char *name; }; typedef struct grub_named_list *grub_named_list_t; @@ -91,7 +91,7 @@ void * EXPORT_FUNC(grub_named_list_find) (grub_named_list_t head, struct grub_prio_list { struct grub_prio_list *next; - const char *name; + char *name; int prio; }; typedef struct grub_prio_list *grub_prio_list_t; diff --git a/kern/command.c b/kern/command.c index 63b536eb7..9b3c92b9b 100644 --- a/kern/command.c +++ b/kern/command.c @@ -31,7 +31,7 @@ grub_register_command_prio (const char *name, { grub_command_t cmd; - cmd = (grub_command_t) grub_malloc (sizeof (*cmd)); + cmd = (grub_command_t) grub_zalloc (sizeof (*cmd)); if (! cmd) return 0; @@ -42,7 +42,6 @@ grub_register_command_prio (const char *name, cmd->flags = GRUB_COMMAND_FLAG_BOTH; cmd->prio = prio; - cmd->data = 0; grub_prio_list_insert (GRUB_AS_PRIO_LIST_P (&grub_command_list), GRUB_AS_PRIO_LIST (cmd)); diff --git a/kern/i386/multiboot_mmap.c b/kern/i386/multiboot_mmap.c index 8331bd5df..67d824313 100644 --- a/kern/i386/multiboot_mmap.c +++ b/kern/i386/multiboot_mmap.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2002,2003,2004,2005,2006,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2002,2003,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 @@ -40,7 +40,7 @@ void grub_machine_mmap_init () { if (! startup_multiboot_info) - grub_fatal ("Must be loaded using Multiboot specification (is this an old version of coreboot?)"); + grub_fatal ("Unable to find Multiboot Information (is CONFIG_MULTIBOOT disabled in coreboot?)"); /* Move MBI to a safe place. */ grub_memmove (&kern_multiboot_info, startup_multiboot_info, sizeof (struct grub_multiboot_info)); @@ -51,7 +51,8 @@ grub_machine_mmap_init () /* Move the memory map to a safe place. */ if (kern_multiboot_info.mmap_length > sizeof (mmap_entries)) { - grub_printf ("WARNING: Memory map size exceeds limit; it will be truncated\n"); + grub_printf ("WARNING: Memory map size exceeds limit (0x%x > 0x%x); it will be truncated\n", + kern_multiboot_info.mmap_length, sizeof (mmap_entries)); kern_multiboot_info.mmap_length = sizeof (mmap_entries); } grub_memmove (mmap_entries, (void *) kern_multiboot_info.mmap_addr, kern_multiboot_info.mmap_length); diff --git a/kern/list.c b/kern/list.c index b879f1320..379b0d886 100644 --- a/kern/list.c +++ b/kern/list.c @@ -19,6 +19,7 @@ #include #include +#include void grub_list_push (grub_list_t *head, grub_list_t item) @@ -81,7 +82,7 @@ grub_list_insert (grub_list_t *head, grub_list_t item, void * grub_named_list_find (grub_named_list_t head, const char *name) { - grub_named_list_t result = 0; + grub_named_list_t result = NULL; auto int list_find (grub_named_list_t item); int list_find (grub_named_list_t item) diff --git a/normal/auth.c b/normal/auth.c index 9029ba1ce..c71262584 100644 --- a/normal/auth.c +++ b/normal/auth.c @@ -22,6 +22,7 @@ #include #include #include +#include struct grub_auth_user { @@ -35,17 +36,19 @@ struct grub_auth_user struct grub_auth_user *users = NULL; int -grub_auth_strcmp (const char *user_input, const char *template) +grub_auth_strcmp (const char *s1, const char *s2) { - int ok = 1; - const char *ptr1, *ptr2; - for (ptr1 = user_input, ptr2 = template; *ptr1; ptr1++) - if (*ptr1 == (ptr2 ? *ptr2 : ptr1[1]) && ok && ptr2 != NULL) - ptr2++; - else - ok = 0; + int ret; + grub_uint64_t end; - return !ok; + end = grub_get_time_ms () + 100; + ret = grub_strcmp (s1, s2); + + /* This prevents an attacker from deriving information about the + password from the time it took to execute this function. */ + while (grub_get_time_ms () < end); + + return ret; } static int @@ -208,6 +211,7 @@ grub_auth_check_authentication (const char *userlist) char login[1024]; struct grub_auth_user *cur = NULL; grub_err_t err; + static unsigned long punishment_delay = 1; auto int hook (grub_list_t item); int hook (grub_list_t item) @@ -228,11 +232,14 @@ grub_auth_check_authentication (const char *userlist) grub_memset (login, 0, sizeof (login)); if (is_authenticated (userlist)) - return GRUB_ERR_NONE; + { + punishment_delay = 1; + return GRUB_ERR_NONE; + } if (!grub_cmdline_get ("Enter username: ", login, sizeof (login) - 1, 0, 0, 0)) - return GRUB_ACCESS_DENIED; + goto access_denied; grub_list_iterate (GRUB_AS_LIST (users), hook); @@ -242,15 +249,25 @@ grub_auth_check_authentication (const char *userlist) /* No users present at all. */ if (!cur) - return GRUB_ACCESS_DENIED; + goto access_denied; /* Display any of available authentication schemes. */ err = cur->callback (login, 0); - return GRUB_ACCESS_DENIED; + goto access_denied; } err = cur->callback (login, cur->arg); if (is_authenticated (userlist)) - return GRUB_ERR_NONE; + { + punishment_delay = 1; + return GRUB_ERR_NONE; + } + + access_denied: + grub_sleep (punishment_delay); + + if (punishment_delay < GRUB_ULONG_MAX / 2) + punishment_delay *= 2; + return GRUB_ACCESS_DENIED; } diff --git a/normal/autofs.c b/normal/autofs.c index 39f2f9ddc..ce354a22c 100644 --- a/normal/autofs.c +++ b/normal/autofs.c @@ -25,22 +25,15 @@ #include /* This is used to store the names of filesystem modules for auto-loading. */ -struct grub_fs_module_list -{ - char *name; - struct grub_fs_module_list *next; -}; -typedef struct grub_fs_module_list *grub_fs_module_list_t; - -static grub_fs_module_list_t fs_module_list = 0; +static grub_named_list_t fs_module_list; /* The auto-loading hook for filesystems. */ static int autoload_fs_module (void) { - grub_fs_module_list_t p; + grub_named_list_t p; - while ((p = fs_module_list) != 0) + while ((p = fs_module_list) != NULL) { if (! grub_dl_get (p->name) && grub_dl_load (p->name)) return 1; @@ -84,7 +77,7 @@ read_fs_list (void) char *buf; char *p; char *q; - grub_fs_module_list_t fs_mod; + grub_named_list_t fs_mod; buf = grub_file_getline (file); if (! buf) diff --git a/normal/dyncmd.c b/normal/dyncmd.c index 154da6114..dc530b07b 100644 --- a/normal/dyncmd.c +++ b/normal/dyncmd.c @@ -83,8 +83,8 @@ read_command_list (void) file = grub_file_open (filename); if (file) { - char *buf = 0; - for (;; grub_free(buf)) + char *buf = NULL; + for (;; grub_free (buf)) { char *p, *name, *modname; grub_command_t cmd; diff --git a/normal/handler.c b/normal/handler.c index fe31478fe..eb19f912f 100644 --- a/normal/handler.c +++ b/normal/handler.c @@ -181,8 +181,8 @@ read_handler_list (void) file = grub_file_open (filename); if (file) { - char *buf = 0; - for (;; grub_free(buf)) + char *buf = NULL; + for (;; grub_free (buf)) { char *p; diff --git a/util/i386/pc/grub-install.in b/util/grub-install.in similarity index 100% rename from util/i386/pc/grub-install.in rename to util/grub-install.in index 8a06213cb..356e161e7 100644 --- a/util/i386/pc/grub-install.in +++ b/util/grub-install.in @@ -31,9 +31,6 @@ target_cpu=@target_cpu@ platform=@platform@ pkglibdir=${libdir}/`echo ${PACKAGE_TARNAME}/${target_cpu}-${platform} | sed ${transform}` -# for make_system_path_relative_to_its_root() -. ${libdir}/grub/grub-mkconfig_lib - grub_setup=${sbindir}/`echo grub-setup | sed ${transform}` if [ "${target_cpu}-${platform}" = "i386-pc" ] ; then grub_mkimage=${bindir}/`echo grub-mkimage | sed ${transform}` @@ -144,6 +141,9 @@ for option in "$@"; do esac done +# for make_system_path_relative_to_its_root() +. ${libdir}/grub/grub-mkconfig_lib + if test "x$install_device" = x; then echo "install_device not specified." 1>&2 usage diff --git a/util/i386/coreboot/grub-mkrescue.in b/util/i386/coreboot/grub-mkrescue.in new file mode 100644 index 000000000..6731e748e --- /dev/null +++ b/util/i386/coreboot/grub-mkrescue.in @@ -0,0 +1,148 @@ +#! /bin/sh -e + +# Make GRUB rescue image +# Copyright (C) 1999,2000,2001,2002,2003,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 . + +# Initialize some variables. +transform="@program_transform_name@" + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +bindir=@bindir@ +libdir=@libdir@ +PACKAGE_NAME=@PACKAGE_NAME@ +PACKAGE_TARNAME=@PACKAGE_TARNAME@ +PACKAGE_VERSION=@PACKAGE_VERSION@ +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_mkisofs=${bindir}/`echo grub-mkisofs | sed ${transform}` + +# Usage: usage +# Print the usage. +usage () { + cat <. +EOF +} + +input_dir=${pkglibdir} + +# Check the arguments. +for option in "$@"; do + case "$option" in + -h | --help) + usage + exit 0 ;; + -v | --version) + echo "$0 (GNU GRUB ${PACKAGE_VERSION})" + exit 0 ;; + --modules=*) + modules=`echo "$option" | sed 's/--modules=//'` ;; + --overlay=*) + overlay=${overlay}${overlay:+ }`echo "$option" | sed 's/--overlay=//'` ;; + --pkglibdir=*) + input_dir=`echo "$option" | sed 's/--pkglibdir=//'` ;; + --grub-mkimage=*) + grub_mkimage=`echo "$option" | sed 's/--grub-mkimage=//'` ;; + --grub-mkisofs=*) + grub_mkisofs=`echo "$option" | sed 's/--grub-mkisofs=//'` ;; + -*) + echo "Unrecognized option \`$option'" 1>&2 + usage + exit 1 + ;; + *) + if test "x$output_image" != x; then + echo "Unrecognized option \`$option'" 1>&2 + usage + exit 1 + fi + output_image="${option}" ;; + esac +done + +if test "x$output_image" = x; then + usage + exit 1 +fi + +memdisk_dir=`mktemp -d` +iso9660_dir=`mktemp -d` +mkdir -p ${memdisk_dir}/boot/grub ${iso9660_dir}/boot/grub + +for file in ${input_dir}/*.mod ${input_dir}/efiemu??.o \ + ${input_dir}/command.lst ${input_dir}/moddep.lst ${input_dir}/fs.lst \ + ${input_dir}/handler.lst ${input_dir}/parttool.lst; do + if test -f "$file"; then + cp -f "$file" ${iso9660_dir}/boot/grub/ + fi +done + +# obtain date-based UUID +iso_uuid=$(date +%Y-%m-%d-%H-%M-%S-00) + +# first-stage grub.cfg +cat << EOF >> ${memdisk_dir}/boot/grub/grub.cfg +search --fs-uuid --set ${iso_uuid} +set prefix=(\${root})/boot/grub +source /boot/grub/grub.cfg +EOF + +# build memdisk +memdisk_img=`mktemp` +tar -C ${memdisk_dir} -cf ${memdisk_img} boot +rm -rf ${memdisk_dir} + +# build core.img +mkdir -p ${iso9660_dir}/boot/grub +${grub_mkimage} -d ${input_dir}/ -m ${memdisk_img} -o ${iso9660_dir}/boot/multiboot.img \ + at_keyboard memdisk tar ata search iso9660 configfile sh +rm -f ${memdisk_img} + +for d in ${overlay}; do + echo "Overlaying $d" + cp -dpR "${d}"/* "${iso9660_dir}"/ +done + +# second-stage grub.cfg +modules="`cat ${input_dir}/partmap.lst` ${modules}" +for i in ${modules} ; do + echo "insmod $i" +done > ${iso9660_dir}/boot/grub/grub.cfg + +# build iso image +${grub_mkisofs} \ + --modification-date=$(echo ${iso_uuid} | sed -e s/-//g) \ + -o ${output_image} -r -J ${iso9660_dir} + +exit 0 diff --git a/util/i386/pc/grub-mkrescue.in b/util/i386/pc/grub-mkrescue.in index da937768b..cc581bffe 100644 --- a/util/i386/pc/grub-mkrescue.in +++ b/util/i386/pc/grub-mkrescue.in @@ -36,7 +36,7 @@ grub_mkimage=${bindir}/`echo grub-mkimage | sed ${transform}` # Print the usage. usage () { cat <. EOF @@ -67,7 +67,7 @@ for option in "$@"; do usage exit 0 ;; -v | --version) - echo "grub-mkrescue (GNU GRUB ${PACKAGE_VERSION})" + echo "$0 (GNU GRUB ${PACKAGE_VERSION})" exit 0 ;; --modules=*) modules=`echo "$option" | sed 's/--modules=//'` ;; diff --git a/util/mkisofs/defaults.h b/util/mkisofs/defaults.h new file mode 100644 index 000000000..dc9ad380c --- /dev/null +++ b/util/mkisofs/defaults.h @@ -0,0 +1,54 @@ +/* + * Header file defaults.h - assorted default values for character strings in + * the volume descriptor. + * + * $Id: defaults.h,v 1.8 1999/03/02 03:41:25 eric Exp $ + */ + +#define PREPARER_DEFAULT NULL +#define PUBLISHER_DEFAULT NULL +#ifndef APPID_DEFAULT +#define APPID_DEFAULT "MKISOFS ISO 9660 FILESYSTEM BUILDER" +#endif +#define COPYRIGHT_DEFAULT NULL +#define BIBLIO_DEFAULT NULL +#define ABSTRACT_DEFAULT NULL +#define VOLSET_ID_DEFAULT NULL +#define VOLUME_ID_DEFAULT "CDROM" +#define BOOT_CATALOG_DEFAULT "boot.catalog" +#define BOOT_IMAGE_DEFAULT NULL +#ifdef __QNX__ +#define SYSTEM_ID_DEFAULT "QNX" +#endif + +#ifdef __osf__ +#define SYSTEM_ID_DEFAULT "OSF" +#endif + +#ifdef __sun +#ifdef __SVR4 +#define SYSTEM_ID_DEFAULT "Solaris" +#else +#define SYSTEM_ID_DEFAULT "SunOS" +#endif +#endif + +#ifdef __hpux +#define SYSTEM_ID_DEFAULT "HP-UX" +#endif + +#ifdef __sgi +#define SYSTEM_ID_DEFAULT "SGI" +#endif + +#ifdef _AIX +#define SYSTEM_ID_DEFAULT "AIX" +#endif + +#ifdef _WIN +#define SYSTEM_ID_DEFAULT "Win32" +#endif /* _WIN */ + +#ifndef SYSTEM_ID_DEFAULT +#define SYSTEM_ID_DEFAULT "LINUX" +#endif diff --git a/util/mkisofs/eltorito.c b/util/mkisofs/eltorito.c new file mode 100644 index 000000000..65fbfe646 --- /dev/null +++ b/util/mkisofs/eltorito.c @@ -0,0 +1,344 @@ +/* + * Program eltorito.c - Handle El Torito specific extensions to iso9660. + * + + Written by Michael Fulbright (1996). + + Copyright 1996 RedHat Software, Incorporated + + Copyright (C) 2009 Free Software Foundation, Inc. + + Boot Info Table generation based on code from genisoimage.c + (from cdrkit 1.1.9), which was originally licensed under GPLv2+. + + 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, 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 . + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +static char rcsid[] ="$Id: eltorito.c,v 1.13 1999/03/02 03:41:25 eric Exp $"; + +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "mkisofs.h" +#include "iso9660.h" + +/* used by Win32 for opening binary file - not used by Unix */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif /* O_BINARY */ + +#undef MIN +#define MIN(a, b) (((a) < (b))? (a): (b)) + +static struct eltorito_validation_entry valid_desc; +static struct eltorito_defaultboot_entry default_desc; +static struct eltorito_boot_descriptor gboot_desc; + +static int tvd_write __PR((FILE * outfile)); + +/* + * Check for presence of boot catalog. If it does not exist then make it + */ +void FDECL1(init_boot_catalog, const char *, path) +{ + FILE *bcat; + char * bootpath; /* filename of boot catalog */ + char * buf; + struct stat statbuf; + + bootpath = (char *) e_malloc(strlen(boot_catalog)+strlen(path)+2); + strcpy(bootpath, path); + if (bootpath[strlen(bootpath)-1] != '/') + { + strcat(bootpath,"/"); + } + + strcat(bootpath, boot_catalog); + + /* + * check for the file existing + */ +#ifdef DEBUG_TORITO + fprintf(stderr,"Looking for boot catalog file %s\n",bootpath); +#endif + + if (!stat_filter(bootpath, &statbuf)) + { + /* + * make sure its big enough to hold what we want + */ + if (statbuf.st_size == 2048) + { + /* + * printf("Boot catalog exists, so we do nothing\n"); + */ + free(bootpath); + return; + } + else + { + fprintf(stderr, "A boot catalog exists and appears corrupted.\n"); + fprintf(stderr, "Please check the following file: %s.\n",bootpath); + fprintf(stderr, "This file must be removed before a bootable CD can be done.\n"); + free(bootpath); + exit(1); + } + } + + /* + * file does not exist, so we create it + * make it one CD sector long + */ + bcat = fopen (bootpath, "wb"); + if (bcat == NULL) + error (1, errno, "Error creating boot catalog (%s)", bootpath); + + buf = (char *) e_malloc( 2048 ); + if (fwrite (buf, 1, 2048, bcat) != 2048) + error (1, errno, "Error writing to boot catalog (%s)", bootpath); + fclose (bcat); + chmod (bootpath, S_IROTH | S_IRGRP | S_IRWXU); + + free(bootpath); +} /* init_boot_catalog(... */ + +void FDECL1(get_torito_desc, struct eltorito_boot_descriptor *, boot_desc) +{ + FILE *bootcat; + int checksum; + unsigned char * checksum_ptr; + struct directory_entry * de; + struct directory_entry * de2; + unsigned int i; + int nsectors; + + memset(boot_desc, 0, sizeof(*boot_desc)); + boot_desc->id[0] = 0; + memcpy(boot_desc->id2, ISO_STANDARD_ID, sizeof(ISO_STANDARD_ID)); + boot_desc->version[0] = 1; + + memcpy(boot_desc->system_id, EL_TORITO_ID, sizeof(EL_TORITO_ID)); + + /* + * search from root of iso fs to find boot catalog + */ + de2 = search_tree_file(root, boot_catalog); + if (!de2) + { + fprintf(stderr,"Uh oh, I cant find the boot catalog!\n"); + exit(1); + } + + set_731(boot_desc->bootcat_ptr, + (unsigned int) get_733(de2->isorec.extent)); + + /* + * now adjust boot catalog + * lets find boot image first + */ + de=search_tree_file(root, boot_image); + if (!de) + { + fprintf(stderr,"Uh oh, I cant find the boot image!\n"); + exit(1); + } + + /* + * we have the boot image, so write boot catalog information + * Next we write out the primary descriptor for the disc + */ + memset(&valid_desc, 0, sizeof(valid_desc)); + valid_desc.headerid[0] = 1; + valid_desc.arch[0] = EL_TORITO_ARCH_x86; + + /* + * we'll shove start of publisher id into id field, may get truncated + * but who really reads this stuff! + */ + if (publisher) + memcpy_max(valid_desc.id, publisher, MIN(23, strlen(publisher))); + + valid_desc.key1[0] = 0x55; + valid_desc.key2[0] = 0xAA; + + /* + * compute the checksum + */ + checksum=0; + checksum_ptr = (unsigned char *) &valid_desc; + for (i=0; isize + 511) & ~(511))/512; + fprintf(stderr, "\nSize of boot image is %d sectors -> ", nsectors); + + if (! use_eltorito_emul_floppy) + { + default_desc.boot_media[0] = EL_TORITO_MEDIA_NOEMUL; + fprintf (stderr, "No emulation\n"); + } + else if (nsectors == 2880 ) + /* + * choose size of emulated floppy based on boot image size + */ + { + default_desc.boot_media[0] = EL_TORITO_MEDIA_144FLOP; + fprintf(stderr, "Emulating a 1.44 meg floppy\n"); + } + else if (nsectors == 5760 ) + { + default_desc.boot_media[0] = EL_TORITO_MEDIA_288FLOP; + fprintf(stderr,"Emulating a 2.88 meg floppy\n"); + } + else if (nsectors == 2400 ) + { + default_desc.boot_media[0] = EL_TORITO_MEDIA_12FLOP; + fprintf(stderr,"Emulating a 1.2 meg floppy\n"); + } + else + { + fprintf(stderr,"\nError - boot image is not the an allowable size.\n"); + exit(1); + } + + /* + * FOR NOW LOAD 1 SECTOR, JUST LIKE FLOPPY BOOT!!! + */ + nsectors = 1; + set_721(default_desc.nsect, (unsigned int) nsectors ); +#ifdef DEBUG_TORITO + fprintf(stderr,"Extent of boot images is %d\n",get_733(de->isorec.extent)); +#endif + set_731(default_desc.bootoff, + (unsigned int) get_733(de->isorec.extent)); + + /* + * now write it to disk + */ + bootcat = fopen (de2->whole_name, "r+b"); + if (bootcat == NULL) + error (1, errno, "Error opening boot catalog for update"); + + /* + * write out + */ + if (fwrite (&valid_desc, 1, 32, bootcat) != 32) + error (1, errno, "Error writing to boot catalog"); + if (fwrite (&default_desc, 1, 32, bootcat) != 32) + error (1, errno, "Error writing to boot catalog"); + fclose (bootcat); + + /* If the user has asked for it, patch the boot image */ + if (use_boot_info_table) + { + FILE *bootimage; + uint32_t bi_checksum; + unsigned int total_len; + static char csum_buffer[SECTOR_SIZE]; + int len; + struct eltorito_boot_info bi_table; + bootimage = fopen (de->whole_name, "r+b"); + if (bootimage == NULL) + error (1, errno, "Error opening boot image file '%s' for update", + de->whole_name); + /* Compute checksum of boot image, sans 64 bytes */ + total_len = 0; + bi_checksum = 0; + while ((len = fread (csum_buffer, 1, SECTOR_SIZE, bootimage)) > 0) + { + if (total_len & 3) + error (1, 0, "Odd alignment at non-end-of-file in boot image '%s'", + de->whole_name); + if (total_len < 64) + memset (csum_buffer, 0, 64 - total_len); + if (len < SECTOR_SIZE) + memset (csum_buffer + len, 0, SECTOR_SIZE - len); + for (i = 0; i < SECTOR_SIZE; i += 4) + bi_checksum += get_731 (&csum_buffer[i]); + total_len += len; + } + + if (total_len != de->size) + error (1, 0, "Boot image file '%s' changed underneath us", + de->whole_name); + /* End of file, set position to byte 8 */ + fseeko (bootimage, (off_t) 8, SEEK_SET); + memset (&bi_table, 0, sizeof (bi_table)); + /* Is it always safe to assume PVD is at session_start+16? */ + set_731 (bi_table.pvd_addr, session_start + 16); + set_731 (bi_table.file_addr, de->starting_block); + set_731 (bi_table.file_length, de->size); + set_731 (bi_table.file_checksum, bi_checksum); + + if (fwrite (&bi_table, 1, sizeof (bi_table), bootimage) != sizeof (bi_table)) + error (1, errno, "Error writing to boot image (%s)", bootimage); + fclose (bootimage); + } + +} /* get_torito_desc(... */ + +/* + * Function to write the EVD for the disc. + */ +static int FDECL1(tvd_write, FILE *, outfile) +{ + /* + * Next we write out the boot volume descriptor for the disc + */ + get_torito_desc(&gboot_desc); + xfwrite(&gboot_desc, 1, 2048, outfile); + last_extent_written ++; + return 0; +} + +struct output_fragment torito_desc = {NULL, oneblock_size, NULL, tvd_write}; diff --git a/util/mkisofs/exclude.h b/util/mkisofs/exclude.h new file mode 100644 index 000000000..87cd6948a --- /dev/null +++ b/util/mkisofs/exclude.h @@ -0,0 +1,10 @@ +/* + * 9-Dec-93 R.-D. Marzusch, marzusch@odiehh.hanse.de: + * added 'exclude' option (-x) to specify pathnames NOT to be included in + * CD image. + * + * $Id: exclude.h,v 1.2 1999/03/02 03:41:25 eric Exp $ + */ + +void exclude __PR((char * fn)); +int is_excluded __PR((char * fn)); diff --git a/util/mkisofs/hash.c b/util/mkisofs/hash.c new file mode 100644 index 000000000..eb673393e --- /dev/null +++ b/util/mkisofs/hash.c @@ -0,0 +1,227 @@ +/* + * File hash.c - generate hash tables for iso9660 filesystem. + + Written by Eric Youngdale (1993). + + Copyright 1993 Yggdrasil Computing, Incorporated + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +static char rcsid[] ="$Id: hash.c,v 1.4 1997/12/06 21:05:04 eric Exp $"; + +#include +#include "config.h" +#include "mkisofs.h" + +#define NR_HASH 1024 + +#define HASH_FN(DEV, INO) ((DEV + INO + (INO >> 2) + (INO << 8)) % NR_HASH) + +static struct file_hash * hash_table[NR_HASH] = {0,}; + +void FDECL1(add_hash, struct directory_entry *, spnt){ + struct file_hash * s_hash; + unsigned int hash_number; + + if(spnt->size == 0 || spnt->starting_block == 0) + if(spnt->size != 0 || spnt->starting_block != 0) { + fprintf(stderr,"Non zero-length file assigned zero extent.\n"); + exit(1); + }; + + if (spnt->dev == (dev_t) UNCACHED_DEVICE || spnt->inode == UNCACHED_INODE) return; + hash_number = HASH_FN((unsigned int) spnt->dev, (unsigned int) spnt->inode); + +#if 0 + if (verbose > 1) fprintf(stderr,"%s ",spnt->name); +#endif + s_hash = (struct file_hash *) e_malloc(sizeof(struct file_hash)); + s_hash->next = hash_table[hash_number]; + s_hash->inode = spnt->inode; + s_hash->dev = spnt->dev; + s_hash->starting_block = spnt->starting_block; + s_hash->size = spnt->size; + hash_table[hash_number] = s_hash; +} + +struct file_hash * FDECL2(find_hash, dev_t, dev, ino_t, inode){ + unsigned int hash_number; + struct file_hash * spnt; + hash_number = HASH_FN((unsigned int) dev, (unsigned int) inode); + if (dev == (dev_t) UNCACHED_DEVICE || inode == UNCACHED_INODE) return NULL; + + spnt = hash_table[hash_number]; + while(spnt){ + if(spnt->inode == inode && spnt->dev == dev) return spnt; + spnt = spnt->next; + }; + return NULL; +} + + +static struct file_hash * directory_hash_table[NR_HASH] = {0,}; + +void FDECL2(add_directory_hash, dev_t, dev, ino_t, inode){ + struct file_hash * s_hash; + unsigned int hash_number; + + if (dev == (dev_t) UNCACHED_DEVICE || inode == UNCACHED_INODE) return; + hash_number = HASH_FN((unsigned int) dev, (unsigned int) inode); + + s_hash = (struct file_hash *) e_malloc(sizeof(struct file_hash)); + s_hash->next = directory_hash_table[hash_number]; + s_hash->inode = inode; + s_hash->dev = dev; + directory_hash_table[hash_number] = s_hash; +} + +struct file_hash * FDECL2(find_directory_hash, dev_t, dev, ino_t, inode){ + unsigned int hash_number; + struct file_hash * spnt; + hash_number = HASH_FN((unsigned int) dev, (unsigned int) inode); + if (dev == (dev_t) UNCACHED_DEVICE || inode == UNCACHED_INODE) return NULL; + + spnt = directory_hash_table[hash_number]; + while(spnt){ + if(spnt->inode == inode && spnt->dev == dev) return spnt; + spnt = spnt->next; + }; + return NULL; +} + +struct name_hash +{ + struct name_hash * next; + struct directory_entry * de; +}; + +#define NR_NAME_HASH 128 + +static struct name_hash * name_hash_table[NR_NAME_HASH] = {0,}; + +/* + * Find the hash bucket for this name. + */ +static unsigned int FDECL1(name_hash, const char *, name) +{ + unsigned int hash = 0; + const char * p; + + p = name; + + while (*p) + { + /* + * Don't hash the iso9660 version number. This way + * we can detect duplicates in cases where we have + * directories (i.e. foo) and non-directories + * (i.e. foo;1). + */ + if( *p == ';' ) + { + break; + } + hash = (hash << 15) + (hash << 3) + (hash >> 3) + *p++; + } + return hash % NR_NAME_HASH; +} + +void FDECL1(add_file_hash, struct directory_entry *, de){ + struct name_hash * new; + int hash; + + new = (struct name_hash *) e_malloc(sizeof(struct name_hash)); + new->de = de; + new->next = NULL; + hash = name_hash(de->isorec.name); + + /* Now insert into the hash table */ + new->next = name_hash_table[hash]; + name_hash_table[hash] = new; +} + +struct directory_entry * FDECL1(find_file_hash, char *, name) +{ + struct name_hash * nh; + char * p1; + char * p2; + + for(nh = name_hash_table[name_hash(name)]; nh; nh = nh->next) + { + p1 = name; + p2 = nh->de->isorec.name; + + /* + * Look for end of string, or a mismatch. + */ + while(1==1) + { + if( (*p1 == '\0' || *p1 == ';') + || (*p2 == '\0' || *p2 == ';') + || (*p1 != *p2) ) + { + break; + } + p1++; + p2++; + } + + /* + * If we are at the end of both strings, then + * we have a match. + */ + if( (*p1 == '\0' || *p1 == ';') + && (*p2 == '\0' || *p2 == ';') ) + { + return nh->de; + } + } + return NULL; +} + +int FDECL1(delete_file_hash, struct directory_entry *, de){ + struct name_hash * nh, *prev; + int hash; + + prev = NULL; + hash = name_hash(de->isorec.name); + for(nh = name_hash_table[hash]; nh; nh = nh->next) { + if(nh->de == de) break; + prev = nh; + } + if(!nh) return 1; + if(!prev) + name_hash_table[hash] = nh->next; + else + prev->next = nh->next; + free(nh); + return 0; +} + +void flush_file_hash(){ + struct name_hash * nh, *nh1; + int i; + + for(i=0; inext; + free(nh); + nh = nh1; + } + name_hash_table[i] = NULL; + + } +} diff --git a/util/mkisofs/include/fctldefs.h b/util/mkisofs/include/fctldefs.h new file mode 100644 index 000000000..de6b6a394 --- /dev/null +++ b/util/mkisofs/include/fctldefs.h @@ -0,0 +1,57 @@ +/* @(#)fctldefs.h 1.2 98/10/08 Copyright 1996 J. Schilling */ +/* + * Generic header for users of open(), creat() and chmod() + * + * Copyright (c) 1996 J. Schilling + */ +/* + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _FCTLDEFS_H +#define _FCTLDEFS_H + +#ifndef _MCONFIG_H +#include +#endif + +#include +#include + +#ifdef HAVE_FCNTL_H + +# include + +#else /* HAVE_FCNTL_H */ + +# include + +#endif /* HAVE_FCNTL_H */ + +/* + * Do not define more than O_RDONLY / O_WRONLY / O_RDWR + * The values may differ. + */ +#ifndef O_RDONLY +#define O_RDONLY 0 +#endif +#ifndef O_WRONLY +#define O_WRONLY 1 +#endif +#ifndef O_RDWR +#define O_RDWR 2 +#endif + +#endif /* _FCTLDEFS_H */ diff --git a/util/mkisofs/include/mconfig.h b/util/mkisofs/include/mconfig.h new file mode 100644 index 000000000..1891d7ded --- /dev/null +++ b/util/mkisofs/include/mconfig.h @@ -0,0 +1,253 @@ +/* @(#)mconfig.h 1.24 98/12/14 Copyright 1995 J. Schilling */ +/* + * definitions for machine configuration + * + * Copyright (c) 1995 J. Schilling + * + * This file must be included before any other file. + * Use only cpp instructions. + * + * NOTE: SING: (Schily Is Not Gnu) + */ +/* + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MCONFIG_H +#define _MCONFIG_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(unix) || defined(__unix) || defined(__unix__) +# define IS_UNIX +#endif + +#ifdef __MSDOS__ +# define IS_MSDOS +#endif + +#if defined(tos) || defined(__tos) +# define IS_TOS +#endif + +#ifdef THINK_C +# define IS_MAC +#endif + +#if defined(sun) || defined(__sun) || defined(__sun__) +# define IS_SUN +#endif + +#if defined(__CYGWIN32__) +# define IS_GCC_WIN32 +#endif + +/*--------------------------------------------------------------------------*/ +/* + * Some magic that cannot (yet) be figured out with autoconf. + */ + +#ifdef sparc +# ifndef HAVE_LDSTUB +# define HAVE_LDSTUB +# endif +# ifndef HAVE_SCANSTACK +# define HAVE_SCANSTACK +# endif +#endif +#if defined(__i386_) || defined(i386) +# ifndef HAVE_XCHG +# define HAVE_XCHG +# endif +# ifndef HAVE_SCANSTACK +# define HAVE_SCANSTACK +# endif +#endif + +#if defined(SOL2) || defined(SOL2) || defined(S5R4) || defined(__S5R4) \ + || defined(SVR4) +# ifndef __SVR4 +# define __SVR4 +# endif +#endif + +#ifdef __SVR4 +# ifndef SVR4 +# define SVR4 +# endif +#endif + +/* + * SunOS 4.x / SunOS 5.x + */ +#if defined(IS_SUN) +# define HAVE_GETAV0 +#endif + +/* + * AIX + */ +#if defined(_IBMR2) || defined(_AIX) +# define IS_UNIX /* ??? really ??? */ +#endif + +/* + * Silicon Graphics (must be before SVR4) + */ +#if defined(sgi) || defined(__sgi) +# define __NOT_SVR4__ /* Not a real SVR4 implementation */ +#endif + +/* + * Data General + */ +#if defined(__DGUX__) +#ifdef XXXXXXX +# undef HAVE_MTGET_DSREG +# undef HAVE_MTGET_RESID +# undef HAVE_MTGET_FILENO +# undef HAVE_MTGET_BLKNO +#endif +# define mt_type mt_model +# define mt_dsreg mt_status1 +# define mt_erreg mt_status2 + /* + * DGUX hides its flock as dg_flock. + */ +# define HAVE_FLOCK +# define flock dg_flock + /* + * Use the BSD style wait on DGUX to get the resource usages of child + * processes. + */ +# define _BSD_WAIT_FLAVOR +#endif + +/* + * Apple Rhapsody + */ +#if defined(__NeXT__) && defined(__TARGET_OSNAME) && __TARGET_OSNAME == rhapsody +# define HAVE_OSDEF /* prevent later definitions to overwrite current */ +#endif + +/* + * NextStep + */ +#if defined(__NeXT__) && !defined(HAVE_OSDEF) +#define NO_PRINT_OVR +#undef HAVE_USG_STDIO /* + * NeXT Step 3.x uses __flsbuf(unsigned char , FILE *) + * instead of __flsbuf(int, FILE *) + */ +#endif + +/* + * NextStep 3.x has a broken linker that does not allow us to override + * these functions. + */ +#ifndef __OPRINTF__ + +#ifdef NO_PRINT_OVR +# define printf Xprintf +# define fprintf Xfprintf +# define sprintf Xsprintf +#endif + +#endif /* __OPRINTF__ */ + +/*--------------------------------------------------------------------------*/ +/* + * If there is no flock defined by the system, use emulation + * through fcntl record locking. + */ +#ifndef HAVE_FLOCK +#define LOCK_SH 1 /* shared lock */ +#define LOCK_EX 2 /* exclusive lock */ +#define LOCK_NB 4 /* don't block when locking */ +#define LOCK_UN 8 /* unlock */ +#endif + +#include + +/* + * gcc 2.x generally implements the long long type. + */ +#ifdef __GNUC__ +# if __GNUC__ > 1 +# ifndef HAVE_LONGLONG +# define HAVE_LONGLONG +# endif +# endif +#endif + +/* + * Convert to GNU name + */ +#ifdef HAVE_STDC_HEADERS +# ifndef STDC_HEADERS +# define STDC_HEADERS +# endif +#endif +/* + * Convert to SCHILY name + */ +#ifdef STDC_HEADERS +# ifndef HAVE_STDC_HEADERS +# define HAVE_STDC_HEADERS +# endif +#endif + +#ifdef IS_UNIX +# define PATH_DELIM '/' +# define PATH_DELIM_STR "/" +# define far +# define near +#endif + +#ifdef IS_GCC_WIN32 +# define PATH_DELIM '/' +# define PATH_DELIM_STR "/" +# define far +# define near +#endif + +#ifdef IS_MSDOS +# define PATH_DELIM '\\' +# define PATH_DELIM_STR "\\" +#endif + +#ifdef IS_TOS +# define PATH_DELIM '\\' +# define PATH_DELIM_STR "\\" +# define far +# define near +#endif + +#ifdef IS_MAC +# define PATH_DELIM ':' +# define PATH_DELIM_STR ":" +# define far +# define near +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _MCONFIG_H */ diff --git a/util/mkisofs/include/prototyp.h b/util/mkisofs/include/prototyp.h new file mode 100644 index 000000000..c74ae0af8 --- /dev/null +++ b/util/mkisofs/include/prototyp.h @@ -0,0 +1,74 @@ +/* @(#)prototyp.h 1.7 98/10/08 Copyright 1995 J. Schilling */ +/* + * Definitions for dealing with ANSI / KR C-Compilers + * + * Copyright (c) 1995 J. Schilling + */ +/* + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _PROTOTYP_H +#define _PROTOTYP_H + +#ifndef PROTOTYPES + /* + * If this has already been defined, + * someone else knows better than us... + */ +# ifdef __STDC__ +# if __STDC__ /* ANSI C */ +# define PROTOTYPES +# endif +# if defined(sun) && __STDC__ - 0 == 0 /* Sun C */ +# define PROTOTYPES +# endif +# endif +#endif /* PROTOTYPES */ + +/* + * If we have prototypes, we should have stdlib.h string.h stdarg.h + */ +#ifdef PROTOTYPES +#if !(defined(SABER) && defined(sun)) +# ifndef HAVE_STDARG_H +# define HAVE_STDARG_H +# endif +#endif +# ifndef HAVE_STDLIB_H +# define HAVE_STDLIB_H +# endif +# ifndef HAVE_STRING_H +# define HAVE_STRING_H +# endif +# ifndef HAVE_STDC_HEADERS +# define HAVE_STDC_HEADERS +# endif +# ifndef STDC_HEADERS +# define STDC_HEADERS /* GNU name */ +# endif +#endif + +#ifdef NO_PROTOTYPES /* Force not to use prototypes */ +# undef PROTOTYPES +#endif + +#ifdef PROTOTYPES +# define __PR(a) a +#else +# define __PR(a) () +#endif + +#endif /* _PROTOTYP_H */ diff --git a/util/mkisofs/include/statdefs.h b/util/mkisofs/include/statdefs.h new file mode 100644 index 000000000..0e34805ce --- /dev/null +++ b/util/mkisofs/include/statdefs.h @@ -0,0 +1,139 @@ +/* @(#)statdefs.h 1.1 98/11/22 Copyright 1998 J. Schilling */ +/* + * Definitions for stat() file mode + * + * Copyright (c) 1998 J. Schilling + */ +/* + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _STATDEFS_H +#define _STATDEFS_H + +#ifndef _MCONFIG_H +#include +#endif + +#ifdef STAT_MACROS_BROKEN +#undef S_ISFIFO /* Named pipe */ +#undef S_ISCHR /* Character special */ +#undef S_ISMPC /* UNUSED multiplexed c */ +#undef S_ISDIR /* Directory */ +#undef S_ISNAM /* Named file (XENIX) */ +#undef S_ISBLK /* Block special */ +#undef S_ISMPB /* UNUSED multiplexed b */ +#undef S_ISREG /* Regular file */ +#undef S_ISCNT /* Contiguous file */ +#undef S_ISLNK /* Symbolic link */ +#undef S_ISSHAD /* Solaris shadow inode */ +#undef S_ISSOCK /* UNIX domain socket */ +#undef S_ISDOOR /* Solaris DOOR */ +#endif + +#ifndef S_ISFIFO /* Named pipe */ +# ifdef S_IFIFO +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +# else +# define S_ISFIFO(m) (0) +# endif +#endif +#ifndef S_ISCHR /* Character special */ +# ifdef S_IFCHR +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +# else +# define S_ISCHR(m) (0) +# endif +#endif +#ifndef S_ISMPC /* UNUSED multiplexed c */ +# ifdef S_IFMPC +# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +# else +# define S_ISMPC(m) (0) +# endif +#endif +#ifndef S_ISDIR /* Directory */ +# ifdef S_IFDIR +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +# else +# define S_ISDIR(m) (0) +# endif +#endif +#ifndef S_ISNAM /* Named file (XENIX) */ +# ifdef S_IFNAM +# define S_ISNAM(m) (((m) & S_IFMT) == S_IFNAM) +# else +# define S_ISNAM(m) (0) +# endif +#endif +#ifndef S_ISBLK /* Block special */ +# ifdef S_IFBLK +# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +# else +# define S_ISBLK(m) (0) +# endif +#endif +#ifndef S_ISMPB /* UNUSED multiplexed b */ +# ifdef S_IFMPB +# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +# else +# define S_ISMPB(m) (0) +# endif +#endif +#ifndef S_ISREG /* Regular file */ +# ifdef S_IFREG +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +# else +# define S_ISREG(m) (0) +# endif +#endif +#ifndef S_ISCNT /* Contiguous file */ +# ifdef S_IFCNT +# define S_ISCNT(m) (((m) & S_IFMT) == S_IFCNT) +# else +# define S_ISCNT(m) (0) +# endif +#endif +#ifndef S_ISLNK /* Symbolic link */ +# ifdef S_IFLNK +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +# else +# define S_ISLNK(m) (0) +# endif +#endif +#ifndef S_ISSHAD /* Solaris shadow inode */ +# ifdef S_IFSHAD +# define S_ISSHAD(m) (((m) & S_IFMT) == S_IFSHAD) +# else +# define S_ISSHAD(m) (0) +# endif +#endif +#ifndef S_ISSOCK /* UNIX domain socket */ +# ifdef S_IFSOCK +# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +# else +# define S_ISSOCK(m) (0) +# endif +#endif +#ifndef S_ISDOOR /* Solaris DOOR */ +# ifdef S_IFDOOR +# define S_ISDOOR(m) (((m) & S_IFMT) == S_IFDOOR) +# else +# define S_ISDOOR(m) (0) +# endif +#endif + +#endif /* _STATDEFS_H */ + diff --git a/util/mkisofs/iso9660.h b/util/mkisofs/iso9660.h new file mode 100644 index 000000000..3ba0faaac --- /dev/null +++ b/util/mkisofs/iso9660.h @@ -0,0 +1,174 @@ +/* + * Header file iso9660.h - assorted structure definitions and typecasts. + * specific to iso9660 filesystem. + + Written by Eric Youngdale (1993). + + Copyright 1993 Yggdrasil Computing, Incorporated + + Copyright (C) 2009 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, 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 . + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * $Id: iso9660.h,v 1.2 1997/05/17 15:46:44 eric Exp $ + */ + +#ifndef _ISOFS_FS_H +#define _ISOFS_FS_H + +/* + * The isofs filesystem constants/structures + */ + +/* This part borrowed from the bsd386 isofs */ +#define ISODCL(from, to) (to - from + 1) + +struct iso_volume_descriptor { + char type[ISODCL(1,1)]; /* 711 */ + char id[ISODCL(2,6)]; + char version[ISODCL(7,7)]; + char data[ISODCL(8,2048)]; +}; + +/* volume descriptor types */ +#define ISO_VD_PRIMARY 1 +#define ISO_VD_SUPPLEMENTARY 2 /* Used by Joliet */ +#define ISO_VD_END 255 + +#define ISO_STANDARD_ID "CD001" + +#define EL_TORITO_ID "EL TORITO SPECIFICATION" +#define EL_TORITO_ARCH_x86 0 +#define EL_TORITO_ARCH_PPC 1 +#define EL_TORITO_ARCH_MAC 2 +#define EL_TORITO_BOOTABLE 0x88 +#define EL_TORITO_MEDIA_NOEMUL 0 +#define EL_TORITO_MEDIA_12FLOP 1 +#define EL_TORITO_MEDIA_144FLOP 2 +#define EL_TORITO_MEDIA_288FLOP 3 +#define EL_TORITO_MEDIA_HD 4 + +struct iso_primary_descriptor { + char type [ISODCL ( 1, 1)]; /* 711 */ + char id [ISODCL ( 2, 6)]; + char version [ISODCL ( 7, 7)]; /* 711 */ + char unused1 [ISODCL ( 8, 8)]; + char system_id [ISODCL ( 9, 40)]; /* achars */ + char volume_id [ISODCL ( 41, 72)]; /* dchars */ + char unused2 [ISODCL ( 73, 80)]; + char volume_space_size [ISODCL ( 81, 88)]; /* 733 */ + char escape_sequences [ISODCL ( 89, 120)]; + char volume_set_size [ISODCL (121, 124)]; /* 723 */ + char volume_sequence_number [ISODCL (125, 128)]; /* 723 */ + char logical_block_size [ISODCL (129, 132)]; /* 723 */ + char path_table_size [ISODCL (133, 140)]; /* 733 */ + char type_l_path_table [ISODCL (141, 144)]; /* 731 */ + char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */ + char type_m_path_table [ISODCL (149, 152)]; /* 732 */ + char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */ + char root_directory_record [ISODCL (157, 190)]; /* 9.1 */ + char volume_set_id [ISODCL (191, 318)]; /* dchars */ + char publisher_id [ISODCL (319, 446)]; /* achars */ + char preparer_id [ISODCL (447, 574)]; /* achars */ + char application_id [ISODCL (575, 702)]; /* achars */ + char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */ + char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */ + char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */ + char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */ + char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */ + char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */ + char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */ + char file_structure_version [ISODCL (882, 882)]; /* 711 */ + char unused4 [ISODCL (883, 883)]; + char application_data [ISODCL (884, 1395)]; + char unused5 [ISODCL (1396, 2048)]; +}; + +/* El Torito Boot Record Volume Descriptor */ +struct eltorito_boot_descriptor { + char id [ISODCL ( 1, 1)]; /* 711 */ + char id2 [ISODCL ( 2, 6)]; + char version [ISODCL ( 7, 7)]; /* 711 */ + char system_id [ISODCL ( 8, 39)]; + char unused2 [ISODCL ( 40, 71)]; + char bootcat_ptr [ISODCL ( 72 , 75)]; + char unused5 [ISODCL ( 76, 2048)]; +}; + +/* Validation entry for El Torito */ +struct eltorito_validation_entry { + char headerid [ISODCL ( 1, 1)]; /* 711 */ + char arch [ISODCL ( 2, 2)]; + char pad1 [ISODCL ( 3, 4)]; /* 711 */ + char id [ISODCL ( 5, 28)]; + char cksum [ISODCL ( 29, 30)]; + char key1 [ISODCL ( 31, 31)]; + char key2 [ISODCL ( 32, 32)]; +}; + +/* El Torito initial/default entry in boot catalog */ +struct eltorito_defaultboot_entry { + char boot_id [ISODCL ( 1, 1)]; /* 711 */ + char boot_media [ISODCL ( 2, 2)]; + char loadseg [ISODCL ( 3, 4)]; /* 711 */ + char arch [ISODCL ( 5, 5)]; + char pad1 [ISODCL ( 6, 6)]; + char nsect [ISODCL ( 7, 8)]; + char bootoff [ISODCL ( 9, 12)]; + char pad2 [ISODCL ( 13, 32)]; +}; + +/* El Torito boot information table */ +struct eltorito_boot_info +{ + /* Address of Primary Volume Descriptor. */ + char pvd_addr[ISODCL (1, 4)]; + /* Boot file address. */ + char file_addr[ISODCL (5, 8)]; + /* Boot file length. */ + char file_length[ISODCL (9, 12)]; + /* Boot file checksum. */ + char file_checksum[ISODCL (13, 16)]; + char dummy[ISODCL (17, 56)]; +}; + + +/* We use this to help us look up the parent inode numbers. */ + +struct iso_path_table{ + unsigned char name_len[2]; /* 721 */ + char extent[4]; /* 731 */ + char parent[2]; /* 721 */ + char name[1]; +}; + +struct iso_directory_record { + unsigned char length [ISODCL (1, 1)]; /* 711 */ + char ext_attr_length [ISODCL (2, 2)]; /* 711 */ + char extent [ISODCL (3, 10)]; /* 733 */ + char size [ISODCL (11, 18)]; /* 733 */ + char date [ISODCL (19, 25)]; /* 7 by 711 */ + char flags [ISODCL (26, 26)]; + char file_unit_size [ISODCL (27, 27)]; /* 711 */ + char interleave [ISODCL (28, 28)]; /* 711 */ + char volume_sequence_number [ISODCL (29, 32)]; /* 723 */ + unsigned char name_len [ISODCL (33, 33)]; /* 711 */ + char name [34]; /* Not really, but we need something here */ +}; +#endif + + + diff --git a/util/mkisofs/joliet.c b/util/mkisofs/joliet.c new file mode 100644 index 000000000..0f207ec2d --- /dev/null +++ b/util/mkisofs/joliet.c @@ -0,0 +1,1025 @@ +/* + * File joliet.c - handle Win95/WinNT long file/unicode extensions for iso9660. + + Copyright 1997 Eric Youngdale. + + Copyright (C) 2009 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, 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 . + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +static char rcsid[] ="$Id: joliet.c,v 1.14 1999/03/07 17:41:19 eric Exp $"; + + +/* + * Joliet extensions for ISO9660. These are spottily documented by + * Microsoft. In their infinite stupidity, they completely ignored + * the possibility of using an SUSP record with the long filename + * in it, and instead wrote out a duplicate directory tree with the + * long filenames in it. + * + * I am not sure why they did this. One reason is that they get the path + * tables with the long filenames in them. + * + * There are two basic principles to Joliet, and the non-Unicode variant + * known as Romeo. Long filenames seem to be the main one, and the second + * is that the character set and a few other things is substantially relaxed. + * + * The SVD is identical to the PVD, except: + * + * Id is 2, not 1 (indicates SVD). + * escape_sequences contains UCS-2 indicator (levels 1, 2 or 3). + * The root directory record points to a different extent (with different + * size). + * There are different path tables for the two sets of directory trees. + * + * The following fields are recorded in Unicode: + * system_id + * volume_id + * volume_set_id + * publisher_id + * preparer_id + * application_id + * copyright_file_id + * abstract_file_id + * bibliographic_file_id + * + * Unicode strings are always encoded in big-endian format. + * + * In a directory record, everything is the same as with iso9660, except + * that the name is recorded in unicode. The name length is specified in + * total bytes, not in number of unicode characters. + * + * The character set used for the names is different with UCS - the + * restrictions are that the following are not allowed: + * + * Characters (00)(00) through (00)(1f) (control chars) + * (00)(2a) '*' + * (00)(2f) '/' + * (00)(3a) ':' + * (00)(3b) ';' + * (00)(3f) '?' + * (00)(5c) '\' + */ +#include "config.h" +#include "mkisofs.h" +#include "iso9660.h" + +#include +#include +#include + +static unsigned int jpath_table_index; +static struct directory ** jpathlist; +static int next_jpath_index = 1; +static int sort_goof; + +static int generate_joliet_path_tables __PR((void)); +static int DECL(joliet_sort_directory, (struct directory_entry ** sort_dir)); +static void DECL(assign_joliet_directory_addresses, (struct directory * node)); +static int jroot_gen __PR((void)); + +/* + * Function: convert_to_unicode + * + * Purpose: Perform a 1/2 assed unicode conversion on a text + * string. + * + * Notes: + */ +static void FDECL3(convert_to_unicode, unsigned char *, buffer, int, size, char *, source ) +{ + unsigned char * tmpbuf; + int i; + int j; + + /* + * If we get a NULL pointer for the source, it means we have an inplace + * copy, and we need to make a temporary working copy first. + */ + if( source == NULL ) + { + tmpbuf = (uint8_t *) e_malloc(size); + memcpy( tmpbuf, buffer, size); + } + else + { + tmpbuf = (uint8_t *)source; + } + + /* + * Now start copying characters. If the size was specified to be 0, then + * assume the input was 0 terminated. + */ + j = 0; + for(i=0; i < size ; i += 2, j++) + { + buffer[i] = 0; + /* + * JS integrated from: Achim_Kaiser@t-online.de + * + * Let all valid unicode characters pass through (assuming ISO-8859-1). + * Others are set to '_' . + */ + if( tmpbuf[j] != 0 && + (tmpbuf[j] <= 0x1f || (tmpbuf[j] >= 0x7F && tmpbuf[j] <= 0xA0)) ) + { + buffer[i+1] = '_'; + } + else + { + switch(tmpbuf[j]) + { + case '*': + case '/': + case ':': + case ';': + case '?': + case '\\': + /* + * Even Joliet has some standards as to what is allowed in a pathname. + * Pretty tame in comparison to what DOS restricts you to. + */ + buffer[i+1] = '_'; + break; + default: + buffer[i+1] = tmpbuf[j]; + break; + } + } + } + + if( source == NULL ) + { + free(tmpbuf); + } +} + +/* + * Function: joliet_strlen + * + * Purpose: Return length in bytes of string after conversion to unicode. + * + * Notes: This is provided mainly as a convenience so that when more intelligent + * Unicode conversion for either Multibyte or 8-bit codes is available that + * we can easily adapt. + */ +static int FDECL1(joliet_strlen, const char *, string) +{ + int rtn; + + rtn = strlen(string) << 1; + + /* + * We do clamp the maximum length of a Joliet string to be the + * maximum path size. This helps to ensure that we don't completely + * bolix things up with very long paths. The Joliet specs say + * that the maximum length is 128 bytes, or 64 unicode characters. + */ + if( rtn > 0x80) + { + rtn = 0x80; + } + return rtn; +} + +/* + * Function: get_joliet_vol_desc + * + * Purpose: generate a Joliet compatible volume desc. + * + * Notes: Assume that we have the non-joliet vol desc + * already present in the buffer. Just modifiy the + * appropriate fields. + */ +static void FDECL1(get_joliet_vol_desc, struct iso_primary_descriptor *, jvol_desc) +{ + jvol_desc->type[0] = ISO_VD_SUPPLEMENTARY; + + /* + * For now, always do Unicode level 3. I don't really know what 1 and 2 + * are - perhaps a more limited Unicode set. + * + * FIXME(eric) - how does Romeo fit in here? As mkisofs just + * "expands" 8 bit character codes to 16 bits and does nothing + * special with the Unicode characters, therefore shouldn't mkisofs + * really be stating that it's using UCS-2 Level 1, not Level 3 for + * the Joliet directory tree. + */ + strcpy(jvol_desc->escape_sequences, "%/@"); + + /* + * Until we have Unicode path tables, leave these unset. + */ + set_733((char *) jvol_desc->path_table_size, jpath_table_size); + set_731(jvol_desc->type_l_path_table, jpath_table[0]); + set_731(jvol_desc->opt_type_l_path_table, jpath_table[1]); + set_732(jvol_desc->type_m_path_table, jpath_table[2]); + set_732(jvol_desc->opt_type_m_path_table, jpath_table[3]); + + /* + * Set this one up. + */ + memcpy(jvol_desc->root_directory_record, &jroot_record, + sizeof(struct iso_directory_record)); + + /* + * Finally, we have a bunch of strings to convert to Unicode. + * FIXME(eric) - I don't know how to do this in general, so we will + * just be really lazy and do a char -> short conversion. We probably + * will want to filter any characters >= 0x80. + */ + convert_to_unicode((uint8_t *)jvol_desc->system_id, sizeof(jvol_desc->system_id), NULL); + convert_to_unicode((uint8_t *)jvol_desc->volume_id, sizeof(jvol_desc->volume_id), NULL); + convert_to_unicode((uint8_t *)jvol_desc->volume_set_id, sizeof(jvol_desc->volume_set_id), NULL); + convert_to_unicode((uint8_t *)jvol_desc->publisher_id, sizeof(jvol_desc->publisher_id), NULL); + convert_to_unicode((uint8_t *)jvol_desc->preparer_id, sizeof(jvol_desc->preparer_id), NULL); + convert_to_unicode((uint8_t *)jvol_desc->application_id, sizeof(jvol_desc->application_id), NULL); + convert_to_unicode((uint8_t *)jvol_desc->copyright_file_id, sizeof(jvol_desc->copyright_file_id), NULL); + convert_to_unicode((uint8_t *)jvol_desc->abstract_file_id, sizeof(jvol_desc->abstract_file_id), NULL); + convert_to_unicode((uint8_t *)jvol_desc->bibliographic_file_id, sizeof(jvol_desc->bibliographic_file_id), NULL); + + +} + +static void FDECL1(assign_joliet_directory_addresses, struct directory *, node) +{ + int dir_size; + struct directory * dpnt; + + dpnt = node; + + while (dpnt) + { + if( (dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) == 0 ) + { + /* + * If we already have an extent for this (i.e. it came from + * a multisession disc), then don't reassign a new extent. + */ + dpnt->jpath_index = next_jpath_index++; + if( dpnt->jextent == 0 ) + { + dpnt->jextent = last_extent; + dir_size = (dpnt->jsize + (SECTOR_SIZE - 1)) >> 11; + last_extent += dir_size; + } + } + + /* skip if hidden - but not for the rr_moved dir */ + if(dpnt->subdir && (!(dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) || dpnt == reloc_dir)) + { + assign_joliet_directory_addresses(dpnt->subdir); + } + dpnt = dpnt->next; + } +} + +static +void FDECL1(build_jpathlist, struct directory *, node) +{ + struct directory * dpnt; + + dpnt = node; + + while (dpnt) + + { + if( (dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) == 0 ) + { + jpathlist[dpnt->jpath_index] = dpnt; + } + if(dpnt->subdir) build_jpathlist(dpnt->subdir); + dpnt = dpnt->next; + } +} /* build_jpathlist(... */ + +static int FDECL2(joliet_compare_paths, void const *, r, void const *, l) +{ + struct directory const *ll = *(struct directory * const *)l; + struct directory const *rr = *(struct directory * const *)r; + int rparent, lparent; + + rparent = rr->parent->jpath_index; + lparent = ll->parent->jpath_index; + if( rr->parent == reloc_dir ) + { + rparent = rr->self->parent_rec->filedir->jpath_index; + } + + if( ll->parent == reloc_dir ) + { + lparent = ll->self->parent_rec->filedir->jpath_index; + } + + if (rparent < lparent) + { + return -1; + } + + if (rparent > lparent) + { + return 1; + } + + return strcmp(rr->self->name, ll->self->name); + +} /* compare_paths(... */ + +static int generate_joliet_path_tables() +{ + struct directory_entry * de; + struct directory * dpnt; + int fix; + int j; + int namelen; + char * npnt; + char * npnt1; + int tablesize; + + /* + * First allocate memory for the tables and initialize the memory + */ + tablesize = jpath_blocks << 11; + jpath_table_m = (char *) e_malloc(tablesize); + jpath_table_l = (char *) e_malloc(tablesize); + memset(jpath_table_l, 0, tablesize); + memset(jpath_table_m, 0, tablesize); + + if( next_jpath_index > 0xffff ) + { + fprintf(stderr, "Unable to generate sane path tables - too many directories (%d)\n", + next_jpath_index); + exit(1); + } + /* + * Now start filling in the path tables. Start with root directory + */ + jpath_table_index = 0; + jpathlist = (struct directory **) e_malloc(sizeof(struct directory *) + * next_jpath_index); + memset(jpathlist, 0, sizeof(struct directory *) * next_jpath_index); + build_jpathlist(root); + + do + { + fix = 0; +#ifdef __STDC__ + qsort(&jpathlist[1], next_jpath_index-1, sizeof(struct directory *), + (int (*)(const void *, const void *))joliet_compare_paths); +#else + qsort(&jpathlist[1], next_jpath_index-1, sizeof(struct directory *), + joliet_compare_paths); +#endif + + for(j=1; jjpath_index != j) + { + jpathlist[j]->jpath_index = j; + fix++; + } + } + } while(fix); + + for(j=1; jde_name; + + npnt1 = strrchr(npnt, PATH_SEPARATOR); + if(npnt1) + { + npnt = npnt1 + 1; + } + + de = dpnt->self; + if(!de) + { + fprintf(stderr,"Fatal goof - directory has amnesia\n"); + exit(1); + } + + namelen = joliet_strlen(de->name); + + if( dpnt == root ) + { + jpath_table_l[jpath_table_index] = 1; + jpath_table_m[jpath_table_index] = 1; + } + else + { + jpath_table_l[jpath_table_index] = namelen; + jpath_table_m[jpath_table_index] = namelen; + } + jpath_table_index += 2; + + set_731(jpath_table_l + jpath_table_index, dpnt->jextent); + set_732(jpath_table_m + jpath_table_index, dpnt->jextent); + jpath_table_index += 4; + + if( dpnt->parent != reloc_dir ) + { + set_721(jpath_table_l + jpath_table_index, + dpnt->parent->jpath_index); + set_722(jpath_table_m + jpath_table_index, + dpnt->parent->jpath_index); + } + else + { + set_721(jpath_table_l + jpath_table_index, + dpnt->self->parent_rec->filedir->jpath_index); + set_722(jpath_table_m + jpath_table_index, + dpnt->self->parent_rec->filedir->jpath_index); + } + + jpath_table_index += 2; + + /* + * The root directory is still represented in non-unicode fashion. + */ + if( dpnt == root ) + { + jpath_table_l[jpath_table_index] = 0; + jpath_table_m[jpath_table_index] = 0; + jpath_table_index ++; + } + else + { + convert_to_unicode((uint8_t *)jpath_table_l + jpath_table_index, + namelen, de->name); + convert_to_unicode((uint8_t *)jpath_table_m + jpath_table_index, + namelen, de->name); + jpath_table_index += namelen; + } + + if(jpath_table_index & 1) + { + jpath_table_index++; /* For odd lengths we pad */ + } + } + + free(jpathlist); + if(jpath_table_index != jpath_table_size) + { + fprintf(stderr,"Joliet path table lengths do not match %d %d\n", + jpath_table_index, + jpath_table_size); + } + return 0; +} /* generate_path_tables(... */ + +static void FDECL2(generate_one_joliet_directory, struct directory *, dpnt, FILE *, outfile) +{ + unsigned int dir_index; + char * directory_buffer; + int new_reclen; + struct directory_entry * s_entry; + struct directory_entry * s_entry1; + struct iso_directory_record jrec; + unsigned int total_size; + int cvt_len; + struct directory * finddir; + + total_size = (dpnt->jsize + (SECTOR_SIZE - 1)) & ~(SECTOR_SIZE - 1); + directory_buffer = (char *) e_malloc(total_size); + memset(directory_buffer, 0, total_size); + dir_index = 0; + + s_entry = dpnt->jcontents; + while(s_entry) + { + if(s_entry->de_flags & INHIBIT_JOLIET_ENTRY) { + s_entry = s_entry->jnext; + continue; + } + + /* + * If this entry was a directory that was relocated, we have a bit + * of trouble here. We need to dig out the real thing and put it + * back here. In the Joliet tree, there is no relocated rock + * ridge, as there are no depth limits to a directory tree. + */ + if( (s_entry->de_flags & RELOCATED_DIRECTORY) != 0 ) + { + for(s_entry1 = reloc_dir->contents; s_entry1; s_entry1 = s_entry1->next) + { + if( s_entry1->parent_rec == s_entry ) + { + break; + } + } + if( s_entry1 == NULL ) + { + /* + * We got trouble. + */ + fprintf(stderr, "Unable to locate relocated directory\n"); + exit(1); + } + } + else + { + s_entry1 = s_entry; + } + + /* + * We do not allow directory entries to cross sector boundaries. + * Simply pad, and then start the next entry at the next sector + */ + new_reclen = s_entry1->jreclen; + if( (dir_index & (SECTOR_SIZE - 1)) + new_reclen >= SECTOR_SIZE ) + { + dir_index = (dir_index + (SECTOR_SIZE - 1)) & + ~(SECTOR_SIZE - 1); + } + + memcpy(&jrec, &s_entry1->isorec, sizeof(struct iso_directory_record) - + sizeof(s_entry1->isorec.name)); + + cvt_len = joliet_strlen(s_entry1->name); + + /* + * Fix the record length - this was the non-Joliet version we + * were seeing. + */ + jrec.name_len[0] = cvt_len; + jrec.length[0] = s_entry1->jreclen; + + /* + * If this is a directory, fix the correct size and extent + * number. + */ + if( (jrec.flags[0] & 2) != 0 ) + { + if(strcmp(s_entry1->name,".") == 0) + { + jrec.name_len[0] = 1; + set_733((char *) jrec.extent, dpnt->jextent); + set_733((char *) jrec.size, ROUND_UP(dpnt->jsize)); + } + else if(strcmp(s_entry1->name,"..") == 0) + { + jrec.name_len[0] = 1; + if( dpnt->parent == reloc_dir ) + { + set_733((char *) jrec.extent, dpnt->self->parent_rec->filedir->jextent); + set_733((char *) jrec.size, ROUND_UP(dpnt->self->parent_rec->filedir->jsize)); + } + else + + { + set_733((char *) jrec.extent, dpnt->parent->jextent); + set_733((char *) jrec.size, ROUND_UP(dpnt->parent->jsize)); + } + } + else + { + if( (s_entry->de_flags & RELOCATED_DIRECTORY) != 0 ) + { + finddir = reloc_dir->subdir; + } + else + { + finddir = dpnt->subdir; + } + while(1==1) + { + if(finddir->self == s_entry1) break; + finddir = finddir->next; + if(!finddir) + { + fprintf(stderr,"Fatal goof - unable to find directory location\n"); exit(1); + } + } + set_733((char *) jrec.extent, finddir->jextent); + set_733((char *) jrec.size, ROUND_UP(finddir->jsize)); + } + } + + memcpy(directory_buffer + dir_index, &jrec, + sizeof(struct iso_directory_record) - + sizeof(s_entry1->isorec.name)); + + + dir_index += sizeof(struct iso_directory_record) - + sizeof (s_entry1->isorec.name); + + /* + * Finally dump the Unicode version of the filename. + * Note - . and .. are the same as with non-Joliet discs. + */ + if( (jrec.flags[0] & 2) != 0 + && strcmp(s_entry1->name, ".") == 0 ) + { + directory_buffer[dir_index++] = 0; + } + else if( (jrec.flags[0] & 2) != 0 + && strcmp(s_entry1->name, "..") == 0 ) + { + directory_buffer[dir_index++] = 1; + } + else + { + convert_to_unicode((uint8_t *)directory_buffer + dir_index, + cvt_len, + s_entry1->name); + dir_index += cvt_len; + } + + if(dir_index & 1) + { + directory_buffer[dir_index++] = 0; + } + + s_entry = s_entry->jnext; + } + + if(dpnt->jsize != dir_index) + { + fprintf(stderr,"Unexpected joliet directory length %d %d %s\n",dpnt->jsize, + dir_index, dpnt->de_name); + } + + xfwrite(directory_buffer, 1, total_size, outfile); + last_extent_written += total_size >> 11; + free(directory_buffer); +} /* generate_one_joliet_directory(... */ + +static int FDECL1(joliet_sort_n_finish, struct directory *, this_dir) +{ + struct directory_entry * s_entry; + int status = 0; + + /* don't want to skip this directory if it's the reloc_dir at the moment */ + if(this_dir != reloc_dir && this_dir->dir_flags & INHIBIT_JOLIET_ENTRY) + { + return 0; + } + + for(s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) + { + /* skip hidden entries */ + if( (s_entry->de_flags & INHIBIT_JOLIET_ENTRY) != 0 ) + { + continue; + } + + /* + * First update the path table sizes for directories. + * + * Finally, set the length of the directory entry if Joliet is used. + * The name is longer, but no Rock Ridge is ever used here, so + * depending upon the options the entry size might turn out to be about + * the same. The Unicode name is always a multiple of 2 bytes, so + * we always add 1 to make it an even number. + */ + if(s_entry->isorec.flags[0] == 2) + { + if (strcmp(s_entry->name,".") && strcmp(s_entry->name,"..")) + { + jpath_table_size += joliet_strlen(s_entry->name) + sizeof(struct iso_path_table) - 1; + if (jpath_table_size & 1) + { + jpath_table_size++; + } + } + else + { + if (this_dir == root && strlen(s_entry->name) == 1) + { + jpath_table_size += sizeof(struct iso_path_table); + if (jpath_table_size & 1) jpath_table_size++; + } + } + } + + if (strcmp(s_entry->name,".") && strcmp(s_entry->name,"..")) + { + s_entry->jreclen = sizeof(struct iso_directory_record) + - sizeof(s_entry->isorec.name) + + joliet_strlen(s_entry->name) + + 1; + } + else + { + /* + * Special - for '.' and '..' we generate the same records we + * did for non-Joliet discs. + */ + s_entry->jreclen = sizeof(struct iso_directory_record) + - sizeof(s_entry->isorec.name) + + 1; + } + + + } + + if( (this_dir->dir_flags & INHIBIT_JOLIET_ENTRY) != 0 ) + { + return 0; + } + + this_dir->jcontents = this_dir->contents; + status = joliet_sort_directory(&this_dir->jcontents); + + /* + * Now go through the directory and figure out how large this one will be. + * Do not split a directory entry across a sector boundary + */ + s_entry = this_dir->jcontents; +/* + * XXX Is it ok to comment this out? + */ +/*XXX JS this_dir->ce_bytes = 0;*/ + for(s_entry = this_dir->jcontents; s_entry; s_entry = s_entry->jnext) + { + int jreclen; + + if( (s_entry->de_flags & INHIBIT_JOLIET_ENTRY) != 0 ) + { + continue; + } + + jreclen = s_entry->jreclen; + + if ((this_dir->jsize & (SECTOR_SIZE - 1)) + jreclen >= SECTOR_SIZE) + { + this_dir->jsize = (this_dir->jsize + (SECTOR_SIZE - 1)) & + ~(SECTOR_SIZE - 1); + } + this_dir->jsize += jreclen; + } + return status; +} + +/* + * Similar to the iso9660 case, except here we perform a full sort based upon the + * regular name of the file, not the 8.3 version. + */ +static int FDECL2(joliet_compare_dirs, const void *, rr, const void *, ll) +{ + char * rpnt, *lpnt; + struct directory_entry ** r, **l; + + r = (struct directory_entry **) rr; + l = (struct directory_entry **) ll; + rpnt = (*r)->name; + lpnt = (*l)->name; + + /* + * If the entries are the same, this is an error. + */ + if( strcmp(rpnt, lpnt) == 0 ) + { + sort_goof++; + } + + /* + * Put the '.' and '..' entries on the head of the sorted list. + * For normal ASCII, this always happens to be the case, but out of + * band characters cause this not to be the case sometimes. + */ + if( strcmp(rpnt, ".") == 0 ) return -1; + if( strcmp(lpnt, ".") == 0 ) return 1; + + if( strcmp(rpnt, "..") == 0 ) return -1; + if( strcmp(lpnt, "..") == 0 ) return 1; + + while(*rpnt && *lpnt) + { + if(*rpnt == ';' && *lpnt != ';') return -1; + if(*rpnt != ';' && *lpnt == ';') return 1; + + if(*rpnt == ';' && *lpnt == ';') return 0; + + /* + * Extensions are not special here. Don't treat the dot as something that + * must be bumped to the start of the list. + */ +#if 0 + if(*rpnt == '.' && *lpnt != '.') return -1; + if(*rpnt != '.' && *lpnt == '.') return 1; +#endif + + if(*rpnt < *lpnt) return -1; + if(*rpnt > *lpnt) return 1; + rpnt++; lpnt++; + } + if(*rpnt) return 1; + if(*lpnt) return -1; + return 0; +} + + +/* + * Function: sort_directory + * + * Purpose: Sort the directory in the appropriate ISO9660 + * order. + * + * Notes: Returns 0 if OK, returns > 0 if an error occurred. + */ +static int FDECL1(joliet_sort_directory, struct directory_entry **, sort_dir) +{ + int dcount = 0; + int i; + struct directory_entry * s_entry; + struct directory_entry ** sortlist; + + s_entry = *sort_dir; + while(s_entry) + { + /* skip hidden entries */ + if (!(s_entry->de_flags & INHIBIT_JOLIET_ENTRY)) + dcount++; + s_entry = s_entry->next; + } + + /* + * OK, now we know how many there are. Build a vector for sorting. + */ + sortlist = (struct directory_entry **) + e_malloc(sizeof(struct directory_entry *) * dcount); + + dcount = 0; + s_entry = *sort_dir; + while(s_entry) + { + /* skip hidden entries */ + if (!(s_entry->de_flags & INHIBIT_JOLIET_ENTRY)) { + sortlist[dcount] = s_entry; + dcount++; + } + s_entry = s_entry->next; + } + + sort_goof = 0; +#ifdef __STDC__ + qsort(sortlist, dcount, sizeof(struct directory_entry *), + (int (*)(const void *, const void *))joliet_compare_dirs); +#else + qsort(sortlist, dcount, sizeof(struct directory_entry *), + joliet_compare_dirs); +#endif + + /* + * Now reassemble the linked list in the proper sorted order + */ + for(i=0; ijnext = sortlist[i+1]; + } + + sortlist[dcount-1]->jnext = NULL; + *sort_dir = sortlist[0]; + + free(sortlist); + return sort_goof; +} + +int FDECL1(joliet_sort_tree, struct directory *, node) +{ + struct directory * dpnt; + int ret = 0; + + dpnt = node; + + while (dpnt){ + ret = joliet_sort_n_finish(dpnt); + if( ret ) + { + break; + } + if(dpnt->subdir) ret = joliet_sort_tree(dpnt->subdir); + if( ret ) + { + break; + } + dpnt = dpnt->next; + } + return ret; +} + +static void FDECL2(generate_joliet_directories, struct directory *, node, FILE*, outfile){ + struct directory * dpnt; + + dpnt = node; + + while (dpnt) + { + if( (dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) == 0 ) + { + /* + * In theory we should never reuse a directory, so this doesn't + * make much sense. + */ + if( dpnt->jextent > session_start ) + { + generate_one_joliet_directory(dpnt, outfile); + } + } + /* skip if hidden - but not for the rr_moved dir */ + if(dpnt->subdir && (!(dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) || dpnt == reloc_dir)) + generate_joliet_directories(dpnt->subdir, outfile); + dpnt = dpnt->next; + } +} + + +/* + * Function to write the EVD for the disc. + */ +static int FDECL1(jpathtab_write, FILE *, outfile) +{ + /* + * Next we write the path tables + */ + xfwrite(jpath_table_l, 1, jpath_blocks << 11, outfile); + xfwrite(jpath_table_m, 1, jpath_blocks << 11, outfile); + last_extent_written += 2*jpath_blocks; + free(jpath_table_l); + free(jpath_table_m); + jpath_table_l = NULL; + jpath_table_m = NULL; + return 0; +} + +static int FDECL1(jdirtree_size, int, starting_extent) +{ + assign_joliet_directory_addresses(root); + return 0; +} + +static int jroot_gen() +{ + jroot_record.length[0] = 1 + sizeof(struct iso_directory_record) + - sizeof(jroot_record.name); + jroot_record.ext_attr_length[0] = 0; + set_733((char *) jroot_record.extent, root->jextent); + set_733((char *) jroot_record.size, ROUND_UP(root->jsize)); + iso9660_date(jroot_record.date, root_statbuf.st_mtime); + jroot_record.flags[0] = 2; + jroot_record.file_unit_size[0] = 0; + jroot_record.interleave[0] = 0; + set_723(jroot_record.volume_sequence_number, volume_sequence_number); + jroot_record.name_len[0] = 1; + return 0; +} + +static int FDECL1(jdirtree_write, FILE *, outfile) +{ + generate_joliet_directories(root, outfile); + return 0; +} + +/* + * Function to write the EVD for the disc. + */ +static int FDECL1(jvd_write, FILE *, outfile) +{ + struct iso_primary_descriptor jvol_desc; + + /* + * Next we write out the boot volume descriptor for the disc + */ + jvol_desc = vol_desc; + get_joliet_vol_desc(&jvol_desc); + xfwrite(&jvol_desc, 1, 2048, outfile); + last_extent_written ++; + return 0; +} + +/* + * Functions to describe padding block at the start of the disc. + */ +static int FDECL1(jpathtab_size, int, starting_extent) +{ + jpath_table[0] = starting_extent; + jpath_table[1] = 0; + jpath_table[2] = jpath_table[0] + jpath_blocks; + jpath_table[3] = 0; + + last_extent += 2*jpath_blocks; + return 0; +} + +struct output_fragment joliet_desc = {NULL, oneblock_size, jroot_gen,jvd_write}; +struct output_fragment jpathtable_desc= {NULL, jpathtab_size, generate_joliet_path_tables, jpathtab_write}; +struct output_fragment jdirtree_desc = {NULL, jdirtree_size, NULL, jdirtree_write}; diff --git a/util/mkisofs/match.c b/util/mkisofs/match.c new file mode 100644 index 000000000..8590be470 --- /dev/null +++ b/util/mkisofs/match.c @@ -0,0 +1,147 @@ +/* + * 27-Mar-96: Jan-Piet Mens + * added 'match' option (-m) to specify regular expressions NOT to be included + * in the CD image. + */ + +static char rcsid[] ="$Id: match.c,v 1.3 1999/03/02 03:41:25 eric Exp $"; + +#include "config.h" +#include +#include +#ifndef VMS +#ifdef HAVE_MALLOC_H +#include +#else +#include +#endif +#endif +#include +#include "match.h" + +#define MAXMATCH 1000 +static char *mat[MAXMATCH]; + +void add_match(fn) +char * fn; +{ + register int i; + + for (i=0; mat[i] && i excluded filenmae */ + } + } + return 0; /* not found -> not excluded */ +} + +/* ISO9660/RR hide */ + +static char *i_mat[MAXMATCH]; + +void i_add_match(fn) +char * fn; +{ + register int i; + + for (i=0; i_mat[i] && i excluded filenmae */ + } + } + return 0; /* not found -> not excluded */ +} + +int i_ishidden() +{ + return (i_mat[0] != NULL); +} + +/* Joliet hide */ + +static char *j_mat[MAXMATCH]; + +void j_add_match(fn) +char * fn; +{ + register int i; + + for (i=0; j_mat[i] && i excluded filenmae */ + } + } + return 0; /* not found -> not excluded */ +} + +int j_ishidden() +{ + return (j_mat[0] != NULL); +} + diff --git a/util/mkisofs/match.h b/util/mkisofs/match.h new file mode 100644 index 000000000..7367dd211 --- /dev/null +++ b/util/mkisofs/match.h @@ -0,0 +1,22 @@ +/* + * 27th March 1996. Added by Jan-Piet Mens for matching regular expressions + * in paths. + * + */ + +/* + * $Id: match.h,v 1.2 1999/03/02 03:41:25 eric Exp $ + */ + +#include "fnmatch.h" + +void add_match __PR((char *fn)); +int matches __PR((char *fn)); + +void i_add_match __PR((char *fn)); +int i_matches __PR((char *fn)); +int i_ishidden __PR((void)); + +void j_add_match __PR((char *fn)); +int j_matches __PR((char *fn)); +int j_ishidden __PR((void)); diff --git a/util/mkisofs/mkisofs.c b/util/mkisofs/mkisofs.c new file mode 100644 index 000000000..0c89a2d8c --- /dev/null +++ b/util/mkisofs/mkisofs.c @@ -0,0 +1,1408 @@ +/* + * Program mkisofs.c - generate iso9660 filesystem based upon directory + * tree on hard disk. + + Written by Eric Youngdale (1993). + + Copyright 1993 Yggdrasil Computing, Incorporated + + Copyright (C) 2009 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, 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 . + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +static char rcsid[] ="$Id: mkisofs.c,v 1.32 1999/03/07 21:48:49 eric Exp $"; + +#include +#include "config.h" +#include "mkisofs.h" +#include "match.h" + +#ifdef linux +#include +#else +#include "getopt.h" +#endif + +#include "iso9660.h" +#include + +#ifndef VMS +#include +#else +#include +#include "vms.h" +#endif + +#include +#include + +#ifndef VMS +#ifdef HAVE_UNISTD_H +#include +#endif +#endif +#include + +#include "exclude.h" + +#ifdef __NetBSD__ +#include +#include +#endif + +struct directory * root = NULL; + +static char version_string[] = "mkisofs 1.12b5"; + +char * outfile; +FILE * discimage; +uint64_t next_extent = 0; +uint64_t last_extent = 0; +uint64_t session_start = 0; +unsigned int path_table_size = 0; +unsigned int path_table[4] = {0,}; +unsigned int path_blocks = 0; + + +unsigned int jpath_table_size = 0; +unsigned int jpath_table[4] = {0,}; +unsigned int jpath_blocks = 0; + +struct iso_directory_record root_record; +struct iso_directory_record jroot_record; + +char * extension_record = NULL; +int extension_record_extent = 0; +int extension_record_size = 0; + +/* These variables are associated with command line options */ +int use_eltorito = 0; +int use_eltorito_emul_floppy = 0; +int use_boot_info_table = 0; +int use_RockRidge = 0; +int use_Joliet = 0; +int verbose = 1; +int all_files = 0; +int follow_links = 0; +int rationalize = 0; +int generate_tables = 0; +int print_size = 0; +int split_output = 0; +char * preparer = PREPARER_DEFAULT; +char * publisher = PUBLISHER_DEFAULT; +char * appid = APPID_DEFAULT; +char * copyright = COPYRIGHT_DEFAULT; +char * biblio = BIBLIO_DEFAULT; +char * abstract = ABSTRACT_DEFAULT; +char * volset_id = VOLSET_ID_DEFAULT; +char * volume_id = VOLUME_ID_DEFAULT; +char * system_id = SYSTEM_ID_DEFAULT; +char * boot_catalog = BOOT_CATALOG_DEFAULT; +char * boot_image = BOOT_IMAGE_DEFAULT; +int volume_set_size = 1; +int volume_sequence_number = 1; + +int omit_period = 0; /* Violates iso9660, but these are a pain */ +int transparent_compression = 0; /* So far only works with linux */ +int omit_version_number = 0; /* May violate iso9660, but noone uses vers*/ +unsigned int RR_relocation_depth = 6; /* Violates iso9660, but most systems work */ +int full_iso9660_filenames = 0; /* Used with Amiga. Disc will not work with + DOS */ +int allow_leading_dots = 0; /* DOS cannot read names with leading dots */ +int split_SL_component = 1; /* circumvent a bug in the SunOS driver */ +int split_SL_field = 1; /* circumvent a bug in the SunOS */ + +struct rcopts{ + char * tag; + char ** variable; +}; + +struct rcopts rcopt[] = { + {"PREP", &preparer}, + {"PUBL", &publisher}, + {"APPI", &appid}, + {"COPY", ©right}, + {"BIBL", &biblio}, + {"ABST", &abstract}, + {"VOLS", &volset_id}, + {"VOLI", &volume_id}, + {"SYSI", &system_id}, + {NULL, NULL} +}; + +/* + * In case it isn't obvious, the option handling code was ripped off from GNU-ld. + */ +struct ld_option +{ + /* The long option information. */ + struct option opt; + /* The short option with the same meaning ('\0' if none). */ + char shortopt; + /* The name of the argument (NULL if none). */ + const char *arg; + /* The documentation string. If this is NULL, this is a synonym for + the previous option. */ + const char *doc; + enum + { + /* Use one dash before long option name. */ + ONE_DASH, + /* Use two dashes before long option name. */ + TWO_DASHES, + /* Don't mention this option in --help output. */ + NO_HELP + } control; +}; + +/* Codes used for the long options with no short synonyms. 150 isn't + special; it's just an arbitrary non-ASCII char value. */ +#define OPTION_HELP 150 +#define OPTION_QUIET 151 +#define OPTION_NOSPLIT_SL_COMPONENT 152 +#define OPTION_NOSPLIT_SL_FIELD 153 +#define OPTION_PRINT_SIZE 154 +#define OPTION_SPLIT_OUTPUT 155 +#define OPTION_ABSTRACT 156 +#define OPTION_BIBLIO 157 +#define OPTION_COPYRIGHT 158 +#define OPTION_SYSID 159 +#define OPTION_VOLSET 160 +#define OPTION_VOLSET_SIZE 161 +#define OPTION_VOLSET_SEQ_NUM 162 +#define OPTION_I_HIDE 163 +#define OPTION_J_HIDE 164 +#define OPTION_LOG_FILE 165 + +#define OPTION_CREAT_DATE 166 +#define OPTION_MODIF_DATE 167 +#define OPTION_EXPIR_DATE 168 +#define OPTION_EFFEC_DATE 169 + +#define OPTION_BOOT_INFO_TABLE 170 +#define OPTION_NO_EMUL_BOOT 171 +#define OPTION_ELTORITO_EMUL_FLOPPY 172 + +static const struct ld_option ld_options[] = +{ + { {"all-files", no_argument, NULL, 'a'}, + 'a', NULL, "Process all files (don't skip backup files)", ONE_DASH }, + { {"abstract", required_argument, NULL, OPTION_ABSTRACT}, + '\0', "FILE", "Set Abstract filename" , ONE_DASH }, + { {"appid", required_argument, NULL, 'A'}, + 'A', "ID", "Set Application ID" , ONE_DASH }, + { {"biblio", required_argument, NULL, OPTION_BIBLIO}, + '\0', "FILE", "Set Bibliographic filename" , ONE_DASH }, + { {"copyright", required_argument, NULL, OPTION_COPYRIGHT}, + '\0', "FILE", "Set Copyright filename" , ONE_DASH }, + { {"eltorito-boot", required_argument, NULL, 'b'}, + 'b', "FILE", "Set El Torito boot image name" , ONE_DASH }, + { {"eltorito-catalog", required_argument, NULL, 'c'}, + 'c', "FILE", "Set El Torito boot catalog name" , ONE_DASH }, + { {"boot-info-table", no_argument, NULL, OPTION_BOOT_INFO_TABLE }, + '\0', NULL, "Patch Boot Info Table in El Torito boot image" , ONE_DASH }, + { {"no-emul-boot", no_argument, NULL, OPTION_NO_EMUL_BOOT }, + '\0', NULL, "Dummy option for backward compatibility" , ONE_DASH }, + { {"eltorito-emul-floppy", no_argument, NULL, OPTION_ELTORITO_EMUL_FLOPPY }, + '\0', NULL, "Enable floppy drive emulation for El Torito" , TWO_DASHES }, + { {"cdwrite-params", required_argument, NULL, 'C'}, + 'C', "PARAMS", "Magic parameters from cdrecord" , ONE_DASH }, + { {"omit-period", no_argument, NULL, 'd'}, + 'd', NULL, "Omit trailing periods from filenames", ONE_DASH }, + { {"disable-deep-relocation", no_argument, NULL, 'D'}, + 'D', NULL, "Disable deep directory relocation", ONE_DASH }, + { {"follow-links", no_argument, NULL, 'f'}, + 'f', NULL, "Follow symbolic links", ONE_DASH }, + { {"help", no_argument, NULL, OPTION_HELP}, + '\0', NULL, "Print option help", ONE_DASH }, + { {"hide", required_argument, NULL, OPTION_I_HIDE}, + '\0', "GLOBFILE", "Hide ISO9660/RR file" , ONE_DASH }, + { {"hide-joliet", required_argument, NULL, OPTION_J_HIDE}, + '\0', "GLOBFILE", "Hide Joliet file" , ONE_DASH }, + { {NULL, required_argument, NULL, 'i'}, + 'i', "ADD_FILES", "No longer supported" , TWO_DASHES }, + { {"joliet", no_argument, NULL, 'J'}, + 'J', NULL, "Generate Joliet directory information", ONE_DASH }, + { {"full-iso9660-filenames", no_argument, NULL, 'l'}, + 'l', NULL, "Allow full 32 character filenames for iso9660 names", ONE_DASH }, + { {"allow-leading-dots", no_argument, NULL, 'L'}, + 'L', NULL, "Allow iso9660 filenames to start with '.'", ONE_DASH }, + { {"log-file", required_argument, NULL, OPTION_LOG_FILE}, + '\0', "LOG_FILE", "Re-direct messages to LOG_FILE", ONE_DASH }, + { {"exclude", required_argument, NULL, 'm'}, + 'm', "GLOBFILE", "Exclude file name" , ONE_DASH }, + { {"prev-session", required_argument, NULL, 'M'}, + 'M', "FILE", "Set path to previous session to merge" , ONE_DASH }, + { {"omit-version-number", no_argument, NULL, 'N'}, + 'N', NULL, "Omit version number from iso9660 filename", ONE_DASH }, + { {"no-split-symlink-components", no_argument, NULL, 0}, + 0, NULL, "Inhibit splitting symlink components" , ONE_DASH }, + { {"no-split-symlink-fields", no_argument, NULL, 0}, + 0, NULL, "Inhibit splitting symlink fields" , ONE_DASH }, + { {"output", required_argument, NULL, 'o'}, + 'o', "FILE", "Set output file name" , ONE_DASH }, + { {"preparer", required_argument, NULL, 'p'}, + 'p', "PREP", "Set Volume preparer" , ONE_DASH }, + { {"print-size", no_argument, NULL, OPTION_PRINT_SIZE}, + '\0', NULL, "Print estimated filesystem size and exit", ONE_DASH }, + { {"publisher", required_argument, NULL, 'P'}, + 'P', "PUB", "Set Volume publisher" , ONE_DASH }, + { {"quiet", no_argument, NULL, OPTION_QUIET}, + '\0', NULL, "Run quietly", ONE_DASH }, + { {"rational-rock", no_argument, NULL, 'r'}, + 'r', NULL, "Generate rationalized Rock Ridge directory information", ONE_DASH }, + { {"rock", no_argument, NULL, 'R'}, + 'R', NULL, "Generate Rock Ridge directory information", ONE_DASH }, + { {"split-output", no_argument, NULL, OPTION_SPLIT_OUTPUT}, + '\0', NULL, "Split output into files of approx. 1GB size", ONE_DASH }, + { {"sysid", required_argument, NULL, OPTION_SYSID}, + '\0', "ID", "Set System ID" , ONE_DASH }, + { {"translation-table", no_argument, NULL, 'T'}, + 'T', NULL, "Generate translation tables for systems that don't understand long filenames", ONE_DASH }, + { {"verbose", no_argument, NULL, 'v'}, + 'v', NULL, "Verbose", ONE_DASH }, + { {"volid", required_argument, NULL, 'V'}, + 'V', "ID", "Set Volume ID" , ONE_DASH }, + { {"volset", required_argument, NULL, OPTION_VOLSET}, + '\0', "ID", "Set Volume set ID" , ONE_DASH }, + { {"volset-size", required_argument, NULL, OPTION_VOLSET_SIZE}, + '\0', "#", "Set Volume set size" , ONE_DASH }, + { {"volset-seqno", required_argument, NULL, OPTION_VOLSET_SEQ_NUM}, + '\0', "#", "Set Volume set sequence number" , ONE_DASH }, + { {"old-exclude", required_argument, NULL, 'x'}, + 'x', "FILE", "Exclude file name(depreciated)" , ONE_DASH }, +#ifdef ERIC_neverdef + { {"transparent-compression", no_argument, NULL, 'z'}, + 'z', NULL, "Enable transparent compression of files", ONE_DASH }, +#endif + { {"creation-date", required_argument, NULL, OPTION_CREAT_DATE }, + '\0', NULL, "Override creation date", TWO_DASHES }, + { {"modification-date", required_argument, NULL, OPTION_MODIF_DATE }, + '\0', NULL, "Override modification date", TWO_DASHES }, + { {"expiration-date", required_argument, NULL, OPTION_EXPIR_DATE }, + '\0', NULL, "Override expiration date", TWO_DASHES }, + { {"effective-date", required_argument, NULL, OPTION_EFFEC_DATE }, + '\0', NULL, "Override effective date", TWO_DASHES }, +}; + +#define OPTION_COUNT (sizeof ld_options / sizeof ld_options[0]) + +#if defined(ultrix) || defined(_AUX_SOURCE) +char *strdup(s) +char *s;{char *c;if(c=(char *)malloc(strlen(s)+1))strcpy(c,s);return c;} +#endif + + void read_rcfile __PR((char * appname)); + void usage __PR((void)); +static void hide_reloc_dir __PR((void)); + +void FDECL1(read_rcfile, char *, appname) +{ + FILE * rcfile; + struct rcopts * rco; + char * pnt, *pnt1; + char linebuffer[256]; + static char rcfn[] = ".mkisofsrc"; + char filename[1000]; + int linum; + + strcpy(filename, rcfn); + rcfile = fopen(filename, "r"); + if (!rcfile && errno != ENOENT) + perror(filename); + + if (!rcfile) + { + pnt = getenv("MKISOFSRC"); + if (pnt && strlen(pnt) <= sizeof(filename)) + { + strcpy(filename, pnt); + rcfile = fopen(filename, "r"); + if (!rcfile && errno != ENOENT) + perror(filename); + } + } + + if (!rcfile) + { + pnt = getenv("HOME"); + if (pnt && strlen(pnt) + strlen(rcfn) + 2 <= sizeof(filename)) + { + strcpy(filename, pnt); + strcat(filename, "/"); + strcat(filename, rcfn); + rcfile = fopen(filename, "r"); + if (!rcfile && errno != ENOENT) + perror(filename); + } + } + if (!rcfile && strlen(appname)+sizeof(rcfn)+2 <= sizeof(filename)) + { + strcpy(filename, appname); + pnt = strrchr(filename, '/'); + if (pnt) + { + strcpy(pnt + 1, rcfn); + rcfile = fopen(filename, "r"); + if (!rcfile && errno != ENOENT) + perror(filename); + } + } + if (!rcfile) + return; + if ( verbose > 0 ) + { + fprintf(stderr, "Using \"%s\"\n", filename); + } + + /* OK, we got it. Now read in the lines and parse them */ + linum = 0; + while (fgets(linebuffer, sizeof(linebuffer), rcfile)) + { + char *name; + char *name_end; + ++linum; + /* skip any leading white space */ + pnt = linebuffer; + while (*pnt == ' ' || *pnt == '\t') + ++pnt; + /* If we are looking at a # character, this line is a comment. */ + if (*pnt == '#') + continue; + /* The name should begin in the left margin. Make sure it is in + upper case. Stop when we see white space or a comment. */ + name = pnt; + while (*pnt && isalpha((unsigned char)*pnt)) + { + if(islower((unsigned char)*pnt)) + *pnt = toupper((unsigned char)*pnt); + pnt++; + } + if (name == pnt) + { + fprintf(stderr, "%s:%d: name required\n", filename, linum); + continue; + } + name_end = pnt; + /* Skip past white space after the name */ + while (*pnt == ' ' || *pnt == '\t') + pnt++; + /* silently ignore errors in the rc file. */ + if (*pnt != '=') + { + fprintf(stderr, "%s:%d: equals sign required\n", filename, linum); + continue; + } + /* Skip pas the = sign, and any white space following it */ + pnt++; /* Skip past '=' sign */ + while (*pnt == ' ' || *pnt == '\t') + pnt++; + + /* now it is safe to NUL terminate the name */ + + *name_end = 0; + + /* Now get rid of trailing newline */ + + pnt1 = pnt; + while (*pnt1) + { + if (*pnt1 == '\n') + { + *pnt1 = 0; + break; + } + pnt1++; + }; + /* OK, now figure out which option we have */ + for(rco = rcopt; rco->tag; rco++) { + if(strcmp(rco->tag, name) == 0) + { + *rco->variable = strdup(pnt); + break; + }; + } + if (rco->tag == NULL) + { + fprintf(stderr, "%s:%d: field name \"%s\" unknown\n", filename, linum, + name); + } + } + if (ferror(rcfile)) + perror(filename); + fclose(rcfile); +} + +char * path_table_l = NULL; +char * path_table_m = NULL; + +char * jpath_table_l = NULL; +char * jpath_table_m = NULL; + +int goof = 0; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +void usage(){ + const char * program_name = "mkisofs"; +#if 0 + fprintf(stderr,"Usage:\n"); + fprintf(stderr, +"mkisofs [-o outfile] [-R] [-V volid] [-v] [-a] \ +[-T]\n [-l] [-d] [-V] [-D] [-L] [-p preparer]" +"[-P publisher] [ -A app_id ] [-z] \n \ +[-b boot_image_name] [-c boot_catalog-name] \ +[-x path -x path ...] path\n"); +#endif + + unsigned int i; +/* const char **targets, **pp;*/ + + fprintf (stderr, "Usage: %s [options] file...\n", program_name); + + fprintf (stderr, "Options:\n"); + for (i = 0; i < OPTION_COUNT; i++) + { + if (ld_options[i].doc != NULL) + { + int comma; + int len; + unsigned int j; + + fprintf (stderr, " "); + + comma = FALSE; + len = 2; + + j = i; + do + { + if (ld_options[j].shortopt != '\0' + && ld_options[j].control != NO_HELP) + { + fprintf (stderr, "%s-%c", comma ? ", " : "", ld_options[j].shortopt); + len += (comma ? 2 : 0) + 2; + if (ld_options[j].arg != NULL) + { + if (ld_options[j].opt.has_arg != optional_argument) + { + fprintf (stderr, " "); + ++len; + } + fprintf (stderr, "%s", ld_options[j].arg); + len += strlen (ld_options[j].arg); + } + comma = TRUE; + } + ++j; + } + while (j < OPTION_COUNT && ld_options[j].doc == NULL); + + j = i; + do + { + if (ld_options[j].opt.name != NULL + && ld_options[j].control != NO_HELP) + { + fprintf (stderr, "%s-%s%s", + comma ? ", " : "", + ld_options[j].control == TWO_DASHES ? "-" : "", + ld_options[j].opt.name); + len += ((comma ? 2 : 0) + + 1 + + (ld_options[j].control == TWO_DASHES ? 1 : 0) + + strlen (ld_options[j].opt.name)); + if (ld_options[j].arg != NULL) + { + fprintf (stderr, " %s", ld_options[j].arg); + len += 1 + strlen (ld_options[j].arg); + } + comma = TRUE; + } + ++j; + } + while (j < OPTION_COUNT && ld_options[j].doc == NULL); + + if (len >= 30) + { + fprintf (stderr, "\n"); + len = 0; + } + + for (; len < 30; len++) + fputc (' ', stderr); + + fprintf (stderr, "%s\n", ld_options[i].doc); + } + } + exit(1); +} + + +/* + * Fill in date in the iso9660 format + * + * The standards state that the timezone offset is in multiples of 15 + * minutes, and is what you add to GMT to get the localtime. The U.S. + * is always at a negative offset, from -5h to -8h (can vary a little + * with DST, I guess). The Linux iso9660 filesystem has had the sign + * of this wrong for ages (mkisofs had it wrong too for the longest time). + */ +int FDECL2(iso9660_date,char *, result, time_t, crtime){ + struct tm *local; + local = localtime(&crtime); + result[0] = local->tm_year; + result[1] = local->tm_mon + 1; + result[2] = local->tm_mday; + result[3] = local->tm_hour; + result[4] = local->tm_min; + result[5] = local->tm_sec; + + /* + * Must recalculate proper timezone offset each time, + * as some files use daylight savings time and some don't... + */ + result[6] = local->tm_yday; /* save yday 'cause gmtime zaps it */ + local = gmtime(&crtime); + local->tm_year -= result[0]; + local->tm_yday -= result[6]; + local->tm_hour -= result[3]; + local->tm_min -= result[4]; + if (local->tm_year < 0) + { + local->tm_yday = -1; + } + else + { + if (local->tm_year > 0) local->tm_yday = 1; + } + + result[6] = -(local->tm_min + 60*(local->tm_hour + 24*local->tm_yday)) / 15; + + return 0; +} + +/* hide "./rr_moved" if all its contents are hidden */ +static void +hide_reloc_dir() +{ + struct directory_entry * s_entry; + + for (s_entry = reloc_dir->contents; s_entry; s_entry = s_entry->next) { + if(strcmp(s_entry->name,".")==0 || strcmp(s_entry->name,"..")==0) + continue; + + if((s_entry->de_flags & INHIBIT_ISO9660_ENTRY) == 0) + return; + } + + /* all entries are hidden, so hide this directory */ + reloc_dir->dir_flags |= INHIBIT_ISO9660_ENTRY; + reloc_dir->self->de_flags |= INHIBIT_ISO9660_ENTRY; +} + +extern char * cdwrite_data; + +int FDECL2(main, int, argc, char **, argv){ + struct directory_entry de; +#ifdef HAVE_SBRK + unsigned long mem_start; +#endif + struct stat statbuf; + char * scan_tree; + char * merge_image = NULL; + struct iso_directory_record * mrootp = NULL; + struct output_fragment * opnt; + int longind; + char shortopts[OPTION_COUNT * 3 + 2]; + struct option longopts[OPTION_COUNT + 1]; + int c; + char *log_file = 0; + + if (argc < 2) + usage(); + + /* Get the defaults from the .mkisofsrc file */ + read_rcfile(argv[0]); + + outfile = NULL; + + /* + * Copy long option initialization from GNU-ld. + */ + /* Starting the short option string with '-' is for programs that + expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. */ + { + unsigned int i; + int is, il; + shortopts[0] = '-'; + is = 1; + il = 0; + for (i = 0; i < OPTION_COUNT; i++) + { + if (ld_options[i].shortopt != '\0') + { + shortopts[is] = ld_options[i].shortopt; + ++is; + if (ld_options[i].opt.has_arg == required_argument + || ld_options[i].opt.has_arg == optional_argument) + { + shortopts[is] = ':'; + ++is; + if (ld_options[i].opt.has_arg == optional_argument) + { + shortopts[is] = ':'; + ++is; + } + } + } + if (ld_options[i].opt.name != NULL) + { + longopts[il] = ld_options[i].opt; + ++il; + } + } + shortopts[is] = '\0'; + longopts[il].name = NULL; + } + + while ((c = getopt_long_only (argc, argv, shortopts, longopts, &longind)) != EOF) + switch (c) + { + case 1: + /* + * A filename that we take as input. + */ + optind--; + goto parse_input_files; + case 'C': + /* + * This is a temporary hack until cdwrite gets the proper hooks in + * it. + */ + cdwrite_data = optarg; + break; + case 'i': + fprintf(stderr, "-i option no longer supported.\n"); + exit(1); + break; + case 'J': + use_Joliet++; + break; + case 'a': + all_files++; + break; + case 'b': + use_eltorito++; + boot_image = optarg; /* pathname of the boot image on cd */ + if (boot_image == NULL) { + fprintf(stderr,"Required boot image pathname missing\n"); + exit(1); + } + break; + case 'c': + use_eltorito++; + boot_catalog = optarg; /* pathname of the boot image on cd */ + if (boot_catalog == NULL) { + fprintf(stderr,"Required boot catalog pathname missing\n"); + exit(1); + } + break; + case OPTION_BOOT_INFO_TABLE: + use_boot_info_table = 1; + break; + case OPTION_NO_EMUL_BOOT: + fprintf (stderr, "Ignoring -no-emul-boot (no-emulation is the default behaviour)\n"); + break; + case OPTION_ELTORITO_EMUL_FLOPPY: + use_eltorito_emul_floppy = 1; + break; + case OPTION_ABSTRACT: + abstract = optarg; + if(strlen(abstract) > 37) { + fprintf(stderr,"Abstract filename string too long\n"); + exit(1); + }; + break; + case 'A': + appid = optarg; + if(strlen(appid) > 128) { + fprintf(stderr,"Application-id string too long\n"); + exit(1); + }; + break; + case OPTION_BIBLIO: + biblio = optarg; + if(strlen(biblio) > 37) { + fprintf(stderr,"Bibliographic filename string too long\n"); + exit(1); + }; + break; + case OPTION_COPYRIGHT: + copyright = optarg; + if(strlen(copyright) > 37) { + fprintf(stderr,"Copyright filename string too long\n"); + exit(1); + }; + break; + case 'd': + omit_period++; + break; + case 'D': + RR_relocation_depth = 32767; + break; + case 'f': + follow_links++; + break; + case 'l': + full_iso9660_filenames++; + break; + case 'L': + allow_leading_dots++; + break; + case OPTION_LOG_FILE: + log_file = optarg; + break; + case 'M': + merge_image = optarg; + break; + case 'N': + omit_version_number++; + break; + case 'o': + outfile = optarg; + break; + case 'p': + preparer = optarg; + if(strlen(preparer) > 128) { + fprintf(stderr,"Preparer string too long\n"); + exit(1); + }; + break; + case OPTION_PRINT_SIZE: + print_size++; + break; + case 'P': + publisher = optarg; + if(strlen(publisher) > 128) { + fprintf(stderr,"Publisher string too long\n"); + exit(1); + }; + break; + case OPTION_QUIET: + verbose = 0; + break; + case 'R': + use_RockRidge++; + break; + case 'r': + rationalize++; + use_RockRidge++; + break; + case OPTION_SPLIT_OUTPUT: + split_output++; + break; + case OPTION_SYSID: + system_id = optarg; + if(strlen(system_id) > 32) { + fprintf(stderr,"System ID string too long\n"); + exit(1); + }; + break; + case 'T': + generate_tables++; + break; + case 'V': + volume_id = optarg; + if(strlen(volume_id) > 32) { + fprintf(stderr,"Volume ID string too long\n"); + exit(1); + }; + break; + case OPTION_VOLSET: + volset_id = optarg; + if(strlen(volset_id) > 128) { + fprintf(stderr,"Volume set ID string too long\n"); + exit(1); + }; + break; + case OPTION_VOLSET_SIZE: + volume_set_size = atoi(optarg); + break; + case OPTION_VOLSET_SEQ_NUM: + volume_sequence_number = atoi(optarg); + if (volume_sequence_number > volume_set_size) { + fprintf(stderr,"Volume set sequence number too big\n"); + exit(1); + } + break; + case 'v': + verbose++; + break; + case 'z': +#ifdef VMS + fprintf(stderr,"Transparent compression not supported with VMS\n"); + exit(1); +#else + transparent_compression++; +#endif + break; + case 'x': + case 'm': + /* + * Somehow two options to do basically the same thing got added somewhere along + * the way. The 'match' code supports limited globbing, so this is the one + * that got selected. Unfortunately the 'x' switch is probably more intuitive. + */ + add_match(optarg); + break; + case OPTION_I_HIDE: + i_add_match(optarg); + break; + case OPTION_J_HIDE: + j_add_match(optarg); + break; + case OPTION_HELP: + usage (); + exit (0); + break; + case OPTION_NOSPLIT_SL_COMPONENT: + split_SL_component = 0; + break; + case OPTION_NOSPLIT_SL_FIELD: + split_SL_field = 0; + break; + case OPTION_CREAT_DATE: + if (strlen (optarg) != 16) { + fprintf (stderr, "date string must be 16 characters.\n"); + exit (1); + } + if (creation_date) + free(creation_date); + creation_date = strdup(optarg); + break; + case OPTION_MODIF_DATE: + if (strlen (optarg) != 16) { + fprintf (stderr, "date string must be 16 characters.\n"); + exit (1); + } + if (modification_date) + free(modification_date); + modification_date = strdup(optarg); + break; + case OPTION_EXPIR_DATE: + if (strlen (optarg) != 16) { + fprintf (stderr, "date string must be 16 characters.\n"); + exit (1); + } + if (expiration_date) + free(expiration_date); + expiration_date = strdup(optarg); + break; + case OPTION_EFFEC_DATE: + if (strlen (optarg) != 16) { + fprintf (stderr, "date string must be 16 characters.\n"); + exit (1); + } + if (effective_date) + free(effective_date); + effective_date = strdup(optarg); + break; + default: + usage(); + exit(1); + } + +parse_input_files: + +#ifdef __NetBSD__ + { + int resource; + struct rlimit rlp; + if (getrlimit(RLIMIT_DATA,&rlp) == -1) + perror("Warning: getrlimit"); + else { + rlp.rlim_cur=33554432; + if (setrlimit(RLIMIT_DATA,&rlp) == -1) + perror("Warning: setrlimit"); + } + } +#endif +#ifdef HAVE_SBRK + mem_start = (unsigned long) sbrk(0); +#endif + + /* if the -hide-joliet option has been given, set the Joliet option */ + if (!use_Joliet && j_ishidden()) + use_Joliet++; + + if(verbose > 1) fprintf(stderr,"%s\n", version_string); + + if(cdwrite_data == NULL && merge_image != NULL) + { + fprintf(stderr,"Multisession usage bug: Must specify -C if -M is used.\n"); + exit(0); + } + + if(cdwrite_data != NULL && merge_image == NULL) + { + fprintf(stderr,"Warning: -C specified without -M: old session data will not be merged.\n"); + } + + /* The first step is to scan the directory tree, and take some notes */ + + scan_tree = argv[optind]; + + + if(!scan_tree){ + usage(); + exit(1); + }; + +#ifndef VMS + if(scan_tree[strlen(scan_tree)-1] != '/') { + scan_tree = (char *) e_malloc(strlen(argv[optind])+2); + strcpy(scan_tree, argv[optind]); + strcat(scan_tree, "/"); + }; +#endif + + if(use_RockRidge){ +#if 1 + extension_record = generate_rr_extension_record("RRIP_1991A", + "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS", + "PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE. SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR CONTACT INFORMATION.", &extension_record_size); +#else + extension_record = generate_rr_extension_record("IEEE_P1282", + "THE IEEE P1282 PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS", + "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, PISCATAWAY, NJ, USA FOR THE P1282 SPECIFICATION.", &extension_record_size); +#endif + } + + if (log_file) { + FILE *lfp; + int i; + + /* open log file - test that we can open OK */ + if ((lfp = fopen(log_file, "w")) == NULL) { + fprintf(stderr,"can't open logfile: %s\n", log_file); + exit (1); + } + fclose(lfp); + + /* redirect all stderr message to log_file */ + fprintf(stderr, "re-directing all messages to %s\n", log_file); + fflush(stderr); + + /* associate stderr with the log file */ + if (freopen(log_file, "w", stderr) == NULL) { + fprintf(stderr,"can't open logfile: %s\n", log_file); + exit (1); + } + if(verbose > 1) { + for (i=0;iextent, 8); + } + + /* + * Create an empty root directory. If we ever scan it for real, we will fill in the + * contents. + */ + find_or_create_directory(NULL, "", &de, TRUE); + + /* + * Scan the actual directory (and any we find below it) + * for files to write out to the output image. Note - we + * take multiple source directories and keep merging them + * onto the image. + */ + while(optind < argc) + { + char * node; + struct directory * graft_dir; + struct stat st; + char * short_name; + int status; + char graft_point[1024]; + + /* + * We would like a syntax like: + * + * /tmp=/usr/tmp/xxx + * + * where the user can specify a place to graft each + * component of the tree. To do this, we may have to create + * directories along the way, of course. + * Secondly, I would like to allow the user to do something + * like: + * + * /home/baz/RMAIL=/u3/users/baz/RMAIL + * + * so that normal files could also be injected into the tree + * at an arbitrary point. + * + * The idea is that the last component of whatever is being + * entered would take the name from the last component of + * whatever the user specifies. + * + * The default will be that the file is injected at the + * root of the image tree. + */ + node = strchr(argv[optind], '='); + short_name = NULL; + + if( node != NULL ) + { + char * pnt; + char * xpnt; + + *node = '\0'; + strcpy(graft_point, argv[optind]); + *node = '='; + node++; + + graft_dir = root; + xpnt = graft_point; + if( *xpnt == PATH_SEPARATOR ) + { + xpnt++; + } + + /* + * Loop down deeper and deeper until we + * find the correct insertion spot. + */ + while(1==1) + { + pnt = strchr(xpnt, PATH_SEPARATOR); + if( pnt == NULL ) + { + if( *xpnt != '\0' ) + { + short_name = xpnt; + } + break; + } + *pnt = '\0'; + graft_dir = find_or_create_directory(graft_dir, + graft_point, + NULL, TRUE); + *pnt = PATH_SEPARATOR; + xpnt = pnt + 1; + } + } + else + { + graft_dir = root; + node = argv[optind]; + } + + /* + * Now see whether the user wants to add a regular file, + * or a directory at this point. + */ + status = stat_filter(node, &st); + if( status != 0 ) + { + /* + * This is a fatal error - the user won't be getting what + * they want if we were to proceed. + */ + fprintf(stderr, "Invalid node - %s\n", node); + exit(1); + } + else + { + if( S_ISDIR(st.st_mode) ) + { + if (!scan_directory_tree(graft_dir, node, &de)) + { + exit(1); + } + } + else + { + if( short_name == NULL ) + { + short_name = strrchr(node, PATH_SEPARATOR); + if( short_name == NULL || short_name < node ) + { + short_name = node; + } + else + { + short_name++; + } + } + if( !insert_file_entry(graft_dir, node, short_name) ) + { + exit(1); + } + } + } + + optind++; + } + + + /* + * Now merge in any previous sessions. This is driven on the source + * side, since we may need to create some additional directories. + */ + if( merge_image != NULL ) + { + merge_previous_session(root, mrootp); + } + + /* hide "./rr_moved" if all its contents have been hidden */ + if (reloc_dir && i_ishidden()) + hide_reloc_dir(); + + /* + * Sort the directories in the required order (by ISO9660). Also, + * choose the names for the 8.3 filesystem if required, and do + * any other post-scan work. + */ + goof += sort_tree(root); + + if( use_Joliet ) + { + goof += joliet_sort_tree(root); + } + + if (goof) + { + fprintf(stderr, "Joliet tree sort failed.\n"); + exit(1); + } + + /* + * Fix a couple of things in the root directory so that everything + * is self consistent. + */ + root->self = root->contents; /* Fix this up so that the path + tables get done right */ + + /* + * OK, ready to write the file. Open it up, and generate the thing. + */ + if (print_size){ + discimage = fopen("/dev/null", "wb"); + if (!discimage){ + fprintf(stderr,"Unable to open /dev/null\n"); + exit(1); + } + } else if (outfile){ + discimage = fopen(outfile, "wb"); + if (!discimage){ + fprintf(stderr,"Unable to open disc image file\n"); + exit(1); + + }; + } else { + discimage = stdout; + +#if defined(__CYGWIN32__) + setmode(fileno(stdout), O_BINARY); +#endif + } + + /* Now assign addresses on the disc for the path table. */ + + path_blocks = (path_table_size + (SECTOR_SIZE - 1)) >> 11; + if (path_blocks & 1) path_blocks++; + + jpath_blocks = (jpath_table_size + (SECTOR_SIZE - 1)) >> 11; + if (jpath_blocks & 1) jpath_blocks++; + + /* + * Start to set up the linked list that we use to track the + * contents of the disc. + */ + outputlist_insert(&padblock_desc); + + /* + * PVD for disc. + */ + outputlist_insert(&voldesc_desc); + + /* + * SVD for El Torito. MUST be immediately after the PVD! + */ + if( use_eltorito) + { + outputlist_insert(&torito_desc); + } + + /* + * SVD for Joliet. + */ + if( use_Joliet) + { + outputlist_insert(&joliet_desc); + } + + /* + * Finally the last volume desctiptor. + */ + outputlist_insert(&end_vol); + + + outputlist_insert(&pathtable_desc); + if( use_Joliet) + { + outputlist_insert(&jpathtable_desc); + } + + outputlist_insert(&dirtree_desc); + if( use_Joliet) + { + outputlist_insert(&jdirtree_desc); + } + + outputlist_insert(&dirtree_clean); + + if(extension_record) + { + outputlist_insert(&extension_desc); + } + + outputlist_insert(&files_desc); + + /* + * Allow room for the various headers we will be writing. There + * will always be a primary and an end volume descriptor. + */ + last_extent = session_start; + + /* + * Calculate the size of all of the components of the disc, and assign + * extent numbers. + */ + for(opnt = out_list; opnt; opnt = opnt->of_next ) + { + if( opnt->of_size != NULL ) + { + (*opnt->of_size)(last_extent); + } + } + + /* + * Generate the contents of any of the sections that we want to generate. + * Not all of the fragments will do anything here - most will generate the + * data on the fly when we get to the write pass. + */ + for(opnt = out_list; opnt; opnt = opnt->of_next ) + { + if( opnt->of_generate != NULL ) + { + (*opnt->of_generate)(); + } + } + + if( in_image != NULL ) + { + fclose(in_image); + } + + /* + * Now go through the list of fragments and write the data that corresponds to + * each one. + */ + for(opnt = out_list; opnt; opnt = opnt->of_next ) + { + if( opnt->of_write != NULL ) + { + (*opnt->of_write)(discimage); + } + } + + if( verbose > 0 ) + { +#ifdef HAVE_SBRK + fprintf(stderr,"Max brk space used %x\n", + (unsigned int)(((unsigned long)sbrk(0)) - mem_start)); +#endif + fprintf (stderr, "%llu extents written (%llu MiB)\n", last_extent, last_extent >> 9); + } + +#ifdef VMS + return 1; +#else + return 0; +#endif +} + +void * +FDECL1(e_malloc, size_t, size) +{ +void* pt = 0; + if( (size > 0) && ((pt=malloc(size))==NULL) ) { + fprintf(stderr, "Not enough memory\n"); + exit (1); + } +return pt; +} diff --git a/util/mkisofs/mkisofs.h b/util/mkisofs/mkisofs.h new file mode 100644 index 000000000..79266e08a --- /dev/null +++ b/util/mkisofs/mkisofs.h @@ -0,0 +1,509 @@ +/* + * Header file mkisofs.h - assorted structure definitions and typecasts. + + Written by Eric Youngdale (1993). + + Copyright 1993 Yggdrasil Computing, Incorporated + + Copyright (C) 2009 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, 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 . + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * $Id: mkisofs.h,v 1.20 1999/03/02 04:16:41 eric Exp $ + */ + +#include +#include +#include +#include + +/* This symbol is used to indicate that we do not have things like + symlinks, devices, and so forth available. Just files and dirs */ + +#ifdef VMS +#define NON_UNIXFS +#endif + +#ifdef DJGPP +#define NON_UNIXFS +#endif + +#ifdef VMS +#include +#define dirent direct +#endif + +#ifdef _WIN32 +#define NON_UNIXFS +#endif /* _WIN32 */ + +#ifndef S_IROTH +#define S_IROTH 0 +#endif + +#ifndef S_IRGRP +#define S_IRGRP 0 +#endif + +#ifndef HAVE_GETUID +static inline int +getuid () +{ + return 0; +} +#endif + +#ifndef HAVE_GETGID +static inline int +getgid () +{ + return 0; +} +#endif + +#ifndef HAVE_LSTAT +static inline int +lstat (const char *filename, struct stat *buf) +{ + return stat (filename, buf); +} +#endif + +#include +#include +#include + +#if defined(HAVE_DIRENT_H) +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if defined(HAVE_SYS_NDIR_H) +# include +# endif +# if defined(HAVE_SYS_DIR_H) +# include +# endif +# if defined(HAVE_NDIR_H) +# include +# endif +#endif + +#if defined(HAVE_STRING_H) +#include +#else +#if defined(HAVE_STRINGS_H) +#include +#endif +#endif + +#ifdef ultrix +extern char *strdup(); +#endif + +#ifdef __STDC__ +#define DECL(NAME,ARGS) NAME ARGS +#define FDECL1(NAME,TYPE0, ARG0) \ + NAME(TYPE0 ARG0) +#define FDECL2(NAME,TYPE0, ARG0,TYPE1, ARG1) \ + NAME(TYPE0 ARG0, TYPE1 ARG1) +#define FDECL3(NAME,TYPE0, ARG0,TYPE1, ARG1, TYPE2, ARG2) \ + NAME(TYPE0 ARG0, TYPE1 ARG1, TYPE2 ARG2) +#define FDECL4(NAME,TYPE0, ARG0,TYPE1, ARG1, TYPE2, ARG2, TYPE3, ARG3) \ + NAME(TYPE0 ARG0, TYPE1 ARG1, TYPE2 ARG2, TYPE3 ARG3) +#define FDECL5(NAME,TYPE0, ARG0,TYPE1, ARG1, TYPE2, ARG2, TYPE3, ARG3, TYPE4, ARG4) \ + NAME(TYPE0 ARG0, TYPE1 ARG1, TYPE2 ARG2, TYPE3 ARG3, TYPE4 ARG4) +#define FDECL6(NAME,TYPE0, ARG0,TYPE1, ARG1, TYPE2, ARG2, TYPE3, ARG3, TYPE4, ARG4, TYPE5, ARG5) \ + NAME(TYPE0 ARG0, TYPE1 ARG1, TYPE2 ARG2, TYPE3 ARG3, TYPE4 ARG4, TYPE5 ARG5) +#else +#define DECL(NAME,ARGS) NAME() +#define FDECL1(NAME,TYPE0, ARG0) NAME(ARG0) TYPE0 ARG0; +#define FDECL2(NAME,TYPE0, ARG0,TYPE1, ARG1) NAME(ARG0, ARG1) TYPE0 ARG0; TYPE1 ARG1; +#define FDECL3(NAME,TYPE0, ARG0,TYPE1, ARG1, TYPE2, ARG2) \ + NAME(ARG0, ARG1, ARG2) TYPE0 ARG0; TYPE1 ARG1; TYPE2 ARG2; +#define FDECL4(NAME,TYPE0, ARG0,TYPE1, ARG1, TYPE2, ARG2, TYPE3, ARG3) \ + NAME(ARG0, ARG1, ARG2, ARG3, ARG4) TYPE0 ARG0; TYPE1 ARG1; TYPE2 ARG2; TYPE3 ARG3; +#define FDECL5(NAME,TYPE0, ARG0,TYPE1, ARG1, TYPE2, ARG2, TYPE3, ARG3, TYPE4, ARG4) \ + NAME(ARG0, ARG1, ARG2, ARG3, ARG4) TYPE0 ARG0; TYPE1 ARG1; TYPE2 ARG2; TYPE3 ARG3; TYPE4 ARG4; +#define FDECL6(NAME,TYPE0, ARG0,TYPE1, ARG1, TYPE2, ARG2, TYPE3, ARG3, TYPE4, ARG4, TYPE5, ARG5) \ + NAME(ARG0, ARG1, ARG2, ARG3, ARG4, ARG5) TYPE0 ARG0; TYPE1 ARG1; TYPE2 ARG2; TYPE3 ARG3; TYPE4 ARG4; TYPE5 ARG5; +#define const +#endif + + +#ifdef __SVR4 +#include +#else +extern int optind; +extern char *optarg; +/* extern int getopt (int __argc, char **__argv, char *__optstring); */ +#endif + +#include "iso9660.h" +#include "defaults.h" + +struct directory_entry{ + struct directory_entry * next; + struct directory_entry * jnext; + struct iso_directory_record isorec; + uint64_t starting_block; + uint64_t size; + unsigned short priority; + unsigned char jreclen; /* Joliet record len */ + char * name; + char * table; + char * whole_name; + struct directory * filedir; + struct directory_entry * parent_rec; + unsigned int de_flags; + ino_t inode; /* Used in the hash table */ + dev_t dev; /* Used in the hash table */ + unsigned char * rr_attributes; + unsigned int rr_attr_size; + unsigned int total_rr_attr_size; + unsigned int got_rr_name; +}; + +struct file_hash{ + struct file_hash * next; + ino_t inode; /* Used in the hash table */ + dev_t dev; /* Used in the hash table */ + unsigned int starting_block; + unsigned int size; +}; + + +/* + * This structure is used to control the output of fragments to the cdrom + * image. Everything that will be written to the output image will eventually + * go through this structure. There are two pieces - first is the sizing where + * we establish extent numbers for everything, and the second is when we actually + * generate the contents and write it to the output image. + * + * This makes it trivial to extend mkisofs to write special things in the image. + * All you need to do is hook an additional structure in the list, and the rest + * works like magic. + * + * The three passes each do the following: + * + * The 'size' pass determines the size of each component and assigns the extent number + * for that component. + * + * The 'generate' pass will adjust the contents and pointers as required now that extent + * numbers are assigned. In some cases, the contents of the record are also generated. + * + * The 'write' pass actually writes the data to the disc. + */ +struct output_fragment +{ + struct output_fragment * of_next; +#ifdef __STDC__ + int (*of_size)(int); + int (*of_generate)(void); + int (*of_write)(FILE *); +#else + int (*of_size)(); + int (*of_generate)(); + int (*of_write)(); +#endif +}; + +extern struct output_fragment * out_list; +extern struct output_fragment * out_tail; + +extern struct output_fragment padblock_desc; +extern struct output_fragment voldesc_desc; +extern struct output_fragment joliet_desc; +extern struct output_fragment torito_desc; +extern struct output_fragment end_vol; +extern struct output_fragment pathtable_desc; +extern struct output_fragment jpathtable_desc; +extern struct output_fragment dirtree_desc; +extern struct output_fragment dirtree_clean; +extern struct output_fragment jdirtree_desc; +extern struct output_fragment extension_desc; +extern struct output_fragment files_desc; + +/* + * This structure describes one complete directory. It has pointers + * to other directories in the overall tree so that it is clear where + * this directory lives in the tree, and it also must contain pointers + * to the contents of the directory. Note that subdirectories of this + * directory exist twice in this stucture. Once in the subdir chain, + * and again in the contents chain. + */ +struct directory{ + struct directory * next; /* Next directory at same level as this one */ + struct directory * subdir; /* First subdirectory in this directory */ + struct directory * parent; + struct directory_entry * contents; + struct directory_entry * jcontents; + struct directory_entry * self; + char * whole_name; /* Entire path */ + char * de_name; /* Entire path */ + unsigned int ce_bytes; /* Number of bytes of CE entries reqd for this dir */ + unsigned int depth; + unsigned int size; + unsigned int extent; + unsigned int jsize; + unsigned int jextent; + unsigned short path_index; + unsigned short jpath_index; + unsigned short dir_flags; + unsigned short dir_nlink; +}; + +extern int goof; +extern struct directory * root; +extern struct directory * reloc_dir; +extern uint64_t next_extent; +extern uint64_t last_extent; +extern uint64_t last_extent_written; +extern uint64_t session_start; + +extern unsigned int path_table_size; +extern unsigned int path_table[4]; +extern unsigned int path_blocks; +extern char * path_table_l; +extern char * path_table_m; + +extern unsigned int jpath_table_size; +extern unsigned int jpath_table[4]; +extern unsigned int jpath_blocks; +extern char * jpath_table_l; +extern char * jpath_table_m; + +extern struct iso_directory_record root_record; +extern struct iso_directory_record jroot_record; + +extern int use_eltorito; +extern int use_eltorito_emul_floppy; +extern int use_boot_info_table; +extern int use_RockRidge; +extern int use_Joliet; +extern int rationalize; +extern int follow_links; +extern int verbose; +extern int all_files; +extern int generate_tables; +extern int print_size; +extern int split_output; +extern int omit_period; +extern int omit_version_number; +extern int transparent_compression; +extern unsigned int RR_relocation_depth; +extern int full_iso9660_filenames; +extern int split_SL_component; +extern int split_SL_field; + +/* tree.c */ +extern int DECL(stat_filter, (char *, struct stat *)); +extern int DECL(lstat_filter, (char *, struct stat *)); +extern int DECL(sort_tree,(struct directory *)); +extern struct directory * + DECL(find_or_create_directory,(struct directory *, const char *, + struct directory_entry * self, int)); +extern void DECL (finish_cl_pl_entries, (void)); +extern int DECL(scan_directory_tree,(struct directory * this_dir, + char * path, + struct directory_entry * self)); +extern int DECL(insert_file_entry,(struct directory *, char *, + char *)); + +extern void DECL(generate_iso9660_directories,(struct directory *, FILE*)); +extern void DECL(dump_tree,(struct directory * node)); +extern struct directory_entry * DECL(search_tree_file, (struct + directory * node,char * filename)); +extern void DECL(update_nlink_field,(struct directory * node)); +extern void DECL (init_fstatbuf, (void)); +extern struct stat root_statbuf; + +/* eltorito.c */ +extern void DECL(init_boot_catalog, (const char * path )); +extern void DECL(get_torito_desc, (struct eltorito_boot_descriptor * path )); + +/* write.c */ +extern int DECL(get_731,(char *)); +extern int DECL(get_733,(char *)); +extern int DECL(isonum_733,(unsigned char *)); +extern void DECL(set_723,(char *, unsigned int)); +extern void DECL(set_731,(char *, unsigned int)); +extern void DECL(set_721,(char *, unsigned int)); +extern void DECL(set_733,(char *, unsigned int)); +extern int DECL(sort_directory,(struct directory_entry **)); +extern void DECL(generate_one_directory,(struct directory *, FILE*)); +extern void DECL(memcpy_max, (char *, char *, int)); +extern int DECL(oneblock_size, (int starting_extent)); +extern struct iso_primary_descriptor vol_desc; +extern void DECL(xfwrite, (void * buffer, uint64_t count, uint64_t size, FILE * file)); +extern void DECL(set_732, (char * pnt, unsigned int i)); +extern void DECL(set_722, (char * pnt, unsigned int i)); +extern void DECL(outputlist_insert, (struct output_fragment * frag)); + +/* + * Set by user command-line to override default date values + */ + +extern char *creation_date; +extern char *modification_date; +extern char *expiration_date; +extern char *effective_date; + +/* multi.c */ + +extern FILE * in_image; +extern struct iso_directory_record * + DECL(merge_isofs,(char * path)); + +extern int DECL(free_mdinfo, (struct directory_entry **, int len)); + +extern struct directory_entry ** + DECL(read_merging_directory,(struct iso_directory_record *, int*)); +extern void + DECL(merge_remaining_entries, (struct directory *, + struct directory_entry **, int)); +extern int + DECL(merge_previous_session, (struct directory *, + struct iso_directory_record *)); + +extern int DECL(get_session_start, (int *)); + +/* joliet.c */ +int DECL(joliet_sort_tree, (struct directory * node)); + +/* match.c */ +extern int DECL(matches, (char *)); +extern void DECL(add_match, (char *)); + +/* files.c */ +struct dirent * DECL(readdir_add_files, (char **, char *, DIR *)); + +/* */ + +extern int DECL(iso9660_file_length,(const char* name, + struct directory_entry * sresult, int flag)); +extern int DECL(iso9660_date,(char *, time_t)); +extern void DECL(add_hash,(struct directory_entry *)); +extern struct file_hash * DECL(find_hash,(dev_t, ino_t)); +extern void DECL(add_directory_hash,(dev_t, ino_t)); +extern struct file_hash * DECL(find_directory_hash,(dev_t, ino_t)); +extern void DECL (flush_file_hash, (void)); +extern int DECL(delete_file_hash,(struct directory_entry *)); +extern struct directory_entry * DECL(find_file_hash,(char *)); +extern void DECL(add_file_hash,(struct directory_entry *)); +extern int DECL(generate_rock_ridge_attributes,(char *, char *, + struct directory_entry *, + struct stat *, struct stat *, + int deep_flag)); +extern char * DECL(generate_rr_extension_record,(char * id, char * descriptor, + char * source, int * size)); + +extern int DECL(check_prev_session, (struct directory_entry **, int len, + struct directory_entry *, + struct stat *, + struct stat *, + struct directory_entry **)); + +#ifdef USE_SCG +/* scsi.c */ +#ifdef __STDC__ +extern int readsecs(int startsecno, void *buffer, int sectorcount); +extern int scsidev_open(char *path); +#else +extern int readsecs(); +extern int scsidev_open(); +#endif +#endif + +extern char * extension_record; +extern int extension_record_extent; +extern int n_data_extents; + +/* These are a few goodies that can be specified on the command line, and are + filled into the root record */ + +extern char * preparer; +extern char * publisher; +extern char * copyright; +extern char * biblio; +extern char * abstract; +extern char * appid; +extern char * volset_id; +extern char * system_id; +extern char * volume_id; +extern char * boot_catalog; +extern char * boot_image; +extern int volume_set_size; +extern int volume_sequence_number; + +extern void * DECL(e_malloc,(size_t)); + + +#define SECTOR_SIZE (2048) +#define ROUND_UP(X) ((X + (SECTOR_SIZE - 1)) & ~(SECTOR_SIZE - 1)) + +#define NEED_RE 1 +#define NEED_PL 2 +#define NEED_CL 4 +#define NEED_CE 8 +#define NEED_SP 16 + +#define PREV_SESS_DEV (sizeof(dev_t) >= 4 ? 0x7ffffffd : 0x7ffd) +#define TABLE_INODE (sizeof(ino_t) >= 4 ? 0x7ffffffe : 0x7ffe) +#define UNCACHED_INODE (sizeof(ino_t) >= 4 ? 0x7fffffff : 0x7fff) +#define UNCACHED_DEVICE (sizeof(dev_t) >= 4 ? 0x7fffffff : 0x7fff) + +#ifdef VMS +#define STAT_INODE(X) (X.st_ino[0]) +#define PATH_SEPARATOR ']' +#define SPATH_SEPARATOR "" +#else +#define STAT_INODE(X) (X.st_ino) +#define PATH_SEPARATOR '/' +#define SPATH_SEPARATOR "/" +#endif + +/* + * When using multi-session, indicates that we can reuse the + * TRANS.TBL information for this directory entry. If this flag + * is set for all entries in a directory, it means we can just + * reuse the TRANS.TBL and not generate a new one. + */ +#define SAFE_TO_REUSE_TABLE_ENTRY 0x01 +#define DIR_HAS_DOT 0x02 +#define DIR_HAS_DOTDOT 0x04 +#define INHIBIT_JOLIET_ENTRY 0x08 +#define INHIBIT_RR_ENTRY 0x10 +#define RELOCATED_DIRECTORY 0x20 +#define INHIBIT_ISO9660_ENTRY 0x40 + +/* + * Volume sequence number to use in all of the iso directory records. + */ +#define DEF_VSN 1 + +/* + * Make sure we have a definition for this. If not, take a very conservative + * guess. From what I can tell SunOS is the only one with this trouble. + */ +#ifndef NAME_MAX +#ifdef FILENAME_MAX +#define NAME_MAX FILENAME_MAX +#else +#define NAME_MAX 128 +#endif +#endif diff --git a/util/mkisofs/multi.c b/util/mkisofs/multi.c new file mode 100644 index 000000000..ef9afa9c1 --- /dev/null +++ b/util/mkisofs/multi.c @@ -0,0 +1,1213 @@ +/* + * File multi.c - scan existing iso9660 image and merge into + * iso9660 filesystem. Used for multisession support. + * + * Written by Eric Youngdale (1996). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +static char rcsid[] ="$Id: multi.c,v 1.14 1999/03/02 04:16:41 eric Exp $"; + +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#ifndef VMS + +#ifdef HAVE_UNISTD_H +#include +#endif + +#else +#include +#include +#include "vms.h" +extern char * strdup(const char *); +#endif + +#include "mkisofs.h" +#include "iso9660.h" + +#define TF_CREATE 1 +#define TF_MODIFY 2 +#define TF_ACCESS 4 +#define TF_ATTRIBUTES 8 + +static int isonum_711 __PR((unsigned char * p)); +static int isonum_721 __PR((unsigned char * p)); +static int isonum_723 __PR((unsigned char * p)); +static int isonum_731 __PR((unsigned char * p)); + +static int DECL(merge_old_directory_into_tree, (struct directory_entry *, + struct directory *)); + +#ifdef __STDC__ +static int +isonum_711 (unsigned char * p) +#else +static int +isonum_711 (p) + unsigned char * p; +#endif +{ + return (*p & 0xff); +} + +#ifdef __STDC__ +static int +isonum_721 (unsigned char * p) +#else +static int +isonum_721 (p) + unsigned char * p; +#endif +{ + return ((p[0] & 0xff) | ((p[1] & 0xff) << 8)); +} + +#ifdef __STDC__ +static int +isonum_723 (unsigned char * p) +#else +static int +isonum_723 (p) + unsigned char * p; +#endif +{ +#if 0 + if (p[0] != p[3] || p[1] != p[2]) { + fprintf (stderr, "invalid format 7.2.3 number\n"); + exit (1); + } +#endif + return (isonum_721 (p)); +} + +#ifdef __STDC__ +static int +isonum_731 (unsigned char * p) +#else +static int +isonum_731 (p) + unsigned char * p; +#endif +{ + return ((p[0] & 0xff) + | ((p[1] & 0xff) << 8) + | ((p[2] & 0xff) << 16) + | ((p[3] & 0xff) << 24)); +} + +#ifdef __STDC__ +int +isonum_733 (unsigned char * p) +#else +int +isonum_733 (p) + unsigned char * p; +#endif +{ + return (isonum_731 (p)); +} + +FILE * in_image = NULL; + +#ifndef USE_SCG +/* + * Don't define readsecs if mkisofs is linked with + * the SCSI library. + * readsecs() will be implemented as SCSI command in this case. + * + * Use global var in_image directly in readsecs() + * the SCSI equivalent will not use a FILE* for I/O. + * + * The main point of this pointless abstraction is that Solaris won't let + * you read 2K sectors from the cdrom driver. The fact that 99.9% of the + * discs out there have a 2K sectorsize doesn't seem to matter that much. + * Anyways, this allows the use of a scsi-generics type of interface on + * Solaris. + */ +#ifdef __STDC__ +static int +readsecs(int startsecno, void *buffer, int sectorcount) +#else +static int +readsecs(startsecno, buffer, sectorcount) + int startsecno; + void *buffer; + int sectorcount; +#endif +{ + int f = fileno(in_image); + + if (lseek(f, (off_t)startsecno * SECTOR_SIZE, 0) == (off_t)-1) { + fprintf(stderr," Seek error on old image\n"); + exit(10); + } + return (read(f, buffer, sectorcount * SECTOR_SIZE)); +} +#endif + +/* + * Parse the RR attributes so we can find the file name. + */ +static int +FDECL3(parse_rr, unsigned char *, pnt, int, len, struct directory_entry *,dpnt) +{ + int cont_extent, cont_offset, cont_size; + char name_buf[256]; + + cont_extent = cont_offset = cont_size = 0; + + while(len >= 4){ + if(pnt[3] != 1) { + fprintf(stderr,"**BAD RRVERSION"); + return -1; + }; + if(strncmp((char *) pnt, "NM", 2) == 0) { + strncpy(name_buf, (char *) pnt+5, pnt[2] - 5); + name_buf[pnt[2] - 5] = 0; + dpnt->name = strdup(name_buf); + dpnt->got_rr_name = 1; + return 0; + } + + if(strncmp((char *) pnt, "CE", 2) == 0) { + cont_extent = isonum_733(pnt+4); + cont_offset = isonum_733(pnt+12); + cont_size = isonum_733(pnt+20); + }; + + len -= pnt[2]; + pnt += pnt[2]; + if(len <= 3 && cont_extent) { + unsigned char sector[SECTOR_SIZE]; + readsecs(cont_extent, sector, 1); + parse_rr(§or[cont_offset], cont_size, dpnt); + }; + }; + + /* Fall back to the iso name if no RR name found */ + if (dpnt->name == NULL) { + char *cp; + + strcpy(name_buf, dpnt->isorec.name); + cp = strchr(name_buf, ';'); + if (cp != NULL) { + *cp = '\0'; + } + + dpnt->name = strdup(name_buf); + } + + return 0; +} /* parse_rr */ + + +static int +FDECL4(check_rr_dates, struct directory_entry *, dpnt, + struct directory_entry *, current, + struct stat *, statbuf, + struct stat *,lstatbuf) +{ + int cont_extent, cont_offset, cont_size; + int offset; + unsigned char * pnt; + int len; + int same_file; + int same_file_type; + mode_t mode; + char time_buf[7]; + + + cont_extent = cont_offset = cont_size = 0; + same_file = 1; + same_file_type = 1; + + pnt = dpnt->rr_attributes; + len = dpnt->rr_attr_size; + /* + * We basically need to parse the rr attributes again, and + * dig out the dates and file types. + */ + while(len >= 4){ + if(pnt[3] != 1) { + fprintf(stderr,"**BAD RRVERSION"); + return -1; + }; + + /* + * If we have POSIX file modes, make sure that the file type + * is the same. If it isn't, then we must always + * write the new file. + */ + if(strncmp((char *) pnt, "PX", 2) == 0) { + mode = isonum_733(pnt + 4); + if( (lstatbuf->st_mode & S_IFMT) != (mode & S_IFMT) ) + { + same_file_type = 0; + same_file = 0; + } + } + + if(strncmp((char *) pnt, "TF", 2) == 0) { + offset = 5; + if( pnt[4] & TF_CREATE ) + { + iso9660_date((char *) time_buf, lstatbuf->st_ctime); + if(memcmp(time_buf, pnt+offset, 7) == 0) + same_file = 0; + offset += 7; + } + if( pnt[4] & TF_MODIFY ) + { + iso9660_date((char *) time_buf, lstatbuf->st_mtime); + if(memcmp(time_buf, pnt+offset, 7) == 0) + same_file = 0; + offset += 7; + } + } + + if(strncmp((char *) pnt, "CE", 2) == 0) { + cont_extent = isonum_733(pnt+4); + cont_offset = isonum_733(pnt+12); + cont_size = isonum_733(pnt+20); + }; + + len -= pnt[2]; + pnt += pnt[2]; + if(len <= 3 && cont_extent) { + unsigned char sector[SECTOR_SIZE]; + + readsecs(cont_extent, sector, 1); + parse_rr(§or[cont_offset], cont_size, dpnt); + }; + }; + + /* + * If we have the same fundamental file type, then it is clearly + * safe to reuse the TRANS.TBL entry. + */ + if( same_file_type ) + { + current->de_flags |= SAFE_TO_REUSE_TABLE_ENTRY; + } + + return same_file; +} + +struct directory_entry ** +FDECL2(read_merging_directory, struct iso_directory_record *, mrootp, + int *, nent) +{ + unsigned char * cpnt; + unsigned char * cpnt1; + char * dirbuff; + int i; + struct iso_directory_record * idr; + int len; + struct directory_entry **pnt; + int rlen; + struct directory_entry **rtn; + int seen_rockridge; + unsigned char * tt_buf; + int tt_extent; + int tt_size; + + static int warning_given = 0; + + /* + * First, allocate a buffer large enough to read in the entire + * directory. + */ + dirbuff = (char *) e_malloc(isonum_733((unsigned char *)mrootp->size)); + + readsecs(isonum_733((unsigned char *)mrootp->extent), dirbuff, + isonum_733((unsigned char *)mrootp->size)/SECTOR_SIZE); + + /* + * Next look over the directory, and count up how many entries we + * have. + */ + len = isonum_733((unsigned char *)mrootp->size); + i = 0; + *nent = 0; + while(i < len ) + { + idr = (struct iso_directory_record *) &dirbuff[i]; + if(idr->length[0] == 0) + { + i = (i + SECTOR_SIZE - 1) & ~(SECTOR_SIZE - 1); + continue; + } + (*nent)++; + i += idr->length[0]; + } + + /* + * Now allocate the buffer which will hold the array we are + * about to return. + */ + rtn = (struct directory_entry **) e_malloc(*nent * sizeof(*rtn)); + + /* + * Finally, scan the directory one last time, and pick out the + * relevant bits of information, and store it in the relevant + * bits of the structure. + */ + i = 0; + pnt = rtn; + tt_extent = 0; + seen_rockridge = 0; + tt_size = 0; + while(i < len ) + { + idr = (struct iso_directory_record *) &dirbuff[i]; + if(idr->length[0] == 0) + { + i = (i + SECTOR_SIZE - 1) & ~(SECTOR_SIZE - 1); + continue; + } + *pnt = (struct directory_entry *) e_malloc(sizeof(**rtn)); + (*pnt)->next = NULL; + (*pnt)->isorec = *idr; + (*pnt)->starting_block = isonum_733((unsigned char *)idr->extent); + (*pnt)->size = isonum_733((unsigned char *)idr->size); + (*pnt)->priority = 0; + (*pnt)->name = NULL; + (*pnt)->got_rr_name = 0; + (*pnt)->table = NULL; + (*pnt)->whole_name = NULL; + (*pnt)->filedir = NULL; + (*pnt)->parent_rec = NULL; + /* + * Set this information so that we correctly cache previous + * session bits of information. + */ + (*pnt)->inode = (*pnt)->starting_block; + (*pnt)->dev = PREV_SESS_DEV; + (*pnt)->rr_attributes = NULL; + (*pnt)->rr_attr_size = 0; + (*pnt)->total_rr_attr_size = 0; + (*pnt)->de_flags = SAFE_TO_REUSE_TABLE_ENTRY; + + /* + * Check for and parse any RR attributes for the file. + * All we are really looking for here is the original name + * of the file. + */ + rlen = idr->length[0] & 0xff; + cpnt = (unsigned char *) idr; + + rlen -= sizeof(struct iso_directory_record); + cpnt += sizeof(struct iso_directory_record); + + rlen += sizeof(idr->name); + cpnt -= sizeof(idr->name); + + rlen -= idr->name_len[0]; + cpnt += idr->name_len[0]; + + if((idr->name_len[0] & 1) == 0){ + cpnt++; + rlen--; + }; + + if( rlen != 0 ) + { + (*pnt)->total_rr_attr_size = (*pnt)->rr_attr_size = rlen; + (*pnt)->rr_attributes = e_malloc(rlen); + memcpy((*pnt)->rr_attributes, cpnt, rlen); + seen_rockridge = 1; + } + + /* + * Now zero out the remainder of the name field. + */ + cpnt = (unsigned char *) &(*pnt)->isorec.name; + cpnt += idr->name_len[0]; + memset(cpnt, 0, sizeof((*pnt)->isorec.name) - idr->name_len[0]); + + parse_rr((*pnt)->rr_attributes, rlen, *pnt); + + if( ((*pnt)->isorec.name_len[0] == 1) + && ( ((*pnt)->isorec.name[0] == 0) + || ((*pnt)->isorec.name[0] == 1)) ) + { + if( (*pnt)->name != NULL ) + { + free((*pnt)->name); + } + if( (*pnt)->whole_name != NULL ) + { + free((*pnt)->whole_name); + } + if( (*pnt)->isorec.name[0] == 0 ) + { + (*pnt)->name = strdup("."); + } + else + { + (*pnt)->name = strdup(".."); + } + } + +#ifdef DEBUG + fprintf(stderr, "got DE name: %s\n", (*pnt)->name); +#endif + + if( strncmp(idr->name, "TRANS.TBL", 9) == 0) + { + if( (*pnt)->name != NULL ) + { + free((*pnt)->name); + } + if( (*pnt)->whole_name != NULL ) + { + free((*pnt)->whole_name); + } + (*pnt)->name = strdup(""); + tt_extent = isonum_733((unsigned char *)idr->extent); + tt_size = isonum_733((unsigned char *)idr->size); + } + + pnt++; + i += idr->length[0]; + } + + /* + * If there was a TRANS.TBL;1 entry, then grab it, read it, and use it + * to get the filenames of the files. Also, save the table info, just + * in case we need to use it. + */ + if( tt_extent != 0 && tt_size != 0 ) + { + tt_buf = (unsigned char *) e_malloc(tt_size); + readsecs(tt_extent, tt_buf, tt_size/SECTOR_SIZE); + + /* + * Loop through the file, examine each entry, and attempt to + * attach it to the correct entry. + */ + cpnt = tt_buf; + cpnt1 = tt_buf; + while( cpnt - tt_buf < tt_size ) + { + while(*cpnt1 != '\n' && *cpnt1 != '\0') cpnt1++; + *cpnt1 = '\0'; + + for(pnt = rtn, i = 0; i <*nent; i++, pnt++) + { + rlen = isonum_711((*pnt)->isorec.name_len); + if( strncmp((char *) cpnt + 2, (*pnt)->isorec.name, + rlen) == 0 + && cpnt[2+rlen] == ' ') + { + (*pnt)->table = e_malloc(strlen((char*)cpnt) - 33); + sprintf((*pnt)->table, "%c\t%s\n", + *cpnt, cpnt+37); + if( !(*pnt)->got_rr_name ) + { + if ((*pnt)->name != NULL) { + free((*pnt)->name); + } + (*pnt)->name = strdup((char *) cpnt+37); + } + break; + } + } + cpnt = cpnt1 + 1; + cpnt1 = cpnt; + } + + free(tt_buf); + } + else if( !seen_rockridge && !warning_given ) + { + /* + * Warn the user that iso (8.3) names were used because neither + * Rock Ridge (-R) nor TRANS.TBL (-T) name translations were found. + */ + fprintf(stderr,"Warning: Neither Rock Ridge (-R) nor TRANS.TBL (-T) \n"); + fprintf(stderr,"name translations were found on previous session.\n"); + fprintf(stderr,"ISO (8.3) file names have been used instead.\n"); + warning_given = 1; + } + + if( dirbuff != NULL ) + { + free(dirbuff); + } + + return rtn; +} /* read_merging_directory */ + +/* + * Free any associated data related to the structures. + */ +int +FDECL2(free_mdinfo, struct directory_entry ** , ptr, int, len ) +{ + int i; + struct directory_entry **p; + + p = ptr; + for(i=0; iname != NULL ) + { + free((*p)->name); + } + + if( (*p)->whole_name != NULL ) + { + free((*p)->whole_name); + } + + if( (*p)->rr_attributes != NULL ) + { + free((*p)->rr_attributes); + } + + if( (*p)->table != NULL ) + { + free((*p)->table); + } + + free(*p); + + } + + free(ptr); + return 0; +} + +/* + * Search the list to see if we have any entries from the previous + * session that match this entry. If so, copy the extent number + * over so we don't bother to write it out to the new session. + */ + +int +FDECL6(check_prev_session, struct directory_entry ** , ptr, int, len, + struct directory_entry *, curr_entry, + struct stat *, statbuf, struct stat *, lstatbuf, + struct directory_entry **, odpnt) +{ + int i; + + for( i=0; i < len; i++ ) + { + if( ptr[i] == NULL ) + { + continue; + } + +#if 0 + if( ptr[i]->name != NULL && ptr[i]->isorec.name_len[0] == 1 + && ptr[i]->name[0] == '\0' ) + { + continue; + } + if( ptr[i]->name != NULL && ptr[i]->isorec.name_len[0] == 1 + && ptr[i]->name[0] == 1) + { + continue; + } +#else + if( ptr[i]->name != NULL && strcmp(ptr[i]->name, ".") == 0 ) + { + continue; + } + if( ptr[i]->name != NULL && strcmp(ptr[i]->name, "..") == 0 ) + { + continue; + } +#endif + + if( ptr[i]->name != NULL + && strcmp(ptr[i]->name, curr_entry->name) != 0 ) + { + continue; + } + + /* + * We know that the files have the same name. If they also have + * the same file type (i.e. file, dir, block, etc), then we + * can safely reuse the TRANS.TBL entry for this file. + * The check_rr_dates function will do this for us. + * + * Verify that the file type and dates are consistent. + * If not, we probably have a different file, and we need + * to write it out again. + */ + if( (ptr[i]->rr_attributes != NULL) + && (check_rr_dates(ptr[i], curr_entry, statbuf, lstatbuf)) ) + { + goto found_it; + } + + + /* + * Verify size and timestamp. If rock ridge is in use, we need + * to compare dates from RR too. Directories are special, we + * calculate their size later. + */ + if( (curr_entry->isorec.flags[0] & 2) == 0 + && ptr[i]->size != curr_entry->size ) + { + goto found_it; + } + + if( memcmp(ptr[i]->isorec.date, curr_entry->isorec.date,7) != 0 ) + { + goto found_it; + } + + /* + * Never ever reuse directory extents. See comments in + * tree.c for an explaination of why this must be the case. + */ + if( (curr_entry->isorec.flags[0] & 2) != 0 ) + { + goto found_it; + } + + memcpy(curr_entry->isorec.extent, ptr[i]->isorec.extent, 8); + curr_entry->de_flags |= SAFE_TO_REUSE_TABLE_ENTRY; + goto found_it; + } + return 0; + +found_it: + if( odpnt != NULL ) + { + *odpnt = ptr[i]; + } + else + { + free(ptr[i]); + } + ptr[i] = NULL; + return 0; +} + +/* + * merge_isofs: Scan an existing image, and return a pointer + * to the root directory for this image. + */ +struct iso_directory_record * FDECL1(merge_isofs, char *, path) +{ + char buffer[SECTOR_SIZE]; + int file_addr; + int i; + struct iso_primary_descriptor * pri = NULL; + struct iso_directory_record * rootp; + struct iso_volume_descriptor * vdp; + + /* + * Start by opening up the image and searching for the volume header. + * Ultimately, we need to search for volume headers in multiple places + * because we might be starting with a multisession image. + * FIXME(eric). + */ + +#ifndef USE_SCG + in_image = fopen(path, "rb"); + if( in_image == NULL ) + { + return NULL; + } +#else + if (strchr(path, '/')) { + in_image = fopen(path, "rb"); + if( in_image == NULL ) { + return NULL; + } + } else { + if (scsidev_open(path) < 0) + return NULL; + } +#endif + + get_session_start(&file_addr); + + for(i = 0; i< 100; i++) + { + if (readsecs(file_addr/SECTOR_SIZE, &buffer, + sizeof(buffer)/SECTOR_SIZE) != sizeof(buffer)) + { + fprintf(stderr," Read error on old image %s\n", path); + exit(10); + } + + vdp = (struct iso_volume_descriptor *)buffer; + + if( (strncmp(vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) + && (isonum_711((unsigned char *) vdp->type) == ISO_VD_PRIMARY) ) + { + break; + } + file_addr += SECTOR_SIZE; + } + + if( i == 100 ) + { + return NULL; + } + + pri = (struct iso_primary_descriptor *)vdp; + + /* + * Check the blocksize of the image to make sure it is compatible. + */ + if( (isonum_723 ((unsigned char *) pri->logical_block_size) != SECTOR_SIZE) + || (isonum_723 ((unsigned char *) pri->volume_set_size) != 1) ) + { + return NULL; + } + + /* + * Get the location and size of the root directory. + */ + rootp = (struct iso_directory_record *) + malloc(sizeof(struct iso_directory_record)); + + memcpy(rootp, pri->root_directory_record, sizeof(*rootp)); + + return rootp; +} + +void FDECL3(merge_remaining_entries, struct directory *, this_dir, + struct directory_entry **, pnt, + int, n_orig) +{ + int i; + struct directory_entry * s_entry; + unsigned int ttbl_extent = 0; + unsigned int ttbl_index = 0; + char whole_path[1024]; + + /* + * Whatever is leftover in the list needs to get merged back + * into the directory. + */ + for( i=0; i < n_orig; i++ ) + { + if( pnt[i] == NULL ) + { + continue; + } + + if( pnt[i]->name != NULL && pnt[i]->whole_name == NULL) + { + /* + * Set the name for this directory. + */ + strcpy(whole_path, this_dir->de_name); + strcat(whole_path, SPATH_SEPARATOR); + strcat(whole_path, pnt[i]->name); + + pnt[i]->whole_name = strdup(whole_path); + } + + if( pnt[i]->name != NULL + && strcmp(pnt[i]->name, "") == 0 ) + { + ttbl_extent = isonum_733((unsigned char *) pnt[i]->isorec.extent); + ttbl_index = i; + continue; + } + /* + * Skip directories for now - these need to be treated + * differently. + */ + if( (pnt[i]->isorec.flags[0] & 2) != 0 ) + { + /* + * FIXME - we need to insert this directory into the + * tree, so that the path tables we generate will + * be correct. + */ + if( (strcmp(pnt[i]->name, ".") == 0) + || (strcmp(pnt[i]->name, "..") == 0) ) + { + free(pnt[i]); + pnt[i] = NULL; + continue; + } + else + { + merge_old_directory_into_tree(pnt[i], this_dir); + } + } + pnt[i]->next = this_dir->contents; + pnt[i]->filedir = this_dir; + this_dir->contents = pnt[i]; + pnt[i] = NULL; + } + + + /* + * If we don't have an entry for the translation table, then + * don't bother trying to copy the starting extent over. + * Note that it is possible that if we are copying the entire + * directory, the entry for the translation table will have already + * been inserted into the linked list and removed from the old + * entries list, in which case we want to leave the extent number + * as it was before. + */ + if( ttbl_extent == 0 ) + { + return; + } + + /* + * Finally, check the directory we are creating to see whether + * there are any new entries in it. If there are not, we can + * reuse the same translation table. + */ + for(s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) + { + /* + * Don't care about '.' or '..'. They are never in the table + * anyways. + */ + if( s_entry->name != NULL && strcmp(s_entry->name, ".") == 0 ) + { + continue; + } + if( s_entry->name != NULL && strcmp(s_entry->name, "..") == 0 ) + { + continue; + } + if( strcmp(s_entry->name, "") == 0) + { + continue; + } + if( (s_entry->de_flags & SAFE_TO_REUSE_TABLE_ENTRY) == 0 ) + { + return; + } + } + + /* + * Locate the translation table, and re-use the same extent. + * It isn't clear that there should ever be one in there already + * so for now we try and muddle through the best we can. + */ + for(s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) + { + if( strcmp(s_entry->name, "") == 0) + { + fprintf(stderr,"Should never get here\n"); + set_733(s_entry->isorec.extent, ttbl_extent); + return; + } + } + + pnt[ttbl_index]->next = this_dir->contents; + pnt[ttbl_index]->filedir = this_dir; + this_dir->contents = pnt[ttbl_index]; + pnt[ttbl_index] = NULL; +} + + +/* + * Here we have a case of a directory that has completely disappeared from + * the face of the earth on the tree we are mastering from. Go through and + * merge it into the tree, as well as everything beneath it. + * + * Note that if a directory has been moved for some reason, this will + * incorrectly pick it up and attempt to merge it back into the old + * location. FIXME(eric). + */ +static int +FDECL2(merge_old_directory_into_tree, struct directory_entry *, dpnt, + struct directory *, parent) +{ + struct directory_entry **contents = NULL; + int i; + int n_orig; + struct directory * this_dir, *next_brother; + char whole_path[1024]; + + this_dir = (struct directory *) e_malloc(sizeof(struct directory)); + memset(this_dir, 0, sizeof(struct directory)); + this_dir->next = NULL; + this_dir->subdir = NULL; + this_dir->self = dpnt; + this_dir->contents = NULL; + this_dir->size = 0; + this_dir->extent = 0; + this_dir->depth = parent->depth + 1; + this_dir->parent = parent; + if(!parent->subdir) + parent->subdir = this_dir; + else { + next_brother = parent->subdir; + while(next_brother->next) next_brother = next_brother->next; + next_brother->next = this_dir; + } + + /* + * Set the name for this directory. + */ + strcpy(whole_path, parent->de_name); + strcat(whole_path, SPATH_SEPARATOR); + strcat(whole_path, dpnt->name); + this_dir->de_name = strdup(whole_path); + this_dir->whole_name = strdup(whole_path); + + /* + * Now fill this directory using information from the previous + * session. + */ + contents = read_merging_directory(&dpnt->isorec, &n_orig); + /* + * Start by simply copying the '.', '..' and non-directory + * entries to this directory. Technically we could let + * merge_remaining_entries handle this, but it gets rather confused + * by the '.' and '..' entries. + */ + for(i=0; i < n_orig; i ++ ) + { + /* + * We can always reuse the TRANS.TBL in this particular case. + */ + contents[i]->de_flags |= SAFE_TO_REUSE_TABLE_ENTRY; + + if( ((contents[i]->isorec.flags[0] & 2) != 0) + && (i >= 2) ) + { + continue; + } + + /* + * If we have a directory, don't reuse the extent number. + */ + if( (contents[i]->isorec.flags[0] & 2) != 0 ) + { + memset(contents[i]->isorec.extent, 0, 8); + + if( strcmp(contents[i]->name, ".") == 0 ) + this_dir->dir_flags |= DIR_HAS_DOT; + + if( strcmp(contents[i]->name, "..") == 0 ) + this_dir->dir_flags |= DIR_HAS_DOTDOT; + } + + /* + * Set the whole name for this file. + */ + strcpy(whole_path, this_dir->whole_name); + strcat(whole_path, SPATH_SEPARATOR); + strcat(whole_path, contents[i]->name); + + contents[i]->whole_name = strdup(whole_path); + + contents[i]->next = this_dir->contents; + contents[i]->filedir = this_dir; + this_dir->contents = contents[i]; + contents[i] = NULL; + } + + /* + * Zero the extent number for ourselves. + */ + memset(dpnt->isorec.extent, 0, 8); + + /* + * Anything that is left are other subdirectories that need to be merged. + */ + merge_remaining_entries(this_dir, contents, n_orig); + free_mdinfo(contents, n_orig); +#if 0 + /* + * This is no longer required. The post-scan sort will handle + * all of this for us. + */ + sort_n_finish(this_dir); +#endif + + return 0; +} + + +char * cdwrite_data = NULL; + +int +FDECL1(get_session_start, int *, file_addr) +{ + char * pnt; + +#ifdef CDWRITE_DETERMINES_FIRST_WRITABLE_ADDRESS + /* + * FIXME(eric). We need to coordinate with cdwrite to obtain + * the parameters. For now, we assume we are writing the 2nd session, + * so we start from the session that starts at 0. + */ + + *file_addr = (16 << 11); + + /* + * We need to coordinate with cdwrite to get the next writable address + * from the device. Here is where we use it. + */ + session_start = last_extent = last_extent_written = cdwrite_result(); + +#else + + if( cdwrite_data == NULL ) + { + fprintf(stderr,"Special parameters for cdwrite not specified with -C\n"); + exit(1); + } + + /* + * Next try and find the ',' in there which delimits the two numbers. + */ + pnt = strchr(cdwrite_data, ','); + if( pnt == NULL ) + { + fprintf(stderr, "Malformed cdwrite parameters\n"); + exit(1); + } + + *pnt = '\0'; + if (file_addr != NULL) { + *file_addr = atol(cdwrite_data) * SECTOR_SIZE; + } + pnt++; + + session_start = last_extent = last_extent_written = atol(pnt); + + pnt--; + *pnt = ','; + +#endif + return 0; +} + +/* + * This function scans the directory tree, looking for files, and it makes + * note of everything that is found. We also begin to construct the ISO9660 + * directory entries, so that we can determine how large each directory is. + */ + +int +FDECL2(merge_previous_session,struct directory *, this_dir, + struct iso_directory_record *, mrootp) +{ + struct directory_entry **orig_contents = NULL; + struct directory_entry * odpnt = NULL; + int n_orig; + struct directory_entry * s_entry; + int status, lstatus; + struct stat statbuf, lstatbuf; + + /* + * Parse the same directory in the image that we are merging + * for multisession stuff. + */ + orig_contents = read_merging_directory(mrootp, &n_orig); + if( orig_contents == NULL ) + { + return 0; + } + + +/* Now we scan the directory itself, and look at what is inside of it. */ + + for(s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) + { + status = stat_filter(s_entry->whole_name, &statbuf); + lstatus = lstat_filter(s_entry->whole_name, &lstatbuf); + + /* + * We always should create an entirely new directory tree whenever + * we generate a new session, unless there were *no* changes whatsoever + * to any of the directories, in which case it would be kind of pointless + * to generate a new session. + * + * I believe it is possible to rigorously prove that any change anywhere + * in the filesystem will force the entire tree to be regenerated + * because the modified directory will get a new extent number. Since + * each subdirectory of the changed directory has a '..' entry, all of + * them will need to be rewritten too, and since the parent directory + * of the modified directory will have an extent pointer to the directory + * it too will need to be rewritten. Thus we will never be able to reuse + * any directory information when writing new sessions. + * + * We still check the previous session so we can mark off the equivalent + * entry in the list we got from the original disc, however. + */ + + /* + * The check_prev_session function looks for an identical entry in + * the previous session. If we see it, then we copy the extent + * number to s_entry, and cross it off the list. + */ + check_prev_session(orig_contents, n_orig, s_entry, + &statbuf, &lstatbuf, &odpnt); + + if(S_ISDIR(statbuf.st_mode) && odpnt != NULL) + { + int dflag; + + if (strcmp(s_entry->name,".") && strcmp(s_entry->name,"..")) + { + struct directory * child; + + child = find_or_create_directory(this_dir, + s_entry->whole_name, + s_entry, 1); + dflag = merge_previous_session(child, + &odpnt->isorec); + /* If unable to scan directory, mark this as a non-directory */ + if(!dflag) + lstatbuf.st_mode = (lstatbuf.st_mode & ~S_IFMT) | S_IFREG; + free(odpnt); + odpnt = NULL; + } + } + } + + /* + * Whatever is left over, are things which are no longer in the tree + * on disk. We need to also merge these into the tree. + */ + merge_remaining_entries(this_dir, orig_contents, n_orig); + free_mdinfo(orig_contents, n_orig); + + return 1; +} + diff --git a/util/mkisofs/name.c b/util/mkisofs/name.c new file mode 100644 index 000000000..4df5b9de4 --- /dev/null +++ b/util/mkisofs/name.c @@ -0,0 +1,396 @@ +/* + * File name.c - map full Unix file names to unique 8.3 names that + * would be valid on DOS. + * + + Written by Eric Youngdale (1993). + + Copyright 1993 Yggdrasil Computing, Incorporated + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +static char rcsid[] ="$Id: name.c,v 1.11 1999/03/02 03:41:26 eric Exp $"; + +#include "config.h" +#include "mkisofs.h" + +#include + +extern int allow_leading_dots; + +/* + * Function: iso9660_file_length + * + * Purpose: Map file name to 8.3 format, return length + * of result. + * + * Arguments: name file name we need to map. + * sresult directory entry structure to contain mapped name. + * dirflag flag indicating whether this is a directory or not. + * + * Notes: This procedure probably needs to be rationalized somehow. + * New options to affect the behavior of this function + * would also be nice to have. + */ +int FDECL3(iso9660_file_length, + const char*, name, + struct directory_entry *, sresult, + int, dirflag) +{ + char * c; + int chars_after_dot = 0; + int chars_before_dot = 0; + int current_length = 0; + int extra = 0; + int ignore = 0; + char * last_dot; + const char * pnt; + int priority = 32767; + char * result; + int seen_dot = 0; + int seen_semic = 0; + int tildes = 0; + + result = sresult->isorec.name; + + /* + * For the '.' entry, generate the correct record, and return + * 1 for the length. + */ + if(strcmp(name,".") == 0) + { + if(result) + { + *result = 0; + } + return 1; + } + + /* + * For the '..' entry, generate the correct record, and return + * 1 for the length. + */ + if(strcmp(name,"..") == 0) + { + if(result) + { + *result++ = 1; + *result++ = 0; + } + return 1; + } + + /* + * Now scan the directory one character at a time, and figure out + * what to do. + */ + pnt = name; + + /* + * Find the '.' that we intend to use for the extension. Usually this + * is the last dot, but if we have . followed by nothing or a ~, we + * would consider this to be unsatisfactory, and we keep searching. + */ + last_dot = strrchr (pnt,'.'); + if( (last_dot != NULL) + && ( (last_dot[1] == '~') + || (last_dot[1] == '\0')) ) + { + c = last_dot; + *c = '\0'; + last_dot = strrchr (pnt,'.'); + *c = '.'; + } + + while(*pnt) + { +#ifdef VMS + if( strcmp(pnt,".DIR;1") == 0 ) + { + break; + } +#endif + + /* + * This character indicates a Unix style of backup file + * generated by some editors. Lower the priority of + * the file. + */ + if(*pnt == '#') + { + priority = 1; + pnt++; + continue; + } + + /* + * This character indicates a Unix style of backup file + * generated by some editors. Lower the priority of + * the file. + */ + if(*pnt == '~') + { + priority = 1; + tildes++; + pnt++; + continue; + } + + /* + * This might come up if we had some joker already try and put + * iso9660 version numbers into the file names. This would be + * a silly thing to do on a Unix box, but we check for it + * anyways. If we see this, then we don't have to add our + * own version number at the end. + * UNLESS the ';' is part of the filename and no version + * number is following. [VK] + */ + if(*pnt == ';') + { + /* [VK] */ + if (pnt[1] != '\0' && (pnt[1] < '0' || pnt[1] > '9')) + { + pnt++; + ignore++; + continue; + } + } + + /* + * If we have a name with multiple '.' characters, we ignore everything + * after we have gotten the extension. + */ + if(ignore) + { + pnt++; + continue; + } + + /* + * Spin past any iso9660 version number we might have. + */ + if(seen_semic) + { + if(*pnt >= '0' && *pnt <= '9') + { + *result++ = *pnt; + } + extra++; + pnt++; + continue; + } + + /* + * If we have full names, the names we generate will not + * work on a DOS machine, since they are not guaranteed + * to be 8.3. Nonetheless, in many cases this is a useful + * option. We still only allow one '.' character in the + * name, however. + */ + if(full_iso9660_filenames) + { + /* Here we allow a more relaxed syntax. */ + if(*pnt == '.') + { + if (seen_dot) + { + ignore++; + continue; + } + seen_dot++; + } + if(current_length < 30) + { + if( *pnt < 0 ) + { + *result++ = '_'; + } + else + { + *result++ = (islower((unsigned char)*pnt) ? toupper((unsigned char)*pnt) : *pnt); + } + } + } + else + { + /* + * Dos style filenames. We really restrict the + * names here. + */ + /* It would be nice to have .tar.gz transform to .tgz, + * .ps.gz to .psz, ... + */ + if(*pnt == '.') + { + if (!chars_before_dot && !allow_leading_dots) + { + /* DOS can't read files with dot first */ + chars_before_dot++; + if (result) + { + *result++ = '_'; /* Substitute underscore */ + } + } + else if( pnt != last_dot ) + { + /* + * If this isn't the dot that we use for the extension, + * then change the character into a '_' instead. + */ + if(chars_before_dot < 8) + { + chars_before_dot++; + if(result) + { + *result++ = '_'; + } + } + } + else + { + if (seen_dot) + { + ignore++; continue; + } + if(result) + { + *result++ = '.'; + } + seen_dot++; + } + } + else + { + if( (seen_dot && (chars_after_dot < 3) && ++chars_after_dot) + || (!seen_dot && (chars_before_dot < 8) && ++chars_before_dot) ) + { + if(result) + { + switch (*pnt) + { + default: + if( *pnt < 0 ) + { + *result++ = '_'; + } + else + { + *result++ = islower((unsigned char)*pnt) ? toupper((unsigned char)*pnt) : *pnt; + } + break; + + /* + * Descriptions of DOS's 'Parse Filename' + * (function 29H) describes V1 and V2.0+ + * separator and terminator characters. + * These characters in a DOS name make + * the file visible but un-manipulable + * (all useful operations error off. + */ + /* separators */ + case '+': + case '=': + case '%': /* not legal DOS filename */ + case ':': + case ';': /* already handled */ + case '.': /* already handled */ + case ',': /* already handled */ + case '\t': + case ' ': + /* V1 only separators */ + case '/': + case '"': + case '[': + case ']': + /* terminators */ + case '>': + case '<': + case '|': + /* Hmm - what to do here? Skip? + * Win95 looks like it substitutes '_' + */ + *result++ = '_'; + break; + } /* switch (*pnt) */ + } /* if (result) */ + } /* if (chars_{after,before}_dot) ... */ + } /* else *pnt == '.' */ + } /* else DOS file names */ + current_length++; + pnt++; + } /* while (*pnt) */ + + /* + * OK, that wraps up the scan of the name. Now tidy up a few other + * things. + */ + + /* + * Look for emacs style of numbered backups, like foo.c.~3~. If + * we see this, convert the version number into the priority + * number. In case of name conflicts, this is what would end + * up being used as the 'extension'. + */ + if(tildes == 2) + { + int prio1 = 0; + pnt = name; + while (*pnt && *pnt != '~') + { + pnt++; + } + if (*pnt) + { + pnt++; + } + while(*pnt && *pnt != '~') + { + prio1 = 10*prio1 + *pnt - '0'; + pnt++; + } + priority = prio1; + } + + /* + * If this is not a directory, force a '.' in case we haven't + * seen one, and add a version number if we haven't seen one + * of those either. + */ + if (!dirflag) + { + if (!seen_dot && !omit_period) + { + if (result) *result++ = '.'; + extra++; + } + if(!omit_version_number && !seen_semic) + { + if(result) + { + *result++ = ';'; + *result++ = '1'; + }; + extra += 2; + } + } + + if(result) + { + *result++ = 0; + } + sresult->priority = priority; + + return (chars_before_dot + chars_after_dot + seen_dot + extra); +} diff --git a/util/mkisofs/rock.c b/util/mkisofs/rock.c new file mode 100644 index 000000000..f2b34fe1b --- /dev/null +++ b/util/mkisofs/rock.c @@ -0,0 +1,601 @@ +/* + * File rock.c - generate RRIP records for iso9660 filesystems. + + Written by Eric Youngdale (1993). + + Copyright 1993 Yggdrasil Computing, Incorporated + + Copyright (C) 2009 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, 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 . + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +static char rcsid[] ="$Id: rock.c,v 1.8 1999/03/02 03:41:26 eric Exp $"; + +#include + +#include "config.h" + +#ifndef VMS +#if defined(MAJOR_IN_SYSMACROS) +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#endif +#if defined(MAJOR_IN_MKDEV) +#include +#include +#endif + +#include "mkisofs.h" +#include "iso9660.h" +#include +#include + +#ifdef DOESNT_WORK + +#ifdef NON_UNIXFS +#define S_ISLNK(m) (0) +#else +#ifndef S_ISLNK +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#endif + +#else +#include +#endif + +#define SU_VERSION 1 + +#define SL_ROOT 8 +#define SL_PARENT 4 +#define SL_CURRENT 2 +#define SL_CONTINUE 1 + +#define CE_SIZE 28 +#define CL_SIZE 12 +#define ER_SIZE 8 +#define NM_SIZE 5 +#define PL_SIZE 12 +#define PN_SIZE 20 +#define PX_SIZE 36 +#define RE_SIZE 4 +#define SL_SIZE 20 +#define ZZ_SIZE 15 +#ifdef __QNX__ +#define TF_SIZE (5 + 4 * 7) +#else +#define TF_SIZE (5 + 3 * 7) +#endif + +/* If we need to store this number of bytes, make sure we + do not box ourselves in so that we do not have room for + a CE entry for the continuation record */ + +#define MAYBE_ADD_CE_ENTRY(BYTES) \ + ((unsigned) ((BYTES) + CE_SIZE + currlen + ipnt) > (unsigned) (recstart + reclimit) ? 1 : 0) + +/* + * Buffer to build RR attributes + */ + +static unsigned char Rock[16384]; +static unsigned char symlink_buff[256]; +static int ipnt = 0; +static int recstart = 0; +static int currlen = 0; +static int mainrec = 0; +static int reclimit; + +static void add_CE_entry __PR((void)); + +static void add_CE_entry(){ + if(recstart) + set_733((char*)Rock + recstart - 8, ipnt + 28 - recstart); + Rock[ipnt++] ='C'; + Rock[ipnt++] ='E'; + Rock[ipnt++] = CE_SIZE; + Rock[ipnt++] = SU_VERSION; + set_733((char*)Rock + ipnt, 0); + ipnt += 8; + set_733((char*)Rock + ipnt, 0); + ipnt += 8; + set_733((char*)Rock + ipnt, 0); + ipnt += 8; + recstart = ipnt; + currlen = 0; + if(!mainrec) mainrec = ipnt; + reclimit = SECTOR_SIZE - 8; /* Limit to one sector */ +} + +#ifdef __STDC__ +int generate_rock_ridge_attributes (char * whole_name, char * name, + struct directory_entry * s_entry, + struct stat * statbuf, + struct stat * lstatbuf, + int deep_opt) +#else +int generate_rock_ridge_attributes (whole_name, name, + s_entry, + statbuf, + lstatbuf, + deep_opt) +char * whole_name; char * name; struct directory_entry * s_entry; +struct stat * statbuf, *lstatbuf; +int deep_opt; +#endif +{ + int flagpos, flagval; + int need_ce; + + statbuf = statbuf; /* this shuts up unreferenced compiler warnings */ + mainrec = recstart = ipnt = 0; + reclimit = 0xf8; + + /* no need to fill in the RR stuff if we won't see the file */ + if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) + return 0; + + /* Obtain the amount of space that is currently used for the directory + record. Assume max for name, since name conflicts may cause us + to rename the file later on */ + currlen = sizeof(s_entry->isorec); + + /* Identify that we are using the SUSP protocol */ + if(deep_opt & NEED_SP){ + Rock[ipnt++] ='S'; + Rock[ipnt++] ='P'; + Rock[ipnt++] = 7; + Rock[ipnt++] = SU_VERSION; + Rock[ipnt++] = 0xbe; + Rock[ipnt++] = 0xef; + Rock[ipnt++] = 0; + }; + + /* First build the posix name field */ + Rock[ipnt++] ='R'; + Rock[ipnt++] ='R'; + Rock[ipnt++] = 5; + Rock[ipnt++] = SU_VERSION; + flagpos = ipnt; + flagval = 0; + Rock[ipnt++] = 0; /* We go back and fix this later */ + + if(strcmp(name,".") && strcmp(name,"..")){ + char * npnt; + int remain, use; + + remain = strlen(name); + npnt = name; + + while(remain){ + use = remain; + need_ce = 0; + /* Can we fit this SUSP and a CE entry? */ + if(use + currlen + CE_SIZE + (ipnt - recstart) > reclimit) { + use = reclimit - currlen - CE_SIZE - (ipnt - recstart); + need_ce++; + } + + /* Only room for 256 per SUSP field */ + if(use > 0xf8) use = 0xf8; + + /* First build the posix name field */ + Rock[ipnt++] ='N'; + Rock[ipnt++] ='M'; + Rock[ipnt++] = NM_SIZE + use; + Rock[ipnt++] = SU_VERSION; + Rock[ipnt++] = (remain != use ? 1 : 0); + flagval |= (1<<3); + strncpy((char *)&Rock[ipnt], npnt, use); + npnt += use; + ipnt += use; + remain -= use; + if(remain && need_ce) add_CE_entry(); + }; + }; + + /* + * Add the posix modes + */ + if(MAYBE_ADD_CE_ENTRY(PX_SIZE)) add_CE_entry(); + Rock[ipnt++] ='P'; + Rock[ipnt++] ='X'; + Rock[ipnt++] = PX_SIZE; + Rock[ipnt++] = SU_VERSION; + flagval |= (1<<0); + set_733((char*)Rock + ipnt, lstatbuf->st_mode); + ipnt += 8; + set_733((char*)Rock + ipnt, lstatbuf->st_nlink); + ipnt += 8; + set_733((char*)Rock + ipnt, lstatbuf->st_uid); + ipnt += 8; + set_733((char*)Rock + ipnt, lstatbuf->st_gid); + ipnt += 8; + + /* + * Check for special devices + */ +#ifndef NON_UNIXFS + if (S_ISCHR(lstatbuf->st_mode) || S_ISBLK(lstatbuf->st_mode)) { + if(MAYBE_ADD_CE_ENTRY(PN_SIZE)) add_CE_entry(); + Rock[ipnt++] ='P'; + Rock[ipnt++] ='N'; + Rock[ipnt++] = PN_SIZE; + Rock[ipnt++] = SU_VERSION; + flagval |= (1<<1); +#if defined(MAJOR_IN_SYSMACROS) || defined(MAJOR_IN_MKDEV) + set_733((char*)Rock + ipnt, major(lstatbuf->st_rdev )); + ipnt += 8; + set_733((char*)Rock + ipnt, minor(lstatbuf->st_rdev)); + ipnt += 8; +#else + /* + * If we don't have sysmacros.h, then we have to guess as to how + * best to pick apart the device number for major/minor. + * Note: this may very well be wrong for many systems, so + * it is always best to use the major/minor macros if the + * system supports it. + */ + if(sizeof(dev_t) <= 2) { + set_733((char*)Rock + ipnt, (lstatbuf->st_rdev >> 8)); + ipnt += 8; + set_733((char*)Rock + ipnt, lstatbuf->st_rdev & 0xff); + ipnt += 8; + } + else if(sizeof(dev_t) <= 4) { + set_733((char*)Rock + ipnt, (lstatbuf->st_rdev >> 8) >> 8); + ipnt += 8; + set_733((char*)Rock + ipnt, lstatbuf->st_rdev & 0xffff); + ipnt += 8; + } + else { + set_733((char*)Rock + ipnt, (lstatbuf->st_rdev >> 16) >> 16); + ipnt += 8; + set_733((char*)Rock + ipnt, lstatbuf->st_rdev); + ipnt += 8; + } +#endif + }; +#endif + /* + * Check for and symbolic links. VMS does not have these. + */ + if (S_ISLNK(lstatbuf->st_mode)){ + int lenpos, lenval, j0, j1; + int nchar; + unsigned char * cpnt, *cpnt1; + nchar = readlink(whole_name, (char *)symlink_buff, sizeof(symlink_buff)); + symlink_buff[nchar < 0 ? 0 : nchar] = 0; + nchar = strlen((char *) symlink_buff); + set_733(s_entry->isorec.size, 0); + cpnt = &symlink_buff[0]; + flagval |= (1<<2); + + if (! split_SL_field) + { + int sl_bytes = 0; + for (cpnt1 = cpnt; *cpnt1 != '\0'; cpnt1++) + { + if (*cpnt1 == '/') + { + sl_bytes += 4; + } + else + { + sl_bytes += 1; + } + } + if (sl_bytes > 250) + { + /* + * the symbolic link won't fit into one SL System Use Field + * print an error message and continue with splited one + */ + fprintf(stderr,"symbolic link ``%s'' to long for one SL System Use Field, splitting", cpnt); + } + if(MAYBE_ADD_CE_ENTRY(SL_SIZE + sl_bytes)) add_CE_entry(); + } + + while(nchar){ + if(MAYBE_ADD_CE_ENTRY(SL_SIZE)) add_CE_entry(); + Rock[ipnt++] ='S'; + Rock[ipnt++] ='L'; + lenpos = ipnt; + Rock[ipnt++] = SL_SIZE; + Rock[ipnt++] = SU_VERSION; + Rock[ipnt++] = 0; /* Flags */ + lenval = 5; + while(*cpnt){ + cpnt1 = (unsigned char *) strchr((char *) cpnt, '/'); + if(cpnt1) { + nchar--; + *cpnt1 = 0; + }; + + /* We treat certain components in a special way. */ + if(cpnt[0] == '.' && cpnt[1] == '.' && cpnt[2] == 0){ + if(MAYBE_ADD_CE_ENTRY(2)) add_CE_entry(); + Rock[ipnt++] = SL_PARENT; + Rock[ipnt++] = 0; /* length is zero */ + lenval += 2; + nchar -= 2; + } else if(cpnt[0] == '.' && cpnt[1] == 0){ + if(MAYBE_ADD_CE_ENTRY(2)) add_CE_entry(); + Rock[ipnt++] = SL_CURRENT; + Rock[ipnt++] = 0; /* length is zero */ + lenval += 2; + nchar -= 1; + } else if(cpnt[0] == 0){ + if(MAYBE_ADD_CE_ENTRY(2)) add_CE_entry(); + Rock[ipnt++] = SL_ROOT; + Rock[ipnt++] = 0; /* length is zero */ + lenval += 2; + } else { + /* If we do not have enough room for a component, start + a new continuations segment now */ + if(split_SL_component ? MAYBE_ADD_CE_ENTRY(6) : + MAYBE_ADD_CE_ENTRY(6 + strlen ((char *) cpnt))) + { + add_CE_entry(); + if(cpnt1) + { + *cpnt1 = '/'; + nchar++; + cpnt1 = NULL; /* A kluge so that we can restart properly */ + } + break; + } + j0 = strlen((char *) cpnt); + while(j0) { + j1 = j0; + if(j1 > 0xf8) j1 = 0xf8; + need_ce = 0; + if(j1 + currlen + CE_SIZE + (ipnt - recstart) > reclimit) { + j1 = reclimit - currlen - CE_SIZE - (ipnt - recstart); + need_ce++; + } + Rock[ipnt++] = (j1 != j0 ? SL_CONTINUE : 0); + Rock[ipnt++] = j1; + strncpy((char *) Rock + ipnt, (char *) cpnt, j1); + ipnt += j1; + lenval += j1 + 2; + cpnt += j1; + nchar -= j1; /* Number we processed this time */ + j0 -= j1; + if(need_ce) { + add_CE_entry(); + if(cpnt1) { + *cpnt1 = '/'; + nchar++; + cpnt1 = NULL; /* A kluge so that we can restart properly */ + } + break; + } + } + }; + if(cpnt1) { + cpnt = cpnt1 + 1; + } else + break; + } + Rock[lenpos] = lenval; + if(nchar) Rock[lenpos + 2] = SL_CONTINUE; /* We need another SL entry */ + } /* while nchar */ + } /* Is a symbolic link */ + /* + * Add in the Rock Ridge TF time field + */ + if(MAYBE_ADD_CE_ENTRY(TF_SIZE)) add_CE_entry(); + Rock[ipnt++] ='T'; + Rock[ipnt++] ='F'; + Rock[ipnt++] = TF_SIZE; + Rock[ipnt++] = SU_VERSION; +#ifdef __QNX__ + Rock[ipnt++] = 0x0f; +#else + Rock[ipnt++] = 0x0e; +#endif + flagval |= (1<<7); +#ifdef __QNX__ + iso9660_date((char *) &Rock[ipnt], lstatbuf->st_ftime); + ipnt += 7; +#endif + iso9660_date((char *) &Rock[ipnt], lstatbuf->st_mtime); + ipnt += 7; + iso9660_date((char *) &Rock[ipnt], lstatbuf->st_atime); + ipnt += 7; + iso9660_date((char *) &Rock[ipnt], lstatbuf->st_ctime); + ipnt += 7; + + /* + * Add in the Rock Ridge RE time field + */ + if(deep_opt & NEED_RE){ + if(MAYBE_ADD_CE_ENTRY(RE_SIZE)) add_CE_entry(); + Rock[ipnt++] ='R'; + Rock[ipnt++] ='E'; + Rock[ipnt++] = RE_SIZE; + Rock[ipnt++] = SU_VERSION; + flagval |= (1<<6); + }; + /* + * Add in the Rock Ridge PL record, if required. + */ + if(deep_opt & NEED_PL){ + if(MAYBE_ADD_CE_ENTRY(PL_SIZE)) add_CE_entry(); + Rock[ipnt++] ='P'; + Rock[ipnt++] ='L'; + Rock[ipnt++] = PL_SIZE; + Rock[ipnt++] = SU_VERSION; + set_733((char*)Rock + ipnt, 0); + ipnt += 8; + flagval |= (1<<5); + }; + + /* + * Add in the Rock Ridge CL field, if required. + */ + if(deep_opt & NEED_CL){ + if(MAYBE_ADD_CE_ENTRY(CL_SIZE)) add_CE_entry(); + Rock[ipnt++] ='C'; + Rock[ipnt++] ='L'; + Rock[ipnt++] = CL_SIZE; + Rock[ipnt++] = SU_VERSION; + set_733((char*)Rock + ipnt, 0); + ipnt += 8; + flagval |= (1<<4); + }; + +#ifndef VMS + /* If transparent compression was requested, fill in the correct + field for this file */ + if(transparent_compression && + S_ISREG(lstatbuf->st_mode) && + strlen(name) > 3 && + strcmp(name + strlen(name) - 3,".gZ") == 0){ + FILE * zipfile; + char * checkname; + unsigned int file_size; + unsigned char header[8]; + int OK_flag; + + /* First open file and verify that the correct algorithm was used */ + file_size = 0; + OK_flag = 1; + + zipfile = fopen(whole_name, "rb"); + if (fread (header, 1, sizeof (header), zipfile) != sizeof(header)) + error (1, errno, "fread"); + + /* Check some magic numbers from gzip. */ + if(header[0] != 0x1f || header[1] != 0x8b || header[2] != 8) OK_flag = 0; + /* Make sure file was blocksized. */ + if(((header[3] & 0x40) == 0)) OK_flag = 0; + /* OK, now go to the end of the file and get some more info */ + if(OK_flag){ + int status; + status = (long)lseek(fileno(zipfile), (off_t)(-8), SEEK_END); + if(status == -1) OK_flag = 0; + } + if(OK_flag){ + if(read(fileno(zipfile), (char*)header, sizeof(header)) != sizeof(header)) + OK_flag = 0; + else { + int blocksize; + blocksize = (header[3] << 8) | header[2]; + file_size = ((unsigned int)header[7] << 24) | + ((unsigned int)header[6] << 16) | + ((unsigned int)header[5] << 8) | header[4]; +#if 0 + fprintf(stderr,"Blocksize = %d %d\n", blocksize, file_size); +#endif + if(blocksize != SECTOR_SIZE) OK_flag = 0; + } + } + fclose(zipfile); + + checkname = strdup(whole_name); + checkname[strlen(whole_name)-3] = 0; + zipfile = fopen(checkname, "rb"); + if(zipfile) { + OK_flag = 0; + fprintf(stderr,"Unable to insert transparent compressed file - name conflict\n"); + fclose(zipfile); + } + + free(checkname); + + if(OK_flag){ + if(MAYBE_ADD_CE_ENTRY(ZZ_SIZE)) add_CE_entry(); + Rock[ipnt++] ='Z'; + Rock[ipnt++] ='Z'; + Rock[ipnt++] = ZZ_SIZE; + Rock[ipnt++] = SU_VERSION; + Rock[ipnt++] = 'g'; /* Identify compression technique used */ + Rock[ipnt++] = 'z'; + Rock[ipnt++] = 3; + set_733((char*)Rock + ipnt, file_size); /* Real file size */ + ipnt += 8; + }; + } +#endif + /* + * Add in the Rock Ridge CE field, if required. We use this for the + * extension record that is stored in the root directory. + */ + if(deep_opt & NEED_CE) add_CE_entry(); + /* + * Done filling in all of the fields. Now copy it back to a buffer for the + * file in question. + */ + + /* Now copy this back to the buffer for the file */ + Rock[flagpos] = flagval; + + /* If there was a CE, fill in the size field */ + if(recstart) + set_733((char*)Rock + recstart - 8, ipnt - recstart); + + s_entry->rr_attributes = (unsigned char *) e_malloc(ipnt); + s_entry->total_rr_attr_size = ipnt; + s_entry->rr_attr_size = (mainrec ? mainrec : ipnt); + memcpy(s_entry->rr_attributes, Rock, ipnt); + return ipnt; +} + +/* Guaranteed to return a single sector with the relevant info */ + +char * FDECL4(generate_rr_extension_record, char *, id, char *, descriptor, + char *, source, int *, size){ + int lipnt = 0; + char * pnt; + int len_id, len_des, len_src; + + len_id = strlen(id); + len_des = strlen(descriptor); + len_src = strlen(source); + Rock[lipnt++] ='E'; + Rock[lipnt++] ='R'; + Rock[lipnt++] = ER_SIZE + len_id + len_des + len_src; + Rock[lipnt++] = 1; + Rock[lipnt++] = len_id; + Rock[lipnt++] = len_des; + Rock[lipnt++] = len_src; + Rock[lipnt++] = 1; + + memcpy(Rock + lipnt, id, len_id); + lipnt += len_id; + + memcpy(Rock + lipnt, descriptor, len_des); + lipnt += len_des; + + memcpy(Rock + lipnt, source, len_src); + lipnt += len_src; + + if(lipnt > SECTOR_SIZE) { + fprintf(stderr,"Extension record too long\n"); + exit(1); + }; + pnt = (char *) e_malloc(SECTOR_SIZE); + memset(pnt, 0, SECTOR_SIZE); + memcpy(pnt, Rock, lipnt); + *size = lipnt; + return pnt; +} diff --git a/util/mkisofs/tree.c b/util/mkisofs/tree.c new file mode 100644 index 000000000..355dd9873 --- /dev/null +++ b/util/mkisofs/tree.c @@ -0,0 +1,1900 @@ +/* + * File tree.c - scan directory tree and build memory structures for iso9660 + * filesystem + + Written by Eric Youngdale (1993). + + Copyright 1993 Yggdrasil Computing, Incorporated + + Copyright (C) 2009 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, 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 . + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +static char rcsid[] ="$Id: tree.c,v 1.29 1999/03/07 17:41:19 eric Exp $"; + +/* ADD_FILES changes made by Ross Biro biro@yggdrasil.com 2/23/95 */ + +#include +#include +#include +#include + +#include "config.h" + +#ifndef VMS +#if defined(MAJOR_IN_SYSMACROS) +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +#if defined(MAJOR_IN_MKDEV) +#include +#include +#endif +#else +#include +#include +#include "vms.h" +extern char * strdup(const char *); +#endif + +/* + * Autoconf should be able to figure this one out for us and let us know + * whether the system has memmove or not. + */ +# ifndef HAVE_MEMMOVE +# define memmove(d, s, n) bcopy ((s), (d), (n)) +# endif + +#include "mkisofs.h" +#include "iso9660.h" +#include "match.h" + +#include + +#include "exclude.h" + +#ifdef DOESNT_WORK + +#ifdef NON_UNIXFS +#define S_ISLNK(m) (0) +#define S_ISSOCK(m) (0) +#define S_ISFIFO(m) (0) +#else +#ifndef S_ISLNK +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#ifndef S_ISSOCK +# ifdef S_IFSOCK +# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +# else +# define S_ISSOCK(m) (0) +# endif +#endif +#endif + +#else +#include +#endif + + +#ifdef __SVR4 +extern char * strdup(const char *); +#endif + +static unsigned char symlink_buff[256]; + +static void stat_fix __PR((struct stat * st)); +static void generate_reloc_directory __PR((void)); + +static void DECL(attach_dot_entries, (struct directory * dirnode, + struct stat * parent_stat)); +static void DECL(delete_directory, (struct directory * parent, struct directory * child)); + +extern int verbose; + +struct stat fstatbuf; /* We use this for the artificial entries we create */ + +struct stat root_statbuf; /* Stat buffer for root directory */ + +struct directory * reloc_dir = NULL; + +static void +FDECL1(stat_fix, struct stat *, st) +{ + /* Remove the uid and gid, they will only be useful on the author's + system. */ + st->st_uid = 0; + st->st_gid = 0; + + /* + * Make sure the file modes make sense. Turn on all read bits. Turn + * on all exec/search bits if any exec/search bit is set. Turn off + * all write bits, and all special mode bits (on a r/o fs lock bits + * are useless, and with uid+gid 0 don't want set-id bits, either). + */ + st->st_mode |= 0444; +#ifndef _WIN32 /* make all file "executable" */ + if (st->st_mode & 0111) +#endif /* _WIN32 */ + st->st_mode |= 0111; + st->st_mode &= ~07222; +} + +int +FDECL2(stat_filter, char *, path, struct stat *, st) +{ + int result = stat(path, st); + if (result >= 0 && rationalize) + stat_fix(st); + + if ((unsigned) st->st_size > UINT32_MAX) + result = -1; + + return result; +} + +int +FDECL2(lstat_filter, char *, path, struct stat *, st) +{ + int result = lstat(path, st); + if (result >= 0 && rationalize) + stat_fix(st); + + if ((unsigned) st->st_size > UINT32_MAX) + result = -1; + + return result; +} + +static int FDECL1(sort_n_finish, struct directory *, this_dir) +{ + struct directory_entry * s_entry; + struct directory_entry * s_entry1; + struct directory_entry * table; + int count; + int d1; + int d2; + int d3; + int new_reclen; + char * c; + int status = 0; + int tablesize = 0; + char newname[34]; + char rootname[34]; + + /* Here we can take the opportunity to toss duplicate entries from the + directory. */ + + /* ignore if it's hidden */ + if(this_dir->dir_flags & INHIBIT_ISO9660_ENTRY) + { + return 0; + } + + table = NULL; + + init_fstatbuf(); + + /* + * If we had artificially created this directory, then we might be + * missing the required '.' entries. Create these now if we need + * them. + */ + if( (this_dir->dir_flags & (DIR_HAS_DOT | DIR_HAS_DOTDOT)) != + (DIR_HAS_DOT | DIR_HAS_DOTDOT) ) + { + attach_dot_entries(this_dir, &fstatbuf); + } + + flush_file_hash(); + s_entry = this_dir->contents; + while(s_entry) + { + /* ignore if it's hidden */ + if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) + { + s_entry = s_entry->next; + continue; + } + + /* + * First assume no conflict, and handle this case + */ + if(!(s_entry1 = find_file_hash(s_entry->isorec.name))) + { + add_file_hash(s_entry); + s_entry = s_entry->next; + continue; + } + + if(s_entry1 == s_entry) + { + fprintf(stderr,"Fatal goof\n"); + exit(1); + } + + /* + * OK, handle the conflicts. Try substitute names until we come + * up with a winner + */ + strcpy(rootname, s_entry->isorec.name); + if(full_iso9660_filenames) + { + if(strlen(rootname) > 27) rootname[27] = 0; + } + + /* + * Strip off the non-significant part of the name so that we are left + * with a sensible root filename. If we don't find a '.', then try + * a ';'. + */ + c = strchr(rootname, '.'); + if (c) + *c = 0; + else + { + c = strchr(rootname, ';'); + if (c) *c = 0; + } + for(d1 = 0; d1 < 36; d1++) + { + for(d2 = 0; d2 < 36; d2++) + { + for(d3 = 0; d3 < 36; d3++) + { + sprintf(newname,"%s.%c%c%c%s", rootname, + (d1 <= 9 ? '0' + d1 : 'A' + d1 - 10), + (d2 <= 9 ? '0' + d2 : 'A' + d2 - 10), + (d3 <= 9 ? '0' + d3 : 'A' + d3 - 10), + (s_entry->isorec.flags[0] == 2 || + omit_version_number ? "" : ";1")); + +#ifdef VMS + /* Sigh. VAXCRTL seems to be broken here */ + { + int ijk = 0; + while(newname[ijk]) + { + if(newname[ijk] == ' ') newname[ijk] = '0'; + ijk++; + } + } +#endif + + if(!find_file_hash(newname)) goto got_valid_name; + } + } + } + + /* + * If we fell off the bottom here, we were in real trouble. + */ + fprintf(stderr,"Unable to generate unique name for file %s\n", s_entry->name); + exit(1); + +got_valid_name: + /* + * OK, now we have a good replacement name. Now decide which one + * of these two beasts should get the name changed + */ + if(s_entry->priority < s_entry1->priority) + { + if( verbose > 0 ) + { + fprintf(stderr,"Using %s for %s%s%s (%s)\n", newname, + this_dir->whole_name, SPATH_SEPARATOR, + s_entry->name, s_entry1->name); + } + s_entry->isorec.name_len[0] = strlen(newname); + new_reclen = sizeof(struct iso_directory_record) - + sizeof(s_entry->isorec.name) + + strlen(newname); + if(use_RockRidge) + { + if (new_reclen & 1) new_reclen++; /* Pad to an even byte */ + new_reclen += s_entry->rr_attr_size; + } + if (new_reclen & 1) new_reclen++; /* Pad to an even byte */ + s_entry->isorec.length[0] = new_reclen; + strcpy(s_entry->isorec.name, newname); + } + else + { + delete_file_hash(s_entry1); + if( verbose > 0 ) + { + fprintf(stderr,"Using %s for %s%s%s (%s)\n", newname, + this_dir->whole_name, SPATH_SEPARATOR, + s_entry1->name, s_entry->name); + } + s_entry1->isorec.name_len[0] = strlen(newname); + new_reclen = sizeof(struct iso_directory_record) - + sizeof(s_entry1->isorec.name) + + strlen(newname); + if(use_RockRidge) + { + if (new_reclen & 1) new_reclen++; /* Pad to an even byte */ + new_reclen += s_entry1->rr_attr_size; + } + if (new_reclen & 1) new_reclen++; /* Pad to an even byte */ + s_entry1->isorec.length[0] = new_reclen; + strcpy(s_entry1->isorec.name, newname); + add_file_hash(s_entry1); + } + add_file_hash(s_entry); + s_entry = s_entry->next; + } + + if(generate_tables + && !find_file_hash("TRANS.TBL") + && (reloc_dir != this_dir) + && (this_dir->extent == 0) ) + { + /* + * First we need to figure out how big this table is + */ + for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) + { + if(strcmp(s_entry->name, ".") == 0 || + strcmp(s_entry->name, "..") == 0) continue; + if(s_entry->de_flags & INHIBIT_ISO9660_ENTRY) continue; + if(s_entry->table) tablesize += 35 + strlen(s_entry->table); + } + } + + if( tablesize > 0 ) + { + table = (struct directory_entry *) + e_malloc(sizeof (struct directory_entry)); + memset(table, 0, sizeof(struct directory_entry)); + table->table = NULL; + table->next = this_dir->contents; + this_dir->contents = table; + + table->filedir = root; + table->isorec.flags[0] = 0; + table->priority = 32768; + iso9660_date(table->isorec.date, fstatbuf.st_mtime); + table->inode = TABLE_INODE; + table->dev = (dev_t) UNCACHED_DEVICE; + set_723(table->isorec.volume_sequence_number, volume_sequence_number); + set_733((char *) table->isorec.size, tablesize); + table->size = tablesize; + table->filedir = this_dir; +#ifdef ERIC_neverdef + table->de_flags |= INHIBIT_JOLIET_ENTRY; +#endif + table->name = strdup(""); + table->table = (char *) e_malloc(ROUND_UP(tablesize)); + memset(table->table, 0, ROUND_UP(tablesize)); + iso9660_file_length ("TRANS.TBL", table, 0); + + if(use_RockRidge) + { + fstatbuf.st_mode = 0444 | S_IFREG; + fstatbuf.st_nlink = 1; + generate_rock_ridge_attributes("", + "TRANS.TBL", table, + &fstatbuf, &fstatbuf, 0); + } + } + + /* + * We have now chosen the 8.3 names and we should now know the length + * of every entry in the directory. + */ + for(s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) + { + /* skip if it's hidden */ + if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) + { + continue; + } + + new_reclen = strlen(s_entry->isorec.name); + + /* + * First update the path table sizes for directories. + */ + if(s_entry->isorec.flags[0] == 2) + { + if (strcmp(s_entry->name,".") && strcmp(s_entry->name,"..")) + { + path_table_size += new_reclen + sizeof(struct iso_path_table) - 1; + if (new_reclen & 1) path_table_size++; + } + else + { + new_reclen = 1; + if (this_dir == root && strlen(s_entry->name) == 1) + { + path_table_size += sizeof(struct iso_path_table); + } + } + } + if(path_table_size & 1) path_table_size++; /* For odd lengths we pad */ + s_entry->isorec.name_len[0] = new_reclen; + + new_reclen += + sizeof(struct iso_directory_record) - + sizeof(s_entry->isorec.name); + + if (new_reclen & 1) + new_reclen++; + + new_reclen += s_entry->rr_attr_size; + + if (new_reclen & 1) new_reclen++; + + if(new_reclen > 0xff) + { + fprintf(stderr,"Fatal error - RR overflow for file %s\n", + s_entry->name); + exit(1); + } + s_entry->isorec.length[0] = new_reclen; + } + + status = sort_directory(&this_dir->contents); + if( status > 0 ) + { + fprintf(stderr, "Unable to sort directory %s\n", + this_dir->whole_name); + } + + /* + * If we are filling out a TRANS.TBL, generate the entries that will + * go in the thing. + */ + if(table) + { + count = 0; + for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next){ + if(s_entry == table) continue; + if(!s_entry->table) continue; + if(strcmp(s_entry->name, ".") == 0 || + strcmp(s_entry->name, "..") == 0) continue; + if(s_entry->de_flags & INHIBIT_ISO9660_ENTRY) continue; + /* + * Warning: we cannot use the return value of sprintf because + * old BSD based sprintf() implementations will return + * a pointer to the result instead of a count. + */ + sprintf(table->table + count, "%c %-34s%s", + s_entry->table[0], + s_entry->isorec.name, s_entry->table+1); + count += strlen(table->table + count); + free(s_entry->table); + s_entry->table = NULL; + } + + if(count != tablesize) + { + fprintf(stderr,"Translation table size mismatch %d %d\n", + count, tablesize); + exit(1); + } + } + + /* + * Now go through the directory and figure out how large this one will be. + * Do not split a directory entry across a sector boundary + */ + s_entry = this_dir->contents; + this_dir->ce_bytes = 0; + while(s_entry) + { + /* skip if it's hidden */ + if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) { + s_entry = s_entry->next; + continue; + } + + new_reclen = s_entry->isorec.length[0]; + if ((this_dir->size & (SECTOR_SIZE - 1)) + new_reclen >= SECTOR_SIZE) + this_dir->size = (this_dir->size + (SECTOR_SIZE - 1)) & + ~(SECTOR_SIZE - 1); + this_dir->size += new_reclen; + + /* See if continuation entries were used on disc */ + if(use_RockRidge && + s_entry->rr_attr_size != s_entry->total_rr_attr_size) + { + unsigned char * pnt; + int len; + int nbytes; + + pnt = s_entry->rr_attributes; + len = s_entry->total_rr_attr_size; + + /* + * We make sure that each continuation entry record is not + * split across sectors, but each file could in theory have more + * than one CE, so we scan through and figure out what we need. + */ + while(len > 3) + { + if(pnt[0] == 'C' && pnt[1] == 'E') + { + nbytes = get_733((char *) pnt+20); + + if((this_dir->ce_bytes & (SECTOR_SIZE - 1)) + nbytes >= + SECTOR_SIZE) this_dir->ce_bytes = + ROUND_UP(this_dir->ce_bytes); + /* Now store the block in the ce buffer */ + this_dir->ce_bytes += nbytes; + if(this_dir->ce_bytes & 1) this_dir->ce_bytes++; + } + len -= pnt[2]; + pnt += pnt[2]; + } + } + s_entry = s_entry->next; + } + return status; +} + +static void generate_reloc_directory() +{ + time_t current_time; + struct directory_entry *s_entry; + + /* Create an entry for our internal tree */ + time (¤t_time); + reloc_dir = (struct directory *) + e_malloc(sizeof(struct directory)); + memset(reloc_dir, 0, sizeof(struct directory)); + reloc_dir->parent = root; + reloc_dir->next = root->subdir; + root->subdir = reloc_dir; + reloc_dir->depth = 1; + reloc_dir->whole_name = strdup("./rr_moved"); + reloc_dir->de_name = strdup("rr_moved"); + reloc_dir->extent = 0; + + + /* Now create an actual directory entry */ + s_entry = (struct directory_entry *) + e_malloc(sizeof (struct directory_entry)); + memset(s_entry, 0, sizeof(struct directory_entry)); + s_entry->next = root->contents; + reloc_dir->self = s_entry; + + /* + * The rr_moved entry will not appear in the Joliet tree. + */ + reloc_dir->dir_flags |= INHIBIT_JOLIET_ENTRY; + s_entry->de_flags |= INHIBIT_JOLIET_ENTRY; + + root->contents = s_entry; + root->contents->name = strdup(reloc_dir->de_name); + root->contents->filedir = root; + root->contents->isorec.flags[0] = 2; + root->contents->priority = 32768; + iso9660_date(root->contents->isorec.date, current_time); + root->contents->inode = UNCACHED_INODE; + root->contents->dev = (dev_t) UNCACHED_DEVICE; + set_723(root->contents->isorec.volume_sequence_number, volume_sequence_number); + iso9660_file_length (reloc_dir->de_name, root->contents, 1); + + if(use_RockRidge){ + fstatbuf.st_mode = 0555 | S_IFDIR; + fstatbuf.st_nlink = 2; + generate_rock_ridge_attributes("", + "rr_moved", s_entry, + &fstatbuf, &fstatbuf, 0); + }; + + /* Now create the . and .. entries in rr_moved */ + /* Now create an actual directory entry */ + attach_dot_entries(reloc_dir, &root_statbuf); +} + +/* + * Function: attach_dot_entries + * + * Purpose: Create . and .. entries for a new directory. + * + * Notes: Only used for artificial directories that + * we are creating. + */ +static void FDECL2(attach_dot_entries, struct directory *, dirnode, + struct stat *, parent_stat) +{ + struct directory_entry *s_entry; + struct directory_entry *orig_contents; + int deep_flag = 0; + + init_fstatbuf(); + + orig_contents = dirnode->contents; + + if( (dirnode->dir_flags & DIR_HAS_DOTDOT) == 0 ) + { + s_entry = (struct directory_entry *) + e_malloc(sizeof (struct directory_entry)); + memcpy(s_entry, dirnode->self, + sizeof(struct directory_entry)); + s_entry->name = strdup(".."); + s_entry->whole_name = NULL; + s_entry->isorec.name_len[0] = 1; + s_entry->isorec.flags[0] = 2; /* Mark as a directory */ + iso9660_file_length ("..", s_entry, 1); + iso9660_date(s_entry->isorec.date, fstatbuf.st_mtime); + s_entry->filedir = dirnode->parent; + + dirnode->contents = s_entry; + dirnode->contents->next = orig_contents; + orig_contents = s_entry; + + if(use_RockRidge) + { + if( parent_stat == NULL ) + { + parent_stat = &fstatbuf; + } + generate_rock_ridge_attributes("", + "..", s_entry, + parent_stat, + parent_stat, 0); + } + dirnode->dir_flags |= DIR_HAS_DOTDOT; + } + + if( (dirnode->dir_flags & DIR_HAS_DOT) == 0 ) + { + s_entry = (struct directory_entry *) + e_malloc(sizeof (struct directory_entry)); + memcpy(s_entry, dirnode->self, + sizeof(struct directory_entry)); + s_entry->name = strdup("."); + s_entry->whole_name = NULL; + s_entry->isorec.name_len[0] = 1; + s_entry->isorec.flags[0] = 2; /* Mark as a directory */ + iso9660_file_length (".", s_entry, 1); + iso9660_date(s_entry->isorec.date, fstatbuf.st_mtime); + s_entry->filedir = dirnode; + + dirnode->contents = s_entry; + dirnode->contents->next = orig_contents; + + if(use_RockRidge) + { + fstatbuf.st_mode = 0555 | S_IFDIR; + fstatbuf.st_nlink = 2; + + if( dirnode == root ) + { + deep_flag |= NEED_CE | NEED_SP; /* For extension record */ + } + + generate_rock_ridge_attributes("", + ".", s_entry, + &fstatbuf, &fstatbuf, deep_flag); + } + + dirnode->dir_flags |= DIR_HAS_DOT; + } + +} + +static void FDECL2(update_nlink, struct directory_entry *, s_entry, int, value) +{ + unsigned char * pnt; + int len; + + pnt = s_entry->rr_attributes; + len = s_entry->total_rr_attr_size; + while(len) + { + if(pnt[0] == 'P' && pnt[1] == 'X') + { + set_733((char *) pnt+12, value); + break; + } + len -= pnt[2]; + pnt += pnt[2]; + } +} + +static void FDECL1(increment_nlink, struct directory_entry *, s_entry) +{ + unsigned char * pnt; + int len, nlink; + + pnt = s_entry->rr_attributes; + len = s_entry->total_rr_attr_size; + while(len) + { + if(pnt[0] == 'P' && pnt[1] == 'X') + { + nlink = get_733((char *) pnt+12); + set_733((char *) pnt+12, nlink+1); + break; + } + len -= pnt[2]; + pnt += pnt[2]; + } +} + +void finish_cl_pl_entries(){ + struct directory_entry *s_entry, *s_entry1; + struct directory * d_entry; + + /* if the reloc_dir is hidden (empty), then return */ + if (reloc_dir->dir_flags & INHIBIT_ISO9660_ENTRY) + return; + + s_entry = reloc_dir->contents; + s_entry = s_entry->next->next; /* Skip past . and .. */ + for(; s_entry; s_entry = s_entry->next){ + /* skip if it's hidden */ + if(s_entry->de_flags & INHIBIT_ISO9660_ENTRY) { + continue; + } + d_entry = reloc_dir->subdir; + while(d_entry){ + if(d_entry->self == s_entry) break; + d_entry = d_entry->next; + }; + if(!d_entry){ + fprintf(stderr,"Unable to locate directory parent\n"); + exit(1); + }; + + /* First fix the PL pointer in the directory in the rr_reloc dir */ + s_entry1 = d_entry->contents->next; + set_733((char *) s_entry1->rr_attributes + s_entry1->total_rr_attr_size - 8, + s_entry->filedir->extent); + + /* Now fix the CL pointer */ + s_entry1 = s_entry->parent_rec; + + set_733((char *) s_entry1->rr_attributes + s_entry1->total_rr_attr_size - 8, + d_entry->extent); + + s_entry->filedir = reloc_dir; /* Now we can fix this */ + } + /* Next we need to modify the NLINK terms in the assorted root directory records + to account for the presence of the RR_MOVED directory */ + + increment_nlink(root->self); + increment_nlink(root->self->next); + d_entry = root->subdir; + while(d_entry){ + increment_nlink(d_entry->contents->next); + d_entry = d_entry->next; + }; +} + +/* + * Function: scan_directory_tree + * + * Purpose: Walk through a directory on the local machine + * filter those things we don't want to include + * and build our representation of a dir. + * + * Notes: + */ +int +FDECL3(scan_directory_tree,struct directory *, this_dir, + char *, path, + struct directory_entry *, de) +{ + DIR * current_dir; + char whole_path[1024]; + struct dirent * d_entry; + struct directory * parent; + int dflag; + char * old_path; + + if (verbose > 1) + { + fprintf(stderr, "Scanning %s\n", path); + } + + current_dir = opendir(path); + d_entry = NULL; + + /* Apparently NFS sometimes allows you to open the directory, but + then refuses to allow you to read the contents. Allow for this */ + + old_path = path; + + if(current_dir) d_entry = readdir(current_dir); + + if(!current_dir || !d_entry) + { + fprintf(stderr,"Unable to open directory %s\n", path); + de->isorec.flags[0] &= ~2; /* Mark as not a directory */ + if(current_dir) closedir(current_dir); + return 0; + } + + parent = de->filedir; + /* Set up the struct for the current directory, and insert it into the + tree */ + +#ifdef VMS + vms_path_fixup(path); +#endif + + /* + * if entry for this sub-directory is hidden, then hide this directory + */ + if (de->de_flags & INHIBIT_ISO9660_ENTRY) + this_dir->dir_flags |= INHIBIT_ISO9660_ENTRY; + + if (de->de_flags & INHIBIT_JOLIET_ENTRY) + this_dir->dir_flags |= INHIBIT_JOLIET_ENTRY; + + /* + * Now we scan the directory itself, and look at what is inside of it. + */ + dflag = 0; + while(1==1){ + + /* The first time through, skip this, since we already asked for + the first entry when we opened the directory. */ + if(dflag) d_entry = readdir(current_dir); + dflag++; + + if(!d_entry) break; + + /* OK, got a valid entry */ + + /* If we do not want all files, then pitch the backups. */ + if(!all_files){ + if( strchr(d_entry->d_name,'~') + || strchr(d_entry->d_name,'#')) + { + if( verbose > 0 ) + { + fprintf(stderr, "Ignoring file %s\n", d_entry->d_name); + } + continue; + } + } + + if(strlen(path)+strlen(d_entry->d_name) + 2 > sizeof(whole_path)){ + fprintf(stderr, "Overflow of stat buffer\n"); + exit(1); + }; + + /* Generate the complete ASCII path for this file */ + strcpy(whole_path, path); +#ifndef VMS + if(whole_path[strlen(whole_path)-1] != '/') + strcat(whole_path, "/"); +#endif + strcat(whole_path, d_entry->d_name); + + /** Should we exclude this file ? */ + if (matches(d_entry->d_name) || matches(whole_path)) { + if (verbose > 1) { + fprintf(stderr, "Excluded by match: %s\n", whole_path); + } + continue; + } + + if( generate_tables + && strcmp(d_entry->d_name, "TRANS.TBL") == 0 ) + { + /* + * Ignore this entry. We are going to be generating new + * versions of these files, and we need to ignore any + * originals that we might have found. + */ + if (verbose > 1) + { + fprintf(stderr, "Excluded: %s\n",whole_path); + } + continue; + } + + /* + * If we already have a '.' or a '..' entry, then don't + * insert new ones. + */ + if( strcmp(d_entry->d_name, ".") == 0 + && this_dir->dir_flags & DIR_HAS_DOT ) + { + continue; + } + + if( strcmp(d_entry->d_name, "..") == 0 + && this_dir->dir_flags & DIR_HAS_DOTDOT ) + { + continue; + } + +#if 0 + if (verbose > 1) fprintf(stderr, "%s\n",whole_path); +#endif + /* + * This actually adds the entry to the directory in question. + */ + insert_file_entry(this_dir, whole_path, d_entry->d_name); + } + closedir(current_dir); + + return 1; +} + + +/* + * Function: insert_file_entry + * + * Purpose: Insert one entry into our directory node. + * + * Note: + * This function inserts a single entry into the directory. It + * is assumed that all filtering and decision making regarding what + * we want to include has already been made, so the purpose of this + * is to insert one entry (file, link, dir, etc), into this directory. + * Note that if the entry is a dir (or if we are following links, + * and the thing it points to is a dir), then we will scan those + * trees before we return. + */ +int +FDECL3(insert_file_entry,struct directory *, this_dir, + char *, whole_path, + char *, short_name) +{ + struct stat statbuf, lstatbuf; + struct directory_entry * s_entry, *s_entry1; + int lstatus; + int status; + int deep_flag; + + status = stat_filter(whole_path, &statbuf); + + lstatus = lstat_filter(whole_path, &lstatbuf); + + if( (status == -1) && (lstatus == -1) ) + { + /* + * This means that the file doesn't exist, or isn't accessible. + * Sometimes this is because of NFS permissions problems. + */ + fprintf(stderr, "Non-existant or inaccessible: %s\n",whole_path); + return 0; + } + + if(this_dir == root && strcmp(short_name, ".") == 0) + root_statbuf = statbuf; /* Save this for later on */ + + /* We do this to make sure that the root entries are consistent */ + if(this_dir == root && strcmp(short_name, "..") == 0) + { + statbuf = root_statbuf; + lstatbuf = root_statbuf; + } + + if(S_ISLNK(lstatbuf.st_mode)) + { + + /* Here we decide how to handle the symbolic links. Here + we handle the general case - if we are not following + links or there is an error, then we must change + something. If RR is in use, it is easy, we let RR + describe the file. If not, then we punt the file. */ + + if((status || !follow_links)) + { + if(use_RockRidge) + { + status = 0; + statbuf.st_size = 0; + STAT_INODE(statbuf) = UNCACHED_INODE; + statbuf.st_dev = (dev_t) UNCACHED_DEVICE; + statbuf.st_mode = (statbuf.st_mode & ~S_IFMT) | S_IFREG; + } else { + if(follow_links) + { + fprintf(stderr, + "Unable to stat file %s - ignoring and continuing.\n", + whole_path); + } + else + { + fprintf(stderr, + "Symlink %s ignored - continuing.\n", + whole_path); + return 0; /* Non Rock Ridge discs - ignore all symlinks */ + } + } + } + + /* Here we handle a different kind of case. Here we have + a symlink, but we want to follow symlinks. If we run + across a directory loop, then we need to pretend that + we are not following symlinks for this file. If this + is the first time we have seen this, then make this + seem as if there was no symlink there in the first + place */ + + if( follow_links + && S_ISDIR(statbuf.st_mode) ) + { + if( strcmp(short_name, ".") + && strcmp(short_name, "..") ) + { + if(find_directory_hash(statbuf.st_dev, STAT_INODE(statbuf))) + { + if(!use_RockRidge) + { + fprintf(stderr, "Already cached directory seen (%s)\n", + whole_path); + return 0; + } + statbuf.st_size = 0; + STAT_INODE(statbuf) = UNCACHED_INODE; + statbuf.st_dev = (dev_t) UNCACHED_DEVICE; + statbuf.st_mode = (statbuf.st_mode & ~S_IFMT) | S_IFREG; + } + else + { + lstatbuf = statbuf; + add_directory_hash(statbuf.st_dev, STAT_INODE(statbuf)); + } + } + } + + /* + * For non-directories, we just copy the stat information over + * so we correctly include this file. + */ + if( follow_links + && !S_ISDIR(statbuf.st_mode) ) + { + lstatbuf = statbuf; + } + } + + /* + * Add directories to the cache so that we don't waste space even + * if we are supposed to be following symlinks. + */ + if( follow_links + && strcmp(short_name, ".") + && strcmp(short_name, "..") + && S_ISDIR(statbuf.st_mode) ) + { + add_directory_hash(statbuf.st_dev, STAT_INODE(statbuf)); + } +#ifdef VMS + if(!S_ISDIR(lstatbuf.st_mode) && (statbuf.st_fab_rfm != FAB$C_FIX && + statbuf.st_fab_rfm != FAB$C_STMLF)) { + fprintf(stderr,"Warning - file %s has an unsupported VMS record" + " format (%d)\n", + whole_path, statbuf.st_fab_rfm); + } +#endif + + if(S_ISREG(lstatbuf.st_mode) && (status = access(whole_path, R_OK))) + { + fprintf(stderr, "File %s is not readable (errno = %d) - ignoring\n", + whole_path, errno); + return 0; + } + + /* Add this so that we can detect directory loops with hard links. + If we are set up to follow symlinks, then we skip this checking. */ + if( !follow_links + && S_ISDIR(lstatbuf.st_mode) + && strcmp(short_name, ".") + && strcmp(short_name, "..") ) + { + if(find_directory_hash(statbuf.st_dev, STAT_INODE(statbuf))) { + fprintf(stderr,"Directory loop - fatal goof (%s %lx %lu).\n", + whole_path, (unsigned long) statbuf.st_dev, + (unsigned long) STAT_INODE(statbuf)); + exit(1); + } + add_directory_hash(statbuf.st_dev, STAT_INODE(statbuf)); + } + + if (!S_ISCHR(lstatbuf.st_mode) && !S_ISBLK(lstatbuf.st_mode) && + !S_ISFIFO(lstatbuf.st_mode) && !S_ISSOCK(lstatbuf.st_mode) + && !S_ISLNK(lstatbuf.st_mode) && !S_ISREG(lstatbuf.st_mode) && + !S_ISDIR(lstatbuf.st_mode)) { + fprintf(stderr,"Unknown file type %s - ignoring and continuing.\n", + whole_path); + return 0; + } + + /* Who knows what trash this is - ignore and continue */ + + if(status) + { + fprintf(stderr, + "Unable to stat file %s - ignoring and continuing.\n", + whole_path); + return 0; + } + + /* + * Check to see if we have already seen this directory node. + * If so, then we don't create a new entry for it, but we do want + * to recurse beneath it and add any new files we do find. + */ + if (S_ISDIR(statbuf.st_mode)) + { + int dflag; + + for( s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) + { + if( strcmp(s_entry->name, short_name) == 0 ) + { + break; + } + } + if ( s_entry != NULL + && strcmp(short_name,".") + && strcmp(short_name,"..")) + { + struct directory * child; + + if ( (s_entry->de_flags & RELOCATED_DIRECTORY) != 0) + { + for( s_entry = reloc_dir->contents; s_entry; s_entry = s_entry->next) + { + if( strcmp(s_entry->name, short_name) == 0 ) + { + break; + } + } + child = find_or_create_directory(reloc_dir, whole_path, + s_entry, 1); + } + else + { + child = find_or_create_directory(this_dir, whole_path, + s_entry, 1); + /* If unable to scan directory, mark this as a non-directory */ + } + dflag = scan_directory_tree(child, whole_path, s_entry); + if(!dflag) + { + lstatbuf.st_mode = (lstatbuf.st_mode & ~S_IFMT) | S_IFREG; + } + return 0; + } + } + + s_entry = (struct directory_entry *) + e_malloc(sizeof (struct directory_entry)); + s_entry->next = this_dir->contents; + memset(s_entry->isorec.extent, 0, 8); + this_dir->contents = s_entry; + deep_flag = 0; + s_entry->table = NULL; + + s_entry->name = strdup(short_name); + s_entry->whole_name = strdup (whole_path); + + s_entry->de_flags = 0; + + /* + * If the current directory is hidden, then hide all it's members + * otherwise check if this entry needs to be hidden as well */ + if (this_dir->dir_flags & INHIBIT_ISO9660_ENTRY) { + s_entry->de_flags |= INHIBIT_ISO9660_ENTRY; + } + else if (strcmp(short_name,".") && strcmp(short_name,"..")) { + if (i_matches(short_name) || i_matches(whole_path)) { + if (verbose > 1) { + fprintf(stderr, "Hidden from ISO9660 tree: %s\n", whole_path); + } + s_entry->de_flags |= INHIBIT_ISO9660_ENTRY; + } + } + + if (this_dir != reloc_dir && this_dir->dir_flags & INHIBIT_JOLIET_ENTRY) { + s_entry->de_flags |= INHIBIT_JOLIET_ENTRY; + } + else if (strcmp(short_name,".") && strcmp(short_name,"..")) { + if (j_matches(short_name) || j_matches(whole_path)) { + if (verbose > 1) { + fprintf(stderr, "Hidden from Joliet tree: %s\n", whole_path); + } + s_entry->de_flags |= INHIBIT_JOLIET_ENTRY; + } + } + + s_entry->filedir = this_dir; + s_entry->isorec.flags[0] = 0; + s_entry->isorec.ext_attr_length[0] = 0; + iso9660_date(s_entry->isorec.date, statbuf.st_mtime); + s_entry->isorec.file_unit_size[0] = 0; + s_entry->isorec.interleave[0] = 0; + + if( strcmp(short_name, ".") == 0) + { + this_dir->dir_flags |= DIR_HAS_DOT; + } + + if( strcmp(short_name, "..") == 0) + { + this_dir->dir_flags |= DIR_HAS_DOTDOT; + } + + if( this_dir->parent + && this_dir->parent == reloc_dir + && strcmp(short_name, "..") == 0) + { + s_entry->inode = UNCACHED_INODE; + s_entry->dev = (dev_t) UNCACHED_DEVICE; + deep_flag = NEED_PL; + } + else + { + s_entry->inode = STAT_INODE(statbuf); + s_entry->dev = statbuf.st_dev; + } + set_723(s_entry->isorec.volume_sequence_number, volume_sequence_number); + iso9660_file_length(short_name, s_entry, S_ISDIR(statbuf.st_mode)); + s_entry->rr_attr_size = 0; + s_entry->total_rr_attr_size = 0; + s_entry->rr_attributes = NULL; + + /* Directories are assigned sizes later on */ + if (!S_ISDIR(statbuf.st_mode)) + { + if (S_ISCHR(lstatbuf.st_mode) || S_ISBLK(lstatbuf.st_mode) || + S_ISFIFO(lstatbuf.st_mode) || S_ISSOCK(lstatbuf.st_mode) + || S_ISLNK(lstatbuf.st_mode)) + { + s_entry->size = 0; + statbuf.st_size = 0; + } + else + { + s_entry->size = statbuf.st_size; + } + + set_733((char *) s_entry->isorec.size, statbuf.st_size); + } + else + { + s_entry->isorec.flags[0] = 2; + } + + if (strcmp(short_name,".") && strcmp(short_name,"..") && + S_ISDIR(statbuf.st_mode) && this_dir->depth > RR_relocation_depth) + { + struct directory * child; + + if(!reloc_dir) generate_reloc_directory(); + + /* + * Replicate the entry for this directory. The old one will stay where it + * is, and it will be neutered so that it no longer looks like a directory. + * The new one will look like a directory, and it will be put in the reloc_dir. + */ + s_entry1 = (struct directory_entry *) + e_malloc(sizeof (struct directory_entry)); + memcpy(s_entry1, s_entry, sizeof(struct directory_entry)); + s_entry1->table = NULL; + s_entry1->name = strdup(this_dir->contents->name); + s_entry1->whole_name = strdup(this_dir->contents->whole_name); + s_entry1->next = reloc_dir->contents; + reloc_dir->contents = s_entry1; + s_entry1->priority = 32768; + s_entry1->parent_rec = this_dir->contents; + + deep_flag = NEED_RE; + + if(use_RockRidge) + { + generate_rock_ridge_attributes(whole_path, + short_name, s_entry1, + &statbuf, &lstatbuf, deep_flag); + } + + deep_flag = 0; + + /* We need to set this temporarily so that the parent to this + is correctly determined. */ + s_entry1->filedir = reloc_dir; + child = find_or_create_directory(reloc_dir, whole_path, + s_entry1, 0); + scan_directory_tree(child, whole_path, s_entry1); + s_entry1->filedir = this_dir; + + statbuf.st_size = 0; + statbuf.st_mode &= 0777; + set_733((char *) s_entry->isorec.size, 0); + s_entry->size = 0; + s_entry->isorec.flags[0] = 0; + s_entry->inode = UNCACHED_INODE; + s_entry->de_flags |= RELOCATED_DIRECTORY; + deep_flag = NEED_CL; + } + + if(generate_tables + && strcmp(s_entry->name, ".") + && strcmp(s_entry->name, "..")) + { + char buffer[2048]; + int nchar; + switch(lstatbuf.st_mode & S_IFMT) + { + case S_IFDIR: + sprintf(buffer,"D\t%s\n", + s_entry->name); + break; +#ifdef S_IFBLK +/* extra for WIN32 - if it doesn't have the major/minor defined, then + S_IFBLK and S_IFCHR type files are unlikely to exist anyway ... + code similar to that in rock.c */ + +/* for some reason, MAJOR_IN_SYSMACROS isn't defined on a SunOS when + it should be, so see if major() is defined instead */ +/* +#if !(defined(MAJOR_IN_SYSMACROS) || defined(MAJOR_IN_MKDEV)) +*/ +#ifndef major +#define major(dev) (sizeof(dev_t) <= 2 ? ((dev) >> 8) : \ + (sizeof(dev_t) <= 4 ? (((dev) >> 8) >> 8) : \ + (((dev) >> 16) >> 16))) +#define minor(dev) (sizeof(dev_t) <= 2 ? (dev) & 0xff : \ + (sizeof(dev_t) <= 4 ? (dev) & 0xffff : \ + (dev) & 0xffffffff)) +#endif + case S_IFBLK: + sprintf(buffer,"B\t%s\t%lu %lu\n", + s_entry->name, + (unsigned long) major(statbuf.st_rdev), + (unsigned long) minor(statbuf.st_rdev)); + break; +#endif +#ifdef S_IFIFO + case S_IFIFO: + sprintf(buffer,"P\t%s\n", + s_entry->name); + break; +#endif +#ifdef S_IFCHR + case S_IFCHR: + sprintf(buffer,"C\t%s\t%lu %lu\n", + s_entry->name, + (unsigned long) major(statbuf.st_rdev), + (unsigned long) minor(statbuf.st_rdev)); + break; +#endif +#ifdef S_IFLNK + case S_IFLNK: + nchar = readlink(whole_path, + (char *)symlink_buff, + sizeof(symlink_buff)); + symlink_buff[nchar < 0 ? 0 : nchar] = 0; + sprintf(buffer,"L\t%s\t%s\n", + s_entry->name, symlink_buff); + break; +#endif +#ifdef S_IFSOCK + case S_IFSOCK: + sprintf(buffer,"S\t%s\n", + s_entry->name); + break; +#endif + case S_IFREG: + default: + sprintf(buffer,"F\t%s\n", + s_entry->name); + break; + }; + s_entry->table = strdup(buffer); + } + + if(S_ISDIR(statbuf.st_mode)) + { + int dflag; + if (strcmp(short_name,".") && strcmp(short_name,"..")) + { + struct directory * child; + + child = find_or_create_directory(this_dir, whole_path, + s_entry, 1); + dflag = scan_directory_tree(child, whole_path, s_entry); + + if(!dflag) + { + lstatbuf.st_mode = (lstatbuf.st_mode & ~S_IFMT) | S_IFREG; + if( child->contents == NULL ) + { + delete_directory(this_dir, child); + } + } + } + /* If unable to scan directory, mark this as a non-directory */ + } + + if(use_RockRidge && this_dir == root && strcmp(s_entry->name, ".") == 0) + { + deep_flag |= NEED_CE | NEED_SP; /* For extension record */ + } + + /* Now figure out how much room this file will take in the + directory */ + + if(use_RockRidge) + { + generate_rock_ridge_attributes(whole_path, + short_name, s_entry, + &statbuf, &lstatbuf, deep_flag); + + } + + return 1; +} + + +void FDECL2(generate_iso9660_directories, struct directory *, node, FILE*, outfile){ + struct directory * dpnt; + + dpnt = node; + + while (dpnt){ + if( dpnt->extent > session_start ) + { + generate_one_directory(dpnt, outfile); + } + if(dpnt->subdir) generate_iso9660_directories(dpnt->subdir, outfile); + dpnt = dpnt->next; + } +} + +/* + * Function: find_or_create_directory + * + * Purpose: Locate a directory entry in the tree, create if needed. + * + * Arguments: + */ +struct directory * FDECL4(find_or_create_directory, struct directory *, parent, + const char *, path, + struct directory_entry *, de, int, flag) +{ + struct directory * dpnt; + struct directory_entry * orig_de; + struct directory * next_brother; + const char * cpnt; + const char * pnt; + + orig_de = de; + + pnt = strrchr(path, PATH_SEPARATOR); + if( pnt == NULL ) + { + pnt = path; + } + else + { + pnt++; + } + + if( parent != NULL ) + { + dpnt = parent->subdir; + + while (dpnt) + { + /* + * Weird hack time - if there are two directories by the + * same name in the reloc_dir, they are not treated as the + * same thing unless the entire path matches completely. + */ + if( flag && strcmp(dpnt->de_name, pnt) == 0 ) + { + return dpnt; + } + dpnt = dpnt->next; + } + } + + /* + * We don't know if we have a valid directory entry for this one + * yet. If not, we need to create one. + */ + if( de == NULL ) + { + de = (struct directory_entry *) + e_malloc(sizeof (struct directory_entry)); + memset(de, 0, sizeof(struct directory_entry)); + de->next = parent->contents; + parent->contents = de; + de->name = strdup(pnt); + de->filedir = parent; + de->isorec.flags[0] = 2; + de->priority = 32768; + de->inode = UNCACHED_INODE; + de->dev = (dev_t) UNCACHED_DEVICE; + set_723(de->isorec.volume_sequence_number, volume_sequence_number); + iso9660_file_length (pnt, de, 1); + + init_fstatbuf(); + /* + * It doesn't exist for real, so we cannot add any Rock Ridge. + */ + if(use_RockRidge) + { + fstatbuf.st_mode = 0555 | S_IFDIR; + fstatbuf.st_nlink = 2; + generate_rock_ridge_attributes("", + (char *) pnt, de, + &fstatbuf, + &fstatbuf, 0); + } + iso9660_date(de->isorec.date, fstatbuf.st_mtime); + + } + + /* + * If we don't have a directory for this one yet, then allocate it + * now, and patch it into the tree in the appropriate place. + */ + dpnt = (struct directory *) e_malloc(sizeof(struct directory)); + memset(dpnt, 0, sizeof(struct directory)); + dpnt->next = NULL; + dpnt->subdir = NULL; + dpnt->self = de; + dpnt->contents = NULL; + dpnt->whole_name = strdup(path); + cpnt = strrchr(path, PATH_SEPARATOR); + if(cpnt) + cpnt++; + else + cpnt = path; + dpnt->de_name = strdup(cpnt); + dpnt->size = 0; + dpnt->extent = 0; + dpnt->jextent = 0; + dpnt->jsize = 0; + + if( orig_de == NULL ) + { + struct stat xstatbuf; + int sts; + + /* + * Now add a . and .. entry in the directory itself. + * This is a little tricky - if the real directory + * exists, we need to stat it first. Otherwise, we + * use the fictitious fstatbuf which points to the time + * at which mkisofs was started. + */ + sts = stat_filter(parent->whole_name, &xstatbuf); + if( sts == 0 ) + { + attach_dot_entries(dpnt, &xstatbuf); + } + else + { + attach_dot_entries(dpnt, &fstatbuf); + } + } + + if(!parent || parent == root) + { + if (!root) + { + root = dpnt; /* First time through for root directory only */ + root->depth = 0; + root->parent = root; + } else { + dpnt->depth = 1; + if(!root->subdir) + { + root->subdir = dpnt; + } + else + { + next_brother = root->subdir; + while(next_brother->next) next_brother = next_brother->next; + next_brother->next = dpnt; + } + dpnt->parent = parent; + } + } + else + { + /* Come through here for normal traversal of tree */ +#ifdef DEBUG + fprintf(stderr,"%s(%d) ", path, dpnt->depth); +#endif + if(parent->depth > RR_relocation_depth) + { + fprintf(stderr,"Directories too deep %s\n", path); + exit(1); + } + + dpnt->parent = parent; + dpnt->depth = parent->depth + 1; + + if(!parent->subdir) + { + parent->subdir = dpnt; + } + else + { + next_brother = parent->subdir; + while(next_brother->next) next_brother = next_brother->next; + next_brother->next = dpnt; + } + } + + return dpnt; +} + +/* + * Function: delete_directory + * + * Purpose: Locate a directory entry in the tree, create if needed. + * + * Arguments: + */ +static void FDECL2(delete_directory, struct directory *, parent, struct directory *, child) +{ + struct directory * tdir; + + if( child->contents != NULL ) + { + fprintf(stderr, "Unable to delete non-empty directory\n"); + exit(1); + } + + free(child->whole_name); + child->whole_name = NULL; + + free(child->de_name); + child->de_name = NULL; + + if( parent->subdir == child ) + { + parent->subdir = child->next; + } + else + { + for( tdir = parent->subdir; tdir->next != NULL; tdir = tdir->next ) + { + if( tdir->next == child ) + { + tdir->next = child->next; + break; + } + } + if( tdir == NULL ) + { + fprintf(stderr, "Unable to locate child directory in parent list\n"); + exit(1); + } + } + free(child); + return; +} + +int FDECL1(sort_tree, struct directory *, node){ + struct directory * dpnt; + int ret = 0; + + dpnt = node; + + while (dpnt){ + ret = sort_n_finish(dpnt); + if( ret ) + { + break; + } + + if(dpnt->subdir) sort_tree(dpnt->subdir); + dpnt = dpnt->next; + } + return ret; +} + +void FDECL1(dump_tree, struct directory *, node){ + struct directory * dpnt; + + dpnt = node; + + while (dpnt){ + fprintf(stderr,"%4d %5d %s\n",dpnt->extent, dpnt->size, dpnt->de_name); + if(dpnt->subdir) dump_tree(dpnt->subdir); + dpnt = dpnt->next; + } +} + +void FDECL1(update_nlink_field, struct directory *, node) +{ + struct directory * dpnt; + struct directory * xpnt; + struct directory_entry * s_entry; + int i; + + dpnt = node; + + while (dpnt) + { + if (dpnt->dir_flags & INHIBIT_ISO9660_ENTRY) { + dpnt = dpnt->next; + continue; + } + + /* + * First, count up the number of subdirectories this guy has. + */ + for(i=0, xpnt = dpnt->subdir; xpnt; xpnt = xpnt->next) + if ((xpnt->dir_flags & INHIBIT_ISO9660_ENTRY) == 0) + i++; + /* + * Next check to see if we have any relocated directories + * in this directory. The nlink field will include these + * as real directories when they are properly relocated. + * + * In the non-rockridge disk, the relocated entries appear + * as zero length files. + */ + for(s_entry = dpnt->contents; s_entry; s_entry = s_entry->next) + { + if( (s_entry->de_flags & RELOCATED_DIRECTORY) != 0 && + (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) == 0) + { + i++; + } + } + /* + * Now update the field in the Rock Ridge entry. + */ + update_nlink(dpnt->self, i + 2); + + /* + * Update the '.' entry for this directory. + */ + update_nlink(dpnt->contents, i + 2); + + /* + * Update all of the '..' entries that point to this guy. + */ + for(xpnt = dpnt->subdir; xpnt; xpnt = xpnt->next) + update_nlink(xpnt->contents->next, i + 2); + + if(dpnt->subdir) update_nlink_field(dpnt->subdir); + dpnt = dpnt->next; + } +} + +/* + * something quick and dirty to locate a file given a path + * recursively walks down path in filename until it finds the + * directory entry for the desired file + */ +struct directory_entry * FDECL2(search_tree_file, struct directory *, + node,char *, filename) +{ + struct directory_entry * depnt; + struct directory * dpnt; + char * p1; + char * rest; + char * subdir; + + /* + * strip off next directory name from filename + */ + subdir = strdup(filename); + + if( (p1=strchr(subdir, '/')) == subdir ) + { + fprintf(stderr,"call to search_tree_file with an absolute path, stripping\n"); + fprintf(stderr,"initial path separator. Hope this was intended...\n"); + memmove(subdir, subdir+1, strlen(subdir)-1); + p1 = strchr(subdir, '/'); + } + + /* + * do we need to find a subdirectory + */ + if (p1) + { + *p1 = '\0'; + +#ifdef DEBUG_TORITO + fprintf(stderr,"Looking for subdir called %s\n",p1); +#endif + + rest = p1+1; + +#ifdef DEBUG_TORITO + fprintf(stderr,"Remainder of path name is now %s\n", rest); +#endif + + dpnt = node->subdir; + while( dpnt ) + { +#ifdef DEBUG_TORITO + fprintf(stderr,"%4d %5d %s\n", dpnt->extent, dpnt->size, + dpnt->de_name); +#endif + if (!strcmp(subdir, dpnt->de_name)) + { +#ifdef DEBUG_TORITO + fprintf(stderr,"Calling next level with filename = %s", rest); +#endif + return(search_tree_file( dpnt, rest )); + } + dpnt = dpnt->next; + } + + /* if we got here means we couldnt find the subdir */ + return (NULL); + } + else + { + /* + * look for a normal file now + */ + depnt = node->contents; + while (depnt) + { +#ifdef DEBUG_TORITO + fprintf(stderr,"%4d %5d %s\n",depnt->isorec.extent, + depnt->size, depnt->name); +#endif + if (!strcmp(filename, depnt->name)) + { +#ifdef DEBUG_TORITO + fprintf(stderr,"Found our file %s", filename); +#endif + return(depnt); + } + depnt = depnt->next; + } + /* + * if we got here means we couldnt find the subdir + */ + return (NULL); + } + fprintf(stderr,"We cant get here in search_tree_file :-/ \n"); +} + +void init_fstatbuf() +{ + time_t current_time; + + if(fstatbuf.st_ctime == 0) + { + time (¤t_time); + if( rationalize ) + { + fstatbuf.st_uid = 0; + fstatbuf.st_gid = 0; + } + else + { + fstatbuf.st_uid = getuid(); + fstatbuf.st_gid = getgid(); + } + fstatbuf.st_ctime = current_time; + fstatbuf.st_mtime = current_time; + fstatbuf.st_atime = current_time; + } +} diff --git a/util/mkisofs/write.c b/util/mkisofs/write.c new file mode 100644 index 000000000..45b630693 --- /dev/null +++ b/util/mkisofs/write.c @@ -0,0 +1,1463 @@ +/* + * Program write.c - dump memory structures to file for iso9660 filesystem. + + Written by Eric Youngdale (1993). + + Copyright 1993 Yggdrasil Computing, Incorporated + + Copyright (C) 2009 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, 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 . + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +static char rcsid[] ="$Id: write.c,v 1.21 1999/03/07 17:41:19 eric Exp $"; + +#include +#include +#include "config.h" +#include "mkisofs.h" +#include "iso9660.h" +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef __SVR4 +extern char * strdup(const char *); +#endif + +#ifdef VMS +extern char * strdup(const char *); +#endif + + +/* Max number of sectors we will write at one time */ +#define NSECT 16 + +/* Counters for statistics */ + +static int table_size = 0; +static int total_dir_size = 0; +static int rockridge_size = 0; +static struct directory ** pathlist; +static int next_path_index = 1; +static int sort_goof; + +struct output_fragment * out_tail; +struct output_fragment * out_list; + +struct iso_primary_descriptor vol_desc; + +static int root_gen __PR((void)); +static int generate_path_tables __PR((void)); +static int file_gen __PR((void)); +static int dirtree_dump __PR((void)); + +/* Routines to actually write the disc. We write sequentially so that + we could write a tape, or write the disc directly */ + + +#define FILL_SPACE(X) memset(vol_desc.X, ' ', sizeof(vol_desc.X)) + +void FDECL2(set_721, char *, pnt, unsigned int, i) +{ + pnt[0] = i & 0xff; + pnt[1] = (i >> 8) & 0xff; +} + +void FDECL2(set_722, char *, pnt, unsigned int, i) +{ + pnt[0] = (i >> 8) & 0xff; + pnt[1] = i & 0xff; +} + +void FDECL2(set_723, char *, pnt, unsigned int, i) +{ + pnt[3] = pnt[0] = i & 0xff; + pnt[2] = pnt[1] = (i >> 8) & 0xff; +} + +void FDECL2(set_731, char *, pnt, unsigned int, i) +{ + pnt[0] = i & 0xff; + pnt[1] = (i >> 8) & 0xff; + pnt[2] = (i >> 16) & 0xff; + pnt[3] = (i >> 24) & 0xff; +} + +void FDECL2(set_732, char *, pnt, unsigned int, i) +{ + pnt[3] = i & 0xff; + pnt[2] = (i >> 8) & 0xff; + pnt[1] = (i >> 16) & 0xff; + pnt[0] = (i >> 24) & 0xff; +} + +int FDECL1(get_731, char *, p) +{ + return ((p[0] & 0xff) + | ((p[1] & 0xff) << 8) + | ((p[2] & 0xff) << 16) + | ((p[3] & 0xff) << 24)); +} + +int FDECL1(get_733, char *, p) +{ + return ((p[0] & 0xff) + | ((p[1] & 0xff) << 8) + | ((p[2] & 0xff) << 16) + | ((p[3] & 0xff) << 24)); +} + +void FDECL2(set_733, char *, pnt, unsigned int, i) +{ + pnt[7] = pnt[0] = i & 0xff; + pnt[6] = pnt[1] = (i >> 8) & 0xff; + pnt[5] = pnt[2] = (i >> 16) & 0xff; + pnt[4] = pnt[3] = (i >> 24) & 0xff; +} + +void FDECL4(xfwrite, void *, buffer, uint64_t, count, uint64_t, size, FILE *, file) +{ + /* + * This is a hack that could be made better. XXXIs this the only place? + * It is definitely needed on Operating Systems that do not + * allow to write files that are > 2GB. + * If the system is fast enough to be able to feed 1400 KB/s + * writing speed of a DVD-R drive, use stdout. + * If the system cannot do this reliable, you need to use this + * hacky option. + */ + static int idx = 0; + if (split_output != 0 && + (idx == 0 || ftell(file) >= (1024 * 1024 * 1024) )) { + char nbuf[512]; + extern char *outfile; + + if (idx == 0) + unlink(outfile); + sprintf(nbuf, "%s_%02d", outfile, idx++); + file = freopen(nbuf, "wb", file); + if (file == NULL) { + fprintf(stderr, "Cannot open '%s'.\n", nbuf); + exit(1); + } + + } + while(count) + { + size_t got = fwrite (buffer, size, count, file); + + if (got != count) + { + fprintf(stderr,"cannot fwrite %llu*%llu\n",size,count); + exit(1); + } + count-=got,*(char**)&buffer+=size*got; + } +} + +struct deferred_write +{ + struct deferred_write * next; + char * table; + uint64_t extent; + uint64_t size; + char * name; +}; + +static struct deferred_write * dw_head = NULL, * dw_tail = NULL; + +uint64_t last_extent_written = 0; +static unsigned int path_table_index; +static time_t begun; + +/* We recursively walk through all of the directories and assign extent + numbers to them. We have already assigned extent numbers to everything that + goes in front of them */ + +static int FDECL1(assign_directory_addresses, struct directory *, node) +{ + int dir_size; + struct directory * dpnt; + + dpnt = node; + + while (dpnt) + { + /* skip if it's hidden */ + if(dpnt->dir_flags & INHIBIT_ISO9660_ENTRY) { + dpnt = dpnt->next; + continue; + } + + /* + * If we already have an extent for this (i.e. it came from + * a multisession disc), then don't reassign a new extent. + */ + dpnt->path_index = next_path_index++; + if( dpnt->extent == 0 ) + { + dpnt->extent = last_extent; + dir_size = (dpnt->size + (SECTOR_SIZE - 1)) >> 11; + + last_extent += dir_size; + + /* + * Leave room for the CE entries for this directory. Keep them + * close to the reference directory so that access will be + * quick. + */ + if(dpnt->ce_bytes) + { + last_extent += ROUND_UP(dpnt->ce_bytes) >> 11; + } + } + + if(dpnt->subdir) + { + assign_directory_addresses(dpnt->subdir); + } + + dpnt = dpnt->next; + } + return 0; +} + +static void FDECL3(write_one_file, char *, filename, + uint64_t, size, FILE *, outfile) +{ + char buffer[SECTOR_SIZE * NSECT]; + FILE * infile; + int64_t remain; + size_t use; + + + if ((infile = fopen(filename, "rb")) == NULL) + { +#if defined(sun) || defined(_AUX_SOURCE) + fprintf(stderr, "cannot open %s: (%d)\n", filename, errno); +#else + fprintf(stderr, "cannot open %s: %s\n", filename, strerror(errno)); +#endif + exit(1); + } + remain = size; + + while(remain > 0) + { + use = (remain > SECTOR_SIZE * NSECT - 1 ? NSECT*SECTOR_SIZE : remain); + use = ROUND_UP(use); /* Round up to nearest sector boundary */ + memset(buffer, 0, use); + if (fread(buffer, 1, use, infile) == 0) + error (1, errno, "cannot read %llu bytes from %s", use, filename); + xfwrite(buffer, 1, use, outfile); + last_extent_written += use/SECTOR_SIZE; +#if 0 + if((last_extent_written % 1000) < use/SECTOR_SIZE) + { + fprintf(stderr,"%d..", last_extent_written); + } +#else + if((last_extent_written % 5000) < use/SECTOR_SIZE) + { + time_t now; + time_t the_end; + double frac; + + time(&now); + frac = last_extent_written / (double)last_extent; + the_end = begun + (now - begun) / frac; + fprintf(stderr, "%6.2f%% done, estimate finish %s", + frac * 100., ctime(&the_end)); + } +#endif + remain -= use; + } + fclose(infile); +} /* write_one_file(... */ + +static void FDECL1(write_files, FILE *, outfile) +{ + struct deferred_write * dwpnt, *dwnext; + dwpnt = dw_head; + while(dwpnt) + { + if(dwpnt->table) + { + write_one_file (dwpnt->table, dwpnt->size, outfile); + table_size += dwpnt->size; + free (dwpnt->table); + } + else + { + +#ifdef VMS + vms_write_one_file(dwpnt->name, dwpnt->size, outfile); +#else + write_one_file(dwpnt->name, dwpnt->size, outfile); +#endif + free(dwpnt->name); + } + + dwnext = dwpnt; + dwpnt = dwpnt->next; + free(dwnext); + } +} /* write_files(... */ + +#if 0 +static void dump_filelist() +{ + struct deferred_write * dwpnt; + dwpnt = dw_head; + while(dwpnt) + { + fprintf(stderr, "File %s\n",dwpnt->name); + dwpnt = dwpnt->next; + } + fprintf(stderr,"\n"); +} +#endif + +static int FDECL2(compare_dirs, const void *, rr, const void *, ll) +{ + char * rpnt, *lpnt; + struct directory_entry ** r, **l; + + r = (struct directory_entry **) rr; + l = (struct directory_entry **) ll; + rpnt = (*r)->isorec.name; + lpnt = (*l)->isorec.name; + + /* + * If the entries are the same, this is an error. + */ + if( strcmp(rpnt, lpnt) == 0 ) + { + sort_goof++; + } + + /* + * Put the '.' and '..' entries on the head of the sorted list. + * For normal ASCII, this always happens to be the case, but out of + * band characters cause this not to be the case sometimes. + * + * FIXME(eric) - these tests seem redundant, in taht the name is + * never assigned these values. It will instead be \000 or \001, + * and thus should always be sorted correctly. I need to figure + * out why I thought I needed this in the first place. + */ +#if 0 + if( strcmp(rpnt, ".") == 0 ) return -1; + if( strcmp(lpnt, ".") == 0 ) return 1; + + if( strcmp(rpnt, "..") == 0 ) return -1; + if( strcmp(lpnt, "..") == 0 ) return 1; +#else + /* + * The code above is wrong (as explained in Eric's comment), leading to incorrect + * sort order iff the -L option ("allow leading dots") is in effect and a directory + * contains entries that start with a dot. + * + * (TF, Tue Dec 29 13:49:24 CET 1998) + */ + if((*r)->isorec.name_len[0] == 1 && *rpnt == 0) return -1; /* '.' */ + if((*l)->isorec.name_len[0] == 1 && *lpnt == 0) return 1; + + if((*r)->isorec.name_len[0] == 1 && *rpnt == 1) return -1; /* '..' */ + if((*l)->isorec.name_len[0] == 1 && *lpnt == 1) return 1; +#endif + + while(*rpnt && *lpnt) + { + if(*rpnt == ';' && *lpnt != ';') return -1; + if(*rpnt != ';' && *lpnt == ';') return 1; + + if(*rpnt == ';' && *lpnt == ';') return 0; + + if(*rpnt == '.' && *lpnt != '.') return -1; + if(*rpnt != '.' && *lpnt == '.') return 1; + + if((unsigned char)*rpnt < (unsigned char)*lpnt) return -1; + if((unsigned char)*rpnt > (unsigned char)*lpnt) return 1; + rpnt++; lpnt++; + } + if(*rpnt) return 1; + if(*lpnt) return -1; + return 0; +} + +/* + * Function: sort_directory + * + * Purpose: Sort the directory in the appropriate ISO9660 + * order. + * + * Notes: Returns 0 if OK, returns > 0 if an error occurred. + */ +int FDECL1(sort_directory, struct directory_entry **, sort_dir) +{ + int dcount = 0; + int xcount = 0; + int j; + int i, len; + struct directory_entry * s_entry; + struct directory_entry ** sortlist; + + /* need to keep a count of how many entries are hidden */ + s_entry = *sort_dir; + while(s_entry) + { + if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) + xcount++; + dcount++; + s_entry = s_entry->next; + } + + if( dcount == 0 ) + { + return 0; + } + + /* + * OK, now we know how many there are. Build a vector for sorting. + */ + sortlist = (struct directory_entry **) + e_malloc(sizeof(struct directory_entry *) * dcount); + + j = dcount - 1; + dcount = 0; + s_entry = *sort_dir; + while(s_entry) + { + if(s_entry->de_flags & INHIBIT_ISO9660_ENTRY) + { + /* put any hidden entries at the end of the vector */ + sortlist[j--] = s_entry; + } + else + { + sortlist[dcount] = s_entry; + dcount++; + } + len = s_entry->isorec.name_len[0]; + s_entry->isorec.name[len] = 0; + s_entry = s_entry->next; + } + + /* + * Each directory is required to contain at least . and .. + */ + if( dcount < 2 ) + { + sort_goof = 1; + + } + else + { + /* only sort the non-hidden entries */ + sort_goof = 0; +#ifdef __STDC__ + qsort(sortlist, dcount, sizeof(struct directory_entry *), + (int (*)(const void *, const void *))compare_dirs); +#else + qsort(sortlist, dcount, sizeof(struct directory_entry *), + compare_dirs); +#endif + + /* + * Now reassemble the linked list in the proper sorted order + * We still need the hidden entries, as they may be used in the + * Joliet tree. + */ + for(i=0; inext = sortlist[i+1]; + } + + sortlist[dcount+xcount-1]->next = NULL; + *sort_dir = sortlist[0]; + } + + free(sortlist); + return sort_goof; +} + +static int root_gen() +{ + init_fstatbuf(); + + root_record.length[0] = 1 + sizeof(struct iso_directory_record) + - sizeof(root_record.name); + root_record.ext_attr_length[0] = 0; + set_733((char *) root_record.extent, root->extent); + set_733((char *) root_record.size, ROUND_UP(root->size)); + iso9660_date(root_record.date, root_statbuf.st_mtime); + root_record.flags[0] = 2; + root_record.file_unit_size[0] = 0; + root_record.interleave[0] = 0; + set_723(root_record.volume_sequence_number, volume_sequence_number); + root_record.name_len[0] = 1; + return 0; +} + +static void FDECL1(assign_file_addresses, struct directory *, dpnt) +{ + struct directory * finddir; + struct directory_entry * s_entry; + struct file_hash *s_hash; + struct deferred_write * dwpnt; + char whole_path[1024]; + + while (dpnt) + { + s_entry = dpnt->contents; + for(s_entry = dpnt->contents; s_entry; s_entry = s_entry->next) + { + /* + * If we already have an extent for this entry, + * then don't assign a new one. It must have come + * from a previous session on the disc. Note that + * we don't end up scheduling the thing for writing + * either. + */ + if( isonum_733((unsigned char *) s_entry->isorec.extent) != 0 ) + { + continue; + } + + /* + * This saves some space if there are symlinks present + */ + s_hash = find_hash(s_entry->dev, s_entry->inode); + if(s_hash) + { + if(verbose > 2) + { + fprintf(stderr, "Cache hit for %s%s%s\n",s_entry->filedir->de_name, + SPATH_SEPARATOR, s_entry->name); + } + set_733((char *) s_entry->isorec.extent, s_hash->starting_block); + set_733((char *) s_entry->isorec.size, s_hash->size); + continue; + } + + /* + * If this is for a directory that is not a . or a .. entry, + * then look up the information for the entry. We have already + * assigned extents for directories, so we just need to + * fill in the blanks here. + */ + if (strcmp(s_entry->name,".") && strcmp(s_entry->name,"..") && + s_entry->isorec.flags[0] == 2) + { + finddir = dpnt->subdir; + while(1==1) + { + if(finddir->self == s_entry) break; + finddir = finddir->next; + if(!finddir) + { + fprintf(stderr,"Fatal goof\n"); exit(1); + } + } + set_733((char *) s_entry->isorec.extent, finddir->extent); + s_entry->starting_block = finddir->extent; + s_entry->size = ROUND_UP(finddir->size); + total_dir_size += s_entry->size; + add_hash(s_entry); + set_733((char *) s_entry->isorec.size, ROUND_UP(finddir->size)); + continue; + } + + + /* + * If this is . or .., then look up the relevant info from the + * tables. + */ + if(strcmp(s_entry->name,".") == 0) + { + set_733((char *) s_entry->isorec.extent, dpnt->extent); + + /* + * Set these so that the hash table has the + * correct information + */ + s_entry->starting_block = dpnt->extent; + s_entry->size = ROUND_UP(dpnt->size); + + add_hash(s_entry); + s_entry->starting_block = dpnt->extent; + set_733((char *) s_entry->isorec.size, ROUND_UP(dpnt->size)); + continue; + } + + if(strcmp(s_entry->name,"..") == 0) + { + if(dpnt == root) + { + total_dir_size += root->size; + } + set_733((char *) s_entry->isorec.extent, dpnt->parent->extent); + + /* + * Set these so that the hash table has the + * correct information + */ + s_entry->starting_block = dpnt->parent->extent; + s_entry->size = ROUND_UP(dpnt->parent->size); + + add_hash(s_entry); + s_entry->starting_block = dpnt->parent->extent; + set_733((char *) s_entry->isorec.size, ROUND_UP(dpnt->parent->size)); + continue; + } + + /* + * Some ordinary non-directory file. Just schedule the + * file to be written. This is all quite + * straightforward, just make a list and assign extents + * as we go. Once we get through writing all of the + * directories, we should be ready write out these + * files + */ + if(s_entry->size) + { + dwpnt = (struct deferred_write *) + e_malloc(sizeof(struct deferred_write)); + if(dw_tail) + { + dw_tail->next = dwpnt; + dw_tail = dwpnt; + } + else + { + dw_head = dwpnt; + dw_tail = dwpnt; + } + if(s_entry->inode == TABLE_INODE) + { + dwpnt->table = s_entry->table; + dwpnt->name = NULL; + sprintf(whole_path,"%s%sTRANS.TBL", + s_entry->filedir->whole_name, SPATH_SEPARATOR); + } + else + { + dwpnt->table = NULL; + strcpy(whole_path, s_entry->whole_name); + dwpnt->name = strdup(whole_path); + } + dwpnt->next = NULL; + dwpnt->size = s_entry->size; + dwpnt->extent = last_extent; + set_733((char *) s_entry->isorec.extent, last_extent); + s_entry->starting_block = last_extent; + add_hash(s_entry); + last_extent += ROUND_UP(s_entry->size) >> 11; + if(verbose > 2) + { + fprintf(stderr,"%llu %llu %s\n", s_entry->starting_block, + last_extent-1, whole_path); + } +#ifdef DBG_ISO + if((ROUND_UP(s_entry->size) >> 11) > 500) + { + fprintf(stderr,"Warning: large file %s\n", whole_path); + fprintf(stderr,"Starting block is %d\n", s_entry->starting_block); + fprintf(stderr,"Reported file size is %d extents\n", s_entry->size); + + } +#endif +#ifdef NOT_NEEDED /* Never use this code if you like to create a DVD */ + + if(last_extent > (800000000 >> 11)) + { + /* + * More than 800Mb? Punt + */ + fprintf(stderr,"Extent overflow processing file %s\n", whole_path); + fprintf(stderr,"Starting block is %d\n", s_entry->starting_block); + fprintf(stderr,"Reported file size is %d extents\n", s_entry->size); + exit(1); + } +#endif + continue; + } + + /* + * This is for zero-length files. If we leave the extent 0, + * then we get screwed, because many readers simply drop files + * that have an extent of zero. Thus we leave the size 0, + * and just assign the extent number. + */ + set_733((char *) s_entry->isorec.extent, last_extent); + } + if(dpnt->subdir) + { + assign_file_addresses(dpnt->subdir); + } + dpnt = dpnt->next; + } +} /* assign_file_addresses(... */ + +static void FDECL1(free_one_directory, struct directory *, dpnt) +{ + struct directory_entry * s_entry; + struct directory_entry * s_entry_d; + + s_entry = dpnt->contents; + while(s_entry) + { + s_entry_d = s_entry; + s_entry = s_entry->next; + + if( s_entry_d->name != NULL ) + { + free (s_entry_d->name); + } + if( s_entry_d->whole_name != NULL ) + { + free (s_entry_d->whole_name); + } + free (s_entry_d); + } + dpnt->contents = NULL; +} /* free_one_directory(... */ + +static void FDECL1(free_directories, struct directory *, dpnt) +{ + while (dpnt) + { + free_one_directory(dpnt); + if(dpnt->subdir) free_directories(dpnt->subdir); + dpnt = dpnt->next; + } +} + +void FDECL2(generate_one_directory, struct directory *, dpnt, FILE *, outfile) +{ + unsigned int ce_address = 0; + char * ce_buffer; + unsigned int ce_index = 0; + unsigned int ce_size; + unsigned int dir_index; + char * directory_buffer; + int new_reclen; + struct directory_entry * s_entry; + struct directory_entry * s_entry_d; + unsigned int total_size; + + total_size = (dpnt->size + (SECTOR_SIZE - 1)) & ~(SECTOR_SIZE - 1); + directory_buffer = (char *) e_malloc(total_size); + memset(directory_buffer, 0, total_size); + dir_index = 0; + + ce_size = (dpnt->ce_bytes + (SECTOR_SIZE - 1)) & ~(SECTOR_SIZE - 1); + ce_buffer = NULL; + + if(ce_size) + { + ce_buffer = (char *) e_malloc(ce_size); + memset(ce_buffer, 0, ce_size); + + ce_index = 0; + + /* + * Absolute byte address of CE entries for this directory + */ + ce_address = last_extent_written + (total_size >> 11); + ce_address = ce_address << 11; + } + + s_entry = dpnt->contents; + while(s_entry) + { + /* skip if it's hidden */ + if(s_entry->de_flags & INHIBIT_ISO9660_ENTRY) { + s_entry = s_entry->next; + continue; + } + + /* + * We do not allow directory entries to cross sector boundaries. + * Simply pad, and then start the next entry at the next sector + */ + new_reclen = s_entry->isorec.length[0]; + if( (dir_index & (SECTOR_SIZE - 1)) + new_reclen >= SECTOR_SIZE ) + { + dir_index = (dir_index + (SECTOR_SIZE - 1)) & + ~(SECTOR_SIZE - 1); + } + + memcpy(directory_buffer + dir_index, &s_entry->isorec, + sizeof(struct iso_directory_record) - + sizeof(s_entry->isorec.name) + s_entry->isorec.name_len[0]); + dir_index += sizeof(struct iso_directory_record) - + sizeof (s_entry->isorec.name)+ s_entry->isorec.name_len[0]; + + /* + * Add the Rock Ridge attributes, if present + */ + if(s_entry->rr_attr_size) + { + if(dir_index & 1) + { + directory_buffer[dir_index++] = 0; + } + + /* + * If the RR attributes were too long, then write the + * CE records, as required. + */ + if(s_entry->rr_attr_size != s_entry->total_rr_attr_size) + { + unsigned char * pnt; + int len, nbytes; + + /* + * Go through the entire record and fix up the CE entries + * so that the extent and offset are correct + */ + + pnt = s_entry->rr_attributes; + len = s_entry->total_rr_attr_size; + while(len > 3) + { +#ifdef DEBUG + if (!ce_size) + { + fprintf(stderr,"Warning: ce_index(%d) && ce_address(%d) not initialized\n", + ce_index, ce_address); + } +#endif + + if(pnt[0] == 'C' && pnt[1] == 'E') + { + nbytes = get_733( (char *) pnt+20); + + if((ce_index & (SECTOR_SIZE - 1)) + nbytes >= + SECTOR_SIZE) + { + ce_index = ROUND_UP(ce_index); + } + + set_733( (char *) pnt+4, + (ce_address + ce_index) >> 11); + set_733( (char *) pnt+12, + (ce_address + ce_index) & (SECTOR_SIZE - 1)); + + + /* + * Now store the block in the ce buffer + */ + memcpy(ce_buffer + ce_index, + pnt + pnt[2], nbytes); + ce_index += nbytes; + if(ce_index & 1) + { + ce_index++; + } + } + len -= pnt[2]; + pnt += pnt[2]; + } + + } + + rockridge_size += s_entry->total_rr_attr_size; + memcpy(directory_buffer + dir_index, s_entry->rr_attributes, + s_entry->rr_attr_size); + dir_index += s_entry->rr_attr_size; + } + if(dir_index & 1) + { + directory_buffer[dir_index++] = 0; + } + + s_entry_d = s_entry; + s_entry = s_entry->next; + + /* + * Joliet doesn't use the Rock Ridge attributes, so we free it here. + */ + if (s_entry_d->rr_attributes) + { + free(s_entry_d->rr_attributes); + s_entry_d->rr_attributes = NULL; + } + } + + if(dpnt->size != dir_index) + { + fprintf(stderr,"Unexpected directory length %d %d %s\n",dpnt->size, + dir_index, dpnt->de_name); + } + + xfwrite(directory_buffer, 1, total_size, outfile); + last_extent_written += total_size >> 11; + free(directory_buffer); + + if(ce_size) + { + if(ce_index != dpnt->ce_bytes) + { + fprintf(stderr,"Continuation entry record length mismatch (%d %d).\n", + ce_index, dpnt->ce_bytes); + } + xfwrite(ce_buffer, 1, ce_size, outfile); + last_extent_written += ce_size >> 11; + free(ce_buffer); + } + +} /* generate_one_directory(... */ + +static +void FDECL1(build_pathlist, struct directory *, node) +{ + struct directory * dpnt; + + dpnt = node; + + while (dpnt) + { + /* skip if it's hidden */ + if( (dpnt->dir_flags & INHIBIT_ISO9660_ENTRY) == 0 ) + pathlist[dpnt->path_index] = dpnt; + + if(dpnt->subdir) build_pathlist(dpnt->subdir); + dpnt = dpnt->next; + } +} /* build_pathlist(... */ + +static int FDECL2(compare_paths, void const *, r, void const *, l) +{ + struct directory const *ll = *(struct directory * const *)l; + struct directory const *rr = *(struct directory * const *)r; + + if (rr->parent->path_index < ll->parent->path_index) + { + return -1; + } + + if (rr->parent->path_index > ll->parent->path_index) + { + return 1; + } + + return strcmp(rr->self->isorec.name, ll->self->isorec.name); + +} /* compare_paths(... */ + +static int generate_path_tables() +{ + struct directory_entry * de; + struct directory * dpnt; + int fix; + int i; + int j; + int namelen; + char * npnt; + char * npnt1; + int tablesize; + + /* + * First allocate memory for the tables and initialize the memory + */ + tablesize = path_blocks << 11; + path_table_m = (char *) e_malloc(tablesize); + path_table_l = (char *) e_malloc(tablesize); + memset(path_table_l, 0, tablesize); + memset(path_table_m, 0, tablesize); + + /* + * Now start filling in the path tables. Start with root directory + */ + if( next_path_index > 0xffff ) + { + fprintf(stderr, "Unable to generate sane path tables - too many directories (%d)\n", + next_path_index); + exit(1); + } + + path_table_index = 0; + pathlist = (struct directory **) e_malloc(sizeof(struct directory *) + * next_path_index); + memset(pathlist, 0, sizeof(struct directory *) * next_path_index); + build_pathlist(root); + + do + { + fix = 0; +#ifdef __STDC__ + qsort(&pathlist[1], next_path_index-1, sizeof(struct directory *), + (int (*)(const void *, const void *))compare_paths); +#else + qsort(&pathlist[1], next_path_index-1, sizeof(struct directory *), + compare_paths); +#endif + + for(j=1; jpath_index != j) + { + pathlist[j]->path_index = j; + fix++; + } + } + } while(fix); + + for(j=1; jde_name; + + /* + * So the root comes out OK + */ + if( (*npnt == 0) || (dpnt == root) ) + { + npnt = "."; + } + npnt1 = strrchr(npnt, PATH_SEPARATOR); + if(npnt1) + { + npnt = npnt1 + 1; + } + + de = dpnt->self; + if(!de) + { + fprintf(stderr,"Fatal goof\n"); + exit(1); + } + + + namelen = de->isorec.name_len[0]; + + path_table_l[path_table_index] = namelen; + path_table_m[path_table_index] = namelen; + path_table_index += 2; + + set_731(path_table_l + path_table_index, dpnt->extent); + set_732(path_table_m + path_table_index, dpnt->extent); + path_table_index += 4; + + set_721(path_table_l + path_table_index, + dpnt->parent->path_index); + set_722(path_table_m + path_table_index, + dpnt->parent->path_index); + path_table_index += 2; + + for(i =0; iisorec.name[i]; + path_table_m[path_table_index] = de->isorec.name[i]; + path_table_index++; + } + if(path_table_index & 1) + { + path_table_index++; /* For odd lengths we pad */ + } + } + + free(pathlist); + if(path_table_index != path_table_size) + { + fprintf(stderr,"Path table lengths do not match %d %d\n", + path_table_index, + path_table_size); + } + return 0; +} /* generate_path_tables(... */ + +void +FDECL3(memcpy_max, char *, to, char *, from, int, max) +{ + int n = strlen(from); + if (n > max) + { + n = max; + } + memcpy(to, from, n); + +} /* memcpy_max(... */ + +void FDECL1(outputlist_insert, struct output_fragment *, frag) +{ + if( out_tail == NULL ) + { + out_list = out_tail = frag; + } + else + { + out_tail->of_next = frag; + out_tail = frag; + } +} + +static int FDECL1(file_write, FILE *, outfile) +{ + int should_write; + + /* + * OK, all done with that crap. Now write out the directories. + * This is where the fur starts to fly, because we need to keep track of + * each file as we find it and keep track of where we put it. + */ + + should_write = last_extent - session_start; + + if( print_size > 0 ) + { + fprintf(stderr,"Total extents scheduled to be written = %llu\n", + last_extent - session_start); + exit(0); + } + if( verbose > 2 ) + { +#ifdef DBG_ISO + fprintf(stderr,"Total directory extents being written = %llu\n", last_extent); +#endif + + fprintf(stderr,"Total extents scheduled to be written = %llu\n", + last_extent - session_start); + } + + /* + * Now write all of the files that we need. + */ + write_files(outfile); + + /* + * The rest is just fluff. + */ + if( verbose == 0 ) + { + return 0; + } + + fprintf(stderr,"Total extents actually written = %llu\n", + last_extent_written - session_start); + + /* + * Hard links throw us off here + */ + assert (last_extent > session_start); + if(should_write + session_start != last_extent) + { + fprintf(stderr,"Number of extents written not what was predicted. Please fix.\n"); + fprintf(stderr,"Predicted = %d, written = %llu\n", should_write, last_extent); + } + + fprintf(stderr,"Total translation table size: %d\n", table_size); + fprintf(stderr,"Total rockridge attributes bytes: %d\n", rockridge_size); + fprintf(stderr,"Total directory bytes: %d\n", total_dir_size); + fprintf(stderr,"Path table size(bytes): %d\n", path_table_size); + +#ifdef DEBUG + fprintf(stderr, "next extent, last_extent, last_extent_written %d %d %d\n", + next_extent, last_extent, last_extent_written); +#endif + + return 0; + +} /* iso_write(... */ + +char *creation_date = NULL; +char *modification_date = NULL; +char *expiration_date = NULL; +char *effective_date = NULL; + +/* + * Function to write the PVD for the disc. + */ +static int FDECL1(pvd_write, FILE *, outfile) +{ + char iso_time[17]; + int should_write; + struct tm local; + struct tm gmt; + + + time(&begun); + + local = *localtime(&begun); + gmt = *gmtime(&begun); + + /* + * This will break in the year 2000, I supose, but there is no good way + * to get the top two digits of the year. + */ + sprintf(iso_time, "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d00", 1900 + local.tm_year, + local.tm_mon+1, local.tm_mday, + local.tm_hour, local.tm_min, local.tm_sec); + + local.tm_min -= gmt.tm_min; + local.tm_hour -= gmt.tm_hour; + local.tm_yday -= gmt.tm_yday; + iso_time[16] = (local.tm_min + 60*(local.tm_hour + 24*local.tm_yday)) / 15; + + /* + * Next we write out the primary descriptor for the disc + */ + memset(&vol_desc, 0, sizeof(vol_desc)); + vol_desc.type[0] = ISO_VD_PRIMARY; + memcpy(vol_desc.id, ISO_STANDARD_ID, sizeof(ISO_STANDARD_ID)); + vol_desc.version[0] = 1; + + memset(vol_desc.system_id, ' ', sizeof(vol_desc.system_id)); + memcpy_max(vol_desc.system_id, system_id, strlen(system_id)); + + memset(vol_desc.volume_id, ' ', sizeof(vol_desc.volume_id)); + memcpy_max(vol_desc.volume_id, volume_id, strlen(volume_id)); + + should_write = last_extent - session_start; + set_733((char *) vol_desc.volume_space_size, should_write); + set_723(vol_desc.volume_set_size, volume_set_size); + set_723(vol_desc.volume_sequence_number, volume_sequence_number); + set_723(vol_desc.logical_block_size, 2048); + + /* + * The path tables are used by DOS based machines to cache directory + * locations + */ + + set_733((char *) vol_desc.path_table_size, path_table_size); + set_731(vol_desc.type_l_path_table, path_table[0]); + set_731(vol_desc.opt_type_l_path_table, path_table[1]); + set_732(vol_desc.type_m_path_table, path_table[2]); + set_732(vol_desc.opt_type_m_path_table, path_table[3]); + + /* + * Now we copy the actual root directory record + */ + memcpy(vol_desc.root_directory_record, &root_record, + sizeof(struct iso_directory_record) + 1); + + /* + * The rest is just fluff. It looks nice to fill in many of these fields, + * though. + */ + FILL_SPACE(volume_set_id); + if(volset_id) memcpy_max(vol_desc.volume_set_id, volset_id, strlen(volset_id)); + + FILL_SPACE(publisher_id); + if(publisher) memcpy_max(vol_desc.publisher_id, publisher, strlen(publisher)); + + FILL_SPACE(preparer_id); + if(preparer) memcpy_max(vol_desc.preparer_id, preparer, strlen(preparer)); + + FILL_SPACE(application_id); + if(appid) memcpy_max(vol_desc.application_id, appid, strlen(appid)); + + FILL_SPACE(copyright_file_id); + if(copyright) memcpy_max(vol_desc.copyright_file_id, copyright, + strlen(copyright)); + + FILL_SPACE(abstract_file_id); + if(abstract) memcpy_max(vol_desc.abstract_file_id, abstract, + strlen(abstract)); + + FILL_SPACE(bibliographic_file_id); + if(biblio) memcpy_max(vol_desc.bibliographic_file_id, biblio, + strlen(biblio)); + + FILL_SPACE(creation_date); + FILL_SPACE(modification_date); + FILL_SPACE(expiration_date); + FILL_SPACE(effective_date); + vol_desc.file_structure_version[0] = 1; + FILL_SPACE(application_data); + + memcpy(vol_desc.creation_date, creation_date ? creation_date : iso_time, 17); + memcpy(vol_desc.modification_date, modification_date ? modification_date : iso_time, 17); + memcpy(vol_desc.expiration_date, expiration_date ? expiration_date : "0000000000000000", 17); + memcpy(vol_desc.effective_date, effective_date ? effective_date : iso_time, 17); + + /* + * if not a bootable cd do it the old way + */ + xfwrite(&vol_desc, 1, 2048, outfile); + last_extent_written++; + return 0; +} + +/* + * Function to write the EVD for the disc. + */ +static int FDECL1(evd_write, FILE *, outfile) +{ + struct iso_primary_descriptor evol_desc; + + /* + * Now write the end volume descriptor. Much simpler than the other one + */ + memset(&evol_desc, 0, sizeof(evol_desc)); + evol_desc.type[0] = ISO_VD_END; + memcpy(evol_desc.id, ISO_STANDARD_ID, sizeof(ISO_STANDARD_ID)); + evol_desc.version[0] = 1; + xfwrite(&evol_desc, 1, 2048, outfile); + last_extent_written += 1; + return 0; +} + +/* + * Function to write the EVD for the disc. + */ +static int FDECL1(pathtab_write, FILE *, outfile) +{ + /* + * Next we write the path tables + */ + xfwrite(path_table_l, 1, path_blocks << 11, outfile); + xfwrite(path_table_m, 1, path_blocks << 11, outfile); + last_extent_written += 2*path_blocks; + free(path_table_l); + free(path_table_m); + path_table_l = NULL; + path_table_m = NULL; + return 0; +} + +static int FDECL1(exten_write, FILE *, outfile) +{ + xfwrite(extension_record, 1, SECTOR_SIZE, outfile); + last_extent_written++; + return 0; +} + +/* + * Functions to describe padding block at the start of the disc. + */ +int FDECL1(oneblock_size, int, starting_extent) +{ + last_extent++; + return 0; +} + +/* + * Functions to describe padding block at the start of the disc. + */ +static int FDECL1(pathtab_size, int, starting_extent) +{ + path_table[0] = starting_extent; + + path_table[1] = 0; + path_table[2] = path_table[0] + path_blocks; + path_table[3] = 0; + last_extent += 2*path_blocks; + return 0; +} + +static int FDECL1(padblock_size, int, starting_extent) +{ + last_extent += 16; + return 0; +} + +static int file_gen() +{ + assign_file_addresses(root); + return 0; +} + +static int dirtree_dump() +{ + if (verbose > 2) + { + dump_tree(root); + } + return 0; +} + +static int FDECL1(dirtree_fixup, int, starting_extent) +{ + if (use_RockRidge && reloc_dir) + finish_cl_pl_entries(); + + if (use_RockRidge ) + update_nlink_field(root); + return 0; +} + +static int FDECL1(dirtree_size, int, starting_extent) +{ + assign_directory_addresses(root); + return 0; +} + +static int FDECL1(ext_size, int, starting_extent) +{ + extern int extension_record_size; + struct directory_entry * s_entry; + extension_record_extent = starting_extent; + s_entry = root->contents; + set_733((char *) s_entry->rr_attributes + s_entry->rr_attr_size - 24, + extension_record_extent); + set_733((char *) s_entry->rr_attributes + s_entry->rr_attr_size - 8, + extension_record_size); + last_extent++; + return 0; +} + +static int FDECL1(dirtree_write, FILE *, outfile) +{ + generate_iso9660_directories(root, outfile); + return 0; +} + +static int FDECL1(dirtree_cleanup, FILE *, outfile) +{ + free_directories(root); + return 0; +} + +static int FDECL1(padblock_write, FILE *, outfile) +{ + char buffer[2048]; + int i; + + memset(buffer, 0, sizeof(buffer)); + + for(i=0; i<16; i++) + { + xfwrite(buffer, 1, sizeof(buffer), outfile); + } + + last_extent_written += 16; + return 0; +} + +struct output_fragment padblock_desc = {NULL, padblock_size, NULL, padblock_write}; +struct output_fragment voldesc_desc = {NULL, oneblock_size, root_gen, pvd_write}; +struct output_fragment end_vol = {NULL, oneblock_size, NULL, evd_write}; +struct output_fragment pathtable_desc = {NULL, pathtab_size, generate_path_tables, pathtab_write}; +struct output_fragment dirtree_desc = {NULL, dirtree_size, NULL, dirtree_write}; +struct output_fragment dirtree_clean = {NULL, dirtree_fixup, dirtree_dump, dirtree_cleanup}; +struct output_fragment extension_desc = {NULL, ext_size, NULL, exten_write}; +struct output_fragment files_desc = {NULL, NULL, file_gen, file_write}; diff --git a/util/powerpc/ieee1275/grub-mkrescue.in b/util/powerpc/ieee1275/grub-mkrescue.in index 63bc7d88b..0110e799c 100644 --- a/util/powerpc/ieee1275/grub-mkrescue.in +++ b/util/powerpc/ieee1275/grub-mkrescue.in @@ -61,7 +61,7 @@ for option in "$@"; do usage exit 0 ;; -v | --version) - echo "grub-install (GNU GRUB ${PACKAGE_VERSION})" + echo "grub-mkrescue (GNU GRUB ${PACKAGE_VERSION})" exit 0 ;; --modules=*) modules=`echo "$option" | sed 's/--modules=//'` ;;